@convex-dev/agent 0.1.1 → 0.1.2-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.
- package/README.md +9 -3
- package/dist/commonjs/client/createTool.d.ts +49 -0
- package/dist/commonjs/client/createTool.d.ts.map +1 -0
- package/dist/commonjs/client/createTool.js +45 -0
- package/dist/commonjs/client/createTool.js.map +1 -0
- package/dist/commonjs/client/index.d.ts +101 -322
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +142 -194
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/client/streaming.d.ts +54 -0
- package/dist/commonjs/client/streaming.d.ts.map +1 -0
- package/dist/commonjs/client/streaming.js +121 -0
- package/dist/commonjs/client/streaming.js.map +1 -0
- package/dist/commonjs/client/types.d.ts +290 -2
- package/dist/commonjs/client/types.d.ts.map +1 -1
- package/dist/commonjs/component/messages.d.ts +104 -138
- package/dist/commonjs/component/messages.d.ts.map +1 -1
- package/dist/commonjs/component/messages.js +15 -14
- package/dist/commonjs/component/messages.js.map +1 -1
- package/dist/commonjs/component/schema.d.ts +873 -3
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +44 -1
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/streams.d.ts +191 -0
- package/dist/commonjs/component/streams.d.ts.map +1 -0
- package/dist/commonjs/component/streams.js +169 -0
- package/dist/commonjs/component/streams.js.map +1 -0
- package/dist/commonjs/component/threads.d.ts +1 -1
- package/dist/commonjs/component/threads.js +3 -3
- package/dist/commonjs/component/threads.js.map +1 -1
- package/dist/commonjs/component/users.js +2 -2
- package/dist/commonjs/component/users.js.map +1 -1
- package/dist/commonjs/mapping.d.ts.map +1 -1
- package/dist/commonjs/mapping.js +3 -1
- package/dist/commonjs/mapping.js.map +1 -1
- package/dist/commonjs/react/index.d.ts +99 -0
- package/dist/commonjs/react/index.d.ts.map +1 -1
- package/dist/commonjs/react/index.js +505 -1
- package/dist/commonjs/react/index.js.map +1 -1
- package/dist/commonjs/react/toUIMessages.d.ts +8 -0
- package/dist/commonjs/react/toUIMessages.d.ts.map +1 -0
- package/dist/commonjs/react/toUIMessages.js +134 -0
- package/dist/commonjs/react/toUIMessages.js.map +1 -0
- package/dist/commonjs/validators.d.ts +506 -4
- package/dist/commonjs/validators.d.ts.map +1 -1
- package/dist/commonjs/validators.js +49 -2
- package/dist/commonjs/validators.js.map +1 -1
- package/dist/esm/client/createTool.d.ts +49 -0
- package/dist/esm/client/createTool.d.ts.map +1 -0
- package/dist/esm/client/createTool.js +45 -0
- package/dist/esm/client/createTool.js.map +1 -0
- package/dist/esm/client/index.d.ts +101 -322
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +142 -194
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/streaming.d.ts +54 -0
- package/dist/esm/client/streaming.d.ts.map +1 -0
- package/dist/esm/client/streaming.js +121 -0
- package/dist/esm/client/streaming.js.map +1 -0
- package/dist/esm/client/types.d.ts +290 -2
- package/dist/esm/client/types.d.ts.map +1 -1
- package/dist/esm/component/messages.d.ts +104 -138
- package/dist/esm/component/messages.d.ts.map +1 -1
- package/dist/esm/component/messages.js +15 -14
- package/dist/esm/component/messages.js.map +1 -1
- package/dist/esm/component/schema.d.ts +873 -3
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +44 -1
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/streams.d.ts +191 -0
- package/dist/esm/component/streams.d.ts.map +1 -0
- package/dist/esm/component/streams.js +169 -0
- package/dist/esm/component/streams.js.map +1 -0
- package/dist/esm/component/threads.d.ts +1 -1
- package/dist/esm/component/threads.js +3 -3
- package/dist/esm/component/threads.js.map +1 -1
- package/dist/esm/component/users.js +2 -2
- package/dist/esm/component/users.js.map +1 -1
- package/dist/esm/mapping.d.ts.map +1 -1
- package/dist/esm/mapping.js +3 -1
- package/dist/esm/mapping.js.map +1 -1
- package/dist/esm/react/index.d.ts +99 -0
- package/dist/esm/react/index.d.ts.map +1 -1
- package/dist/esm/react/index.js +505 -1
- package/dist/esm/react/index.js.map +1 -1
- package/dist/esm/react/toUIMessages.d.ts +8 -0
- package/dist/esm/react/toUIMessages.d.ts.map +1 -0
- package/dist/esm/react/toUIMessages.js +134 -0
- package/dist/esm/react/toUIMessages.js.map +1 -0
- package/dist/esm/validators.d.ts +506 -4
- package/dist/esm/validators.d.ts.map +1 -1
- package/dist/esm/validators.js +49 -2
- package/dist/esm/validators.js.map +1 -1
- package/package.json +5 -1
- package/src/client/createTool.ts +108 -0
- package/src/client/index.ts +270 -689
- package/src/client/streaming.ts +166 -0
- package/src/client/types.ts +430 -5
- package/src/component/_generated/api.d.ts +339 -188
- package/src/component/messages.ts +15 -14
- package/src/component/schema.ts +51 -0
- package/src/component/streams.ts +206 -0
- package/src/component/threads.ts +3 -3
- package/src/component/users.ts +2 -2
- package/src/mapping.ts +3 -1
- package/src/react/index.ts +687 -1
- package/src/react/toUIMessages.ts +153 -0
- package/src/validators.test.ts +18 -0
- package/src/validators.ts +72 -2
package/src/component/schema.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
vReasoningDetails,
|
|
15
15
|
vFile,
|
|
16
16
|
vFileWithStringId,
|
|
17
|
+
vTextStreamPart,
|
|
17
18
|
} from "../validators.js";
|
|
18
19
|
import { typedV } from "convex-helpers/validators";
|
|
19
20
|
import vectorTables, { vVectorId } from "./vector/tables.js";
|
|
@@ -105,6 +106,56 @@ export const schema = defineSchema({
|
|
|
105
106
|
"stepOrder",
|
|
106
107
|
]),
|
|
107
108
|
|
|
109
|
+
// Status: if it's done, it's deleted, then deltas are vacuumed
|
|
110
|
+
streamingMessages: defineTable({
|
|
111
|
+
// extra metadata?
|
|
112
|
+
userId: v.optional(v.string()),
|
|
113
|
+
agentName: v.optional(v.string()),
|
|
114
|
+
model: v.optional(v.string()),
|
|
115
|
+
provider: v.optional(v.string()),
|
|
116
|
+
providerOptions: v.optional(vProviderOptions), // Sent to model
|
|
117
|
+
|
|
118
|
+
threadId: v.id("threads"),
|
|
119
|
+
order: v.number(),
|
|
120
|
+
/**
|
|
121
|
+
* The step order of the first message in the stream.
|
|
122
|
+
* If the stream ends up with both a tool call and a tool result,
|
|
123
|
+
* the stepOrder of the result will be +1 of the tool call.
|
|
124
|
+
*/
|
|
125
|
+
stepOrder: v.number(),
|
|
126
|
+
state: v.union(
|
|
127
|
+
v.object({
|
|
128
|
+
kind: v.literal("streaming"),
|
|
129
|
+
lastHeartbeat: v.number(),
|
|
130
|
+
timeoutFnId: v.optional(v.id("_scheduled_functions")),
|
|
131
|
+
}),
|
|
132
|
+
v.object({
|
|
133
|
+
kind: v.literal("finished"),
|
|
134
|
+
endedAt: v.number(),
|
|
135
|
+
}),
|
|
136
|
+
v.object({
|
|
137
|
+
kind: v.literal("error"),
|
|
138
|
+
error: v.string(),
|
|
139
|
+
})
|
|
140
|
+
),
|
|
141
|
+
})
|
|
142
|
+
// There should only be one per "order" index
|
|
143
|
+
// If another exists, it's deleted and replaced
|
|
144
|
+
.index("threadId_state_order_stepOrder", [
|
|
145
|
+
"threadId",
|
|
146
|
+
"state.kind",
|
|
147
|
+
"order",
|
|
148
|
+
"stepOrder",
|
|
149
|
+
]),
|
|
150
|
+
|
|
151
|
+
streamDeltas: defineTable({
|
|
152
|
+
streamId: v.id("streamingMessages"),
|
|
153
|
+
// the indexes work like: 0 <first> 1 <second> 2 <third> 3 ...
|
|
154
|
+
start: v.number(), // inclusive
|
|
155
|
+
end: v.number(), // exclusive
|
|
156
|
+
parts: v.array(vTextStreamPart),
|
|
157
|
+
}).index("streamId_start_end", ["streamId", "start", "end"]),
|
|
158
|
+
|
|
108
159
|
memories: defineTable({
|
|
109
160
|
threadId: v.optional(v.id("threads")),
|
|
110
161
|
userId: v.optional(v.string()),
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import {
|
|
3
|
+
internalMutation,
|
|
4
|
+
mutation,
|
|
5
|
+
MutationCtx,
|
|
6
|
+
query,
|
|
7
|
+
} from "./_generated/server";
|
|
8
|
+
import { omit, pick } from "convex-helpers";
|
|
9
|
+
import { StreamDelta, vStreamDelta, vStreamMessage } from "../validators";
|
|
10
|
+
import schema from "./schema";
|
|
11
|
+
import { internal } from "./_generated/api";
|
|
12
|
+
import { Id } from "./_generated/dataModel";
|
|
13
|
+
|
|
14
|
+
const MAX_DELTAS_PER_REQUEST = 1000;
|
|
15
|
+
const MAX_DELTAS_PER_STREAM = 100;
|
|
16
|
+
const TIMEOUT_INTERVAL = 1000 * 60; // 1 minute
|
|
17
|
+
|
|
18
|
+
const deltaValidator = schema.tables.streamDeltas.validator;
|
|
19
|
+
|
|
20
|
+
export const addDelta = mutation({
|
|
21
|
+
args: deltaValidator,
|
|
22
|
+
returns: v.null(),
|
|
23
|
+
handler: async (ctx, args) => {
|
|
24
|
+
await ctx.db.insert("streamDeltas", args);
|
|
25
|
+
await heartbeatStream(ctx, { streamId: args.streamId });
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const listDeltas = query({
|
|
30
|
+
args: {
|
|
31
|
+
threadId: v.id("threads"),
|
|
32
|
+
cursors: v.array(
|
|
33
|
+
v.object({
|
|
34
|
+
streamId: v.id("streamingMessages"),
|
|
35
|
+
cursor: v.number(),
|
|
36
|
+
})
|
|
37
|
+
),
|
|
38
|
+
},
|
|
39
|
+
returns: v.array(vStreamDelta),
|
|
40
|
+
handler: async (ctx, args): Promise<StreamDelta[]> => {
|
|
41
|
+
let totalDeltas = 0;
|
|
42
|
+
const deltas: StreamDelta[] = [];
|
|
43
|
+
for (const cursor of args.cursors) {
|
|
44
|
+
const streamDeltas = await ctx.db
|
|
45
|
+
.query("streamDeltas")
|
|
46
|
+
.withIndex("streamId_start_end", (q) =>
|
|
47
|
+
q.eq("streamId", cursor.streamId).gte("start", cursor.cursor)
|
|
48
|
+
)
|
|
49
|
+
.take(
|
|
50
|
+
Math.min(MAX_DELTAS_PER_STREAM, MAX_DELTAS_PER_REQUEST - totalDeltas)
|
|
51
|
+
);
|
|
52
|
+
totalDeltas += streamDeltas.length;
|
|
53
|
+
deltas.push(
|
|
54
|
+
...streamDeltas.map((d) => ({
|
|
55
|
+
streamId: d.streamId.toString(),
|
|
56
|
+
...pick(d, ["start", "end", "parts"]),
|
|
57
|
+
}))
|
|
58
|
+
);
|
|
59
|
+
if (totalDeltas >= MAX_DELTAS_PER_REQUEST) {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return deltas;
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
export const create = mutation({
|
|
68
|
+
args: omit(schema.tables.streamingMessages.validator.fields, ["state"]),
|
|
69
|
+
returns: v.id("streamingMessages"),
|
|
70
|
+
handler: async (ctx, args) => {
|
|
71
|
+
const state = {
|
|
72
|
+
kind: "streaming" as const,
|
|
73
|
+
lastHeartbeat: Date.now(),
|
|
74
|
+
};
|
|
75
|
+
const streamId = await ctx.db.insert("streamingMessages", {
|
|
76
|
+
...args,
|
|
77
|
+
state,
|
|
78
|
+
});
|
|
79
|
+
const timeoutFnId = await ctx.scheduler.runAfter(
|
|
80
|
+
TIMEOUT_INTERVAL,
|
|
81
|
+
internal.streams.timeoutStream,
|
|
82
|
+
{ streamId }
|
|
83
|
+
);
|
|
84
|
+
await ctx.db.patch(streamId, { state: { ...state, timeoutFnId } });
|
|
85
|
+
return streamId;
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export const list = query({
|
|
90
|
+
args: {
|
|
91
|
+
threadId: v.id("threads"),
|
|
92
|
+
},
|
|
93
|
+
returns: v.array(vStreamMessage),
|
|
94
|
+
handler: async (ctx, args) => {
|
|
95
|
+
return ctx.db
|
|
96
|
+
.query("streamingMessages")
|
|
97
|
+
.withIndex("threadId_state_order_stepOrder", (q) =>
|
|
98
|
+
q.eq("threadId", args.threadId).eq("state.kind", "streaming")
|
|
99
|
+
)
|
|
100
|
+
.order("desc")
|
|
101
|
+
.take(100)
|
|
102
|
+
.then((msgs) =>
|
|
103
|
+
msgs.map((m) => ({
|
|
104
|
+
streamId: m._id,
|
|
105
|
+
...pick(m, [
|
|
106
|
+
"order",
|
|
107
|
+
"stepOrder",
|
|
108
|
+
"userId",
|
|
109
|
+
"agentName",
|
|
110
|
+
"model",
|
|
111
|
+
"provider",
|
|
112
|
+
"providerOptions",
|
|
113
|
+
]),
|
|
114
|
+
}))
|
|
115
|
+
);
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
export const finish = mutation({
|
|
120
|
+
args: {
|
|
121
|
+
streamId: v.id("streamingMessages"),
|
|
122
|
+
finalDelta: v.optional(deltaValidator),
|
|
123
|
+
},
|
|
124
|
+
returns: v.null(),
|
|
125
|
+
handler: async (ctx, args) => {
|
|
126
|
+
if (args.finalDelta) {
|
|
127
|
+
await ctx.db.insert("streamDeltas", args.finalDelta);
|
|
128
|
+
}
|
|
129
|
+
const stream = await ctx.db.get(args.streamId);
|
|
130
|
+
if (!stream) {
|
|
131
|
+
throw new Error(`Stream not found: ${args.streamId}`);
|
|
132
|
+
}
|
|
133
|
+
if (stream.state.kind !== "streaming") {
|
|
134
|
+
throw new Error(`Stream is not streaming: ${args.streamId}`);
|
|
135
|
+
}
|
|
136
|
+
if (stream.state.timeoutFnId) {
|
|
137
|
+
const timeoutFn = await ctx.db.system.get(stream.state.timeoutFnId);
|
|
138
|
+
if (timeoutFn?.state.kind === "pending") {
|
|
139
|
+
await ctx.scheduler.cancel(stream.state.timeoutFnId);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
await ctx.db.patch(args.streamId, {
|
|
143
|
+
state: { kind: "finished", endedAt: Date.now() },
|
|
144
|
+
});
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
async function heartbeatStream(
|
|
149
|
+
ctx: MutationCtx,
|
|
150
|
+
args: { streamId: Id<"streamingMessages"> }
|
|
151
|
+
) {
|
|
152
|
+
const stream = await ctx.db.get(args.streamId);
|
|
153
|
+
if (!stream) {
|
|
154
|
+
console.warn("Stream not found", args.streamId);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (stream.state.kind !== "streaming") {
|
|
158
|
+
console.warn("Stream is not streaming", args.streamId);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (Date.now() - stream.state.lastHeartbeat < TIMEOUT_INTERVAL / 4) {
|
|
162
|
+
// Debounce heartbeating.
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (!stream.state.timeoutFnId) {
|
|
166
|
+
throw new Error("Stream has no timeout function");
|
|
167
|
+
}
|
|
168
|
+
const timeoutFn = await ctx.db.system.get(stream.state.timeoutFnId);
|
|
169
|
+
if (!timeoutFn) {
|
|
170
|
+
throw new Error("Timeout function not found");
|
|
171
|
+
}
|
|
172
|
+
if (timeoutFn.state.kind !== "pending") {
|
|
173
|
+
throw new Error("Timeout function is not pending");
|
|
174
|
+
}
|
|
175
|
+
await ctx.scheduler.cancel(stream.state.timeoutFnId);
|
|
176
|
+
const timeoutFnId = await ctx.scheduler.runAfter(
|
|
177
|
+
TIMEOUT_INTERVAL,
|
|
178
|
+
internal.streams.timeoutStream,
|
|
179
|
+
{ streamId: args.streamId }
|
|
180
|
+
);
|
|
181
|
+
await ctx.db.patch(args.streamId, {
|
|
182
|
+
state: {
|
|
183
|
+
kind: "streaming",
|
|
184
|
+
lastHeartbeat: Date.now(),
|
|
185
|
+
timeoutFnId,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export const timeoutStream = internalMutation({
|
|
191
|
+
args: { streamId: v.id("streamingMessages") },
|
|
192
|
+
returns: v.null(),
|
|
193
|
+
handler: async (ctx, args) => {
|
|
194
|
+
const stream = await ctx.db.get(args.streamId);
|
|
195
|
+
if (!stream) {
|
|
196
|
+
console.warn("Stream not found", args.streamId);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
await ctx.db.patch(args.streamId, {
|
|
200
|
+
state: {
|
|
201
|
+
kind: "finished",
|
|
202
|
+
endedAt: Date.now(),
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
},
|
|
206
|
+
});
|
package/src/component/threads.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { assert, omit, pick } from "convex-helpers";
|
|
|
2
2
|
import { paginator } from "convex-helpers/server/pagination";
|
|
3
3
|
import { partial } from "convex-helpers/validators";
|
|
4
4
|
import { ObjectType } from "convex/values";
|
|
5
|
-
import {
|
|
5
|
+
import { vPaginationResult } from "../validators.js";
|
|
6
6
|
import { api, internal } from "./_generated/api.js";
|
|
7
7
|
import { Doc } from "./_generated/dataModel.js";
|
|
8
8
|
import {
|
|
@@ -38,7 +38,7 @@ export const getThread = query({
|
|
|
38
38
|
|
|
39
39
|
export const listThreadsByUserId = query({
|
|
40
40
|
args: {
|
|
41
|
-
userId: v.string(),
|
|
41
|
+
userId: v.optional(v.string()),
|
|
42
42
|
order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
|
|
43
43
|
paginationOpts: v.optional(paginationOptsValidator),
|
|
44
44
|
},
|
|
@@ -53,7 +53,7 @@ export const listThreadsByUserId = query({
|
|
|
53
53
|
page: threads.page.map(publicThread),
|
|
54
54
|
};
|
|
55
55
|
},
|
|
56
|
-
returns:
|
|
56
|
+
returns: vPaginationResult(vThreadDoc),
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
const vThread = schema.tables.threads.validator;
|
package/src/component/users.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { schema, v } from "./schema.js";
|
|
|
13
13
|
import { deleteMessage } from "./messages.js";
|
|
14
14
|
import { paginationOptsValidator } from "convex/server";
|
|
15
15
|
import { stream } from "convex-helpers/server/stream";
|
|
16
|
-
import {
|
|
16
|
+
import { vPaginationResult } from "../validators.js";
|
|
17
17
|
import { Id } from "./_generated/dataModel.js";
|
|
18
18
|
|
|
19
19
|
// Note: it only searches for users with threads
|
|
@@ -33,7 +33,7 @@ export const listUsersWithThreads = query({
|
|
|
33
33
|
page: results.page.map((t) => t.userId).filter((t): t is string => !!t),
|
|
34
34
|
};
|
|
35
35
|
},
|
|
36
|
-
returns:
|
|
36
|
+
returns: vPaginationResult(v.string()),
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
export const deleteAllForUserId = action({
|
package/src/mapping.ts
CHANGED
|
@@ -108,7 +108,9 @@ export function serializeNewMessagesInStep<TOOLS extends ToolSet>(
|
|
|
108
108
|
).map(
|
|
109
109
|
(message): MessageWithMetadata => ({
|
|
110
110
|
message: serializeMessage(message),
|
|
111
|
-
|
|
111
|
+
// Let's not store the ID by default here. It's being generated internally
|
|
112
|
+
// and not referenced elsewhere that we know of.
|
|
113
|
+
// id: message.id,
|
|
112
114
|
...(message.role === "tool" ? toolFields : assistantFields),
|
|
113
115
|
text: step.text,
|
|
114
116
|
// fileId: message.fileId,
|