@convex-dev/agent 0.1.9-alpha.1 → 0.1.9-alpha.11
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 +3 -3
- package/dist/commonjs/client/index.d.ts +1 -0
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +5 -1
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/client/streaming.d.ts +3 -0
- package/dist/commonjs/client/streaming.d.ts.map +1 -1
- package/dist/commonjs/client/streaming.js +29 -1
- package/dist/commonjs/client/streaming.js.map +1 -1
- package/dist/commonjs/client/types.d.ts +0 -2
- package/dist/commonjs/client/types.d.ts.map +1 -1
- package/dist/commonjs/component/_generated/api.d.ts +24 -5
- package/dist/commonjs/component/schema.d.ts +26 -26
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +2 -2
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/streams.d.ts +11 -4
- package/dist/commonjs/component/streams.d.ts.map +1 -1
- package/dist/commonjs/component/streams.js +63 -22
- package/dist/commonjs/component/streams.js.map +1 -1
- package/dist/commonjs/component/threads.d.ts +8 -2
- package/dist/commonjs/component/threads.d.ts.map +1 -1
- package/dist/commonjs/component/threads.js +44 -18
- package/dist/commonjs/component/threads.js.map +1 -1
- package/dist/commonjs/component/vector/tables.d.ts +4 -2
- package/dist/commonjs/component/vector/tables.d.ts.map +1 -1
- package/dist/commonjs/react/deltas.d.ts.map +1 -1
- package/dist/commonjs/react/deltas.js +3 -2
- package/dist/commonjs/react/deltas.js.map +1 -1
- package/dist/commonjs/react/index.d.ts +4 -2
- package/dist/commonjs/react/index.d.ts.map +1 -1
- package/dist/commonjs/react/index.js +22 -9
- package/dist/commonjs/react/index.js.map +1 -1
- package/dist/commonjs/react/optimisticallySendMessage.js +1 -1
- package/dist/commonjs/react/optimisticallySendMessage.js.map +1 -1
- package/dist/commonjs/validators.d.ts +8 -3
- package/dist/commonjs/validators.d.ts.map +1 -1
- package/dist/commonjs/validators.js +2 -0
- package/dist/commonjs/validators.js.map +1 -1
- package/dist/commonjs.tsbuildinfo +1 -1
- package/dist/esm/client/index.d.ts +1 -0
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +5 -1
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/streaming.d.ts +3 -0
- package/dist/esm/client/streaming.d.ts.map +1 -1
- package/dist/esm/client/streaming.js +29 -1
- package/dist/esm/client/streaming.js.map +1 -1
- package/dist/esm/client/types.d.ts +0 -2
- package/dist/esm/client/types.d.ts.map +1 -1
- package/dist/esm/component/_generated/api.d.ts +24 -5
- package/dist/esm/component/schema.d.ts +26 -26
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +2 -2
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/streams.d.ts +11 -4
- package/dist/esm/component/streams.d.ts.map +1 -1
- package/dist/esm/component/streams.js +63 -22
- package/dist/esm/component/streams.js.map +1 -1
- package/dist/esm/component/threads.d.ts +8 -2
- package/dist/esm/component/threads.d.ts.map +1 -1
- package/dist/esm/component/threads.js +44 -18
- package/dist/esm/component/threads.js.map +1 -1
- package/dist/esm/component/vector/tables.d.ts +4 -2
- package/dist/esm/component/vector/tables.d.ts.map +1 -1
- package/dist/esm/react/deltas.d.ts.map +1 -1
- package/dist/esm/react/deltas.js +3 -2
- package/dist/esm/react/deltas.js.map +1 -1
- package/dist/esm/react/index.d.ts +4 -2
- package/dist/esm/react/index.d.ts.map +1 -1
- package/dist/esm/react/index.js +22 -9
- package/dist/esm/react/index.js.map +1 -1
- package/dist/esm/react/optimisticallySendMessage.js +1 -1
- package/dist/esm/react/optimisticallySendMessage.js.map +1 -1
- package/dist/esm/validators.d.ts +8 -3
- package/dist/esm/validators.d.ts.map +1 -1
- package/dist/esm/validators.js +2 -0
- package/dist/esm/validators.js.map +1 -1
- package/dist/esm.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +7 -3
- package/src/client/streaming.ts +32 -1
- package/src/client/types.ts +0 -2
- package/src/component/_generated/api.d.ts +24 -5
- package/src/component/schema.ts +2 -2
- package/src/component/streams.ts +100 -37
- package/src/component/threads.ts +48 -26
- package/src/react/deltas.ts +5 -2
- package/src/react/index.ts +23 -11
- package/src/react/optimisticallySendMessage.ts +1 -1
- package/src/validators.ts +6 -0
|
@@ -1414,6 +1414,12 @@ export type Mounts = {
|
|
|
1414
1414
|
>;
|
|
1415
1415
|
};
|
|
1416
1416
|
streams: {
|
|
1417
|
+
abort: FunctionReference<
|
|
1418
|
+
"mutation",
|
|
1419
|
+
"public",
|
|
1420
|
+
{ reason: string; streamId: string },
|
|
1421
|
+
null
|
|
1422
|
+
>;
|
|
1417
1423
|
addDelta: FunctionReference<
|
|
1418
1424
|
"mutation",
|
|
1419
1425
|
"public",
|
|
@@ -1467,7 +1473,7 @@ export type Mounts = {
|
|
|
1467
1473
|
start: number;
|
|
1468
1474
|
streamId: string;
|
|
1469
1475
|
},
|
|
1470
|
-
|
|
1476
|
+
boolean
|
|
1471
1477
|
>;
|
|
1472
1478
|
create: FunctionReference<
|
|
1473
1479
|
"mutation",
|
|
@@ -1569,13 +1575,18 @@ export type Mounts = {
|
|
|
1569
1575
|
list: FunctionReference<
|
|
1570
1576
|
"query",
|
|
1571
1577
|
"public",
|
|
1572
|
-
{
|
|
1578
|
+
{
|
|
1579
|
+
startOrder?: number;
|
|
1580
|
+
statuses?: Array<"streaming" | "finished" | "aborted">;
|
|
1581
|
+
threadId: string;
|
|
1582
|
+
},
|
|
1573
1583
|
Array<{
|
|
1574
1584
|
agentName?: string;
|
|
1575
1585
|
model?: string;
|
|
1576
1586
|
order: number;
|
|
1577
1587
|
provider?: string;
|
|
1578
1588
|
providerOptions?: Record<string, Record<string, any>>;
|
|
1589
|
+
status: "streaming" | "finished" | "aborted";
|
|
1579
1590
|
stepOrder: number;
|
|
1580
1591
|
streamId: string;
|
|
1581
1592
|
userId?: string;
|
|
@@ -1663,13 +1674,21 @@ export type Mounts = {
|
|
|
1663
1674
|
deleteAllForThreadIdAsync: FunctionReference<
|
|
1664
1675
|
"mutation",
|
|
1665
1676
|
"public",
|
|
1666
|
-
{
|
|
1667
|
-
|
|
1677
|
+
{
|
|
1678
|
+
cursor?: string;
|
|
1679
|
+
deltaCursor?: string;
|
|
1680
|
+
limit?: number;
|
|
1681
|
+
messagesDone?: boolean;
|
|
1682
|
+
streamOrder?: number;
|
|
1683
|
+
streamsDone?: boolean;
|
|
1684
|
+
threadId: string;
|
|
1685
|
+
},
|
|
1686
|
+
{ isDone: boolean }
|
|
1668
1687
|
>;
|
|
1669
1688
|
deleteAllForThreadIdSync: FunctionReference<
|
|
1670
1689
|
"action",
|
|
1671
1690
|
"public",
|
|
1672
|
-
{
|
|
1691
|
+
{ limit?: number; threadId: string },
|
|
1673
1692
|
null
|
|
1674
1693
|
>;
|
|
1675
1694
|
getThread: FunctionReference<
|
package/src/component/schema.ts
CHANGED
package/src/component/streams.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
vStreamMessage,
|
|
7
7
|
} from "../validators.js";
|
|
8
8
|
import { api, internal } from "./_generated/api.js";
|
|
9
|
-
import type { Id } from "./_generated/dataModel.js";
|
|
9
|
+
import type { Doc, Id } from "./_generated/dataModel.js";
|
|
10
10
|
import {
|
|
11
11
|
internalMutation,
|
|
12
12
|
mutation,
|
|
@@ -17,20 +17,30 @@ import {
|
|
|
17
17
|
import schema from "./schema.js";
|
|
18
18
|
import { stream } from "convex-helpers/server/stream";
|
|
19
19
|
import { mergedStream } from "convex-helpers/server/stream";
|
|
20
|
+
import { paginator } from "convex-helpers/server/pagination";
|
|
21
|
+
|
|
22
|
+
const SECOND = 1000;
|
|
23
|
+
const MINUTE = 60 * SECOND;
|
|
20
24
|
|
|
21
25
|
const MAX_DELTAS_PER_REQUEST = 1000;
|
|
22
26
|
const MAX_DELTAS_PER_STREAM = 100;
|
|
23
|
-
const TIMEOUT_INTERVAL =
|
|
24
|
-
const DELETE_STREAM_DELAY =
|
|
27
|
+
const TIMEOUT_INTERVAL = 10 * MINUTE;
|
|
28
|
+
const DELETE_STREAM_DELAY = MINUTE * 5; // 5 minutes
|
|
25
29
|
|
|
26
30
|
const deltaValidator = schema.tables.streamDeltas.validator;
|
|
27
31
|
|
|
28
32
|
export const addDelta = mutation({
|
|
29
33
|
args: deltaValidator,
|
|
30
|
-
returns: v.
|
|
34
|
+
returns: v.boolean(),
|
|
31
35
|
handler: async (ctx, args) => {
|
|
32
36
|
await ctx.db.insert("streamDeltas", args);
|
|
33
37
|
await heartbeatStream(ctx, { streamId: args.streamId });
|
|
38
|
+
const stream = await ctx.db.get(args.streamId);
|
|
39
|
+
if (stream?.state.kind !== "streaming") {
|
|
40
|
+
console.warn(`Stream is not streaming: ${args.streamId}`);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
34
44
|
},
|
|
35
45
|
});
|
|
36
46
|
|
|
@@ -96,33 +106,87 @@ export const create = mutation({
|
|
|
96
106
|
export const list = query({
|
|
97
107
|
args: {
|
|
98
108
|
threadId: v.id("threads"),
|
|
109
|
+
startOrder: v.optional(v.number()),
|
|
110
|
+
statuses: v.optional(
|
|
111
|
+
v.array(
|
|
112
|
+
v.union(
|
|
113
|
+
v.literal("streaming"),
|
|
114
|
+
v.literal("finished"),
|
|
115
|
+
v.literal("aborted")
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
),
|
|
99
119
|
},
|
|
100
120
|
returns: v.array(vStreamMessage),
|
|
101
121
|
handler: async (ctx, args) => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
const statuses = args.statuses ?? ["streaming"];
|
|
123
|
+
const messages = await mergedStream(
|
|
124
|
+
statuses.map((status) =>
|
|
125
|
+
stream(ctx.db, schema)
|
|
126
|
+
.query("streamingMessages")
|
|
127
|
+
.withIndex("threadId_state_order_stepOrder", (q) =>
|
|
128
|
+
q
|
|
129
|
+
.eq("threadId", args.threadId)
|
|
130
|
+
.eq("state.kind", status)
|
|
131
|
+
.gte("order", args.startOrder ?? 0)
|
|
132
|
+
)
|
|
133
|
+
.order("desc")
|
|
134
|
+
),
|
|
135
|
+
["order", "stepOrder"]
|
|
136
|
+
).take(100);
|
|
137
|
+
|
|
138
|
+
return messages.map((m) => ({
|
|
139
|
+
streamId: m._id,
|
|
140
|
+
status: m.state.kind,
|
|
141
|
+
...pick(m, [
|
|
142
|
+
"order",
|
|
143
|
+
"stepOrder",
|
|
144
|
+
"userId",
|
|
145
|
+
"agentName",
|
|
146
|
+
"model",
|
|
147
|
+
"provider",
|
|
148
|
+
"providerOptions",
|
|
149
|
+
]),
|
|
150
|
+
}));
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
export const abort = mutation({
|
|
155
|
+
args: {
|
|
156
|
+
streamId: v.id("streamingMessages"),
|
|
157
|
+
reason: v.string(),
|
|
158
|
+
},
|
|
159
|
+
returns: v.null(),
|
|
160
|
+
handler: async (ctx, args) => {
|
|
161
|
+
const stream = await ctx.db.get(args.streamId);
|
|
162
|
+
if (!stream) {
|
|
163
|
+
throw new Error(`Stream not found: ${args.streamId}`);
|
|
164
|
+
}
|
|
165
|
+
if (stream.state.kind !== "streaming") {
|
|
166
|
+
console.warn(
|
|
167
|
+
`Stream trying to abort but not currently streaming (${stream.state.kind}): ${args.streamId}`
|
|
122
168
|
);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
await cleanupTimeoutFn(ctx, stream);
|
|
172
|
+
await ctx.db.patch(args.streamId, {
|
|
173
|
+
state: { kind: "aborted", reason: args.reason },
|
|
174
|
+
});
|
|
123
175
|
},
|
|
124
176
|
});
|
|
125
177
|
|
|
178
|
+
async function cleanupTimeoutFn(
|
|
179
|
+
ctx: MutationCtx,
|
|
180
|
+
stream: Doc<"streamingMessages">
|
|
181
|
+
) {
|
|
182
|
+
if (stream.state.kind === "streaming" && stream.state.timeoutFnId) {
|
|
183
|
+
const timeoutFn = await ctx.db.system.get(stream.state.timeoutFnId);
|
|
184
|
+
if (timeoutFn?.state.kind === "pending") {
|
|
185
|
+
await ctx.scheduler.cancel(stream.state.timeoutFnId);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
126
190
|
export const finish = mutation({
|
|
127
191
|
args: {
|
|
128
192
|
streamId: v.id("streamingMessages"),
|
|
@@ -138,14 +202,12 @@ export const finish = mutation({
|
|
|
138
202
|
throw new Error(`Stream not found: ${args.streamId}`);
|
|
139
203
|
}
|
|
140
204
|
if (stream.state.kind !== "streaming") {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (timeoutFn?.state.kind === "pending") {
|
|
146
|
-
await ctx.scheduler.cancel(stream.state.timeoutFnId);
|
|
147
|
-
}
|
|
205
|
+
console.warn(
|
|
206
|
+
`Stream trying to finish but not currently streaming: ${args.streamId}`
|
|
207
|
+
);
|
|
208
|
+
return;
|
|
148
209
|
}
|
|
210
|
+
await cleanupTimeoutFn(ctx, stream);
|
|
149
211
|
await ctx.db.patch(args.streamId, {
|
|
150
212
|
state: { kind: "finished", endedAt: Date.now() },
|
|
151
213
|
});
|
|
@@ -210,8 +272,8 @@ export const timeoutStream = internalMutation({
|
|
|
210
272
|
}
|
|
211
273
|
await ctx.db.patch(args.streamId, {
|
|
212
274
|
state: {
|
|
213
|
-
kind: "
|
|
214
|
-
|
|
275
|
+
kind: "aborted",
|
|
276
|
+
reason: "timeout",
|
|
215
277
|
},
|
|
216
278
|
});
|
|
217
279
|
},
|
|
@@ -221,7 +283,7 @@ async function deletePageForStreamId(
|
|
|
221
283
|
ctx: MutationCtx,
|
|
222
284
|
args: { streamId: Id<"streamingMessages">; cursor?: string }
|
|
223
285
|
) {
|
|
224
|
-
const deltas = await ctx.db
|
|
286
|
+
const deltas = await paginator(ctx.db, schema)
|
|
225
287
|
.query("streamDeltas")
|
|
226
288
|
.withIndex("streamId_start_end", (q) => q.eq("streamId", args.streamId))
|
|
227
289
|
.paginate({
|
|
@@ -229,6 +291,9 @@ async function deletePageForStreamId(
|
|
|
229
291
|
cursor: args.cursor ?? null,
|
|
230
292
|
});
|
|
231
293
|
await Promise.all(deltas.page.map((d) => ctx.db.delete(d._id)));
|
|
294
|
+
if (deltas.isDone) {
|
|
295
|
+
await ctx.db.delete(args.streamId);
|
|
296
|
+
}
|
|
232
297
|
return deltas;
|
|
233
298
|
}
|
|
234
299
|
|
|
@@ -268,13 +333,12 @@ export async function deleteStreamsPageForThreadId(
|
|
|
268
333
|
cursor: deltaCursor,
|
|
269
334
|
});
|
|
270
335
|
if (result.isDone) {
|
|
271
|
-
await ctx.db.delete(streamMessage._id);
|
|
272
336
|
deltaCursor = undefined;
|
|
273
337
|
}
|
|
274
338
|
return {
|
|
275
339
|
isDone: false,
|
|
276
340
|
streamOrder: streamMessage.order,
|
|
277
|
-
deltaCursor
|
|
341
|
+
deltaCursor,
|
|
278
342
|
};
|
|
279
343
|
}
|
|
280
344
|
|
|
@@ -333,7 +397,6 @@ export const deleteStreamSync = mutation({
|
|
|
333
397
|
cursor: deltas.continueCursor,
|
|
334
398
|
});
|
|
335
399
|
}
|
|
336
|
-
await ctx.db.delete(args.streamId);
|
|
337
400
|
},
|
|
338
401
|
});
|
|
339
402
|
|
package/src/component/threads.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from "./_generated/server.js";
|
|
17
17
|
import { deleteMessage } from "./messages.js";
|
|
18
18
|
import { schema, v } from "./schema.js";
|
|
19
|
+
import { deleteStreamsPageForThreadId } from "./streams.js";
|
|
19
20
|
|
|
20
21
|
function publicThreadOrNull(thread: Doc<"threads"> | null): ThreadDoc | null {
|
|
21
22
|
if (thread === null) {
|
|
@@ -100,29 +101,17 @@ export const updateThread = mutation({
|
|
|
100
101
|
|
|
101
102
|
// TODO: delete thread
|
|
102
103
|
|
|
103
|
-
const deleteThreadArgs = {
|
|
104
|
-
threadId: v.id("threads"),
|
|
105
|
-
cursor: v.optional(v.string()),
|
|
106
|
-
limit: v.optional(v.number()),
|
|
107
|
-
};
|
|
108
|
-
type DeleteThreadArgs = ObjectType<typeof deleteThreadArgs>;
|
|
109
|
-
const deleteThreadReturns = {
|
|
110
|
-
cursor: v.string(),
|
|
111
|
-
isDone: v.boolean(),
|
|
112
|
-
};
|
|
113
|
-
type DeleteThreadReturns = ObjectType<typeof deleteThreadReturns>;
|
|
114
|
-
|
|
115
104
|
/**
|
|
116
105
|
* Use this to delete a thread and everything it contains.
|
|
117
106
|
* It will try to delete all pages synchronously.
|
|
118
107
|
* If it times out or fails, you'll have to run it again.
|
|
119
108
|
*/
|
|
120
109
|
export const deleteAllForThreadIdSync = action({
|
|
121
|
-
args:
|
|
110
|
+
args: { threadId: v.id("threads"), limit: v.optional(v.number()) },
|
|
122
111
|
handler: async (ctx, args) => {
|
|
123
|
-
let cursor =
|
|
112
|
+
let cursor: string | undefined = undefined;
|
|
124
113
|
while (true) {
|
|
125
|
-
const result = await ctx.runMutation(
|
|
114
|
+
const result: DeleteThreadReturns = await ctx.runMutation(
|
|
126
115
|
internal.threads._deletePageForThreadId,
|
|
127
116
|
{ threadId: args.threadId, cursor, limit: args.limit }
|
|
128
117
|
);
|
|
@@ -138,6 +127,22 @@ export const deleteAllForThreadIdSync = action({
|
|
|
138
127
|
returns: v.null(),
|
|
139
128
|
});
|
|
140
129
|
|
|
130
|
+
const deleteThreadArgs = {
|
|
131
|
+
threadId: v.id("threads"),
|
|
132
|
+
cursor: v.optional(v.string()),
|
|
133
|
+
messagesDone: v.optional(v.boolean()),
|
|
134
|
+
streamsDone: v.optional(v.boolean()),
|
|
135
|
+
streamOrder: v.optional(v.number()),
|
|
136
|
+
deltaCursor: v.optional(v.string()),
|
|
137
|
+
limit: v.optional(v.number()),
|
|
138
|
+
};
|
|
139
|
+
type DeleteThreadArgs = ObjectType<typeof deleteThreadArgs>;
|
|
140
|
+
const deleteThreadReturns = {
|
|
141
|
+
cursor: v.string(),
|
|
142
|
+
isDone: v.boolean(),
|
|
143
|
+
};
|
|
144
|
+
type DeleteThreadReturns = ObjectType<typeof deleteThreadReturns>;
|
|
145
|
+
|
|
141
146
|
export const _deletePageForThreadId = internalMutation({
|
|
142
147
|
args: deleteThreadArgs,
|
|
143
148
|
handler: deletePageForThreadIdHandler,
|
|
@@ -151,22 +156,39 @@ export const _deletePageForThreadId = internalMutation({
|
|
|
151
156
|
export const deleteAllForThreadIdAsync = mutation({
|
|
152
157
|
args: deleteThreadArgs,
|
|
153
158
|
handler: async (ctx, args) => {
|
|
154
|
-
|
|
155
|
-
|
|
159
|
+
let messagesResult = {
|
|
160
|
+
isDone: args.messagesDone ?? false,
|
|
161
|
+
cursor: args.cursor,
|
|
162
|
+
};
|
|
163
|
+
if (!args.messagesDone) {
|
|
164
|
+
messagesResult = await deletePageForThreadIdHandler(ctx, args);
|
|
165
|
+
}
|
|
166
|
+
let streamResult = {
|
|
167
|
+
isDone: args.streamsDone ?? false,
|
|
168
|
+
streamOrder: args.streamOrder,
|
|
169
|
+
deltaCursor: args.deltaCursor,
|
|
170
|
+
};
|
|
171
|
+
if (!args.streamsDone) {
|
|
172
|
+
streamResult = await deleteStreamsPageForThreadId(ctx, {
|
|
173
|
+
threadId: args.threadId,
|
|
174
|
+
streamOrder: args.streamOrder,
|
|
175
|
+
deltaCursor: args.deltaCursor,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
const isDone = messagesResult.isDone && streamResult.isDone;
|
|
179
|
+
if (!isDone) {
|
|
156
180
|
await ctx.scheduler.runAfter(0, api.threads.deleteAllForThreadIdAsync, {
|
|
157
181
|
threadId: args.threadId,
|
|
158
|
-
cursor:
|
|
182
|
+
cursor: messagesResult.cursor,
|
|
183
|
+
messagesDone: messagesResult.isDone,
|
|
184
|
+
streamsDone: streamResult.isDone,
|
|
185
|
+
streamOrder: streamResult.streamOrder,
|
|
186
|
+
deltaCursor: streamResult.deltaCursor,
|
|
159
187
|
});
|
|
160
188
|
}
|
|
161
|
-
|
|
162
|
-
await ctx.scheduler.runAfter(
|
|
163
|
-
0,
|
|
164
|
-
api.streams.deleteAllStreamsForThreadIdSync,
|
|
165
|
-
{ threadId: args.threadId }
|
|
166
|
-
);
|
|
167
|
-
return result;
|
|
189
|
+
return { isDone };
|
|
168
190
|
},
|
|
169
|
-
returns:
|
|
191
|
+
returns: v.object({ isDone: v.boolean() }),
|
|
170
192
|
});
|
|
171
193
|
|
|
172
194
|
async function deletePageForThreadIdHandler(
|
package/src/react/deltas.ts
CHANGED
|
@@ -189,6 +189,7 @@ export function applyDeltasToStreamMessage(
|
|
|
189
189
|
contentToAdd = part;
|
|
190
190
|
break;
|
|
191
191
|
case "reasoning":
|
|
192
|
+
currentMessage.reasoning += part.textDelta;
|
|
192
193
|
if (lastContent?.type === "reasoning") {
|
|
193
194
|
lastContent.text += part.textDelta;
|
|
194
195
|
} else {
|
|
@@ -255,12 +256,14 @@ export function createStreamingMessage(
|
|
|
255
256
|
): MessageDoc {
|
|
256
257
|
const { streamId, ...rest } = message;
|
|
257
258
|
const metadata: MessageDoc = {
|
|
259
|
+
...rest,
|
|
258
260
|
_id: `${streamId}-${index}`,
|
|
259
261
|
_creationTime: Date.now(),
|
|
260
|
-
status:
|
|
262
|
+
status: (
|
|
263
|
+
{ streaming: "pending", finished: "success", aborted: "failed" } as const
|
|
264
|
+
)[message.status],
|
|
261
265
|
threadId,
|
|
262
266
|
tool: false,
|
|
263
|
-
...rest,
|
|
264
267
|
};
|
|
265
268
|
switch (part.type) {
|
|
266
269
|
case "text-delta":
|
package/src/react/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import type
|
|
2
|
+
import { omit, type ErrorMessage } from "convex-helpers";
|
|
3
3
|
import {
|
|
4
4
|
type PaginatedQueryArgs,
|
|
5
5
|
type UsePaginatedQueryResult,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "convex/react";
|
|
8
8
|
import { usePaginatedQuery } from "convex-helpers/react";
|
|
9
9
|
import type { FunctionArgs } from "convex/server";
|
|
10
|
-
import { useMemo, useState } from "react";
|
|
10
|
+
import { useMemo, useRef, useState } from "react";
|
|
11
11
|
import type { MessageDoc } from "../client/index.js";
|
|
12
12
|
import type { SyncStreamsReturnValue } from "../client/types.js";
|
|
13
13
|
import type { StreamArgs } from "../validators.js";
|
|
@@ -103,14 +103,18 @@ export function useThreadMessages<
|
|
|
103
103
|
ThreadMessagesArgs<Query>,
|
|
104
104
|
ThreadMessagesResult<Query>
|
|
105
105
|
>,
|
|
106
|
-
!options.stream
|
|
106
|
+
!options.stream ||
|
|
107
|
+
args === "skip" ||
|
|
108
|
+
paginated.status === "LoadingFirstPage"
|
|
109
|
+
? "skip"
|
|
110
|
+
: { ...args, startOrder: paginated.results.at(-1)?.order }
|
|
107
111
|
);
|
|
108
112
|
|
|
109
113
|
const merged = useMemo(() => {
|
|
110
114
|
const streamListMessages =
|
|
111
115
|
streamMessages?.map((m) => ({
|
|
112
116
|
...m,
|
|
113
|
-
streaming:
|
|
117
|
+
streaming: !m.status || m.status === "streaming",
|
|
114
118
|
})) ?? [];
|
|
115
119
|
return {
|
|
116
120
|
...paginated,
|
|
@@ -150,7 +154,7 @@ export function useStreamingThreadMessages<
|
|
|
150
154
|
Query extends ThreadStreamQuery<any, any>,
|
|
151
155
|
>(
|
|
152
156
|
query: Query,
|
|
153
|
-
args: ThreadMessagesArgs<Query> | "skip"
|
|
157
|
+
args: (ThreadMessagesArgs<Query> & { startOrder?: number }) | "skip"
|
|
154
158
|
): Array<ThreadMessagesResult<Query>> | undefined {
|
|
155
159
|
// Invariant: streamMessages[streamId] is comprised of all deltas up to the
|
|
156
160
|
// cursor. There can be multiple messages in the same stream, e.g. for tool
|
|
@@ -158,15 +162,23 @@ export function useStreamingThreadMessages<
|
|
|
158
162
|
const [streams, setStreams] = useState<
|
|
159
163
|
Array<{ streamId: string; cursor: number; messages: MessageDoc[] }>
|
|
160
164
|
>([]);
|
|
165
|
+
const startOrderRef = useRef<number>(0);
|
|
166
|
+
const queryArgs = args === "skip" ? args : omit(args, ["startOrder"]);
|
|
167
|
+
if (args !== "skip" && !startOrderRef.current && args.startOrder) {
|
|
168
|
+
startOrderRef.current = args.startOrder;
|
|
169
|
+
}
|
|
161
170
|
// Get all the active streams
|
|
162
171
|
const streamList = useQuery(
|
|
163
172
|
query,
|
|
164
|
-
|
|
165
|
-
?
|
|
173
|
+
queryArgs === "skip"
|
|
174
|
+
? queryArgs
|
|
166
175
|
: ({
|
|
167
|
-
...
|
|
176
|
+
...queryArgs,
|
|
168
177
|
paginationOpts: { cursor: null, numItems: 0 },
|
|
169
|
-
streamArgs: {
|
|
178
|
+
streamArgs: {
|
|
179
|
+
kind: "list",
|
|
180
|
+
startOrder: startOrderRef.current,
|
|
181
|
+
} as StreamArgs,
|
|
170
182
|
} as FunctionArgs<Query>)
|
|
171
183
|
) as
|
|
172
184
|
| { streams: Extract<SyncStreamsReturnValue, { kind: "list" }> }
|
|
@@ -186,10 +198,10 @@ export function useStreamingThreadMessages<
|
|
|
186
198
|
// Get the deltas for all the active streams, if any.
|
|
187
199
|
const cursorQuery = useQuery(
|
|
188
200
|
query,
|
|
189
|
-
|
|
201
|
+
queryArgs === "skip" || !streamList
|
|
190
202
|
? ("skip" as const)
|
|
191
203
|
: ({
|
|
192
|
-
...
|
|
204
|
+
...queryArgs,
|
|
193
205
|
paginationOpts: { cursor: null, numItems: 0 },
|
|
194
206
|
streamArgs: { kind: "deltas", cursors } as StreamArgs,
|
|
195
207
|
} as FunctionArgs<Query>)
|
|
@@ -11,7 +11,7 @@ export function optimisticallySendMessage(
|
|
|
11
11
|
) => void {
|
|
12
12
|
return (store, args) => {
|
|
13
13
|
const queries = store.getAllQueries(query);
|
|
14
|
-
let maxOrder =
|
|
14
|
+
let maxOrder = -1;
|
|
15
15
|
let maxStepOrder = 0;
|
|
16
16
|
for (const q of queries) {
|
|
17
17
|
if (q.args?.threadId !== args.threadId) continue;
|
package/src/validators.ts
CHANGED
|
@@ -460,6 +460,7 @@ export const vStreamArgs = v.optional(
|
|
|
460
460
|
v.union(
|
|
461
461
|
v.object({
|
|
462
462
|
kind: v.literal("list"),
|
|
463
|
+
startOrder: v.optional(v.number()),
|
|
463
464
|
}),
|
|
464
465
|
v.object({
|
|
465
466
|
kind: v.literal("deltas"),
|
|
@@ -471,6 +472,11 @@ export type StreamArgs = Infer<typeof vStreamArgs>;
|
|
|
471
472
|
|
|
472
473
|
export const vStreamMessage = v.object({
|
|
473
474
|
streamId: v.string(),
|
|
475
|
+
status: v.union(
|
|
476
|
+
v.literal("streaming"),
|
|
477
|
+
v.literal("finished"),
|
|
478
|
+
v.literal("aborted")
|
|
479
|
+
),
|
|
474
480
|
order: v.number(),
|
|
475
481
|
stepOrder: v.number(),
|
|
476
482
|
// metadata
|