@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,93 +1,279 @@
1
- import type { Schema, Tool, ToolCallOptions, ToolSet } from "ai";
1
+ import type { ToolResultOutput } from "@ai-sdk/provider-utils";
2
+ import type {
3
+ FlexibleSchema,
4
+ ModelMessage,
5
+ Tool,
6
+ ToolExecutionOptions,
7
+ ToolSet,
8
+ } from "ai";
2
9
  import { tool } from "ai";
3
- import { z } from "zod";
4
- import type { Agent } from "./index.js";
5
10
  import type { GenericActionCtx, GenericDataModel } from "convex/server";
6
11
  import type { ProviderOptions } from "../validators.js";
12
+ import type { Agent } from "./index.js";
13
+
14
+ const MIGRATION_URL = "node_modules/@convex-dev/agent/MIGRATION.md";
15
+ const warnedDeprecations = new Set<string>();
16
+ function warnDeprecation(key: string, message: string) {
17
+ if (!warnedDeprecations.has(key)) {
18
+ warnedDeprecations.add(key);
19
+ console.warn(`[@convex-dev/agent] ${message}\n See: ${MIGRATION_URL}`);
20
+ }
21
+ }
7
22
 
8
23
  export type ToolCtx<DataModel extends GenericDataModel = GenericDataModel> =
9
24
  GenericActionCtx<DataModel> & {
10
- agent: Agent;
25
+ agent?: Agent;
11
26
  userId?: string;
12
27
  threadId?: string;
13
28
  messageId?: string;
14
29
  };
15
30
 
16
31
  /**
17
- * This is a wrapper around the ai.tool function that adds extra context to the
18
- * tool call, including the action context, userId, threadId, and messageId.
19
- * @param tool The tool. See https://sdk.vercel.ai/docs/ai-sdk-core/tools-and-tool-calling
20
- * but swap parameters for args and handler for execute.
21
- * @returns A tool to be used with the AI SDK.
32
+ * Function that is called to determine if the tool needs approval before it can be executed.
22
33
  */
23
- export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(def: {
24
- /**
25
- An optional description of what the tool does.
26
- Will be used by the language model to decide whether to use the tool.
27
- Not used for provider-defined tools.
34
+ export type ToolNeedsApprovalFunctionCtx<
35
+ INPUT,
36
+ Ctx extends ToolCtx = ToolCtx,
37
+ > = (
38
+ ctx: Ctx,
39
+ input: INPUT,
40
+ options: {
41
+ /**
42
+ * The ID of the tool call. You can use it e.g. when sending tool-call related information with stream data.
28
43
  */
29
- description?: string;
30
- /**
31
- The schema of the input that the tool expects. The language model will use this to generate the input.
32
- It is also used to validate the output of the language model.
33
- Use descriptions to make the input understandable for the language model.
44
+ toolCallId: string;
45
+ /**
46
+ * Messages that were sent to the language model to initiate the response that contained the tool call.
47
+ * The messages **do not** include the system prompt nor the assistant response that contained the tool call.
34
48
  */
35
- args: ToolParameters<INPUT>;
36
- /**
37
- An async function that is called with the arguments from the tool call and produces a result.
38
- If not provided, the tool will not be executed automatically.
49
+ messages: ModelMessage[];
50
+ /**
51
+ * Additional context.
52
+ *
53
+ * Experimental (can break in patch releases).
54
+ */
55
+ experimental_context?: unknown;
56
+ },
57
+ ) => boolean | PromiseLike<boolean>;
39
58
 
