@convex-dev/agent 0.5.0-alpha.1 → 0.6.0-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 (233) hide show
  1. package/README.md +32 -27
  2. package/dist/UIMessages.d.ts +46 -0
  3. package/dist/UIMessages.d.ts.map +1 -0
  4. package/dist/UIMessages.js +546 -0
  5. package/dist/UIMessages.js.map +1 -0
  6. package/dist/client/createTool.d.ts +129 -27
  7. package/dist/client/createTool.d.ts.map +1 -1
  8. package/dist/client/createTool.js +66 -12
  9. package/dist/client/createTool.js.map +1 -1
  10. package/dist/client/defaultComponent.d.ts +11 -0
  11. package/dist/client/defaultComponent.d.ts.map +1 -0
  12. package/dist/client/defaultComponent.js +7 -0
  13. package/dist/client/defaultComponent.js.map +1 -0
  14. package/dist/client/definePlaygroundAPI.d.ts +1323 -192
  15. package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
  16. package/dist/client/definePlaygroundAPI.js +52 -28
  17. package/dist/client/definePlaygroundAPI.js.map +1 -1
  18. package/dist/client/files.d.ts +20 -7
  19. package/dist/client/files.d.ts.map +1 -1
  20. package/dist/client/files.js +68 -11
  21. package/dist/client/files.js.map +1 -1
  22. package/dist/client/index.d.ts +1056 -965
  23. package/dist/client/index.d.ts.map +1 -1
  24. package/dist/client/index.js +242 -748
  25. package/dist/client/index.js.map +1 -1
  26. package/dist/client/messages.d.ts +461 -0
  27. package/dist/client/messages.d.ts.map +1 -0
  28. package/dist/client/messages.js +106 -0
  29. package/dist/client/messages.js.map +1 -0
  30. package/dist/client/mockModel.d.ts +42 -0
  31. package/dist/client/mockModel.d.ts.map +1 -0
  32. package/dist/client/mockModel.js +175 -0
  33. package/dist/client/mockModel.js.map +1 -0
  34. package/dist/client/saveInputMessages.d.ts +20 -0
  35. package/dist/client/saveInputMessages.d.ts.map +1 -0
  36. package/dist/client/saveInputMessages.js +58 -0
  37. package/dist/client/saveInputMessages.js.map +1 -0
  38. package/dist/client/search.d.ts +346 -35
  39. package/dist/client/search.d.ts.map +1 -1
  40. package/dist/client/search.js +350 -39
  41. package/dist/client/search.js.map +1 -1
  42. package/dist/client/start.d.ts +84 -0
  43. package/dist/client/start.d.ts.map +1 -0
  44. package/dist/client/start.js +171 -0
  45. package/dist/client/start.js.map +1 -0
  46. package/dist/client/streamText.d.ts +46 -0
  47. package/dist/client/streamText.d.ts.map +1 -0
  48. package/dist/client/streamText.js +93 -0
  49. package/dist/client/streamText.js.map +1 -0
  50. package/dist/client/streaming.d.ts +3705 -32
  51. package/dist/client/streaming.d.ts.map +1 -1
  52. package/dist/client/streaming.js +141 -59
  53. package/dist/client/streaming.js.map +1 -1
  54. package/dist/client/threads.d.ts +46 -0
  55. package/dist/client/threads.d.ts.map +1 -0
  56. package/dist/client/threads.js +49 -0
  57. package/dist/client/threads.js.map +1 -0
  58. package/dist/client/types.d.ts +265 -128
  59. package/dist/client/types.d.ts.map +1 -1
  60. package/dist/client/utils.d.ts +4 -0
  61. package/dist/client/utils.d.ts.map +1 -0
  62. package/dist/client/utils.js +21 -0
  63. package/dist/client/utils.js.map +1 -0
  64. package/dist/component/_generated/api.d.ts +24 -2178
  65. package/dist/component/_generated/api.d.ts.map +1 -1
  66. package/dist/component/_generated/api.js +10 -1
  67. package/dist/component/_generated/api.js.map +1 -1
  68. package/dist/component/_generated/component.d.ts +3119 -0
  69. package/dist/component/_generated/component.d.ts.map +1 -0
  70. package/dist/component/_generated/component.js +11 -0
  71. package/dist/component/_generated/component.js.map +1 -0
  72. package/dist/component/_generated/dataModel.d.ts +4 -18
  73. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  74. package/dist/component/_generated/dataModel.js +11 -0
  75. package/dist/component/_generated/dataModel.js.map +1 -0
  76. package/dist/component/_generated/server.d.ts +10 -38
  77. package/dist/component/_generated/server.d.ts.map +1 -1
  78. package/dist/component/_generated/server.js +9 -5
  79. package/dist/component/_generated/server.js.map +1 -1
  80. package/dist/component/files.d.ts +16 -10
  81. package/dist/component/files.d.ts.map +1 -1
  82. package/dist/component/files.js +10 -2
  83. package/dist/component/files.js.map +1 -1
  84. package/dist/component/messages.d.ts +2553 -342
  85. package/dist/component/messages.d.ts.map +1 -1
  86. package/dist/component/messages.js +387 -154
  87. package/dist/component/messages.js.map +1 -1
  88. package/dist/component/schema.d.ts +5697 -3584
  89. package/dist/component/schema.d.ts.map +1 -1
  90. package/dist/component/schema.js +18 -41
  91. package/dist/component/schema.js.map +1 -1
  92. package/dist/component/streams.d.ts +35 -335
  93. package/dist/component/streams.d.ts.map +1 -1
  94. package/dist/component/streams.js +114 -73
  95. package/dist/component/streams.js.map +1 -1
  96. package/dist/component/threads.d.ts +16 -16
  97. package/dist/component/users.d.ts +4 -4
  98. package/dist/component/vector/index.d.ts +1 -1
  99. package/dist/component/vector/index.d.ts.map +1 -1
  100. package/dist/component/vector/index.js +1 -3
  101. package/dist/component/vector/index.js.map +1 -1
  102. package/dist/deltas.d.ts +43 -0
  103. package/dist/deltas.d.ts.map +1 -0
  104. package/dist/deltas.js +447 -0
  105. package/dist/deltas.js.map +1 -0
  106. package/dist/mapping.d.ts +20 -20
  107. package/dist/mapping.d.ts.map +1 -1
  108. package/dist/mapping.js +313 -96
  109. package/dist/mapping.js.map +1 -1
  110. package/dist/react/SmoothText.d.ts +5 -0
  111. package/dist/react/SmoothText.d.ts.map +1 -0
  112. package/dist/react/SmoothText.js +6 -0
  113. package/dist/react/SmoothText.js.map +1 -0
  114. package/dist/react/index.d.ts +5 -77
  115. package/dist/react/index.d.ts.map +1 -1
  116. package/dist/react/index.js +6 -160
  117. package/dist/react/index.js.map +1 -1
  118. package/dist/react/optimisticallySendMessage.d.ts +36 -3
  119. package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
  120. package/dist/react/optimisticallySendMessage.js +35 -9
  121. package/dist/react/optimisticallySendMessage.js.map +1 -1
  122. package/dist/react/types.d.ts +4 -18
  123. package/dist/react/types.d.ts.map +1 -1
  124. package/dist/react/useDeltaStreams.d.ts +10 -0
  125. package/dist/react/useDeltaStreams.d.ts.map +1 -0
  126. package/dist/react/useDeltaStreams.js +101 -0
  127. package/dist/react/useDeltaStreams.js.map +1 -0
  128. package/dist/react/useSmoothText.d.ts +13 -12
  129. package/dist/react/useSmoothText.d.ts.map +1 -1
  130. package/dist/react/useSmoothText.js +32 -15
  131. package/dist/react/useSmoothText.js.map +1 -1
  132. package/dist/react/useStreamingUIMessages.d.ts +22 -0
  133. package/dist/react/useStreamingUIMessages.d.ts.map +1 -0
  134. package/dist/react/useStreamingUIMessages.js +92 -0
  135. package/dist/react/useStreamingUIMessages.js.map +1 -0
  136. package/dist/react/useThreadMessages.d.ts +104 -0
  137. package/dist/react/useThreadMessages.d.ts.map +1 -0
  138. package/dist/react/useThreadMessages.js +148 -0
  139. package/dist/react/useThreadMessages.js.map +1 -0
  140. package/dist/react/useUIMessages.d.ts +96 -0
  141. package/dist/react/useUIMessages.d.ts.map +1 -0
  142. package/dist/react/useUIMessages.js +108 -0
  143. package/dist/react/useUIMessages.js.map +1 -0
  144. package/dist/shared.d.ts +20 -4
  145. package/dist/shared.d.ts.map +1 -1
  146. package/dist/shared.js +45 -8
  147. package/dist/shared.js.map +1 -1
  148. package/dist/validators.d.ts +22981 -5666
  149. package/dist/validators.d.ts.map +1 -1
  150. package/dist/validators.js +245 -137
  151. package/dist/validators.js.map +1 -1
  152. package/package.json +98 -50
  153. package/src/UIMessages.combineUIMessages.test.ts +239 -0
  154. package/src/UIMessages.test.ts +273 -0
  155. package/src/UIMessages.ts +739 -0
  156. package/src/client/createTool.ts +293 -76
  157. package/src/client/defaultComponent.ts +17 -0
  158. package/src/client/definePlaygroundAPI.ts +67 -31
  159. package/src/client/files.ts +100 -20
  160. package/src/client/index.test.ts +40 -85
  161. package/src/client/index.ts +520 -1290
  162. package/src/client/messages.ts +237 -0
  163. package/src/client/mockModel.ts +245 -0
  164. package/src/client/saveInputMessages.test.ts +583 -0
  165. package/src/client/saveInputMessages.ts +101 -0
  166. package/src/client/search.test.ts +1207 -0
  167. package/src/client/search.ts +577 -70
  168. package/src/client/start.ts +310 -0
  169. package/src/client/streamText.ts +163 -0
  170. package/src/client/streaming.test.ts +186 -0
  171. package/src/client/streaming.ts +219 -97
  172. package/src/client/threads.ts +83 -0
  173. package/src/client/types.ts +368 -219
  174. package/src/client/utils.ts +27 -0
  175. package/src/component/_generated/api.ts +64 -0
  176. package/src/component/_generated/component.ts +4913 -0
  177. package/src/component/_generated/{server.d.ts → server.ts} +33 -21
  178. package/src/component/files.ts +11 -2
  179. package/src/component/messages.test.ts +195 -51
  180. package/src/component/messages.ts +490 -201
  181. package/src/component/schema.ts +20 -46
  182. package/src/component/setup.test.ts +7 -0
  183. package/src/component/streams.ts +184 -83
  184. package/src/component/users.test.ts +0 -1
  185. package/src/component/vector/index.ts +1 -3
  186. package/src/deltas.test.ts +626 -0
  187. package/src/deltas.ts +570 -0
  188. package/src/fromUIMessages.test.ts +497 -0
  189. package/src/mapping.test.ts +103 -6
  190. package/src/mapping.ts +422 -161
  191. package/src/react/SmoothText.tsx +9 -0
  192. package/src/react/index.ts +10 -230
  193. package/src/react/optimisticallySendMessage.ts +55 -12
  194. package/src/react/types.ts +6 -39
  195. package/src/react/useDeltaStreams.ts +154 -0
  196. package/src/react/useSmoothText.ts +56 -36
  197. package/src/react/useStreamingUIMessages.ts +143 -0
  198. package/src/react/useThreadMessages.ts +262 -0
  199. package/src/react/useUIMessages.test.ts +255 -0
  200. package/src/react/useUIMessages.ts +195 -0
  201. package/src/shared.ts +88 -12
  202. package/src/test.ts +18 -0
  203. package/src/toUIMessages.test.ts +1269 -0
  204. package/src/validators.test.ts +18 -19
  205. package/src/validators.ts +325 -185
  206. package/dist/client/_generated/_ignore.d.ts +0 -1
  207. package/dist/client/_generated/_ignore.d.ts.map +0 -1
  208. package/dist/client/_generated/_ignore.js +0 -3
  209. package/dist/client/_generated/_ignore.js.map +0 -1
  210. package/dist/client/listMessages.d.ts +0 -22
  211. package/dist/client/listMessages.d.ts.map +0 -1
  212. package/dist/client/listMessages.js +0 -25
  213. package/dist/client/listMessages.js.map +0 -1
  214. package/dist/package.json +0 -3
  215. package/dist/react/deltas.d.ts +0 -26
  216. package/dist/react/deltas.d.ts.map +0 -1
  217. package/dist/react/deltas.js +0 -384
  218. package/dist/react/deltas.js.map +0 -1
  219. package/dist/react/toUIMessages.d.ts +0 -15
  220. package/dist/react/toUIMessages.d.ts.map +0 -1
  221. package/dist/react/toUIMessages.js +0 -211
  222. package/dist/react/toUIMessages.js.map +0 -1
  223. package/src/client/listMessages.ts +0 -38
  224. package/src/component/_generated/api.d.ts +0 -2202
  225. package/src/component/_generated/api.js +0 -23
  226. package/src/component/_generated/server.js +0 -90
  227. package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  228. package/src/react/deltas.test.ts +0 -315
  229. package/src/react/deltas.ts +0 -478
  230. package/src/react/toUIMessages.test.ts +0 -420
  231. package/src/react/toUIMessages.ts +0 -253
  232. package/src/vitest.config.ts +0 -7
  233. /package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +0 -0
