@convex-dev/agent 0.0.1-alpha.1 → 0.0.1-alpha.2
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 +297 -6
- package/dist/commonjs/client/index.d.ts +480 -56
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +154 -78
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/client/types.d.ts +3 -0
- package/dist/commonjs/client/types.d.ts.map +1 -1
- package/dist/commonjs/component/messages.d.ts +10 -9
- package/dist/commonjs/component/messages.d.ts.map +1 -1
- package/dist/commonjs/component/messages.js +84 -41
- package/dist/commonjs/component/messages.js.map +1 -1
- package/dist/commonjs/component/schema.d.ts +10 -6
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +9 -2
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/mapping.d.ts +6 -1
- package/dist/commonjs/mapping.d.ts.map +1 -1
- package/dist/commonjs/mapping.js +25 -0
- package/dist/commonjs/mapping.js.map +1 -1
- package/dist/commonjs/validators.d.ts +1375 -0
- package/dist/commonjs/validators.d.ts.map +1 -1
- package/dist/commonjs/validators.js +27 -0
- package/dist/commonjs/validators.js.map +1 -1
- package/dist/esm/client/index.d.ts +480 -56
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +154 -78
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/types.d.ts +3 -0
- package/dist/esm/client/types.d.ts.map +1 -1
- package/dist/esm/component/messages.d.ts +10 -9
- package/dist/esm/component/messages.d.ts.map +1 -1
- package/dist/esm/component/messages.js +84 -41
- package/dist/esm/component/messages.js.map +1 -1
- package/dist/esm/component/schema.d.ts +10 -6
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +9 -2
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/mapping.d.ts +6 -1
- package/dist/esm/mapping.d.ts.map +1 -1
- package/dist/esm/mapping.js +25 -0
- package/dist/esm/mapping.js.map +1 -1
- package/dist/esm/validators.d.ts +1375 -0
- package/dist/esm/validators.d.ts.map +1 -1
- package/dist/esm/validators.js +27 -0
- package/dist/esm/validators.js.map +1 -1
- package/package.json +2 -2
- package/src/client/index.ts +290 -188
- package/src/client/types.ts +4 -0
- package/src/component/_generated/api.d.ts +7 -6
- package/src/component/messages.ts +106 -58
- package/src/component/schema.ts +9 -2
- package/src/mapping.ts +46 -11
- package/src/validators.test.ts +9 -0
- package/src/validators.ts +32 -0
package/src/client/types.ts
CHANGED
|
@@ -7,6 +7,10 @@ import {
|
|
|
7
7
|
GenericActionCtx,
|
|
8
8
|
} from "convex/server";
|
|
9
9
|
import { GenericId } from "convex/values";
|
|
10
|
+
import type { Doc } from "../component/_generated/dataModel";
|
|
11
|
+
|
|
12
|
+
export type ChatDoc = OpaqueIds<Doc<"chats">>;
|
|
13
|
+
export type MessageDoc = OpaqueIds<Doc<"messages">>;
|
|
10
14
|
|
|
11
15
|
/* Type utils follow */
|
|
12
16
|
export type RunQueryCtx = {
|
|
@@ -40,7 +40,7 @@ export type Mounts = {
|
|
|
40
40
|
"public",
|
|
41
41
|
{
|
|
42
42
|
agentName?: string;
|
|
43
|
-
chatId
|
|
43
|
+
chatId?: string;
|
|
44
44
|
failPendingSteps?: boolean;
|
|
45
45
|
messages: Array<{
|
|
46
46
|
fileId?: string;
|
|
@@ -147,13 +147,14 @@ export type Mounts = {
|
|
|
147
147
|
parentMessageId?: string;
|
|
148
148
|
pending?: boolean;
|
|
149
149
|
stepId?: string;
|
|
150
|
+
userId?: string;
|
|
150
151
|
},
|
|
151
152
|
{
|
|
152
153
|
messages: Array<{
|
|
153
154
|
_creationTime: number;
|
|
154
155
|
_id: string;
|
|
155
156
|
agentName?: string;
|
|
156
|
-
chatId
|
|
157
|
+
chatId?: string;
|
|
157
158
|
embeddingId?:
|
|
158
159
|
| string
|
|
159
160
|
| string
|
|
@@ -277,7 +278,7 @@ export type Mounts = {
|
|
|
277
278
|
_creationTime: number;
|
|
278
279
|
_id: string;
|
|
279
280
|
agentName?: string;
|
|
280
|
-
chatId
|
|
281
|
+
chatId?: string;
|
|
281
282
|
embeddingId?:
|
|
282
283
|
| string
|
|
283
284
|
| string
|
|
@@ -1021,7 +1022,7 @@ export type Mounts = {
|
|
|
1021
1022
|
_creationTime: number;
|
|
1022
1023
|
_id: string;
|
|
1023
1024
|
agentName?: string;
|
|
1024
|
-
chatId
|
|
1025
|
+
chatId?: string;
|
|
1025
1026
|
embeddingId?:
|
|
1026
1027
|
| string
|
|
1027
1028
|
| string
|
|
@@ -1208,7 +1209,7 @@ export type Mounts = {
|
|
|
1208
1209
|
_creationTime: number;
|
|
1209
1210
|
_id: string;
|
|
1210
1211
|
agentName?: string;
|
|
1211
|
-
chatId
|
|
1212
|
+
chatId?: string;
|
|
1212
1213
|
embeddingId?:
|
|
1213
1214
|
| string
|
|
1214
1215
|
| string
|
|
@@ -1337,7 +1338,7 @@ export type Mounts = {
|
|
|
1337
1338
|
_creationTime: number;
|
|
1338
1339
|
_id: string;
|
|
1339
1340
|
agentName?: string;
|
|
1340
|
-
chatId
|
|
1341
|
+
chatId?: string;
|
|
1341
1342
|
embeddingId?:
|
|
1342
1343
|
| string
|
|
1343
1344
|
| string
|
|
@@ -2,19 +2,14 @@ import { assert, omit, pick } from "convex-helpers";
|
|
|
2
2
|
import { paginator } from "convex-helpers/server/pagination";
|
|
3
3
|
import { mergedStream, stream } from "convex-helpers/server/stream";
|
|
4
4
|
import { nullable, partial } from "convex-helpers/validators";
|
|
5
|
-
import {
|
|
5
|
+
import { ObjectType } from "convex/values";
|
|
6
6
|
import { DEFAULT_MESSAGE_RANGE, extractText, isTool } from "../shared.js";
|
|
7
7
|
import {
|
|
8
|
-
Message,
|
|
9
|
-
MessageWithFileAndId,
|
|
10
|
-
vAssistantMessage,
|
|
11
8
|
vChatStatus,
|
|
12
9
|
vMessageStatus,
|
|
13
10
|
vMessageWithFileAndId,
|
|
14
11
|
vSearchOptions,
|
|
15
|
-
vStep,
|
|
16
12
|
vStepWithMessagesWithFileAndId,
|
|
17
|
-
vToolMessage,
|
|
18
13
|
} from "../validators.js";
|
|
19
14
|
import { api, internal } from "./_generated/api.js";
|
|
20
15
|
import { Doc, Id } from "./_generated/dataModel.js";
|
|
@@ -63,7 +58,7 @@ export const getChatsByUserId = query({
|
|
|
63
58
|
.gte("order", args.offset ?? 0)
|
|
64
59
|
)
|
|
65
60
|
);
|
|
66
|
-
const chats = await mergedStream(streams, ["order"
|
|
61
|
+
const chats = await mergedStream(streams, ["order"]).paginate({
|
|
67
62
|
numItems: args.limit ?? 100,
|
|
68
63
|
cursor: args.cursor ?? null,
|
|
69
64
|
});
|
|
@@ -377,7 +372,8 @@ export const messageStatuses = vMessageDoc.fields.status.members.map(
|
|
|
377
372
|
);
|
|
378
373
|
|
|
379
374
|
const addMessagesArgs = {
|
|
380
|
-
|
|
375
|
+
userId: v.optional(v.string()),
|
|
376
|
+
chatId: v.optional(v.id("chats")),
|
|
381
377
|
stepId: v.optional(v.id("steps")),
|
|
382
378
|
parentMessageId: v.optional(v.id("messages")),
|
|
383
379
|
messages: v.array(vMessageWithFileAndId),
|
|
@@ -398,15 +394,22 @@ async function addMessagesHandler(
|
|
|
398
394
|
ctx: MutationCtx,
|
|
399
395
|
args: ObjectType<typeof addMessagesArgs>
|
|
400
396
|
) {
|
|
401
|
-
|
|
402
|
-
|
|
397
|
+
let userId = args.userId;
|
|
398
|
+
const chatId = args.chatId;
|
|
399
|
+
if (!userId && args.chatId) {
|
|
400
|
+
const chat = await ctx.db.get(args.chatId);
|
|
401
|
+
assert(chat, `Chat ${args.chatId} not found`);
|
|
402
|
+
userId = chat._id;
|
|
403
|
+
}
|
|
403
404
|
const { failPendingSteps, pending, messages, parentMessageId, ...rest } =
|
|
404
405
|
args;
|
|
405
|
-
|
|
406
|
+
const parent = parentMessageId && (await ctx.db.get(parentMessageId));
|
|
407
|
+
if (failPendingSteps && parent?.status !== "pending") {
|
|
408
|
+
assert(args.chatId, "chatId is required to fail pending steps");
|
|
406
409
|
const pendingMessages = await ctx.db
|
|
407
410
|
.query("messages")
|
|
408
411
|
.withIndex("chatId_status_tool_order_stepOrder", (q) =>
|
|
409
|
-
q.eq("chatId",
|
|
412
|
+
q.eq("chatId", chatId).eq("status", "pending")
|
|
410
413
|
)
|
|
411
414
|
.collect();
|
|
412
415
|
await Promise.all(
|
|
@@ -415,14 +418,14 @@ async function addMessagesHandler(
|
|
|
415
418
|
)
|
|
416
419
|
);
|
|
417
420
|
}
|
|
418
|
-
let
|
|
419
|
-
const maxMessage = await getMaxMessage(ctx,
|
|
421
|
+
let threadId = parentMessageId;
|
|
422
|
+
const maxMessage = await getMaxMessage(ctx, chatId, userId);
|
|
420
423
|
// If the previous message isn't our parent, we make a new thread.
|
|
421
|
-
|
|
424
|
+
threadId =
|
|
422
425
|
parentMessageId && maxMessage?._id === parentMessageId
|
|
423
426
|
? maxMessage.threadId ?? parentMessageId
|
|
424
427
|
: parentMessageId;
|
|
425
|
-
order = maxMessage?.order ?? -1;
|
|
428
|
+
let order = maxMessage?.order ?? -1;
|
|
426
429
|
const toReturn: Doc<"messages">[] = [];
|
|
427
430
|
if (messages.length > 0) {
|
|
428
431
|
for (const { message, fileId, id } of messages) {
|
|
@@ -434,7 +437,7 @@ async function addMessagesHandler(
|
|
|
434
437
|
const messageId = await ctx.db.insert("messages", {
|
|
435
438
|
...rest,
|
|
436
439
|
threadId,
|
|
437
|
-
userId
|
|
440
|
+
userId,
|
|
438
441
|
message,
|
|
439
442
|
id,
|
|
440
443
|
order,
|
|
@@ -449,18 +452,37 @@ async function addMessagesHandler(
|
|
|
449
452
|
return { messages: toReturn };
|
|
450
453
|
}
|
|
451
454
|
|
|
452
|
-
async function getMaxMessage(
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
455
|
+
async function getMaxMessage(
|
|
456
|
+
ctx: QueryCtx,
|
|
457
|
+
chatId: Id<"chats"> | undefined,
|
|
458
|
+
userId: string | undefined
|
|
459
|
+
) {
|
|
460
|
+
assert(chatId || userId, "One of chatId or userId is required");
|
|
461
|
+
if (chatId) {
|
|
462
|
+
return mergedStream(
|
|
463
|
+
["success" as const, "pending" as const].map((status) =>
|
|
464
|
+
stream(ctx.db, schema)
|
|
465
|
+
.query("messages")
|
|
466
|
+
.withIndex("chatId_status_tool_order_stepOrder", (q) =>
|
|
467
|
+
q.eq("chatId", chatId).eq("status", status).eq("tool", false)
|
|
468
|
+
)
|
|
469
|
+
.order("desc")
|
|
470
|
+
),
|
|
471
|
+
["order", "stepOrder"]
|
|
472
|
+
).first();
|
|
473
|
+
} else {
|
|
474
|
+
return mergedStream(
|
|
475
|
+
["success" as const, "pending" as const].map((status) =>
|
|
476
|
+
stream(ctx.db, schema)
|
|
477
|
+
.query("messages")
|
|
478
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) =>
|
|
479
|
+
q.eq("userId", userId).eq("status", status).eq("tool", false)
|
|
480
|
+
)
|
|
481
|
+
.order("desc")
|
|
482
|
+
),
|
|
483
|
+
["order", "stepOrder"]
|
|
484
|
+
).first();
|
|
485
|
+
}
|
|
464
486
|
}
|
|
465
487
|
|
|
466
488
|
const addStepsArgs = {
|
|
@@ -518,7 +540,8 @@ async function addStepsHandler(
|
|
|
518
540
|
pending: step.finishReason === "stop" ? false : true,
|
|
519
541
|
failPendingSteps: false,
|
|
520
542
|
});
|
|
521
|
-
if
|
|
543
|
+
// We don't commit if the parent is still pending.
|
|
544
|
+
if (step.finishReason === "stop" && parentMessage.status === "success") {
|
|
522
545
|
await commitMessageHandler(ctx, { messageId: args.messageId });
|
|
523
546
|
}
|
|
524
547
|
steps.push((await ctx.db.get(stepId))!);
|
|
@@ -585,6 +608,7 @@ async function commitMessageHandler(
|
|
|
585
608
|
).collect();
|
|
586
609
|
for (const message of messages) {
|
|
587
610
|
await ctx.db.patch(message._id, { status: "success" });
|
|
611
|
+
// TODO: recursively commit steps & messages that might depend on this one.
|
|
588
612
|
}
|
|
589
613
|
}
|
|
590
614
|
|
|
@@ -727,18 +751,19 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
727
751
|
handler: async (ctx, args): Promise<Doc<"messages">[]> => {
|
|
728
752
|
const parent =
|
|
729
753
|
args.parentMessageId && (await ctx.db.get(args.parentMessageId));
|
|
754
|
+
const { userId, chatId } = args;
|
|
755
|
+
assert(userId || chatId, "Specify userId or chatId to search");
|
|
730
756
|
let messages = (
|
|
731
757
|
await Promise.all(
|
|
732
758
|
args.vectorIds.map((embeddingId) =>
|
|
733
759
|
ctx.db
|
|
734
760
|
.query("messages")
|
|
735
761
|
.withIndex("embeddingId", (q) => q.eq("embeddingId", embeddingId))
|
|
736
|
-
.filter(
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
q.eq("chatId", args.chatId as any) // not sure why it's failing...
|
|
762
|
+
.filter((q) =>
|
|
763
|
+
userId
|
|
764
|
+
? q.eq("userId", userId)
|
|
765
|
+
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
766
|
+
q.eq("chatId", chatId as any)
|
|
742
767
|
)
|
|
743
768
|
.first()
|
|
744
769
|
)
|
|
@@ -752,48 +777,71 @@ export const _fetchVectorMessages = internalQuery({
|
|
|
752
777
|
messages.sort((a, b) => a.order! - b.order!);
|
|
753
778
|
messages = messages.slice(0, args.limit);
|
|
754
779
|
// Fetch the surrounding messages
|
|
755
|
-
|
|
780
|
+
if (!chatId) {
|
|
781
|
+
return messages.sort((a, b) => a.order - b.order);
|
|
782
|
+
}
|
|
783
|
+
const included: Record<string, Set<number>> = {};
|
|
756
784
|
for (const m of messages) {
|
|
757
|
-
|
|
758
|
-
|
|
785
|
+
const searchId = m.chatId ?? m.userId!;
|
|
786
|
+
if (!included[searchId]) {
|
|
787
|
+
included[searchId] = new Set();
|
|
759
788
|
}
|
|
760
|
-
included[
|
|
789
|
+
included[searchId].add(m.order!);
|
|
761
790
|
}
|
|
762
|
-
const ranges: Record<
|
|
791
|
+
const ranges: Record<string, Doc<"messages">[]> = {};
|
|
763
792
|
const { before, after } = args.messageRange;
|
|
764
793
|
for (const m of messages) {
|
|
794
|
+
const searchId = m.chatId ?? m.userId!;
|
|
765
795
|
const order = m.order!;
|
|
766
796
|
let earliest = order - before;
|
|
767
797
|
let latest = order + after;
|
|
768
798
|
for (; earliest <= latest; earliest++) {
|
|
769
|
-
if (!included[
|
|
799
|
+
if (!included[searchId].has(earliest)) {
|
|
770
800
|
break;
|
|
771
801
|
}
|
|
772
802
|
}
|
|
773
803
|
for (; latest >= earliest; latest--) {
|
|
774
|
-
if (!included[
|
|
804
|
+
if (!included[searchId].has(latest)) {
|
|
775
805
|
break;
|
|
776
806
|
}
|
|
777
807
|
}
|
|
778
808
|
for (let i = earliest; i <= latest; i++) {
|
|
779
|
-
included[
|
|
809
|
+
included[searchId].add(i);
|
|
780
810
|
}
|
|
781
811
|
if (earliest !== latest) {
|
|
782
|
-
|
|
783
|
-
.
|
|
784
|
-
|
|
785
|
-
q
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
ranges[
|
|
812
|
+
if (m.chatId) {
|
|
813
|
+
const surrounding = await ctx.db
|
|
814
|
+
.query("messages")
|
|
815
|
+
.withIndex("chatId_status_tool_order_stepOrder", (q) =>
|
|
816
|
+
q
|
|
817
|
+
.eq("chatId", m.chatId)
|
|
818
|
+
.eq("status", "success")
|
|
819
|
+
.eq("tool", false)
|
|
820
|
+
.gt("order", earliest)
|
|
821
|
+
.lt("order", latest)
|
|
822
|
+
)
|
|
823
|
+
.collect();
|
|
824
|
+
if (!ranges[searchId]) {
|
|
825
|
+
ranges[searchId] = [];
|
|
826
|
+
}
|
|
827
|
+
ranges[searchId].push(...surrounding);
|
|
828
|
+
} else {
|
|
829
|
+
const surrounding = await ctx.db
|
|
830
|
+
.query("messages")
|
|
831
|
+
.withIndex("userId_status_tool_order_stepOrder", (q) =>
|
|
832
|
+
q
|
|
833
|
+
.eq("userId", m.userId!)
|
|
834
|
+
.eq("status", "success")
|
|
835
|
+
.eq("tool", false)
|
|
836
|
+
.gt("order", earliest)
|
|
837
|
+
.lt("order", latest)
|
|
838
|
+
)
|
|
839
|
+
.collect();
|
|
840
|
+
if (!ranges[searchId]) {
|
|
841
|
+
ranges[searchId] = [];
|
|
842
|
+
}
|
|
843
|
+
ranges[searchId].push(...surrounding);
|
|
795
844
|
}
|
|
796
|
-
ranges[m.chatId].push(...surrounding);
|
|
797
845
|
}
|
|
798
846
|
}
|
|
799
847
|
return Object.values(ranges)
|
package/src/component/schema.ts
CHANGED
|
@@ -22,7 +22,7 @@ export const schema = defineSchema({
|
|
|
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"),
|
|
25
|
+
chatId: v.optional(v.id("chats")),
|
|
26
26
|
threadId: v.optional(v.id("messages")),
|
|
27
27
|
stepId: v.optional(v.id("steps")),
|
|
28
28
|
agentName: v.optional(v.string()),
|
|
@@ -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
|
-
//
|
|
36
|
+
// Unset if it's not in a chat.
|
|
37
37
|
order: v.number(),
|
|
38
38
|
stepOrder: v.optional(v.number()),
|
|
39
39
|
fileId: v.optional(v.id("files")),
|
|
@@ -48,6 +48,13 @@ export const schema = defineSchema({
|
|
|
48
48
|
"order",
|
|
49
49
|
"stepOrder",
|
|
50
50
|
])
|
|
51
|
+
.index("userId_status_tool_order_stepOrder", [
|
|
52
|
+
"userId",
|
|
53
|
+
"status",
|
|
54
|
+
"tool",
|
|
55
|
+
"order",
|
|
56
|
+
"stepOrder",
|
|
57
|
+
])
|
|
51
58
|
// Allows finding all threaded messages in order
|
|
52
59
|
// Allows finding all failed messages to evaluate
|
|
53
60
|
// .index("status_threadId_order_stepOrder", [
|
package/src/mapping.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
+
}
|
package/src/validators.test.ts
CHANGED
|
@@ -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
|
@@ -256,3 +256,35 @@ export const vSearchOptions = v.object({
|
|
|
256
256
|
messageRange: v.optional(v.object({ before: v.number(), after: v.number() })),
|
|
257
257
|
});
|
|
258
258
|
export type SearchOptions = Infer<typeof vSearchOptions>;
|
|
259
|
+
|
|
260
|
+
export const vContextOptionsSearchOptions = v.object({
|
|
261
|
+
limit: v.number(),
|
|
262
|
+
textSearch: v.optional(v.boolean()),
|
|
263
|
+
vectorSearch: v.optional(v.boolean()),
|
|
264
|
+
messageRange: v.optional(v.object({ before: v.number(), after: v.number() })),
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
export const vContextOptions = v.object({
|
|
268
|
+
includeToolCalls: v.optional(v.boolean()),
|
|
269
|
+
recentMessages: v.optional(v.number()),
|
|
270
|
+
searchOptions: v.optional(vContextOptionsSearchOptions),
|
|
271
|
+
searchOtherChats: v.optional(v.boolean()),
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
export const vStorageOptions = v.object({
|
|
275
|
+
saveAllInputMessages: v.optional(v.boolean()),
|
|
276
|
+
saveAllOutputMessages: v.optional(v.boolean()),
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
export const vChatArgs = v.object({
|
|
280
|
+
maxSteps: v.optional(v.number()),
|
|
281
|
+
prompt: v.optional(v.string()),
|
|
282
|
+
messages: v.optional(v.array(vMessage)),
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
export const vObjectArgs = v.object({
|
|
286
|
+
output: v.optional(v.any()),
|
|
287
|
+
mode: v.optional(v.literal("json")),
|
|
288
|
+
prompt: v.optional(v.string()),
|
|
289
|
+
messages: v.optional(v.array(vMessage)),
|
|
290
|
+
});
|