@convex-dev/agent 0.2.4 → 0.2.5

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.
Files changed (47) hide show
  1. package/dist/client/definePlaygroundAPI.d.ts +85 -0
  2. package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
  3. package/dist/client/index.d.ts +85 -23
  4. package/dist/client/index.d.ts.map +1 -1
  5. package/dist/client/index.js +52 -51
  6. package/dist/client/index.js.map +1 -1
  7. package/dist/client/messages.d.ts +17 -0
  8. package/dist/client/messages.d.ts.map +1 -1
  9. package/dist/client/messages.js +5 -5
  10. package/dist/client/messages.js.map +1 -1
  11. package/dist/client/mockModel.d.ts +30 -0
  12. package/dist/client/mockModel.d.ts.map +1 -0
  13. package/dist/client/mockModel.js +153 -0
  14. package/dist/client/mockModel.js.map +1 -0
  15. package/dist/client/search.d.ts +17 -0
  16. package/dist/client/search.d.ts.map +1 -1
  17. package/dist/client/streaming.d.ts +190 -2
  18. package/dist/client/streaming.d.ts.map +1 -1
  19. package/dist/client/streaming.js +10 -8
  20. package/dist/client/streaming.js.map +1 -1
  21. package/dist/component/_generated/api.d.ts +176 -0
  22. package/dist/component/messages.d.ts +187 -0
  23. package/dist/component/messages.d.ts.map +1 -1
  24. package/dist/component/messages.js +1 -1
  25. package/dist/component/messages.js.map +1 -1
  26. package/dist/component/schema.d.ts +496 -3
  27. package/dist/component/schema.d.ts.map +1 -1
  28. package/dist/mapping.d.ts.map +1 -1
  29. package/dist/mapping.js +5 -0
  30. package/dist/mapping.js.map +1 -1
  31. package/dist/react/toUIMessages.d.ts.map +1 -1
  32. package/dist/react/toUIMessages.js +29 -19
  33. package/dist/react/toUIMessages.js.map +1 -1
  34. package/dist/validators.d.ts +1183 -8
  35. package/dist/validators.d.ts.map +1 -1
  36. package/dist/validators.js +19 -1
  37. package/dist/validators.js.map +1 -1
  38. package/package.json +3 -3
  39. package/src/client/index.ts +91 -82
  40. package/src/client/messages.ts +6 -6
  41. package/src/client/mockModel.ts +195 -0
  42. package/src/client/streaming.ts +8 -7
  43. package/src/component/_generated/api.d.ts +176 -0
  44. package/src/component/messages.ts +1 -1
  45. package/src/mapping.ts +7 -0
  46. package/src/react/toUIMessages.ts +36 -19
  47. package/src/validators.ts +24 -0
