@convex-dev/agent 0.5.0-alpha.1 → 0.6.0-alpha.1

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 (235) hide show
  1. package/MIGRATION.md +153 -0
  2. package/README.md +32 -27
  3. package/dist/UIMessages.d.ts +46 -0
  4. package/dist/UIMessages.d.ts.map +1 -0
  5. package/dist/UIMessages.js +546 -0
  6. package/dist/UIMessages.js.map +1 -0
  7. package/dist/client/createTool.d.ts +126 -27
  8. package/dist/client/createTool.d.ts.map +1 -1
  9. package/dist/client/createTool.js +67 -12
  10. package/dist/client/createTool.js.map +1 -1
  11. package/dist/client/defaultComponent.d.ts +11 -0
  12. package/dist/client/defaultComponent.d.ts.map +1 -0
  13. package/dist/client/defaultComponent.js +7 -0
  14. package/dist/client/defaultComponent.js.map +1 -0
  15. package/dist/client/definePlaygroundAPI.d.ts +1335 -204
  16. package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
  17. package/dist/client/definePlaygroundAPI.js +52 -28
  18. package/dist/client/definePlaygroundAPI.js.map +1 -1
  19. package/dist/client/files.d.ts +20 -7
  20. package/dist/client/files.d.ts.map +1 -1
  21. package/dist/client/files.js +68 -11
  22. package/dist/client/files.js.map +1 -1
  23. package/dist/client/index.d.ts +1116 -978
  24. package/dist/client/index.d.ts.map +1 -1
  25. package/dist/client/index.js +332 -747
  26. package/dist/client/index.js.map +1 -1
  27. package/dist/client/messages.d.ts +461 -0
  28. package/dist/client/messages.d.ts.map +1 -0
  29. package/dist/client/messages.js +106 -0
  30. package/dist/client/messages.js.map +1 -0
  31. package/dist/client/mockModel.d.ts +42 -0
  32. package/dist/client/mockModel.d.ts.map +1 -0
  33. package/dist/client/mockModel.js +182 -0
  34. package/dist/client/mockModel.js.map +1 -0
  35. package/dist/client/saveInputMessages.d.ts +20 -0
  36. package/dist/client/saveInputMessages.d.ts.map +1 -0
  37. package/dist/client/saveInputMessages.js +58 -0
  38. package/dist/client/saveInputMessages.js.map +1 -0
  39. package/dist/client/search.d.ts +350 -39
  40. package/dist/client/search.d.ts.map +1 -1
  41. package/dist/client/search.js +350 -39
  42. package/dist/client/search.js.map +1 -1
  43. package/dist/client/start.d.ts +84 -0
  44. package/dist/client/start.d.ts.map +1 -0
  45. package/dist/client/start.js +185 -0
  46. package/dist/client/start.js.map +1 -0
  47. package/dist/client/streamText.d.ts +46 -0
  48. package/dist/client/streamText.d.ts.map +1 -0
  49. package/dist/client/streamText.js +117 -0
  50. package/dist/client/streamText.js.map +1 -0
  51. package/dist/client/streaming.d.ts +3716 -32
  52. package/dist/client/streaming.d.ts.map +1 -1
  53. package/dist/client/streaming.js +161 -59
  54. package/dist/client/streaming.js.map +1 -1
  55. package/dist/client/threads.d.ts +46 -0
  56. package/dist/client/threads.d.ts.map +1 -0
  57. package/dist/client/threads.js +49 -0
  58. package/dist/client/threads.js.map +1 -0
  59. package/dist/client/types.d.ts +266 -128
  60. package/dist/client/types.d.ts.map +1 -1
  61. package/dist/client/utils.d.ts +4 -0
  62. package/dist/client/utils.d.ts.map +1 -0
  63. package/dist/client/utils.js +21 -0
  64. package/dist/client/utils.js.map +1 -0
  65. package/dist/component/_generated/api.d.ts +24 -2178
  66. package/dist/component/_generated/api.d.ts.map +1 -1
  67. package/dist/component/_generated/api.js +10 -1
  68. package/dist/component/_generated/api.js.map +1 -1
  69. package/dist/component/_generated/component.d.ts +3120 -0
  70. package/dist/component/_generated/component.d.ts.map +1 -0
  71. package/dist/component/_generated/component.js +11 -0
  72. package/dist/component/_generated/component.js.map +1 -0
  73. package/dist/component/_generated/dataModel.d.ts +4 -18
  74. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  75. package/dist/component/_generated/dataModel.js +11 -0
  76. package/dist/component/_generated/dataModel.js.map +1 -0
  77. package/dist/component/_generated/server.d.ts +10 -38
  78. package/dist/component/_generated/server.d.ts.map +1 -1
  79. package/dist/component/_generated/server.js +9 -5
  80. package/dist/component/_generated/server.js.map +1 -1
  81. package/dist/component/files.d.ts +16 -10
  82. package/dist/component/files.d.ts.map +1 -1
  83. package/dist/component/files.js +10 -2
  84. package/dist/component/files.js.map +1 -1
  85. package/dist/component/messages.d.ts +2578 -366
  86. package/dist/component/messages.d.ts.map +1 -1
  87. package/dist/component/messages.js +397 -154
  88. package/dist/component/messages.js.map +1 -1
  89. package/dist/component/schema.d.ts +5697 -3584
  90. package/dist/component/schema.d.ts.map +1 -1
  91. package/dist/component/schema.js +18 -41
  92. package/dist/component/schema.js.map +1 -1
  93. package/dist/component/streams.d.ts +39 -339
  94. package/dist/component/streams.d.ts.map +1 -1
  95. package/dist/component/streams.js +114 -73
  96. package/dist/component/streams.js.map +1 -1
  97. package/dist/component/threads.d.ts +13 -13
  98. package/dist/component/users.d.ts +7 -7
  99. package/dist/component/vector/index.d.ts +1 -1
  100. package/dist/component/vector/index.d.ts.map +1 -1
  101. package/dist/component/vector/index.js +1 -3
  102. package/dist/component/vector/index.js.map +1 -1
  103. package/dist/deltas.d.ts +43 -0
  104. package/dist/deltas.d.ts.map +1 -0
  105. package/dist/deltas.js +446 -0
  106. package/dist/deltas.js.map +1 -0
  107. package/dist/mapping.d.ts +38 -20
  108. package/dist/mapping.d.ts.map +1 -1
  109. package/dist/mapping.js +365 -97
  110. package/dist/mapping.js.map +1 -1
  111. package/dist/react/SmoothText.d.ts +5 -0
  112. package/dist/react/SmoothText.d.ts.map +1 -0
  113. package/dist/react/SmoothText.js +6 -0
  114. package/dist/react/SmoothText.js.map +1 -0
  115. package/dist/react/index.d.ts +5 -77
  116. package/dist/react/index.d.ts.map +1 -1
  117. package/dist/react/index.js +6 -160
  118. package/dist/react/index.js.map +1 -1
  119. package/dist/react/optimisticallySendMessage.d.ts +36 -3
  120. package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
  121. package/dist/react/optimisticallySendMessage.js +35 -9
  122. package/dist/react/optimisticallySendMessage.js.map +1 -1
  123. package/dist/react/types.d.ts +4 -18
  124. package/dist/react/types.d.ts.map +1 -1
  125. package/dist/react/useDeltaStreams.d.ts +10 -0
  126. package/dist/react/useDeltaStreams.d.ts.map +1 -0
  127. package/dist/react/useDeltaStreams.js +106 -0
  128. package/dist/react/useDeltaStreams.js.map +1 -0
  129. package/dist/react/useSmoothText.d.ts +13 -12
  130. package/dist/react/useSmoothText.d.ts.map +1 -1
  131. package/dist/react/useSmoothText.js +32 -15
  132. package/dist/react/useSmoothText.js.map +1 -1
  133. package/dist/react/useStreamingUIMessages.d.ts +22 -0
  134. package/dist/react/useStreamingUIMessages.d.ts.map +1 -0
  135. package/dist/react/useStreamingUIMessages.js +92 -0
  136. package/dist/react/useStreamingUIMessages.js.map +1 -0
  137. package/dist/react/useThreadMessages.d.ts +104 -0
  138. package/dist/react/useThreadMessages.d.ts.map +1 -0
  139. package/dist/react/useThreadMessages.js +148 -0
  140. package/dist/react/useThreadMessages.js.map +1 -0
  141. package/dist/react/useUIMessages.d.ts +96 -0
  142. package/dist/react/useUIMessages.d.ts.map +1 -0
  143. package/dist/react/useUIMessages.js +108 -0
  144. package/dist/react/useUIMessages.js.map +1 -0
  145. package/dist/shared.d.ts +20 -4
  146. package/dist/shared.d.ts.map +1 -1
  147. package/dist/shared.js +45 -8
  148. package/dist/shared.js.map +1 -1
  149. package/dist/validators.d.ts +22981 -5666
  150. package/dist/validators.d.ts.map +1 -1
  151. package/dist/validators.js +245 -137
  152. package/dist/validators.js.map +1 -1
  153. package/package.json +101 -51
  154. package/src/UIMessages.combineUIMessages.test.ts +239 -0
  155. package/src/UIMessages.test.ts +273 -0
  156. package/src/UIMessages.ts +739 -0
  157. package/src/client/approval.test.ts +350 -0
  158. package/src/client/createTool.ts +291 -76
  159. package/src/client/defaultComponent.ts +17 -0
  160. package/src/client/definePlaygroundAPI.ts +67 -31
  161. package/src/client/files.ts +100 -20
  162. package/src/client/index.test.ts +40 -85
  163. package/src/client/index.ts +638 -1289
  164. package/src/client/messages.ts +237 -0
  165. package/src/client/mockModel.ts +252 -0
  166. package/src/client/saveInputMessages.test.ts +583 -0
  167. package/src/client/saveInputMessages.ts +101 -0
  168. package/src/client/search.test.ts +1207 -0
  169. package/src/client/search.ts +581 -70
  170. package/src/client/start.ts +327 -0
  171. package/src/client/streamText.ts +187 -0
  172. package/src/client/streaming.test.ts +186 -0
  173. package/src/client/streaming.ts +241 -97
  174. package/src/client/threads.ts +83 -0
  175. package/src/client/types.ts +370 -219
  176. package/src/client/utils.ts +27 -0
  177. package/src/component/_generated/api.ts +64 -0
  178. package/src/component/_generated/component.ts +4902 -0
  179. package/src/component/_generated/{server.d.ts → server.ts} +33 -21
  180. package/src/component/files.ts +11 -2
  181. package/src/component/messages.test.ts +195 -51
  182. package/src/component/messages.ts +500 -201
  183. package/src/component/schema.ts +20 -46
  184. package/src/component/setup.test.ts +7 -0
  185. package/src/component/streams.ts +184 -83
  186. package/src/component/users.test.ts +0 -1
  187. package/src/component/vector/index.ts +1 -3
  188. package/src/deltas.test.ts +626 -0
  189. package/src/deltas.ts +569 -0
  190. package/src/fromUIMessages.test.ts +497 -0
  191. package/src/mapping.test.ts +180 -6
  192. package/src/mapping.ts +479 -162
  193. package/src/react/SmoothText.tsx +9 -0
  194. package/src/react/index.ts +10 -230
  195. package/src/react/optimisticallySendMessage.ts +55 -12
  196. package/src/react/types.ts +6 -39
  197. package/src/react/useDeltaStreams.ts +160 -0
  198. package/src/react/useSmoothText.ts +56 -36
  199. package/src/react/useStreamingUIMessages.ts +143 -0
  200. package/src/react/useThreadMessages.ts +262 -0
  201. package/src/react/useUIMessages.test.ts +255 -0
  202. package/src/react/useUIMessages.ts +195 -0
  203. package/src/shared.ts +88 -12
  204. package/src/test.ts +18 -0
  205. package/src/toUIMessages.test.ts +1269 -0
  206. package/src/validators.test.ts +18 -19
  207. package/src/validators.ts +325 -185
  208. package/dist/client/_generated/_ignore.d.ts +0 -1
  209. package/dist/client/_generated/_ignore.d.ts.map +0 -1
  210. package/dist/client/_generated/_ignore.js +0 -3
  211. package/dist/client/_generated/_ignore.js.map +0 -1
  212. package/dist/client/listMessages.d.ts +0 -22
  213. package/dist/client/listMessages.d.ts.map +0 -1
  214. package/dist/client/listMessages.js +0 -25
  215. package/dist/client/listMessages.js.map +0 -1
  216. package/dist/package.json +0 -3
  217. package/dist/react/deltas.d.ts +0 -26
  218. package/dist/react/deltas.d.ts.map +0 -1
  219. package/dist/react/deltas.js +0 -384
  220. package/dist/react/deltas.js.map +0 -1
  221. package/dist/react/toUIMessages.d.ts +0 -15
  222. package/dist/react/toUIMessages.d.ts.map +0 -1
  223. package/dist/react/toUIMessages.js +0 -211
  224. package/dist/react/toUIMessages.js.map +0 -1
  225. package/src/client/listMessages.ts +0 -38
  226. package/src/component/_generated/api.d.ts +0 -2202
  227. package/src/component/_generated/api.js +0 -23
  228. package/src/component/_generated/server.js +0 -90
  229. package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  230. package/src/react/deltas.test.ts +0 -315
  231. package/src/react/deltas.ts +0 -478
  232. package/src/react/toUIMessages.test.ts +0 -420
  233. package/src/react/toUIMessages.ts +0 -253
  234. package/src/vitest.config.ts +0 -7
  235. /package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +0 -0
