@filigran/chatbot 3.11.1 โ 3.12.0
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 +100 -7
- package/dist/index.d.ts +65 -6
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/markdown.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Filigran chat panel โ a standalone React + Tailwind chatbot component with SSE
|
|
|
13
13
|
- ๐ฅ **Agent-Generated Files** โ Renders downloadable file cards from agent output and strips the `[[FILE:id]]` markers from the prose
|
|
14
14
|
- ๐ **Full Markdown** โ Tables (mis-delimited ones repaired), code blocks with copy button, lists, blockquotes, soft line breaks, inline images with a lightbox
|
|
15
15
|
- ๐ผ๏ธ **Image Previews** โ `data:image/*` charts and image attachments render inline, click to expand
|
|
16
|
-
- ๐ **
|
|
16
|
+
- ๐ **Message Footer** โ Every message says when it was sent; copy any message, rate an answer ๐/๐ (stored by the backend or handed to the host, with an optional comment on a ๐) and have it read aloud โ see [Message feedback](#message-feedback)
|
|
17
17
|
- ๐งฐ **Composer Toolbar** โ Prompt library and quota indicator, both driven by whether the host serves the route; plus a slot for the host's own controls
|
|
18
18
|
- ๐ง **Context Gauge** โ Ring + percentage showing how full the model's context window is, so a long chat's silent summarising is visible before it happens
|
|
19
19
|
- ๐๏ธ **Dictation** โ Speech-to-text via the browser's own Web Speech API; no endpoint, no key, hidden where unsupported
|
|
@@ -83,7 +83,8 @@ import { ChatPanel } from '@filigran/chatbot';
|
|
|
83
83
|
| `onWidthChange` | `(width: number) => void` | โ | Called when sidebar width changes during resize |
|
|
84
84
|
| `onResizeStart` | `() => void` | โ | Called when resize drag starts |
|
|
85
85
|
| `onResizeEnd` | `() => void` | โ | Called when resize drag ends |
|
|
86
|
-
| `onMessageFeedback` | `(id, feedback, message) => void` | โ |
|
|
86
|
+
| `onMessageFeedback` | `(id, feedback, message) => void` | โ | Receives each rating of an answer (`null` clears it); `id` is the message's persisted id when the backend reported one (`done.message_id`, a restored entry's `id`), else the panel's own. On its own it enables ๐/๐ and the panel stores nothing; with `apiEndpoints.feedback` set it is called once the backend has stored the rating. With neither, no thumbs. |
|
|
87
|
+
| `locale` | `string` | browser's | BCP 47 language tag the message times are written in and answers are read aloud in (the platform's language, e.g. `'fr-FR'`) |
|
|
87
88
|
| `disableImagePreviews` | `boolean` | `false` | Render image attachments as download cards instead of inline previews |
|
|
88
89
|
| `contextUsageEnabled` | `boolean` | `true` | Show how full the model's context window is for the current conversation (ring + percentage in the composer toolbar). Data-driven, so it stays absent until the backend reports occupancy โ see [Context usage](#context-usage). |
|
|
89
90
|
| `composerToolbar` | `React.ReactNode` | โ | Extra controls appended to the composer toolbar. The escape hatch for host-specific affordances (XTM One's session-tool picker) โ the package never learns what they are. Pass nothing and the toolbar simply has none. |
|
|
@@ -175,12 +176,26 @@ Restores conversation history (and, implicitly, resolves the session).
|
|
|
175
176
|
{
|
|
176
177
|
"conversation_id": "uuid-here",
|
|
177
178
|
"messages": [
|
|
178
|
-
{ "role": "user", "content": "Hello" },
|
|
179
|
-
{
|
|
179
|
+
{ "id": "msg-1", "role": "user", "content": "Hello", "created_at": "2026-06-10T08:30:00Z" },
|
|
180
|
+
{
|
|
181
|
+
"id": "msg-2",
|
|
182
|
+
"role": "assistant",
|
|
183
|
+
"content": "Hi! How can I help?",
|
|
184
|
+
"created_at": "2026-06-10T08:30:04Z",
|
|
185
|
+
"feedback": { "rating": "positive", "comment": null }
|
|
186
|
+
}
|
|
180
187
|
]
|
|
181
188
|
}
|
|
182
189
|
```
|
|
183
190
|
|
|
191
|
+
`id`, `created_at` and `feedback` are optional. `id` is the message's
|
|
192
|
+
persisted id, what its rating is stored against (see
|
|
193
|
+
[Message feedback](#message-feedback));
|
|
194
|
+
`created_at` (ISO 8601) is when it was sent, shown in its footer - a message
|
|
195
|
+
without one shows no time. `feedback` is the caller's own rating of an
|
|
196
|
+
assistant answer: the thumbs are then locked on it ("Feedback already
|
|
197
|
+
submitted").
|
|
198
|
+
|
|
184
199
|
The response **should echo a `conversation_id`**. When the stored
|
|
185
200
|
`conversation_id` no longer exists (e.g. the platform was reset but the
|
|
186
201
|
browser kept an old id in `localStorage`), the backend is expected to
|
|
@@ -280,9 +295,13 @@ data: {"type": "status", "status": "analyzing"}
|
|
|
280
295
|
data: {"type": "status", "status": "streaming"}
|
|
281
296
|
data: {"type": "stream", "content": "The weather "}
|
|
282
297
|
data: {"type": "stream", "content": "today is sunny."}
|
|
283
|
-
data: {"type": "done", "content": "The weather today is sunny.", "conversation_id": "new-uuid", "tool_names": ["search_web"], "tool_call_count": 1, "iterations": 1, "reasoning": "Let me check the weather data first.", "tool_call_trace": [{"name": "search_web", "input": "{\"query\": \"weather\"}", "output": "Sunny, 24C", "success": true}], "transfer_chain": [{"agent_id": "uuid", "agent_name": "General"}], "is_truncated": false}
|
|
298
|
+
data: {"type": "done", "content": "The weather today is sunny.", "conversation_id": "new-uuid", "message_id": "msg-2", "tool_names": ["search_web"], "tool_call_count": 1, "iterations": 1, "reasoning": "Let me check the weather data first.", "tool_call_trace": [{"name": "search_web", "input": "{\"query\": \"weather\"}", "output": "Sunny, 24C", "success": true}], "transfer_chain": [{"agent_id": "uuid", "agent_name": "General"}], "is_truncated": false}
|
|
284
299
|
```
|
|
285
300
|
|
|
301
|
+
The optional `message_id` on `done` is the answer's persisted id: without it
|
|
302
|
+
(and without a restore naming the message) the answer cannot be rated through
|
|
303
|
+
`apiEndpoints.feedback`.
|
|
304
|
+
|
|
286
305
|
The optional `reasoning` field on `done` (and on restored session messages)
|
|
287
306
|
carries the accumulated model reasoning / pre-tool preamble prose for the
|
|
288
307
|
turn. When present it is surfaced in the per-message reasoning-details dialog
|
|
@@ -530,6 +549,58 @@ abandonment, never the person deciding.
|
|
|
530
549
|
|
|
531
550
|
REST backend only: `legacy` and `ag-ui` never meet this gate.
|
|
532
551
|
|
|
552
|
+
### Message feedback
|
|
553
|
+
|
|
554
|
+
Every message ends with a footer. An assistant answer (once it has finished
|
|
555
|
+
streaming) shows when it was sent, the "i" reasoning details, **Copy
|
|
556
|
+
response**, ๐ / ๐ and **Read aloud**; a user message shows **Copy message**
|
|
557
|
+
and its time, right-aligned. The time is written in the `locale` prop's
|
|
558
|
+
language (the time alone today, the date and time before; the full date in
|
|
559
|
+
the tooltip). Time and "i" are always visible; the other actions appear when
|
|
560
|
+
the message is hovered or focused, and always on a touch screen. A rating
|
|
561
|
+
given, or the answer being read, stays visible.
|
|
562
|
+
|
|
563
|
+
The thumbs render when `apiEndpoints.feedback` is set **or**
|
|
564
|
+
`onMessageFeedback` is passed. **Opt-in: `feedback` has no default path**
|
|
565
|
+
(the same choice as `approve`): a host names its route, or the panel stores
|
|
566
|
+
nothing.
|
|
567
|
+
|
|
568
|
+
```
|
|
569
|
+
POST {apiBaseUrl}{apiEndpoints.feedback}/{conversation_id}/messages/{message_id}/feedback
|
|
570
|
+
{ "rating": "positive" | "negative", "comment": string | null }
|
|
571
|
+
-> 200 { "rating": ..., "comment": ... }
|
|
572
|
+
DELETE {apiBaseUrl}{apiEndpoints.feedback}/{conversation_id}/messages/{message_id}/feedback
|
|
573
|
+
-> 204
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
Both are sent with `credentials: 'include'` and your `requestHeaders`
|
|
577
|
+
(XTM One: `feedback: '/chat/conversations'`). `message_id` is the answer's
|
|
578
|
+
persisted id โ the `done` event's `message_id`, or the restored entry's
|
|
579
|
+
`id` โ so an answer the backend did not identify shows no thumbs.
|
|
580
|
+
|
|
581
|
+
- A thumb is shown as given at once and rolled back, with "Could not submit
|
|
582
|
+
feedback." (or "Could not retract feedback." for a retraction) under the
|
|
583
|
+
message, when the backend refuses it or cannot be reached.
|
|
584
|
+
- Clicking the given thumb again retracts it (`DELETE`).
|
|
585
|
+
- Once a ๐ is stored the panel asks what went wrong: **Save** re-posts the
|
|
586
|
+
rating with the comment (trimmed, at most 2000 characters), **Skip** keeps
|
|
587
|
+
the rating without one.
|
|
588
|
+
- A rating restored with the conversation (`feedback` on a restored entry) is
|
|
589
|
+
final: only its thumb is shown, "Feedback already submitted".
|
|
590
|
+
- `onMessageFeedback`, when passed too, is called once the backend has
|
|
591
|
+
stored the rating. On its own it receives every rating and the panel
|
|
592
|
+
stores nothing (no comment step, since the callback has nowhere to put
|
|
593
|
+
one).
|
|
594
|
+
|
|
595
|
+
REST backend only, and not in `singleEndpoint` mode (there is no per-path
|
|
596
|
+
routing to name a feedback route with).
|
|
597
|
+
|
|
598
|
+
**Read aloud** uses the browser's own speech synthesis โ no endpoint, hidden
|
|
599
|
+
where unsupported, and absent on an answer with nothing to say (code only). It
|
|
600
|
+
reads the answer's text without its markdown or file markers, in the `locale`
|
|
601
|
+
prop's language, one answer at a time; switching conversation, sending a
|
|
602
|
+
message or leaving the panel stops it.
|
|
603
|
+
|
|
533
604
|
## Customization
|
|
534
605
|
|
|
535
606
|
### Custom Logo
|
|
@@ -685,16 +756,26 @@ Every key the package can ask for, grouped by where it appears:
|
|
|
685
756
|
- `'Copied'`
|
|
686
757
|
- `'Copied!'`
|
|
687
758
|
- `'Copy code'`
|
|
759
|
+
- `'Copy message'`
|
|
688
760
|
- `'Copy response'`
|
|
761
|
+
- `'Could not retract feedback.'`
|
|
762
|
+
- `'Could not submit feedback.'`
|
|
689
763
|
- `'Download'`
|
|
690
764
|
- `'Expand image'`
|
|
765
|
+
- `'Feedback already submitted'`
|
|
691
766
|
- `'Good response'`
|
|
692
767
|
- `'Image could not be loaded'`
|
|
693
768
|
- `'Image preview'`
|
|
694
769
|
- `'Load earlier messages'`
|
|
695
770
|
- `'Loading imageโฆ'`
|
|
771
|
+
- `'Read aloud'`
|
|
696
772
|
- `'Reasoning details'`
|
|
697
773
|
- `'Reasoning details โ turn limit reached'`
|
|
774
|
+
- `'Save'`
|
|
775
|
+
- `'Skip'`
|
|
776
|
+
- `'Stop reading'`
|
|
777
|
+
- `'Tell us what went wrong (optional)'`
|
|
778
|
+
- `'What could be improved?'`
|
|
698
779
|
|
|
699
780
|
**Agent status**
|
|
700
781
|
|
|
@@ -787,6 +868,10 @@ Every key the package can ask for, grouped by where it appears:
|
|
|
787
868
|
- `'Quota reached'`
|
|
788
869
|
- `'Usage'`
|
|
789
870
|
- `'Usage ยท {period}'`
|
|
871
|
+
- `'this month'`
|
|
872
|
+
- `'this year'`
|
|
873
|
+
- `'today'`
|
|
874
|
+
- `'{quota} (shared across all users)'`
|
|
790
875
|
|
|
791
876
|
**Notices, errors and timestamps**
|
|
792
877
|
|
|
@@ -823,12 +908,20 @@ backend cannot answer, and there is no mode flag to keep in step.
|
|
|
823
908
|
| Endpoint | Default path | Response |
|
|
824
909
|
| --- | --- | --- |
|
|
825
910
|
| Prompt library | `GET {apiBaseUrl}/chat/prompts` | `[{ id, title, content, description? }]` (or `{ prompts: [...] }`) |
|
|
826
|
-
| Quota status | `GET {apiBaseUrl}/chat/quota` | `{ used: number, limit: number \| null, period: string }` |
|
|
911
|
+
| Quota status | `GET {apiBaseUrl}/chat/quota` | `{ used: number, limit: number \| null, period: string, scope?: 'user' \| 'global' }` |
|
|
827
912
|
| Agent suggestions | `GET {apiBaseUrl}/chat/suggestions?agent_slug=<slug>` | `["..."]` (or `{ suggestions: [...] }`, or objects with `prompt`/`label`/`text`) |
|
|
828
913
|
|
|
829
914
|
Set either to `null` in `apiEndpoints` to hide it. `limit: null` means no
|
|
830
915
|
ceiling โ the indicator then shows consumption without a bar. The quota is
|
|
831
|
-
re-read whenever a turn finishes.
|
|
916
|
+
re-read whenever a turn finishes. The periods XTM One sets a quota for
|
|
917
|
+
(`daily`, `monthly`, `yearly`) are translated (`today`, `this month`,
|
|
918
|
+
`this year`), any other one is shown as sent; `scope: 'global'` says the
|
|
919
|
+
limit is shared across all users.
|
|
920
|
+
|
|
921
|
+
A host behind its own proxy points the routes at it, relative to its
|
|
922
|
+
`apiBaseUrl` โ OpenCTI and OpenAEV pass
|
|
923
|
+
`{ prompts: '/prompts', quota: '/quota', feedback: '/conversations' }`
|
|
924
|
+
(`feedback`: see [Message feedback](#message-feedback)).
|
|
832
925
|
|
|
833
926
|
The welcome screen names the selected agent and shows its own suggestions โ
|
|
834
927
|
which is also how switching agent is confirmed: the thread resets to that
|
package/dist/index.d.ts
CHANGED
|
@@ -125,6 +125,21 @@ interface ApiEndpoints {
|
|
|
125
125
|
* both today's generic suggestions and per-agent (later per-user) ones.
|
|
126
126
|
*/
|
|
127
127
|
suggestions?: string | null;
|
|
128
|
+
/**
|
|
129
|
+
* Base path of the persisted message feedback resource. A rating is sent as
|
|
130
|
+
* `POST {apiBaseUrl}{feedback}/{conversation_id}/messages/{message_id}/feedback`
|
|
131
|
+
* with `{ rating: 'positive' | 'negative', comment: string | null }` and
|
|
132
|
+
* retracted with a `DELETE` on the same URL. XTM One's platform router
|
|
133
|
+
* serves it at `/chat/conversations`.
|
|
134
|
+
*
|
|
135
|
+
* No default, for the same reason as {@link ApiEndpoints.approve}: a host
|
|
136
|
+
* proxying the chat names the route once it exposes it, and a panel upgraded
|
|
137
|
+
* before its host never rates into a 404. Unset, the thumbs still render
|
|
138
|
+
* when the host passes `onMessageFeedback`, as they always did.
|
|
139
|
+
*
|
|
140
|
+
* REST backend only, and not in `singleEndpoint` mode.
|
|
141
|
+
*/
|
|
142
|
+
feedback?: string | null;
|
|
128
143
|
}
|
|
129
144
|
/** A reusable prompt the user can insert into the composer. */
|
|
130
145
|
interface ChatPromptTemplate {
|
|
@@ -144,8 +159,18 @@ interface ChatQuotaStatus {
|
|
|
144
159
|
used: number;
|
|
145
160
|
/** null means unlimited โ the indicator then shows usage without a bar. */
|
|
146
161
|
limit: number | null;
|
|
147
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* The period the limit applies to: `daily`, `monthly` and `yearly` are
|
|
164
|
+
* translated ("today", "this month", "this year"), any other value is shown
|
|
165
|
+
* as sent.
|
|
166
|
+
*/
|
|
148
167
|
period: string;
|
|
168
|
+
/**
|
|
169
|
+
* Whose actions count against the limit: `global` is one allowance shared
|
|
170
|
+
* by every user of the platform, which the indicator says in its tooltip.
|
|
171
|
+
* Absent reads as the user's own.
|
|
172
|
+
*/
|
|
173
|
+
scope?: 'user' | 'global';
|
|
149
174
|
}
|
|
150
175
|
/**
|
|
151
176
|
* One tool call the agent wants to make and is waiting on a human to approve.
|
|
@@ -345,12 +370,22 @@ interface ChatPanelProps {
|
|
|
345
370
|
*/
|
|
346
371
|
onTaskComplete?: (title: string, body: string) => void;
|
|
347
372
|
/**
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
*
|
|
373
|
+
* Receives each rating of an assistant answer. `messageId` is the persisted
|
|
374
|
+
* id when the backend reported one (`serverId`), else the panel's own id;
|
|
375
|
+
* `feedback` is `null` when the user clears a previous rating.
|
|
376
|
+
*
|
|
377
|
+
* With {@link ApiEndpoints.feedback} set the panel stores the rating itself
|
|
378
|
+
* and calls this once the backend accepted it. Without it, passing this is
|
|
379
|
+
* what shows the ๐/๐ affordance: the host stores the rating, so a host with
|
|
380
|
+
* neither shows no buttons.
|
|
352
381
|
*/
|
|
353
382
|
onMessageFeedback?: (messageId: string, feedback: MessageFeedback | null, message: ChatMessage) => void;
|
|
383
|
+
/**
|
|
384
|
+
* The user's language as a BCP 47 tag (`'fr'`, `'en-US'`): formats the time
|
|
385
|
+
* under each message and is the language answers are read aloud in. Default:
|
|
386
|
+
* the browser's own.
|
|
387
|
+
*/
|
|
388
|
+
locale?: string;
|
|
354
389
|
/**
|
|
355
390
|
* Disable the inline preview of image attachments (they render as ordinary
|
|
356
391
|
* download cards instead). Previews fetch the image through the host download
|
|
@@ -391,10 +426,29 @@ interface ChatToggleButtonProps {
|
|
|
391
426
|
icon?: React.ReactNode;
|
|
392
427
|
}
|
|
393
428
|
interface ChatMessage {
|
|
429
|
+
/** The panel's own key for the row; stable for the life of the row. */
|
|
394
430
|
id: string;
|
|
431
|
+
/**
|
|
432
|
+
* The backend's id for the message: the `done` event's `message_id` for a
|
|
433
|
+
* live answer, the entry's `id` for a restored one. What feedback is stored
|
|
434
|
+
* against; absent until the backend reports it.
|
|
435
|
+
*/
|
|
436
|
+
serverId?: string;
|
|
395
437
|
role: 'user' | 'assistant';
|
|
396
438
|
content: string;
|
|
439
|
+
/** When the message was sent: the entry's `created_at` for a restored one. */
|
|
397
440
|
timestamp: Date;
|
|
441
|
+
/**
|
|
442
|
+
* True for a restored message whose backend did not say when it was sent:
|
|
443
|
+
* `timestamp` is then the time of the restore, and the footer shows no time
|
|
444
|
+
* rather than that one.
|
|
445
|
+
*/
|
|
446
|
+
timestampUnknown?: boolean;
|
|
447
|
+
/**
|
|
448
|
+
* The rating the user gave this answer before the conversation was restored.
|
|
449
|
+
* The panel locks the thumbs on it ("Feedback already submitted").
|
|
450
|
+
*/
|
|
451
|
+
feedback?: ChatMessagePersistedFeedback;
|
|
398
452
|
/**
|
|
399
453
|
* The agent that produced *this* message, when the backend says so.
|
|
400
454
|
*
|
|
@@ -433,6 +487,11 @@ interface ChatMessage {
|
|
|
433
487
|
*/
|
|
434
488
|
isTruncated?: boolean;
|
|
435
489
|
}
|
|
490
|
+
/** A rating as the feedback endpoint stores it. */
|
|
491
|
+
interface ChatMessagePersistedFeedback {
|
|
492
|
+
rating: 'positive' | 'negative';
|
|
493
|
+
comment: string | null;
|
|
494
|
+
}
|
|
436
495
|
/** One tool call in the reasoning-details execution trace. */
|
|
437
496
|
interface ToolCallTraceEntry {
|
|
438
497
|
name: string;
|
|
@@ -516,4 +575,4 @@ interface TransferredAgent {
|
|
|
516
575
|
}
|
|
517
576
|
|
|
518
577
|
export { ChatPanel, ChatToggleButton };
|
|
519
|
-
export type { ApiEndpoints, BackendType, ChatAttachment, ChatContextBreakdown, ChatContextUsage, ChatConversationSummary, ChatFile, ChatMessage, ChatMode, ChatPanelProps, ChatPromptTemplate, ChatQuotaStatus, ChatToggleButtonProps, MessageFeedback, ToolApprovalDecision, ToolApprovalProposal, ToolApprovalVerdict, TransferredAgent, Translate, XtmAgent };
|
|
578
|
+
export type { ApiEndpoints, BackendType, ChatAttachment, ChatContextBreakdown, ChatContextUsage, ChatConversationSummary, ChatFile, ChatMessage, ChatMessagePersistedFeedback, ChatMode, ChatPanelProps, ChatPromptTemplate, ChatQuotaStatus, ChatToggleButtonProps, MessageFeedback, ToolApprovalDecision, ToolApprovalProposal, ToolApprovalVerdict, TransferredAgent, Translate, XtmAgent };
|