@convex-dev/agent 0.0.1-alpha.1 → 0.0.1-alpha.3

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 (70) hide show
  1. package/README.md +293 -6
  2. package/dist/commonjs/client/index.d.ts +520 -96
  3. package/dist/commonjs/client/index.d.ts.map +1 -1
  4. package/dist/commonjs/client/index.js +210 -131
  5. package/dist/commonjs/client/index.js.map +1 -1
  6. package/dist/commonjs/client/types.d.ts +3 -0
  7. package/dist/commonjs/client/types.d.ts.map +1 -1
  8. package/dist/commonjs/component/messages.d.ts +57 -56
  9. package/dist/commonjs/component/messages.d.ts.map +1 -1
  10. package/dist/commonjs/component/messages.js +184 -147
  11. package/dist/commonjs/component/messages.js.map +1 -1
  12. package/dist/commonjs/component/schema.d.ts +260 -256
  13. package/dist/commonjs/component/schema.d.ts.map +1 -1
  14. package/dist/commonjs/component/schema.js +25 -18
  15. package/dist/commonjs/component/schema.js.map +1 -1
  16. package/dist/commonjs/component/vector/index.d.ts +4 -4
  17. package/dist/commonjs/component/vector/index.d.ts.map +1 -1
  18. package/dist/commonjs/component/vector/index.js +12 -10
  19. package/dist/commonjs/component/vector/index.js.map +1 -1
  20. package/dist/commonjs/component/vector/tables.d.ts +3 -3
  21. package/dist/commonjs/component/vector/tables.js +6 -6
  22. package/dist/commonjs/component/vector/tables.js.map +1 -1
  23. package/dist/commonjs/mapping.d.ts +6 -1
  24. package/dist/commonjs/mapping.d.ts.map +1 -1
  25. package/dist/commonjs/mapping.js +25 -0
  26. package/dist/commonjs/mapping.js.map +1 -1
  27. package/dist/commonjs/validators.d.ts +1376 -1
  28. package/dist/commonjs/validators.d.ts.map +1 -1
  29. package/dist/commonjs/validators.js +28 -1
  30. package/dist/commonjs/validators.js.map +1 -1
  31. package/dist/esm/client/index.d.ts +520 -96
  32. package/dist/esm/client/index.d.ts.map +1 -1
  33. package/dist/esm/client/index.js +210 -131
  34. package/dist/esm/client/index.js.map +1 -1
  35. package/dist/esm/client/types.d.ts +3 -0
  36. package/dist/esm/client/types.d.ts.map +1 -1
  37. package/dist/esm/component/messages.d.ts +57 -56
  38. package/dist/esm/component/messages.d.ts.map +1 -1
  39. package/dist/esm/component/messages.js +184 -147
  40. package/dist/esm/component/messages.js.map +1 -1
  41. package/dist/esm/component/schema.d.ts +260 -256
  42. package/dist/esm/component/schema.d.ts.map +1 -1
  43. package/dist/esm/component/schema.js +25 -18
  44. package/dist/esm/component/schema.js.map +1 -1
  45. package/dist/esm/component/vector/index.d.ts +4 -4
  46. package/dist/esm/component/vector/index.d.ts.map +1 -1
  47. package/dist/esm/component/vector/index.js +12 -10
  48. package/dist/esm/component/vector/index.js.map +1 -1
  49. package/dist/esm/component/vector/tables.d.ts +3 -3
  50. package/dist/esm/component/vector/tables.js +6 -6
  51. package/dist/esm/component/vector/tables.js.map +1 -1
  52. package/dist/esm/mapping.d.ts +6 -1
  53. package/dist/esm/mapping.d.ts.map +1 -1
  54. package/dist/esm/mapping.js +25 -0
  55. package/dist/esm/mapping.js.map +1 -1
  56. package/dist/esm/validators.d.ts +1376 -1
  57. package/dist/esm/validators.d.ts.map +1 -1
  58. package/dist/esm/validators.js +28 -1
  59. package/dist/esm/validators.js.map +1 -1
  60. package/package.json +3 -3
  61. package/src/client/index.ts +384 -276
  62. package/src/client/types.ts +4 -0
  63. package/src/component/_generated/api.d.ts +54 -53
  64. package/src/component/messages.ts +219 -177
  65. package/src/component/schema.ts +25 -18
  66. package/src/component/vector/index.ts +13 -11
  67. package/src/component/vector/tables.ts +6 -6
  68. package/src/mapping.ts +46 -11
  69. package/src/validators.test.ts +9 -0
  70. package/src/validators.ts +36 -1
