@convex-dev/agent 0.2.10-alpha.0 → 0.2.11-alpha.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/dist/UIMessages.d.ts.map +1 -1
- package/dist/UIMessages.js +3 -8
- package/dist/UIMessages.js.map +1 -1
- package/dist/client/definePlaygroundAPI.d.ts +42 -42
- package/dist/client/files.d.ts +2 -2
- package/dist/client/files.d.ts.map +1 -1
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +58 -58
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +15 -15
- package/dist/client/messages.d.ts.map +1 -1
- package/dist/client/messages.js.map +1 -1
- package/dist/client/saveInputMessages.d.ts +2 -2
- package/dist/client/saveInputMessages.d.ts.map +1 -1
- package/dist/client/saveInputMessages.js.map +1 -1
- package/dist/client/search.d.ts +17 -17
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +4 -13
- package/dist/client/search.js.map +1 -1
- package/dist/client/streaming.d.ts +95 -95
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js.map +1 -1
- package/dist/client/threads.d.ts +5 -5
- package/dist/client/threads.d.ts.map +1 -1
- package/dist/client/threads.js.map +1 -1
- package/dist/client/types.d.ts +6 -8
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/messages.d.ts +113 -113
- package/dist/component/streams.d.ts +7 -7
- package/dist/component/threads.d.ts +7 -7
- package/dist/component/users.d.ts +3 -3
- package/dist/deltas.d.ts.map +1 -1
- package/dist/deltas.js +6 -17
- package/dist/deltas.js.map +1 -1
- package/dist/mapping.d.ts +5 -4
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +7 -0
- package/dist/mapping.js.map +1 -1
- package/dist/shared.d.ts +4 -2
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +10 -10
- package/dist/shared.js.map +1 -1
- package/dist/validators.d.ts +1 -0
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js.map +1 -1
- package/package.json +2 -2
- package/src/UIMessages.ts +9 -9
- package/src/client/files.ts +2 -2
- package/src/client/index.ts +22 -22
- package/src/client/messages.ts +10 -5
- package/src/client/saveInputMessages.test.ts +13 -5
- package/src/client/saveInputMessages.ts +3 -3
- package/src/client/search.test.ts +19 -15
- package/src/client/search.ts +20 -28
- package/src/client/streaming.ts +7 -6
- package/src/client/threads.ts +10 -5
- package/src/client/types.ts +6 -8
- package/src/deltas.test.ts +24 -14
- package/src/deltas.ts +6 -17
- package/src/mapping.ts +12 -3
- package/src/shared.ts +37 -12
- package/src/toUIMessages.test.ts +80 -11
- package/src/validators.ts +10 -0
package/src/client/streaming.ts
CHANGED
|
@@ -19,9 +19,10 @@ import {
|
|
|
19
19
|
type StreamMessage,
|
|
20
20
|
} from "../validators.js";
|
|
21
21
|
import type {
|
|
22
|
+
ActionCtx,
|
|
22
23
|
AgentComponent,
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
MutationCtx,
|
|
25
|
+
QueryCtx,
|
|
25
26
|
SyncStreamsReturnValue,
|
|
26
27
|
} from "./types.js";
|
|
27
28
|
|
|
@@ -45,7 +46,7 @@ export const vStreamMessagesReturnValue = v.object({
|
|
|
45
46
|
* @returns The deltas for each stream from their existing cursor.
|
|
46
47
|
*/
|
|
47
48
|
export async function syncStreams(
|
|
48
|
-
ctx:
|
|
49
|
+
ctx: QueryCtx | MutationCtx | ActionCtx,
|
|
49
50
|
component: AgentComponent,
|
|
50
51
|
{
|
|
51
52
|
threadId,
|
|
@@ -80,7 +81,7 @@ export async function syncStreams(
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
export async function abortStream(
|
|
83
|
-
ctx:
|
|
84
|
+
ctx: MutationCtx,
|
|
84
85
|
component: AgentComponent,
|
|
85
86
|
args: { reason: string } & (
|
|
86
87
|
| { streamId: string }
|
|
@@ -111,7 +112,7 @@ export async function abortStream(
|
|
|
111
112
|
* @returns The streams for the thread.
|
|
112
113
|
*/
|
|
113
114
|
export async function listStreams(
|
|
114
|
-
ctx:
|
|
115
|
+
ctx: QueryCtx | MutationCtx | ActionCtx,
|
|
115
116
|
component: AgentComponent,
|
|
116
117
|
{
|
|
117
118
|
threadId,
|
|
@@ -212,7 +213,7 @@ export class DeltaStreamer<T> {
|
|
|
212
213
|
|
|
213
214
|
constructor(
|
|
214
215
|
public readonly component: AgentComponent,
|
|
215
|
-
public readonly ctx:
|
|
216
|
+
public readonly ctx: MutationCtx,
|
|
216
217
|
config: {
|
|
217
218
|
throttleMs: number | undefined;
|
|
218
219
|
onAsyncAbort: (reason: string) => Promise<void>;
|
package/src/client/threads.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { WithoutSystemFields } from "convex/server";
|
|
2
2
|
import type { ThreadDoc } from "../validators.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ActionCtx,
|
|
5
|
+
AgentComponent,
|
|
6
|
+
MutationCtx,
|
|
7
|
+
QueryCtx,
|
|
8
|
+
} from "./types.js";
|
|
4
9
|
|
|
5
10
|
/**
|
|
6
11
|
* Create a thread to store messages with an Agent.
|
|
@@ -10,7 +15,7 @@ import type { AgentComponent, RunMutationCtx, RunQueryCtx } from "./types.js";
|
|
|
10
15
|
* @returns The id of the created thread.
|
|
11
16
|
*/
|
|
12
17
|
export async function createThread(
|
|
13
|
-
ctx:
|
|
18
|
+
ctx: MutationCtx,
|
|
14
19
|
component: AgentComponent,
|
|
15
20
|
args?: { userId?: string | null; title?: string; summary?: string },
|
|
16
21
|
) {
|
|
@@ -32,7 +37,7 @@ export async function createThread(
|
|
|
32
37
|
* @returns The metadata for the thread.
|
|
33
38
|
*/
|
|
34
39
|
export async function getThreadMetadata(
|
|
35
|
-
ctx:
|
|
40
|
+
ctx: QueryCtx | MutationCtx | ActionCtx,
|
|
36
41
|
component: AgentComponent,
|
|
37
42
|
args: { threadId: string },
|
|
38
43
|
): Promise<ThreadDoc> {
|
|
@@ -46,7 +51,7 @@ export async function getThreadMetadata(
|
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
export async function updateThreadMetadata(
|
|
49
|
-
ctx:
|
|
54
|
+
ctx: MutationCtx,
|
|
50
55
|
component: AgentComponent,
|
|
51
56
|
args: { threadId: string; patch: Partial<WithoutSystemFields<ThreadDoc>> },
|
|
52
57
|
) {
|
|
@@ -62,7 +67,7 @@ export async function updateThreadMetadata(
|
|
|
62
67
|
* @returns The threads matching the search, paginated.
|
|
63
68
|
*/
|
|
64
69
|
export async function searchThreadTitles(
|
|
65
|
-
ctx:
|
|
70
|
+
ctx: QueryCtx | MutationCtx | ActionCtx,
|
|
66
71
|
component: AgentComponent,
|
|
67
72
|
{
|
|
68
73
|
userId,
|
package/src/client/types.ts
CHANGED
|
@@ -223,7 +223,7 @@ export type GenerationOutputMetadata = {
|
|
|
223
223
|
};
|
|
224
224
|
|
|
225
225
|
export type UsageHandler = (
|
|
226
|
-
ctx:
|
|
226
|
+
ctx: ActionCtx,
|
|
227
227
|
args: {
|
|
228
228
|
userId: string | undefined;
|
|
229
229
|
threadId: string | undefined;
|
|
@@ -242,7 +242,7 @@ export type UsageHandler = (
|
|
|
242
242
|
* out, add in, or reorder messages.
|
|
243
243
|
*/
|
|
244
244
|
export type ContextHandler = (
|
|
245
|
-
ctx:
|
|
245
|
+
ctx: ActionCtx,
|
|
246
246
|
args: {
|
|
247
247
|
/**
|
|
248
248
|
* All messages in the default order.
|
|
@@ -727,26 +727,24 @@ export type SyncStreamsReturnValue =
|
|
|
727
727
|
| undefined;
|
|
728
728
|
|
|
729
729
|
/* Type utils follow */
|
|
730
|
-
|
|
730
|
+
type RunQueryCtx = {
|
|
731
731
|
runQuery: <Query extends FunctionReference<"query", "internal">>(
|
|
732
732
|
query: Query,
|
|
733
733
|
args: FunctionArgs<Query>,
|
|
734
734
|
) => Promise<FunctionReturnType<Query>>;
|
|
735
735
|
};
|
|
736
|
-
export type
|
|
736
|
+
export type MutationCtx = RunQueryCtx & {
|
|
737
737
|
runMutation: <Mutation extends FunctionReference<"mutation", "internal">>(
|
|
738
738
|
mutation: Mutation,
|
|
739
739
|
args: FunctionArgs<Mutation>,
|
|
740
740
|
) => Promise<FunctionReturnType<Mutation>>;
|
|
741
741
|
};
|
|
742
|
-
export type
|
|
742
|
+
export type UserActionCtx = GenericActionCtx<GenericDataModel>;
|
|
743
|
+
export type ActionCtx = MutationCtx & {
|
|
743
744
|
runAction<Action extends FunctionReference<"action", "internal">>(
|
|
744
745
|
action: Action,
|
|
745
746
|
args: FunctionArgs<Action>,
|
|
746
747
|
): Promise<FunctionReturnType<Action>>;
|
|
747
|
-
};
|
|
748
|
-
export type UserActionCtx = GenericActionCtx<GenericDataModel>;
|
|
749
|
-
export type ActionCtx = RunActionCtx & {
|
|
750
748
|
auth: Auth;
|
|
751
749
|
storage: StorageActionWriter;
|
|
752
750
|
};
|
package/src/deltas.test.ts
CHANGED
|
@@ -100,7 +100,7 @@ describe("UIMessageChunks", () => {
|
|
|
100
100
|
{ type: "finish" },
|
|
101
101
|
]);
|
|
102
102
|
expect(updatedMessage.text).toBe(
|
|
103
|
-
"Hey ho.The best ice cream flavor is vanilla.",
|
|
103
|
+
"Hey ho. The best ice cream flavor is vanilla.",
|
|
104
104
|
);
|
|
105
105
|
const expectedParts = [
|
|
106
106
|
{
|
|
@@ -590,7 +590,9 @@ describe("combineUIMessages", () => {
|
|
|
590
590
|
expect(combined[0].text).toBe("The result is 5.");
|
|
591
591
|
expect(combined[0].parts).toHaveLength(2);
|
|
592
592
|
|
|
593
|
-
const toolPart = combined[0].parts.find(
|
|
593
|
+
const toolPart = combined[0].parts.find(
|
|
594
|
+
(p) => p.type === "tool-calculator",
|
|
595
|
+
);
|
|
594
596
|
expect(toolPart).toMatchObject({
|
|
595
597
|
type: "tool-calculator",
|
|
596
598
|
state: "output-available",
|
|
@@ -599,7 +601,7 @@ describe("combineUIMessages", () => {
|
|
|
599
601
|
output: { result: 5 },
|
|
600
602
|
});
|
|
601
603
|
|
|
602
|
-
const textPart = combined[0].parts.find(p => p.type === "text");
|
|
604
|
+
const textPart = combined[0].parts.find((p) => p.type === "text");
|
|
603
605
|
expect(textPart).toMatchObject({
|
|
604
606
|
type: "text",
|
|
605
607
|
text: "The result is 5.",
|
|
@@ -629,7 +631,13 @@ describe("combineUIMessages", () => {
|
|
|
629
631
|
status: "success" as const,
|
|
630
632
|
role: "assistant" as const,
|
|
631
633
|
text: "The result is 5.",
|
|
632
|
-
parts: [
|
|
634
|
+
parts: [
|
|
635
|
+
{
|
|
636
|
+
type: "text" as const,
|
|
637
|
+
text: "The result is 5.",
|
|
638
|
+
state: "done" as const,
|
|
639
|
+
},
|
|
640
|
+
],
|
|
633
641
|
};
|
|
634
642
|
|
|
635
643
|
const combined = combineUIMessages([userMessage, assistantMessage]);
|
|
@@ -706,8 +714,11 @@ describe("combineUIMessages", () => {
|
|
|
706
714
|
expect(combined[0].text).toBe("The formatted result is: 5");
|
|
707
715
|
expect(combined[0].parts).toHaveLength(3);
|
|
708
716
|
|
|
709
|
-
const calculatorPart = combined[0].parts.find(
|
|
710
|
-
p
|
|
717
|
+
const calculatorPart = combined[0].parts.find(
|
|
718
|
+
(p) =>
|
|
719
|
+
p.type === "tool-calculator" &&
|
|
720
|
+
"toolCallId" in p &&
|
|
721
|
+
p.toolCallId === "call_1",
|
|
711
722
|
);
|
|
712
723
|
expect(calculatorPart).toMatchObject({
|
|
713
724
|
type: "tool-calculator",
|
|
@@ -717,8 +728,11 @@ describe("combineUIMessages", () => {
|
|
|
717
728
|
output: { result: 5 },
|
|
718
729
|
});
|
|
719
730
|
|
|
720
|
-
const formatterPart = combined[0].parts.find(
|
|
721
|
-
p
|
|
731
|
+
const formatterPart = combined[0].parts.find(
|
|
732
|
+
(p) =>
|
|
733
|
+
p.type === "tool-formatter" &&
|
|
734
|
+
"toolCallId" in p &&
|
|
735
|
+
p.toolCallId === "call_2",
|
|
722
736
|
);
|
|
723
737
|
expect(formatterPart).toMatchObject({
|
|
724
738
|
type: "tool-formatter",
|
|
@@ -775,9 +789,7 @@ describe("combineUIMessages", () => {
|
|
|
775
789
|
expect(combined[0].text).toBe("Still processing...");
|
|
776
790
|
expect(combined[0].parts).toHaveLength(2);
|
|
777
791
|
|
|
778
|
-
const toolPart = combined[0].parts.find(p =>
|
|
779
|
-
p.type === "dynamic-tool"
|
|
780
|
-
);
|
|
792
|
+
const toolPart = combined[0].parts.find((p) => p.type === "dynamic-tool");
|
|
781
793
|
expect(toolPart).toMatchObject({
|
|
782
794
|
type: "dynamic-tool",
|
|
783
795
|
state: "input-available",
|
|
@@ -786,9 +798,7 @@ describe("combineUIMessages", () => {
|
|
|
786
798
|
input: { operation: "add", a: 2, b: 3 },
|
|
787
799
|
});
|
|
788
800
|
|
|
789
|
-
const textPart = combined[0].parts.find(p =>
|
|
790
|
-
p.type === "text"
|
|
791
|
-
);
|
|
801
|
+
const textPart = combined[0].parts.find((p) => p.type === "text");
|
|
792
802
|
expect(textPart).toMatchObject({
|
|
793
803
|
type: "text",
|
|
794
804
|
text: "Still processing...",
|
package/src/deltas.ts
CHANGED
|
@@ -11,12 +11,13 @@ import {
|
|
|
11
11
|
} from "ai";
|
|
12
12
|
import { assert, pick } from "convex-helpers";
|
|
13
13
|
import { type UIMessage } from "./UIMessages.js";
|
|
14
|
-
import { sorted } from "./shared.js";
|
|
14
|
+
import { joinText, sorted } from "./shared.js";
|
|
15
15
|
import {
|
|
16
16
|
type MessageStatus,
|
|
17
17
|
type StreamDelta,
|
|
18
18
|
type StreamMessage,
|
|
19
19
|
} from "./validators.js";
|
|
20
|
+
import { getErrorMessage } from "@ai-sdk/provider-utils";
|
|
20
21
|
|
|
21
22
|
export function blankUIMessage<METADATA = unknown>(
|
|
22
23
|
streamMessage: StreamMessage & { metadata?: METADATA },
|
|
@@ -85,10 +86,7 @@ export async function updateFromUIMessageChunks(
|
|
|
85
86
|
if (failed) {
|
|
86
87
|
message.status = "failed";
|
|
87
88
|
}
|
|
88
|
-
message.text = message.parts
|
|
89
|
-
.filter((p) => p.type === "text")
|
|
90
|
-
.map((p) => p.text)
|
|
91
|
-
.join("");
|
|
89
|
+
message.text = joinText(message.parts);
|
|
92
90
|
return message;
|
|
93
91
|
}
|
|
94
92
|
|
|
@@ -470,10 +468,7 @@ export function updateFromTextStreamParts(
|
|
|
470
468
|
case "tool-error": {
|
|
471
469
|
const toolPart = toolPartsById.get(part.toolCallId);
|
|
472
470
|
if (toolPart) {
|
|
473
|
-
toolPart.errorText =
|
|
474
|
-
part.error instanceof Error
|
|
475
|
-
? part.error.message.toString()
|
|
476
|
-
: String(part.error);
|
|
471
|
+
toolPart.errorText = getErrorMessage(part.error);
|
|
477
472
|
}
|
|
478
473
|
break;
|
|
479
474
|
}
|
|
@@ -501,10 +496,7 @@ export function updateFromTextStreamParts(
|
|
|
501
496
|
part.state = "done";
|
|
502
497
|
}
|
|
503
498
|
}
|
|
504
|
-
message.text = message.parts
|
|
505
|
-
.filter((p) => p.type === "text")
|
|
506
|
-
.map((p) => p.text)
|
|
507
|
-
.join("");
|
|
499
|
+
message.text = joinText(message.parts);
|
|
508
500
|
return [
|
|
509
501
|
{
|
|
510
502
|
streamId: streamMessage.streamId,
|
|
@@ -571,10 +563,7 @@ export function combineUIMessages(messages: UIMessage[]): UIMessage[] {
|
|
|
571
563
|
...previous,
|
|
572
564
|
...pick(message, ["status", "metadata", "agentName"]),
|
|
573
565
|
parts: newParts,
|
|
574
|
-
text: newParts
|
|
575
|
-
.filter((p) => p.type === "text")
|
|
576
|
-
.map((p) => p.text)
|
|
577
|
-
.join(""),
|
|
566
|
+
text: joinText(newParts),
|
|
578
567
|
});
|
|
579
568
|
return acc;
|
|
580
569
|
}, [] as UIMessage[]);
|
package/src/mapping.ts
CHANGED
|
@@ -35,9 +35,10 @@ import {
|
|
|
35
35
|
type vToolResultPart,
|
|
36
36
|
type SourcePart,
|
|
37
37
|
vToolResultOutput,
|
|
38
|
+
type MessageDoc,
|
|
38
39
|
} from "./validators.js";
|
|
39
40
|
import type { ActionCtx, AgentComponent } from "./client/types.js";
|
|
40
|
-
import type {
|
|
41
|
+
import type { MutationCtx } from "./client/types.js";
|
|
41
42
|
import { MAX_FILE_SIZE, storeFile } from "./client/files.js";
|
|
42
43
|
import type { Infer } from "convex/values";
|
|
43
44
|
import {
|
|
@@ -70,7 +71,7 @@ export type SerializedContent = Message["content"];
|
|
|
70
71
|
export type SerializedMessage = Message;
|
|
71
72
|
|
|
72
73
|
export async function serializeMessage(
|
|
73
|
-
ctx: ActionCtx |
|
|
74
|
+
ctx: ActionCtx | MutationCtx,
|
|
74
75
|
component: AgentComponent,
|
|
75
76
|
message: ModelMessage | Message,
|
|
76
77
|
): Promise<{ message: SerializedMessage; fileIds?: string[] }> {
|
|
@@ -132,6 +133,14 @@ export function toModelMessage(
|
|
|
132
133
|
} as ModelMessage;
|
|
133
134
|
}
|
|
134
135
|
|
|
136
|
+
export function docsToModelMessages(messages: MessageDoc[]): ModelMessage[] {
|
|
137
|
+
return messages
|
|
138
|
+
.map((m) => m.message)
|
|
139
|
+
.filter((m) => !!m)
|
|
140
|
+
.filter((m) => !!m.content.length)
|
|
141
|
+
.map(toModelMessage);
|
|
142
|
+
}
|
|
143
|
+
|
|
135
144
|
export function serializeUsage(usage: LanguageModelUsage): Usage {
|
|
136
145
|
return {
|
|
137
146
|
promptTokens: usage.inputTokens ?? 0,
|
|
@@ -254,7 +263,7 @@ function getMimeOrMediaType(part: { mediaType?: string; mimeType?: string }) {
|
|
|
254
263
|
}
|
|
255
264
|
|
|
256
265
|
export async function serializeContent(
|
|
257
|
-
ctx: ActionCtx |
|
|
266
|
+
ctx: ActionCtx | MutationCtx,
|
|
258
267
|
component: AgentComponent,
|
|
259
268
|
content: Content | Message["content"],
|
|
260
269
|
): Promise<{ content: SerializedContent; fileIds?: string[] }> {
|
package/src/shared.ts
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
FilePart,
|
|
3
|
+
ImagePart,
|
|
4
|
+
ReasoningPart,
|
|
5
|
+
ToolCallPart,
|
|
6
|
+
ToolResultPart,
|
|
7
|
+
} from "@ai-sdk/provider-utils";
|
|
8
|
+
import type {
|
|
9
|
+
ModelMessage,
|
|
10
|
+
TextPart,
|
|
11
|
+
UIDataTypes,
|
|
12
|
+
UIMessagePart,
|
|
13
|
+
UITools,
|
|
14
|
+
} from "ai";
|
|
15
|
+
import type { Message, MessageContentParts } from "./validators.js";
|
|
3
16
|
|
|
4
17
|
export const DEFAULT_RECENT_MESSAGES = 100;
|
|
5
18
|
|
|
@@ -18,19 +31,12 @@ export function extractText(message: Message | ModelMessage) {
|
|
|
18
31
|
if (typeof message.content === "string") {
|
|
19
32
|
return message.content;
|
|
20
33
|
}
|
|
21
|
-
return message.content
|
|
22
|
-
.filter((c) => c.type === "text")
|
|
23
|
-
.map((c) => c.text)
|
|
24
|
-
.join("");
|
|
34
|
+
return joinText(message.content);
|
|
25
35
|
case "assistant":
|
|
26
36
|
if (typeof message.content === "string") {
|
|
27
37
|
return message.content;
|
|
28
38
|
} else {
|
|
29
|
-
|
|
30
|
-
if (!textParts.length) {
|
|
31
|
-
return undefined;
|
|
32
|
-
}
|
|
33
|
-
return textParts.map((c) => c.text).join("");
|
|
39
|
+
return joinText(message.content) || undefined;
|
|
34
40
|
}
|
|
35
41
|
case "system":
|
|
36
42
|
return message.content;
|
|
@@ -39,6 +45,25 @@ export function extractText(message: Message | ModelMessage) {
|
|
|
39
45
|
return undefined;
|
|
40
46
|
}
|
|
41
47
|
|
|
48
|
+
export function joinText(
|
|
49
|
+
parts: (
|
|
50
|
+
| UIMessagePart<UIDataTypes, UITools>
|
|
51
|
+
| TextPart
|
|
52
|
+
| ImagePart
|
|
53
|
+
| FilePart
|
|
54
|
+
| ReasoningPart
|
|
55
|
+
| ToolCallPart
|
|
56
|
+
| ToolResultPart
|
|
57
|
+
| MessageContentParts
|
|
58
|
+
)[],
|
|
59
|
+
) {
|
|
60
|
+
return parts
|
|
61
|
+
.filter((p) => p.type === "text")
|
|
62
|
+
.map((p) => p.text)
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.join(" ");
|
|
65
|
+
}
|
|
66
|
+
|
|
42
67
|
export function extractReasoning(message: Message | ModelMessage) {
|
|
43
68
|
if (typeof message.content === "string") {
|
|
44
69
|
return undefined;
|
|
@@ -46,7 +71,7 @@ export function extractReasoning(message: Message | ModelMessage) {
|
|
|
46
71
|
return message.content
|
|
47
72
|
.filter((c) => c.type === "reasoning")
|
|
48
73
|
.map((c) => c.text)
|
|
49
|
-
.join("");
|
|
74
|
+
.join(" ");
|
|
50
75
|
}
|
|
51
76
|
|
|
52
77
|
export const DEFAULT_MESSAGE_RANGE = { before: 2, after: 1 };
|
package/src/toUIMessages.test.ts
CHANGED
|
@@ -132,10 +132,7 @@ describe("toUIMessages", () => {
|
|
|
132
132
|
type: "tool-myTool",
|
|
133
133
|
toolCallId: "call1",
|
|
134
134
|
state: "output-available",
|
|
135
|
-
output:
|
|
136
|
-
type: "text",
|
|
137
|
-
value: "42",
|
|
138
|
-
},
|
|
135
|
+
output: "42",
|
|
139
136
|
});
|
|
140
137
|
});
|
|
141
138
|
|
|
@@ -163,14 +160,14 @@ describe("toUIMessages", () => {
|
|
|
163
160
|
},
|
|
164
161
|
],
|
|
165
162
|
},
|
|
166
|
-
reasoning: "I'm thinking...I'm thinking...",
|
|
163
|
+
reasoning: "I'm thinking... I'm thinking...",
|
|
167
164
|
text: "Here's one idea. Here's another idea.",
|
|
168
165
|
}),
|
|
169
166
|
];
|
|
170
167
|
const uiMessages = toUIMessages(messages);
|
|
171
168
|
expect(uiMessages).toHaveLength(1);
|
|
172
169
|
expect(uiMessages[0].role).toBe("assistant");
|
|
173
|
-
expect(uiMessages[0].text).toBe("Here's one idea.Here's another idea.");
|
|
170
|
+
expect(uiMessages[0].text).toBe("Here's one idea. Here's another idea.");
|
|
174
171
|
expect(uiMessages[0].parts.filter((p) => p.type === "reasoning")).toEqual([
|
|
175
172
|
{
|
|
176
173
|
providerOptions: undefined,
|
|
@@ -206,6 +203,81 @@ describe("toUIMessages", () => {
|
|
|
206
203
|
);
|
|
207
204
|
});
|
|
208
205
|
|
|
206
|
+
it("combines text from between messages", () => {
|
|
207
|
+
const messages = [
|
|
208
|
+
baseMessageDoc({
|
|
209
|
+
message: {
|
|
210
|
+
role: "assistant",
|
|
211
|
+
content: [
|
|
212
|
+
{
|
|
213
|
+
type: "reasoning",
|
|
214
|
+
text: "I'm thinking...",
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
type: "text",
|
|
218
|
+
text: "I'm going to ask a question.",
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
type: "tool-call",
|
|
222
|
+
args: "What's the meaning of life?",
|
|
223
|
+
toolCallId: "call1",
|
|
224
|
+
toolName: "myTool",
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
reasoning: "I'm thinking...",
|
|
229
|
+
text: "Here's one idea.",
|
|
230
|
+
tool: true,
|
|
231
|
+
order: 1,
|
|
232
|
+
stepOrder: 1,
|
|
233
|
+
}),
|
|
234
|
+
baseMessageDoc({
|
|
235
|
+
message: {
|
|
236
|
+
role: "tool",
|
|
237
|
+
content: [
|
|
238
|
+
{
|
|
239
|
+
type: "tool-result",
|
|
240
|
+
toolCallId: "call1",
|
|
241
|
+
toolName: "myTool",
|
|
242
|
+
output: {
|
|
243
|
+
type: "text",
|
|
244
|
+
value: "42",
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
},
|
|
249
|
+
text: "",
|
|
250
|
+
tool: true,
|
|
251
|
+
order: 1,
|
|
252
|
+
stepOrder: 2,
|
|
253
|
+
}),
|
|
254
|
+
baseMessageDoc({
|
|
255
|
+
message: {
|
|
256
|
+
role: "assistant",
|
|
257
|
+
content: [
|
|
258
|
+
{
|
|
259
|
+
type: "reasoning",
|
|
260
|
+
text: "Thinking again...",
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
type: "text",
|
|
264
|
+
text: "Ok now I know.",
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
text: "One last thing.",
|
|
269
|
+
order: 1,
|
|
270
|
+
stepOrder: 3,
|
|
271
|
+
}),
|
|
272
|
+
];
|
|
273
|
+
const uiMessages = toUIMessages(messages);
|
|
274
|
+
expect(uiMessages).toHaveLength(1);
|
|
275
|
+
expect(uiMessages[0].role).toBe("assistant");
|
|
276
|
+
expect(uiMessages[0].text).toBe(
|
|
277
|
+
"I'm going to ask a question. Ok now I know.",
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
|
|
209
281
|
it("handles system message", () => {
|
|
210
282
|
const messages = [
|
|
211
283
|
baseMessageDoc({
|
|
@@ -252,7 +324,7 @@ describe("toUIMessages", () => {
|
|
|
252
324
|
type: "tool-result",
|
|
253
325
|
toolName: "myTool",
|
|
254
326
|
toolCallId: "call1",
|
|
255
|
-
|
|
327
|
+
output: {
|
|
256
328
|
type: "json",
|
|
257
329
|
value: { data: "wrapped result", success: true },
|
|
258
330
|
},
|
|
@@ -648,10 +720,7 @@ describe("toUIMessages", () => {
|
|
|
648
720
|
toolCallId: "call1",
|
|
649
721
|
state: "output-available",
|
|
650
722
|
input: { operation: "add", a: 40, b: 2 },
|
|
651
|
-
output:
|
|
652
|
-
type: "text",
|
|
653
|
-
value: "42",
|
|
654
|
-
},
|
|
723
|
+
output: "42",
|
|
655
724
|
});
|
|
656
725
|
|
|
657
726
|
// Should also have text part
|
package/src/validators.ts
CHANGED
|
@@ -221,6 +221,16 @@ export const vMessage = v.union(
|
|
|
221
221
|
);
|
|
222
222
|
export type Message = Infer<typeof vMessage>;
|
|
223
223
|
|
|
224
|
+
export type MessageContentParts =
|
|
225
|
+
| Infer<typeof vTextPart>
|
|
226
|
+
| Infer<typeof vImagePart>
|
|
227
|
+
| Infer<typeof vFilePart>
|
|
228
|
+
| Infer<typeof vReasoningPart>
|
|
229
|
+
| Infer<typeof vRedactedReasoningPart>
|
|
230
|
+
| Infer<typeof vToolCallPart>
|
|
231
|
+
| Infer<typeof vToolResultPart>
|
|
232
|
+
| Infer<typeof vSourcePart>;
|
|
233
|
+
|
|
224
234
|
export const vSource = v.union(
|
|
225
235
|
v.object({
|
|
226
236
|
type: v.optional(v.literal("source")),
|