@@ -0,0 +1,195 @@
1
+ import type {
2
+ LanguageModelV2,
3
+ LanguageModelV2StreamPart,
4
+ } from "@ai-sdk/provider";
5
+ import type { ReasoningPart, TextPart } from "@ai-sdk/provider-utils";
6
+ import { simulateReadableStream } from "ai";
7
+
8
+ const longDefaultText = `
9
+ A A A A A A A A A A A A A A A
10
+ B B B B B B B B B B B B B B B
11
+ C C C C C C C C C C C C C C C
12
+ D D D D D D D D D D D D D D D
13
+ `;
14
+ const defaultUsage = { outputTokens: 10, inputTokens: 3, totalTokens: 13 };
15
+
16
+ export type MockModelArgs = {
17
+ provider?: LanguageModelV2["provider"];
18
+ modelId?: LanguageModelV2["modelId"];
19
+ supportedUrls?:
20
+ | LanguageModelV2["supportedUrls"]
21
+ | (() => LanguageModelV2["supportedUrls"]);
22
+ chunkDelayInMs?: number;
23
+ initialDelayInMs?: number;
24
+ // provide either content or doGenerate & doStream
25
+ content?: (TextPart | ReasoningPart)[];
26
+ doGenerate?: LanguageModelV2["doGenerate"];
27
+ doStream?: LanguageModelV2["doStream"];
28
+ fail?:
29
+ | boolean
30
+ | {
31
+ probability?: number;
32
+ error?: string;
33
+ };
34
+ };
35
+
36
+ export function mockModel(args?: MockModelArgs): LanguageModelV2 {
37
+ return new MockLanguageModel(args ?? {});
38
+ }
39
+
40
+ export class MockLanguageModel implements LanguageModelV2 {
41
+ readonly specificationVersion = "v2";
42
+
43
+ private _supportedUrls: () => LanguageModelV2["supportedUrls"];
44
+
45
+ readonly provider: LanguageModelV2["provider"];
46
+ readonly modelId: LanguageModelV2["modelId"];
47
+
48
+ doGenerate: LanguageModelV2["doGenerate"];
49
+ doStream: LanguageModelV2["doStream"];
50
+
51
+ doGenerateCalls: Parameters<LanguageModelV2["doGenerate"]>[0][] = [];
52
+ doStreamCalls: Parameters<LanguageModelV2["doStream"]>[0][] = [];
53
+
54
+ constructor(args: MockModelArgs) {
55
+ this.provider = args.provider || "mock-provider";
56
+ this.modelId = args.modelId || "mock-model-id";
57
+ const {
58
+ content = [{ type: "text", text: longDefaultText }],
59
+ chunkDelayInMs = 200,
60
+ initialDelayInMs = 1000,
61
+ supportedUrls = {},
62
+ } = args;
63
+ const fail =
64
+ args.fail &&
65
+ (args.fail === true ||
66
+ !args.fail.probability ||
67
+ Math.random() < args.fail.probability);
68
+ const error =
69
+ (typeof args.fail === "object" && args.fail.error) ||
70
+ "Mock error message";
71
+
72
+ const chunks: LanguageModelV2StreamPart[] = [
73
+ { type: "stream-start", warnings: [] },
74
+ ];
75
+ chunks.push(
76
+ ...content.flatMap((c, ci) => {
77
+ const deltas = c.text.split(" ");
78
+ let parts: LanguageModelV2StreamPart[] = [];
79
+ if (c.type === "reasoning") {
80
+ parts.push({
81
+ type: "reasoning-start",
82
+ id: `${ci}-reasoning-start`,
83
+ });
84
+ parts.push(
85
+ ...deltas.map(
86
+ (delta, di) =>
87
+ ({
88
+ type: "reasoning-delta",
89
+ delta,
90
+ id: `${ci}-reasoning-${di}`,
91
+ providerMetadata: {
92
+ mockProvider: { mock: { reasoningDetails: null } },
93
+ },
94
+ }) satisfies LanguageModelV2StreamPart,
95
+ ),
96
+ );
97
+ parts.push({
98
+ type: "reasoning-end",
99
+ id: `${ci}-reasoning-end`,
100
+ });
101
+ } else if (c.type === "text") {
102
+ parts.push({
103
+ type: "text-start",
104
+ id: `${ci}-text-start`,
105
+ });
106
+ parts = deltas.map((delta, di) => ({
107
+ type: "text-delta",
108
+ delta,
109
+ id: `${ci}-text-${di}`,
110
+ }));
111
+ parts.push({
112
+ type: "text-end",
113
+ id: `${ci}-text-end`,
114
+ });
115
+ }
116
+ return parts;
117
+ }),
118
+ );
119
+ if (fail) {
120
+ chunks.push({
121
+ type: "error",
122
+ error,
123
+ });
124
+ }
125
+ chunks.push({
126
+ type: "finish",
127
+ finishReason: fail ? "error" : "stop",
128
+ usage: defaultUsage,
129
+ providerMetadata: {
130
+ mockProvider: { mock: "mock metadata" },
131
+ },
132
+ });
133
+ this.doGenerate = async (options) => {
134
+ this.doGenerateCalls.push(options);
135
+
136
+ if (fail) {
137
+ throw new Error(error);
138
+ }
139
+ if (typeof args.doGenerate === "function") {
140
+ return args.doGenerate(options);
141
+ } else if (Array.isArray(args.doGenerate)) {
142
+ return args.doGenerate[this.doGenerateCalls.length];
143
+ } else if (content) {
144
+ return {
145
+ content,
146
+ finishReason: "stop",
147
+ usage: defaultUsage,
148
+ providerMetadata: { mockProvider: { mock: "mock metadata" } },
149
+ warnings: [],
150
+ };
151
+ } else {
152
+ throw new Error("Unexpected: no content or doGenerate");
153
+ }
154
+ };
155
+ this._supportedUrls =
156
+ typeof supportedUrls === "function"
157
+ ? supportedUrls
158
+ : async () => supportedUrls;
159
+ this.doStream = async (options) => {
160
+ this.doStreamCalls.push(options);
161
+
162
+ if (typeof args.doStream === "function") {
163
+ return args.doStream(options);
164
+ } else if (Array.isArray(args.doStream)) {
165
+ return args.doStream[this.doStreamCalls.length];
166
+ } else if (content) {
167
+ const stream = simulateReadableStream({
168
+ chunks,
169
+ initialDelayInMs,
170
+ chunkDelayInMs,
171
+ });
172
+ if (options.abortSignal) {
173
+ throw new Error("abortSignal in mock model");
174
+ }
175
+ return {
176
+ stream,
177
+ request: { body: {} },
178
+ response: { headers: {} },
179
+ };
180
+ } else if (args.doStream) {
181
+ return args.doStream;
182
+ } else {
183
+ throw new Error("Provide either content or doStream");
184
+ }
185
+ };
186
+ this._supportedUrls =
187
+ typeof supportedUrls === "function"
188
+ ? supportedUrls
189
+ : async () => supportedUrls;
190
+ }
191
+
192
+ get supportedUrls() {
193
+ return this._supportedUrls();
194
+ }
195
+ }
@@ -197,6 +197,7 @@ export class DeltaStreamer {
197
197
  public readonly component: AgentComponent,
198
198
  public readonly ctx: RunActionCtx,
199
199
  options: true | StreamingOptions,
200
+ private onAsyncAbort: (reason: string) => Promise<void>,
200
201
  public readonly metadata: {
201
202
  threadId: string;
202
203
  userId?: string;
@@ -210,21 +211,22 @@ export class DeltaStreamer {
210
211
  },
211
212
  ) {
212
213
  this.options =
213
- typeof options === "boolean"
214
+ options === true
214
215
  ? DEFAULT_STREAMING_OPTIONS
215
216
  : { ...DEFAULT_STREAMING_OPTIONS, ...options };
216
217
  this.#nextParts = [];
217
218
  this.abortController = new AbortController();
218
219
  if (metadata.abortSignal) {
219
220
  metadata.abortSignal.addEventListener("abort", async () => {
221
+ if (this.abortController.signal.aborted) {
222
+ return;
223
+ }
220
224
  if (this.streamId) {
221
225
  this.abortController.abort();
222
- const finalDelta = this.#createDelta();
223
226
  await this.#ongoingWrite;
224
227
  await this.ctx.runMutation(this.component.streams.abort, {
225
228
  streamId: this.streamId,
226
229
  reason: "abortSignal",
227
- finalDelta,
228
230
  });
229
231
  }
230
232
  });
@@ -265,9 +267,12 @@ export class DeltaStreamer {
265
267
  delta,
266
268
  );
267
269
  if (!success) {
270
+ await this.onAsyncAbort("async abort");
268
271
  this.abortController.abort();
272
+ return;
269
273
  }
270
274
  } catch (e) {
275
+ await this.onAsyncAbort(e instanceof Error ? e.message : "unknown error");
271
276
  this.abortController.abort();
272
277
  throw e;
273
278
  }
@@ -302,11 +307,9 @@ export class DeltaStreamer {
302
307
  if (!this.streamId) {
303
308
  return;
304
309
  }
305
- const finalDelta = this.#createDelta();
306
310
  await this.#ongoingWrite;
307
311
  await this.ctx.runMutation(this.component.streams.finish, {
308
312
  streamId: this.streamId,
309
- finalDelta,
310
313
  });
311
314
  }
312
315
 
@@ -318,12 +321,10 @@ export class DeltaStreamer {
318
321
  if (!this.streamId) {
319
322
  return;
320
323
  }
321
- const finalDelta = this.#createDelta();
322
324
  await this.#ongoingWrite;
323
325
  await this.ctx.runMutation(this.component.streams.abort, {
324
326
  streamId: this.streamId,
325
327
  reason,
326
- finalDelta,
327
328
  });
328
329
  }
329
330
  }
@@ -260,6 +260,31 @@ export type Mounts = {
260
260
  toolName: string;
261
261
  type: "tool-result";
262
262
  }
263
+ | {
264
+ id: string;
265
+ providerMetadata?: Record<
266
+ string,
267
+ Record<string, any>
268
+ >;
269
+ providerOptions?: Record<string, Record<string, any>>;
270
+ sourceType: "url";
271
+ title: string;
272
+ type: "source";
273
+ url: string;
274
+ }
275
+ | {
276
+ filename?: string;
277
+ id: string;
278
+ mediaType: string;
279
+ providerMetadata?: Record<
280
+ string,
281
+ Record<string, any>
282
+ >;
283
+ providerOptions?: Record<string, Record<string, any>>;
284
+ sourceType: "document";
285
+ title: string;
286
+ type: "source";
287
+ }
263
288
  >;
264
289
  providerOptions?: Record<string, Record<string, any>>;
265
290
  role: "assistant";
@@ -469,6 +494,31 @@ export type Mounts = {
469
494
  toolName: string;
470
495
  type: "tool-result";
471
496
  }
497
+ | {
498
+ id: string;
499
+ providerMetadata?: Record<
500
+ string,
501
+ Record<string, any>
502
+ >;
503
+ providerOptions?: Record<string, Record<string, any>>;
504
+ sourceType: "url";
505
+ title: string;
506
+ type: "source";
507
+ url: string;
508
+ }
509
+ | {
510
+ filename?: string;
511
+ id: string;
512
+ mediaType: string;
513
+ providerMetadata?: Record<
514
+ string,
515
+ Record<string, any>
516
+ >;
517
+ providerOptions?: Record<string, Record<string, any>>;
518
+ sourceType: "document";
519
+ title: string;
520
+ type: "source";
521
+ }
472
522
  >;
473
523
  providerOptions?: Record<string, Record<string, any>>;
474
524
  role: "assistant";
@@ -687,6 +737,25 @@ export type Mounts = {
687
737
  toolName: string;
688
738
  type: "tool-result";
689
739
  }
740
+ | {
741
+ id: string;
742
+ providerMetadata?: Record<string, Record<string, any>>;
743
+ providerOptions?: Record<string, Record<string, any>>;
744
+ sourceType: "url";
745
+ title: string;
746
+ type: "source";
747
+ url: string;
748
+ }
749
+ | {
750
+ filename?: string;
751
+ id: string;
752
+ mediaType: string;
753
+ providerMetadata?: Record<string, Record<string, any>>;
754
+ providerOptions?: Record<string, Record<string, any>>;
755
+ sourceType: "document";
756
+ title: string;
757
+ type: "source";
758
+ }
690
759
  >;
691
760
  providerOptions?: Record<string, Record<string, any>>;
692
761
  role: "assistant";
@@ -918,6 +987,31 @@ export type Mounts = {
918
987
  toolName: string;
919
988
  type: "tool-result";
920
989
  }
990
+ | {
991
+ id: string;
992
+ providerMetadata?: Record<
993
+ string,
994
+ Record<string, any>
995
+ >;
996
+ providerOptions?: Record<string, Record<string, any>>;
997
+ sourceType: "url";
998
+ title: string;
999
+ type: "source";
1000
+ url: string;
1001
+ }
1002
+ | {
1003
+ filename?: string;
1004
+ id: string;
1005
+ mediaType: string;
1006
+ providerMetadata?: Record<
1007
+ string,
1008
+ Record<string, any>
1009
+ >;
1010
+ providerOptions?: Record<string, Record<string, any>>;
1011
+ sourceType: "document";
1012
+ title: string;
1013
+ type: "source";
1014
+ }
921
1015
  >;
922
1016
  providerOptions?: Record<string, Record<string, any>>;
923
1017
  role: "assistant";
@@ -1121,6 +1215,25 @@ export type Mounts = {
1121
1215
  toolName: string;
1122
1216
  type: "tool-result";
1123
1217
  }
1218
+ | {
1219
+ id: string;
1220
+ providerMetadata?: Record<string, Record<string, any>>;
1221
+ providerOptions?: Record<string, Record<string, any>>;
1222
+ sourceType: "url";
1223
+ title: string;
1224
+ type: "source";
1225
+ url: string;
1226
+ }
1227
+ | {
1228
+ filename?: string;
1229
+ id: string;
1230
+ mediaType: string;
1231
+ providerMetadata?: Record<string, Record<string, any>>;
1232
+ providerOptions?: Record<string, Record<string, any>>;
1233
+ sourceType: "document";
1234
+ title: string;
1235
+ type: "source";
1236
+ }
1124
1237
  >;
1125
1238
  providerOptions?: Record<string, Record<string, any>>;
1126
1239
  role: "assistant";
@@ -1317,6 +1430,25 @@ export type Mounts = {
1317
1430
  toolName: string;
1318
1431
  type: "tool-result";
1319
1432
  }
1433
+ | {
1434
+ id: string;
1435
+ providerMetadata?: Record<string, Record<string, any>>;
1436
+ providerOptions?: Record<string, Record<string, any>>;
1437
+ sourceType: "url";
1438
+ title: string;
1439
+ type: "source";
1440
+ url: string;
1441
+ }
1442
+ | {
1443
+ filename?: string;
1444
+ id: string;
1445
+ mediaType: string;
1446
+ providerMetadata?: Record<string, Record<string, any>>;
1447
+ providerOptions?: Record<string, Record<string, any>>;
1448
+ sourceType: "document";
1449
+ title: string;
1450
+ type: "source";
1451
+ }
1320
1452
  >;
1321
1453
  providerOptions?: Record<string, Record<string, any>>;
1322
1454
  role: "assistant";
@@ -1527,6 +1659,31 @@ export type Mounts = {
1527
1659
  toolName: string;
1528
1660
  type: "tool-result";
1529
1661
  }
1662
+ | {
1663
+ id: string;
1664
+ providerMetadata?: Record<
1665
+ string,
1666
+ Record<string, any>
1667
+ >;
1668
+ providerOptions?: Record<string, Record<string, any>>;
1669
+ sourceType: "url";
1670
+ title: string;
1671
+ type: "source";
1672
+ url: string;
1673
+ }
1674
+ | {
1675
+ filename?: string;
1676
+ id: string;
1677
+ mediaType: string;
1678
+ providerMetadata?: Record<
1679
+ string,
1680
+ Record<string, any>
1681
+ >;
1682
+ providerOptions?: Record<string, Record<string, any>>;
1683
+ sourceType: "document";
1684
+ title: string;
1685
+ type: "source";
1686
+ }
1530
1687
  >;
1531
1688
  providerOptions?: Record<string, Record<string, any>>;
1532
1689
  role: "assistant";
@@ -1661,6 +1818,25 @@ export type Mounts = {
1661
1818
  toolName: string;
1662
1819
  type: "tool-result";
1663
1820
  }
1821
+ | {
1822
+ id: string;
1823
+ providerMetadata?: Record<string, Record<string, any>>;
1824
+ providerOptions?: Record<string, Record<string, any>>;
1825
+ sourceType: "url";
1826
+ title: string;
1827
+ type: "source";
1828
+ url: string;
1829
+ }
1830
+ | {
1831
+ filename?: string;
1832
+ id: string;
1833
+ mediaType: string;
1834
+ providerMetadata?: Record<string, Record<string, any>>;
1835
+ providerOptions?: Record<string, Record<string, any>>;
1836
+ sourceType: "document";
1837
+ title: string;
1838
+ type: "source";
1839
+ }
1664
1840
  >;
1665
1841
  providerOptions?: Record<string, Record<string, any>>;
1666
1842
  role: "assistant";
@@ -351,7 +351,7 @@ export const finalizeMessage = mutation({
351
351
  return;
352
352
  }
353
353
  // See if we can add any in-progress data
354
- if (message.message === undefined) {
354
+ if (!message.message?.content.length) {
355
355
  const messages = await getStreamingMessagesWithMetadata(
356
356
  ctx,
357
357
  message,
package/src/mapping.ts CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  } from "ai";
21
21
  import {
22
22
  vMessageWithMetadata,
23
+ type vSourcePart,
23
24
  type Message,
24
25
  type MessageWithMetadata,
25
26
  type Usage,
@@ -31,6 +32,7 @@ import {
31
32
  type vTextPart,
32
33
  type vToolCallPart,
33
34
  type vToolResultPart,
35
+ type SourcePart,
34
36
  } from "./validators.js";
35
37
  import type { ActionCtx, AgentComponent } from "./client/types.js";
36
38
  import type { RunMutationCtx } from "./client/types.js";
@@ -335,6 +337,9 @@ export async function serializeContent(
335
337
  ...metadata,
336
338
  } satisfies Infer<typeof vRedactedReasoningPart>;
337
339
  }
340
+ case "source": {
341
+ return part satisfies Infer<typeof vSourcePart>;
342
+ }
338
343
  default:
339
344
  return part satisfies Infer<typeof vContent>;
340
345
  }
@@ -431,6 +436,8 @@ export function deserializeContent(
431
436
  }
432
437
  : undefined,
433
438
  } satisfies ReasoningPart;
439
+ case "source":
440
+ return part satisfies SourcePart;
434
441
  default:
435
442
  return part satisfies Content;
436
443
  }
@@ -12,8 +12,9 @@ import type {
12
12
  } from "ai";
13
13
  import { extractText, type MessageDoc } from "../client/index.js";
14
14
  import { deserializeMessage, toUIFilePart } from "../mapping.js";
15
- import type { MessageStatus } from "../validators.js";
15
+ import type { MessageStatus, SourcePart, vSource } from "../validators.js";
16
16
  import { sorted } from "../shared.js";
17
+ import type { Infer } from "convex/values";
17
18
 
18
19
  export type UIMessage<
19
20
  METADATA = unknown,
@@ -388,29 +389,23 @@ function createAssistantUIMessage<
388
389
  }