@@ -1,29 +1,29 @@
1
1
  import { defineSchema, defineTable } from "convex/server";
2
2
  import { v } from "convex/values";
3
- import { vChatStatus, vMessage, vMessageStatus, vStep } from "../validators";
3
+ import { vThreadStatus, vMessage, vMessageStatus, vStep } from "../validators";
4
4
  import { typedV } from "convex-helpers/validators";
5
5
  import vectorTables, { vVectorId } from "./vector/tables";
6
6
 
7
7
  export const schema = defineSchema({
8
- chats: defineTable({
8
+ threads: defineTable({
9
9
  userId: v.optional(v.string()), // Unset for anonymous
10
10
  order: v.optional(v.number()), // within a domain
11
11
  // TODO: is this bubbling up in continue?
12
12
  defaultSystemPrompt: v.optional(v.string()),
13
13
  title: v.optional(v.string()),
14
14
  summary: v.optional(v.string()),
15
- status: vChatStatus,
16
- // If this is a chat continuation, we can use this to find context from
17
- // the parent chat(s). There are multiple if the chat is a merging of
18
- // multiple chats.
19
- parentChatIds: v.optional(v.array(v.id("chats"))),
15
+ status: vThreadStatus,
16
+ // If this is a thread continuation, we can use this to find context from
17
+ // the parent thread(s). There are multiple if the thread is a merging of
18
+ // multiple threads.
19
+ parentThreadIds: v.optional(v.array(v.id("threads"))),
20
20
  }).index("status_userId_order", ["status", "userId", "order"]),
21
21
  // TODO: text search on title/ summary
22
22
  messages: defineTable({
23
23
  id: v.optional(v.string()), // external id, e.g. from Vercel AI SDK
24
24
  userId: v.optional(v.string()), // useful for future indexes (text search)
25
- chatId: v.id("chats"),
26
- threadId: v.optional(v.id("messages")),
25
+ threadId: v.optional(v.id("threads")),
26
+ parentMessageId: v.optional(v.id("messages")),
27
27
  stepId: v.optional(v.id("steps")),
28
28
  agentName: v.optional(v.string()),
29
29
  message: v.optional(vMessage),
@@ -33,7 +33,7 @@ export const schema = defineSchema({
33
33
  // TODO: add sub-messages back in? or be able to skip them?
34
34
  tool: v.boolean(),
35
35
  // Repeats until a non-tool message.
36
- // Set when the message is finished
36
+ // Unset if it's not in a thread.
37
37
  order: v.number(),
38
38
  stepOrder: v.optional(v.number()),
39
39
  fileId: v.optional(v.id("files")),
@@ -41,8 +41,15 @@ export const schema = defineSchema({
41
41
  })
42
42
  // Allows finding successful visible messages in order
43
43
  // Also surface pending messages separately to e.g. stream
44
- .index("chatId_status_tool_order_stepOrder", [
45
- "chatId",
44
+ .index("threadId_status_tool_order_stepOrder", [
45
+ "threadId",
46
+ "status",
47
+ "tool",
48
+ "order",
49
+ "stepOrder",
50
+ ])
51
+ .index("userId_status_tool_order_stepOrder", [
52
+ "userId",
46
53
  "status",
47
54
  "tool",
48
55
  "order",
@@ -50,22 +57,22 @@ export const schema = defineSchema({
50
57
  ])
51
58
  // Allows finding all threaded messages in order
52
59
  // Allows finding all failed messages to evaluate
53
- // .index("status_threadId_order_stepOrder", [
60
+ // .index("status_parentMessageId_order_stepOrder", [
54
61
  // "status",
55
- // "threadId",
62
+ // "parentMessageId",
56
63
  // "order",
57
64
  // "stepOrder",
58
65
  // ])
59
66
  // Allows text search on message content
60
67
  .searchIndex("text_search", {
61
68
  searchField: "text",
62
- filterFields: ["userId", "chatId"],
69
+ filterFields: ["userId", "threadId"],
63
70
  })
64
71
  // Allows finding messages by vector embedding id
65
72
  .index("embeddingId", ["embeddingId"]),
66
73
 
67
74
  steps: defineTable({
68
- chatId: v.id("chats"),
75
+ threadId: v.id("threads"),
69
76
  // Could be different from the order if we fail.
70
77
  parentMessageId: v.id("messages"),
71
78
  order: v.number(), // parent message order
@@ -73,9 +80,9 @@ export const schema = defineSchema({
73
80
  step: vStep,
74
81
  status: vMessageStatus,
75
82
  })
76
- .index("status_chatId_order_stepOrder", [
83
+ .index("status_threadId_order_stepOrder", [
77
84
  "status",
78
- "chatId",
85
+ "threadId",
79
86
  "order",
80
87
  "stepOrder",
81
88
  ])
@@ -23,7 +23,7 @@ export const paginate = query({
23
23
  const vectors = await paginator(ctx.db, schema)
24
24
  .query(tableName)
25
25
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
- .withIndex("model_kind_chatId" as any, (q) =>
26
+ .withIndex("model_kind_threadId" as any, (q) =>
27
27
  q.eq("model", args.targetModel)
28
28
  )
29
29
  .paginate({
@@ -39,11 +39,11 @@ export const paginate = query({
39
39
  },
40
40
  });
41
41
 
42
- export const deleteBatchForChat = mutation({
42
+ export const deleteBatchForThread = mutation({
43
43
  args: {
44
44
  vectorDimension: vVectorDimension,
45
45
  model: v.string(),
46
- chatId: v.string(),
46
+ threadId: v.string(),
47
47
  cursor: v.optional(v.string()),
48
48
  limit: v.number(),
49
49
  },
@@ -54,18 +54,18 @@ export const deleteBatchForChat = mutation({
54
54
  handler: async (ctx, args) => {
55
55
  const tableName = getVectorTableName(args.vectorDimension);
56
56
  const vectors = await mergedStream(
57
- ["chat", "memory"].map((kind) =>
57
+ ["thread", "memory"].map((kind) =>
58
58
  stream(ctx.db, schema)
59
59
  .query(tableName)
60
60
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
- .withIndex("model_kind_chatId" as any, (q) =>
61
+ .withIndex("model_kind_threadId" as any, (q) =>
62
62
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
63
  (q.eq("model", args.model) as any)
64
64
  .eq("kind", kind)
65
- .eq("chatId", args.chatId)
65
+ .eq("threadId", args.threadId)
66
66
  )
67
67
  ),
68
- ["chatId"]
68
+ ["threadId"]
69
69
  ).paginate({
70
70
  cursor: args.cursor ?? null,
71
71
  numItems: args.limit,
@@ -85,9 +85,9 @@ export const insertBatch = mutation({
85
85
  vectors: v.array(
86
86
  v.object({
87
87
  model: v.string(),
88
- kind: v.union(v.literal("chat"), v.literal("memory")),
88
+ kind: v.union(v.literal("thread"), v.literal("memory")),
89
89
  userId: v.optional(v.string()),
90
- chatId: v.optional(v.string()),
90
+ threadId: v.optional(v.string()),
91
91
  vector: v.array(v.number()),
92
92
  })
93
93
  ),
@@ -100,10 +100,12 @@ export const insertBatch = mutation({
100
100
  model: v.model,
101
101
  kind: v.kind,
102
102
  userId: v.userId,
103
- chatId: v.chatId,
103
+ threadId: v.threadId,
104
104
  vector: v.vector,
105
105
  model_kind_userId: v.userId ? [v.model, v.kind, v.userId] : undefined,
106
- model_kind_chatId: v.chatId ? [v.model, v.kind, v.chatId] : undefined,
106
+ model_kind_threadId: v.threadId
107
+ ? [v.model, v.kind, v.threadId]
108
+ : undefined,
107
109
  })
108
110
  )
109
111
  );
@@ -10,12 +10,12 @@ import { QueryCtx } from "../_generated/server";
10
10
  // We only generate embeddings for non-tool, non-system messages
11
11
  const embeddings = {
12
12
  model: v.string(),
13
- kind: v.union(v.literal("chat"), v.literal("memory")),
13
+ kind: v.union(v.literal("thread"), v.literal("memory")),
14
14
  userId: v.optional(v.string()),
15
- chatId: v.optional(v.string()),
16
- // not set for private chats
15
+ threadId: v.optional(v.string()),
16
+ // not set for private threads
17
17
  model_kind_userId: v.optional(v.array(v.string())),
18
- model_kind_chatId: v.optional(v.array(v.string())),
18
+ model_kind_threadId: v.optional(v.array(v.string())),
19
19
  vector: v.array(v.number()),
20
20
  };
21
21
 
@@ -24,9 +24,9 @@ function table<D extends number>(dimensions: D): Table<D> {
24
24
  .vectorIndex("vector", {
25
25
  vectorField: "vector",
26
26
  dimensions,
27
- filterFields: ["model_kind_userId", "model_kind_chatId"],
27
+ filterFields: ["model_kind_userId", "model_kind_threadId"],
28
28
  })
29
- .index("model_kind_chatId", ["model", "kind", "chatId"]);
29
+ .index("model_kind_threadId", ["model", "kind", "threadId"]);
30
30
  }
31
31
 
32
32
  export const VectorDimensions = [
package/src/mapping.ts CHANGED
@@ -1,16 +1,20 @@
1
- import type {
2
- AssistantContent,
3
- CoreAssistantMessage,
4
- CoreMessage,
5
- CoreToolMessage,
6
- DataContent,
7
- GenerateTextResult,
8
- StepResult,
9
- ToolContent,
10
- ToolSet,
11
- UserContent,
1
+ import {
2
+ convertToCoreMessages,
3
+ coreMessageSchema,
4
+ type AssistantContent,
5
+ type CoreAssistantMessage,
6
+ type CoreMessage,
7
+ type CoreToolMessage,
8
+ type DataContent,
9
+ type GenerateTextResult,
10
+ type StepResult,
11
+ type ToolContent,
12
+ type ToolSet,
13
+ type Message as UIMessage,
14
+ type UserContent,
12
15
  } from "ai";
13
16
  import { MessageWithFileAndId, Step } from "./validators";
17
+ import { assert } from "convex-helpers";
14
18
 
15
19
  export type SerializeUrlsAndUint8Arrays<T> = T extends URL
16
20
  ? string
@@ -158,3 +162,34 @@ function deserializeUrl(urlOrString: string | ArrayBuffer): URL | DataContent {
158
162
  }
159
163
  return urlOrString;
160
164
  }
165
+
166
+ export function promptOrMessagesToCoreMessages(args: {
167
+ system?: string;
168
+ prompt?: string;
169
+ messages?: CoreMessage[] | Omit<UIMessage, "id">[];
170
+ }): CoreMessage[] {
171
+ const messages: CoreMessage[] = [];
172
+ if (args.system) {
173
+ messages.push({ role: "system", content: args.system });
174
+ }
175
+ if (!args.messages) {
176
+ assert(args.prompt, "messages or prompt is required");
177
+ messages.push({ role: "user", content: args.prompt });
178
+ } else if (
179
+ args.messages.some(
180
+ (m) =>
181
+ typeof m === "object" &&
182
+ m !== null &&
183
+ (m.role === "data" || // UI-only role
184
+ "toolInvocations" in m || // UI-specific field
185
+ "parts" in m || // UI-specific field
186
+ "experimental_attachments" in m)
187
+ )
188
+ ) {
189
+ messages.push(...convertToCoreMessages(args.messages as UIMessage[]));
190
+ } else {
191
+ messages.push(...coreMessageSchema.array().parse(args.messages));
192
+ }
193
+ assert(messages.length > 0, "Messages must contain at least one message");
194
+ return messages;
195
+ }
@@ -3,11 +3,13 @@ import { expectTypeOf, test } from "vitest";
3
3
  import {
4
4
  vAssistantContent,
5
5
  vAssistantMessage,
6
+ vContextOptions,
6
7
  vFilePart,
7
8
  vImagePart,
8
9
  vMessage,
9
10
  vReasoningPart,
10
11
  vRedactedReasoningPart,
12
+ vStorageOptions,
11
13
  vSystemMessage,
12
14
  vTextPart,
13
15
  vToolCallPart,
@@ -31,6 +33,7 @@ import {
31
33
  UserContent,
32
34
  } from "ai";
33
35
  import { SerializeUrlsAndUint8Arrays } from "./mapping";
36
+ import { ContextOptions, StorageOptions } from "./client";
34
37
 
35
38
  // type assertion
36
39
  type OurUserContent = SerializeUrlsAndUint8Arrays<UserContent>;
@@ -98,4 +101,10 @@ type OurMessage = SerializeUrlsAndUint8Arrays<CoreMessage>;
98
101
  expectTypeOf<OurMessage>().toExtend<Infer<typeof vMessage>>();
99
102
  expectTypeOf<Infer<typeof vMessage>>().toExtend<OurMessage>();
100
103
 
104
+ expectTypeOf<Infer<typeof vContextOptions>>().toExtend<ContextOptions>();
105
+ expectTypeOf<ContextOptions>().toExtend<Infer<typeof vContextOptions>>();
106
+
107
+ expectTypeOf<Infer<typeof vStorageOptions>>().toExtend<StorageOptions>();
108
+ expectTypeOf<StorageOptions>().toExtend<Infer<typeof vStorageOptions>>();
109
+
101
110
  test("noop", () => { });
package/src/validators.ts CHANGED
@@ -5,7 +5,10 @@ import { Infer, v } from "convex/values";
5
5
  const providerOptions = v.optional(v.record(v.string(), v.any()));
6
6
  const experimental_providerMetadata = providerOptions;
7
7
 
8
- export const vChatStatus = v.union(v.literal("active"), v.literal("archived"));
8
+ export const vThreadStatus = v.union(
9
+ v.literal("active"),
10
+ v.literal("archived")
11
+ );
9
12
  export const vMessageStatus = v.union(
10
13
  v.literal("pending"),
11
14
  v.literal("success"),
@@ -256,3 +259,35 @@ export const vSearchOptions = v.object({
256
259
  messageRange: v.optional(v.object({ before: v.number(), after: v.number() })),
257
260
  });
258
261
  export type SearchOptions = Infer<typeof vSearchOptions>;
262
+
263
+ export const vContextOptionsSearchOptions = v.object({
264
+ limit: v.number(),
265
+ textSearch: v.optional(v.boolean()),
266
+ vectorSearch: v.optional(v.boolean()),
267
+ messageRange: v.optional(v.object({ before: v.number(), after: v.number() })),
268
+ });
269
+
270
+ export const vContextOptions = v.object({
271
+ includeToolCalls: v.optional(v.boolean()),
272
+ recentMessages: v.optional(v.number()),
273
+ searchOptions: v.optional(vContextOptionsSearchOptions),
274
+ searchOtherThreads: v.optional(v.boolean()),
275
+ });
276
+
277
+ export const vStorageOptions = v.object({
278
+ saveAllInputMessages: v.optional(v.boolean()),
279
+ saveAllOutputMessages: v.optional(v.boolean()),
280
+ });
281
+
282
+ export const vThreadArgs = v.object({
283
+ maxSteps: v.optional(v.number()),
284
+ prompt: v.optional(v.string()),
285
+ messages: v.optional(v.array(vMessage)),
286
+ });
287
+
288
+ export const vObjectArgs = v.object({
289
+ output: v.optional(v.any()),
290
+ mode: v.optional(v.literal("json")),
291
+ prompt: v.optional(v.string()),
292
+ messages: v.optional(v.array(vMessage)),
293
+ });