@convex-dev/agent 0.1.4-alpha.0 → 0.1.5-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.
Files changed (165) hide show
  1. package/README.md +24 -35
  2. package/dist/commonjs/client/createTool.d.ts +3 -5
  3. package/dist/commonjs/client/createTool.d.ts.map +1 -1
  4. package/dist/commonjs/client/createTool.js +1 -0
  5. package/dist/commonjs/client/createTool.js.map +1 -1
  6. package/dist/commonjs/client/index.d.ts +6 -6
  7. package/dist/commonjs/client/index.d.ts.map +1 -1
  8. package/dist/commonjs/client/index.js.map +1 -1
  9. package/dist/commonjs/client/streaming.d.ts +5 -5
  10. package/dist/commonjs/client/streaming.d.ts.map +1 -1
  11. package/dist/commonjs/client/streaming.js +3 -2
  12. package/dist/commonjs/client/streaming.js.map +1 -1
  13. package/dist/commonjs/component/files.d.ts +2 -2
  14. package/dist/commonjs/component/files.d.ts.map +1 -1
  15. package/dist/commonjs/component/files.js.map +1 -1
  16. package/dist/commonjs/component/messages.d.ts +24 -24
  17. package/dist/commonjs/component/messages.d.ts.map +1 -1
  18. package/dist/commonjs/component/messages.js +4 -5
  19. package/dist/commonjs/component/messages.js.map +1 -1
  20. package/dist/commonjs/component/schema.d.ts +45 -45
  21. package/dist/commonjs/component/schema.d.ts.map +1 -1
  22. package/dist/commonjs/component/schema.js.map +1 -1
  23. package/dist/commonjs/component/streams.d.ts.map +1 -1
  24. package/dist/commonjs/component/streams.js +4 -7
  25. package/dist/commonjs/component/streams.js.map +1 -1
  26. package/dist/commonjs/component/threads.d.ts +8 -8
  27. package/dist/commonjs/component/threads.js +3 -3
  28. package/dist/commonjs/component/threads.js.map +1 -1
  29. package/dist/commonjs/component/users.js +4 -4
  30. package/dist/commonjs/component/users.js.map +1 -1
  31. package/dist/commonjs/component/vector/index.d.ts +3 -3
  32. package/dist/commonjs/component/vector/index.d.ts.map +1 -1
  33. package/dist/commonjs/component/vector/index.js +4 -5
  34. package/dist/commonjs/component/vector/index.js.map +1 -1
  35. package/dist/commonjs/component/vector/tables.d.ts +5 -7
  36. package/dist/commonjs/component/vector/tables.d.ts.map +1 -1
  37. package/dist/commonjs/mapping.d.ts +2 -2
  38. package/dist/commonjs/mapping.d.ts.map +1 -1
  39. package/dist/commonjs/mapping.js.map +1 -1
  40. package/dist/commonjs/react/deltas.d.ts +29 -0
  41. package/dist/commonjs/react/deltas.d.ts.map +1 -0
  42. package/dist/commonjs/react/deltas.js +267 -0
  43. package/dist/commonjs/react/deltas.js.map +1 -0
  44. package/dist/commonjs/react/index.d.ts +4 -49
  45. package/dist/commonjs/react/index.d.ts.map +1 -1
  46. package/dist/commonjs/react/index.js +15 -446
  47. package/dist/commonjs/react/index.js.map +1 -1
  48. package/dist/commonjs/react/optimisticallySendMessage.d.ts +8 -0
  49. package/dist/commonjs/react/optimisticallySendMessage.d.ts.map +1 -0
  50. package/dist/commonjs/react/optimisticallySendMessage.js +40 -0
  51. package/dist/commonjs/react/optimisticallySendMessage.js.map +1 -0
  52. package/dist/commonjs/react/toUIMessages.d.ts +3 -3
  53. package/dist/commonjs/react/toUIMessages.d.ts.map +1 -1
  54. package/dist/commonjs/react/toUIMessages.js +4 -2
  55. package/dist/commonjs/react/toUIMessages.js.map +1 -1
  56. package/dist/commonjs/react/types.d.ts +26 -0
  57. package/dist/commonjs/react/types.d.ts.map +1 -0
  58. package/dist/commonjs/react/types.js +2 -0
  59. package/dist/commonjs/react/types.js.map +1 -0
  60. package/dist/commonjs/react/useSmoothText.d.ts +20 -0
  61. package/dist/commonjs/react/useSmoothText.d.ts.map +1 -0
  62. package/dist/commonjs/react/useSmoothText.js +50 -0
  63. package/dist/commonjs/react/useSmoothText.js.map +1 -0
  64. package/dist/commonjs/shared.d.ts +2 -2
  65. package/dist/commonjs/shared.d.ts.map +1 -1
  66. package/dist/commonjs/validators.d.ts +1 -1
  67. package/dist/commonjs/validators.d.ts.map +1 -1
  68. package/dist/commonjs/validators.js +2 -2
  69. package/dist/commonjs/validators.js.map +1 -1
  70. package/dist/commonjs.tsbuildinfo +1 -0
  71. package/dist/esm/client/createTool.d.ts +3 -5
  72. package/dist/esm/client/createTool.d.ts.map +1 -1
  73. package/dist/esm/client/createTool.js +1 -0
  74. package/dist/esm/client/createTool.js.map +1 -1
  75. package/dist/esm/client/index.d.ts +6 -6
  76. package/dist/esm/client/index.d.ts.map +1 -1
  77. package/dist/esm/client/index.js.map +1 -1
  78. package/dist/esm/client/streaming.d.ts +5 -5
  79. package/dist/esm/client/streaming.d.ts.map +1 -1
  80. package/dist/esm/client/streaming.js +3 -2
  81. package/dist/esm/client/streaming.js.map +1 -1
  82. package/dist/esm/component/files.d.ts +2 -2
  83. package/dist/esm/component/files.d.ts.map +1 -1
  84. package/dist/esm/component/files.js.map +1 -1
  85. package/dist/esm/component/messages.d.ts +24 -24
  86. package/dist/esm/component/messages.d.ts.map +1 -1
  87. package/dist/esm/component/messages.js +4 -5
  88. package/dist/esm/component/messages.js.map +1 -1
  89. package/dist/esm/component/schema.d.ts +45 -45
  90. package/dist/esm/component/schema.d.ts.map +1 -1
  91. package/dist/esm/component/schema.js.map +1 -1
  92. package/dist/esm/component/streams.d.ts.map +1 -1
  93. package/dist/esm/component/streams.js +4 -7
  94. package/dist/esm/component/streams.js.map +1 -1
  95. package/dist/esm/component/threads.d.ts +8 -8
  96. package/dist/esm/component/threads.js +3 -3
  97. package/dist/esm/component/threads.js.map +1 -1
  98. package/dist/esm/component/users.js +4 -4
  99. package/dist/esm/component/users.js.map +1 -1
  100. package/dist/esm/component/vector/index.d.ts +3 -3
  101. package/dist/esm/component/vector/index.d.ts.map +1 -1
  102. package/dist/esm/component/vector/index.js +4 -5
  103. package/dist/esm/component/vector/index.js.map +1 -1
  104. package/dist/esm/component/vector/tables.d.ts +5 -7
  105. package/dist/esm/component/vector/tables.d.ts.map +1 -1
  106. package/dist/esm/mapping.d.ts +2 -2
  107. package/dist/esm/mapping.d.ts.map +1 -1
  108. package/dist/esm/mapping.js.map +1 -1
  109. package/dist/esm/react/deltas.d.ts +29 -0
  110. package/dist/esm/react/deltas.d.ts.map +1 -0
  111. package/dist/esm/react/deltas.js +267 -0
  112. package/dist/esm/react/deltas.js.map +1 -0
  113. package/dist/esm/react/index.d.ts +4 -49
  114. package/dist/esm/react/index.d.ts.map +1 -1
  115. package/dist/esm/react/index.js +15 -446
  116. package/dist/esm/react/index.js.map +1 -1
  117. package/dist/esm/react/optimisticallySendMessage.d.ts +8 -0
  118. package/dist/esm/react/optimisticallySendMessage.d.ts.map +1 -0
  119. package/dist/esm/react/optimisticallySendMessage.js +40 -0
  120. package/dist/esm/react/optimisticallySendMessage.js.map +1 -0
  121. package/dist/esm/react/toUIMessages.d.ts +3 -3
  122. package/dist/esm/react/toUIMessages.d.ts.map +1 -1
  123. package/dist/esm/react/toUIMessages.js +4 -2
  124. package/dist/esm/react/toUIMessages.js.map +1 -1
  125. package/dist/esm/react/types.d.ts +26 -0
  126. package/dist/esm/react/types.d.ts.map +1 -0
  127. package/dist/esm/react/types.js +2 -0
  128. package/dist/esm/react/types.js.map +1 -0
  129. package/dist/esm/react/useSmoothText.d.ts +20 -0
  130. package/dist/esm/react/useSmoothText.d.ts.map +1 -0
  131. package/dist/esm/react/useSmoothText.js +50 -0
  132. package/dist/esm/react/useSmoothText.js.map +1 -0
  133. package/dist/esm/shared.d.ts +2 -2
  134. package/dist/esm/shared.d.ts.map +1 -1
  135. package/dist/esm/validators.d.ts +1 -1
  136. package/dist/esm/validators.d.ts.map +1 -1
  137. package/dist/esm/validators.js +2 -2
  138. package/dist/esm/validators.js.map +1 -1
  139. package/dist/esm.tsbuildinfo +1 -0
  140. package/package.json +5 -1
  141. package/src/client/createTool.ts +3 -5
  142. package/src/client/index.test.ts +1 -1
  143. package/src/client/index.ts +9 -10
  144. package/src/client/streaming.ts +15 -5
  145. package/src/component/files.ts +2 -2
  146. package/src/component/messages.test.ts +4 -4
  147. package/src/component/messages.ts +15 -15
  148. package/src/component/schema.ts +1 -1
  149. package/src/component/streams.ts +8 -9
  150. package/src/component/threads.ts +6 -6
  151. package/src/component/users.ts +7 -7
  152. package/src/component/vector/index.ts +12 -8
  153. package/src/component/vector/tables.ts +10 -10
  154. package/src/mapping.ts +4 -4
  155. package/src/react/deltas.test.ts +298 -0
  156. package/src/react/deltas.ts +340 -0
  157. package/src/react/index.ts +26 -589
  158. package/src/react/optimisticallySendMessage.ts +46 -0
  159. package/src/react/toUIMessages.test.ts +301 -0
  160. package/src/react/toUIMessages.ts +9 -5
  161. package/src/react/types.ts +52 -0
  162. package/src/react/useSmoothText.ts +76 -0
  163. package/src/shared.ts +2 -2
  164. package/src/validators.test.ts +22 -22
  165. package/src/validators.ts +8 -2