389
390
  break;
390
391
  }
392
+ default: {
393
+ const maybeSource = contentPart as SourcePart;
394
+ if (maybeSource.type === "source") {
395
+ allParts.push(toSourcePart(maybeSource));
396
+ } else {
397
+ console.warn(
398
+ "Unknown content part type for assistant",
399
+ contentPart,
400
+ );
401
+ }
402
+ }
391
403
  }
392
404
  }
393
405
 
394
406
  // Add source parts
395
407
  for (const source of message.sources ?? []) {
396
- if (source.sourceType === "url") {
397
- allParts.push({
398
- type: "source-url",
399
- url: source.url!,
400
- sourceId: source.id,
401
- providerMetadata: message.providerMetadata,
402
- title: source.title,
403
- } satisfies SourceUrlUIPart);
404
- } else {
405
- allParts.push({
406
- type: "source-document",
407
- mediaType: source.mediaType,
408
- sourceId: source.id,
409
- title: source.title,
410
- filename: source.filename,
411
- providerMetadata: message.providerMetadata,
412
- } satisfies SourceDocumentUIPart);
413
- }
408
+ allParts.push(toSourcePart(source));
414
409
  }
415
410
  }
416
411
 
@@ -422,3 +417,25 @@ function createAssistantUIMessage<
422
417
  parts: allParts,
