@filigran/chatbot 3.2.2 β 3.3.2
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/README.md +94 -45
- package/dist/index.d.ts +64 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ Filigran chat panel β a standalone React + Tailwind chatbot component with SSE
|
|
|
7
7
|
- π **SSE Message Streaming** β Real-time response streaming with status indicators
|
|
8
8
|
- π€ **Multi-Agent Support** β Switch between different AI agents
|
|
9
9
|
- π **File Attachments** β Upload and paste files (PDF, TXT, images)
|
|
10
|
+
- π₯ **Agent-Generated Files** β Renders downloadable file cards from agent output and strips the `[[FILE:id]]` markers from the prose
|
|
10
11
|
- π **Full Markdown** β Tables, code blocks with copy button, lists, blockquotes
|
|
11
12
|
- π¨ **Customizable Theme** β Accent color and logo customization
|
|
12
13
|
- π± **3 Display Modes** β Floating, sidebar (resizable), and fullscreen
|
|
@@ -32,20 +33,9 @@ function App() {
|
|
|
32
33
|
|
|
33
34
|
return (
|
|
34
35
|
<>
|
|
35
|
-
<ChatToggleButton
|
|
36
|
-
isOpen={isOpen}
|
|
37
|
-
onToggle={() => setIsOpen(!isOpen)}
|
|
38
|
-
label="Ask Assistant"
|
|
39
|
-
accentColor="#7b5cff"
|
|
40
|
-
/>
|
|
36
|
+
<ChatToggleButton isOpen={isOpen} onToggle={() => setIsOpen(!isOpen)} label="Ask Assistant" accentColor="#7b5cff" />
|
|
41
37
|
{isOpen && (
|
|
42
|
-
<ChatPanel
|
|
43
|
-
mode={mode}
|
|
44
|
-
onClose={() => setIsOpen(false)}
|
|
45
|
-
onModeChange={setMode}
|
|
46
|
-
apiBaseUrl="/api/assistant"
|
|
47
|
-
user={{ firstName: 'John' }}
|
|
48
|
-
/>
|
|
38
|
+
<ChatPanel mode={mode} onClose={() => setIsOpen(false)} onModeChange={setMode} apiBaseUrl="/api/assistant" user={{ firstName: 'John' }} />
|
|
49
39
|
)}
|
|
50
40
|
</>
|
|
51
41
|
);
|
|
@@ -64,23 +54,24 @@ import { ChatPanel } from '@filigran/chatbot';
|
|
|
64
54
|
|
|
65
55
|
#### Props
|
|
66
56
|
|
|
67
|
-
| Prop
|
|
68
|
-
|
|
69
|
-
| `mode`
|
|
70
|
-
| `onClose`
|
|
71
|
-
| `onModeChange`
|
|
72
|
-
| `apiBaseUrl`
|
|
73
|
-
| `user`
|
|
74
|
-
| `topOffset`
|
|
75
|
-
| `agentDashboardUrl` | `string`
|
|
76
|
-
| `t`
|
|
77
|
-
| `accentColor`
|
|
78
|
-
| `logoIcon`
|
|
79
|
-
| `promptSuggestions` | `string[]`
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
57
|
+
| Prop | Type | Default | Description |
|
|
58
|
+
| ------------------- | ----------------------------------------- | ------------ | ---------------------------------------------------------------- |
|
|
59
|
+
| `mode` | `'floating' \| 'sidebar' \| 'fullscreen'` | **required** | Display mode |
|
|
60
|
+
| `onClose` | `() => void` | **required** | Called when close button is clicked |
|
|
61
|
+
| `onModeChange` | `(mode: ChatMode) => void` | **required** | Called when user switches display mode |
|
|
62
|
+
| `apiBaseUrl` | `string` | **required** | Base URL for chat API endpoints |
|
|
63
|
+
| `user` | `{ firstName: string }` | **required** | Current user info |
|
|
64
|
+
| `topOffset` | `number` | `0` | Top offset in pixels (for sidebar/fullscreen with fixed headers) |
|
|
65
|
+
| `agentDashboardUrl` | `string` | β | URL for "Browse agents" / "Create agent" links |
|
|
66
|
+
| `t` | `(key: string) => string` | identity | Translation function for i18n |
|
|
67
|
+
| `accentColor` | `string` | `'#7b5cff'` | Primary accent color (hex) |
|
|
68
|
+
| `logoIcon` | `React.ReactNode` | default icon | Custom logo/icon for the assistant |
|
|
69
|
+
| `promptSuggestions` | `string[]` | default list | Prompt suggestions shown on welcome screen |
|
|
70
|
+
| `pageContext` | `Record<string, unknown>` | β | Arbitrary host page context (e.g. `{ url: '/dashboard/...' }`) sent as `context` on each `rest` message so the agent knows where the user is. Must be JSON-serializable (skipped if not). Read fresh at send time; omitted when empty. |
|
|
71
|
+
| `resizable` | `boolean` | `false` | Enable drag-to-resize for sidebar mode |
|
|
72
|
+
| `onWidthChange` | `(width: number) => void` | β | Called when sidebar width changes during resize |
|
|
73
|
+
| `onResizeStart` | `() => void` | β | Called when resize drag starts |
|
|
74
|
+
| `onResizeEnd` | `() => void` | β | Called when resize drag ends |
|
|
84
75
|
|
|
85
76
|
#### Resizable Sidebar Example
|
|
86
77
|
|
|
@@ -117,13 +108,13 @@ import { ChatToggleButton } from '@filigran/chatbot';
|
|
|
117
108
|
|
|
118
109
|
#### Props
|
|
119
110
|
|
|
120
|
-
| Prop
|
|
121
|
-
|
|
122
|
-
| `isOpen`
|
|
123
|
-
| `onToggle`
|
|
124
|
-
| `label`
|
|
125
|
-
| `accentColor` | `string`
|
|
126
|
-
| `icon`
|
|
111
|
+
| Prop | Type | Default | Description |
|
|
112
|
+
| ------------- | ----------------- | ------------ | ------------------------------ |
|
|
113
|
+
| `isOpen` | `boolean` | **required** | Whether the chat panel is open |
|
|
114
|
+
| `onToggle` | `() => void` | **required** | Called when button is clicked |
|
|
115
|
+
| `label` | `string` | `'Chat'` | Tooltip/aria label |
|
|
116
|
+
| `accentColor` | `string` | `'#7b5cff'` | Button background color |
|
|
117
|
+
| `icon` | `React.ReactNode` | default icon | Custom icon |
|
|
127
118
|
|
|
128
119
|
## API Contract
|
|
129
120
|
|
|
@@ -150,6 +141,7 @@ Returns available AI agents.
|
|
|
150
141
|
Restores conversation history.
|
|
151
142
|
|
|
152
143
|
**Request:**
|
|
144
|
+
|
|
153
145
|
```json
|
|
154
146
|
{
|
|
155
147
|
"conversation_id": "uuid-here",
|
|
@@ -158,6 +150,7 @@ Restores conversation history.
|
|
|
158
150
|
```
|
|
159
151
|
|
|
160
152
|
**Response:**
|
|
153
|
+
|
|
161
154
|
```json
|
|
162
155
|
{
|
|
163
156
|
"messages": [
|
|
@@ -167,19 +160,48 @@ Restores conversation history.
|
|
|
167
160
|
}
|
|
168
161
|
```
|
|
169
162
|
|
|
163
|
+
Assistant history messages **should echo the same `attachments[]` array** that
|
|
164
|
+
was sent on the original `done` event (see [Agent-generated file attachments](#agent-generated-file-attachments)),
|
|
165
|
+
keyed by `file_id`. The component re-surfaces the download cards on restore,
|
|
166
|
+
so omitting them means download cards silently disappear after a page reload
|
|
167
|
+
even though streaming downloads work:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"messages": [
|
|
172
|
+
{
|
|
173
|
+
"role": "assistant",
|
|
174
|
+
"content": "Here is your export. [[FILE:0f3a...]]",
|
|
175
|
+
"attachments": [
|
|
176
|
+
{ "file_id": "0f3a...", "filename": "iocs.csv", "type": "csv", "size": 2048, "content_type": "text/csv", "file_tag": "download_file" }
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
170
183
|
### `POST {apiBaseUrl}/chat/messages`
|
|
171
184
|
|
|
172
185
|
Sends a message and streams the response via SSE.
|
|
173
186
|
|
|
174
187
|
**Request:**
|
|
188
|
+
|
|
175
189
|
```json
|
|
176
190
|
{
|
|
177
191
|
"content": "What is the weather?",
|
|
178
192
|
"conversation_id": "uuid-or-null",
|
|
179
|
-
"agent_slug": "general"
|
|
193
|
+
"agent_slug": "general",
|
|
194
|
+
"context": { "url": "/dashboard/analyses/reports/<id>/overview" }
|
|
180
195
|
}
|
|
181
196
|
```
|
|
182
197
|
|
|
198
|
+
The optional `context` object is forwarded verbatim from the `pageContext`
|
|
199
|
+
prop (REST backend only) and is omitted entirely when empty. Use it to make
|
|
200
|
+
the agent aware of the user's current location/page; the shape is up to the
|
|
201
|
+
host and can be extended later (page title, selected entity, user role, etc.).
|
|
202
|
+
It must be JSON-serializable β a non-serializable value (circular reference,
|
|
203
|
+
`BigInt`, β¦) is skipped rather than breaking the request.
|
|
204
|
+
|
|
183
205
|
**Response:** Server-Sent Events stream with these event types:
|
|
184
206
|
|
|
185
207
|
```
|
|
@@ -192,7 +214,35 @@ data: {"type": "stream", "content": "today is sunny."}
|
|
|
192
214
|
data: {"type": "done", "content": "The weather today is sunny.", "conversation_id": "new-uuid", "tool_names": ["search_web"], "tool_call_count": 1, "iterations": 1}
|
|
193
215
|
```
|
|
194
216
|
|
|
217
|
+
#### Agent-generated file attachments
|
|
218
|
+
|
|
219
|
+
When an agent produces a downloadable file, the `done` event carries an `attachments` array and the streamed prose embeds `[[FILE:<file_id>]]` markers. The component strips those markers and renders a download card per attachment:
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"type": "done",
|
|
224
|
+
"content": "Here is your export. [[FILE:0f3a...]]",
|
|
225
|
+
"conversation_id": "uuid",
|
|
226
|
+
"attachments": [
|
|
227
|
+
{ "file_id": "0f3a...", "filename": "iocs.csv", "type": "csv", "size": 2048, "content_type": "text/csv", "file_tag": "download_file" }
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Each attachment carries only `file_id` + display metadata β **never an absolute download URL**. Clicking a card issues:
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
GET {apiBaseUrl}{apiEndpoints.download ?? '/chat/files'}/{file_id}/download
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
with `credentials: 'include'` and your `requestHeaders`. Point `apiEndpoints.download` at your **own backend proxy** so the download is authenticated by your platform (the proxy mints any upstream token server-side) β the user never authenticates to the upstream chat service directly. Set `apiEndpoints.download` to `null` to disable download cards.
|
|
239
|
+
|
|
240
|
+
The `/chat/files` default applies to REST-style endpoints. In `singleEndpoint` mode there is no per-path routing, so the default is **not** applied β download cards stay disabled unless you set `apiEndpoints.download` explicitly to a proxy route.
|
|
241
|
+
|
|
242
|
+
Download failures (403/404/5xx/network) are reported through the optional `onDownloadError(error, attachment)` callback so the host can surface them via its own notification system (the chatbot has no toast surface of its own).
|
|
243
|
+
|
|
195
244
|
**Status values:**
|
|
245
|
+
|
|
196
246
|
- `thinking` β Agent is processing
|
|
197
247
|
- `tool_start` β Agent is using tools (with `tools` array)
|
|
198
248
|
- `analyzing` β Agent is analyzing tool results
|
|
@@ -200,6 +250,7 @@ data: {"type": "done", "content": "The weather today is sunny.", "conversation_i
|
|
|
200
250
|
- `streaming` β Content is being streamed
|
|
201
251
|
|
|
202
252
|
**Error event:**
|
|
253
|
+
|
|
203
254
|
```
|
|
204
255
|
data: {"type": "error", "content": "Something went wrong"}
|
|
205
256
|
```
|
|
@@ -214,7 +265,7 @@ import { MyLogo } from './icons';
|
|
|
214
265
|
<ChatPanel
|
|
215
266
|
logoIcon={<MyLogo size={24} />}
|
|
216
267
|
// ...
|
|
217
|
-
|
|
268
|
+
/>;
|
|
218
269
|
```
|
|
219
270
|
|
|
220
271
|
### Custom Accent Color
|
|
@@ -230,11 +281,7 @@ import { MyLogo } from './icons';
|
|
|
230
281
|
|
|
231
282
|
```tsx
|
|
232
283
|
<ChatPanel
|
|
233
|
-
promptSuggestions={[
|
|
234
|
-
'Help me write a report',
|
|
235
|
-
'Analyze this data',
|
|
236
|
-
'Summarize recent activity',
|
|
237
|
-
]}
|
|
284
|
+
promptSuggestions={['Help me write a report', 'Analyze this data', 'Summarize recent activity']}
|
|
238
285
|
// ...
|
|
239
286
|
/>
|
|
240
287
|
```
|
|
@@ -246,7 +293,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
246
293
|
|
|
247
294
|
function App() {
|
|
248
295
|
const { t } = useTranslation();
|
|
249
|
-
|
|
296
|
+
|
|
250
297
|
return (
|
|
251
298
|
<ChatPanel
|
|
252
299
|
t={t}
|
|
@@ -257,6 +304,7 @@ function App() {
|
|
|
257
304
|
```
|
|
258
305
|
|
|
259
306
|
**Translation keys used:**
|
|
307
|
+
|
|
260
308
|
- `'Thinking...'`
|
|
261
309
|
- `'Using toolsβ¦'`
|
|
262
310
|
- `'Analyzing resultsβ¦'`
|
|
@@ -270,6 +318,7 @@ function App() {
|
|
|
270
318
|
- `'Browse agents'`
|
|
271
319
|
- `'Create agent'`
|
|
272
320
|
- `'Reasoning details'`
|
|
321
|
+
- `'Download'`
|
|
273
322
|
- `'tool call'` / `'tool calls'`
|
|
274
323
|
- `'Uses AI. Verify results.'`
|
|
275
324
|
- `'How can I help you, '`
|
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,20 @@ interface ApiEndpoints {
|
|
|
18
18
|
sessions?: string | null;
|
|
19
19
|
/** Path for uploading files. Default: '/chat/upload'. Set to null to disable file uploads. */
|
|
20
20
|
upload?: string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Base path for downloading agent-generated files. Default: '/chat/files'.
|
|
23
|
+
* The download URL is built as
|
|
24
|
+
* `${apiBaseUrl}${download}/${fileId}/download`, resolved against the
|
|
25
|
+
* host app's own backend proxy. This keeps the download authenticated by
|
|
26
|
+
* the host platform (e.g. OpenCTI / OpenAEV session) β the proxy mints
|
|
27
|
+
* any upstream token server-side, so the user never authenticates to the
|
|
28
|
+
* upstream chat service directly. Set to null to disable download chips.
|
|
29
|
+
*
|
|
30
|
+
* Exception: in `singleEndpoint` mode the `/chat/files` default is NOT
|
|
31
|
+
* applied (there is no per-path routing), so download cards stay disabled
|
|
32
|
+
* unless this path is set explicitly to a proxy route.
|
|
33
|
+
*/
|
|
34
|
+
download?: string | null;
|
|
21
35
|
}
|
|
22
36
|
interface ChatPanelProps {
|
|
23
37
|
mode: ChatMode;
|
|
@@ -51,12 +65,37 @@ interface ChatPanelProps {
|
|
|
51
65
|
disableFileManagement?: boolean;
|
|
52
66
|
/** Called when a relative markdown link is clicked in assistant messages. */
|
|
53
67
|
onRelativeLinkClick?: (href: string) => void;
|
|
68
|
+
/**
|
|
69
|
+
* Called when an agent-generated file download fails (non-2xx response or
|
|
70
|
+
* network error). Lets the host surface the failure through its own
|
|
71
|
+
* notification system (the chatbot has no toast surface of its own).
|
|
72
|
+
*/
|
|
73
|
+
onDownloadError?: (error: unknown, attachment: ChatAttachment) => void;
|
|
54
74
|
/** Maximum number of files attachable in one chat context. Default: 10. */
|
|
55
75
|
maxFileCount?: number;
|
|
56
76
|
/** Maximum total size in bytes for attached files. Default: 50 * 1024 * 1024 (50 MB). */
|
|
57
77
|
maxTotalSize?: number;
|
|
58
78
|
/** Additional HTTP headers added to chatbot API requests (messages, sessions, agents, uploads). */
|
|
59
79
|
requestHeaders?: Record<string, string>;
|
|
80
|
+
/**
|
|
81
|
+
* Arbitrary contextual metadata about the host page/application, forwarded
|
|
82
|
+
* to the backend alongside every message as a `context` JSON object so the
|
|
83
|
+
* agent is aware of where the user is.
|
|
84
|
+
*
|
|
85
|
+
* The shape is up to the host. Today it typically carries the current
|
|
86
|
+
* relative URL, e.g.
|
|
87
|
+
* `{ url: '/dashboard/analyses/reports/<id>/overview' }`, and can be
|
|
88
|
+
* extended later (page title, selected entity, user role, etc.).
|
|
89
|
+
*
|
|
90
|
+
* Must be JSON-serializable β it is sent via `JSON.stringify`. A
|
|
91
|
+
* non-serializable value (circular reference, `BigInt`, etc.) is skipped
|
|
92
|
+
* rather than thrown, so it can never break message sending.
|
|
93
|
+
*
|
|
94
|
+
* Read fresh at send time, so it always reflects the page the user is on
|
|
95
|
+
* when the message is sent (not when the panel was opened). Only emitted
|
|
96
|
+
* for the `rest` backend, and omitted entirely when empty.
|
|
97
|
+
*/
|
|
98
|
+
pageContext?: Record<string, unknown>;
|
|
60
99
|
/**
|
|
61
100
|
* CSS selector for the main content element that should be pushed when sidebar is open.
|
|
62
101
|
* When set, the component will automatically apply margin-right to push the content.
|
|
@@ -84,10 +123,34 @@ interface ChatMessage {
|
|
|
84
123
|
content: string;
|
|
85
124
|
timestamp: Date;
|
|
86
125
|
files?: ChatFile[];
|
|
126
|
+
/** Agent-generated downloadable files attached to an assistant message. */
|
|
127
|
+
attachments?: ChatAttachment[];
|
|
87
128
|
toolNames?: string[];
|
|
88
129
|
toolCallCount?: number;
|
|
89
130
|
iterations?: number;
|
|
90
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* An agent-generated file produced during a chat turn (via the backend
|
|
134
|
+
* `generate_file` tool / custom-tool `$output_files`). Rendered as a
|
|
135
|
+
* download card in assistant messages.
|
|
136
|
+
*
|
|
137
|
+
* Intentionally carries no absolute URL: the download is resolved against
|
|
138
|
+
* the host app's backend proxy from `fileId` (see `ApiEndpoints.download`)
|
|
139
|
+
* so the user stays authenticated to the host platform only.
|
|
140
|
+
*/
|
|
141
|
+
interface ChatAttachment {
|
|
142
|
+
fileId: string;
|
|
143
|
+
filename: string;
|
|
144
|
+
/** Short extension-style label surfaced under the filename (e.g. "PDF"). */
|
|
145
|
+
type?: string;
|
|
146
|
+
size?: number;
|
|
147
|
+
contentType?: string;
|
|
148
|
+
/**
|
|
149
|
+
* `download_file` β prominent download card (user deliverable).
|
|
150
|
+
* `working_file` β de-emphasized scratch/working artifact chip.
|
|
151
|
+
*/
|
|
152
|
+
fileTag?: 'download_file' | 'working_file';
|
|
153
|
+
}
|
|
91
154
|
interface ChatFile {
|
|
92
155
|
name: string;
|
|
93
156
|
type: string;
|
|
@@ -117,4 +180,4 @@ interface TransferredAgent {
|
|
|
117
180
|
}
|
|
118
181
|
|
|
119
182
|
export { ChatPanel, ChatToggleButton };
|
|
120
|
-
export type { ApiEndpoints, BackendType, ChatFile, ChatMessage, ChatMode, ChatPanelProps, ChatToggleButtonProps, TransferredAgent, XtmAgent };
|
|
183
|
+
export type { ApiEndpoints, BackendType, ChatAttachment, ChatFile, ChatMessage, ChatMode, ChatPanelProps, ChatToggleButtonProps, TransferredAgent, XtmAgent };
|