@@ -0,0 +1,340 @@
1
+ import type { TextPart, ToolCallPart, ToolResultPart } from "ai";
2
+ import type { MessageDoc } from "../client";
3
+ import type {
4
+ Message,
5
+ StreamDelta,
6
+ StreamMessage,
7
+ TextStreamPart,
8
+ } from "../validators";
9
+ import type { UIMessage } from "./toUIMessages";
10
+ import { toUIMessages } from "./toUIMessages";
11
+
12
+ export { toUIMessages, type UIMessage };
13
+
14
+ export function mergeDeltas(
15
+ threadId: string,
16
+ streamMessages: StreamMessage[],
17
+ existingStreams: Array<{
18
+ streamId: string;
19
+ cursor: number;
20
+ messages: MessageDoc[];
21
+ }>,
22
+ allDeltas: StreamDelta[]
23
+ ): [
24
+ MessageDoc[],
25
+ Array<{ streamId: string; cursor: number; messages: MessageDoc[] }>,
26
+ boolean,
27
+ ] {
28
+ const newStreams: Array<{
29
+ streamId: string;
30
+ cursor: number;
31
+ messages: MessageDoc[];
32
+ }> = [];
33
+ // Seed the existing chunks
34
+ let changed = false;
35
+ for (const streamMessage of streamMessages) {
36
+ const deltas = allDeltas.filter(
37
+ (d) => d.streamId === streamMessage.streamId
38
+ );
39
+ const existing = existingStreams.find(
40
+ (s) => s.streamId === streamMessage.streamId
41
+ );
42
+ const [newStream, messageChanged] = applyDeltasToStreamMessage(
43
+ threadId,
44
+ streamMessage,
45
+ existing,
46
+ deltas
47
+ );
48
+ newStreams.push(newStream);
49
+ if (messageChanged) changed = true;
50
+ }
51
+ for (const { streamId } of existingStreams) {
52
+ if (!newStreams.find((s) => s.streamId === streamId)) {
53
+ // There's a stream that's no longer active.
54
+ changed = true;
55
+ }
56
+ }
57
+ const messages = newStreams
58
+ .map((s) => s.messages)
59
+ .flat()
60
+ .sort((a, b) => a.order - b.order || a.stepOrder - b.stepOrder);
61
+ return [messages, newStreams, changed];
62
+ }
63
+
64
+ // exported for testing
65
+ export function applyDeltasToStreamMessage(
66
+ threadId: string,
67
+ streamMessage: StreamMessage,
68
+ existing:
69
+ | { streamId: string; cursor: number; messages: MessageDoc[] }
70
+ | undefined,
71
+ deltas: StreamDelta[]
72
+ ): [{ streamId: string; cursor: number; messages: MessageDoc[] }, boolean] {
73
+ let changed = false;
74
+ let cursor = existing?.cursor ?? 0;
75
+ let parts: TextStreamPart[] = [];
76
+ for (const delta of deltas.sort((a, b) => a.start - b.start)) {
77
+ if (delta.parts.length === 0) {
78
+ console.warn(`Got delta with no parts: ${JSON.stringify(delta)}`);
79
+ continue;
80
+ }
81
+ if (cursor !== delta.start) {
82
+ if (cursor >= delta.end) {
83
+ console.debug(
84
+ `Got duplicate delta for stream ${delta.streamId} at ${delta.start}`
85
+ );
86
+ continue;
87
+ } else if (cursor < delta.start) {
88
+ console.warn(
89
+ `Got delta for stream ${delta.streamId} that has a gap ${cursor} -> ${delta.start}`
90
+ );
91
+ continue;
92
+ } else {
93
+ throw new Error(
94
+ `Got unexpected delta for stream ${delta.streamId}: delta: ${delta.start} -> ${delta.end} existing cursor: ${cursor}`
95
+ );
96
+ }
97
+ }
98
+ changed = true;
99
+ cursor = delta.end;
100
+ parts.push(...delta.parts);
101
+ }
102
+ if (!changed) {
103
+ return [
104
+ existing ?? { streamId: streamMessage.streamId, cursor, messages: [] },
105
+ false,
106
+ ];
107
+ }
108
+
109
+ const existingMessages = existing?.messages ?? [];
110
+
111
+ let currentMessage: MessageDoc;
112
+ if (existingMessages.length > 0) {
113
+ // replace the last message with a new one
114
+ const lastMessage = existingMessages.at(-1)!;
115
+ currentMessage = {
116
+ ...lastMessage,
117
+ message: cloneMessageAndContent(lastMessage.message),
118
+ };
119
+ } else {
120
+ const newMessage = createStreamingMessage(
121
+ threadId,
122
+ streamMessage,
123
+ parts[0]!,
124
+ existingMessages.length
125
+ );
126
+ parts = parts.slice(1);
127
+ currentMessage = newMessage;
128
+ }
129
+ const newStream = {
130
+ streamId: streamMessage.streamId,
131
+ cursor,
132
+ messages: [...existingMessages.slice(0, -1), currentMessage],
133
+ };
134
+ let lastContent = getLastContent(currentMessage);
135
+ for (const part of parts) {
136
+ let contentToAdd:
137
+ | TextPart
138
+ | ToolCallPart
139
+ | { type: "reasoning"; text: string }
140
+ | ToolResultPart
141
+ | undefined;
142
+ const isToolRole = part.type === "source" || part.type === "tool-result";
143
+ if (isToolRole !== (currentMessage.message!.role === "tool")) {
144
+ currentMessage = createStreamingMessage(
145
+ threadId,
146
+ streamMessage,
147
+ part,
148
+ newStream.messages.length
149
+ );
150
+ lastContent = getLastContent(currentMessage);
151
+ newStream.messages.push(currentMessage);
152
+ continue;
153
+ }
154
+ switch (part.type) {
155
+ case "text-delta":
156
+ currentMessage.text += part.textDelta;
157
+ if (lastContent?.type === "text") {
158
+ lastContent.text += part.textDelta;
159
+ } else {
160
+ contentToAdd = {
161
+ type: "text",
162
+ text: part.textDelta,
163
+ };
164
+ }
165
+ break;
166
+ case "tool-call-streaming-start":
167
+ currentMessage.tool = true;
168
+ contentToAdd = {
169
+ type: "tool-call",
170
+ toolCallId: part.toolCallId,
171
+ toolName: part.toolName,
172
+ args: "",
173
+ };
174
+ break;
175
+ case "tool-call-delta":
176
+ {
177
+ currentMessage.tool = true;
178
+ if (lastContent?.type !== "tool-call") {
179
+ throw new Error("Expected last content to be a tool call");
180
+ }
181
+ if (typeof lastContent.args !== "string") {
182
+ throw new Error("Expected args to be a string");
183
+ }
184
+ lastContent.args += part.argsTextDelta;
185
+ }
186
+ break;
187
+ case "tool-call":
188
+ currentMessage.tool = true;
189
+ contentToAdd = part;
190
+ break;
191
+ case "reasoning":
192
+ if (lastContent?.type === "reasoning") {
193
+ lastContent.text += part.textDelta;
194
+ } else {
195
+ contentToAdd = {
196
+ type: "reasoning",
197
+ text: part.textDelta,
198
+ };
199
+ }
200
+ break;
201
+ case "source":
202
+ if (!currentMessage.sources) {
203
+ currentMessage.sources = [];
204
+ }
205
+ currentMessage.sources.push(part.source);
206
+ break;
207
+ case "tool-result":
208
+ contentToAdd = part;
209
+ break;
210
+ default:
211
+ console.warn(`Received unexpected part: ${JSON.stringify(part)}`);
212
+ break;
213
+ }
214
+ if (contentToAdd) {
215
+ if (!currentMessage.message!.content) {
216
+ currentMessage.message!.content = [];
217
+ }
218
+ if (!Array.isArray(currentMessage.message?.content)) {
219
+ throw new Error("Expected message content to be an array");
220
+ }
221
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
222
+ currentMessage.message.content.push(contentToAdd as any);
223
+ lastContent = contentToAdd;
224
+ }
225
+ }
226
+ return [newStream, true];
227
+ }
228
+
229
+ function cloneMessageAndContent(
230
+ message: Message | undefined
231
+ ): Message | undefined {
232
+ return (
233
+ message &&
234
+ ({
235
+ ...message,
236
+ content: Array.isArray(message.content)
237
+ ? [...message.content]
238
+ : message.content,
239
+ } as typeof message)
240
+ );
241
+ }
242
+
243
+ function getLastContent(message: MessageDoc) {
244
+ if (Array.isArray(message.message?.content)) {
245
+ return message.message.content.at(-1);
246
+ }
247
+ return undefined;
248
+ }
249
+
250
+ export function createStreamingMessage(
251
+ threadId: string,
252
+ message: StreamMessage,
253
+ part: TextStreamPart,
254
+ index: number
255
+ ): MessageDoc {
256
+ const { streamId, ...rest } = message;
257
+ const metadata: MessageDoc = {
258
+ _id: `${streamId}-${index}`,
259
+ _creationTime: Date.now(),
260
+ status: "pending",
261
+ threadId,
262
+ tool: false,
263
+ ...rest,
264
+ };
265
+ switch (part.type) {
266
+ case "text-delta":
267
+ return {
268
+ ...metadata,
269
+ message: {
270
+ role: "assistant",
271
+ content: [{ type: "text", text: part.textDelta }],
272
+ },
273
+ text: part.textDelta,
274
+ };
275
+ case "tool-call-streaming-start":
276
+ return {
277
+ ...metadata,
278
+ tool: true,
279
+ message: {
280
+ role: "assistant",
281
+ content: [
282
+ {
283
+ type: "tool-call",
284
+ toolName: part.toolName,
285
+ toolCallId: part.toolCallId,
286
+ args: "", // when it's a string, it's a partial call
287
+ },
288
+ ],
289
+ },
290
+ };
291
+ case "reasoning":
292
+ return {
293
+ ...metadata,
294
+ message: {
295
+ role: "assistant",
296
+ content: [{ type: "reasoning", text: part.textDelta }],
297
+ },
298
+ reasoning: part.textDelta,
299
+ };
300
+ case "source":
301
+ console.warn("Received source part first??");
302
+ return {
303
+ ...metadata,
304
+ tool: true,
305
+ message: { role: "tool", content: [] },
306
+ sources: [part.source],
307
+ };
308
+ case "tool-call":
309
+ return {
310
+ ...metadata,
311
+ tool: true,
312
+ message: { role: "assistant", content: [part] },
313
+ };
314
+ case "tool-call-delta":
315
+ console.warn("Received tool call delta part first??");
316
+ return {
317
+ ...metadata,
318
+ tool: true,
319
+ message: {
320
+ role: "assistant",
321
+ content: [
322
+ {
323
+ type: "tool-call",
324
+ toolCallId: part.toolCallId,
325
+ toolName: part.toolName,
326
+ args: part.argsTextDelta,
327
+ },
328
+ ],
329
+ },
330
+ };
331
+ case "tool-result":
332
+ return {
333
+ ...metadata,
334
+ tool: true,
335
+ message: { role: "tool", content: [part] },
336
+ };
337
+ default:
338
+ throw new Error(`Unexpected part type: ${JSON.stringify(part)}`);
339
+ }
340
+ }