@assistant-ui/react 0.12.19 → 0.12.21
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 +1 -1
- package/dist/client/ExternalThread.d.ts +24 -3
- package/dist/client/ExternalThread.d.ts.map +1 -1
- package/dist/client/ExternalThread.js +106 -27
- package/dist/client/ExternalThread.js.map +1 -1
- package/dist/client/InMemoryThreadList.js +23 -30
- package/dist/client/InMemoryThreadList.js.map +1 -1
- package/dist/client/SingleThreadList.d.ts +12 -0
- package/dist/client/SingleThreadList.d.ts.map +1 -0
- package/dist/client/SingleThreadList.js +68 -0
- package/dist/client/SingleThreadList.js.map +1 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +37 -7
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerQueue.d.ts +2 -0
- package/dist/primitives/composer/ComposerQueue.d.ts.map +1 -0
- package/dist/primitives/composer/ComposerQueue.js +3 -0
- package/dist/primitives/composer/ComposerQueue.js.map +1 -0
- package/dist/primitives/composer/ComposerSend.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerSend.js +3 -1
- package/dist/primitives/composer/ComposerSend.js.map +1 -1
- package/dist/primitives/composer/mention/ComposerMentionBack.d.ts +21 -0
- package/dist/primitives/composer/mention/ComposerMentionBack.d.ts.map +1 -0
- package/dist/primitives/composer/mention/ComposerMentionBack.js +28 -0
- package/dist/primitives/composer/mention/ComposerMentionBack.js.map +1 -0
- package/dist/primitives/composer/mention/ComposerMentionCategories.d.ts +42 -0
- package/dist/primitives/composer/mention/ComposerMentionCategories.d.ts.map +1 -0
- package/dist/primitives/composer/mention/ComposerMentionCategories.js +32 -0
- package/dist/primitives/composer/mention/ComposerMentionCategories.js.map +1 -0
- package/dist/primitives/composer/mention/ComposerMentionContext.d.ts +23 -0
- package/dist/primitives/composer/mention/ComposerMentionContext.d.ts.map +1 -0
- package/dist/primitives/composer/mention/ComposerMentionContext.js +66 -0
- package/dist/primitives/composer/mention/ComposerMentionContext.js.map +1 -0
- package/dist/primitives/composer/mention/ComposerMentionItems.d.ts +46 -0
- package/dist/primitives/composer/mention/ComposerMentionItems.d.ts.map +1 -0
- package/dist/primitives/composer/mention/ComposerMentionItems.js +30 -0
- package/dist/primitives/composer/mention/ComposerMentionItems.js.map +1 -0
- package/dist/primitives/composer/mention/ComposerMentionPopover.d.ts +24 -0
- package/dist/primitives/composer/mention/ComposerMentionPopover.d.ts.map +1 -0
- package/dist/primitives/composer/mention/ComposerMentionPopover.js +28 -0
- package/dist/primitives/composer/mention/ComposerMentionPopover.js.map +1 -0
- package/dist/primitives/composer/mention/MentionResource.d.ts +39 -0
- package/dist/primitives/composer/mention/MentionResource.d.ts.map +1 -0
- package/dist/primitives/composer/mention/MentionResource.js +230 -0
- package/dist/primitives/composer/mention/MentionResource.js.map +1 -0
- package/dist/primitives/composer/mention/detectMentionTrigger.d.ts +2 -0
- package/dist/primitives/composer/mention/detectMentionTrigger.d.ts.map +1 -0
- package/dist/primitives/composer/mention/detectMentionTrigger.js +26 -0
- package/dist/primitives/composer/mention/detectMentionTrigger.js.map +1 -0
- package/dist/primitives/composer/mention/index.d.ts +6 -0
- package/dist/primitives/composer/mention/index.d.ts.map +1 -0
- package/dist/primitives/composer/mention/index.js +6 -0
- package/dist/primitives/composer/mention/index.js.map +1 -0
- package/dist/primitives/composer.d.ts +10 -0
- package/dist/primitives/composer.d.ts.map +1 -1
- package/dist/primitives/composer.js +10 -0
- package/dist/primitives/composer.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemRemove.d.ts +19 -0
- package/dist/primitives/queueItem/QueueItemRemove.d.ts.map +1 -0
- package/dist/primitives/queueItem/QueueItemRemove.js +21 -0
- package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -0
- package/dist/primitives/queueItem/QueueItemSteer.d.ts +19 -0
- package/dist/primitives/queueItem/QueueItemSteer.d.ts.map +1 -0
- package/dist/primitives/queueItem/QueueItemSteer.js +21 -0
- package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -0
- package/dist/primitives/queueItem/QueueItemText.d.ts +18 -0
- package/dist/primitives/queueItem/QueueItemText.d.ts.map +1 -0
- package/dist/primitives/queueItem/QueueItemText.js +19 -0
- package/dist/primitives/queueItem/QueueItemText.js.map +1 -0
- package/dist/primitives/queueItem.d.ts +4 -0
- package/dist/primitives/queueItem.d.ts.map +1 -0
- package/dist/primitives/queueItem.js +4 -0
- package/dist/primitives/queueItem.js.map +1 -0
- package/dist/unstable/useToolMentionAdapter.d.ts +42 -0
- package/dist/unstable/useToolMentionAdapter.d.ts.map +1 -0
- package/dist/unstable/useToolMentionAdapter.js +65 -0
- package/dist/unstable/useToolMentionAdapter.js.map +1 -0
- package/package.json +10 -10
- package/src/client/ExternalThread.ts +160 -32
- package/src/client/InMemoryThreadList.ts +24 -35
- package/src/client/SingleThreadList.ts +95 -0
- package/src/index.ts +26 -0
- package/src/primitives/composer/ComposerInput.tsx +49 -5
- package/src/primitives/composer/ComposerQueue.tsx +3 -0
- package/src/primitives/composer/ComposerSend.ts +3 -1
- package/src/primitives/composer/mention/ComposerMentionBack.tsx +55 -0
- package/src/primitives/composer/mention/ComposerMentionCategories.tsx +104 -0
- package/src/primitives/composer/mention/ComposerMentionContext.tsx +141 -0
- package/src/primitives/composer/mention/ComposerMentionItems.tsx +104 -0
- package/src/primitives/composer/mention/ComposerMentionPopover.tsx +52 -0
- package/src/primitives/composer/mention/MentionResource.ts +328 -0
- package/src/primitives/composer/mention/detectMentionTrigger.test.ts +78 -0
- package/src/primitives/composer/mention/detectMentionTrigger.ts +37 -0
- package/src/primitives/composer/mention/index.ts +16 -0
- package/src/primitives/composer.ts +10 -0
- package/src/primitives/queueItem/QueueItemRemove.ts +37 -0
- package/src/primitives/queueItem/QueueItemSteer.ts +37 -0
- package/src/primitives/queueItem/QueueItemText.tsx +37 -0
- package/src/primitives/queueItem.ts +3 -0
- package/src/tests/BaseComposerRuntimeCore.test.ts +3 -1
- package/src/unstable/useToolMentionAdapter.ts +114 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { QueueItemPrimitiveText as Text } from "./queueItem/QueueItemText.js";
|
|
2
|
+
export { QueueItemPrimitiveSteer as Steer } from "./queueItem/QueueItemSteer.js";
|
|
3
|
+
export { QueueItemPrimitiveRemove as Remove } from "./queueItem/QueueItemRemove.js";
|
|
4
|
+
//# sourceMappingURL=queueItem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queueItem.js","sourceRoot":"","sources":["../../src/primitives/queueItem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,IAAI,IAAI,EAAE,qCAAkC;AAC3E,OAAO,EAAE,uBAAuB,IAAI,KAAK,EAAE,sCAAmC;AAC9E,OAAO,EAAE,wBAAwB,IAAI,MAAM,EAAE,uCAAoC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Unstable_MentionAdapter, Unstable_MentionItem } from "@assistant-ui/core";
|
|
2
|
+
export type Unstable_ToolMentionAdapterOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* Explicit list of tools to show in the mention picker.
|
|
5
|
+
* If provided, model context tools are NOT included unless
|
|
6
|
+
* `includeModelContextTools` is true.
|
|
7
|
+
*/
|
|
8
|
+
tools?: readonly Unstable_MentionItem[] | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Include tools from the model context (registered via useAssistantTool).
|
|
11
|
+
* Defaults to true when `tools` is not provided, false otherwise.
|
|
12
|
+
*/
|
|
13
|
+
includeModelContextTools?: boolean | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Custom function to format the display label for a tool.
|
|
16
|
+
* Receives the tool name (id) and returns the display label.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* formatLabel: (name) => name.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase())
|
|
21
|
+
* // "get_current_weather" → "Get Current Weather"
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
formatLabel?: ((toolName: string) => string) | undefined;
|
|
25
|
+
/** Custom label for the tools category. @default "Tools" */
|
|
26
|
+
categoryLabel?: string | undefined;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* @deprecated This API is still under active development and might change without notice.
|
|
30
|
+
*
|
|
31
|
+
* Creates a MentionAdapter for tools. When a user types `@`, they see
|
|
32
|
+
* available tools and can mention them to hint the LLM to use a specific tool.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* const mentionAdapter = unstable_useToolMentionAdapter({
|
|
37
|
+
* formatLabel: (name) => name.replaceAll("_", " "),
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function unstable_useToolMentionAdapter(options?: Unstable_ToolMentionAdapterOptions): Unstable_MentionAdapter;
|
|
42
|
+
//# sourceMappingURL=useToolMentionAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useToolMentionAdapter.d.ts","sourceRoot":"","sources":["../../src/unstable/useToolMentionAdapter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,uBAAuB,EAEvB,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,kCAAkC,GAAG;IAC/C;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,oBAAoB,EAAE,GAAG,SAAS,CAAC;IAEpD;;;OAGG;IACH,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAE/C;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;IAEzD,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,CAAC,EAAE,kCAAkC,GAC3C,uBAAuB,CA0DzB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { useAui } from "@assistant-ui/store";
|
|
4
|
+
/**
|
|
5
|
+
* @deprecated This API is still under active development and might change without notice.
|
|
6
|
+
*
|
|
7
|
+
* Creates a MentionAdapter for tools. When a user types `@`, they see
|
|
8
|
+
* available tools and can mention them to hint the LLM to use a specific tool.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const mentionAdapter = unstable_useToolMentionAdapter({
|
|
13
|
+
* formatLabel: (name) => name.replaceAll("_", " "),
|
|
14
|
+
* });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function unstable_useToolMentionAdapter(options) {
|
|
18
|
+
const aui = useAui();
|
|
19
|
+
const explicitTools = options?.tools;
|
|
20
|
+
const includeModelContext = options?.includeModelContextTools ?? !explicitTools;
|
|
21
|
+
const formatLabel = options?.formatLabel;
|
|
22
|
+
const categoryLabel = options?.categoryLabel;
|
|
23
|
+
return useMemo(() => {
|
|
24
|
+
const getTools = () => {
|
|
25
|
+
const items = [];
|
|
26
|
+
if (explicitTools) {
|
|
27
|
+
items.push(...explicitTools);
|
|
28
|
+
}
|
|
29
|
+
if (includeModelContext) {
|
|
30
|
+
const context = aui.thread().getModelContext();
|
|
31
|
+
const tools = context.tools;
|
|
32
|
+
if (tools) {
|
|
33
|
+
for (const [name, tool] of Object.entries(tools)) {
|
|
34
|
+
if (!items.some((i) => i.id === name)) {
|
|
35
|
+
items.push({
|
|
36
|
+
id: name,
|
|
37
|
+
type: "tool",
|
|
38
|
+
label: formatLabel ? formatLabel(name) : name,
|
|
39
|
+
description: tool.description ?? undefined,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return items;
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
categories() {
|
|
49
|
+
return [
|
|
50
|
+
{ id: "tools", label: categoryLabel ?? "Tools", icon: undefined },
|
|
51
|
+
];
|
|
52
|
+
},
|
|
53
|
+
categoryItems(_categoryId) {
|
|
54
|
+
return getTools();
|
|
55
|
+
},
|
|
56
|
+
search(query) {
|
|
57
|
+
const lower = query.toLowerCase();
|
|
58
|
+
return getTools().filter((item) => item.id.toLowerCase().includes(lower) ||
|
|
59
|
+
item.label.toLowerCase().includes(lower) ||
|
|
60
|
+
item.description?.toLowerCase().includes(lower));
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}, [aui, explicitTools, includeModelContext, formatLabel, categoryLabel]);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=useToolMentionAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useToolMentionAdapter.js","sourceRoot":"","sources":["../../src/unstable/useToolMentionAdapter.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAqC7C;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,8BAA8B,CAC5C,OAA4C;IAE5C,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,aAAa,GAAG,OAAO,EAAE,KAAK,CAAC;IACrC,MAAM,mBAAmB,GACvB,OAAO,EAAE,wBAAwB,IAAI,CAAC,aAAa,CAAC;IACtD,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,CAAC;IACzC,MAAM,aAAa,GAAG,OAAO,EAAE,aAAa,CAAC;IAE7C,OAAO,OAAO,CAA0B,GAAG,EAAE;QAC3C,MAAM,QAAQ,GAAG,GAA2B,EAAE;YAC5C,MAAM,KAAK,GAA2B,EAAE,CAAC;YAEzC,IAAI,aAAa,EAAE,CAAC;gBAClB,KAAK,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,CAAC;YAC/B,CAAC;YAED,IAAI,mBAAmB,EAAE,CAAC;gBACxB,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,eAAe,EAAE,CAAC;gBAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;gBAC5B,IAAI,KAAK,EAAE,CAAC;oBACV,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;wBACjD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;4BACtC,KAAK,CAAC,IAAI,CAAC;gCACT,EAAE,EAAE,IAAI;gCACR,IAAI,EAAE,MAAM;gCACZ,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;gCAC7C,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,SAAS;6BAC3C,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;QAEF,OAAO;YACL,UAAU;gBACR,OAAO;oBACL,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,IAAI,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;iBAClE,CAAC;YACJ,CAAC;YAED,aAAa,CAAC,WAAmB;gBAC/B,OAAO,QAAQ,EAAE,CAAC;YACpB,CAAC;YAED,MAAM,CAAC,KAAa;gBAClB,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;gBAClC,OAAO,QAAQ,EAAE,CAAC,MAAM,CACtB,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;oBACrC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;oBACxC,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAClD,CAAC;YACJ,CAAC;SACF,CAAC;IACJ,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC;AAC5E,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.21",
|
|
4
4
|
"description": "TypeScript/React library for AI Chat",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"radix-ui",
|
|
@@ -49,22 +49,22 @@
|
|
|
49
49
|
],
|
|
50
50
|
"sideEffects": false,
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@assistant-ui/core": "^0.1.
|
|
53
|
-
"@assistant-ui/store": "^0.2.
|
|
54
|
-
"@assistant-ui/tap": "^0.5.
|
|
52
|
+
"@assistant-ui/core": "^0.1.9",
|
|
53
|
+
"@assistant-ui/store": "^0.2.5",
|
|
54
|
+
"@assistant-ui/tap": "^0.5.5",
|
|
55
55
|
"@radix-ui/primitive": "^1.1.3",
|
|
56
56
|
"@radix-ui/react-compose-refs": "^1.1.2",
|
|
57
57
|
"@radix-ui/react-context": "^1.1.3",
|
|
58
58
|
"@radix-ui/react-primitive": "^2.1.4",
|
|
59
59
|
"@radix-ui/react-use-callback-ref": "^1.1.1",
|
|
60
60
|
"@radix-ui/react-use-escape-keydown": "^1.1.1",
|
|
61
|
-
"assistant-cloud": "^0.1.
|
|
62
|
-
"assistant-stream": "^0.3.
|
|
63
|
-
"nanoid": "^5.1.
|
|
61
|
+
"assistant-cloud": "^0.1.23",
|
|
62
|
+
"assistant-stream": "^0.3.8",
|
|
63
|
+
"nanoid": "^5.1.7",
|
|
64
64
|
"radix-ui": "^1.4.3",
|
|
65
65
|
"react-textarea-autosize": "^8.5.9",
|
|
66
66
|
"zod": "^4.3.6",
|
|
67
|
-
"zustand": "^5.0.
|
|
67
|
+
"zustand": "^5.0.12"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"@types/react": "*",
|
|
@@ -86,10 +86,10 @@
|
|
|
86
86
|
"@types/node": "^25.5.0",
|
|
87
87
|
"@types/react": "^19.2.14",
|
|
88
88
|
"@types/react-dom": "^19.2.3",
|
|
89
|
-
"jsdom": "^
|
|
89
|
+
"jsdom": "^29.0.1",
|
|
90
90
|
"react": "^19.2.4",
|
|
91
91
|
"react-dom": "^19.2.4",
|
|
92
|
-
"vitest": "^4.1.
|
|
92
|
+
"vitest": "^4.1.1",
|
|
93
93
|
"@assistant-ui/x-buildutils": "0.0.3"
|
|
94
94
|
},
|
|
95
95
|
"publishConfig": {
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
tapEffectEvent,
|
|
7
7
|
} from "@assistant-ui/tap";
|
|
8
8
|
import {
|
|
9
|
+
type ClientElement,
|
|
9
10
|
type ClientOutput,
|
|
10
11
|
tapClientLookup,
|
|
11
12
|
attachTransformScopes,
|
|
@@ -14,34 +15,60 @@ import {
|
|
|
14
15
|
} from "@assistant-ui/store";
|
|
15
16
|
import { withKey } from "@assistant-ui/tap";
|
|
16
17
|
import type {
|
|
18
|
+
AppendMessage,
|
|
17
19
|
Attachment,
|
|
18
20
|
CreateAttachment,
|
|
19
21
|
ThreadAssistantMessagePart,
|
|
20
22
|
ThreadUserMessagePart,
|
|
21
23
|
ThreadMessage,
|
|
22
24
|
} from "@assistant-ui/core";
|
|
25
|
+
import type { QueueItemState } from "@assistant-ui/core/store";
|
|
26
|
+
import type { ComposerSendOptions } from "@assistant-ui/core/store";
|
|
23
27
|
import { ModelContext, Suggestions } from "@assistant-ui/core/store";
|
|
24
28
|
import { Tools, DataRenderers } from "@assistant-ui/core/react";
|
|
29
|
+
import { SingleThreadList } from "./SingleThreadList";
|
|
30
|
+
|
|
31
|
+
const EMPTY_QUEUE_ITEMS: readonly QueueItemState[] = [];
|
|
25
32
|
|
|
26
33
|
export type ExternalThreadMessage = ThreadMessage & {
|
|
27
34
|
id: string;
|
|
28
35
|
};
|
|
29
36
|
|
|
37
|
+
export type ExternalThreadQueueAdapter = {
|
|
38
|
+
/** The current queue items. */
|
|
39
|
+
items: readonly QueueItemState[];
|
|
40
|
+
/** Called when a message is submitted via the composer. Receives the steer preference. */
|
|
41
|
+
enqueue: (message: AppendMessage, opts: { steer: boolean }) => void;
|
|
42
|
+
/** Called to promote an existing queue item (cancel current run, run this immediately). */
|
|
43
|
+
steer: (queueItemId: string) => void;
|
|
44
|
+
/** Called to remove an item from the queue. */
|
|
45
|
+
remove: (queueItemId: string) => void;
|
|
46
|
+
/** Called to clear all pending queue items, with the reason for clearing. */
|
|
47
|
+
clear: (reason: "edit" | "reload" | "cancel-run") => void;
|
|
48
|
+
};
|
|
49
|
+
|
|
30
50
|
export type ExternalThreadProps = {
|
|
31
51
|
messages: readonly ExternalThreadMessage[];
|
|
32
52
|
isRunning?: boolean;
|
|
33
|
-
|
|
34
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Callback for new messages (non-queue runtimes).
|
|
55
|
+
* @note Unused when `queue` is provided — new messages are routed through `queue.enqueue` instead.
|
|
56
|
+
*/
|
|
57
|
+
onNew?: (message: AppendMessage) => void;
|
|
58
|
+
onEdit?: (message: AppendMessage) => void;
|
|
35
59
|
onReload?: (parentId: string | null) => void;
|
|
36
60
|
onStartRun?: () => void;
|
|
37
61
|
onCancel?: () => void;
|
|
62
|
+
/** Queue adapter for runtimes that support message queuing and steering. */
|
|
63
|
+
queue?: ExternalThreadQueueAdapter;
|
|
38
64
|
};
|
|
39
65
|
|
|
40
66
|
type MessageClientProps = {
|
|
41
67
|
message: ExternalThreadMessage;
|
|
42
68
|
index: number;
|
|
43
|
-
onEdit?: (message:
|
|
69
|
+
onEdit?: (message: AppendMessage) => void;
|
|
44
70
|
onReload?: () => void;
|
|
71
|
+
queue?: ExternalThreadQueueAdapter | undefined;
|
|
45
72
|
};
|
|
46
73
|
|
|
47
74
|
// Message Client - minimal implementation
|
|
@@ -51,6 +78,7 @@ const MessageClient = resource(
|
|
|
51
78
|
index,
|
|
52
79
|
onEdit,
|
|
53
80
|
onReload,
|
|
81
|
+
queue,
|
|
54
82
|
}: MessageClientProps): ClientOutput<"message"> => {
|
|
55
83
|
const [isCopied, setIsCopied] = tapState(false);
|
|
56
84
|
const [isHovering, setIsHovering] = tapState(false);
|
|
@@ -86,7 +114,8 @@ const MessageClient = resource(
|
|
|
86
114
|
setIsEditing(false);
|
|
87
115
|
};
|
|
88
116
|
|
|
89
|
-
const handleSendEdit = (msg:
|
|
117
|
+
const handleSendEdit = (msg: AppendMessage) => {
|
|
118
|
+
queue?.clear("edit");
|
|
90
119
|
onEdit?.({
|
|
91
120
|
...msg,
|
|
92
121
|
parentId: message.id,
|
|
@@ -104,6 +133,7 @@ const MessageClient = resource(
|
|
|
104
133
|
onBeginEdit: handleBeginEdit,
|
|
105
134
|
onSend: handleSendEdit,
|
|
106
135
|
message,
|
|
136
|
+
queue,
|
|
107
137
|
}),
|
|
108
138
|
);
|
|
109
139
|
|
|
@@ -214,10 +244,29 @@ type ComposerClientResourceProps = {
|
|
|
214
244
|
canCancel: boolean;
|
|
215
245
|
onCancel: () => void;
|
|
216
246
|
onBeginEdit?: () => void;
|
|
217
|
-
onSend?: (message:
|
|
247
|
+
onSend?: (message: AppendMessage) => void;
|
|
218
248
|
message?: ExternalThreadMessage;
|
|
249
|
+
queue?: ExternalThreadQueueAdapter | undefined;
|
|
219
250
|
};
|
|
220
251
|
|
|
252
|
+
const QueueItemClient = resource(
|
|
253
|
+
({
|
|
254
|
+
item,
|
|
255
|
+
onSteer,
|
|
256
|
+
onRemove,
|
|
257
|
+
}: {
|
|
258
|
+
item: QueueItemState;
|
|
259
|
+
onSteer: () => void;
|
|
260
|
+
onRemove: () => void;
|
|
261
|
+
}): ClientOutput<"queueItem"> => {
|
|
262
|
+
return {
|
|
263
|
+
getState: () => item,
|
|
264
|
+
steer: onSteer,
|
|
265
|
+
remove: onRemove,
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
);
|
|
269
|
+
|
|
221
270
|
// Composer Client - minimal implementation
|
|
222
271
|
const ComposerClientResource = resource(
|
|
223
272
|
({
|
|
@@ -228,6 +277,7 @@ const ComposerClientResource = resource(
|
|
|
228
277
|
onBeginEdit,
|
|
229
278
|
onSend,
|
|
230
279
|
message,
|
|
280
|
+
queue,
|
|
231
281
|
}: ComposerClientResourceProps): ClientOutput<"composer"> => {
|
|
232
282
|
const [text, setText] = tapState("");
|
|
233
283
|
const [role, setRole] = tapState<"user" | "assistant" | "system">("user");
|
|
@@ -276,6 +326,22 @@ const ComposerClientResource = resource(
|
|
|
276
326
|
[attachments],
|
|
277
327
|
);
|
|
278
328
|
|
|
329
|
+
const queueItems = queue?.items ?? EMPTY_QUEUE_ITEMS;
|
|
330
|
+
const queueItemClients = tapClientLookup(
|
|
331
|
+
() =>
|
|
332
|
+
queueItems.map((item) =>
|
|
333
|
+
withKey(
|
|
334
|
+
item.id,
|
|
335
|
+
QueueItemClient({
|
|
336
|
+
item,
|
|
337
|
+
onSteer: () => queue?.steer(item.id),
|
|
338
|
+
onRemove: () => queue?.remove(item.id),
|
|
339
|
+
}),
|
|
340
|
+
),
|
|
341
|
+
),
|
|
342
|
+
[queueItems],
|
|
343
|
+
);
|
|
344
|
+
|
|
279
345
|
const state = tapMemo(
|
|
280
346
|
() => ({
|
|
281
347
|
text,
|
|
@@ -289,6 +355,7 @@ const ComposerClientResource = resource(
|
|
|
289
355
|
type,
|
|
290
356
|
dictation: undefined,
|
|
291
357
|
quote,
|
|
358
|
+
queue: queueItems,
|
|
292
359
|
}),
|
|
293
360
|
[
|
|
294
361
|
text,
|
|
@@ -300,6 +367,7 @@ const ComposerClientResource = resource(
|
|
|
300
367
|
type,
|
|
301
368
|
attachments.length,
|
|
302
369
|
quote,
|
|
370
|
+
queueItems,
|
|
303
371
|
],
|
|
304
372
|
);
|
|
305
373
|
|
|
@@ -348,19 +416,25 @@ const ComposerClientResource = resource(
|
|
|
348
416
|
setAttachments([]);
|
|
349
417
|
setQuote(undefined);
|
|
350
418
|
},
|
|
351
|
-
send: () => {
|
|
419
|
+
send: (opts?: ComposerSendOptions) => {
|
|
352
420
|
const currentQuote = quote;
|
|
353
|
-
const
|
|
421
|
+
const composedMessage: AppendMessage = {
|
|
354
422
|
role,
|
|
355
423
|
content: text ? [{ type: "text" as const, text }] : [],
|
|
356
424
|
attachments: attachments as any,
|
|
357
425
|
createdAt: new Date(),
|
|
426
|
+
parentId: null,
|
|
427
|
+
sourceId: null,
|
|
358
428
|
runConfig,
|
|
359
429
|
metadata: {
|
|
360
430
|
custom: { ...(currentQuote ? { quote: currentQuote } : {}) },
|
|
361
431
|
},
|
|
362
432
|
};
|
|
363
|
-
|
|
433
|
+
if (queue) {
|
|
434
|
+
queue.enqueue(composedMessage, { steer: opts?.steer ?? false });
|
|
435
|
+
} else {
|
|
436
|
+
onSend?.(composedMessage);
|
|
437
|
+
}
|
|
364
438
|
setText("");
|
|
365
439
|
setAttachments([]);
|
|
366
440
|
setQuote(undefined);
|
|
@@ -372,6 +446,9 @@ const ComposerClientResource = resource(
|
|
|
372
446
|
startDictation: () => {},
|
|
373
447
|
stopDictation: () => {},
|
|
374
448
|
setQuote,
|
|
449
|
+
queueItem: (selector: { index: number }) => {
|
|
450
|
+
return queueItemClients.get(selector);
|
|
451
|
+
},
|
|
375
452
|
};
|
|
376
453
|
},
|
|
377
454
|
);
|
|
@@ -386,12 +463,14 @@ export const ExternalThread = resource(
|
|
|
386
463
|
onReload,
|
|
387
464
|
onStartRun,
|
|
388
465
|
onCancel,
|
|
466
|
+
queue,
|
|
389
467
|
}: ExternalThreadProps): ClientOutput<"thread"> => {
|
|
390
468
|
const handleReload = (messageId: string) => {
|
|
391
469
|
const messageIndex = messages.findIndex((m) => m.id === messageId);
|
|
392
470
|
if (messageIndex === -1) return;
|
|
393
471
|
|
|
394
472
|
const parentId = messageIndex > 0 ? messages[messageIndex - 1]!.id : null;
|
|
473
|
+
queue?.clear("reload");
|
|
395
474
|
onReload?.(parentId);
|
|
396
475
|
};
|
|
397
476
|
|
|
@@ -402,18 +481,20 @@ export const ExternalThread = resource(
|
|
|
402
481
|
message: msg,
|
|
403
482
|
index,
|
|
404
483
|
onReload: () => handleReload(msg.id),
|
|
484
|
+
queue,
|
|
405
485
|
};
|
|
406
486
|
if (onEdit) props.onEdit = onEdit;
|
|
407
487
|
return withKey(msg.id, MessageClient(props));
|
|
408
488
|
}),
|
|
409
|
-
[messages, onEdit],
|
|
489
|
+
[messages, onEdit, queue],
|
|
410
490
|
);
|
|
411
491
|
|
|
412
492
|
const handleCancelRun = () => {
|
|
493
|
+
queue?.clear("cancel-run");
|
|
413
494
|
onCancel?.();
|
|
414
495
|
};
|
|
415
496
|
|
|
416
|
-
const handleSendNew = (message:
|
|
497
|
+
const handleSendNew = (message: AppendMessage) => {
|
|
417
498
|
onNew?.(message);
|
|
418
499
|
};
|
|
419
500
|
|
|
@@ -424,9 +505,11 @@ export const ExternalThread = resource(
|
|
|
424
505
|
canCancel: isRunning,
|
|
425
506
|
onCancel: handleCancelRun,
|
|
426
507
|
onSend: handleSendNew,
|
|
508
|
+
queue,
|
|
427
509
|
}),
|
|
428
510
|
);
|
|
429
511
|
|
|
512
|
+
const hasQueue = !!queue;
|
|
430
513
|
const state = tapMemo(() => {
|
|
431
514
|
const messageStates = messageClients.state.map((s, idx, arr) => ({
|
|
432
515
|
...s,
|
|
@@ -449,6 +532,7 @@ export const ExternalThread = resource(
|
|
|
449
532
|
switchBranchDuringRun: false,
|
|
450
533
|
unstable_copy: false,
|
|
451
534
|
dictation: false,
|
|
535
|
+
queue: hasQueue,
|
|
452
536
|
},
|
|
453
537
|
messages: messageStates,
|
|
454
538
|
state: {},
|
|
@@ -457,13 +541,46 @@ export const ExternalThread = resource(
|
|
|
457
541
|
speech: undefined,
|
|
458
542
|
composer: composerClient.state,
|
|
459
543
|
};
|
|
460
|
-
}, [
|
|
544
|
+
}, [
|
|
545
|
+
messages,
|
|
546
|
+
isRunning,
|
|
547
|
+
hasQueue,
|
|
548
|
+
messageClients.state,
|
|
549
|
+
composerClient.state,
|
|
550
|
+
]);
|
|
461
551
|
|
|
462
552
|
return {
|
|
463
553
|
getState: () => state,
|
|
464
554
|
composer: () => composerClient.methods,
|
|
465
555
|
append: (message) => {
|
|
466
|
-
|
|
556
|
+
const appendMessage: AppendMessage =
|
|
557
|
+
typeof message === "string"
|
|
558
|
+
? {
|
|
559
|
+
createdAt: new Date(),
|
|
560
|
+
parentId: messages.at(-1)?.id ?? null,
|
|
561
|
+
sourceId: null,
|
|
562
|
+
runConfig: {},
|
|
563
|
+
role: "user",
|
|
564
|
+
content: [{ type: "text", text: message }],
|
|
565
|
+
attachments: [],
|
|
566
|
+
metadata: { custom: {} },
|
|
567
|
+
}
|
|
568
|
+
: {
|
|
569
|
+
createdAt: message.createdAt ?? new Date(),
|
|
570
|
+
parentId: message.parentId ?? messages.at(-1)?.id ?? null,
|
|
571
|
+
sourceId: message.sourceId ?? null,
|
|
572
|
+
role: message.role ?? "user",
|
|
573
|
+
content: message.content,
|
|
574
|
+
attachments: message.attachments ?? [],
|
|
575
|
+
metadata: message.metadata ?? { custom: {} },
|
|
576
|
+
runConfig: message.runConfig ?? {},
|
|
577
|
+
startRun: message.startRun,
|
|
578
|
+
};
|
|
579
|
+
if (queue) {
|
|
580
|
+
queue.enqueue(appendMessage, { steer: false });
|
|
581
|
+
} else {
|
|
582
|
+
onNew?.(appendMessage);
|
|
583
|
+
}
|
|
467
584
|
},
|
|
468
585
|
startRun: () => {
|
|
469
586
|
onStartRun?.();
|
|
@@ -489,29 +606,40 @@ export const ExternalThread = resource(
|
|
|
489
606
|
);
|
|
490
607
|
|
|
491
608
|
attachTransformScopes(ExternalThread, (scopes, parent) => {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
609
|
+
if (!scopes.threads && parent.threads.source === null) {
|
|
610
|
+
const threadElement = scopes.thread as ClientElement<"thread">;
|
|
611
|
+
scopes.threads = SingleThreadList({ thread: threadElement });
|
|
612
|
+
scopes.thread = Derived({
|
|
613
|
+
source: "threads",
|
|
614
|
+
query: { type: "main" },
|
|
615
|
+
get: (aui) => aui.threads().thread("main"),
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (!scopes.threadListItem && parent.threadListItem.source === null) {
|
|
620
|
+
scopes.threadListItem = Derived({
|
|
621
|
+
source: "threads",
|
|
622
|
+
query: { type: "main" },
|
|
623
|
+
get: (aui) => aui.threads().item("main"),
|
|
624
|
+
});
|
|
625
|
+
}
|
|
502
626
|
|
|
503
|
-
|
|
504
|
-
|
|
627
|
+
scopes.composer ??= Derived({
|
|
628
|
+
source: "thread",
|
|
629
|
+
query: {},
|
|
630
|
+
get: (aui) => aui.thread().composer(),
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
if (!scopes.modelContext && parent.modelContext.source === null) {
|
|
634
|
+
scopes.modelContext = ModelContext();
|
|
505
635
|
}
|
|
506
|
-
if (!
|
|
507
|
-
|
|
636
|
+
if (!scopes.tools && parent.tools.source === null) {
|
|
637
|
+
scopes.tools = Tools({});
|
|
508
638
|
}
|
|
509
|
-
if (!
|
|
510
|
-
|
|
639
|
+
if (!scopes.dataRenderers && parent.dataRenderers.source === null) {
|
|
640
|
+
scopes.dataRenderers = DataRenderers();
|
|
511
641
|
}
|
|
512
|
-
if (!
|
|
513
|
-
|
|
642
|
+
if (!scopes.suggestions && parent.suggestions.source === null) {
|
|
643
|
+
scopes.suggestions = Suggestions();
|
|
514
644
|
}
|
|
515
|
-
|
|
516
|
-
return result;
|
|
517
645
|
});
|
|
@@ -168,43 +168,32 @@ export const InMemoryThreadList = resource(
|
|
|
168
168
|
);
|
|
169
169
|
|
|
170
170
|
attachTransformScopes(InMemoryThreadList, (scopes, parent) => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
Derived({
|
|
190
|
-
source: "thread",
|
|
191
|
-
query: {},
|
|
192
|
-
get: (aui) => aui.threads().thread("main").composer(),
|
|
193
|
-
}),
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
if (!result.modelContext && parent.modelContext.source === null) {
|
|
197
|
-
result.modelContext = ModelContext();
|
|
171
|
+
scopes.thread ??= Derived({
|
|
172
|
+
source: "threads",
|
|
173
|
+
query: { type: "main" },
|
|
174
|
+
get: (aui) => aui.threads().thread("main"),
|
|
175
|
+
});
|
|
176
|
+
scopes.threadListItem ??= Derived({
|
|
177
|
+
source: "threads",
|
|
178
|
+
query: { type: "main" },
|
|
179
|
+
get: (aui) => aui.threads().item("main"),
|
|
180
|
+
});
|
|
181
|
+
scopes.composer ??= Derived({
|
|
182
|
+
source: "thread",
|
|
183
|
+
query: {},
|
|
184
|
+
get: (aui) => aui.threads().thread("main").composer(),
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (!scopes.modelContext && parent.modelContext.source === null) {
|
|
188
|
+
scopes.modelContext = ModelContext();
|
|
198
189
|
}
|
|
199
|
-
if (!
|
|
200
|
-
|
|
190
|
+
if (!scopes.tools && parent.tools.source === null) {
|
|
191
|
+
scopes.tools = Tools({});
|
|
201
192
|
}
|
|
202
|
-
if (!
|
|
203
|
-
|
|
193
|
+
if (!scopes.dataRenderers && parent.dataRenderers.source === null) {
|
|
194
|
+
scopes.dataRenderers = DataRenderers();
|
|
204
195
|
}
|
|
205
|
-
if (!
|
|
206
|
-
|
|
196
|
+
if (!scopes.suggestions && parent.suggestions.source === null) {
|
|
197
|
+
scopes.suggestions = Suggestions();
|
|
207
198
|
}
|
|
208
|
-
|
|
209
|
-
return result;
|
|
210
199
|
});
|