package/README.md CHANGED
@@ -21,34 +21,39 @@ The Agent component is a core building block for building AI agents. It manages
21
21
  threads and messages, around which you Agents can cooperate in static or dynamic
22
22
  workflows.
23
23
 
24
- - [Agents](./docs/getting-started.mdx) provide an abstraction for using LLMs to
25
- represent units of use-case-specific prompting with associated models,
26
- prompts, [Tool Calls](./docs/tools.mdx), and behavior in relation to other
27
- Agents, functions, APIs, and more.
28
- - [Threads](./docs/threads.mdx) persist [messages](./docs/messages.mdx) and can
29
- be shared by multiple users and agents (including
30
- [human agents](./docs/human-agents.mdx)).
24
+ - [Agents](https://docs.convex.dev/agents/agent-usage) provide an abstraction
25
+ for using LLMs to represent units of use-case-specific prompting with
26
+ associated models, prompts,
27
+ [Tool Calls](https://docs.convex.dev/agents/tools), and behavior in relation
28
+ to other Agents, functions, APIs, and more.
29
+ - [Threads](https://docs.convex.dev/agents/threads) persist
30
+ [messages](https://docs.convex.dev/agents/messages) and can be shared by
31
+ multiple users and agents (including
32
+ [human agents](https://docs.convex.dev/agents/human-agents)).
31
33
  - Streaming text and objects using deltas over websockets so all clients stay in
32
34
  sync efficiently, without http streaming. Enables streaming from async
33
35
  functions.
34
- - [Conversation context](./docs/context.mdx) is automatically included in each
35
- LLM call, including built-in hybrid vector/text search for messages in the
36
- thread and opt-in search for messages from other threads (for the same
37
- specified user).
38
- - [RAG](./docs/rag.mdx) techniques are supported for prompt augmentation from
39
- other sources, either up front in the prompt or as tool calls. Integrates with
40
- the [RAG Component](https://www.convex.dev/components/rag), or DIY.
41
- - [Workflows](./docs/workflows.mdx) allow building multi-step operations that
42
- can span agents, users, durably and reliably.
43
- - [Files](./docs/files.mdx) are supported in thread history with automatic
44
- saving to [file storage](https://docs.convex.dev/file-storage) and
45
- ref-counting.
46
- - [Debugging](./docs/debugging.mdx) is enabled by callbacks, the
47
- [agent playground](./docs/playground.mdx) where you can inspect all metadata
48
- and iterate on prompts and context settings, and inspection in the dashboard.
49
- - [Usage tracking](./docs/usage-tracking.mdx) is easy to set up, enabling usage
50
- attribution per-provider, per-model, per-user, per-agent, for billing & more.
51
- - [Rate limiting](./docs/rate-limiting.mdx), powered by the
36
+ - [Conversation context](https://docs.convex.dev/agents/context) is
37
+ automatically included in each LLM call, including built-in hybrid vector/text
38
+ search for messages in the thread and opt-in search for messages from other
39
+ threads (for the same specified user).
40
+ - [RAG](https://docs.convex.dev/agents/rag) techniques are supported for prompt
41
+ augmentation from other sources, either up front in the prompt or as tool
42
+ calls. Integrates with the
43
+ [RAG Component](https://www.convex.dev/components/rag), or DIY.
44
+ - [Workflows](https://docs.convex.dev/agents/workflows) allow building
45
+ multi-step operations that can span agents, users, durably and reliably.
46
+ - [Files](https://docs.convex.dev/agents/files) are supported in thread history
47
+ with automatic saving to [file storage](https://docs.convex.dev/file-storage)
48
+ and ref-counting.
49
+ - [Debugging](https://docs.convex.dev/agents/debugging) is enabled by callbacks,
50
+ the [agent playground](https://docs.convex.dev/agents/playground) where you
51
+ can inspect all metadata and iterate on prompts and context settings, and
52
+ inspection in the dashboard.
53
+ - [Usage tracking](https://docs.convex.dev/agents/usage-tracking) is easy to set
54
+ up, enabling usage attribution per-provider, per-model, per-user, per-agent,
55
+ for billing & more.
56
+ - [Rate limiting](https://docs.convex.dev/agents/rate-limiting), powered by the
52
57
  [Rate Limiter Component](https://www.convex.dev/components/rate-limiter),
53
58
  helps control the rate at which users can interact with agents and keep you
54
59
  from exceeding your LLM provider's limits.
@@ -58,13 +63,13 @@ workflows.
58
63
  [![Powerful AI Apps Made Easy with the Agent Component](https://thumbs.video-to-markdown.com/b323ac24.jpg)](https://youtu.be/tUKMPUlOCHY)
59
64
  **Read the [docs](https://docs.convex.dev/agents) for more details.**
60
65
 
61
- Play with the [example](./example/):
66
+ Play with the example:
62
67
 
63
68
  ```sh
64
69
  git clone https://github.com/get-convex/agent.git
65
70
  cd agent
66
71
  npm run setup
67
- npm run example
72
+ npm run dev
68
73
  ```
69
74
 
70
75
  Found a bug? Feature request?
@@ -0,0 +1,46 @@
1
+ import { type UIMessage as AIUIMessage, type UIDataTypes, type UITools } from "ai";
2
+ import type { MessageDoc, MessageStatus, ProviderOptions } from "./validators.js";
3
+ export type UIStatus = "streaming" | MessageStatus;
4
+ export type UIMessage<METADATA = unknown, DATA_PARTS extends UIDataTypes = UIDataTypes, TOOLS extends UITools = UITools> = AIUIMessage<METADATA, DATA_PARTS, TOOLS> & {
5
+ key: string;
6
+ order: number;
7
+ stepOrder: number;
8
+ status: UIStatus;
9
+ agentName?: string;
10
+ userId?: string;
11
+ text: string;
12
+ _creationTime: number;
13
+ };
14
+ /**
15
+ * Converts a list of UIMessages to MessageDocs, along with extra metadata that
16
+ * may be available to associate with the MessageDocs.
17
+ * @param messages - The UIMessages to convert to MessageDocs.
18
+ * @param meta - The metadata to add to the MessageDocs.
19
+ * @returns
20
+ */
21
+ export declare function fromUIMessages<METADATA = unknown>(messages: UIMessage<METADATA>[], meta: {
22
+ threadId: string;
23
+ userId?: string;
24
+ model?: string;
25
+ provider?: string;
26
+ providerOptions?: ProviderOptions;
27
+ metadata?: METADATA;
28
+ }): Promise<(MessageDoc & {
29
+ streaming: boolean;
30
+ metadata?: METADATA;
31
+ })[]>;
32
+ type ExtraFields<METADATA = unknown> = {
33
+ streaming?: boolean;
34
+ metadata?: METADATA;
35
+ };
36
+ /**
37
+ * Converts a list of MessageDocs to UIMessages.
38
+ * This is somewhat lossy, as many fields are not supported by UIMessages, e.g.
39
+ * the model, provider, userId, etc.
40
+ * The UIMessage type is the augmented type that includes more fields such as
41
+ * key, order, stepOrder, status, agentName, text, etc.
42
+ */
43
+ export declare function toUIMessages<METADATA = unknown, DATA_PARTS extends UIDataTypes = UIDataTypes, TOOLS extends UITools = UITools>(messages: (MessageDoc & ExtraFields<METADATA>)[]): UIMessage<METADATA, DATA_PARTS, TOOLS>[];
44
+ export declare function combineUIMessages(messages: UIMessage[]): UIMessage[];
45
+ export {};
46
+ //# sourceMappingURL=UIMessages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UIMessages.d.ts","sourceRoot":"","sources":["../src/UIMessages.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,IAAI,WAAW,EAU7B,KAAK,WAAW,EAChB,KAAK,OAAO,EACb,MAAM,IAAI,CAAC;AAUZ,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,eAAe,EAGhB,MAAM,iBAAiB,CAAC;AAGzB,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;AAEnD,MAAM,MAAM,SAAS,CACnB,QAAQ,GAAG,OAAO,EAClB,UAAU,SAAS,WAAW,GAAG,WAAW,EAC5C,KAAK,SAAS,OAAO,GAAG,OAAO,IAC7B,WAAW,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,GAAG;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,QAAQ,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,QAAQ,GAAG,OAAO,EACrD,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,EAC/B,IAAI,EAAE;IACJ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,GACA,OAAO,CAAC,CAAC,UAAU,GAAG;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,CAAA;CAAE,CAAC,EAAE,CAAC,CAiEvE;AA8BD,KAAK,WAAW,CAAC,QAAQ,GAAG,OAAO,IAAI;IACrC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,GAAG,OAAO,EAClB,UAAU,SAAS,WAAW,GAAG,WAAW,EAC5C,KAAK,SAAS,OAAO,GAAG,OAAO,EAE/B,QAAQ,EAAE,CAAC,UAAU,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,EAAE,GAC/C,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,CAiB1C;AA+dD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CA2CpE"}
@@ -0,0 +1,546 @@
1
+ import { convertToModelMessages, } from "ai";
2
+ import { toModelMessage, fromModelMessage, toUIFilePart } from "./mapping.js";
3
+ import { extractReasoning, extractText, isTool, joinText, sorted, } from "./shared.js";
4
+ import { omit, pick } from "convex-helpers";
5
+ /**
6
+ * Converts a list of UIMessages to MessageDocs, along with extra metadata that
7
+ * may be available to associate with the MessageDocs.
8
+ * @param messages - The UIMessages to convert to MessageDocs.
9
+ * @param meta - The metadata to add to the MessageDocs.
10
+ * @returns
11
+ */
12
+ export async function fromUIMessages(messages, meta) {
13
+ const nested = await Promise.all(messages.map(async (uiMessage) => {
14
+ const stepOrder = uiMessage.stepOrder;
15
+ const commonFields = {
16
+ ...pick(meta, [
17
+ "threadId",
18
+ "userId",
19
+ "model",
20
+ "provider",
21
+ "providerOptions",
22
+ "metadata",
23
+ ]),
24
+ ...omit(uiMessage, ["parts", "role", "key", "text", "userId"]),
25
+ userId: uiMessage.userId ?? meta.userId,
26
+ status: uiMessage.status === "streaming" ? "pending" : "success",
27
+ streaming: uiMessage.status === "streaming",
28
+ // to override
29
+ _id: uiMessage.id,
30
+ tool: false,
31
+ };
32
+ const modelMessages = await convertToModelMessages([uiMessage]);
33
+ return modelMessages
34
+ .map((modelMessage, i) => {
35
+ if (modelMessage.content.length === 0) {
36
+ return undefined;
37
+ }
38
+ const message = fromModelMessage(modelMessage);
39
+ const tool = isTool(message);
40
+ const doc = {
41
+ ...commonFields,
42
+ _id: uiMessage.id + `-${i}`,
43
+ stepOrder: stepOrder + i,
44
+ message,
45
+ tool,
46
+ text: extractText(message),
47
+ reasoning: extractReasoning(message),
48
+ finishReason: tool ? "tool-calls" : "stop",
49
+ sources: fromSourceParts(uiMessage.parts),
50
+ };
51
+ if (Array.isArray(modelMessage.content)) {
52
+ // Find a content part with providerOptions (type assertion needed for SDK compatibility)
53
+ const partWithProviderOptions = modelMessage.content.find((c) => "providerOptions" in c && c.providerOptions !== undefined);
54
+ if (partWithProviderOptions?.providerOptions) {
55
+ // convertToModelMessages changes providerMetadata to providerOptions
56
+ const providerOptions = partWithProviderOptions.providerOptions;
57
+ if (providerOptions) {
58
+ doc.providerMetadata = providerOptions;
59
+ doc.providerOptions ??= providerOptions;
60
+ }
61
+ }
62
+ }
63
+ return doc;
64
+ })
65
+ .filter((d) => d !== undefined);
66
+ }));
67
+ return nested.flat();
68
+ }
69
+ function fromSourceParts(parts) {
70
+ return parts
71
+ .map((part) => {
72
+ if (part.type === "source-url") {
73
+ return {
74
+ type: "source",
75
+ sourceType: "url",
76
+ url: part.url,
77
+ id: part.sourceId,
78
+ providerMetadata: part.providerMetadata,
79
+ title: part.title,
80
+ };
81
+ }
82
+ if (part.type === "source-document") {
83
+ return {
84
+ type: "source",
85
+ sourceType: "document",
86
+ mediaType: part.mediaType,
87
+ id: part.sourceId,
88
+ providerMetadata: part.providerMetadata,
89
+ title: part.title,
90
+ };
91
+ }
92
+ return undefined;
93
+ })
94
+ .filter((p) => p !== undefined);
95
+ }
96
+ /**
97
+ * Converts a list of MessageDocs to UIMessages.
98
+ * This is somewhat lossy, as many fields are not supported by UIMessages, e.g.
99
+ * the model, provider, userId, etc.
100
+ * The UIMessage type is the augmented type that includes more fields such as
101
+ * key, order, stepOrder, status, agentName, text, etc.
102
+ */
103
+ export function toUIMessages(messages) {
104
+ // Group assistant and tool messages together
105
+ const assistantGroups = groupAssistantMessages(sorted(messages));
106
+ const uiMessages = [];
107
+ for (const group of assistantGroups) {
108
+ if (group.role === "system") {
109
+ uiMessages.push(createSystemUIMessage(group.message));
110
+ }
111
+ else if (group.role === "user") {
112
+ uiMessages.push(createUserUIMessage(group.message));
113
+ }
114
+ else {
115
+ // Assistant/tool group
116
+ uiMessages.push(createAssistantUIMessage(group.messages));
117
+ }
118
+ }
119
+ return uiMessages;
120
+ }
121
+ function groupAssistantMessages(messages) {
122
+ const groups = [];
123
+ let currentAssistantGroup = [];
124
+ let currentOrder;
125
+ for (const message of messages) {
126
+ const coreMessage = message.message && toModelMessage(message.message);
127
+ if (!coreMessage)
128
+ continue;
129
+ if (coreMessage.role === "user" || coreMessage.role === "system") {
130
+ // Finish any current assistant group
131
+ if (currentAssistantGroup.length > 0) {
132
+ groups.push({
133
+ role: "assistant",
134
+ messages: currentAssistantGroup,
135
+ });
136
+ currentAssistantGroup = [];
137
+ currentOrder = undefined;
138
+ }
139
+ // Add singleton group
140
+ groups.push({
141
+ role: coreMessage.role,
142
+ message,
143
+ });
144
+ }
145
+ else {
146
+ // Assistant or tool message
147
+ // Start new group if order changes or this is the first assistant/tool message
148
+ if (currentOrder !== undefined && message.order !== currentOrder) {
149
+ if (currentAssistantGroup.length > 0) {
150
+ groups.push({
151
+ role: "assistant",
152
+ messages: currentAssistantGroup,
153
+ });
154
+ currentAssistantGroup = [];
155
+ }
156
+ }
157
+ currentOrder = message.order;
158
+ currentAssistantGroup.push(message);
159
+ // End group if this is an assistant message without tool calls
160
+ if (coreMessage.role === "assistant" && !message.tool) {
161
+ groups.push({
162
+ role: "assistant",
163
+ messages: currentAssistantGroup,
164
+ });
165
+ currentAssistantGroup = [];
166
+ currentOrder = undefined;
167
+ }
168
+ }
169
+ }
170
+ // Add any remaining assistant group
171
+ if (currentAssistantGroup.length > 0) {
172
+ groups.push({
173
+ role: "assistant",
174
+ messages: currentAssistantGroup,
175
+ });
176
+ }
177
+ return groups;
178
+ }
179
+ function createSystemUIMessage(message) {
180
+ const text = extractTextFromMessageDoc(message);
181
+ const partCommon = {
182
+ state: message.streaming ? "streaming" : "done",
183
+ ...(message.providerMetadata
184
+ ? { providerMetadata: message.providerMetadata }
185
+ : {}),
186
+ };
187
+ return {
188
+ id: message._id,
189
+ _creationTime: message._creationTime,
190
+ order: message.order,
191
+ stepOrder: message.stepOrder,
192
+ status: message.streaming ? "streaming" : message.status,
193
+ key: `${message.threadId}-${message.order}-${message.stepOrder}`,
194
+ text,
195
+ role: "system",
196
+ agentName: message.agentName,
197
+ userId: message.userId,
198
+ parts: [{ type: "text", text, ...partCommon }],
199
+ metadata: message.metadata,
200
+ };
201
+ }
202
+ function extractTextFromMessageDoc(message) {
203
+ return ((message.message && extractText(message.message)) || message.text || "");
204
+ }
205
+ function createUserUIMessage(message) {
206
+ const text = extractTextFromMessageDoc(message);
207
+ const coreMessage = toModelMessage(message.message);
208
+ const content = coreMessage.content;
209
+ const nonStringContent = content && typeof content !== "string" ? content : [];
210
+ const partCommon = {
211
+ state: message.streaming ? "streaming" : "done",
212
+ ...(message.providerMetadata
213
+ ? { providerMetadata: message.providerMetadata }
214
+ : {}),
215
+ };
216
+ const parts = [];
217
+ if (text && !nonStringContent.length) {
218
+ parts.push({ type: "text", text });
219
+ }
220
+ for (const contentPart of nonStringContent) {
221
+ switch (contentPart.type) {
222
+ case "text":
223
+ parts.push({ type: "text", text: contentPart.text, ...partCommon });
224
+ break;
225
+ case "file":
226
+ case "image":
227
+ parts.push(toUIFilePart(contentPart));
228
+ break;
229
+ default:
230
+ console.warn("Unknown content part type for user", contentPart);
231
+ break;
232
+ }
233
+ }
234
+ return {
235
+ id: message._id,
236
+ _creationTime: message._creationTime,
237
+ order: message.order,
238
+ stepOrder: message.stepOrder,
239
+ status: message.streaming ? "streaming" : message.status,
240
+ key: `${message.threadId}-${message.order}-${message.stepOrder}`,
241
+ text,
242
+ role: "user",
243
+ userId: message.userId,
244
+ parts,
245
+ metadata: message.metadata,
246
+ };
247
+ }
248
+ function createAssistantUIMessage(groupUnordered) {
249
+ const group = sorted(groupUnordered);
250
+ const firstMessage = group[0];
251
+ // Use first message for special fields
252
+ const common = {
253
+ id: firstMessage._id,
254
+ _creationTime: firstMessage._creationTime,
255
+ order: firstMessage.order,
256
+ stepOrder: firstMessage.stepOrder,
257
+ key: `${firstMessage.threadId}-${firstMessage.order}-${firstMessage.stepOrder}`,
258
+ agentName: firstMessage.agentName,
259
+ userId: firstMessage.userId,
260
+ };
261
+ // Get status from last message
262
+ const lastMessage = group[group.length - 1];
263
+ const status = lastMessage.streaming
264
+ ? "streaming"
265
+ : lastMessage.status;
266
+ // Collect all parts from all messages
267
+ const allParts = [];
268
+ for (const message of group) {
269
+ const coreMessage = message.message && toModelMessage(message.message);
270
+ if (!coreMessage)
271
+ continue;
272
+ const content = coreMessage.content;
273
+ const nonStringContent = content && typeof content !== "string" ? content : [];
274
+ const text = extractTextFromMessageDoc(message);
275
+ const partCommon = {
276
+ state: message.streaming ? "streaming" : "done",
277
+ ...(message.providerMetadata
278
+ ? { providerMetadata: message.providerMetadata }
279
+ : {}),
280
+ };
281
+ // Add reasoning parts
282
+ if (message.reasoning &&
283
+ !nonStringContent.some((c) => c.type === "reasoning")) {
284
+ allParts.push({
285
+ type: "reasoning",
286
+ text: message.reasoning,
287
+ ...partCommon,
288
+ });
289
+ }
290
+ // Add text parts if no structured content
291
+ if (text && !nonStringContent.length) {
292
+ allParts.push({
293
+ type: "text",
294
+ text: text,
295
+ ...partCommon,
296
+ });
297
+ }
298
+ // Add all structured content parts
299
+ for (const contentPart of nonStringContent) {
300
+ switch (contentPart.type) {
301
+ case "text":
302
+ allParts.push({
303
+ ...partCommon,
304
+ ...contentPart,
305
+ });
306
+ break;
307
+ case "reasoning":
308
+ allParts.push({
309
+ ...partCommon,
310
+ ...contentPart,
311
+ });
312
+ break;
313
+ case "file":
314
+ case "image":
315
+ allParts.push(toUIFilePart(contentPart));
316
+ break;
317
+ case "tool-call": {
318
+ allParts.push({
319
+ type: "step-start",
320
+ });
321
+ const toolPart = {
322
+ type: `tool-${contentPart.toolName}`,
323
+ toolCallId: contentPart.toolCallId,
324
+ input: contentPart.input,
325
+ providerExecuted: contentPart.providerExecuted,
326
+ ...(message.streaming
327
+ ? { state: "input-streaming" }
328
+ : {
329
+ state: "input-available",
330
+ callProviderMetadata: message.providerMetadata,
331
+ }),
332
+ };
333
+ allParts.push(toolPart);
334
+ break;
335
+ }
336
+ case "tool-result": {
337
+ const typedPart = contentPart;
338
+ // Check if this is an execution-denied result
339
+ if (typedPart.output?.type === "execution-denied") {
340
+ const call = allParts.find((part) => part.type === `tool-${contentPart.toolName}` &&
341
+ "toolCallId" in part &&
342
+ part.toolCallId === contentPart.toolCallId);
343
+ if (call) {
344
+ call.state = "output-denied";
345
+ if (!("approval" in call) || !call.approval) {
346
+ call.approval = {
347
+ id: "",
348
+ approved: false,
349
+ reason: typedPart.output.reason,
350
+ };
351
+ }
352
+ else {
353
+ const approval = call.approval;
354
+ approval.approved = false;
355
+ approval.reason = typedPart.output.reason;
356
+ }
357
+ }
358
+ break;
359
+ }
360
+ const output = typeof typedPart.output?.type === "string"
361
+ ? typedPart.output.value
362
+ : typedPart.output;
363
+ // Check for error at both the content part level (isError) and message level
364
+ // isError may exist on stored tool results but isn't in ToolResultPart type
365
+ const hasError = contentPart.isError || message.error;
366
+ const errorText = message.error || (hasError ? String(output) : undefined);
367
+ const call = allParts.find((part) => part.type === `tool-${contentPart.toolName}` &&
368
+ "toolCallId" in part &&
369
+ part.toolCallId === contentPart.toolCallId);
370
+ if (call) {
371
+ if (hasError) {
372
+ call.state = "output-error";
373
+ call.errorText = errorText ?? "Unknown error";
374
+ call.output = output;
375
+ }
376
+ else {
377
+ call.state = "output-available";
378
+ call.output = output;
379
+ }
380
+ }
381
+ else {
382
+ // Tool call is on a previous page - create standalone tool part
383
+ if (hasError) {
384
+ allParts.push({
385
+ type: `tool-${contentPart.toolName}`,
386
+ toolCallId: contentPart.toolCallId,
387
+ state: "output-error",
388
+ input: undefined,
389
+ errorText: errorText ?? "Unknown error",
390
+ callProviderMetadata: message.providerMetadata,
391
+ });
392
+ }
393
+ else {
394
+ allParts.push({
395
+ type: `tool-${contentPart.toolName}`,
396
+ toolCallId: contentPart.toolCallId,
397
+ state: "output-available",
398
+ input: undefined,
399
+ output,
400
+ callProviderMetadata: message.providerMetadata,
401
+ });
402
+ }
403
+ }
404
+ break;
405
+ }
406
+ case "tool-approval-request": {
407
+ // Find the matching tool call
408
+ const typedPart = contentPart;
409
+ const toolCallPart = allParts.find((part) => "toolCallId" in part && part.toolCallId === typedPart.toolCallId);
410
+ if (toolCallPart) {
411
+ toolCallPart.state = "approval-requested";
412
+ toolCallPart.approval = {
413
+ id: typedPart.approvalId,
414
+ };
415
+ }
416
+ else {
417
+ console.warn("Tool approval request without preceding tool call", contentPart);
418
+ }
419
+ break;
420
+ }
421
+ case "tool-approval-response": {
422
+ // Find the tool call that has this approval by matching approval.id
423
+ const typedPart = contentPart;
424
+ const toolCallPart = allParts.find((part) => "approval" in part &&
425
+ part.approval
426
+ ?.id === typedPart.approvalId);
427
+ if (toolCallPart) {
428
+ if (typedPart.approved) {
429
+ toolCallPart.state = "approval-responded";
430
+ toolCallPart.approval = {
431
+ id: typedPart.approvalId,
432
+ approved: true,
433
+ reason: typedPart.reason,
434
+ };
435
+ }
436
+ else {
437
+ toolCallPart.state = "output-denied";
438
+ toolCallPart.approval = {
439
+ id: typedPart.approvalId,
440
+ approved: false,
441
+ reason: typedPart.reason,
442
+ };
443
+ }
444
+ }
445
+ else {
446
+ console.warn("Tool approval response without matching approval request", contentPart);
447
+ }
448
+ break;
449
+ }
450
+ default: {
451
+ const maybeSource = contentPart;
452
+ if (maybeSource.type === "source") {
453
+ allParts.push(toSourcePart(maybeSource));
454
+ }
455
+ else {
456
+ console.warn("Unknown content part type for assistant", contentPart);
457
+ }
458
+ }
459
+ }
460
+ }
461
+ // Add source parts
462
+ for (const source of message.sources ?? []) {
463
+ allParts.push(toSourcePart(source));
464
+ }
465
+ }
466
+ return {
467
+ ...common,
468
+ role: "assistant",
469
+ text: joinText(allParts),
470
+ status,
471
+ parts: allParts,
472
+ metadata: group.find((m) => m.metadata)?.metadata,
473
+ };
474
+ }
475
+ function toSourcePart(part) {
476
+ if (part.sourceType === "url") {
477
+ return {
478
+ type: "source-url",
479
+ url: part.url,
480
+ sourceId: part.id,
481
+ providerMetadata: part.providerMetadata,
482
+ title: part.title,
483
+ };
484
+ }
485
+ return {
486
+ type: "source-document",
487
+ mediaType: part.mediaType,
488
+ sourceId: part.id,
489
+ title: part.title,
490
+ filename: part.filename,
491
+ providerMetadata: part.providerMetadata,
492
+ };
493
+ }
494
+ export function combineUIMessages(messages) {
495
+ const combined = messages.reduce((acc, message) => {
496
+ if (!acc.length) {
497
+ return [message];
498
+ }
499
+ const previous = acc.at(-1);
500
+ if (message.order !== previous.order ||
501
+ previous.role !== message.role ||
502
+ message.role !== "assistant") {
503
+ acc.push(message);
504
+ return acc;
505
+ }
506
+ // We will replace it with a combined message
507
+ acc.pop();
508
+ const newParts = [...previous.parts];
509
+ for (const part of message.parts) {
510
+ const toolCallId = getToolCallId(part);
511
+ if (!toolCallId) {
512
+ newParts.push(part);
513
+ continue;
514
+ }
515
+ const previousPartIndex = newParts.findIndex((p) => getToolCallId(p) === toolCallId);
516
+ if (previousPartIndex === -1) {
517
+ // Tool call not found in previous parts, add it as new
518
+ newParts.push(part);
519
+ continue;
520
+ }
521
+ const previousPart = newParts.splice(previousPartIndex, 1)[0];
522
+ newParts.push(mergeParts(previousPart, part));
523
+ }
524
+ acc.push({
525
+ ...previous,
526
+ ...pick(message, ["status", "metadata", "agentName"]),
527
+ parts: newParts,
528
+ text: joinText(newParts),
529
+ });
530
+ return acc;
531
+ }, []);
532
+ return combined;
533
+ }
534
+ function getToolCallId(part) {
535
+ return part.toolCallId;
536
+ }
537
+ function mergeParts(previousPart, part) {
538
+ const merged = { ...previousPart };
539
+ for (const [key, value] of Object.entries(part)) {
540
+ if (value !== undefined) {
541
+ merged[key] = value;
542
+ }
543
+ }
544
+ return merged;
545
+ }
546
+ //# sourceMappingURL=UIMessages.js.map