@@ -1,23 +0,0 @@
1
- /* eslint-disable */
2
- /**
3
- * Generated `api` utility.
4
- *
5
- * THIS CODE IS AUTOMATICALLY GENERATED.
6
- *
7
- * To regenerate, run `npx convex dev`.
8
- * @module
9
- */
10
-
11
- import { anyApi, componentsGeneric } from "convex/server";
12
-
13
- /**
14
- * A utility for referencing Convex functions in your app's API.
15
- *
16
- * Usage:
17
- * ```js
18
- * const myFunctionReference = api.myModule.myFunction;
19
- * ```
20
- */
21
- export const api = anyApi;
22
- export const internal = anyApi;
23
- export const components = componentsGeneric();
@@ -1,90 +0,0 @@
1
- /* eslint-disable */
2
- /**
3
- * Generated utilities for implementing server-side Convex query and mutation functions.
4
- *
5
- * THIS CODE IS AUTOMATICALLY GENERATED.
6
- *
7
- * To regenerate, run `npx convex dev`.
8
- * @module
9
- */
10
-
11
- import {
12
- actionGeneric,
13
- httpActionGeneric,
14
- queryGeneric,
15
- mutationGeneric,
16
- internalActionGeneric,
17
- internalMutationGeneric,
18
- internalQueryGeneric,
19
- componentsGeneric,
20
- } from "convex/server";
21
-
22
- /**
23
- * Define a query in this Convex app's public API.
24
- *
25
- * This function will be allowed to read your Convex database and will be accessible from the client.
26
- *
27
- * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
28
- * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
29
- */
30
- export const query = queryGeneric;
31
-
32
- /**
33
- * Define a query that is only accessible from other Convex functions (but not from the client).
34
- *
35
- * This function will be allowed to read from your Convex database. It will not be accessible from the client.
36
- *
37
- * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
38
- * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
39
- */
40
- export const internalQuery = internalQueryGeneric;
41
-
42
- /**
43
- * Define a mutation in this Convex app's public API.
44
- *
45
- * This function will be allowed to modify your Convex database and will be accessible from the client.
46
- *
47
- * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
48
- * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
49
- */
50
- export const mutation = mutationGeneric;
51
-
52
- /**
53
- * Define a mutation that is only accessible from other Convex functions (but not from the client).
54
- *
55
- * This function will be allowed to modify your Convex database. It will not be accessible from the client.
56
- *
57
- * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
58
- * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
59
- */
60
- export const internalMutation = internalMutationGeneric;
61
-
62
- /**
63
- * Define an action in this Convex app's public API.
64
- *
65
- * An action is a function which can execute any JavaScript code, including non-deterministic
66
- * code and code with side-effects, like calling third-party services.
67
- * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
68
- * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
69
- *
70
- * @param func - The action. It receives an {@link ActionCtx} as its first argument.
71
- * @returns The wrapped action. Include this as an `export` to name it and make it accessible.
72
- */
73
- export const action = actionGeneric;
74
-
75
- /**
76
- * Define an action that is only accessible from other Convex functions (but not from the client).
77
- *
78
- * @param func - The function. It receives an {@link ActionCtx} as its first argument.
79
- * @returns The wrapped function. Include this as an `export` to name it and make it accessible.
80
- */
81
- export const internalAction = internalActionGeneric;
82
-
83
- /**
84
- * Define a Convex HTTP action.
85
- *
86
- * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
87
- * as its second.
88
- * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
89
- */
90
- export const httpAction = httpActionGeneric;
@@ -1 +0,0 @@
1
- {"version":"3.2.4","results":[[":client/index.test.ts",{"duration":75.65995979309082,"failed":false}],[":component/messages.test.ts",{"duration":43.858792304992676,"failed":false}],[":react/toUIMessages.test.ts",{"duration":4.371167182922363,"failed":false}]]}
@@ -1,315 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { mergeDeltas, applyDeltasToStreamMessage } from "./deltas.js";
3
- import type {
4
- StreamMessage,
5
- StreamDelta,
6
- TextStreamPart,
7
- } from "../validators.js";
8
- import { omit } from "convex-helpers";
9
-
10
- function makeStreamMessage(
11
- streamId: string,
12
- order: number,
13
- stepOrder: number,
14
- ): StreamMessage {
15
- return {
16
- streamId,
17
- order,
18
- stepOrder,
19
- } as StreamMessage;
20
- }
21
-
22
- function makeDelta(
23
- streamId: string,
24
- start: number,
25
- end: number,
26
- parts: TextStreamPart[],
27
- ): StreamDelta {
28
- return {
29
- streamId,
30
- start,
31
- end,
32
- parts,
33
- };
34
- }
35
-
36
- describe("mergeDeltas", () => {
37
- it("merges a single text-delta into a message", () => {
38
- const streamId = "s1";
39
- const streamMessages = [makeStreamMessage(streamId, 1, 0)];
40
- const deltas = [
41
- makeDelta(streamId, 0, 5, [{ type: "text-delta", textDelta: "Hello" }]),
42
- ];
43
- const [messages, newStreams, changed] = mergeDeltas(
44
- "thread1",
45
- streamMessages,
46
- [],
47
- deltas,
48
- );
49
- expect(messages).toHaveLength(1);
50
- expect(messages[0].text).toBe("Hello");
51
- expect(messages[0].message?.role).toBe("assistant");
52
- expect(changed).toBe(true);
53
- expect(newStreams[0].cursor).toBe(5);
54
- });
55
-
56
- it("merges multiple deltas for the same stream", () => {
57
- const streamId = "s1";
58
- const streamMessages = [makeStreamMessage(streamId, 1, 0)];
59
- const deltas = [
60
- makeDelta(streamId, 0, 5, [{ type: "text-delta", textDelta: "Hello" }]),
61
- makeDelta(streamId, 5, 11, [
62
- { type: "text-delta", textDelta: " World!" },
63
- ]),
64
- ];
65
- const [messages, newStreams, changed] = mergeDeltas(
66
- "thread1",
67
- streamMessages,
68
- [],
69
- deltas,
70
- );
71
- expect(messages).toHaveLength(1);
72
- expect(messages[0].text).toBe("Hello World!");
73
- expect(changed).toBe(true);
74
- expect(newStreams[0].cursor).toBe(11);
75
- });
76
-
77
- it("handles tool-call and tool-result parts", () => {
78
- const streamId = "s2";
79
- const streamMessages = [makeStreamMessage(streamId, 2, 0)];
80
- const deltas = [
81
- makeDelta(streamId, 0, 1, [
82
- {
83
- type: "tool-call",
84
- toolCallId: "call1",
85
- toolName: "myTool",
86
- args: "",
87
- },
88
- ]),
89
- makeDelta(streamId, 1, 2, [
90
- {
91
- type: "tool-result",
92
- toolCallId: "call1",
93
- toolName: "myTool",
94
- result: "42",
95
- },
96
- ]),
97
- ];
98
- const [messages, _, changed] = mergeDeltas(
99
- "thread1",
100
- streamMessages,
101
- [],
102
- deltas,
103
- );
104
- expect(messages).toHaveLength(2);
105
- expect(messages[0].message?.role).toBe("assistant");
106
- expect(messages[0].tool).toBe(true);
107
- const content = messages[0].message?.content;
108
- expect(content).toEqual([
109
- {
110
- type: "tool-call",
111
- toolCallId: "call1",
112
- toolName: "myTool",
113
- args: "",
114
- },
115
- ]);
116
- expect(messages[1].message?.role).toBe("tool");
117
- expect(messages[1].tool).toBe(true);
118
- expect(messages[1].message?.content).toEqual([
119
- {
120
- type: "tool-result",
121
- toolCallId: "call1",
122
- toolName: "myTool",
123
- result: "42",
124
- },
125
- ]);
126
- expect(changed).toBe(true);
127
- });
128
-
129
- it("returns changed=false if no new deltas", () => {
130
- const streamId = "s3";
131
- const streamMessages = [makeStreamMessage(streamId, 3, 0)];
132
- const deltas: StreamDelta[] = [];
133
- const [messages, newStreams, changed] = mergeDeltas(
134
- "thread1",
135
- streamMessages,
136
- [],
137
- deltas,
138
- );
139
- expect(messages).toHaveLength(0);
140
- expect(changed).toBe(false);
141
- expect(newStreams[0].cursor).toBe(0);
142
- });
143
-
144
- it("handles multiple streams and sorts by order/stepOrder", () => {
145
- const s1 = makeStreamMessage("s1", 1, 0);
146
- const s2 = makeStreamMessage("s2", 2, 0);
147
- const deltas = [
148
- makeDelta("s2", 0, 3, [{ type: "text-delta", textDelta: "B" }]),
149
- makeDelta("s1", 0, 3, [{ type: "text-delta", textDelta: "A" }]),
150
- ];
151
- const [messages, _, changed] = mergeDeltas("thread1", [s2, s1], [], deltas);
152
- expect(messages).toHaveLength(2);
153
- expect(messages[0].text).toBe("A");
154
- expect(messages[1].text).toBe("B");
155
- expect(changed).toBe(true);
156
- // Sorted by order
157
- expect(messages[0].order).toBe(1);
158
- expect(messages[1].order).toBe(2);
159
- });
160
-
161
- it("does not duplicate text content when merging sequential text-deltas", () => {
162
- const streamId = "s4";
163
- const streamMessages = [makeStreamMessage(streamId, 4, 0)];
164
- const deltas = [
165
- makeDelta(streamId, 0, 5, [{ type: "text-delta", textDelta: "Hello" }]),
166
- makeDelta(streamId, 5, 11, [
167
- { type: "text-delta", textDelta: " World!" },
168
- ]),
169
- makeDelta(streamId, 11, 12, [{ type: "text-delta", textDelta: "!" }]),
170
- ];
171
- const [messages] = mergeDeltas("thread1", streamMessages, [], deltas);
172
- expect(messages).toHaveLength(1);
173
- expect(messages[0].text).toBe("Hello World!!");
174
- // There should only be one text part per message
175
- const content = messages[0].message?.content;
176
- if (Array.isArray(content)) {
177
- const textParts = content.filter((p) => p.type === "text");
178
- expect(textParts).toHaveLength(1);
179
- expect(textParts[0].text).toBe("Hello World!!");
180
- }
181
- });
182
-
183
- it("does not duplicate reasoning parts", () => {
184
- const streamId = "s6";
185
- const streamMessages = [makeStreamMessage(streamId, 6, 0)];
186
- const deltas = [
187
- makeDelta(streamId, 0, 1, [
188
- { type: "reasoning", textDelta: "I'm thinking..." },
189
- ]),
190
- makeDelta(streamId, 1, 2, [
191
- { type: "reasoning", textDelta: " Still thinking..." },
192
- ]),
193
- ];
194
- const [messages] = mergeDeltas("thread1", streamMessages, [], deltas);
195
- expect(messages).toHaveLength(1);
196
- if (Array.isArray(messages[0].message?.content)) {
197
- const reasoningParts = messages[0].message.content.filter(
198
- (p) => p.type === "reasoning",
199
- );
200
- expect(reasoningParts).toHaveLength(1);
201
- expect(reasoningParts[0].text).toBe("I'm thinking... Still thinking...");
202
- }
203
- });
204
-
205
- it("applyDeltasToStreamMessage is idempotent and does not duplicate content", () => {
206
- const streamId = "s7";
207
- const streamMessage = makeStreamMessage(streamId, 7, 0);
208
- const deltas = [
209
- makeDelta(streamId, 0, 5, [{ type: "text-delta", textDelta: "Hello" }]),
210
- makeDelta(streamId, 5, 11, [
211
- { type: "text-delta", textDelta: " World!" },
212
- ]),
213
- ];
214
- // First call: apply both deltas
215
- let [result, changed] = applyDeltasToStreamMessage(
216
- "thread1",
217
- streamMessage,
218
- undefined,
219
- deltas,
220
- );
221
- expect(result.messages).toHaveLength(1);
222
- expect(result.messages[0].text).toBe("Hello World!");
223
- // Second call: re-apply the same deltas (should not duplicate)
224
- [result, changed] = applyDeltasToStreamMessage(
225
- "thread1",
226
- streamMessage,
227
- result,
228
- deltas,
229
- );
230
- expect(result.messages).toHaveLength(1);
231
- expect(result.messages[0].text).toBe("Hello World!");
232
- // Third call: add a new delta
233
- const moreDeltas = [
234
- ...deltas,
235
- makeDelta(streamId, 11, 12, [{ type: "text-delta", textDelta: "!" }]),
236
- ];
237
- [result, changed] = applyDeltasToStreamMessage(
238
- "thread1",
239
- streamMessage,
240
- result,
241
- moreDeltas,
242
- );
243
- expect(changed).toBe(true);
244
- expect(result.messages).toHaveLength(1);
245
- expect(result.messages[0].text).toBe("Hello World!!");
246
- // Re-apply all deltas again (should still not duplicate)
247
- [result, changed] = applyDeltasToStreamMessage(
248
- "thread1",
249
- streamMessage,
250
- result,
251
- moreDeltas,
252
- );
253
- expect(changed).toBe(false);
254
- expect(result.messages).toHaveLength(1);
255
- expect(result.messages[0].text).toBe("Hello World!!");
256
- });
257
-
258
- it("mergeDeltas is pure and does not mutate inputs", () => {
259
- const streamId = "s8";
260
- const streamMessages = [makeStreamMessage(streamId, 8, 0)];
261
- const deltas = [
262
- makeDelta(streamId, 0, 5, [{ type: "text-delta", textDelta: "Hello" }]),
263
- makeDelta(streamId, 5, 11, [
264
- { type: "text-delta", textDelta: " World!" },
265
- ]),
266
- ];
267
- // Deep freeze inputs to catch mutation
268
- function deepFreeze(obj: unknown): unknown {
269
- if (obj && typeof obj === "object" && !Object.isFrozen(obj)) {
270
- Object.freeze(obj);
271
- for (const key of Object.keys(obj)) {
272
- deepFreeze((obj as Record<string, unknown>)[key]);
273
- }
274
- }
275
- return obj;
276
- }
277
- deepFreeze(streamMessages);
278
- deepFreeze(deltas);
279
- const [messages1, streams1, changed1] = mergeDeltas(
280
- "thread1",
281
- streamMessages,
282
- [],
283
- deltas,
284
- );
285
- const [messages2, streams2, changed2] = mergeDeltas(
286
- "thread1",
287
- streamMessages,
288
- [],
289
- deltas,
290
- );
291
- expect(messages1.map((m) => omit(m, ["_creationTime"]))).toEqual(
292
- messages2.map((m) => omit(m, ["_creationTime"])),
293
- );
294
- expect(
295
- streams1.map((s) => ({
296
- ...s,
297
- messages: s.messages.map((m) => omit(m, ["_creationTime"])),
298
- })),
299
- ).toEqual(
300
- streams2.map((s) => ({
301
- ...s,
302
- messages: s.messages.map((m) => omit(m, ["_creationTime"])),
303
- })),
304
- );
305
- expect(changed1).toBe(changed2);
306
- // Inputs should remain unchanged
307
- expect(streamMessages).toEqual([makeStreamMessage(streamId, 8, 0)]);
308
- expect(deltas).toEqual([
309
- makeDelta(streamId, 0, 5, [{ type: "text-delta", textDelta: "Hello" }]),
310
- makeDelta(streamId, 5, 11, [
311
- { type: "text-delta", textDelta: " World!" },
312
- ]),
313
- ]);
314
- });
315
- });