40
- @args is the input of the tool call.
41
- @options.abortSignal is a signal that can be used to abort the tool call.
59
+ export type ToolExecuteFunctionCtx<
60
+ INPUT,
61
+ OUTPUT,
62
+ Ctx extends ToolCtx = ToolCtx,
63
+ > = (
64
+ ctx: Ctx,
65
+ input: INPUT,
66
+ options: ToolExecutionOptions,
67
+ ) => AsyncIterable<OUTPUT> | PromiseLike<OUTPUT>;
68
+
69
+ type NeverOptional<N, T> = 0 extends 1 & N
70
+ ? Partial<T>
71
+ : [N] extends [never]
72
+ ? Partial<Record<keyof T, undefined>>
73
+ : T;
74
+
75
+ /**
76
+ * Error message type for deprecated 'handler' property.
77
+ * Using a string literal type causes TypeScript to show this message in errors.
78
+ */
79
+ type HANDLER_REMOVED_ERROR =
80
+ "⚠️ 'handler' was removed in @convex-dev/agent v0.6.0. Rename to 'execute'. See: node_modules/@convex-dev/agent/MIGRATION.md";
81
+
82
+ export type ToolOutputPropertiesCtx<
83
+ INPUT,
84
+ OUTPUT,
85
+ Ctx extends ToolCtx = ToolCtx,
86
+ > = NeverOptional<
87
+ OUTPUT,
88
+ {
89
+ /**
90
+ * An async function that is called with the arguments from the tool call and produces a result.
91
+ * If `execute` is not provided, the tool will not be executed automatically.
92
+ *
93
+ * @param input - The input of the tool call.
94
+ * @param options.abortSignal - A signal that can be used to abort the tool call.
42
95
  */
43
- handler: (
44
- ctx: Ctx,
45
- args: INPUT,
46
- options: ToolCallOptions,
47
- ) => PromiseLike<OUTPUT>;
48
- /**
49
- * Provide the context to use, e.g. when defining the tool at runtime.
50
- */
51
- ctx?: Ctx;
52
- /**
53
- * Optional function that is called when the argument streaming starts.
54
- * Only called when the tool is used in a streaming context.
55
- */
56
- onInputStart?: (
57
- ctx: Ctx,
58
- options: ToolCallOptions,
59
- ) => void | PromiseLike<void>;
96
+ execute?: ToolExecuteFunctionCtx<INPUT, OUTPUT, Ctx>;
97
+ outputSchema?: FlexibleSchema<OUTPUT>;
98
+ /**
99
+ * @deprecated Removed in v0.6.0. Use `execute` instead.
100
+ */
101
+ handler?: HANDLER_REMOVED_ERROR;
102
+ }
103
+ >;
104
+
105
+ /**
106
+ * Error message type for deprecated 'args' property.
107
+ * Using a string literal type causes TypeScript to show this message in errors.
108
+ */
109
+ type ARGS_REMOVED_ERROR =
110
+ "⚠️ 'args' was removed in @convex-dev/agent v0.6.0. Rename to 'inputSchema'. See: node_modules/@convex-dev/agent/MIGRATION.md";
111
+
112
+ export type ToolInputProperties<INPUT> = {
60
113
  /**
61
- * Optional function that is called when an argument streaming delta is available.
62
- * Only called when the tool is used in a streaming context.
114
+ * The schema of the input that the tool expects.
115
+ * The language model will use this to generate the input.
116
+ * It is also used to validate the output of the language model.
117
+ *
118
+ * You can use descriptions on the schema properties to make the input understandable for the language model.
63
119
  */
64
- onInputDelta?: (
65
- ctx: Ctx,
66
- options: {
67
- inputTextDelta: string;
68
- } & ToolCallOptions,
69
- ) => void | PromiseLike<void>;
120
+ inputSchema: FlexibleSchema<INPUT>;
70
121
  /**
71
- * Optional function that is called when a tool call can be started,
72
- * even if the execute function is not provided.
122
+ * @deprecated Removed in v0.6.0. Use `inputSchema` instead.
73
123
  */
74
- onInputAvailable?: (
75
- ctx: Ctx,
76
- options: {
77
- input: [INPUT] extends [never] ? undefined : INPUT;
78
- } & ToolCallOptions,
79
- ) => void | PromiseLike<void>;
80
-
81
- // Extra AI SDK pass-through options.
82
- providerOptions?: ProviderOptions;
83
- }): Tool<INPUT, OUTPUT> {
84
- const t = tool({
124
+ args?: ARGS_REMOVED_ERROR;
125
+ };
126
+
127
+ /**
128
+ * This is a wrapper around the ai.tool function that adds extra context to the
129
+ * tool call, including the action context, userId, threadId, and messageId.
130
+ * @param tool The tool. See https://sdk.vercel.ai/docs/ai-sdk-core/tools-and-tool-calling
131
+ * Currently contains deprecated parameters `args` and `handler` to maintain backwards compatibility
132
+ * but these will be removed in the future. Use `inputSchema` and `execute` instead, respectively.
133
+ *
134
+ * @returns A tool to be used with the AI SDK.
135
+ */
136
+ export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(
137
+ def: {
138
+ /**
139
+ * An optional description of what the tool does.
140
+ * Will be used by the language model to decide whether to use the tool.
141
+ * Not used for provider-defined tools.
142
+ */
143
+ description?: string;
144
+ /**
145
+ * An optional title of the tool.
146
+ */
147
+ title?: string;
148
+ /**
149
+ * Additional provider-specific metadata. They are passed through
150
+ * to the provider from the AI SDK and enable provider-specific
151
+ * functionality that can be fully encapsulated in the provider.
152
+ */
153
+ providerOptions?: ProviderOptions;
154
+ } & ToolInputProperties<INPUT> & {
155
+ /**
156
+ * An optional list of input examples that show the language
157
+ * model what the input should look like.
158
+ */
159
+ inputExamples?: Array<{
160
+ input: NoInfer<INPUT>;
161
+ }>;
162
+ /**
163
+ * Whether the tool needs approval before it can be executed.
164
+ */
165
+ needsApproval?:
166
+ | boolean
167
+ | ToolNeedsApprovalFunctionCtx<
168
+ [INPUT] extends [never] ? unknown : INPUT,
169
+ Ctx
170
+ >;
171
+ /**
172
+ * Strict mode setting for the tool.
173
+ *
174
+ * Providers that support strict mode will use this setting to determine
175
+ * how the input should be generated. Strict mode will always produce
176
+ * valid inputs, but it might limit what input schemas are supported.
177
+ */
178
+ strict?: boolean;
179
+ /**
180
+ * Provide the context to use, e.g. when defining the tool at runtime.
181
+ */
182
+ ctx?: Ctx;
183
+ /**
184
+ * Optional function that is called when the argument streaming starts.
185
+ * Only called when the tool is used in a streaming context.
186
+ */
187
+ onInputStart?: (
188
+ ctx: Ctx,
189
+ options: ToolExecutionOptions,
190
+ ) => void | PromiseLike<void>;
191
+ /**
192
+ * Optional function that is called when an argument streaming delta is available.
193
+ * Only called when the tool is used in a streaming context.
194
+ */
195
+ onInputDelta?: (
196
+ ctx: Ctx,
197
+ options: { inputTextDelta: string } & ToolExecutionOptions,
198
+ ) => void | PromiseLike<void>;
199
+ /**
200
+ * Optional function that is called when a tool call can be started,
201
+ * even if the execute function is not provided.
202
+ */
203
+ onInputAvailable?: (
204
+ ctx: Ctx,
205
+ options: {
206
+ input: [INPUT] extends [never] ? unknown : INPUT;
207
+ } & ToolExecutionOptions,
208
+ ) => void | PromiseLike<void>;
209
+ } & ToolOutputPropertiesCtx<INPUT, OUTPUT, Ctx> & {
210
+ /**
211
+ * Optional conversion function that maps the tool result to an output that can be used by the language model.
212
+ *
213
+ * If not provided, the tool result will be sent as a JSON object.
214
+ */
215
+ toModelOutput?: (
216
+ ctx: Ctx,
217
+ options: {
218
+ /**
219
+ * The ID of the tool call. You can use it e.g. when sending tool-call related information with stream data.
220
+ */
221
+ toolCallId: string;
222
+ /**
223
+ * The input of the tool call.
224
+ */
225
+ input: [INPUT] extends [never] ? unknown : INPUT;
226
+ /**
227
+ * The output of the tool call.
228
+ */
229
+ output: 0 extends 1 & OUTPUT
230
+ ? any
231
+ : [OUTPUT] extends [never]
232
+ ? any
233
+ : NoInfer<OUTPUT>;
234
+ },
235
+ ) => ToolResultOutput | PromiseLike<ToolResultOutput>;
236
+ },
237
+ ): Tool<INPUT, OUTPUT> {
238
+ // Runtime backwards compat - types will show errors but runtime still works
239
+ const inputSchema = def.inputSchema ?? (def as any).args;
240
+ if (!inputSchema)
241
+ throw new Error("To use a Convex tool, you must provide an `inputSchema`");
242
+
243
+ if ((def as any).args && !def.inputSchema) {
244
+ warnDeprecation(
245
+ "createTool.args",
246
+ "createTool: 'args' is deprecated. Use 'inputSchema' instead.",
247
+ );
248
+ }
249
+ if ((def as any).handler && !def.execute) {
250
+ warnDeprecation(
251
+ "createTool.handler",
252
+ "createTool: 'handler' is deprecated. Use 'execute' instead.",
253
+ );
254
+ }
255
+
256
+ const executeHandler = def.execute ?? (def as any).handler;
257
+ if (!executeHandler && !def.outputSchema)
258
+ throw new Error(
259
+ "To use a Convex tool, you must either provide an execute" +
260
+ " handler function, define an outputSchema, or both",
261
+ );
262
+
263
+ const t = tool<INPUT, OUTPUT>({
85
264
  type: "function",
86
265
  __acceptsCtx: true,
87
266
  ctx: def.ctx,
88
267
  description: def.description,
89
- inputSchema: def.args,
90
- async execute(args: INPUT, options: ToolCallOptions) {
268
+ title: def.title,
269
+ providerOptions: def.providerOptions,
270
+ inputSchema,
271
+ inputExamples: def.inputExamples,
272
+ needsApproval(this: Tool<INPUT, OUTPUT>, input, options) {
273
+ const needsApproval = def.needsApproval;
274
+ if (!needsApproval || typeof needsApproval === "boolean")
275
+ return Boolean(needsApproval);
276
+
91
277
  if (!getCtx(this)) {
92
278
  throw new Error(
93
279
  "To use a Convex tool, you must either provide the ctx" +
@@ -95,23 +281,56 @@ export function createTool<INPUT, OUTPUT, Ctx extends ToolCtx = ToolCtx>(def: {
95
281
  " call it (which injects the ctx, userId and threadId)",
96
282
  );
97
283
  }
98
- return def.handler(getCtx(this), args, options);
284
+ return needsApproval(getCtx(this), input, options);
99
285
  },
100
- providerOptions: def.providerOptions,
286
+ strict: def.strict,
287
+ ...(executeHandler
288
+ ? {
289
+ execute(
290
+ this: Tool<INPUT, OUTPUT>,
291
+ input: INPUT,
292
+ options: ToolExecutionOptions,
293
+ ) {
294
+ if (!getCtx(this)) {
295
+ throw new Error(
296
+ "To use a Convex tool, you must either provide the ctx" +
297
+ " at definition time (dynamically in an action), or use the Agent to" +
298
+ " call it (which injects the ctx, userId and threadId)",
299
+ );
300
+ }
301
+ return executeHandler(getCtx(this), input, options);
302
+ },
303
+ }
304
+ : {}),
305
+ outputSchema: def.outputSchema,
101
306
  });
102
307
  if (def.onInputStart) {
103
- t.onInputStart = def.onInputStart.bind(t, getCtx(t));
308
+ const origOnInputStart = def.onInputStart;
309
+ t.onInputStart = function (this: Tool<INPUT, OUTPUT>, options) {
310
+ return origOnInputStart.call(this, getCtx(this), options);
311
+ };
104
312
  }
105
313
  if (def.onInputDelta) {
106
- t.onInputDelta = def.onInputDelta.bind(t, getCtx(t));
314
+ const origOnInputDelta = def.onInputDelta;
315
+ t.onInputDelta = function (this: Tool<INPUT, OUTPUT>, options) {
316
+ return origOnInputDelta.call(this, getCtx(this), options);
317
+ };
107
318
  }
108
319
  if (def.onInputAvailable) {
109
- t.onInputAvailable = def.onInputAvailable.bind(t, getCtx(t));
320
+ const origOnInputAvailable = def.onInputAvailable;
321
+ t.onInputAvailable = function (this: Tool<INPUT, OUTPUT>, options) {
322
+ return origOnInputAvailable.call(this, getCtx(this), options);
323
+ };
324
+ }
325
+ if (def.toModelOutput) {
326
+ const origToModelOutput = def.toModelOutput;
327
+ t.toModelOutput = function (this: Tool<INPUT, OUTPUT>, options) {
328
+ return origToModelOutput.call(this, getCtx(this), options);
329
+ };
110
330
  }
111
331
  return t;
112
332
  }
113
333
 
114
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
115
334
  function getCtx<Ctx extends ToolCtx>(tool: any): Ctx {
116
335
  return (tool as { ctx: Ctx }).ctx;
117
336
  }
@@ -126,8 +345,7 @@ export function wrapTools(
126
345
  continue;
127
346
  }
128
347
  for (const [name, tool] of Object.entries(toolSet)) {
129
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
130
- if (!(tool as any).__acceptsCtx) {
348
+ if (tool && !(tool as any).__acceptsCtx) {
131
349
  output[name] = tool;
132
350
  } else {
133
351
  const out = { ...tool, ctx };
@@ -137,6 +355,3 @@ export function wrapTools(
137
355
  }
138
356
  return output;
139
357
  }
140
-
141
- // Vendoring in from "ai" package since it wasn't exported
142
- type ToolParameters<T> = z.Schema<T> | Schema<T>;
@@ -0,0 +1,17 @@
1
+ import { componentsGeneric } from "convex/server";
2
+ import type { AgentComponent } from "./types.js";
3
+
4
+ export type CustomComponent = {
5
+ /**
6
+ * If you have a custom name for the agent component, you can pass it here
7
+ * as components.myAgentName.
8
+ */
9
+ component?: AgentComponent;
10
+ };
11
+
12
+ export function componentAPI(args?: CustomComponent): AgentComponent {
13
+ return args?.component ?? defaultComponent;
14
+ }
15
+
16
+ export const defaultComponent = componentsGeneric()
17
+ .agent as unknown as AgentComponent;
@@ -12,7 +12,7 @@ import { v } from "convex/values";
12
12
  import {
13
13
  createThread as createThread_,
14
14
  listMessages as listMessages_,
15
- deserializeMessage,
15
+ toModelMessage,
16
16
  vContextOptions,
17
17
  vMessage,
18
18
  vMessageDoc,
@@ -21,7 +21,15 @@ import {
21
21
  vThreadDoc,
22
22
  type Agent,
23
23
  type AgentComponent,
24
+ vStreamArgs,
25
+ syncStreams,
26
+ vStreamMessagesReturnValue,
27
+ isTool,
28
+ extractText,
29
+ type MessageDoc,
24
30
  } from "./index.js";
31
+ import { serializeNewMessagesInStep } from "../mapping.js";
32
+ import { getModelName, getProviderName } from "../shared.js";
25
33
 
26
34
  export type PlaygroundAPI = ApiFromModules<{
27
35
  playground: ReturnType<typeof definePlaygroundAPI>;
@@ -61,9 +69,7 @@ export function definePlaygroundAPI<DataModel extends GenericDataModel>(
61
69
  }
62
70
 
63
71
  const isApiKeyValid = queryGeneric({
64
- args: {
65
- apiKey: v.string(),
66
- },
72
+ args: { apiKey: v.string() },
67
73
  handler: async (ctx, args) => {
68
74
  try {
69
75
  await validateApiKey(ctx, args.apiKey);
@@ -107,17 +113,14 @@ export function definePlaygroundAPI<DataModel extends GenericDataModel>(
107
113
  instructions: agent.options.instructions,
108
114
  contextOptions: agent.options.contextOptions,
109
115
  storageOptions: agent.options.storageOptions,
110
- maxRetries: agent.options.maxRetries,
116
+ maxRetries: agent.options.callSettings?.maxRetries,
111
117
  tools: agent.options.tools ? Object.keys(agent.options.tools) : [],
112
118
  }));
113
119
  },
114
120
  });
115
121
 
116
122
  const listUsers = queryGeneric({
117
- args: {
118
- apiKey: v.string(),
119
- paginationOpts: paginationOptsValidator,
120
- },
123
+ args: { apiKey: v.string(), paginationOpts: paginationOptsValidator },
121
124
  handler: async (ctx, args) => {
122
125
  await validateApiKey(ctx, args.apiKey);
123
126
  const users = await ctx.runQuery(component.users.listUsersWithThreads, {
@@ -133,12 +136,7 @@ export function definePlaygroundAPI<DataModel extends GenericDataModel>(
133
136
  ),
134
137
  };
135
138
  },
136
- returns: vPaginationResult(
137
- v.object({
138
- _id: v.string(),
139
- name: v.string(),
140
- }),
141
- ),
139
+ returns: vPaginationResult(v.object({ _id: v.string(), name: v.string() })),
142
140
  });
143
141
 
144
142
  // List threads for a user (query)
@@ -167,10 +165,7 @@ export function definePlaygroundAPI<DataModel extends GenericDataModel>(
167
165
  } = await ctx.runQuery(component.messages.listMessagesByThreadId, {
168
166
  threadId: thread._id,
169
167
  order: "desc",
170
- paginationOpts: {
171
- numItems: 1,
172
- cursor: null,
173
- },
168
+ paginationOpts: { numItems: 1, cursor: null },
174
169
  });
175
170
  return {
176
171
  ...thread,
@@ -198,16 +193,20 @@ export function definePlaygroundAPI<DataModel extends GenericDataModel>(
198
193
  apiKey: v.string(),
199
194
  threadId: v.string(),
200
195
  paginationOpts: paginationOptsValidator,
196
+ streamArgs: vStreamArgs,
201
197
  },
202
198
  handler: async (ctx, args) => {
203
199
  await validateApiKey(ctx, args.apiKey);
204
- return listMessages_(ctx, component, {
200
+ const paginated = await listMessages_(ctx, component, {
205
201
  threadId: args.threadId,
206
202
  paginationOpts: args.paginationOpts,
207
203
  statuses: ["success", "failed", "pending"],
208
204
  });
205
+ const streams = await syncStreams(ctx, component, args);
206
+
207
+ return { ...paginated, streams };
209
208
  },
210
- returns: vPaginationResult(vMessageDoc),
209
+ returns: vStreamMessagesReturnValue,
211
210
  });
212
211
 
213
212
  // Create a thread (mutation)
@@ -267,18 +266,46 @@ export function definePlaygroundAPI<DataModel extends GenericDataModel>(
267
266
  const namedAgent = agents.find(({ name }) => name === agentName);
268
267
  if (!namedAgent) throw new Error(`Unknown agent: ${agentName}`);
269
268
  const { agent } = namedAgent;
270
- const { messageId, text } = await agent.generateText(
269
+ const { text, steps } = await agent.streamText(
271
270
  ctx,
272
271
  { threadId, userId },
273
272
  {
274
273
  ...rest,
275
274
  ...(system ? { system } : {}),
276
- ...(messages ? { messages: messages.map(deserializeMessage) } : {}),
275
+ ...(messages ? { messages: messages.map(toModelMessage) } : {}),
277
276
  },
278
- { contextOptions, storageOptions },
277
+ { contextOptions, storageOptions, saveStreamDeltas: true },
279
278
  );
280
- return { messageId, text };
279
+ const outputMessages = await Promise.all(
280
+ (await steps).map(async (step) => {
281
+ const { messages } = await serializeNewMessagesInStep(
282
+ ctx,
283
+ component,
284
+ step,
285
+ {
286
+ model: getModelName(agent.options.languageModel),
287
+ provider: getProviderName(agent.options.languageModel),
288
+ },
289
+ );
290
+ return messages.map((messageWithMetadata, i) => {
291
+ return {
292
+ ...messageWithMetadata,
293
+ tool: isTool(messageWithMetadata.message),
294
+ text: extractText(messageWithMetadata.message),
295
+ status: "success",
296
+ providerMetadata: {},
297
+ threadId,
298
+ _id: crypto.randomUUID(),
299
+ _creationTime: Date.now(),
300
+ order: 0,
301
+ stepOrder: i + 1,
302
+ } satisfies MessageDoc;
303
+ });
304
+ }),
305
+ );
306
+ return { text: await text, messages: outputMessages.flat() };
281
307
  },
308
+ returns: v.object({ text: v.string(), messages: v.array(vMessageDoc) }),
282
309
  });
283
310
 
284
311
  // Fetch prompt context (action)
@@ -288,8 +315,11 @@ export function definePlaygroundAPI<DataModel extends GenericDataModel>(
288
315
  agentName: v.string(),
289
316
  userId: v.optional(v.string()),
290
317
  threadId: v.optional(v.string()),
291
- messages: v.array(vMessage),
318
+ searchText: v.optional(v.string()),
319
+ targetMessageId: v.optional(v.string()),
292
320
  contextOptions: vContextOptions,
321
+ // @deprecated use searchText and targetMessageId instead
322
+ messages: v.optional(v.array(vMessage)),
293
323
  beforeMessageId: v.optional(v.string()),
294
324
  },
295
325
  handler: async (ctx, args) => {
@@ -302,20 +332,26 @@ export function definePlaygroundAPI<DataModel extends GenericDataModel>(
302
332
  if (!namedAgent) throw new Error(`Unknown agent: ${args.agentName}`);
303
333
  const { agent } = namedAgent;
304
334
  const contextOptions = args.contextOptions;
305
- if (args.beforeMessageId) {
335
+ const targetMessageId = args.targetMessageId ?? args.beforeMessageId;
336
+ if (targetMessageId) {
306
337
  contextOptions.recentMessages =
307
338
  (contextOptions.recentMessages ?? 10) + 1;
308
339
  }
309
340
  const messages = await agent.fetchContextMessages(ctx, {
310
341
  userId: args.userId,
311
342
  threadId: args.threadId,
312
- messages: args.messages.map(deserializeMessage),
343
+ targetMessageId,
344
+ searchText: args.searchText,
313
345
  contextOptions: args.contextOptions,
314
- upToAndIncludingMessageId: args.beforeMessageId,
346
+ messages: args.messages?.map(toModelMessage),
315
347
  });
316
- return messages.filter(
317
- (m) => !args.beforeMessageId || m._id !== args.beforeMessageId,
348
+ const targetMessageIndex = messages.findIndex(
349
+ (m) => m._id === targetMessageId,
318
350
  );
351
+ if (targetMessageIndex !== -1) {
352
+ return messages.slice(0, targetMessageIndex);
353
+ }
354
+ return messages;
319
355
  },
320
356
  });
321
357