423
418
  };
424
419
  }
420
+
421
+ function toSourcePart(
422
+ part: SourcePart | Infer<typeof vSource>,
423
+ ): SourceUrlUIPart | SourceDocumentUIPart {
424
+ if (part.sourceType === "url") {
425
+ return {
426
+ type: "source-url",
427
+ url: part.url,
428
+ sourceId: part.id,
429
+ providerMetadata: part.providerMetadata,
430
+ title: part.title,
431
+ } satisfies SourceUrlUIPart;
432
+ }
433
+ return {
434
+ type: "source-document",
435
+ mediaType: part.mediaType,
436
+ sourceId: part.id,
437
+ title: part.title,
438
+ filename: part.filename,
439
+ providerMetadata: part.providerMetadata,
440
+ } satisfies SourceDocumentUIPart;
441
+ }
package/src/validators.ts CHANGED
@@ -87,6 +87,29 @@ export const vReasoningDetails = v.array(
87
87
  ),
88
88
  );
89
89
 
90
+ export const vSourcePart = v.union(
91
+ v.object({
92
+ type: v.literal("source"),
93
+ sourceType: v.literal("url"),
94
+ id: v.string(),
95
+ url: v.string(),
96
+ title: v.string(),
97
+ providerOptions,
98
+ providerMetadata,
99
+ }),
100
+ v.object({
101
+ type: v.literal("source"),
102
+ sourceType: v.literal("document"),
103
+ id: v.string(),
104
+ mediaType: v.string(),
105
+ title: v.string(),
106
+ filename: v.optional(v.string()),
107
+ providerOptions,
108
+ providerMetadata,
109
+ }),
110
+ );
111
+ export type SourcePart = Infer<typeof vSourcePart>;
112
+
90
113
  export const vToolCallPart = v.object({
91
114
  type: v.literal("tool-call"),
92
115
  toolCallId: v.string(),
@@ -136,6 +159,7 @@ export const vAssistantContent = v.union(
136
159
  vRedactedReasoningPart,
137
160
  vToolCallPart,
138
161
  vToolResultPart,
162
+ vSourcePart,
139
163
  ),
140
164
  ),
141
165
  );