@agentica/vector-selector 0.44.0-dev.20260313-2 → 0.44.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/LICENSE +21 -21
- package/README.md +218 -218
- package/lib/select.js +5 -5
- package/lib/strategy/sqlite.strategy.js +14 -14
- package/lib/tools.js +29 -29
- package/package.json +3 -3
- package/src/extract_query.ts +51 -51
- package/src/index.ts +67 -67
- package/src/select.ts +212 -212
- package/src/strategy/index.ts +2 -2
- package/src/strategy/postgres.strategy.ts +116 -116
- package/src/strategy/sqlite.strategy.ts +123 -123
- package/src/tools.ts +99 -99
- package/src/utils.test.ts +357 -357
- package/src/utils.ts +123 -123
package/src/extract_query.ts
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import type { AgenticaContext } from "@agentica/core";
|
|
2
|
-
import type { FromSchema } from "json-schema-to-ts";
|
|
3
|
-
|
|
4
|
-
import { factory, utils } from "@agentica/core";
|
|
5
|
-
|
|
6
|
-
import { Tools } from "./tools";
|
|
7
|
-
|
|
8
|
-
export async function extractQuery(ctx: AgenticaContext) {
|
|
9
|
-
const completionStream = await ctx.request("select", {
|
|
10
|
-
messages: [
|
|
11
|
-
{
|
|
12
|
-
role: "system",
|
|
13
|
-
content: [
|
|
14
|
-
"You are a function searcher. You will extract search queries from the user's message, and the query results will be function names.",
|
|
15
|
-
"A query is a 2–3 sentence description of the action the user needs to perform.",
|
|
16
|
-
"Therefore, the extracted queries must be suitable for function search.",
|
|
17
|
-
"You need to identify the actions required to achieve what the user wants and extract queries that can be used to search for those actions.",
|
|
18
|
-
"Extract only one query per task.",
|
|
19
|
-
].join("\n"),
|
|
20
|
-
},
|
|
21
|
-
...ctx.histories
|
|
22
|
-
.map(factory.decodeHistory)
|
|
23
|
-
.flat(),
|
|
24
|
-
{
|
|
25
|
-
role: "user",
|
|
26
|
-
content: ctx.prompt.contents.map(factory.decodeUserMessageContent),
|
|
27
|
-
},
|
|
28
|
-
],
|
|
29
|
-
tool_choice: "required",
|
|
30
|
-
|
|
31
|
-
tools: [Tools.extract_query],
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const completion = await (async () => {
|
|
35
|
-
if (completionStream.type === "none-stream") {
|
|
36
|
-
return completionStream.value;
|
|
37
|
-
}
|
|
38
|
-
return utils.ChatGptCompletionMessageUtil.merge(await utils.StreamUtil.readAll(completionStream.value));
|
|
39
|
-
})();
|
|
40
|
-
|
|
41
|
-
const queries = completion.choices[0]?.message.tool_calls?.filter(tc => tc.type === "function").flatMap((v) => {
|
|
42
|
-
const arg = JSON.parse(v.function.arguments) as Partial<FromSchema<typeof Tools.extract_query.function.parameters>>;
|
|
43
|
-
if (!Array.isArray(arg.query_list)) {
|
|
44
|
-
return [];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return arg.query_list.map(v => v.query);
|
|
48
|
-
}) ?? [];
|
|
49
|
-
|
|
50
|
-
return queries;
|
|
51
|
-
}
|
|
1
|
+
import type { AgenticaContext } from "@agentica/core";
|
|
2
|
+
import type { FromSchema } from "json-schema-to-ts";
|
|
3
|
+
|
|
4
|
+
import { factory, utils } from "@agentica/core";
|
|
5
|
+
|
|
6
|
+
import { Tools } from "./tools";
|
|
7
|
+
|
|
8
|
+
export async function extractQuery(ctx: AgenticaContext) {
|
|
9
|
+
const completionStream = await ctx.request("select", {
|
|
10
|
+
messages: [
|
|
11
|
+
{
|
|
12
|
+
role: "system",
|
|
13
|
+
content: [
|
|
14
|
+
"You are a function searcher. You will extract search queries from the user's message, and the query results will be function names.",
|
|
15
|
+
"A query is a 2–3 sentence description of the action the user needs to perform.",
|
|
16
|
+
"Therefore, the extracted queries must be suitable for function search.",
|
|
17
|
+
"You need to identify the actions required to achieve what the user wants and extract queries that can be used to search for those actions.",
|
|
18
|
+
"Extract only one query per task.",
|
|
19
|
+
].join("\n"),
|
|
20
|
+
},
|
|
21
|
+
...ctx.histories
|
|
22
|
+
.map(factory.decodeHistory)
|
|
23
|
+
.flat(),
|
|
24
|
+
{
|
|
25
|
+
role: "user",
|
|
26
|
+
content: ctx.prompt.contents.map(factory.decodeUserMessageContent),
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
tool_choice: "required",
|
|
30
|
+
|
|
31
|
+
tools: [Tools.extract_query],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const completion = await (async () => {
|
|
35
|
+
if (completionStream.type === "none-stream") {
|
|
36
|
+
return completionStream.value;
|
|
37
|
+
}
|
|
38
|
+
return utils.ChatGptCompletionMessageUtil.merge(await utils.StreamUtil.readAll(completionStream.value));
|
|
39
|
+
})();
|
|
40
|
+
|
|
41
|
+
const queries = completion.choices[0]?.message.tool_calls?.filter(tc => tc.type === "function").flatMap((v) => {
|
|
42
|
+
const arg = JSON.parse(v.function.arguments) as Partial<FromSchema<typeof Tools.extract_query.function.parameters>>;
|
|
43
|
+
if (!Array.isArray(arg.query_list)) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return arg.query_list.map(v => v.query);
|
|
48
|
+
}) ?? [];
|
|
49
|
+
|
|
50
|
+
return queries;
|
|
51
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
import type { AgenticaContext } from "@agentica/core";
|
|
2
|
-
|
|
3
|
-
import { extractQuery } from "./extract_query";
|
|
4
|
-
import { selectFunction } from "./select";
|
|
5
|
-
import { uniqBy } from "./utils";
|
|
6
|
-
|
|
7
|
-
export interface IAgenticaVectorSelectorBootProps {
|
|
8
|
-
strategy: IAgenticaVectorSelectorStrategy;
|
|
9
|
-
}
|
|
10
|
-
export interface IAgenticaVectorSelectorStrategy {
|
|
11
|
-
searchTool: (ctx: AgenticaContext, query: string) => Promise<{
|
|
12
|
-
name: string;
|
|
13
|
-
description: string | undefined;
|
|
14
|
-
}[]>;
|
|
15
|
-
embedContext: (
|
|
16
|
-
props: {
|
|
17
|
-
ctx: AgenticaContext;
|
|
18
|
-
setEmbedded: () => void;
|
|
19
|
-
},
|
|
20
|
-
) => Promise<void>;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function BootAgenticaVectorSelector(props: IAgenticaVectorSelectorBootProps) {
|
|
24
|
-
const { isEmbedded, setEmbedded } = useEmbeddedContext();
|
|
25
|
-
const { searchTool, embedContext } = props.strategy;
|
|
26
|
-
const selectorExecute = async (
|
|
27
|
-
ctx: AgenticaContext,
|
|
28
|
-
): Promise<void> => {
|
|
29
|
-
if (!isEmbedded(ctx)) {
|
|
30
|
-
await embedContext({ ctx, setEmbedded: () => setEmbedded(ctx) });
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const queries = await extractQuery(ctx);
|
|
34
|
-
const toolList = await Promise.all(
|
|
35
|
-
queries.map(async query => searchTool(ctx, query)),
|
|
36
|
-
).then(res => res.flat().map((v) => {
|
|
37
|
-
const op = ctx.operations.flat.get(v.name);
|
|
38
|
-
if (op === undefined || op.protocol !== "http") {
|
|
39
|
-
return v;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
...v,
|
|
44
|
-
method: op.function.method,
|
|
45
|
-
path: op.function.path,
|
|
46
|
-
tags: op.function.tags,
|
|
47
|
-
};
|
|
48
|
-
})).then(arr => uniqBy(arr, v => v.name));
|
|
49
|
-
|
|
50
|
-
if (toolList.length === 0) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
await selectFunction({ ctx, toolList });
|
|
54
|
-
};
|
|
55
|
-
return selectorExecute;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function useEmbeddedContext() {
|
|
59
|
-
const set = new Set<string>();
|
|
60
|
-
return {
|
|
61
|
-
isEmbedded: (ctx: AgenticaContext) =>
|
|
62
|
-
set.has(JSON.stringify(ctx.operations.array)),
|
|
63
|
-
setEmbedded: (ctx: AgenticaContext) => {
|
|
64
|
-
set.add(JSON.stringify(ctx.operations.array));
|
|
65
|
-
},
|
|
66
|
-
} as const;
|
|
67
|
-
}
|
|
1
|
+
import type { AgenticaContext } from "@agentica/core";
|
|
2
|
+
|
|
3
|
+
import { extractQuery } from "./extract_query";
|
|
4
|
+
import { selectFunction } from "./select";
|
|
5
|
+
import { uniqBy } from "./utils";
|
|
6
|
+
|
|
7
|
+
export interface IAgenticaVectorSelectorBootProps {
|
|
8
|
+
strategy: IAgenticaVectorSelectorStrategy;
|
|
9
|
+
}
|
|
10
|
+
export interface IAgenticaVectorSelectorStrategy {
|
|
11
|
+
searchTool: (ctx: AgenticaContext, query: string) => Promise<{
|
|
12
|
+
name: string;
|
|
13
|
+
description: string | undefined;
|
|
14
|
+
}[]>;
|
|
15
|
+
embedContext: (
|
|
16
|
+
props: {
|
|
17
|
+
ctx: AgenticaContext;
|
|
18
|
+
setEmbedded: () => void;
|
|
19
|
+
},
|
|
20
|
+
) => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function BootAgenticaVectorSelector(props: IAgenticaVectorSelectorBootProps) {
|
|
24
|
+
const { isEmbedded, setEmbedded } = useEmbeddedContext();
|
|
25
|
+
const { searchTool, embedContext } = props.strategy;
|
|
26
|
+
const selectorExecute = async (
|
|
27
|
+
ctx: AgenticaContext,
|
|
28
|
+
): Promise<void> => {
|
|
29
|
+
if (!isEmbedded(ctx)) {
|
|
30
|
+
await embedContext({ ctx, setEmbedded: () => setEmbedded(ctx) });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const queries = await extractQuery(ctx);
|
|
34
|
+
const toolList = await Promise.all(
|
|
35
|
+
queries.map(async query => searchTool(ctx, query)),
|
|
36
|
+
).then(res => res.flat().map((v) => {
|
|
37
|
+
const op = ctx.operations.flat.get(v.name);
|
|
38
|
+
if (op === undefined || op.protocol !== "http") {
|
|
39
|
+
return v;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
...v,
|
|
44
|
+
method: op.function.method,
|
|
45
|
+
path: op.function.path,
|
|
46
|
+
tags: op.function.tags,
|
|
47
|
+
};
|
|
48
|
+
})).then(arr => uniqBy(arr, v => v.name));
|
|
49
|
+
|
|
50
|
+
if (toolList.length === 0) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
await selectFunction({ ctx, toolList });
|
|
54
|
+
};
|
|
55
|
+
return selectorExecute;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function useEmbeddedContext() {
|
|
59
|
+
const set = new Set<string>();
|
|
60
|
+
return {
|
|
61
|
+
isEmbedded: (ctx: AgenticaContext) =>
|
|
62
|
+
set.has(JSON.stringify(ctx.operations.array)),
|
|
63
|
+
setEmbedded: (ctx: AgenticaContext) => {
|
|
64
|
+
set.add(JSON.stringify(ctx.operations.array));
|
|
65
|
+
},
|
|
66
|
+
} as const;
|
|
67
|
+
}
|
package/src/select.ts
CHANGED
|
@@ -1,212 +1,212 @@
|
|
|
1
|
-
import type { AgenticaAssistantMessageEvent, AgenticaContext, AgenticaOperationSelection } from "@agentica/core";
|
|
2
|
-
|
|
3
|
-
import { AgenticaDefaultPrompt, AgenticaSystemPrompt, factory, utils } from "@agentica/core";
|
|
4
|
-
|
|
5
|
-
import { Tools } from "./tools";
|
|
6
|
-
|
|
7
|
-
interface IFailure {
|
|
8
|
-
id: string;
|
|
9
|
-
name: string;
|
|
10
|
-
validation: {
|
|
11
|
-
data: string;
|
|
12
|
-
errors: string[];
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function selectFunction(props: {
|
|
17
|
-
ctx: AgenticaContext;
|
|
18
|
-
toolList: object[];
|
|
19
|
-
prevFailures?: IFailure[];
|
|
20
|
-
restRetry?: number;
|
|
21
|
-
}): Promise<void> {
|
|
22
|
-
const { ctx, toolList, prevFailures = [], restRetry = 5 } = props;
|
|
23
|
-
const selectCompletion = await ctx.request("select", {
|
|
24
|
-
messages: [
|
|
25
|
-
{
|
|
26
|
-
role: "assistant",
|
|
27
|
-
tool_calls: [
|
|
28
|
-
{
|
|
29
|
-
type: "function",
|
|
30
|
-
id: "getApiFunctions",
|
|
31
|
-
function: {
|
|
32
|
-
name: "getApiFunctions",
|
|
33
|
-
arguments: JSON.stringify({}),
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
role: "tool",
|
|
40
|
-
tool_call_id: "getApiFunctions",
|
|
41
|
-
content: JSON.stringify(toolList),
|
|
42
|
-
},
|
|
43
|
-
...ctx.histories.flatMap(factory.decodeHistory),
|
|
44
|
-
{
|
|
45
|
-
role: "user",
|
|
46
|
-
content: ctx.prompt.contents.map(factory.decodeUserMessageContent),
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
role: "system",
|
|
50
|
-
content: `${ctx.config?.systemPrompt?.select?.(ctx.histories)
|
|
51
|
-
?? AgenticaSystemPrompt.SELECT}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
When selecting functions, consider what the user can call from their perspective, and choose all the functions necessary to accomplish the task.
|
|
55
|
-
Select them in a logical sequence, taking into account the relationships between each function.
|
|
56
|
-
`,
|
|
57
|
-
},
|
|
58
|
-
...emendMessages(prevFailures),
|
|
59
|
-
{
|
|
60
|
-
role: "system",
|
|
61
|
-
content: AgenticaDefaultPrompt.write(ctx.config),
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
tool_choice: {
|
|
65
|
-
type: "function",
|
|
66
|
-
function: {
|
|
67
|
-
name: "select_functions",
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
// parallel_tool_calls: false,
|
|
71
|
-
tools: [Tools.select_functions],
|
|
72
|
-
})
|
|
73
|
-
.then(async (v) => {
|
|
74
|
-
if (v.type === "none-stream") {
|
|
75
|
-
return v.value;
|
|
76
|
-
}
|
|
77
|
-
return utils.ChatGptCompletionMessageUtil.merge(await utils.StreamUtil.readAll(v.value));
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
const toolCalls = selectCompletion.choices
|
|
81
|
-
.filter(v => v.message.tool_calls != null);
|
|
82
|
-
|
|
83
|
-
if (toolCalls.length === 0) {
|
|
84
|
-
selectCompletion.choices.forEach((v) => {
|
|
85
|
-
if (v.message.content != null && v.message.content !== "") {
|
|
86
|
-
const event: AgenticaAssistantMessageEvent = factory.createAssistantMessageEvent({
|
|
87
|
-
stream: utils.toAsyncGenerator(v.message.content),
|
|
88
|
-
done: () => true,
|
|
89
|
-
get: () => v.message.content as string,
|
|
90
|
-
join: async () => (v.message.content as string),
|
|
91
|
-
});
|
|
92
|
-
void ctx.dispatch(event).catch(() => {});
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const failures = toolCalls.reduce<IFailure[]>((acc, cur) => {
|
|
98
|
-
cur.message.tool_calls?.filter(tc => tc.type === "function").forEach((tc) => {
|
|
99
|
-
const errors: string[] = [];
|
|
100
|
-
const arg = JSON.parse(tc.function.arguments) as Partial<{ reason: string; function_name: string }>[];
|
|
101
|
-
if (!Array.isArray(arg)) {
|
|
102
|
-
errors.push(JSON.stringify({
|
|
103
|
-
path: "$input",
|
|
104
|
-
expected: "array",
|
|
105
|
-
value: arg,
|
|
106
|
-
}));
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
arg.forEach((v, idx) => {
|
|
110
|
-
if (v.reason == null || typeof v.reason !== "string") {
|
|
111
|
-
errors.push(JSON.stringify({
|
|
112
|
-
path: `$$input[${idx}].reason`,
|
|
113
|
-
expected: "string",
|
|
114
|
-
value: v.reason,
|
|
115
|
-
}));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (v.function_name == null || typeof v.function_name !== "string") {
|
|
119
|
-
errors.push(JSON.stringify({
|
|
120
|
-
path: `$$input[${idx}].function_name`,
|
|
121
|
-
expected: "string",
|
|
122
|
-
value: v.function_name,
|
|
123
|
-
}));
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
if (errors.length !== 0) {
|
|
128
|
-
acc.push({
|
|
129
|
-
id: tc.id,
|
|
130
|
-
name: tc.function.name,
|
|
131
|
-
validation: { data: tc.function.arguments, errors },
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
return acc;
|
|
136
|
-
}, []);
|
|
137
|
-
|
|
138
|
-
if (failures.length !== 0) {
|
|
139
|
-
const feedback = [...prevFailures, ...failures];
|
|
140
|
-
if (restRetry === 0) {
|
|
141
|
-
throw new Error(`Failed to select function after ${restRetry} retries\n${JSON.stringify(feedback)}`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return selectFunction({
|
|
145
|
-
ctx,
|
|
146
|
-
toolList,
|
|
147
|
-
prevFailures: feedback,
|
|
148
|
-
restRetry: restRetry - 1,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
toolCalls.forEach((v) => {
|
|
153
|
-
v.message.tool_calls!.filter(tc => tc.type === "function").forEach((tc) => {
|
|
154
|
-
const arg = JSON.parse(tc.function.arguments) as {
|
|
155
|
-
function_list: {
|
|
156
|
-
reason: string;
|
|
157
|
-
function_name: string;
|
|
158
|
-
}[];
|
|
159
|
-
};
|
|
160
|
-
arg.function_list.forEach((v) => {
|
|
161
|
-
const operation = ctx.operations.flat.get(v.function_name);
|
|
162
|
-
|
|
163
|
-
if (operation === undefined) {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
const selection: AgenticaOperationSelection
|
|
167
|
-
= factory.createOperationSelection({
|
|
168
|
-
reason: v.reason,
|
|
169
|
-
operation,
|
|
170
|
-
});
|
|
171
|
-
ctx.stack.push(selection);
|
|
172
|
-
void ctx.dispatch(
|
|
173
|
-
factory.createSelectEvent({
|
|
174
|
-
selection,
|
|
175
|
-
}),
|
|
176
|
-
).catch(() => {});
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function emendMessages(failures: IFailure[]): ReturnType<typeof factory.decodeHistory> {
|
|
183
|
-
return failures
|
|
184
|
-
.flatMap(f => [
|
|
185
|
-
{
|
|
186
|
-
role: "assistant",
|
|
187
|
-
tool_calls: [
|
|
188
|
-
{
|
|
189
|
-
type: "function",
|
|
190
|
-
id: f.id,
|
|
191
|
-
function: {
|
|
192
|
-
name: f.name,
|
|
193
|
-
arguments: JSON.stringify(f.validation.data),
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
],
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
role: "tool",
|
|
200
|
-
content: JSON.stringify(f.validation.errors),
|
|
201
|
-
tool_call_id: f.id,
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
role: "system",
|
|
205
|
-
content: [
|
|
206
|
-
"You A.I. assistant has composed wrong typed arguments.",
|
|
207
|
-
"",
|
|
208
|
-
"Correct it at the next function calling.",
|
|
209
|
-
].join("\n"),
|
|
210
|
-
},
|
|
211
|
-
]) satisfies ReturnType<typeof factory.decodeHistory>;
|
|
212
|
-
}
|
|
1
|
+
import type { AgenticaAssistantMessageEvent, AgenticaContext, AgenticaOperationSelection } from "@agentica/core";
|
|
2
|
+
|
|
3
|
+
import { AgenticaDefaultPrompt, AgenticaSystemPrompt, factory, utils } from "@agentica/core";
|
|
4
|
+
|
|
5
|
+
import { Tools } from "./tools";
|
|
6
|
+
|
|
7
|
+
interface IFailure {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
validation: {
|
|
11
|
+
data: string;
|
|
12
|
+
errors: string[];
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function selectFunction(props: {
|
|
17
|
+
ctx: AgenticaContext;
|
|
18
|
+
toolList: object[];
|
|
19
|
+
prevFailures?: IFailure[];
|
|
20
|
+
restRetry?: number;
|
|
21
|
+
}): Promise<void> {
|
|
22
|
+
const { ctx, toolList, prevFailures = [], restRetry = 5 } = props;
|
|
23
|
+
const selectCompletion = await ctx.request("select", {
|
|
24
|
+
messages: [
|
|
25
|
+
{
|
|
26
|
+
role: "assistant",
|
|
27
|
+
tool_calls: [
|
|
28
|
+
{
|
|
29
|
+
type: "function",
|
|
30
|
+
id: "getApiFunctions",
|
|
31
|
+
function: {
|
|
32
|
+
name: "getApiFunctions",
|
|
33
|
+
arguments: JSON.stringify({}),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
role: "tool",
|
|
40
|
+
tool_call_id: "getApiFunctions",
|
|
41
|
+
content: JSON.stringify(toolList),
|
|
42
|
+
},
|
|
43
|
+
...ctx.histories.flatMap(factory.decodeHistory),
|
|
44
|
+
{
|
|
45
|
+
role: "user",
|
|
46
|
+
content: ctx.prompt.contents.map(factory.decodeUserMessageContent),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
role: "system",
|
|
50
|
+
content: `${ctx.config?.systemPrompt?.select?.(ctx.histories)
|
|
51
|
+
?? AgenticaSystemPrompt.SELECT}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
When selecting functions, consider what the user can call from their perspective, and choose all the functions necessary to accomplish the task.
|
|
55
|
+
Select them in a logical sequence, taking into account the relationships between each function.
|
|
56
|
+
`,
|
|
57
|
+
},
|
|
58
|
+
...emendMessages(prevFailures),
|
|
59
|
+
{
|
|
60
|
+
role: "system",
|
|
61
|
+
content: AgenticaDefaultPrompt.write(ctx.config),
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
tool_choice: {
|
|
65
|
+
type: "function",
|
|
66
|
+
function: {
|
|
67
|
+
name: "select_functions",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
// parallel_tool_calls: false,
|
|
71
|
+
tools: [Tools.select_functions],
|
|
72
|
+
})
|
|
73
|
+
.then(async (v) => {
|
|
74
|
+
if (v.type === "none-stream") {
|
|
75
|
+
return v.value;
|
|
76
|
+
}
|
|
77
|
+
return utils.ChatGptCompletionMessageUtil.merge(await utils.StreamUtil.readAll(v.value));
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const toolCalls = selectCompletion.choices
|
|
81
|
+
.filter(v => v.message.tool_calls != null);
|
|
82
|
+
|
|
83
|
+
if (toolCalls.length === 0) {
|
|
84
|
+
selectCompletion.choices.forEach((v) => {
|
|
85
|
+
if (v.message.content != null && v.message.content !== "") {
|
|
86
|
+
const event: AgenticaAssistantMessageEvent = factory.createAssistantMessageEvent({
|
|
87
|
+
stream: utils.toAsyncGenerator(v.message.content),
|
|
88
|
+
done: () => true,
|
|
89
|
+
get: () => v.message.content as string,
|
|
90
|
+
join: async () => (v.message.content as string),
|
|
91
|
+
});
|
|
92
|
+
void ctx.dispatch(event).catch(() => {});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const failures = toolCalls.reduce<IFailure[]>((acc, cur) => {
|
|
98
|
+
cur.message.tool_calls?.filter(tc => tc.type === "function").forEach((tc) => {
|
|
99
|
+
const errors: string[] = [];
|
|
100
|
+
const arg = JSON.parse(tc.function.arguments) as Partial<{ reason: string; function_name: string }>[];
|
|
101
|
+
if (!Array.isArray(arg)) {
|
|
102
|
+
errors.push(JSON.stringify({
|
|
103
|
+
path: "$input",
|
|
104
|
+
expected: "array",
|
|
105
|
+
value: arg,
|
|
106
|
+
}));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
arg.forEach((v, idx) => {
|
|
110
|
+
if (v.reason == null || typeof v.reason !== "string") {
|
|
111
|
+
errors.push(JSON.stringify({
|
|
112
|
+
path: `$$input[${idx}].reason`,
|
|
113
|
+
expected: "string",
|
|
114
|
+
value: v.reason,
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (v.function_name == null || typeof v.function_name !== "string") {
|
|
119
|
+
errors.push(JSON.stringify({
|
|
120
|
+
path: `$$input[${idx}].function_name`,
|
|
121
|
+
expected: "string",
|
|
122
|
+
value: v.function_name,
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (errors.length !== 0) {
|
|
128
|
+
acc.push({
|
|
129
|
+
id: tc.id,
|
|
130
|
+
name: tc.function.name,
|
|
131
|
+
validation: { data: tc.function.arguments, errors },
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
return acc;
|
|
136
|
+
}, []);
|
|
137
|
+
|
|
138
|
+
if (failures.length !== 0) {
|
|
139
|
+
const feedback = [...prevFailures, ...failures];
|
|
140
|
+
if (restRetry === 0) {
|
|
141
|
+
throw new Error(`Failed to select function after ${restRetry} retries\n${JSON.stringify(feedback)}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return selectFunction({
|
|
145
|
+
ctx,
|
|
146
|
+
toolList,
|
|
147
|
+
prevFailures: feedback,
|
|
148
|
+
restRetry: restRetry - 1,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
toolCalls.forEach((v) => {
|
|
153
|
+
v.message.tool_calls!.filter(tc => tc.type === "function").forEach((tc) => {
|
|
154
|
+
const arg = JSON.parse(tc.function.arguments) as {
|
|
155
|
+
function_list: {
|
|
156
|
+
reason: string;
|
|
157
|
+
function_name: string;
|
|
158
|
+
}[];
|
|
159
|
+
};
|
|
160
|
+
arg.function_list.forEach((v) => {
|
|
161
|
+
const operation = ctx.operations.flat.get(v.function_name);
|
|
162
|
+
|
|
163
|
+
if (operation === undefined) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const selection: AgenticaOperationSelection
|
|
167
|
+
= factory.createOperationSelection({
|
|
168
|
+
reason: v.reason,
|
|
169
|
+
operation,
|
|
170
|
+
});
|
|
171
|
+
ctx.stack.push(selection);
|
|
172
|
+
void ctx.dispatch(
|
|
173
|
+
factory.createSelectEvent({
|
|
174
|
+
selection,
|
|
175
|
+
}),
|
|
176
|
+
).catch(() => {});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function emendMessages(failures: IFailure[]): ReturnType<typeof factory.decodeHistory> {
|
|
183
|
+
return failures
|
|
184
|
+
.flatMap(f => [
|
|
185
|
+
{
|
|
186
|
+
role: "assistant",
|
|
187
|
+
tool_calls: [
|
|
188
|
+
{
|
|
189
|
+
type: "function",
|
|
190
|
+
id: f.id,
|
|
191
|
+
function: {
|
|
192
|
+
name: f.name,
|
|
193
|
+
arguments: JSON.stringify(f.validation.data),
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
role: "tool",
|
|
200
|
+
content: JSON.stringify(f.validation.errors),
|
|
201
|
+
tool_call_id: f.id,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
role: "system",
|
|
205
|
+
content: [
|
|
206
|
+
"You A.I. assistant has composed wrong typed arguments.",
|
|
207
|
+
"",
|
|
208
|
+
"Correct it at the next function calling.",
|
|
209
|
+
].join("\n"),
|
|
210
|
+
},
|
|
211
|
+
]) satisfies ReturnType<typeof factory.decodeHistory>;
|
|
212
|
+
}
|
package/src/strategy/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./postgres.strategy";
|
|
2
|
-
export { configureSqliteStrategy } from "./sqlite.strategy";
|
|
1
|
+
export * from "./postgres.strategy";
|
|
2
|
+
export { configureSqliteStrategy } from "./sqlite.strategy";
|