@assistant-ui/react 0.12.1 → 0.12.3
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/dist/client/Suggestions.d.ts +11 -0
- package/dist/client/Suggestions.d.ts.map +1 -0
- package/dist/client/Suggestions.js +43 -0
- package/dist/client/Suggestions.js.map +1 -0
- package/dist/client/Tools.d.ts.map +1 -1
- package/dist/client/Tools.js +5 -1
- package/dist/client/Tools.js.map +1 -1
- package/dist/context/providers/SuggestionByIndexProvider.d.ts +6 -0
- package/dist/context/providers/SuggestionByIndexProvider.d.ts.map +1 -0
- package/dist/context/providers/SuggestionByIndexProvider.js +14 -0
- package/dist/context/providers/SuggestionByIndexProvider.js.map +1 -0
- package/dist/context/providers/index.d.ts +1 -0
- package/dist/context/providers/index.d.ts.map +1 -1
- package/dist/context/providers/index.js +1 -0
- package/dist/context/providers/index.js.map +1 -1
- package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts +5 -0
- package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts.map +1 -1
- package/dist/legacy-runtime/AssistantRuntimeProvider.js +2 -4
- package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
- package/dist/legacy-runtime/RuntimeAdapter.js +2 -1
- package/dist/legacy-runtime/RuntimeAdapter.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +3 -5
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/utils.d.ts +10 -12
- package/dist/legacy-runtime/runtime-cores/assistant-transport/utils.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/utils.js +14 -19
- package/dist/legacy-runtime/runtime-cores/assistant-transport/utils.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.js +32 -4
- package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/remote-thread-list/BaseSubscribable.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/remote-thread-list/BaseSubscribable.js +3 -0
- package/dist/legacy-runtime/runtime-cores/remote-thread-list/BaseSubscribable.js.map +1 -1
- package/dist/model-context/index.d.ts +1 -0
- package/dist/model-context/index.d.ts.map +1 -1
- package/dist/model-context/index.js +1 -0
- package/dist/model-context/index.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
- package/dist/primitives/index.d.ts +1 -0
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +1 -0
- package/dist/primitives/index.js.map +1 -1
- package/dist/primitives/message/MessageParts.d.ts +11 -0
- package/dist/primitives/message/MessageParts.d.ts.map +1 -1
- package/dist/primitives/message/MessageParts.js +18 -2
- package/dist/primitives/message/MessageParts.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionDescription.d.ts +18 -0
- package/dist/primitives/suggestion/SuggestionDescription.d.ts.map +1 -0
- package/dist/primitives/suggestion/SuggestionDescription.js +19 -0
- package/dist/primitives/suggestion/SuggestionDescription.js.map +1 -0
- package/dist/primitives/suggestion/SuggestionTitle.d.ts +18 -0
- package/dist/primitives/suggestion/SuggestionTitle.d.ts.map +1 -0
- package/dist/primitives/suggestion/SuggestionTitle.js +19 -0
- package/dist/primitives/suggestion/SuggestionTitle.js.map +1 -0
- package/dist/primitives/suggestion/SuggestionTrigger.d.ts +49 -0
- package/dist/primitives/suggestion/SuggestionTrigger.d.ts.map +1 -0
- package/dist/primitives/suggestion/SuggestionTrigger.js +45 -0
- package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -0
- package/dist/primitives/suggestion/index.d.ts +4 -0
- package/dist/primitives/suggestion/index.d.ts.map +1 -0
- package/dist/primitives/suggestion/index.js +4 -0
- package/dist/primitives/suggestion/index.js.map +1 -0
- package/dist/primitives/thread/ThreadSuggestions.d.ts +53 -0
- package/dist/primitives/thread/ThreadSuggestions.d.ts.map +1 -0
- package/dist/primitives/thread/ThreadSuggestions.js +58 -0
- package/dist/primitives/thread/ThreadSuggestions.js.map +1 -0
- package/dist/primitives/thread/index.d.ts +1 -0
- package/dist/primitives/thread/index.d.ts.map +1 -1
- package/dist/primitives/thread/index.js +1 -0
- package/dist/primitives/thread/index.js.map +1 -1
- package/dist/types/scopes/index.d.ts +2 -0
- package/dist/types/scopes/index.d.ts.map +1 -1
- package/dist/types/scopes/suggestion.d.ts +20 -0
- package/dist/types/scopes/suggestion.d.ts.map +1 -0
- package/dist/types/scopes/suggestion.js +2 -0
- package/dist/types/scopes/suggestion.js.map +1 -0
- package/dist/types/scopes/suggestions.d.ts +20 -0
- package/dist/types/scopes/suggestions.d.ts.map +1 -0
- package/dist/types/scopes/suggestions.js +2 -0
- package/dist/types/scopes/suggestions.js.map +1 -0
- package/dist/types/store-augmentation.d.ts +4 -0
- package/dist/types/store-augmentation.d.ts.map +1 -1
- package/dist/utils/idUtils.d.ts +2 -0
- package/dist/utils/idUtils.d.ts.map +1 -1
- package/dist/utils/idUtils.js +3 -0
- package/dist/utils/idUtils.js.map +1 -1
- package/package.json +10 -10
- package/src/client/Suggestions.ts +74 -0
- package/src/client/Tools.ts +10 -1
- package/src/context/providers/SuggestionByIndexProvider.tsx +23 -0
- package/src/context/providers/index.ts +1 -0
- package/src/legacy-runtime/AssistantRuntimeProvider.tsx +8 -5
- package/src/legacy-runtime/RuntimeAdapter.ts +2 -1
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.tsx +3 -4
- package/src/legacy-runtime/runtime-cores/assistant-transport/utils.ts +19 -24
- package/src/legacy-runtime/runtime-cores/external-store/external-message-converter.tsx +42 -7
- package/src/legacy-runtime/runtime-cores/remote-thread-list/BaseSubscribable.tsx +3 -0
- package/src/model-context/index.ts +2 -0
- package/src/primitives/assistantModal/AssistantModalRoot.tsx +1 -1
- package/src/primitives/index.ts +1 -0
- package/src/primitives/message/MessageParts.tsx +45 -1
- package/src/primitives/suggestion/SuggestionDescription.tsx +33 -0
- package/src/primitives/suggestion/SuggestionTitle.tsx +33 -0
- package/src/primitives/suggestion/SuggestionTrigger.tsx +79 -0
- package/src/primitives/suggestion/index.ts +3 -0
- package/src/primitives/thread/ThreadSuggestions.tsx +109 -0
- package/src/primitives/thread/index.ts +4 -0
- package/src/tests/external-message-converter.test.ts +105 -16
- package/src/types/scopes/index.ts +12 -0
- package/src/types/scopes/suggestion.ts +20 -0
- package/src/types/scopes/suggestions.ts +21 -0
- package/src/types/store-augmentation.ts +4 -0
- package/src/utils/idUtils.tsx +4 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ActionButtonElement,
|
|
5
|
+
ActionButtonProps,
|
|
6
|
+
createActionButton,
|
|
7
|
+
} from "../../utils/createActionButton";
|
|
8
|
+
import { useCallback } from "react";
|
|
9
|
+
import { useAuiState, useAui } from "@assistant-ui/store";
|
|
10
|
+
|
|
11
|
+
const useSuggestionTrigger = ({
|
|
12
|
+
send,
|
|
13
|
+
clearComposer = true,
|
|
14
|
+
}: {
|
|
15
|
+
/**
|
|
16
|
+
* When true, automatically sends the message.
|
|
17
|
+
* When false, replaces or appends the composer text with the suggestion - depending on the value of `clearComposer`.
|
|
18
|
+
*/
|
|
19
|
+
send?: boolean | undefined;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Whether to clear the composer after sending.
|
|
23
|
+
* When send is set to false, determines if composer text is replaced with suggestion (true, default),
|
|
24
|
+
* or if it's appended to the composer text (false).
|
|
25
|
+
*
|
|
26
|
+
* @default true
|
|
27
|
+
*/
|
|
28
|
+
clearComposer?: boolean | undefined;
|
|
29
|
+
}) => {
|
|
30
|
+
const aui = useAui();
|
|
31
|
+
const disabled = useAuiState(({ thread }) => thread.isDisabled);
|
|
32
|
+
const prompt = useAuiState(({ suggestion }) => suggestion.prompt);
|
|
33
|
+
|
|
34
|
+
const resolvedSend = send ?? false;
|
|
35
|
+
|
|
36
|
+
const callback = useCallback(() => {
|
|
37
|
+
const isRunning = aui.thread().getState().isRunning;
|
|
38
|
+
|
|
39
|
+
if (resolvedSend && !isRunning) {
|
|
40
|
+
aui.thread().append(prompt);
|
|
41
|
+
if (clearComposer) {
|
|
42
|
+
aui.composer().setText("");
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
if (clearComposer) {
|
|
46
|
+
aui.composer().setText(prompt);
|
|
47
|
+
} else {
|
|
48
|
+
const currentText = aui.composer().getState().text;
|
|
49
|
+
aui
|
|
50
|
+
.composer()
|
|
51
|
+
.setText(currentText.trim() ? `${currentText} ${prompt}` : prompt);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}, [aui, resolvedSend, clearComposer, prompt]);
|
|
55
|
+
|
|
56
|
+
if (disabled) return null;
|
|
57
|
+
return callback;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export namespace SuggestionPrimitiveTrigger {
|
|
61
|
+
export type Element = ActionButtonElement;
|
|
62
|
+
export type Props = ActionButtonProps<typeof useSuggestionTrigger>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A button that triggers the suggestion action (send or insert into composer).
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```tsx
|
|
70
|
+
* <SuggestionPrimitive.Trigger send>
|
|
71
|
+
* Click me
|
|
72
|
+
* </SuggestionPrimitive.Trigger>
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export const SuggestionPrimitiveTrigger = createActionButton(
|
|
76
|
+
"SuggestionPrimitive.Trigger",
|
|
77
|
+
useSuggestionTrigger,
|
|
78
|
+
["send", "clearComposer"],
|
|
79
|
+
);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { type ComponentType, type FC, memo, useMemo } from "react";
|
|
4
|
+
import { useAuiState } from "@assistant-ui/store";
|
|
5
|
+
import { SuggestionByIndexProvider } from "../../context/providers";
|
|
6
|
+
|
|
7
|
+
export namespace ThreadPrimitiveSuggestions {
|
|
8
|
+
export type Props = {
|
|
9
|
+
/**
|
|
10
|
+
* Component to render for each suggestion.
|
|
11
|
+
*/
|
|
12
|
+
components: {
|
|
13
|
+
/** Component used to render each suggestion */
|
|
14
|
+
Suggestion: ComponentType;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type SuggestionComponentProps = {
|
|
20
|
+
components: ThreadPrimitiveSuggestions.Props["components"];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const SuggestionComponent: FC<SuggestionComponentProps> = ({ components }) => {
|
|
24
|
+
const Component = components.Suggestion;
|
|
25
|
+
return <Component />;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export namespace ThreadPrimitiveSuggestionByIndex {
|
|
29
|
+
export type Props = {
|
|
30
|
+
index: number;
|
|
31
|
+
components: ThreadPrimitiveSuggestions.Props["components"];
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Renders a single suggestion at the specified index.
|
|
37
|
+
*
|
|
38
|
+
* This component provides suggestion context for a specific suggestion
|
|
39
|
+
* and renders it using the provided component configuration.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* <ThreadPrimitive.SuggestionByIndex
|
|
44
|
+
* index={0}
|
|
45
|
+
* components={{
|
|
46
|
+
* Suggestion: MySuggestion
|
|
47
|
+
* }}
|
|
48
|
+
* />
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export const ThreadPrimitiveSuggestionByIndex: FC<ThreadPrimitiveSuggestionByIndex.Props> =
|
|
52
|
+
memo(
|
|
53
|
+
({ index, components }) => {
|
|
54
|
+
return (
|
|
55
|
+
<SuggestionByIndexProvider index={index}>
|
|
56
|
+
<SuggestionComponent components={components} />
|
|
57
|
+
</SuggestionByIndexProvider>
|
|
58
|
+
);
|
|
59
|
+
},
|
|
60
|
+
(prev, next) =>
|
|
61
|
+
prev.index === next.index &&
|
|
62
|
+
prev.components.Suggestion === next.components.Suggestion,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
ThreadPrimitiveSuggestionByIndex.displayName =
|
|
66
|
+
"ThreadPrimitive.SuggestionByIndex";
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Renders all suggestions using the provided component configuration.
|
|
70
|
+
*
|
|
71
|
+
* This component automatically renders all suggestions from the suggestions scope,
|
|
72
|
+
* providing the appropriate suggestion context for each one.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```tsx
|
|
76
|
+
* <ThreadPrimitive.Suggestions
|
|
77
|
+
* components={{
|
|
78
|
+
* Suggestion: MySuggestion
|
|
79
|
+
* }}
|
|
80
|
+
* />
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export const ThreadPrimitiveSuggestionsImpl: FC<
|
|
84
|
+
ThreadPrimitiveSuggestions.Props
|
|
85
|
+
> = ({ components }) => {
|
|
86
|
+
const suggestionsLength = useAuiState(
|
|
87
|
+
({ suggestions }) => suggestions.suggestions.length,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const suggestionElements = useMemo(() => {
|
|
91
|
+
if (suggestionsLength === 0) return null;
|
|
92
|
+
return Array.from({ length: suggestionsLength }, (_, index) => (
|
|
93
|
+
<ThreadPrimitiveSuggestionByIndex
|
|
94
|
+
key={index}
|
|
95
|
+
index={index}
|
|
96
|
+
components={components}
|
|
97
|
+
/>
|
|
98
|
+
));
|
|
99
|
+
}, [suggestionsLength, components]);
|
|
100
|
+
|
|
101
|
+
return suggestionElements;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
ThreadPrimitiveSuggestionsImpl.displayName = "ThreadPrimitive.Suggestions";
|
|
105
|
+
|
|
106
|
+
export const ThreadPrimitiveSuggestions = memo(
|
|
107
|
+
ThreadPrimitiveSuggestionsImpl,
|
|
108
|
+
(prev, next) => prev.components.Suggestion === next.components.Suggestion,
|
|
109
|
+
);
|
|
@@ -9,3 +9,7 @@ export { ThreadPrimitiveMessages as Messages } from "./ThreadMessages";
|
|
|
9
9
|
export { ThreadPrimitiveMessageByIndex as MessageByIndex } from "./ThreadMessages";
|
|
10
10
|
export { ThreadPrimitiveScrollToBottom as ScrollToBottom } from "./ThreadScrollToBottom";
|
|
11
11
|
export { ThreadPrimitiveSuggestion as Suggestion } from "./ThreadSuggestion";
|
|
12
|
+
export {
|
|
13
|
+
ThreadPrimitiveSuggestions as Suggestions,
|
|
14
|
+
ThreadPrimitiveSuggestionByIndex as SuggestionByIndex,
|
|
15
|
+
} from "./ThreadSuggestions";
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
2
|
import { convertExternalMessages } from "../legacy-runtime/runtime-cores/external-store/external-message-converter";
|
|
3
3
|
import type { useExternalMessageConverter } from "../legacy-runtime/runtime-cores/external-store/external-message-converter";
|
|
4
|
+
import { isErrorMessageId } from "../utils/idUtils";
|
|
4
5
|
|
|
5
|
-
/**
|
|
6
|
-
* Tests for the external message converter, specifically the joinExternalMessages logic.
|
|
7
|
-
*/
|
|
8
6
|
describe("convertExternalMessages", () => {
|
|
9
7
|
describe("reasoning part merging", () => {
|
|
10
|
-
/**
|
|
11
|
-
* Tests that reasoning parts with the same parentId are merged together.
|
|
12
|
-
* The text should be concatenated with a double newline separator.
|
|
13
|
-
*/
|
|
14
8
|
it("should merge reasoning parts with the same parentId", () => {
|
|
15
9
|
const messages = [
|
|
16
10
|
{
|
|
@@ -56,9 +50,6 @@ describe("convertExternalMessages", () => {
|
|
|
56
50
|
expect((reasoningParts[0] as any).parentId).toBe("parent1");
|
|
57
51
|
});
|
|
58
52
|
|
|
59
|
-
/**
|
|
60
|
-
* Tests that reasoning parts without parentId remain separate.
|
|
61
|
-
*/
|
|
62
53
|
it("should keep reasoning parts without parentId separate", () => {
|
|
63
54
|
const messages = [
|
|
64
55
|
{
|
|
@@ -89,9 +80,6 @@ describe("convertExternalMessages", () => {
|
|
|
89
80
|
expect((reasoningParts[1] as any).text).toBe("Second reasoning");
|
|
90
81
|
});
|
|
91
82
|
|
|
92
|
-
/**
|
|
93
|
-
* Tests that reasoning parts with different parentIds remain separate.
|
|
94
|
-
*/
|
|
95
83
|
it("should keep reasoning parts with different parentIds separate", () => {
|
|
96
84
|
const messages = [
|
|
97
85
|
{
|
|
@@ -134,9 +122,6 @@ describe("convertExternalMessages", () => {
|
|
|
134
122
|
expect((reasoningParts[1] as any).parentId).toBe("parent2");
|
|
135
123
|
});
|
|
136
124
|
|
|
137
|
-
/**
|
|
138
|
-
* Tests that tool result merging still works correctly alongside reasoning merging.
|
|
139
|
-
*/
|
|
140
125
|
it("should still merge tool results with matching tool calls", () => {
|
|
141
126
|
const messages = [
|
|
142
127
|
{
|
|
@@ -174,4 +159,108 @@ describe("convertExternalMessages", () => {
|
|
|
174
159
|
expect((toolCallParts[0] as any).result).toEqual({ data: "result" });
|
|
175
160
|
});
|
|
176
161
|
});
|
|
162
|
+
|
|
163
|
+
describe("synthetic error message", () => {
|
|
164
|
+
it("should create synthetic error message when error exists and no messages", () => {
|
|
165
|
+
const messages: never[] = [];
|
|
166
|
+
const callback: useExternalMessageConverter.Callback<never> = (msg) =>
|
|
167
|
+
msg;
|
|
168
|
+
|
|
169
|
+
const result = convertExternalMessages(messages, callback, false, {
|
|
170
|
+
error: "API key is missing",
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
expect(result).toHaveLength(1);
|
|
174
|
+
expect(result[0]!.role).toBe("assistant");
|
|
175
|
+
expect(result[0]!.content).toHaveLength(0);
|
|
176
|
+
expect(result[0]!.status).toEqual({
|
|
177
|
+
type: "incomplete",
|
|
178
|
+
reason: "error",
|
|
179
|
+
error: "API key is missing",
|
|
180
|
+
});
|
|
181
|
+
expect(isErrorMessageId(result[0]!.id)).toBe(true);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("should create synthetic error message when error exists and last message is user", () => {
|
|
185
|
+
const messages = [
|
|
186
|
+
{
|
|
187
|
+
id: "user1",
|
|
188
|
+
role: "user" as const,
|
|
189
|
+
content: "Hello",
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
const callback: useExternalMessageConverter.Callback<
|
|
194
|
+
(typeof messages)[number]
|
|
195
|
+
> = (msg) => msg;
|
|
196
|
+
|
|
197
|
+
const result = convertExternalMessages(messages, callback, false, {
|
|
198
|
+
error: { message: "Invalid API key" },
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(result).toHaveLength(2);
|
|
202
|
+
expect(result[0]!.role).toBe("user");
|
|
203
|
+
expect(result[1]!.role).toBe("assistant");
|
|
204
|
+
expect(result[1]!.content).toHaveLength(0);
|
|
205
|
+
expect(result[1]!.status).toEqual({
|
|
206
|
+
type: "incomplete",
|
|
207
|
+
reason: "error",
|
|
208
|
+
error: { message: "Invalid API key" },
|
|
209
|
+
});
|
|
210
|
+
expect(isErrorMessageId(result[1]!.id)).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("should not create synthetic error message when last message is assistant", () => {
|
|
214
|
+
const messages = [
|
|
215
|
+
{
|
|
216
|
+
id: "user1",
|
|
217
|
+
role: "user" as const,
|
|
218
|
+
content: "Hello",
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: "assistant1",
|
|
222
|
+
role: "assistant" as const,
|
|
223
|
+
content: "Hi there",
|
|
224
|
+
},
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
const callback: useExternalMessageConverter.Callback<
|
|
228
|
+
(typeof messages)[number]
|
|
229
|
+
> = (msg) => msg;
|
|
230
|
+
|
|
231
|
+
const result = convertExternalMessages(messages, callback, false, {
|
|
232
|
+
error: "Connection error",
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
expect(result).toHaveLength(2);
|
|
236
|
+
expect(result[0]!.role).toBe("user");
|
|
237
|
+
expect(result[1]!.role).toBe("assistant");
|
|
238
|
+
expect(result[1]!.id).toBe("assistant1");
|
|
239
|
+
expect(result[1]!.status).toMatchObject({
|
|
240
|
+
type: "incomplete",
|
|
241
|
+
reason: "error",
|
|
242
|
+
error: "Connection error",
|
|
243
|
+
});
|
|
244
|
+
expect(isErrorMessageId(result[1]!.id)).toBe(false);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("should not create synthetic message when no error", () => {
|
|
248
|
+
const messages = [
|
|
249
|
+
{
|
|
250
|
+
id: "user1",
|
|
251
|
+
role: "user" as const,
|
|
252
|
+
content: "Hello",
|
|
253
|
+
},
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
const callback: useExternalMessageConverter.Callback<
|
|
257
|
+
(typeof messages)[number]
|
|
258
|
+
> = (msg) => msg;
|
|
259
|
+
|
|
260
|
+
const result = convertExternalMessages(messages, callback, false, {});
|
|
261
|
+
|
|
262
|
+
expect(result).toHaveLength(1);
|
|
263
|
+
expect(result[0]!.role).toBe("user");
|
|
264
|
+
});
|
|
265
|
+
});
|
|
177
266
|
});
|
|
@@ -43,6 +43,18 @@ export type {
|
|
|
43
43
|
AttachmentClientSchema,
|
|
44
44
|
} from "./attachment";
|
|
45
45
|
export type { ToolsState, ToolsMethods, ToolsClientSchema } from "./tools";
|
|
46
|
+
export type {
|
|
47
|
+
SuggestionsState,
|
|
48
|
+
SuggestionsMethods,
|
|
49
|
+
SuggestionsClientSchema,
|
|
50
|
+
Suggestion,
|
|
51
|
+
} from "./suggestions";
|
|
52
|
+
export type {
|
|
53
|
+
SuggestionState,
|
|
54
|
+
SuggestionMethods,
|
|
55
|
+
SuggestionMeta,
|
|
56
|
+
SuggestionClientSchema,
|
|
57
|
+
} from "./suggestion";
|
|
46
58
|
export type {
|
|
47
59
|
ModelContextState,
|
|
48
60
|
ModelContextMethods,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type SuggestionState = {
|
|
2
|
+
title: string;
|
|
3
|
+
label: string;
|
|
4
|
+
prompt: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type SuggestionMethods = {
|
|
8
|
+
getState(): SuggestionState;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type SuggestionMeta = {
|
|
12
|
+
source: "suggestions";
|
|
13
|
+
query: { index: number };
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type SuggestionClientSchema = {
|
|
17
|
+
state: SuggestionState;
|
|
18
|
+
methods: SuggestionMethods;
|
|
19
|
+
meta: SuggestionMeta;
|
|
20
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SuggestionMethods } from "./suggestion";
|
|
2
|
+
|
|
3
|
+
export type Suggestion = {
|
|
4
|
+
title: string;
|
|
5
|
+
label: string;
|
|
6
|
+
prompt: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type SuggestionsState = {
|
|
10
|
+
suggestions: Suggestion[];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type SuggestionsMethods = {
|
|
14
|
+
getState(): SuggestionsState;
|
|
15
|
+
suggestion(query: { index: number }): SuggestionMethods;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type SuggestionsClientSchema = {
|
|
19
|
+
state: SuggestionsState;
|
|
20
|
+
methods: SuggestionsMethods;
|
|
21
|
+
};
|
|
@@ -9,6 +9,8 @@ import type { ComposerClientSchema } from "./scopes/composer";
|
|
|
9
9
|
import type { AttachmentClientSchema } from "./scopes/attachment";
|
|
10
10
|
import type { ToolsClientSchema } from "./scopes/tools";
|
|
11
11
|
import type { ModelContextClientSchema } from "./scopes/modelContext";
|
|
12
|
+
import type { SuggestionsClientSchema } from "./scopes/suggestions";
|
|
13
|
+
import type { SuggestionClientSchema } from "./scopes/suggestion";
|
|
12
14
|
|
|
13
15
|
declare module "@assistant-ui/store" {
|
|
14
16
|
interface ClientRegistry {
|
|
@@ -21,5 +23,7 @@ declare module "@assistant-ui/store" {
|
|
|
21
23
|
attachment: AttachmentClientSchema;
|
|
22
24
|
tools: ToolsClientSchema;
|
|
23
25
|
modelContext: ModelContextClientSchema;
|
|
26
|
+
suggestions: SuggestionsClientSchema;
|
|
27
|
+
suggestion: SuggestionClientSchema;
|
|
24
28
|
}
|
|
25
29
|
}
|
package/src/utils/idUtils.tsx
CHANGED
|
@@ -8,3 +8,7 @@ export const generateId = customAlphabet(
|
|
|
8
8
|
const optimisticPrefix = "__optimistic__";
|
|
9
9
|
export const generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
|
|
10
10
|
export const isOptimisticId = (id: string) => id.startsWith(optimisticPrefix);
|
|
11
|
+
|
|
12
|
+
const errorPrefix = "__error__";
|
|
13
|
+
export const generateErrorMessageId = () => `${errorPrefix}${generateId()}`;
|
|
14
|
+
export const isErrorMessageId = (id: string) => id.startsWith(errorPrefix);
|