@convex-dev/agent 0.2.6-alpha.0 → 0.2.6
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/definePlaygroundAPI.d.ts +6 -4
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
- package/dist/client/definePlaygroundAPI.js +15 -6
- package/dist/client/definePlaygroundAPI.js.map +1 -1
- package/dist/client/index.d.ts +26 -120
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +48 -373
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +1 -1
- package/dist/client/messages.d.ts.map +1 -1
- package/dist/client/mockModel.d.ts +3 -3
- package/dist/client/mockModel.d.ts.map +1 -1
- package/dist/client/mockModel.js +22 -17
- package/dist/client/mockModel.js.map +1 -1
- package/dist/client/saveInputMessages.d.ts +20 -0
- package/dist/client/saveInputMessages.d.ts.map +1 -0
- package/dist/client/saveInputMessages.js +57 -0
- package/dist/client/saveInputMessages.js.map +1 -0
- package/dist/client/search.d.ts +110 -9
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +271 -39
- package/dist/client/search.js.map +1 -1
- package/dist/client/start.d.ts +83 -0
- package/dist/client/start.d.ts.map +1 -0
- package/dist/client/start.js +171 -0
- package/dist/client/start.js.map +1 -0
- package/dist/client/streaming.d.ts +8 -8
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +2 -1
- package/dist/client/streaming.js.map +1 -1
- package/dist/client/textStreamParts.d.ts.map +1 -1
- package/dist/client/textStreamParts.js +2 -9
- package/dist/client/textStreamParts.js.map +1 -1
- package/dist/client/threads.d.ts +1 -1
- package/dist/client/threads.d.ts.map +1 -1
- package/dist/client/types.d.ts +137 -5
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/_generated/api.d.ts +11 -3
- package/dist/component/messages.d.ts +13 -4
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +67 -25
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +2 -1643
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +0 -24
- package/dist/component/schema.js.map +1 -1
- package/dist/mapping.d.ts +7 -9
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +73 -7
- package/dist/mapping.js.map +1 -1
- package/dist/react/deltas.d.ts.map +1 -1
- package/dist/react/deltas.js +15 -5
- package/dist/react/deltas.js.map +1 -1
- package/dist/react/fromUIMessages.d.ts +13 -0
- package/dist/react/fromUIMessages.d.ts.map +1 -0
- package/dist/react/fromUIMessages.js +70 -0
- package/dist/react/fromUIMessages.js.map +1 -0
- package/dist/react/toUIMessages.d.ts +5 -2
- package/dist/react/toUIMessages.d.ts.map +1 -1
- package/dist/react/toUIMessages.js +3 -0
- package/dist/react/toUIMessages.js.map +1 -1
- package/dist/shared.d.ts +10 -0
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +26 -0
- package/dist/shared.js.map +1 -1
- package/dist/validators.d.ts +1640 -0
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +41 -0
- package/dist/validators.js.map +1 -1
- package/package.json +1 -1
- package/src/client/definePlaygroundAPI.ts +16 -7
- package/src/client/index.test.ts +11 -46
- package/src/client/index.ts +99 -558
- package/src/client/messages.ts +1 -1
- package/src/client/mock.json +68 -0
- package/src/client/mockModel.ts +34 -23
- package/src/client/saveInputMessages.test.ts +576 -0
- package/src/client/saveInputMessages.ts +100 -0
- package/src/client/search.test.ts +1017 -0
- package/src/client/search.ts +446 -68
- package/src/client/start.ts +313 -0
- package/src/client/stream.json +48 -0
- package/src/client/streaming.ts +3 -3
- package/src/client/textStreamParts.ts +2 -11
- package/src/client/threads.ts +1 -1
- package/src/client/types.ts +143 -3
- package/src/component/_generated/api.d.ts +11 -3
- package/src/component/messages.ts +73 -27
- package/src/component/schema.ts +1 -29
- package/src/mapping.ts +84 -7
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/src/react/deltas.ts +18 -5
- package/src/react/fromUIMessages.test.ts +427 -0
- package/src/react/fromUIMessages.ts +85 -0
- package/src/react/toUIMessages.ts +21 -13
- package/src/shared.ts +33 -0
- package/src/validators.test.ts +13 -2
- package/src/validators.ts +48 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import {
|
|
2
|
+
stepCountIs,
|
|
3
|
+
type CallSettings,
|
|
4
|
+
type GenerateObjectResult,
|
|
5
|
+
type IdGenerator,
|
|
6
|
+
type LanguageModel,
|
|
7
|
+
type ModelMessage,
|
|
8
|
+
type StepResult,
|
|
9
|
+
type StopCondition,
|
|
10
|
+
type ToolSet,
|
|
11
|
+
} from "ai";
|
|
12
|
+
import {
|
|
13
|
+
serializeNewMessagesInStep,
|
|
14
|
+
serializeObjectResult,
|
|
15
|
+
} from "../mapping.js";
|
|
16
|
+
import { embedMessages, fetchContextWithPrompt } from "./search.js";
|
|
17
|
+
import type {
|
|
18
|
+
ActionCtx,
|
|
19
|
+
AgentComponent,
|
|
20
|
+
Config,
|
|
21
|
+
Options,
|
|
22
|
+
UserActionCtx,
|
|
23
|
+
} from "./types.js";
|
|
24
|
+
import { saveMessages } from "./messages.js";
|
|
25
|
+
import type { Message, MessageDoc } from "../validators.js";
|
|
26
|
+
import {
|
|
27
|
+
getModelName,
|
|
28
|
+
getProviderName,
|
|
29
|
+
type ModelOrMetadata,
|
|
30
|
+
} from "../shared.js";
|
|
31
|
+
import { wrapTools, type ToolCtx } from "./createTool.js";
|
|
32
|
+
import type { Agent } from "./index.js";
|
|
33
|
+
import { omit } from "convex-helpers";
|
|
34
|
+
import { saveInputMessages } from "./saveInputMessages.js";
|
|
35
|
+
|
|
36
|
+
export async function start<
|
|
37
|
+
T,
|
|
38
|
+
Tools extends ToolSet = ToolSet,
|
|
39
|
+
CustomCtx extends object = object,
|
|
40
|
+
>(
|
|
41
|
+
ctx: ActionCtx & CustomCtx,
|
|
42
|
+
component: AgentComponent,
|
|
43
|
+
/**
|
|
44
|
+
* These are the arguments you'll pass to the LLM call such as
|
|
45
|
+
* `generateText` or `streamText`. This function will look up the context
|
|
46
|
+
* and provide functions to save the steps, abort the generation, and more.
|
|
47
|
+
* The type of the arguments returned infers from the type of the arguments
|
|
48
|
+
* you pass here.
|
|
49
|
+
*/
|
|
50
|
+
args: T & {
|
|
51
|
+
/**
|
|
52
|
+
* If provided, this message will be used as the "prompt" for the LLM call,
|
|
53
|
+
* instead of the prompt or messages.
|
|
54
|
+
* This is useful if you want to first save a user message, then use it as
|
|
55
|
+
* the prompt for the LLM call in another call.
|
|
56
|
+
*/
|
|
57
|
+
promptMessageId?: string;
|
|
58
|
+
/**
|
|
59
|
+
* The model to use for the LLM calls. This will override the model specified
|
|
60
|
+
* in the Agent constructor.
|
|
61
|
+
*/
|
|
62
|
+
model?: LanguageModel;
|
|
63
|
+
/**
|
|
64
|
+
* The tools to use for the tool calls. This will override tools specified
|
|
65
|
+
* in the Agent constructor or createThread / continueThread.
|
|
66
|
+
*/
|
|
67
|
+
tools?: Tools;
|
|
68
|
+
/**
|
|
69
|
+
* The single prompt message to use for the LLM call. This will be the
|
|
70
|
+
* last message in the context. If it's a string, it will be a user role.
|
|
71
|
+
*/
|
|
72
|
+
prompt?: string | (ModelMessage | Message)[];
|
|
73
|
+
/**
|
|
74
|
+
* If provided alongside prompt, the ordering will be:
|
|
75
|
+
* 1. system prompt
|
|
76
|
+
* 2. search context
|
|
77
|
+
* 3. recent messages
|
|
78
|
+
* 4. these messages
|
|
79
|
+
* 5. prompt messages, including those already on the same `order` as
|
|
80
|
+
* the promptMessageId message, if provided.
|
|
81
|
+
*/
|
|
82
|
+
messages?: (ModelMessage | Message)[];
|
|
83
|
+
/**
|
|
84
|
+
* The abort signal to be passed to the LLM call. If triggered, it will
|
|
85
|
+
* mark the pending message as failed. If the generation is asynchronously
|
|
86
|
+
* aborted, it will trigger this signal when detected.
|
|
87
|
+
*/
|
|
88
|
+
abortSignal?: AbortSignal;
|
|
89
|
+
stopWhen?: StopCondition<Tools> | Array<StopCondition<Tools>>;
|
|
90
|
+
_internal?: { generateId?: IdGenerator };
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
threadId,
|
|
94
|
+
...opts
|
|
95
|
+
}: Options &
|
|
96
|
+
Config & {
|
|
97
|
+
userId?: string | null;
|
|
98
|
+
threadId?: string;
|
|
99
|
+
languageModel: LanguageModel;
|
|
100
|
+
agentName: string;
|
|
101
|
+
agentForToolCtx?: Agent;
|
|
102
|
+
},
|
|
103
|
+
): Promise<{
|
|
104
|
+
args: T & {
|
|
105
|
+
system?: string;
|
|
106
|
+
model: LanguageModel;
|
|
107
|
+
messages: ModelMessage[];
|
|
108
|
+
tools?: Tools;
|
|
109
|
+
} & CallSettings;
|
|
110
|
+
order: number;
|
|
111
|
+
stepOrder: number;
|
|
112
|
+
userId: string | undefined;
|
|
113
|
+
promptMessageId: string | undefined;
|
|
114
|
+
updateModel: (model: ModelOrMetadata | undefined) => void;
|
|
115
|
+
save: <TOOLS extends ToolSet>(
|
|
116
|
+
toSave:
|
|
117
|
+
| { step: StepResult<TOOLS> }
|
|
118
|
+
| { object: GenerateObjectResult<unknown> },
|
|
119
|
+
createPendingMessage?: boolean,
|
|
120
|
+
) => Promise<void>;
|
|
121
|
+
fail: (reason: string) => Promise<void>;
|
|
122
|
+
getSavedMessages: () => MessageDoc[];
|
|
123
|
+
}> {
|
|
124
|
+
const userId =
|
|
125
|
+
opts.userId ??
|
|
126
|
+
(threadId &&
|
|
127
|
+
(await ctx.runQuery(component.threads.getThread, { threadId }))
|
|
128
|
+
?.userId) ??
|
|
129
|
+
undefined;
|
|
130
|
+
|
|
131
|
+
const context = await fetchContextWithPrompt(ctx, component, {
|
|
132
|
+
...opts,
|
|
133
|
+
userId,
|
|
134
|
+
threadId,
|
|
135
|
+
messages: args.messages,
|
|
136
|
+
prompt: args.prompt,
|
|
137
|
+
promptMessageId: args.promptMessageId,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const saveMessages = opts.storageOptions?.saveMessages ?? "promptAndOutput";
|
|
141
|
+
const { promptMessageId, pendingMessage, savedMessages } =
|
|
142
|
+
threadId && saveMessages !== "none"
|
|
143
|
+
? await saveInputMessages(ctx, component, {
|
|
144
|
+
...opts,
|
|
145
|
+
userId,
|
|
146
|
+
threadId,
|
|
147
|
+
prompt: args.prompt,
|
|
148
|
+
messages: args.messages,
|
|
149
|
+
promptMessageId: args.promptMessageId,
|
|
150
|
+
storageOptions: { saveMessages },
|
|
151
|
+
})
|
|
152
|
+
: {
|
|
153
|
+
promptMessageId: args.promptMessageId,
|
|
154
|
+
pendingMessage: undefined,
|
|
155
|
+
savedMessages: [] as MessageDoc[],
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const order = pendingMessage?.order ?? context.order;
|
|
159
|
+
const stepOrder = pendingMessage?.stepOrder ?? context.stepOrder;
|
|
160
|
+
let pendingMessageId = pendingMessage?._id;
|
|
161
|
+
|
|
162
|
+
const model = args.model ?? opts.languageModel;
|
|
163
|
+
let activeModel: ModelOrMetadata = model;
|
|
164
|
+
|
|
165
|
+
const fail = async (reason: string) => {
|
|
166
|
+
if (pendingMessageId) {
|
|
167
|
+
await ctx.runMutation(component.messages.finalizeMessage, {
|
|
168
|
+
messageId: pendingMessageId,
|
|
169
|
+
result: { status: "failed", error: reason },
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
if (args.abortSignal) {
|
|
174
|
+
const abortSignal = args.abortSignal;
|
|
175
|
+
abortSignal.addEventListener(
|
|
176
|
+
"abort",
|
|
177
|
+
async () => {
|
|
178
|
+
await fail(abortSignal.reason?.toString() ?? "abortSignal");
|
|
179
|
+
},
|
|
180
|
+
{ once: true },
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
const toolCtx = {
|
|
184
|
+
...(ctx as UserActionCtx & CustomCtx),
|
|
185
|
+
userId,
|
|
186
|
+
threadId,
|
|
187
|
+
promptMessageId,
|
|
188
|
+
agent: opts.agentForToolCtx,
|
|
189
|
+
} satisfies ToolCtx;
|
|
190
|
+
const tools = wrapTools(toolCtx, args.tools) as Tools;
|
|
191
|
+
const aiArgs = {
|
|
192
|
+
...opts.callSettings,
|
|
193
|
+
providerOptions: opts.providerOptions,
|
|
194
|
+
...omit(args, ["promptMessageId", "messages", "prompt"]),
|
|
195
|
+
model,
|
|
196
|
+
messages: context.messages,
|
|
197
|
+
stopWhen:
|
|
198
|
+
args.stopWhen ?? (opts.maxSteps ? stepCountIs(opts.maxSteps) : undefined),
|
|
199
|
+
tools,
|
|
200
|
+
} as T & {
|
|
201
|
+
model: LanguageModel;
|
|
202
|
+
messages: ModelMessage[];
|
|
203
|
+
tools?: Tools;
|
|
204
|
+
_internal?: { generateId?: IdGenerator };
|
|
205
|
+
} & CallSettings;
|
|
206
|
+
if (pendingMessageId) {
|
|
207
|
+
if (!aiArgs._internal?.generateId) {
|
|
208
|
+
aiArgs._internal = {
|
|
209
|
+
...aiArgs._internal,
|
|
210
|
+
generateId: pendingMessageId
|
|
211
|
+
? () => pendingMessageId ?? crypto.randomUUID()
|
|
212
|
+
: undefined,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
args: aiArgs,
|
|
218
|
+
order: order ?? 0,
|
|
219
|
+
stepOrder: stepOrder ?? 0,
|
|
220
|
+
userId,
|
|
221
|
+
promptMessageId,
|
|
222
|
+
getSavedMessages: () => savedMessages,
|
|
223
|
+
updateModel: (model: ModelOrMetadata | undefined) => {
|
|
224
|
+
if (model) {
|
|
225
|
+
activeModel = model;
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
fail,
|
|
229
|
+
save: async <TOOLS extends ToolSet>(
|
|
230
|
+
toSave:
|
|
231
|
+
| { step: StepResult<TOOLS> }
|
|
232
|
+
| { object: GenerateObjectResult<unknown> },
|
|
233
|
+
createPendingMessage?: boolean,
|
|
234
|
+
) => {
|
|
235
|
+
if (threadId && saveMessages !== "none") {
|
|
236
|
+
const serialized =
|
|
237
|
+
"object" in toSave
|
|
238
|
+
? await serializeObjectResult(
|
|
239
|
+
ctx,
|
|
240
|
+
component,
|
|
241
|
+
toSave.object,
|
|
242
|
+
activeModel,
|
|
243
|
+
)
|
|
244
|
+
: await serializeNewMessagesInStep(
|
|
245
|
+
ctx,
|
|
246
|
+
component,
|
|
247
|
+
toSave.step,
|
|
248
|
+
activeModel,
|
|
249
|
+
);
|
|
250
|
+
const embeddings = await embedMessages(
|
|
251
|
+
ctx,
|
|
252
|
+
{ threadId, ...opts, userId },
|
|
253
|
+
serialized.messages.map((m) => m.message),
|
|
254
|
+
);
|
|
255
|
+
if (createPendingMessage) {
|
|
256
|
+
serialized.messages.push({
|
|
257
|
+
message: { role: "assistant", content: [] },
|
|
258
|
+
status: "pending",
|
|
259
|
+
});
|
|
260
|
+
embeddings?.vectors.push(null);
|
|
261
|
+
}
|
|
262
|
+
const saved = await ctx.runMutation(component.messages.addMessages, {
|
|
263
|
+
userId,
|
|
264
|
+
threadId,
|
|
265
|
+
agentName: opts.agentName,
|
|
266
|
+
promptMessageId,
|
|
267
|
+
pendingMessageId,
|
|
268
|
+
messages: serialized.messages,
|
|
269
|
+
embeddings,
|
|
270
|
+
failPendingSteps: false,
|
|
271
|
+
});
|
|
272
|
+
const lastMessage = saved.messages.at(-1)!;
|
|
273
|
+
if (createPendingMessage) {
|
|
274
|
+
if (lastMessage.status === "failed") {
|
|
275
|
+
pendingMessageId = undefined;
|
|
276
|
+
savedMessages.push(...saved.messages);
|
|
277
|
+
await fail(
|
|
278
|
+
lastMessage.error ??
|
|
279
|
+
"Aborting - the pending message was marked as failed",
|
|
280
|
+
);
|
|
281
|
+
} else {
|
|
282
|
+
pendingMessageId = lastMessage._id;
|
|
283
|
+
savedMessages.push(...saved.messages.slice(0, -1));
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
pendingMessageId = undefined;
|
|
287
|
+
savedMessages.push(...saved.messages);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const output = "object" in toSave ? toSave.object : toSave.step;
|
|
291
|
+
if (opts.rawRequestResponseHandler) {
|
|
292
|
+
await opts.rawRequestResponseHandler(ctx, {
|
|
293
|
+
userId,
|
|
294
|
+
threadId,
|
|
295
|
+
agentName: opts.agentName,
|
|
296
|
+
request: output.request,
|
|
297
|
+
response: output.response,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
if (opts.usageHandler && output.usage) {
|
|
301
|
+
await opts.usageHandler(ctx, {
|
|
302
|
+
userId,
|
|
303
|
+
threadId,
|
|
304
|
+
agentName: opts.agentName,
|
|
305
|
+
model: getModelName(activeModel),
|
|
306
|
+
provider: getProviderName(activeModel),
|
|
307
|
+
usage: output.usage,
|
|
308
|
+
providerMetadata: output.providerMetadata,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "type": "start" },
|
|
3
|
+
{ "type": "start-step" },
|
|
4
|
+
{ "type": "reasoning-start", "id": "reasoning-0" },
|
|
5
|
+
{ "type": "reasoning-delta", "id": "reasoning-0", "delta": "Okay" },
|
|
6
|
+
{
|
|
7
|
+
"type": "reasoning-delta",
|
|
8
|
+
"id": "reasoning-0",
|
|
9
|
+
"delta": ", the user is asking, \"What is the best flavor of ice cream?\" I need to figure out how to respond. Let me check the tools provided. The only tool available is the \"say\" function, which allows me to ask a friend for their favorite ice cream flavor. The function requires a \"question\" parameter.\n\nSo, since I don't have any other functions, I can't look up information or calculate the answer. The best approach is to use the \"say\" function to ask a friend. I should formulate the question to match the friend's parameters. The user is asking for the \"best\" flavor, which is subjective. Therefore, asking a friend's favorite makes sense.\n\nI need to structure the function call correctly. The function name is \"say\" and the argument is the question. The question should be, \"What is your favorite flavor of ice cream?\" That's the parameter required. Let me make sure the JSON is properly formatted with the arguments as a JSON object. Yep, that should work. I'll output the tool_call with the function name and the question argument.\n"
|
|
10
|
+
},
|
|
11
|
+
{ "type": "tool-input-start", "toolCallId": "0ychh9k6f", "toolName": "say" },
|
|
12
|
+
{
|
|
13
|
+
"type": "tool-input-delta",
|
|
14
|
+
"toolCallId": "0ychh9k6f",
|
|
15
|
+
"inputTextDelta": "{\"question\":\"What is your favorite flavor of ice cream?\"}"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"type": "tool-input-available",
|
|
19
|
+
"toolCallId": "0ychh9k6f",
|
|
20
|
+
"toolName": "say",
|
|
21
|
+
"input": { "question": "What is your favorite flavor of ice cream?" }
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "tool-output-available",
|
|
25
|
+
"toolCallId": "0ychh9k6f",
|
|
26
|
+
"output": "I'm sorry I can't help you. Stop asking me questions."
|
|
27
|
+
},
|
|
28
|
+
{ "type": "reasoning-end", "id": "reasoning-0" },
|
|
29
|
+
{ "type": "finish-step" },
|
|
30
|
+
{ "type": "start-step" },
|
|
31
|
+
{ "type": "reasoning-start", "id": "reasoning-0" },
|
|
32
|
+
{ "type": "reasoning-delta", "id": "reasoning-0", "delta": "Okay" },
|
|
33
|
+
{
|
|
34
|
+
"type": "reasoning-delta",
|
|
35
|
+
"id": "reasoning-0",
|
|
36
|
+
"delta": ", the user initially asked for the best ice cream flavor. I tried using the 'say' function to ask a friend, but the friend didn't help. Now I need to respond. Since I can't get an answer from the friend, I should tell the user that I can't determine the best flavor because it's subjective. Maybe suggest they try different ones. Keep the response friendly and helpful.\n"
|
|
37
|
+
},
|
|
38
|
+
{ "type": "text-start", "id": "txt-0" },
|
|
39
|
+
{
|
|
40
|
+
"type": "text-delta",
|
|
41
|
+
"id": "txt-0",
|
|
42
|
+
"delta": "The \"best\" ice cream flavor is subjective—it depends on personal taste! Some people love classic vanilla, while others might prefer adventurous options like matcha or salted caramel. Why not try a few and see which one you like most? 🍦"
|
|
43
|
+
},
|
|
44
|
+
{ "type": "reasoning-end", "id": "reasoning-0" },
|
|
45
|
+
{ "type": "text-end", "id": "txt-0" },
|
|
46
|
+
{ "type": "finish-step" },
|
|
47
|
+
{ "type": "finish" }
|
|
48
|
+
]
|
package/src/client/streaming.ts
CHANGED
|
@@ -10,13 +10,12 @@ import {
|
|
|
10
10
|
} from "../validators.js";
|
|
11
11
|
import type {
|
|
12
12
|
AgentComponent,
|
|
13
|
-
RunActionCtx,
|
|
14
13
|
RunMutationCtx,
|
|
15
14
|
RunQueryCtx,
|
|
16
15
|
SyncStreamsReturnValue,
|
|
17
16
|
} from "./types.js";
|
|
18
17
|
import { v } from "convex/values";
|
|
19
|
-
import { vMessageDoc } from "../
|
|
18
|
+
import { vMessageDoc } from "../validators.js";
|
|
20
19
|
|
|
21
20
|
export const vStreamMessagesReturnValue = v.object({
|
|
22
21
|
...vPaginationResult(vMessageDoc).fields,
|
|
@@ -175,7 +174,7 @@ export class DeltaStreamer<T> {
|
|
|
175
174
|
|
|
176
175
|
constructor(
|
|
177
176
|
public readonly component: AgentComponent,
|
|
178
|
-
public readonly ctx:
|
|
177
|
+
public readonly ctx: RunMutationCtx,
|
|
179
178
|
config: {
|
|
180
179
|
stream: true | StreamingOptions;
|
|
181
180
|
onAsyncAbort: (reason: string) => Promise<void>;
|
|
@@ -299,6 +298,7 @@ export class DeltaStreamer<T> {
|
|
|
299
298
|
return;
|
|
300
299
|
}
|
|
301
300
|
await this.#ongoingWrite;
|
|
301
|
+
await this.#sendDelta();
|
|
302
302
|
await this.ctx.runMutation(this.component.streams.finish, {
|
|
303
303
|
streamId: this.streamId,
|
|
304
304
|
});
|
|
@@ -23,21 +23,12 @@ export function serializeTextStreamingPartsV5(
|
|
|
23
23
|
) {
|
|
24
24
|
last.text += part.text;
|
|
25
25
|
} else {
|
|
26
|
-
if (
|
|
27
|
-
part.type === "start-step" ||
|
|
28
|
-
part.type === "finish-step" ||
|
|
29
|
-
part.type === "start" ||
|
|
30
|
-
part.type === "finish"
|
|
31
|
-
) {
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
26
|
if (part.type === "file") {
|
|
35
27
|
compressed.push({
|
|
36
28
|
type: "file",
|
|
37
29
|
file: {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
uint8Array: new Uint8Array([]),
|
|
30
|
+
...part.file,
|
|
31
|
+
uint8Array: undefined as unknown as Uint8Array,
|
|
41
32
|
},
|
|
42
33
|
});
|
|
43
34
|
}
|
package/src/client/threads.ts
CHANGED
package/src/client/types.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
InferSchema,
|
|
3
|
+
ModelMessage,
|
|
3
4
|
ProviderOptions,
|
|
4
5
|
Schema,
|
|
5
6
|
} from "@ai-sdk/provider-utils";
|
|
6
7
|
import type { JSONValue } from "@ai-sdk/provider";
|
|
7
8
|
import type {
|
|
9
|
+
EmbeddingModel,
|
|
8
10
|
GenerateObjectResult,
|
|
9
11
|
generateText,
|
|
10
12
|
GenerateTextResult,
|
|
@@ -36,16 +38,96 @@ import type {
|
|
|
36
38
|
} from "convex/server";
|
|
37
39
|
import type { GenericId } from "convex/values";
|
|
38
40
|
import type { Mounts } from "../component/_generated/api.js";
|
|
39
|
-
import type { MessageDoc, ThreadDoc } from "../component/schema.js";
|
|
40
41
|
import type {
|
|
42
|
+
MessageDoc,
|
|
41
43
|
ProviderMetadata,
|
|
42
44
|
StreamDelta,
|
|
43
45
|
StreamMessage,
|
|
46
|
+
ThreadDoc,
|
|
44
47
|
} from "../validators.js";
|
|
45
48
|
import type { StreamingOptions } from "./streaming.js";
|
|
46
49
|
import type * as z3 from "zod/v3";
|
|
47
50
|
import type * as z4 from "zod/v4";
|
|
48
51
|
|
|
52
|
+
export type Config = {
|
|
53
|
+
/**
|
|
54
|
+
* The LLM model to use for generating / streaming text and objects.
|
|
55
|
+
* e.g.
|
|
56
|
+
* import { openai } from "@ai-sdk/openai"
|
|
57
|
+
* const myAgent = new Agent(components.agent, {
|
|
58
|
+
* languageModel: openai.chat("gpt-4o-mini"),
|
|
59
|
+
*/
|
|
60
|
+
languageModel?: LanguageModel;
|
|
61
|
+
/**
|
|
62
|
+
* The model to use for text embeddings. Optional.
|
|
63
|
+
* If specified, it will use this for generating vector embeddings
|
|
64
|
+
* of chats, and can opt-in to doing vector search for automatic context
|
|
65
|
+
* on generateText, etc.
|
|
66
|
+
* e.g.
|
|
67
|
+
* import { openai } from "@ai-sdk/openai"
|
|
68
|
+
* const myAgent = new Agent(components.agent, {
|
|
69
|
+
* ...
|
|
70
|
+
* textEmbeddingModel: openai.embedding("text-embedding-3-small")
|
|
71
|
+
*/
|
|
72
|
+
textEmbeddingModel?: EmbeddingModel<string>;
|
|
73
|
+
/**
|
|
74
|
+
* Options to determine what messages are included as context in message
|
|
75
|
+
* generation. To disable any messages automatically being added, pass:
|
|
76
|
+
* { recentMessages: 0 }
|
|
77
|
+
*/
|
|
78
|
+
contextOptions?: ContextOptions;
|
|
79
|
+
/**
|
|
80
|
+
* Determines whether messages are automatically stored when passed as
|
|
81
|
+
* arguments or generated.
|
|
82
|
+
*/
|
|
83
|
+
storageOptions?: StorageOptions;
|
|
84
|
+
/**
|
|
85
|
+
* The usage handler to use for this agent.
|
|
86
|
+
*/
|
|
87
|
+
usageHandler?: UsageHandler;
|
|
88
|
+
/**
|
|
89
|
+
* By default, messages are ordered with context in `fetchContextWithPrompt`,
|
|
90
|
+
* but you can override this by providing a context handler. Here you can
|
|
91
|
+
* filter, modify, or enrich the context messages. If provided, the default
|
|
92
|
+
* ordering will not apply. This excludes the system message / instructions.
|
|
93
|
+
*/
|
|
94
|
+
contextHandler?: ContextHandler;
|
|
95
|
+
/**
|
|
96
|
+
* Called for each LLM request/response, so you can do things like
|
|
97
|
+
* log the raw request body or response headers to a table, or logs.
|
|
98
|
+
*/
|
|
99
|
+
rawRequestResponseHandler?: RawRequestResponseHandler;
|
|
100
|
+
/**
|
|
101
|
+
* @deprecated Reach out if you use this. Otherwise will be removed soon.
|
|
102
|
+
* Default provider options to pass for the LLM calls.
|
|
103
|
+
* This can be overridden at each generate/stream callsite on a per-field
|
|
104
|
+
* basis. To clear a default setting, you'll need to pass `undefined`.
|
|
105
|
+
*/
|
|
106
|
+
providerOptions?: ProviderOptions;
|
|
107
|
+
/**
|
|
108
|
+
* The default settings to use for the LLM calls.
|
|
109
|
+
* This can be overridden at each generate/stream callsite on a per-field
|
|
110
|
+
* basis. To clear a default setting, you'll need to pass `undefined`.
|
|
111
|
+
*/
|
|
112
|
+
callSettings?: CallSettings;
|
|
113
|
+
/**
|
|
114
|
+
* The maximum number of steps to allow for a single generation.
|
|
115
|
+
*
|
|
116
|
+
* For example, if an agent wants to call a tool, that call and tool response
|
|
117
|
+
* will be one step. Generating a response based on the tool call & response
|
|
118
|
+
* will be a second step.
|
|
119
|
+
* If it runs out of steps, it will return the last step result, which may
|
|
120
|
+
* not be an assistant message.
|
|
121
|
+
|
|
122
|
+
* This becomes the default value when `stopWhen` is not specified in the
|
|
123
|
+
* Agent or generation callsite.
|
|
124
|
+
* AI SDK v5 removed the `maxSteps` argument, but this is kept here for
|
|
125
|
+
* convenience and backwards compatibility.
|
|
126
|
+
* Defaults to 1.
|
|
127
|
+
*/
|
|
128
|
+
maxSteps?: number;
|
|
129
|
+
};
|
|
130
|
+
|
|
49
131
|
/**
|
|
50
132
|
* Options to configure what messages are fetched as context,
|
|
51
133
|
* automatically with thread.generateText, or directly via search.
|
|
@@ -153,6 +235,50 @@ export type UsageHandler = (
|
|
|
153
235
|
},
|
|
154
236
|
) => void | Promise<void>;
|
|
155
237
|
|
|
238
|
+
/**
|
|
239
|
+
* By default, messages are ordered with context in `fetchContextWithPrompt`,
|
|
240
|
+
* but you can override this by providing a context handler. Here you can filter
|
|
241
|
+
* out, add in, or reorder messages.
|
|
242
|
+
*/
|
|
243
|
+
export type ContextHandler = (
|
|
244
|
+
ctx: RunActionCtx,
|
|
245
|
+
args: {
|
|
246
|
+
/**
|
|
247
|
+
* The messages fetched from search.
|
|
248
|
+
*/
|
|
249
|
+
search: ModelMessage[];
|
|
250
|
+
/**
|
|
251
|
+
* The recent messages already in the thread history,
|
|
252
|
+
* excluding any messages that came after promptMessageId.
|
|
253
|
+
*/
|
|
254
|
+
recent: ModelMessage[];
|
|
255
|
+
/**
|
|
256
|
+
* The messages passed as the `messages` argument to e.g. generateText.
|
|
257
|
+
*/
|
|
258
|
+
inputMessages: ModelMessage[];
|
|
259
|
+
/**
|
|
260
|
+
* The message(s) passed as the `prompt` argument to e.g. generateText.
|
|
261
|
+
* Otherwise, if `promptMessageId` was provided, the message at that id.
|
|
262
|
+
* `prompt` will override the message at `promptMessageId`.
|
|
263
|
+
*/
|
|
264
|
+
inputPrompt: ModelMessage[];
|
|
265
|
+
/**
|
|
266
|
+
* Any messages on the same `order` as the promptMessageId message after the
|
|
267
|
+
* prompt message. These are presumably existing responses to the prompt
|
|
268
|
+
* message.
|
|
269
|
+
*/
|
|
270
|
+
existingResponses: ModelMessage[];
|
|
271
|
+
/**
|
|
272
|
+
* The user associated with the generation, if any.
|
|
273
|
+
*/
|
|
274
|
+
userId: string | undefined;
|
|
275
|
+
/**
|
|
276
|
+
* The thread associated with the generation, if any.
|
|
277
|
+
*/
|
|
278
|
+
threadId: string | undefined;
|
|
279
|
+
},
|
|
280
|
+
) => ModelMessage[] | Promise<ModelMessage[]>;
|
|
281
|
+
|
|
156
282
|
export type RawRequestResponseHandler = (
|
|
157
283
|
ctx: ActionCtx,
|
|
158
284
|
args: {
|
|
@@ -262,8 +388,15 @@ export type GenerateObjectArgs<
|
|
|
262
388
|
? Array<InferSchema<SCHEMA>>
|
|
263
389
|
: InferSchema<SCHEMA>,
|
|
264
390
|
> = Omit<CallSettings, "stopSequences"> &
|
|
265
|
-
Prompt &
|
|
266
|
-
|
|
391
|
+
Prompt & {
|
|
392
|
+
/**
|
|
393
|
+
* If provided, this message will be used as the "prompt" for the LLM call,
|
|
394
|
+
* instead of the prompt or messages.
|
|
395
|
+
* This is useful if you want to first save a user message, then use it as
|
|
396
|
+
* the prompt for the LLM call in another call.
|
|
397
|
+
*/
|
|
398
|
+
promptMessageId?: string;
|
|
399
|
+
} & (OUTPUT extends "enum"
|
|
267
400
|
? {
|
|
268
401
|
/**
|
|
269
402
|
The enum values that the model should use.
|
|
@@ -570,6 +703,13 @@ export type Options = {
|
|
|
570
703
|
* set in the agent constructor.
|
|
571
704
|
*/
|
|
572
705
|
usageHandler?: UsageHandler;
|
|
706
|
+
/**
|
|
707
|
+
* By default, messages are ordered with context in `fetchContextWithPrompt`,
|
|
708
|
+
* but you can override this by providing a context handler. Here you can
|
|
709
|
+
* filter, modify, or enrich the context messages. If provided, the default
|
|
710
|
+
* ordering will not apply. This excludes the system message / instructions.
|
|
711
|
+
*/
|
|
712
|
+
contextHandler?: ContextHandler;
|
|
573
713
|
};
|
|
574
714
|
|
|
575
715
|
export type SyncStreamsReturnValue =
|
|
@@ -689,6 +689,12 @@ export type Mounts = {
|
|
|
689
689
|
},
|
|
690
690
|
null
|
|
691
691
|
>;
|
|
692
|
+
getMessageSearchFields: FunctionReference<
|
|
693
|
+
"query",
|
|
694
|
+
"public",
|
|
695
|
+
{ messageId: string },
|
|
696
|
+
{ embedding?: Array<number>; embeddingModel?: string; text?: string }
|
|
697
|
+
>;
|
|
692
698
|
getMessagesByIds: FunctionReference<
|
|
693
699
|
"query",
|
|
694
700
|
"public",
|
|
@@ -1217,15 +1223,17 @@ export type Mounts = {
|
|
|
1217
1223
|
"action",
|
|
1218
1224
|
"public",
|
|
1219
1225
|
{
|
|
1220
|
-
beforeMessageId?: string;
|
|
1221
1226
|
embedding?: Array<number>;
|
|
1222
1227
|
embeddingModel?: string;
|
|
1223
1228
|
limit: number;
|
|
1224
1229
|
messageRange?: { after: number; before: number };
|
|
1225
1230
|
searchAllMessagesForUserId?: string;
|
|
1231
|
+
targetMessageId?: string;
|
|
1226
1232
|
text?: string;
|
|
1233
|
+
textSearch?: boolean;
|
|
1227
1234
|
threadId?: string;
|
|
1228
1235
|
vectorScoreThreshold?: number;
|
|
1236
|
+
vectorSearch?: boolean;
|
|
1229
1237
|
},
|
|
1230
1238
|
Array<{
|
|
1231
1239
|
_creationTime: number;
|
|
@@ -1464,10 +1472,10 @@ export type Mounts = {
|
|
|
1464
1472
|
"query",
|
|
1465
1473
|
"public",
|
|
1466
1474
|
{
|
|
1467
|
-
beforeMessageId?: string;
|
|
1468
1475
|
limit: number;
|
|
1469
1476
|
searchAllMessagesForUserId?: string;
|
|
1470
|
-
|
|
1477
|
+
targetMessageId?: string;
|
|
1478
|
+
text?: string;
|
|
1471
1479
|
threadId?: string;
|
|
1472
1480
|
},
|
|
1473
1481
|
Array<{
|