omnichatkit 0.0.22-b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +545 -0
- package/dist/index.cjs +6184 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4385 -0
- package/dist/index.d.mts +4385 -0
- package/dist/index.mjs +6125 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server/index.cjs +71 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +64 -0
- package/dist/server/index.d.mts +64 -0
- package/dist/server/index.mjs +70 -0
- package/dist/server/index.mjs.map +1 -0
- package/package.json +106 -0
package/README.md
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
# OmniChatKit
|
|
2
|
+
|
|
3
|
+
> [!CAUTION]
|
|
4
|
+
> Disclaimer: Initial release. Not Everything is working yet. Not yet production ready. Please use with caution.
|
|
5
|
+
|
|
6
|
+
OmniChatKit is a comprehensive, modular React component library designed for building next-generation AI chat interfaces. It provides robust state management, native support for Generative UI (A2UI), and out-of-the-box compatibility with both the **Vercel AI SDK** and the **AG-UI Protocol**.
|
|
7
|
+
|
|
8
|
+
OmniChatKit comes with a fully bundled set of pre-styled Shadcn components, meaning you can drop it into any Next.js or React application without having to copy-paste or maintain UI primitives.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
- 🔌 **Dual Protocol Support**: Choose between Vercel's standard Data Stream Protocol (`useChat`) or the advanced AG-UI Protocol (`@ag-ui/client`).
|
|
12
|
+
- 🎨 **Self-Contained UI**: Bundles 60+ customized Shadcn/Radix components internally (using `@base-ui/react` and Tailwind CSS).
|
|
13
|
+
- 🧩 **Generative UI (A2UI)**: Native support for rendering complex, interactive components dynamically via the `A2UICanvas` and a built-in catalog registry.
|
|
14
|
+
- 🏗️ **Unified Wrapper**: A single `<OmniChat>` wrapper component that handles provider injection, A2UI layout (`chat` vs `detached`), and API modes effortlessly.
|
|
15
|
+
- 📦 **Zustand State Management**: A unified, reactive state layer (`useAIChatStore`) decoupled from the underlying chat protocol.
|
|
16
|
+
- 🧠 **Native Reasoning Support**: Automatically extracts and beautifully renders `<think>` tags (e.g., from DeepSeek R1) as collapsible reasoning blocks.
|
|
17
|
+
- 🚦 **Advanced Interaction Control**: Built-in hooks for Human-in-the-Loop (HITL) workflows (`useHITL`) and streaming interrupts (`useInterrupts`).
|
|
18
|
+
- 📡 **Event Bus**: A lightweight `surface-bus.ts` to manage cross-component messaging and lifecycle events.
|
|
19
|
+
- 🔁 **Auto Context Hook**: `useChatContext` automatically resolves the correct chat context (classic or ag-ui) without prop-drilling.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
You can install OmniChatKit via the repo or from your package manager once published.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install omnichatkit
|
|
27
|
+
# or
|
|
28
|
+
pnpm add omnichatkit
|
|
29
|
+
# or
|
|
30
|
+
yarn add omnichatkit
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
*Note: Since OmniChatKit relies on React 19+ and bundles its own Shadcn dependencies, you may occasionally need to use `--legacy-peer-deps` depending on your host application's configuration.*
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Comprehensive Guide & Setup
|
|
38
|
+
|
|
39
|
+
OmniChatKit is designed to be highly flexible. You can use the all-in-one `<OmniChat>` wrapper or manually compose your UI using individual providers and managers.
|
|
40
|
+
|
|
41
|
+
### 1. The `<OmniChat>` Wrapper (Recommended)
|
|
42
|
+
|
|
43
|
+
The `<OmniChat>` component is the easiest way to orchestrate providers, generative UI, and chat interfaces. Simply choose your `api_mode` and drop in your components.
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { OmniChat, SessionManager } from 'omnichatkit';
|
|
47
|
+
|
|
48
|
+
export default function ChatPage() {
|
|
49
|
+
return (
|
|
50
|
+
<OmniChat
|
|
51
|
+
api_mode="ag-ui" // "ag-ui" or "classic"
|
|
52
|
+
apiEndpoint="/api/agent"
|
|
53
|
+
useA2UI={true}
|
|
54
|
+
a2uiProps={{
|
|
55
|
+
a2uiToolName: "render-dynamic-ui",
|
|
56
|
+
agentId: "orchestratr_agent",
|
|
57
|
+
a2uiRenderingOption: "detached" // "detached" (split pane) or "chat" (inline)
|
|
58
|
+
}}
|
|
59
|
+
theme="dark"
|
|
60
|
+
>
|
|
61
|
+
{/* SessionManager slides out from the left by default */}
|
|
62
|
+
<SessionManager storageMode="api" collapsible={false} className="w-80 shrink-0" />
|
|
63
|
+
|
|
64
|
+
{/* A2UICanvas renders Generative UI tools. It automatically inherits config from OmniChat! */}
|
|
65
|
+
<A2UICanvas emptyState={<div className="p-4 text-center">Waiting for UI...</div>} />
|
|
66
|
+
|
|
67
|
+
{/* ChatManager handles the message feed and input */}
|
|
68
|
+
<ChatManager display="embedded" displayOptions={{ collapsible: true }} position="right" />
|
|
69
|
+
</OmniChat>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 2. Custom Composition
|
|
75
|
+
|
|
76
|
+
If you need finer control over the layout, you can compose the providers and managers manually:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { AGUIChatProvider, ChatManager, A2UICanvas, SessionManager } from 'omnichatkit';
|
|
80
|
+
|
|
81
|
+
export default function ChatPage() {
|
|
82
|
+
return (
|
|
83
|
+
<AGUIChatProvider apiRoute="/api/agent">
|
|
84
|
+
<div className="flex h-screen w-full flex-row">
|
|
85
|
+
{/* Sidebar */}
|
|
86
|
+
<SessionManager storageMode="api" collapsible={true} position="left" />
|
|
87
|
+
|
|
88
|
+
<div className="flex flex-1 flex-col relative">
|
|
89
|
+
{/* ChatManager handles the message feed and input */}
|
|
90
|
+
<ChatManager display="embedded" displayOptions={{ collapsible: true }} position="right" />
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</AGUIChatProvider>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 3. API Route Setup
|
|
99
|
+
|
|
100
|
+
Depending on your `api_mode`, you need to set up your backend endpoint.
|
|
101
|
+
|
|
102
|
+
#### Vercel AI SDK Route (`api_mode="classic"`)
|
|
103
|
+
```typescript
|
|
104
|
+
// app/api/chat/route.ts
|
|
105
|
+
import { streamText } from 'ai';
|
|
106
|
+
import { openai } from '@ai-sdk/openai';
|
|
107
|
+
|
|
108
|
+
export async function POST(req: Request) {
|
|
109
|
+
const { messages } = await req.json();
|
|
110
|
+
const result = streamText({
|
|
111
|
+
model: openai('gpt-4o'),
|
|
112
|
+
messages,
|
|
113
|
+
});
|
|
114
|
+
return result.toDataStreamResponse();
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### AG-UI Protocol Route (`api_mode="ag-ui"`)
|
|
119
|
+
```typescript
|
|
120
|
+
// app/api/agent/route.ts
|
|
121
|
+
import { AGUIServer } from '@ag-ui/server';
|
|
122
|
+
|
|
123
|
+
export async function POST(req: Request) {
|
|
124
|
+
const { messages, session_id } = await req.json();
|
|
125
|
+
|
|
126
|
+
// Setup your agent stream and return the AG-UI formatted response
|
|
127
|
+
const stream = await myCustomAgent.run(messages);
|
|
128
|
+
return new Response(stream);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 4. `chatApiSchema` — Custom Backend Mapping (`classic` mode)
|
|
133
|
+
|
|
134
|
+
When `api_mode="classic"`, OmniChatKit uses the Vercel AI SDK's `useChat` hook under the hood, which sends messages in its own standard body shape. If your backend API expects a **different request/response format**, pass a `chatApiSchema` prop to `<OmniChat>` to act as a transparent mapper between the two.
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
<OmniChat
|
|
138
|
+
api_mode="classic"
|
|
139
|
+
apiEndpoint="/api/my-custom-agent"
|
|
140
|
+
chatApiSchema={{
|
|
141
|
+
apiRequestSchema: { /* how to serialize the outbound request */ },
|
|
142
|
+
apiResponseSchema: { /* how to deserialize the inbound response */ },
|
|
143
|
+
}}
|
|
144
|
+
>
|
|
145
|
+
<ChatManager />
|
|
146
|
+
</OmniChat>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
> [!NOTE]
|
|
150
|
+
> `chatApiSchema` is a **compile-time narrowed prop** — TypeScript will reject it (type error) if you pass it while `api_mode="ag-ui"`.
|
|
151
|
+
|
|
152
|
+
#### `apiRequestSchema`
|
|
153
|
+
|
|
154
|
+
Controls how the outgoing payload is built before each request.
|
|
155
|
+
|
|
156
|
+
| Field | Type | Default | Description |
|
|
157
|
+
|---|---|---|---|
|
|
158
|
+
| `messagesKey` | `string` | `"messages"` | Top-level key used for the messages array in the body |
|
|
159
|
+
| `userMessageKey` | `string` | — | If set, adds an extra key containing only the latest user message text |
|
|
160
|
+
| `extraBody` | `Record<string, unknown>` | — | Static fields merged into every request body |
|
|
161
|
+
| `transform` | `(payload) => payload` | — | Full custom serializer — receives the default payload, returns the final body |
|
|
162
|
+
|
|
163
|
+
**Example — rename the messages array and add a static model field:**
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
chatApiSchema: {
|
|
167
|
+
apiRequestSchema: {
|
|
168
|
+
messagesKey: 'history',
|
|
169
|
+
extraBody: { model: 'gpt-4o', temperature: 0.7 },
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
// Sends: { history: [...messages], model: 'gpt-4o', temperature: 0.7 }
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Example — completely custom body using `transform`:**
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
chatApiSchema: {
|
|
179
|
+
apiRequestSchema: {
|
|
180
|
+
transform: (payload) => ({
|
|
181
|
+
query: (payload.messages as any[]).at(-1)?.content ?? '',
|
|
182
|
+
context: payload.messages,
|
|
183
|
+
stream: true,
|
|
184
|
+
}),
|
|
185
|
+
},
|
|
186
|
+
}
|
|
187
|
+
// Sends: { query: "latest user message", context: [...], stream: true }
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### `apiResponseSchema`
|
|
191
|
+
|
|
192
|
+
Controls how the raw API JSON response is mapped back into OmniChatKit's internal message shape.
|
|
193
|
+
|
|
194
|
+
| Field | Type | Default | Description |
|
|
195
|
+
|---|---|---|---|
|
|
196
|
+
| `contentPath` | `string` | `"content"` | Dot-separated path to the assistant text inside the response JSON |
|
|
197
|
+
| `transform` | `(raw) => Partial<Message>` | — | Full custom deserializer — receives the raw parsed JSON, returns a message-compatible object |
|
|
198
|
+
|
|
199
|
+
**Example — your API returns `{ reply: "..." }` instead of `{ content: "..." }`:**
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
chatApiSchema: {
|
|
203
|
+
apiResponseSchema: {
|
|
204
|
+
contentPath: 'reply',
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Example — nested path `data.message.text`:**
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
chatApiSchema: {
|
|
213
|
+
apiResponseSchema: {
|
|
214
|
+
contentPath: 'data.message.text',
|
|
215
|
+
},
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Example — full custom transform:**
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
chatApiSchema: {
|
|
223
|
+
apiResponseSchema: {
|
|
224
|
+
transform: (raw) => ({
|
|
225
|
+
role: 'assistant',
|
|
226
|
+
content: (raw as any).output?.text ?? '',
|
|
227
|
+
}),
|
|
228
|
+
},
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### Accessing `chatApiSchema` from other components
|
|
233
|
+
|
|
234
|
+
The schema is also stored in the global Zustand store so any downstream component or hook can read it without prop-drilling:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import { useAIChatStore } from 'omnichatkit';
|
|
238
|
+
|
|
239
|
+
const chatApiSchema = useAIChatStore((s) => s.chatApiSchema);
|
|
240
|
+
// chatApiSchema?.apiRequestSchema, chatApiSchema?.apiResponseSchema
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
### 5. Customizing ChatManager
|
|
246
|
+
|
|
247
|
+
The `ChatManager` component comes with extensive styling and layout capabilities.
|
|
248
|
+
|
|
249
|
+
#### Layout Props
|
|
250
|
+
- **`display`** (`"floating" | "embedded"`): Controls the layout mode of the chat manager.
|
|
251
|
+
- **`displayOptions`** (`object`): Configuration for the selected display mode.
|
|
252
|
+
- `collapsible` (`boolean`): When display is "embedded", renders the component as a floating drawer (`<Sheet>`) with a dynamic toggle button.
|
|
253
|
+
- `isResizable` (`boolean`): When display is "embedded", allows the chat drawer to be resized by the user.
|
|
254
|
+
- **`position`** (`"left" | "right" | "top" | "bottom"`): Controls where the drawer docks and automatically aligns the close button correctly.
|
|
255
|
+
- **`welcomeScreen`** (`boolean | ReactNode`): Set to `true` (default) to show the default welcome screen, or pass a custom React element.
|
|
256
|
+
- **`maxInputCharacter`** (`number`): Optional limit for the maximum number of characters allowed in the chat input box.
|
|
257
|
+
- **`autoScroll`** (`boolean`): Automatically scrolls the chat feed to the bottom when new messages arrive. Defaults to `true`. Scrolling up manually will pause auto-scroll and show a "Scroll to bottom" button.
|
|
258
|
+
- **`streaming`** (`boolean`): Enable or disable streaming for responses. When set, this flag is forwarded to the backend via the request body. Omit to let the backend decide.
|
|
259
|
+
- **`sendHistory`** (`boolean`): Set to `false` to send only the latest message to the API instead of the entire chat history. Defaults to `true`.
|
|
260
|
+
- **`showToolCalls`** (`boolean`): Set to `true` to display tool invocations in the chat feed. Defaults to `false`.
|
|
261
|
+
- **`showReasoning`** (`boolean`): Set to `true` to display AI reasoning blocks (e.g. `<think>` tags) in the chat feed. Defaults to `false` (or handled automatically in some setups).
|
|
262
|
+
- **`inputTypeList`** (`Array<"image" | "document" | "audio" | "video">`): Enables the multimodal attachment menu and specifies which file types users can upload.
|
|
263
|
+
- **`promptChips`** (`PromptChips`): Render actionable chips above the input box (e.g., for suggested questions or starter prompts). Includes a `promptChipList` (title, hoverText, prompt) and an `alwaysShow` boolean flag.
|
|
264
|
+
- **`toggleButtonProps`** (`object`): Deep customization for the collapse/expand trigger button (replaces old `toggleButtonStyle`).
|
|
265
|
+
- `toggleButtonStyle`: Overall button container styles.
|
|
266
|
+
- `toggleButtonIconProps`: Nested object for `{ toggleButtonIcon, toggleButtonIconStyle }`. By default, renders a `MessageCircle` icon.
|
|
267
|
+
- `toggleButtonLabelProps`: Nested object for `{ toggleButtonLabel, toggleButtonLabelStyle }` to add text alongside the icon.
|
|
268
|
+
|
|
269
|
+
#### Streaming Toggle
|
|
270
|
+
|
|
271
|
+
Use the `streaming` prop to explicitly control whether responses are streamed:
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
{/* Disable streaming — receive the full response at once */}
|
|
275
|
+
<ChatManager streaming={false} />
|
|
276
|
+
|
|
277
|
+
{/* Force streaming on (default behavior for most backends) */}
|
|
278
|
+
<ChatManager streaming={true} />
|
|
279
|
+
|
|
280
|
+
{/* Omit the prop entirely to let the backend decide */}
|
|
281
|
+
<ChatManager />
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The `streaming` flag is forwarded in the request body (`{ streaming: true|false }`) on every message send, including prompt chip clicks. Your API route can read and act on this:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
export async function POST(req: Request) {
|
|
288
|
+
const { messages, streaming } = await req.json();
|
|
289
|
+
const result = streamText({ model: openai('gpt-4o'), messages });
|
|
290
|
+
return streaming === false
|
|
291
|
+
? result.toTextResponse()
|
|
292
|
+
: result.toDataStreamResponse();
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### Component Styling (`chatManagerComponentStyles`)
|
|
297
|
+
You can deeply customize the appearance of the ChatManager by passing nested style objects. We support `backgroundStyle` for all major layout sections, as well as advanced message and badge styling:
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
import { User, Bot } from 'lucide-react';
|
|
301
|
+
|
|
302
|
+
<ChatManager
|
|
303
|
+
chatManagerComponentStyles={{
|
|
304
|
+
backgroundStyle: "bg-slate-900", // Main container background
|
|
305
|
+
headerStyle: {
|
|
306
|
+
backgroundStyle: "bg-slate-950 border-b-slate-800",
|
|
307
|
+
titleStyle: "text-blue-400 font-bold",
|
|
308
|
+
collapseButtonStyle: "hover:bg-slate-800"
|
|
309
|
+
},
|
|
310
|
+
// Customize user and agent badges (name tags/icons)
|
|
311
|
+
userBadgeStyle: {
|
|
312
|
+
containerStyle: "bg-blue-100 dark:bg-blue-900/50 px-2 py-0.5 rounded-full flex items-center gap-1",
|
|
313
|
+
textStyle: "text-blue-700 dark:text-blue-300 font-medium text-xs",
|
|
314
|
+
icon: <User size={12} />
|
|
315
|
+
},
|
|
316
|
+
agentBadgeStyle: {
|
|
317
|
+
containerStyle: "bg-purple-100 dark:bg-purple-900/50 px-2 py-0.5 rounded-full flex items-center gap-1",
|
|
318
|
+
textStyle: "text-purple-700 dark:text-purple-300 font-medium text-xs",
|
|
319
|
+
icon: <Bot size={12} />
|
|
320
|
+
},
|
|
321
|
+
messageStyle: {
|
|
322
|
+
backgroundStyle: "bg-slate-900", // Message feed background
|
|
323
|
+
|
|
324
|
+
// Advanced message layout & styling
|
|
325
|
+
// Note: OmniChatKit automatically applies a sharp "notch" (corner radius)
|
|
326
|
+
// to the bottom-right or bottom-left depending on the alignment!
|
|
327
|
+
userMessageStyles: {
|
|
328
|
+
alignment: "right", // Align left, right, or center
|
|
329
|
+
bubbleStyle: "bg-blue-600 text-white shadow-md rounded-2xl px-4 py-3",
|
|
330
|
+
containerStyle: "mt-2",
|
|
331
|
+
attachmentPreviewStyles: {
|
|
332
|
+
containerStyle: "mt-2",
|
|
333
|
+
itemStyle: "border-blue-400 bg-blue-700/50"
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
assistantMessageStyles: {
|
|
337
|
+
alignment: "left", // Defaults to left, but can be overridden to center or right
|
|
338
|
+
bubbleStyle: "bg-slate-800 text-slate-200 shadow-sm rounded-2xl px-4 py-3"
|
|
339
|
+
},
|
|
340
|
+
stopResponseStyle: "text-slate-400" // Styles the Response Stopped divider
|
|
341
|
+
},
|
|
342
|
+
// Customize suggested prompt chips
|
|
343
|
+
promptChipStyles: {
|
|
344
|
+
promptChipContainerStyle: "pt-4 gap-2 border-t-slate-800",
|
|
345
|
+
promptChipTitleStyle: "bg-slate-800 text-slate-300 hover:bg-slate-700 rounded-full border border-slate-700",
|
|
346
|
+
promptChipHoverTextStyle: "transition-colors"
|
|
347
|
+
},
|
|
348
|
+
// Customize the "Scroll to bottom" button (appears when auto-scroll is interrupted)
|
|
349
|
+
scrollButtonStyles: {
|
|
350
|
+
iconStyles: "text-slate-300",
|
|
351
|
+
},
|
|
352
|
+
inputSectionStyle: {
|
|
353
|
+
backgroundStyle: "bg-slate-950",
|
|
354
|
+
containerStyle: "border-t-slate-800",
|
|
355
|
+
inputStyle: "bg-slate-900 border-slate-700 text-white",
|
|
356
|
+
sendButtonStyles: {
|
|
357
|
+
containerStyle: "bg-blue-600 hover:bg-blue-700 text-white"
|
|
358
|
+
},
|
|
359
|
+
attachmentMenuStyles: {
|
|
360
|
+
plusButtonContainerStyles: "text-slate-400 hover:text-white border-slate-700",
|
|
361
|
+
menuContainerStyles: "bg-slate-900 border-slate-700"
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}}
|
|
365
|
+
labels={{
|
|
366
|
+
title: "Support Assistant",
|
|
367
|
+
placeholder: "How can I help you today?",
|
|
368
|
+
sendButton: "Send Message"
|
|
369
|
+
}}
|
|
370
|
+
/>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### 6. Customizing SessionManager
|
|
374
|
+
|
|
375
|
+
The `SessionManager` handles chat history.
|
|
376
|
+
|
|
377
|
+
Enable sessions on `OmniChat` (or either chat provider) before rendering it. Session handling is disabled by default, so chats have no session creation, persistence, or rename requests unless you opt in.
|
|
378
|
+
|
|
379
|
+
```tsx
|
|
380
|
+
<OmniChat api_mode="ag-ui" sessionStorageMode="api">
|
|
381
|
+
<SessionManager />
|
|
382
|
+
<ChatManager />
|
|
383
|
+
</OmniChat>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
#### Props
|
|
387
|
+
- **`sessionStorageMode`** (`"disabled" | "api" | "memory"`): Set on `OmniChat` or a chat provider. `"disabled"` is the default; `SessionManager` throws if it is rendered in this mode.
|
|
388
|
+
- **`collapsible`** (`boolean`): Enables the drawer view for space-saving layouts.
|
|
389
|
+
- **`position`** (`"left" | "right"`): Where the manager should dock.
|
|
390
|
+
|
|
391
|
+
#### Session list style slots
|
|
392
|
+
|
|
393
|
+
Use `sessionManagerComponentStyles.listStyle` to replace the list icons or style each action:
|
|
394
|
+
|
|
395
|
+
```tsx
|
|
396
|
+
<SessionManager
|
|
397
|
+
sessionManagerComponentStyles={{
|
|
398
|
+
listStyle: {
|
|
399
|
+
listItemIconStyles: { icon: <MessageSquare />, iconStyle: 'text-primary' },
|
|
400
|
+
listItemPinButtonStyles: { icon: <Pin />, iconStyles: 'text-primary' },
|
|
401
|
+
listItemMenuButtonStyles: { icon: <MoreHorizontal />, iconStyle: 'text-primary' },
|
|
402
|
+
listItemRenameButtonStyles: { icon: <Pencil />, iconStyle: 'text-primary', text: 'Edit', textStyle: 'font-semibold' },
|
|
403
|
+
listItemDeleteButtonStyles: { icon: <Trash2 />, iconStyle: 'text-destructive', text: 'Remove', textStyle: 'font-semibold' },
|
|
404
|
+
},
|
|
405
|
+
}}
|
|
406
|
+
/>
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### 7. `useChatContext` — Auto Context Hook
|
|
410
|
+
|
|
411
|
+
`useChatContext` is a convenience hook that automatically resolves the correct chat context based on whichever provider (`AIChatProvider` or `AGUIChatProvider`) is present in the React tree. Use it instead of calling `useAIChatContext` or `useAGUIChatContext` directly.
|
|
412
|
+
|
|
413
|
+
```tsx
|
|
414
|
+
import { useChatContext } from 'omnichatkit';
|
|
415
|
+
|
|
416
|
+
function MyCustomChatUI() {
|
|
417
|
+
const { messages, append, status, stop } = useChatContext();
|
|
418
|
+
|
|
419
|
+
return (
|
|
420
|
+
<div>
|
|
421
|
+
{messages.map(m => <p key={m.id}>{m.content}</p>)}
|
|
422
|
+
<button onClick={() => append({ role: 'user', content: 'Hello!' })}>
|
|
423
|
+
Send
|
|
424
|
+
</button>
|
|
425
|
+
{status === 'streaming' && (
|
|
426
|
+
<button onClick={stop}>Stop</button>
|
|
427
|
+
)}
|
|
428
|
+
</div>
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**Resolution logic:**
|
|
434
|
+
1. **Only one provider in the tree** → returns that context directly. No store lookup needed.
|
|
435
|
+
2. **Both providers present** → uses the `api_mode` registered by `<OmniChat>` as a tiebreaker.
|
|
436
|
+
3. **Neither present** → throws a descriptive error.
|
|
437
|
+
|
|
438
|
+
> [!NOTE]
|
|
439
|
+
> `useChatContext` is safe to use immediately on first render. It reads context values synchronously from the React tree rather than relying on the store's `apiMode` value, which is set via `useEffect` and would not be available on the initial render.
|
|
440
|
+
|
|
441
|
+
### 8. Secure API Proxy (`omnichatkit/server`)
|
|
442
|
+
|
|
443
|
+
OmniChatKit provides a built-in, secure API proxy handler designed to sit between your Next.js frontend and your LLM backend. It utilizes Hono's native middleware to provide robust authentication and request forwarding.
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
// app/api/[[...slug]]/route.ts
|
|
447
|
+
import { serveOmniChat } from "omnichatkit/server";
|
|
448
|
+
|
|
449
|
+
const handler = serveOmniChat({
|
|
450
|
+
basePath: "/api",
|
|
451
|
+
backendUrl: process.env.BACKEND_URL,
|
|
452
|
+
|
|
453
|
+
// Optional: Automatically extract identity from requests
|
|
454
|
+
identifyUser: async (req) => {
|
|
455
|
+
// ... custom logic ...
|
|
456
|
+
return { id: "user_123", name: "Alice" };
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
// Native security middleware
|
|
460
|
+
security: {
|
|
461
|
+
// 1. Static API Key validation
|
|
462
|
+
apiKey: process.env.MY_API_KEY,
|
|
463
|
+
|
|
464
|
+
// 2. Bearer Token validation (Authorization: Bearer <token>)
|
|
465
|
+
// bearerToken: async (token) => await verifyOAuthToken(token),
|
|
466
|
+
|
|
467
|
+
// 3. JWT validation
|
|
468
|
+
// jwt: { secret: process.env.JWT_SECRET, alg: 'HS256' },
|
|
469
|
+
|
|
470
|
+
// 4. Custom Hono Middleware
|
|
471
|
+
// customMiddleware: [ ... ]
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
export const { GET, POST, PATCH, PUT, DELETE } = handler;
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
This proxy ensures unauthorized requests are immediately dropped (returning `401 Unauthorized`) before they hit your expensive LLM APIs.
|
|
479
|
+
|
|
480
|
+
### 9. Working with AI Reasoning (e.g. DeepSeek `<think>`)
|
|
481
|
+
OmniChatKit automatically parses and extracts `<think>` tags from incoming model streams. It strips these out of the primary text response and renders them natively as a beautiful, collapsible "Reasoning" accordion inside the message block! No extra configuration is required.
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## Core Architecture
|
|
486
|
+
|
|
487
|
+
OmniChatKit is structured around a few core pillars:
|
|
488
|
+
|
|
489
|
+
### 1. State Management (`useAIChatStore`)
|
|
490
|
+
OmniChatKit abstracts the chat stream state into a global Zustand store. Both `AIChatProvider` and `AGUIChatProvider` map their internal streaming events (Vercel SDK vs AG-UI) into this store. This means your UI components interact exclusively with `useAIChatStore`, completely decoupling your frontend from the backend protocol.
|
|
491
|
+
|
|
492
|
+
### 2. A2UI Canvas & Catalog
|
|
493
|
+
The `A2UICanvas` listens for specific tool invocations from the LLM and dynamically renders registered React components. You can pass your own custom components via the `catalog` prop on the provider, or rely on the robust default catalog bundled within OmniChatKit.
|
|
494
|
+
|
|
495
|
+
Tool invocations are read from the Vercel AI SDK's `message.parts` array (using `ToolInvocationUIPart` entries) with a transparent fallback to `message.toolInvocations` for AG-UI messages that predate the `parts` API.
|
|
496
|
+
|
|
497
|
+
### 3. Pre-bundled UI Primitives
|
|
498
|
+
Unlike traditional Shadcn implementations that require you to copy source code into your repository, OmniChatKit pre-bundles everything inside `src/components/ui`. This includes highly customized versions of `Button`, `Input`, `ScrollArea`, `Sheet`, and 50+ other components tailored for chat interfaces.
|
|
499
|
+
|
|
500
|
+
## Available Exports
|
|
501
|
+
|
|
502
|
+
OmniChatKit exports all necessary hooks, components, and types to give you full control over your chat experience:
|
|
503
|
+
|
|
504
|
+
**Providers & Managers**
|
|
505
|
+
- `OmniChat`
|
|
506
|
+
- `AIChatProvider`
|
|
507
|
+
- `AGUIChatProvider`
|
|
508
|
+
- `ChatManager`
|
|
509
|
+
- `SessionManager`
|
|
510
|
+
|
|
511
|
+
**Hooks**
|
|
512
|
+
- `useAIChatStore`
|
|
513
|
+
- `useChatContext` — auto-selects the correct context based on the active provider
|
|
514
|
+
- `useAIChatContext` — explicit classic (Vercel AI SDK) context accessor
|
|
515
|
+
- `useAGUIChatContext` — explicit AG-UI context accessor
|
|
516
|
+
- `useAGUIChat`
|
|
517
|
+
- `useHITL`
|
|
518
|
+
- `useInterrupts`
|
|
519
|
+
|
|
520
|
+
**UI & Generative Elements**
|
|
521
|
+
- `A2UICanvas`
|
|
522
|
+
- All pre-bundled Shadcn components (e.g., `Button`, `Input`, `ScrollArea`, `Sheet`, etc.)
|
|
523
|
+
|
|
524
|
+
## Development & Building
|
|
525
|
+
|
|
526
|
+
To contribute to OmniChatKit or build it locally:
|
|
527
|
+
|
|
528
|
+
```bash
|
|
529
|
+
# Install dependencies
|
|
530
|
+
npm install
|
|
531
|
+
|
|
532
|
+
# Run the development watcher
|
|
533
|
+
npm run dev
|
|
534
|
+
|
|
535
|
+
# Build the library (ESM, CJS, and Types)
|
|
536
|
+
npm run build
|
|
537
|
+
|
|
538
|
+
# Run typechecking
|
|
539
|
+
npm run lint
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
## License
|
|
543
|
+
|
|
544
|
+
Apache-2.0
|
|
545
|
+
|