@convex-dev/agent 0.5.0-alpha.1 → 0.6.0-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/MIGRATION.md +153 -0
- package/README.md +32 -27
- package/dist/UIMessages.d.ts +46 -0
- package/dist/UIMessages.d.ts.map +1 -0
- package/dist/UIMessages.js +546 -0
- package/dist/UIMessages.js.map +1 -0
- package/dist/client/createTool.d.ts +126 -27
- package/dist/client/createTool.d.ts.map +1 -1
- package/dist/client/createTool.js +67 -12
- package/dist/client/createTool.js.map +1 -1
- package/dist/client/defaultComponent.d.ts +11 -0
- package/dist/client/defaultComponent.d.ts.map +1 -0
- package/dist/client/defaultComponent.js +7 -0
- package/dist/client/defaultComponent.js.map +1 -0
- package/dist/client/definePlaygroundAPI.d.ts +1335 -204
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
- package/dist/client/definePlaygroundAPI.js +52 -28
- package/dist/client/definePlaygroundAPI.js.map +1 -1
- package/dist/client/files.d.ts +20 -7
- package/dist/client/files.d.ts.map +1 -1
- package/dist/client/files.js +68 -11
- package/dist/client/files.js.map +1 -1
- package/dist/client/index.d.ts +1116 -978
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +332 -747
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +461 -0
- package/dist/client/messages.d.ts.map +1 -0
- package/dist/client/messages.js +106 -0
- package/dist/client/messages.js.map +1 -0
- package/dist/client/mockModel.d.ts +42 -0
- package/dist/client/mockModel.d.ts.map +1 -0
- package/dist/client/mockModel.js +182 -0
- package/dist/client/mockModel.js.map +1 -0
- package/dist/client/saveInputMessages.d.ts +20 -0
- package/dist/client/saveInputMessages.d.ts.map +1 -0
- package/dist/client/saveInputMessages.js +58 -0
- package/dist/client/saveInputMessages.js.map +1 -0
- package/dist/client/search.d.ts +350 -39
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +350 -39
- package/dist/client/search.js.map +1 -1
- package/dist/client/start.d.ts +84 -0
- package/dist/client/start.d.ts.map +1 -0
- package/dist/client/start.js +185 -0
- package/dist/client/start.js.map +1 -0
- package/dist/client/streamText.d.ts +46 -0
- package/dist/client/streamText.d.ts.map +1 -0
- package/dist/client/streamText.js +117 -0
- package/dist/client/streamText.js.map +1 -0
- package/dist/client/streaming.d.ts +3716 -32
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +161 -59
- package/dist/client/streaming.js.map +1 -1
- package/dist/client/threads.d.ts +46 -0
- package/dist/client/threads.d.ts.map +1 -0
- package/dist/client/threads.js +49 -0
- package/dist/client/threads.js.map +1 -0
- package/dist/client/types.d.ts +266 -128
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/utils.d.ts +4 -0
- package/dist/client/utils.d.ts.map +1 -0
- package/dist/client/utils.js +21 -0
- package/dist/client/utils.js.map +1 -0
- package/dist/component/_generated/api.d.ts +24 -2178
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js +10 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +3120 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +4 -18
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +10 -38
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js +9 -5
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/files.d.ts +16 -10
- package/dist/component/files.d.ts.map +1 -1
- package/dist/component/files.js +10 -2
- package/dist/component/files.js.map +1 -1
- package/dist/component/messages.d.ts +2578 -366
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +397 -154
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +5697 -3584
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +18 -41
- package/dist/component/schema.js.map +1 -1
- package/dist/component/streams.d.ts +39 -339
- package/dist/component/streams.d.ts.map +1 -1
- package/dist/component/streams.js +114 -73
- package/dist/component/streams.js.map +1 -1
- package/dist/component/threads.d.ts +13 -13
- package/dist/component/users.d.ts +7 -7
- package/dist/component/vector/index.d.ts +1 -1
- package/dist/component/vector/index.d.ts.map +1 -1
- package/dist/component/vector/index.js +1 -3
- package/dist/component/vector/index.js.map +1 -1
- package/dist/deltas.d.ts +43 -0
- package/dist/deltas.d.ts.map +1 -0
- package/dist/deltas.js +446 -0
- package/dist/deltas.js.map +1 -0
- package/dist/mapping.d.ts +38 -20
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +365 -97
- package/dist/mapping.js.map +1 -1
- package/dist/react/SmoothText.d.ts +5 -0
- package/dist/react/SmoothText.d.ts.map +1 -0
- package/dist/react/SmoothText.js +6 -0
- package/dist/react/SmoothText.js.map +1 -0
- package/dist/react/index.d.ts +5 -77
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +6 -160
- package/dist/react/index.js.map +1 -1
- package/dist/react/optimisticallySendMessage.d.ts +36 -3
- package/dist/react/optimisticallySendMessage.d.ts.map +1 -1
- package/dist/react/optimisticallySendMessage.js +35 -9
- package/dist/react/optimisticallySendMessage.js.map +1 -1
- package/dist/react/types.d.ts +4 -18
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/useDeltaStreams.d.ts +10 -0
- package/dist/react/useDeltaStreams.d.ts.map +1 -0
- package/dist/react/useDeltaStreams.js +106 -0
- package/dist/react/useDeltaStreams.js.map +1 -0
- package/dist/react/useSmoothText.d.ts +13 -12
- package/dist/react/useSmoothText.d.ts.map +1 -1
- package/dist/react/useSmoothText.js +32 -15
- package/dist/react/useSmoothText.js.map +1 -1
- package/dist/react/useStreamingUIMessages.d.ts +22 -0
- package/dist/react/useStreamingUIMessages.d.ts.map +1 -0
- package/dist/react/useStreamingUIMessages.js +92 -0
- package/dist/react/useStreamingUIMessages.js.map +1 -0
- package/dist/react/useThreadMessages.d.ts +104 -0
- package/dist/react/useThreadMessages.d.ts.map +1 -0
- package/dist/react/useThreadMessages.js +148 -0
- package/dist/react/useThreadMessages.js.map +1 -0
- package/dist/react/useUIMessages.d.ts +96 -0
- package/dist/react/useUIMessages.d.ts.map +1 -0
- package/dist/react/useUIMessages.js +108 -0
- package/dist/react/useUIMessages.js.map +1 -0
- package/dist/shared.d.ts +20 -4
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +45 -8
- package/dist/shared.js.map +1 -1
- package/dist/validators.d.ts +22981 -5666
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +245 -137
- package/dist/validators.js.map +1 -1
- package/package.json +101 -51
- package/src/UIMessages.combineUIMessages.test.ts +239 -0
- package/src/UIMessages.test.ts +273 -0
- package/src/UIMessages.ts +739 -0
- package/src/client/approval.test.ts +350 -0
- package/src/client/createTool.ts +291 -76
- package/src/client/defaultComponent.ts +17 -0
- package/src/client/definePlaygroundAPI.ts +67 -31
- package/src/client/files.ts +100 -20
- package/src/client/index.test.ts +40 -85
- package/src/client/index.ts +638 -1289
- package/src/client/messages.ts +237 -0
- package/src/client/mockModel.ts +252 -0
- package/src/client/saveInputMessages.test.ts +583 -0
- package/src/client/saveInputMessages.ts +101 -0
- package/src/client/search.test.ts +1207 -0
- package/src/client/search.ts +581 -70
- package/src/client/start.ts +327 -0
- package/src/client/streamText.ts +187 -0
- package/src/client/streaming.test.ts +186 -0
- package/src/client/streaming.ts +241 -97
- package/src/client/threads.ts +83 -0
- package/src/client/types.ts +370 -219
- package/src/client/utils.ts +27 -0
- package/src/component/_generated/api.ts +64 -0
- package/src/component/_generated/component.ts +4902 -0
- package/src/component/_generated/{server.d.ts → server.ts} +33 -21
- package/src/component/files.ts +11 -2
- package/src/component/messages.test.ts +195 -51
- package/src/component/messages.ts +500 -201
- package/src/component/schema.ts +20 -46
- package/src/component/setup.test.ts +7 -0
- package/src/component/streams.ts +184 -83
- package/src/component/users.test.ts +0 -1
- package/src/component/vector/index.ts +1 -3
- package/src/deltas.test.ts +626 -0
- package/src/deltas.ts +569 -0
- package/src/fromUIMessages.test.ts +497 -0
- package/src/mapping.test.ts +180 -6
- package/src/mapping.ts +479 -162
- package/src/react/SmoothText.tsx +9 -0
- package/src/react/index.ts +10 -230
- package/src/react/optimisticallySendMessage.ts +55 -12
- package/src/react/types.ts +6 -39
- package/src/react/useDeltaStreams.ts +160 -0
- package/src/react/useSmoothText.ts +56 -36
- package/src/react/useStreamingUIMessages.ts +143 -0
- package/src/react/useThreadMessages.ts +262 -0
- package/src/react/useUIMessages.test.ts +255 -0
- package/src/react/useUIMessages.ts +195 -0
- package/src/shared.ts +88 -12
- package/src/test.ts +18 -0
- package/src/toUIMessages.test.ts +1269 -0
- package/src/validators.test.ts +18 -19
- package/src/validators.ts +325 -185
- package/dist/client/_generated/_ignore.d.ts +0 -1
- package/dist/client/_generated/_ignore.d.ts.map +0 -1
- package/dist/client/_generated/_ignore.js +0 -3
- package/dist/client/_generated/_ignore.js.map +0 -1
- package/dist/client/listMessages.d.ts +0 -22
- package/dist/client/listMessages.d.ts.map +0 -1
- package/dist/client/listMessages.js +0 -25
- package/dist/client/listMessages.js.map +0 -1
- package/dist/package.json +0 -3
- package/dist/react/deltas.d.ts +0 -26
- package/dist/react/deltas.d.ts.map +0 -1
- package/dist/react/deltas.js +0 -384
- package/dist/react/deltas.js.map +0 -1
- package/dist/react/toUIMessages.d.ts +0 -15
- package/dist/react/toUIMessages.d.ts.map +0 -1
- package/dist/react/toUIMessages.js +0 -211
- package/dist/react/toUIMessages.js.map +0 -1
- package/src/client/listMessages.ts +0 -38
- package/src/component/_generated/api.d.ts +0 -2202
- package/src/component/_generated/api.js +0 -23
- package/src/component/_generated/server.js +0 -90
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/src/react/deltas.test.ts +0 -315
- package/src/react/deltas.ts +0 -478
- package/src/react/toUIMessages.test.ts +0 -420
- package/src/react/toUIMessages.ts +0 -253
- package/src/vitest.config.ts +0 -7
- /package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +0 -0
package/src/client/search.ts
CHANGED
|
@@ -1,25 +1,59 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from "./types.js";
|
|
7
|
-
import type { MessageDoc } from "../component/schema.js";
|
|
8
|
-
import type { ModelMessage } from "ai";
|
|
1
|
+
import {
|
|
2
|
+
embedMany as embedMany_,
|
|
3
|
+
type EmbeddingModel,
|
|
4
|
+
type ModelMessage,
|
|
5
|
+
} from "ai";
|
|
9
6
|
import { assert } from "convex-helpers";
|
|
7
|
+
import type { MessageDoc } from "../validators.js";
|
|
8
|
+
import {
|
|
9
|
+
validateVectorDimension,
|
|
10
|
+
type VectorDimension,
|
|
11
|
+
} from "../component/vector/tables.js";
|
|
10
12
|
import {
|
|
11
13
|
DEFAULT_MESSAGE_RANGE,
|
|
12
14
|
DEFAULT_RECENT_MESSAGES,
|
|
13
15
|
extractText,
|
|
16
|
+
getModelName,
|
|
17
|
+
getProviderName,
|
|
18
|
+
isTool,
|
|
19
|
+
sorted,
|
|
14
20
|
} from "../shared.js";
|
|
15
21
|
import type { Message } from "../validators.js";
|
|
22
|
+
import type {
|
|
23
|
+
AgentComponent,
|
|
24
|
+
Config,
|
|
25
|
+
ContextOptions,
|
|
26
|
+
Options,
|
|
27
|
+
QueryCtx,
|
|
28
|
+
MutationCtx,
|
|
29
|
+
ActionCtx,
|
|
30
|
+
} from "./types.js";
|
|
31
|
+
import { inlineMessagesFiles } from "./files.js";
|
|
32
|
+
import {
|
|
33
|
+
docsToModelMessages,
|
|
34
|
+
mergeApprovalResponseMessages,
|
|
35
|
+
toModelMessage,
|
|
36
|
+
} from "../mapping.js";
|
|
16
37
|
|
|
17
38
|
const DEFAULT_VECTOR_SCORE_THRESHOLD = 0.0;
|
|
39
|
+
// 10k characters should be more than enough for most cases, and stays under
|
|
40
|
+
// the 8k token limit for some models.
|
|
41
|
+
const MAX_EMBEDDING_TEXT_LENGTH = 10_000;
|
|
18
42
|
|
|
19
|
-
export type GetEmbedding = (text: string) => Promise<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
43
|
+
export type GetEmbedding = (text: string) => Promise<
|
|
44
|
+
| {
|
|
45
|
+
embedding: number[];
|
|
46
|
+
/** @deprecated Use embeddingModel instead. */
|
|
47
|
+
textEmbeddingModel: string | EmbeddingModel;
|
|
48
|
+
embeddingModel?: string | EmbeddingModel;
|
|
49
|
+
}
|
|
50
|
+
| {
|
|
51
|
+
embedding: number[];
|
|
52
|
+
/** @deprecated Use embeddingModel instead. */
|
|
53
|
+
textEmbeddingModel?: string | EmbeddingModel;
|
|
54
|
+
embeddingModel: string | EmbeddingModel;
|
|
55
|
+
}
|
|
56
|
+
>;
|
|
23
57
|
|
|
24
58
|
/**
|
|
25
59
|
* Fetch the context messages for a thread.
|
|
@@ -30,31 +64,78 @@ export type GetEmbedding = (text: string) => Promise<{
|
|
|
30
64
|
* @returns
|
|
31
65
|
*/
|
|
32
66
|
export async function fetchContextMessages(
|
|
33
|
-
ctx:
|
|
67
|
+
ctx: QueryCtx | MutationCtx | ActionCtx,
|
|
34
68
|
component: AgentComponent,
|
|
35
69
|
args: {
|
|
36
70
|
userId: string | undefined;
|
|
37
71
|
threadId: string | undefined;
|
|
38
|
-
messages: (ModelMessage | Message)[];
|
|
39
72
|
/**
|
|
40
|
-
* If
|
|
41
|
-
*
|
|
42
|
-
|
|
73
|
+
* If targetMessageId is not provided, this text will be used
|
|
74
|
+
* for text and vector search
|
|
75
|
+
*/
|
|
76
|
+
searchText?: string;
|
|
77
|
+
/**
|
|
78
|
+
* If provided, it will use this message for text/vector search (if enabled)
|
|
79
|
+
* and will only fetch messages up to (and including) this message's "order"
|
|
80
|
+
*/
|
|
81
|
+
targetMessageId?: string;
|
|
82
|
+
/**
|
|
83
|
+
* @deprecated use searchText and targetMessageId instead
|
|
84
|
+
*/
|
|
85
|
+
messages?: (ModelMessage | Message)[];
|
|
86
|
+
/**
|
|
87
|
+
* @deprecated use targetMessageId instead
|
|
43
88
|
*/
|
|
44
89
|
upToAndIncludingMessageId?: string;
|
|
45
90
|
contextOptions: ContextOptions;
|
|
46
91
|
getEmbedding?: GetEmbedding;
|
|
47
92
|
},
|
|
48
93
|
): Promise<MessageDoc[]> {
|
|
94
|
+
const { recentMessages, searchMessages } = await fetchRecentAndSearchMessages(
|
|
95
|
+
ctx,
|
|
96
|
+
component,
|
|
97
|
+
args,
|
|
98
|
+
);
|
|
99
|
+
return [...searchMessages, ...recentMessages];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function fetchRecentAndSearchMessages(
|
|
103
|
+
ctx: QueryCtx | MutationCtx | ActionCtx,
|
|
104
|
+
component: AgentComponent,
|
|
105
|
+
args: {
|
|
106
|
+
userId: string | undefined;
|
|
107
|
+
threadId: string | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* If targetMessageId is not provided, this text will be used
|
|
110
|
+
* for text and vector search
|
|
111
|
+
*/
|
|
112
|
+
searchText?: string;
|
|
113
|
+
/**
|
|
114
|
+
* If provided, it will use this message for text/vector search (if enabled)
|
|
115
|
+
* and will only fetch messages up to (and including) this message's "order"
|
|
116
|
+
*/
|
|
117
|
+
targetMessageId?: string;
|
|
118
|
+
/**
|
|
119
|
+
* @deprecated use searchText and targetMessageId instead
|
|
120
|
+
*/
|
|
121
|
+
messages?: (ModelMessage | Message)[];
|
|
122
|
+
/**
|
|
123
|
+
* @deprecated use targetMessageId instead
|
|
124
|
+
*/
|
|
125
|
+
upToAndIncludingMessageId?: string;
|
|
126
|
+
contextOptions: ContextOptions;
|
|
127
|
+
getEmbedding?: GetEmbedding;
|
|
128
|
+
},
|
|
129
|
+
): Promise<{ recentMessages: MessageDoc[]; searchMessages: MessageDoc[] }> {
|
|
49
130
|
assert(args.userId || args.threadId, "Specify userId or threadId");
|
|
50
131
|
const opts = args.contextOptions;
|
|
51
132
|
// Fetch the latest messages from the thread
|
|
52
133
|
let included: Set<string> | undefined;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
) {
|
|
134
|
+
let recentMessages: MessageDoc[] = [];
|
|
135
|
+
let searchMessages: MessageDoc[] = [];
|
|
136
|
+
const targetMessageId =
|
|
137
|
+
args.targetMessageId ?? args.upToAndIncludingMessageId;
|
|
138
|
+
if (args.threadId && opts.recentMessages !== 0) {
|
|
58
139
|
const { page } = await ctx.runQuery(
|
|
59
140
|
component.messages.listMessagesByThreadId,
|
|
60
141
|
{
|
|
@@ -64,106 +145,187 @@ export async function fetchContextMessages(
|
|
|
64
145
|
numItems: opts.recentMessages ?? DEFAULT_RECENT_MESSAGES,
|
|
65
146
|
cursor: null,
|
|
66
147
|
},
|
|
67
|
-
upToAndIncludingMessageId:
|
|
148
|
+
upToAndIncludingMessageId: targetMessageId,
|
|
68
149
|
order: "desc",
|
|
69
150
|
statuses: ["success"],
|
|
70
151
|
},
|
|
71
152
|
);
|
|
72
153
|
included = new Set(page.map((m) => m._id));
|
|
73
|
-
|
|
74
|
-
// Reverse since we fetched in descending order
|
|
75
|
-
...page.reverse(),
|
|
76
|
-
);
|
|
154
|
+
recentMessages = filterOutOrphanedToolMessages(sorted(page));
|
|
77
155
|
}
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const messagesToSearch = targetMessage ? [targetMessage] : args.messages;
|
|
156
|
+
if (
|
|
157
|
+
(opts.searchOptions?.textSearch || opts.searchOptions?.vectorSearch) &&
|
|
158
|
+
opts.searchOptions?.limit
|
|
159
|
+
) {
|
|
83
160
|
if (!("runAction" in ctx)) {
|
|
84
161
|
throw new Error("searchUserMessages only works in an action");
|
|
85
162
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
163
|
+
let text = args.searchText;
|
|
164
|
+
let embedding: number[] | undefined;
|
|
165
|
+
let embeddingModel: string | undefined;
|
|
166
|
+
if (!text) {
|
|
167
|
+
if (targetMessageId) {
|
|
168
|
+
const targetMessage = recentMessages.find(
|
|
169
|
+
(m) => m._id === targetMessageId,
|
|
170
|
+
);
|
|
171
|
+
if (targetMessage) {
|
|
172
|
+
text = targetMessage.text;
|
|
173
|
+
} else {
|
|
174
|
+
const targetSearchFields = await ctx.runQuery(
|
|
175
|
+
component.messages.getMessageSearchFields,
|
|
176
|
+
{
|
|
177
|
+
messageId: targetMessageId,
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
text = targetSearchFields.text;
|
|
181
|
+
embedding = targetSearchFields.embedding;
|
|
182
|
+
embeddingModel = targetSearchFields.embeddingModel;
|
|
183
|
+
}
|
|
184
|
+
assert(text, "Target message has no text for searching");
|
|
185
|
+
} else if (args.messages?.length) {
|
|
186
|
+
text = extractText(args.messages.at(-1)!);
|
|
187
|
+
assert(text, "Final context message has no text to search");
|
|
188
|
+
}
|
|
189
|
+
assert(text, "No text to search");
|
|
190
|
+
}
|
|
191
|
+
if (opts.searchOptions?.vectorSearch) {
|
|
192
|
+
if (!embedding && args.getEmbedding) {
|
|
193
|
+
const embeddingFields = await args.getEmbedding(text);
|
|
194
|
+
embedding = embeddingFields.embedding;
|
|
195
|
+
const effectiveModel =
|
|
196
|
+
embeddingFields.embeddingModel ?? embeddingFields.textEmbeddingModel;
|
|
197
|
+
embeddingModel = effectiveModel
|
|
198
|
+
? getModelName(effectiveModel)
|
|
199
|
+
: undefined;
|
|
200
|
+
// TODO: if the text matches the target message, save the embedding
|
|
201
|
+
// for the target message and return the embeddingId on the message.
|
|
202
|
+
}
|
|
98
203
|
}
|
|
99
|
-
const
|
|
100
|
-
? await args.getEmbedding?.(text)
|
|
101
|
-
: undefined;
|
|
102
|
-
const searchMessages = await ctx.runAction(
|
|
204
|
+
const searchResults = await ctx.runAction(
|
|
103
205
|
component.messages.searchMessages,
|
|
104
206
|
{
|
|
105
207
|
searchAllMessagesForUserId: opts?.searchOtherThreads
|
|
106
|
-
? args.userId ??
|
|
208
|
+
? (args.userId ??
|
|
107
209
|
(args.threadId &&
|
|
108
210
|
(
|
|
109
211
|
await ctx.runQuery(component.threads.getThread, {
|
|
110
212
|
threadId: args.threadId,
|
|
111
213
|
})
|
|
112
|
-
)?.userId)
|
|
214
|
+
)?.userId))
|
|
113
215
|
: undefined,
|
|
114
216
|
threadId: args.threadId,
|
|
115
|
-
|
|
217
|
+
targetMessageId,
|
|
116
218
|
limit: opts.searchOptions?.limit ?? 10,
|
|
117
219
|
messageRange: {
|
|
118
220
|
...DEFAULT_MESSAGE_RANGE,
|
|
119
221
|
...opts.searchOptions?.messageRange,
|
|
120
222
|
},
|
|
121
223
|
text,
|
|
224
|
+
textSearch: opts.searchOptions?.textSearch,
|
|
225
|
+
vectorSearch: opts.searchOptions?.vectorSearch,
|
|
122
226
|
vectorScoreThreshold:
|
|
123
227
|
opts.searchOptions?.vectorScoreThreshold ??
|
|
124
228
|
DEFAULT_VECTOR_SCORE_THRESHOLD,
|
|
125
|
-
embedding
|
|
126
|
-
embeddingModel
|
|
229
|
+
embedding,
|
|
230
|
+
embeddingModel,
|
|
127
231
|
},
|
|
128
232
|
);
|
|
129
233
|
// TODO: track what messages we used for context
|
|
130
|
-
|
|
131
|
-
|
|
234
|
+
searchMessages = filterOutOrphanedToolMessages(
|
|
235
|
+
sorted(searchResults.filter((m) => !included?.has(m._id))),
|
|
132
236
|
);
|
|
133
237
|
}
|
|
134
238
|
// Ensure we don't include tool messages without a corresponding tool call
|
|
135
|
-
return
|
|
136
|
-
contextMessages.sort((a, b) =>
|
|
137
|
-
// Sort the raw MessageDocs by order and stepOrder
|
|
138
|
-
a.order === b.order ? a.stepOrder - b.stepOrder : a.order - b.order,
|
|
139
|
-
),
|
|
140
|
-
);
|
|
239
|
+
return { recentMessages, searchMessages };
|
|
141
240
|
}
|
|
142
241
|
|
|
143
242
|
/**
|
|
144
243
|
* Filter out tool messages that don't have both a tool call and response.
|
|
244
|
+
* For the approval workflow, tool calls with approval responses (but no tool-results yet)
|
|
245
|
+
* should also be kept.
|
|
145
246
|
* @param docs The messages to filter.
|
|
146
247
|
* @returns The filtered messages.
|
|
147
248
|
*/
|
|
148
249
|
export function filterOutOrphanedToolMessages(docs: MessageDoc[]) {
|
|
149
250
|
const toolCallIds = new Set<string>();
|
|
251
|
+
const toolResultIds = new Set<string>();
|
|
252
|
+
// Track approval workflow: toolCallId → approvalId
|
|
253
|
+
const approvalRequestsByToolCallId = new Map<string, string>();
|
|
254
|
+
// Track which approvalIds have responses
|
|
255
|
+
const approvalResponseIds = new Set<string>();
|
|
256
|
+
|
|
150
257
|
const result: MessageDoc[] = [];
|
|
151
258
|
for (const doc of docs) {
|
|
152
|
-
if (
|
|
153
|
-
doc.message?.role === "assistant" &&
|
|
154
|
-
Array.isArray(doc.message.content)
|
|
155
|
-
) {
|
|
259
|
+
if (doc.message && Array.isArray(doc.message.content)) {
|
|
156
260
|
for (const content of doc.message.content) {
|
|
157
261
|
if (content.type === "tool-call") {
|
|
158
262
|
toolCallIds.add(content.toolCallId);
|
|
263
|
+
} else if (content.type === "tool-result") {
|
|
264
|
+
toolResultIds.add(content.toolCallId);
|
|
265
|
+
} else if (content.type === "tool-approval-request") {
|
|
266
|
+
const approvalRequest = content as {
|
|
267
|
+
type: "tool-approval-request";
|
|
268
|
+
toolCallId: string;
|
|
269
|
+
approvalId: string;
|
|
270
|
+
};
|
|
271
|
+
approvalRequestsByToolCallId.set(
|
|
272
|
+
approvalRequest.toolCallId,
|
|
273
|
+
approvalRequest.approvalId,
|
|
274
|
+
);
|
|
275
|
+
} else if (content.type === "tool-approval-response") {
|
|
276
|
+
const approvalResponse = content as {
|
|
277
|
+
type: "tool-approval-response";
|
|
278
|
+
approvalId: string;
|
|
279
|
+
};
|
|
280
|
+
approvalResponseIds.add(approvalResponse.approvalId);
|
|
159
281
|
}
|
|
160
282
|
}
|
|
161
|
-
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Helper: check if tool call has a corresponding approval response
|
|
287
|
+
const hasApprovalResponse = (toolCallId: string) => {
|
|
288
|
+
const approvalId = approvalRequestsByToolCallId.get(toolCallId);
|
|
289
|
+
return approvalId !== undefined && approvalResponseIds.has(approvalId);
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
for (const doc of docs) {
|
|
293
|
+
if (
|
|
294
|
+
doc.message?.role === "assistant" &&
|
|
295
|
+
Array.isArray(doc.message.content)
|
|
296
|
+
) {
|
|
297
|
+
const content = doc.message.content.filter(
|
|
298
|
+
(p) =>
|
|
299
|
+
p.type !== "tool-call" ||
|
|
300
|
+
toolResultIds.has(p.toolCallId) ||
|
|
301
|
+
hasApprovalResponse(p.toolCallId),
|
|
302
|
+
);
|
|
303
|
+
if (content.length) {
|
|
304
|
+
result.push({
|
|
305
|
+
...doc,
|
|
306
|
+
message: {
|
|
307
|
+
...doc.message,
|
|
308
|
+
content,
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
}
|
|
162
312
|
} else if (doc.message?.role === "tool") {
|
|
163
|
-
|
|
164
|
-
result
|
|
165
|
-
|
|
166
|
-
|
|
313
|
+
const content = doc.message.content.filter((c) => {
|
|
314
|
+
// tool-result parts have toolCallId
|
|
315
|
+
if (c.type === "tool-result") {
|
|
316
|
+
return toolCallIds.has(c.toolCallId);
|
|
317
|
+
}
|
|
318
|
+
// tool-approval-response parts don't have toolCallId, so include them
|
|
319
|
+
return true;
|
|
320
|
+
});
|
|
321
|
+
if (content.length) {
|
|
322
|
+
result.push({
|
|
323
|
+
...doc,
|
|
324
|
+
message: {
|
|
325
|
+
...doc.message,
|
|
326
|
+
content,
|
|
327
|
+
},
|
|
328
|
+
});
|
|
167
329
|
}
|
|
168
330
|
} else {
|
|
169
331
|
result.push(doc);
|
|
@@ -171,3 +333,352 @@ export function filterOutOrphanedToolMessages(docs: MessageDoc[]) {
|
|
|
171
333
|
}
|
|
172
334
|
return result;
|
|
173
335
|
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Embed a list of messages, including calling any usage handler.
|
|
339
|
+
* This will not save the embeddings to the database.
|
|
340
|
+
*/
|
|
341
|
+
export async function embedMessages(
|
|
342
|
+
ctx: ActionCtx,
|
|
343
|
+
{
|
|
344
|
+
userId,
|
|
345
|
+
threadId,
|
|
346
|
+
...options
|
|
347
|
+
}: {
|
|
348
|
+
userId: string | undefined;
|
|
349
|
+
threadId: string | undefined;
|
|
350
|
+
agentName?: string;
|
|
351
|
+
} & Pick<
|
|
352
|
+
Config,
|
|
353
|
+
"usageHandler" | "textEmbeddingModel" | "embeddingModel" | "callSettings"
|
|
354
|
+
>,
|
|
355
|
+
messages: (ModelMessage | Message)[],
|
|
356
|
+
): Promise<
|
|
357
|
+
| {
|
|
358
|
+
vectors: (number[] | null)[];
|
|
359
|
+
dimension: VectorDimension;
|
|
360
|
+
model: string;
|
|
361
|
+
}
|
|
362
|
+
| undefined
|
|
363
|
+
> {
|
|
364
|
+
const textEmbeddingModel =
|
|
365
|
+
options.embeddingModel ?? options.textEmbeddingModel;
|
|
366
|
+
if (!textEmbeddingModel) {
|
|
367
|
+
return undefined;
|
|
368
|
+
}
|
|
369
|
+
let embeddings:
|
|
370
|
+
| {
|
|
371
|
+
vectors: (number[] | null)[];
|
|
372
|
+
dimension: VectorDimension;
|
|
373
|
+
model: string;
|
|
374
|
+
}
|
|
375
|
+
| undefined;
|
|
376
|
+
const messageTexts = messages.map((m) => !isTool(m) && extractText(m));
|
|
377
|
+
// Find the indexes of the messages that have text.
|
|
378
|
+
const textIndexes = messageTexts
|
|
379
|
+
.map((t, i) => (t ? i : undefined))
|
|
380
|
+
.filter((i) => i !== undefined);
|
|
381
|
+
if (textIndexes.length === 0) {
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
384
|
+
const values = messageTexts
|
|
385
|
+
.map((t) => t && t.trim().slice(0, MAX_EMBEDDING_TEXT_LENGTH))
|
|
386
|
+
.filter((t): t is string => !!t);
|
|
387
|
+
// Then embed those messages.
|
|
388
|
+
const textEmbeddings = await embedMany(ctx, {
|
|
389
|
+
...options,
|
|
390
|
+
userId,
|
|
391
|
+
threadId,
|
|
392
|
+
values,
|
|
393
|
+
});
|
|
394
|
+
// Then assemble the embeddings into a single array with nulls for the messages without text.
|
|
395
|
+
const embeddingsOrNull = Array(messages.length).fill(null);
|
|
396
|
+
textIndexes.forEach((i, j) => {
|
|
397
|
+
embeddingsOrNull[i] = textEmbeddings.embeddings[j];
|
|
398
|
+
});
|
|
399
|
+
if (textEmbeddings.embeddings.length > 0) {
|
|
400
|
+
const dimension = textEmbeddings.embeddings[0].length;
|
|
401
|
+
validateVectorDimension(dimension);
|
|
402
|
+
const model = getModelName(textEmbeddingModel);
|
|
403
|
+
embeddings = { vectors: embeddingsOrNull, dimension, model };
|
|
404
|
+
}
|
|
405
|
+
return embeddings;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Embeds many strings, calling any usage handler.
|
|
410
|
+
* @param ctx The ctx parameter to an action.
|
|
411
|
+
* @param args Arguments to AI SDK's embedMany, and context for the embedding,
|
|
412
|
+
* passed to the usage handler.
|
|
413
|
+
* @returns The embeddings for the strings, matching the order of the values.
|
|
414
|
+
*/
|
|
415
|
+
export async function embedMany(
|
|
416
|
+
ctx: ActionCtx,
|
|
417
|
+
args: {
|
|
418
|
+
userId: string | undefined;
|
|
419
|
+
threadId: string | undefined;
|
|
420
|
+
values: string[];
|
|
421
|
+
abortSignal?: AbortSignal;
|
|
422
|
+
headers?: Record<string, string>;
|
|
423
|
+
agentName?: string;
|
|
424
|
+
} & Pick<
|
|
425
|
+
Config,
|
|
426
|
+
"usageHandler" | "textEmbeddingModel" | "embeddingModel" | "callSettings"
|
|
427
|
+
>,
|
|
428
|
+
): Promise<{ embeddings: number[][] }> {
|
|
429
|
+
const {
|
|
430
|
+
userId,
|
|
431
|
+
threadId,
|
|
432
|
+
values,
|
|
433
|
+
abortSignal,
|
|
434
|
+
headers,
|
|
435
|
+
agentName,
|
|
436
|
+
usageHandler,
|
|
437
|
+
textEmbeddingModel,
|
|
438
|
+
embeddingModel,
|
|
439
|
+
callSettings,
|
|
440
|
+
} = args;
|
|
441
|
+
const effectiveEmbeddingModel = embeddingModel ?? textEmbeddingModel;
|
|
442
|
+
assert(
|
|
443
|
+
effectiveEmbeddingModel,
|
|
444
|
+
"an embeddingModel (or textEmbeddingModel) is required to be set for vector search",
|
|
445
|
+
);
|
|
446
|
+
const result = await embedMany_({
|
|
447
|
+
...callSettings,
|
|
448
|
+
model: effectiveEmbeddingModel,
|
|
449
|
+
values,
|
|
450
|
+
abortSignal,
|
|
451
|
+
headers,
|
|
452
|
+
});
|
|
453
|
+
if (usageHandler && result.usage) {
|
|
454
|
+
await usageHandler(ctx, {
|
|
455
|
+
userId,
|
|
456
|
+
threadId,
|
|
457
|
+
agentName,
|
|
458
|
+
model: getModelName(effectiveEmbeddingModel),
|
|
459
|
+
provider: getProviderName(effectiveEmbeddingModel),
|
|
460
|
+
providerMetadata: undefined,
|
|
461
|
+
usage: {
|
|
462
|
+
inputTokens: result.usage.tokens,
|
|
463
|
+
outputTokens: 0,
|
|
464
|
+
totalTokens: result.usage.tokens,
|
|
465
|
+
// These detail fields are required by LanguageModelUsage type but we don't
|
|
466
|
+
// have the granular data, so we provide objects with undefined values.
|
|
467
|
+
inputTokenDetails: {
|
|
468
|
+
cacheReadTokens: undefined,
|
|
469
|
+
cacheWriteTokens: undefined,
|
|
470
|
+
noCacheTokens: undefined,
|
|
471
|
+
},
|
|
472
|
+
outputTokenDetails: {
|
|
473
|
+
textTokens: undefined,
|
|
474
|
+
reasoningTokens: undefined,
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
return { embeddings: result.embeddings };
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Embed a list of messages, and save the embeddings to the database.
|
|
484
|
+
* @param ctx The ctx parameter to an action.
|
|
485
|
+
* @param component The agent component, usually components.agent.
|
|
486
|
+
* @param args The context for the embedding, passed to the usage handler.
|
|
487
|
+
* @param messages The messages to embed, in the Agent MessageDoc format.
|
|
488
|
+
*/
|
|
489
|
+
export async function generateAndSaveEmbeddings(
|
|
490
|
+
ctx: ActionCtx,
|
|
491
|
+
component: AgentComponent,
|
|
492
|
+
args: {
|
|
493
|
+
threadId: string | undefined;
|
|
494
|
+
userId: string | undefined;
|
|
495
|
+
agentName?: string;
|
|
496
|
+
/**
|
|
497
|
+
* @deprecated Use embeddingModel instead.
|
|
498
|
+
*/
|
|
499
|
+
textEmbeddingModel?: EmbeddingModel;
|
|
500
|
+
embeddingModel?: EmbeddingModel;
|
|
501
|
+
} & Pick<Config, "usageHandler" | "callSettings">,
|
|
502
|
+
messages: MessageDoc[],
|
|
503
|
+
) {
|
|
504
|
+
const effectiveEmbeddingModel =
|
|
505
|
+
args.embeddingModel ?? args.textEmbeddingModel;
|
|
506
|
+
if (!effectiveEmbeddingModel) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
"an embeddingModel (or textEmbeddingModel) is required to generate and save embeddings",
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
const toEmbed = messages.filter((m) => !m.embeddingId && m.message);
|
|
512
|
+
if (toEmbed.length === 0) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const embeddings = await embedMessages(
|
|
516
|
+
ctx,
|
|
517
|
+
{ ...args, embeddingModel: effectiveEmbeddingModel },
|
|
518
|
+
toEmbed.map((m) => m.message!),
|
|
519
|
+
);
|
|
520
|
+
if (embeddings && embeddings.vectors.some((v) => v !== null)) {
|
|
521
|
+
await ctx.runMutation(component.vector.index.insertBatch, {
|
|
522
|
+
vectorDimension: embeddings.dimension,
|
|
523
|
+
vectors: toEmbed
|
|
524
|
+
.map((m, i) => ({
|
|
525
|
+
messageId: m._id,
|
|
526
|
+
model: embeddings.model,
|
|
527
|
+
table: "messages",
|
|
528
|
+
userId: m.userId,
|
|
529
|
+
threadId: m.threadId,
|
|
530
|
+
vector: embeddings.vectors[i]!,
|
|
531
|
+
}))
|
|
532
|
+
.filter((v) => v.vector !== null),
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Similar to fetchContextMessages, but also combines the input messages,
|
|
539
|
+
* with search context, recent messages, input messages, then prompt messages.
|
|
540
|
+
* If there is a promptMessageId and prompt message(s) provided, it will splice
|
|
541
|
+
* the prompt messages into the history to replace the promptMessageId message,
|
|
542
|
+
* but still be followed by any existing messages that were in response to the
|
|
543
|
+
* promptMessageId message.
|
|
544
|
+
*/
|
|
545
|
+
export async function fetchContextWithPrompt(
|
|
546
|
+
ctx: ActionCtx,
|
|
547
|
+
component: AgentComponent,
|
|
548
|
+
args: {
|
|
549
|
+
prompt: string | (ModelMessage | Message)[] | undefined;
|
|
550
|
+
messages: (ModelMessage | Message)[] | undefined;
|
|
551
|
+
promptMessageId: string | undefined;
|
|
552
|
+
userId: string | undefined;
|
|
553
|
+
threadId: string | undefined;
|
|
554
|
+
agentName?: string;
|
|
555
|
+
} & Options &
|
|
556
|
+
Config,
|
|
557
|
+
): Promise<{
|
|
558
|
+
messages: ModelMessage[];
|
|
559
|
+
order: number | undefined;
|
|
560
|
+
stepOrder: number | undefined;
|
|
561
|
+
}> {
|
|
562
|
+
const { threadId, userId, textEmbeddingModel, embeddingModel } = args;
|
|
563
|
+
const effectiveEmbeddingModel = embeddingModel ?? textEmbeddingModel;
|
|
564
|
+
|
|
565
|
+
const promptArray = getPromptArray(args.prompt);
|
|
566
|
+
|
|
567
|
+
const searchText = promptArray.length
|
|
568
|
+
? extractText(promptArray.at(-1)!)
|
|
569
|
+
: args.promptMessageId
|
|
570
|
+
? undefined
|
|
571
|
+
: args.messages?.at(-1)
|
|
572
|
+
? extractText(args.messages.at(-1)!)
|
|
573
|
+
: undefined;
|
|
574
|
+
// If only a messageId is provided, this will add that message to the end.
|
|
575
|
+
const { recentMessages, searchMessages } = await fetchRecentAndSearchMessages(
|
|
576
|
+
ctx,
|
|
577
|
+
component,
|
|
578
|
+
{
|
|
579
|
+
userId,
|
|
580
|
+
threadId,
|
|
581
|
+
targetMessageId: args.promptMessageId,
|
|
582
|
+
searchText,
|
|
583
|
+
contextOptions: args.contextOptions ?? {},
|
|
584
|
+
getEmbedding: async (text) => {
|
|
585
|
+
assert(
|
|
586
|
+
effectiveEmbeddingModel,
|
|
587
|
+
"An embeddingModel (or textEmbeddingModel) is required to be set on the Agent that you're doing vector search with",
|
|
588
|
+
);
|
|
589
|
+
return {
|
|
590
|
+
embedding: (
|
|
591
|
+
await embedMany(ctx, {
|
|
592
|
+
...args,
|
|
593
|
+
userId,
|
|
594
|
+
values: [text],
|
|
595
|
+
embeddingModel: effectiveEmbeddingModel,
|
|
596
|
+
})
|
|
597
|
+
).embeddings[0],
|
|
598
|
+
embeddingModel: effectiveEmbeddingModel,
|
|
599
|
+
};
|
|
600
|
+
},
|
|
601
|
+
},
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
const promptMessageIndex = args.promptMessageId
|
|
605
|
+
? recentMessages.findIndex((m) => m._id === args.promptMessageId)
|
|
606
|
+
: -1;
|
|
607
|
+
const promptMessage =
|
|
608
|
+
promptMessageIndex !== -1 ? recentMessages[promptMessageIndex] : undefined;
|
|
609
|
+
let prePromptDocs = recentMessages;
|
|
610
|
+
const messages = args.messages ?? [];
|
|
611
|
+
let existingResponseDocs: MessageDoc[] = [];
|
|
612
|
+
if (promptMessage) {
|
|
613
|
+
prePromptDocs = recentMessages.slice(0, promptMessageIndex);
|
|
614
|
+
existingResponseDocs = recentMessages.slice(promptMessageIndex + 1);
|
|
615
|
+
if (promptArray.length === 0) {
|
|
616
|
+
// If they didn't override the prompt, use the existing prompt message.
|
|
617
|
+
if (promptMessage.message) {
|
|
618
|
+
promptArray.push(promptMessage.message);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (!promptMessage.embeddingId && effectiveEmbeddingModel) {
|
|
622
|
+
// Lazily generate embeddings for the prompt message, if it doesn't have
|
|
623
|
+
// embeddings yet. This can happen if the message was saved in a mutation
|
|
624
|
+
// where the LLM is not available.
|
|
625
|
+
await generateAndSaveEmbeddings(
|
|
626
|
+
ctx,
|
|
627
|
+
component,
|
|
628
|
+
{
|
|
629
|
+
...args,
|
|
630
|
+
userId,
|
|
631
|
+
embeddingModel: effectiveEmbeddingModel,
|
|
632
|
+
},
|
|
633
|
+
[promptMessage],
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const search = docsToModelMessages(searchMessages);
|
|
639
|
+
const recent = docsToModelMessages(prePromptDocs);
|
|
640
|
+
const inputMessages = messages.map(toModelMessage);
|
|
641
|
+
const inputPrompt = promptArray.map(toModelMessage);
|
|
642
|
+
const existingResponses = docsToModelMessages(existingResponseDocs);
|
|
643
|
+
|
|
644
|
+
const allMessages = mergeApprovalResponseMessages([
|
|
645
|
+
...search,
|
|
646
|
+
...recent,
|
|
647
|
+
...inputMessages,
|
|
648
|
+
...inputPrompt,
|
|
649
|
+
...existingResponses,
|
|
650
|
+
]);
|
|
651
|
+
let processedMessages = args.contextHandler
|
|
652
|
+
? await args.contextHandler(ctx, {
|
|
653
|
+
allMessages,
|
|
654
|
+
search,
|
|
655
|
+
recent,
|
|
656
|
+
inputMessages,
|
|
657
|
+
inputPrompt,
|
|
658
|
+
existingResponses,
|
|
659
|
+
userId,
|
|
660
|
+
threadId,
|
|
661
|
+
})
|
|
662
|
+
: allMessages;
|
|
663
|
+
|
|
664
|
+
// Process messages to inline localhost files (if not, file urls pointing to localhost will be sent to LLM providers)
|
|
665
|
+
if (process.env.CONVEX_CLOUD_URL?.startsWith("http://127.0.0.1")) {
|
|
666
|
+
processedMessages = await inlineMessagesFiles(processedMessages);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
return {
|
|
670
|
+
messages: processedMessages,
|
|
671
|
+
order: promptMessage?.order,
|
|
672
|
+
stepOrder: promptMessage?.stepOrder,
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
export function getPromptArray(
|
|
677
|
+
prompt: string | (ModelMessage | Message)[] | undefined,
|
|
678
|
+
): (ModelMessage | Message)[] {
|
|
679
|
+
return !prompt
|
|
680
|
+
? []
|
|
681
|
+
: Array.isArray(prompt)
|
|
682
|
+
? prompt
|
|
683
|
+
: [{ role: "user", content: prompt }];
|
|
684
|
+
}
|