diff --git a/packages/web-ui/CHANGELOG.md b/packages/web-ui/CHANGELOG.md index 517fdef1..8a1c26e4 100644 --- a/packages/web-ui/CHANGELOG.md +++ b/packages/web-ui/CHANGELOG.md @@ -14,6 +14,10 @@ - **`CustomMessages` interface removed**: Use declaration merging on `CustomAgentMessages` from `@mariozechner/pi-agent-core` instead. +- **`agent.appendMessage()` removed**: Use `agent.queueMessage()` instead. + +- **Agent event types changed**: `AgentInterface` now handles new event types from `@mariozechner/pi-agent-core`: `message_start`, `message_end`, `message_update`, `turn_start`, `turn_end`, `agent_start`, `agent_end`. + ### Added - **`defaultConvertToLlm`**: Default message transformer that handles `UserMessageWithAttachments` and `ArtifactMessage`. Apps can extend this for custom message types. @@ -38,6 +42,7 @@ - `AgentTransport` interface - `AgentRunConfig` type - `ProxyAssistantMessageEvent` type +- `test-sessions.ts` example file ### Migration Guide diff --git a/packages/web-ui/README.md b/packages/web-ui/README.md index fc459bd0..64006c10 100644 --- a/packages/web-ui/README.md +++ b/packages/web-ui/README.md @@ -1,23 +1,23 @@ # @mariozechner/pi-web-ui -Reusable web UI components for building AI chat interfaces powered by [@mariozechner/pi-ai](../ai). +Reusable web UI components for building AI chat interfaces powered by [@mariozechner/pi-ai](../ai) and [@mariozechner/pi-agent-core](../agent). - Built with [mini-lit](https://github.com/badlogic/mini-lit) web components and Tailwind CSS v4. +Built with [mini-lit](https://github.com/badlogic/mini-lit) web components and Tailwind CSS v4. ## Features -- Modern Chat Interface - Complete chat UI with message history, streaming responses, and tool execution -- Tool Support - Built-in renderers for calculator, bash, time, and custom tools -- Attachments - PDF, Office documents, images with preview and text extraction -- Artifacts - HTML, SVG, Markdown, and text artifact rendering with sandboxed execution -- Pluggable Transports - Direct API calls or proxy server support -- Platform Agnostic - Works in browser extensions, web apps, VS Code extensions, Electron apps -- TypeScript - Full type safety with TypeScript +- Modern Chat Interface: Complete chat UI with message history, streaming responses, and tool execution +- Tool Support: Built-in renderers for common tools plus custom tool rendering +- Attachments: PDF, Office documents, images with preview and text extraction +- Artifacts: HTML, SVG, Markdown, and text artifact rendering with sandboxed execution +- CORS Proxy Support: Automatic proxy handling for browser environments +- Platform Agnostic: Works in browser extensions, web apps, VS Code extensions, Electron apps +- TypeScript: Full type safety ## Installation ```bash -npm install @mariozechner/pi-web-ui +npm install @mariozechner/pi-web-ui @mariozechner/pi-agent-core @mariozechner/pi-ai ``` ## Quick Start @@ -25,19 +25,37 @@ npm install @mariozechner/pi-web-ui See the [example](./example) directory for a complete working application. ```typescript -import { Agent, ChatPanel, ProviderTransport, AppStorage, - SessionIndexedDBBackend, setAppStorage } from '@mariozechner/pi-web-ui'; +import { Agent } from '@mariozechner/pi-agent-core'; import { getModel } from '@mariozechner/pi-ai'; +import { + ChatPanel, + AppStorage, + IndexedDBStorageBackend, + ProviderKeysStore, + SessionsStore, + SettingsStore, + setAppStorage, + defaultConvertToLlm, +} from '@mariozechner/pi-web-ui'; import '@mariozechner/pi-web-ui/app.css'; // Set up storage -const storage = new AppStorage({ - sessions: new SessionIndexedDBBackend('my-app-sessions'), -}); -setAppStorage(storage); +const settings = new SettingsStore(); +const providerKeys = new ProviderKeysStore(); +const sessions = new SessionsStore(); -// Create transport -const transport = new ProviderTransport(); +const backend = new IndexedDBStorageBackend({ + dbName: 'my-app', + version: 1, + stores: [settings.getConfig(), providerKeys.getConfig(), sessions.getConfig()], +}); + +settings.setBackend(backend); +providerKeys.setBackend(backend); +sessions.setBackend(backend); + +const storage = new AppStorage(settings, providerKeys, sessions, undefined, backend); +setAppStorage(storage); // Create agent const agent = new Agent({ @@ -48,12 +66,17 @@ const agent = new Agent({ messages: [], tools: [], }, - transport, + convertToLlm: defaultConvertToLlm, }); // Create chat panel and attach agent const chatPanel = new ChatPanel(); -await chatPanel.setAgent(agent); +await chatPanel.setAgent(agent, { + onApiKeyRequired: async (provider) => { + // Prompt user for API key + return await ApiKeyPromptDialog.prompt(provider); + }, +}); document.body.appendChild(chatPanel); ``` @@ -66,117 +89,237 @@ npm install npm run dev ``` +## Architecture + +The web-ui package provides UI components that work with the `Agent` class from `@mariozechner/pi-agent-core`. The Agent handles: + +- Conversation state management +- LLM streaming via `streamFn` +- Tool execution +- Event emission + +The web-ui provides: + +- `ChatPanel` / `AgentInterface`: UI components that subscribe to Agent events +- `defaultConvertToLlm`: Message transformer for web-ui custom message types +- Storage backends for API keys, sessions, and settings +- CORS proxy utilities for browser environments + ## Core Components ### ChatPanel -The main chat interface component. Displays messages, handles input, and coordinates with the Agent. +High-level chat interface with artifacts panel support. ```typescript import { ChatPanel, ApiKeyPromptDialog } from '@mariozechner/pi-web-ui'; const chatPanel = new ChatPanel(); - -// Optional: Handle API key prompts -chatPanel.onApiKeyRequired = async (provider: string) => { - return await ApiKeyPromptDialog.prompt(provider); -}; - -// Attach an agent -await chatPanel.setAgent(agent); -``` - -### Agent - -Core state manager that handles conversation state, tool execution, and streaming. - -```typescript -import { Agent, ProviderTransport } from '@mariozechner/pi-web-ui'; -import { getModel } from '@mariozechner/pi-ai'; - -const agent = new Agent({ - initialState: { - model: getModel('anthropic', 'claude-sonnet-4-5-20250929'), - systemPrompt: 'You are a helpful assistant.', - thinkingLevel: 'off', - messages: [], - tools: [], +await chatPanel.setAgent(agent, { + onApiKeyRequired: async (provider) => ApiKeyPromptDialog.prompt(provider), + onBeforeSend: async () => { /* pre-send hook */ }, + onCostClick: () => { /* cost display clicked */ }, + toolsFactory: (agent, agentInterface, artifactsPanel, runtimeProvidersFactory) => { + // Return additional tools + return [createJavaScriptReplTool()]; }, - transport: new ProviderTransport(), }); - -// Subscribe to events -agent.subscribe((event) => { - if (event.type === 'state-update') { - console.log('Messages:', event.state.messages); - } -}); - -// Send a message -await agent.send('Hello!'); ``` ### AgentInterface -Lower-level chat interface for custom implementations. Used internally by ChatPanel. +Lower-level chat interface for custom layouts (used internally by ChatPanel). ```typescript import { AgentInterface } from '@mariozechner/pi-web-ui'; -const chat = new AgentInterface(); -await chat.setAgent(agent); +const chat = document.createElement('agent-interface') as AgentInterface; +chat.session = agent; +chat.enableAttachments = true; +chat.enableModelSelector = true; +chat.onApiKeyRequired = async (provider) => { /* ... */ }; ``` -## Transports +### Agent (from pi-agent-core) -Transport layers handle communication with AI providers. - -### ProviderTransport - -The main transport that calls AI provider APIs using stored API keys. +The Agent class is imported from `@mariozechner/pi-agent-core`: ```typescript -import { ProviderTransport } from '@mariozechner/pi-web-ui'; - -const transport = new ProviderTransport(); +import { Agent } from '@mariozechner/pi-agent-core'; +import { defaultConvertToLlm } from '@mariozechner/pi-web-ui'; const agent = new Agent({ - initialState: { /* ... */ }, - transport, + initialState: { + model: getModel('anthropic', 'claude-sonnet-4-5-20250929'), + systemPrompt: 'You are helpful.', + thinkingLevel: 'off', + messages: [], + tools: [], + }, + convertToLlm: defaultConvertToLlm, +}); + +// Subscribe to events +agent.subscribe((event) => { + switch (event.type) { + case 'agent_start': + case 'agent_end': + case 'message_start': + case 'message_update': + case 'message_end': + case 'turn_start': + case 'turn_end': + // Handle events + break; + } +}); + +// Send a message +await agent.prompt('Hello!'); + +// Or with custom message type +await agent.prompt({ + role: 'user-with-attachments', + content: 'Check this image', + attachments: [imageAttachment], + timestamp: Date.now(), }); ``` -### AppTransport +## Message Types -Alternative transport for proxying requests through a custom server. +### UserMessageWithAttachments + +Custom message type for user messages with file attachments: ```typescript -import { AppTransport } from '@mariozechner/pi-web-ui'; +import { isUserMessageWithAttachments, type UserMessageWithAttachments } from '@mariozechner/pi-web-ui'; -const transport = new AppTransport(); +const message: UserMessageWithAttachments = { + role: 'user-with-attachments', + content: 'Analyze this document', + attachments: [pdfAttachment, imageAttachment], + timestamp: Date.now(), +}; +``` +### ArtifactMessage + +For session persistence of created artifacts: + +```typescript +import { isArtifactMessage, type ArtifactMessage } from '@mariozechner/pi-web-ui'; + +const artifact: ArtifactMessage = { + role: 'artifact', + artifactId: 'chart-1', + type: 'html', + title: 'Sales Chart', + content: '