Skip to main content
A complete chat application built with React, Vite, and the Polpo React SDK. Features streaming responses, session history, agent selection, tool call display, and dark/light theme.
Source code: examples/chat-app/

Quick start

# Clone the repo
git clone https://github.com/lumea-labs/polpo.git
cd polpo/examples/chat-app

# Install dependencies
npm install

# Configure
cp .env.example .env.local
# Edit .env.local with your API key

Environment variables

VariableDescriptionDefault
VITE_POLPO_URLPolpo API URL (leave empty to use proxy)
VITE_POLPO_API_KEYYour project API key
VITE_POLPO_AGENTDefault agent name (optional)

Run locally

# Option 1: Against your local Polpo server
polpo start  # Terminal 1
npm run dev  # Terminal 2

# Option 2: Against Polpo Cloud (set API key in .env.local)
npm run dev

What’s included

Polpo React SDK integration

The app uses PolpoProvider for context and hooks for data:
import { PolpoProvider } from "@polpo-ai/react";

<PolpoProvider baseUrl={BASE_URL} apiKey={API_KEY} autoConnect={false}>
  <App />
</PolpoProvider>

Agent selection

On first load, the app fetches available agents using useAgents() and displays a selector:
const { agents } = useAgents();

// Renders a list of agents to choose from
{agents.map((a) => (
  <button onClick={() => setSelectedAgent(a.name)}>
    {a.name}
  </button>
))}

Streaming chat completions

Messages are streamed using chatCompletionsStream() from the client SDK:
const { client } = usePolpo();

const stream = client.chatCompletionsStream({
  messages: history,
  stream: true,
  agent: selectedAgent,
  sessionId: sessionId,
});

for await (const chunk of stream) {
  const delta = chunk.choices?.[0]?.delta?.content;
  // Append delta to the current message
}
The session ID is captured from the response header (x-session-id) so subsequent messages continue the same conversation.

Session history

Sessions are loaded from the server using useSessions():
const { sessions, getMessages, deleteSession } = useSessions();

// Load a previous session
const msgs = await getMessages(sessionId);

// Delete a session
await deleteSession(sessionId);
The sidebar shows all past sessions with agent name and date.

Tool calls

When the agent executes tools (bash, read, write, etc.), tool call events appear as collapsible blocks showing the tool name, arguments, and result:
const tc = choice.tool_call;
if (tc) {
  // Add to message's toolCalls array
}

Abort / Stop

Streaming can be stopped at any time using the Stop button, which calls stream.abort().

Markdown rendering

Responses are rendered using Streamdown with the @streamdown/code plugin for syntax-highlighted code blocks:
import { Streamdown } from "streamdown";
import { code } from "@streamdown/code";

<Streamdown mode="streaming" plugins={{ code }}>
  {message.content}
</Streamdown>

Dark / Light theme

Toggle between dark and light mode. Defaults to system preference. Persisted in localStorage.

Project structure

examples/chat-app/
├── src/
│   ├── main.tsx          # PolpoProvider setup
│   ├── App.tsx           # Full app: sidebar, chat, agent selector
│   ├── styles.css        # Dark/light theme CSS variables
│   └── vite-env.d.ts
├── .env.example          # Environment template
├── vite.config.ts        # Proxy configuration
├── package.json
└── README.md

Customization

Change the proxy target

Edit vite.config.ts to point to a different server:
const API_TARGET = process.env.POLPO_PROXY_TARGET ?? "http://api.polpo.localhost";

Add more features

The example is intentionally minimal. You can extend it with:
  • File attachments — using the attachments API
  • Approval gates — using useApprovals() hook
  • Task monitoring — using useTasks() and useMissions() hooks
  • Vault credentials — using useVaultEntries() hook
All hooks are available from @polpo-ai/react. See the React SDK docs for the full API.