# Chat components

> Add streaming chat without writing an event reducer.

## Install

[Create an API key](https://pantheon-todo.alethiconsulting.com/docs/get-a-key). Install the SDK for your application.

**Terminal, TypeScript**

```typescript title="Terminal, TypeScript"
npm install @alethi/pantheon-chat react@18 react-dom@18
```

**Terminal, Python**

```bash title="Terminal, Python"
pip install pantheon-chat
```

## React

Render the component with an agent slug and application tool handlers.

```typescript title="App.tsx"
import { PantheonChat } from "@alethi/pantheon-chat/react";

export default function App() {
  return (
    <PantheonChat
      baseUrl="PANTHEON_BASE_URL"
      apiKey="PANTHEON_API_KEY"
      agentSlug="todo-assistant"
      userId={signedInUserId}
      tools={{
        echo_tool: async (input) => ({ echoed: input.message }),
        todo_ops: {
          requireApproval: true,
          execute: async (input) => ({ applied: true, operations: input.operations }),
        },
      }}
    />
  );
}
```

`requireApproval` shows an in-page Approve or Decline card.

## Node client

Use the core client with Node 18 or later.

```javascript title="app.mjs"
import { PantheonClient } from "@alethi/pantheon-chat";

const baseUrl = process.env.PANTHEON_BASE_URL;
const apiKey = process.env.PANTHEON_API_KEY;
const client = new PantheonClient({ baseUrl, apiKey });
const session = await client.createSession({
  agentSlug: "todo-assistant",
  userId: signedInUserId,
});
const run = await client.startRun(session.id, "Hello");
for await (const event of client.streamEvents(session.id)) {
  if (event.type === "message.delta") process.stdout.write(event.delta.text || "");
  if (event.type === "run.completed") break;
}
```

## Identify the person chatting

Pass the id of the tenant user this conversation belongs to. The agent keeps long-lived notes about whoever it is talking to, and this is what tells Pantheon who that is.

With it, those notes follow the person across their sessions. Without it, they stay inside the single session, so nothing the agent learns from one visitor can reach another. Read [Agent memory](https://pantheon-todo.alethiconsulting.com/docs/sessions-and-runs#memory) before deciding.

## Props

| Prop           | Use                                                                                                                                                                                                                                        |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `baseUrl`      | Pantheon API base URL.                                                                                                                                                                                                                     |
| `apiKey`       | Bearer API key.                                                                                                                                                                                                                            |
| `agentSlug`    | Agent for a new session.                                                                                                                                                                                                                   |
| `deploymentId` | Explicit deployment instead of a slug.                                                                                                                                                                                                     |
| `userId`       | Which of your end users is chatting. It decides whose agent memory the conversation reads. Omit it and memory stays inside this one session. See [Agent memory](https://pantheon-todo.alethiconsulting.com/docs/sessions-and-runs#memory). |
| `tools`        | Application tool handlers and approval rules.                                                                                                                                                                                              |
| `theme`        | `light`, `dark`, or `auto`.                                                                                                                                                                                                                |
| `title`        | Session title.                                                                                                                                                                                                                             |

## Python

Use the async client directly.

```python title="app.py"
import asyncio
import os
from pantheon_chat import PantheonClient

async def main():
    async with PantheonClient(
        os.environ["PANTHEON_BASE_URL"],
        os.environ["PANTHEON_API_KEY"],
    ) as client:
        session = await client.create_session(
            agent_slug="todo-assistant",
            user_id=signed_in_user_id,
        )
        run = await client.start_run(session.id, "What is 15 + 15?")
        async for event in client.stream_events(session.id, run_id=run.run_id, stop_on_terminal=True):
            print(event.type, event.content or event.data)

asyncio.run(main())
```

### Terminal chat

```bash title="Terminal"
export PANTHEON_BASE_URL="https://pantheon-todo.alethiconsulting.com/pantheon-api"
export PANTHEON_API_KEY="PANTHEON_API_KEY"
export PANTHEON_AGENT_SLUG="todo-assistant"
python -m pantheon_chat
```

Application tools ask for approval. Pass `--auto-approve` only for trusted tools.

### Streamlit

```bash title="Terminal"
pip install "pantheon-chat[streamlit]"
export PANTHEON_BASE_URL="https://pantheon-todo.alethiconsulting.com/pantheon-api"
export PANTHEON_API_KEY="PANTHEON_API_KEY"
export PANTHEON_AGENT_SLUG="todo-assistant"
pantheon-chat-streamlit
```

## Pitfalls

> **Warning**
>
> - **Queued text can wait behind approval:** Resolve the approval before expecting the queued message to run.
> - **A network reconnect repeats output:** Persist only durable SSE ids. Never use an ephemeral delta's JSON id as the cursor.
> - **A Stop request fails:** Keep the run active and show the error until cancel returns HTTP 202.
