@convex-dev/agent 0.1.9 → 0.1.10-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/dist/commonjs/client/index.d.ts +8 -7
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +2 -0
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/client/streaming.d.ts.map +1 -1
- package/dist/commonjs/client/streaming.js +7 -1
- package/dist/commonjs/client/streaming.js.map +1 -1
- package/dist/commonjs/component/_generated/api.d.ts +12 -1
- package/dist/commonjs/component/messages.d.ts +30 -30
- package/dist/commonjs/component/schema.d.ts +84 -74
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +3 -2
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/streams.d.ts +7 -0
- package/dist/commonjs/component/streams.d.ts.map +1 -1
- package/dist/commonjs/component/streams.js +55 -18
- package/dist/commonjs/component/streams.js.map +1 -1
- package/dist/commonjs/component/threads.d.ts +8 -8
- package/dist/commonjs/react/deltas.d.ts.map +1 -1
- package/dist/commonjs/react/deltas.js +2 -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 +15 -10
- 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 +8 -7
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +2 -0
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/streaming.d.ts.map +1 -1
- package/dist/esm/client/streaming.js +7 -1
- package/dist/esm/client/streaming.js.map +1 -1
- package/dist/esm/component/_generated/api.d.ts +12 -1
- package/dist/esm/component/messages.d.ts +30 -30
- package/dist/esm/component/schema.d.ts +84 -74
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +3 -2
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/streams.d.ts +7 -0
- package/dist/esm/component/streams.d.ts.map +1 -1
- package/dist/esm/component/streams.js +55 -18
- package/dist/esm/component/streams.js.map +1 -1
- package/dist/esm/component/threads.d.ts +8 -8
- package/dist/esm/react/deltas.d.ts.map +1 -1
- package/dist/esm/react/deltas.js +2 -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 +15 -10
- 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 +4 -0
- package/src/client/streaming.ts +7 -1
- package/src/component/_generated/api.d.ts +12 -1
- package/src/component/schema.ts +3 -2
- package/src/component/streams.ts +91 -35
- package/src/react/deltas.ts +4 -2
- package/src/react/index.ts +23 -11
- package/src/react/optimisticallySendMessage.ts +1 -1
- package/src/validators.ts +6 -0
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,
|
|
@@ -106,33 +106,87 @@ export const create = mutation({
|
|
|
106
106
|
export const list = query({
|
|
107
107
|
args: {
|
|
108
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
|
+
),
|
|
109
119
|
},
|
|
110
120
|
returns: v.array(vStreamMessage),
|
|
111
121
|
handler: async (ctx, args) => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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}`
|
|
132
168
|
);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
await cleanupTimeoutFn(ctx, stream);
|
|
172
|
+
await ctx.db.patch(args.streamId, {
|
|
173
|
+
state: { kind: "aborted", reason: args.reason },
|
|
174
|
+
});
|
|
133
175
|
},
|
|
134
176
|
});
|
|
135
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
|
+
|
|
136
190
|
export const finish = mutation({
|
|
137
191
|
args: {
|
|
138
192
|
streamId: v.id("streamingMessages"),
|
|
@@ -153,20 +207,15 @@ export const finish = mutation({
|
|
|
153
207
|
);
|
|
154
208
|
return;
|
|
155
209
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (timeoutFn?.state.kind === "pending") {
|
|
159
|
-
await ctx.scheduler.cancel(stream.state.timeoutFnId);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
await ctx.db.patch(args.streamId, {
|
|
163
|
-
state: { kind: "finished", endedAt: Date.now() },
|
|
164
|
-
});
|
|
165
|
-
await ctx.scheduler.runAfter(
|
|
210
|
+
await cleanupTimeoutFn(ctx, stream);
|
|
211
|
+
const cleanupFnId = await ctx.scheduler.runAfter(
|
|
166
212
|
DELETE_STREAM_DELAY,
|
|
167
213
|
api.streams.deleteStreamAsync,
|
|
168
214
|
{ streamId: args.streamId }
|
|
169
215
|
);
|
|
216
|
+
await ctx.db.patch(args.streamId, {
|
|
217
|
+
state: { kind: "finished", endedAt: Date.now(), cleanupFnId },
|
|
218
|
+
});
|
|
170
219
|
},
|
|
171
220
|
});
|
|
172
221
|
|
|
@@ -217,14 +266,14 @@ export const timeoutStream = internalMutation({
|
|
|
217
266
|
returns: v.null(),
|
|
218
267
|
handler: async (ctx, args) => {
|
|
219
268
|
const stream = await ctx.db.get(args.streamId);
|
|
220
|
-
if (!stream) {
|
|
269
|
+
if (!stream || stream.state.kind !== "streaming") {
|
|
221
270
|
console.warn("Stream not found", args.streamId);
|
|
222
271
|
return;
|
|
223
272
|
}
|
|
224
273
|
await ctx.db.patch(args.streamId, {
|
|
225
274
|
state: {
|
|
226
|
-
kind: "
|
|
227
|
-
|
|
275
|
+
kind: "aborted",
|
|
276
|
+
reason: "timeout",
|
|
228
277
|
},
|
|
229
278
|
});
|
|
230
279
|
},
|
|
@@ -243,7 +292,14 @@ async function deletePageForStreamId(
|
|
|
243
292
|
});
|
|
244
293
|
await Promise.all(deltas.page.map((d) => ctx.db.delete(d._id)));
|
|
245
294
|
if (deltas.isDone) {
|
|
246
|
-
await ctx.db.
|
|
295
|
+
const stream = await ctx.db.get(args.streamId);
|
|
296
|
+
if (stream) {
|
|
297
|
+
await cleanupTimeoutFn(ctx, stream);
|
|
298
|
+
if (stream.state.kind === "finished" && stream.state.cleanupFnId) {
|
|
299
|
+
await ctx.scheduler.cancel(stream.state.cleanupFnId);
|
|
300
|
+
}
|
|
301
|
+
await ctx.db.delete(args.streamId);
|
|
302
|
+
}
|
|
247
303
|
}
|
|
248
304
|
return deltas;
|
|
249
305
|
}
|
package/src/react/deltas.ts
CHANGED
|
@@ -256,12 +256,14 @@ export function createStreamingMessage(
|
|
|
256
256
|
): MessageDoc {
|
|
257
257
|
const { streamId, ...rest } = message;
|
|
258
258
|
const metadata: MessageDoc = {
|
|
259
|
+
...rest,
|
|
259
260
|
_id: `${streamId}-${index}`,
|
|
260
261
|
_creationTime: Date.now(),
|
|
261
|
-
status:
|
|
262
|
+
status: (
|
|
263
|
+
{ streaming: "pending", finished: "success", aborted: "failed" } as const
|
|
264
|
+
)[message.status],
|
|
262
265
|
threadId,
|
|
263
266
|
tool: false,
|
|
264
|
-
...rest,
|
|
265
267
|
};
|
|
266
268
|
switch (part.type) {
|
|
267
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
|