You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
80 lines
2.2 KiB
80 lines
2.2 KiB
import { XllmError } from "../core/errors";
|
|
import type {
|
|
XChatWithToolsOptions,
|
|
XChatWithToolsResult,
|
|
XClientOptions,
|
|
XMessage,
|
|
XRequest,
|
|
XToolExecutorMap,
|
|
} from "../core/types";
|
|
import { generate } from "./generate";
|
|
import { runToolCall } from "./tool-loop-shared";
|
|
|
|
export const chatWithTools = async (
|
|
options: XClientOptions,
|
|
input: XChatWithToolsOptions,
|
|
executors: XToolExecutorMap,
|
|
): Promise<XChatWithToolsResult> => {
|
|
const maxRounds = input.maxRounds ?? 5;
|
|
const strategy = input.toolErrorStrategy ?? "throw";
|
|
if (maxRounds < 1) {
|
|
throw new XllmError({
|
|
code: "INVALID_REQUEST",
|
|
message: "maxRounds must be >= 1",
|
|
provider: input.provider ?? options.provider ?? "openai-compatible",
|
|
});
|
|
}
|
|
|
|
let rounds = 0;
|
|
let toolCallsExecuted = 0;
|
|
let currentMessages = [...input.messages];
|
|
const requestBase: Omit<XRequest, "messages"> = {
|
|
tools: input.tools,
|
|
toolChoice: input.toolChoice,
|
|
stream: false,
|
|
temperature: input.temperature,
|
|
topP: input.topP,
|
|
maxTokens: input.maxTokens,
|
|
metadata: input.metadata,
|
|
provider: input.provider,
|
|
model: input.model,
|
|
apiKey: input.apiKey,
|
|
baseURL: input.baseURL,
|
|
};
|
|
|
|
while (rounds < maxRounds) {
|
|
rounds += 1;
|
|
const response = await generate(options, {
|
|
...requestBase,
|
|
messages: currentMessages,
|
|
});
|
|
|
|
if (response.toolCalls.length === 0) {
|
|
return { response, rounds, toolCallsExecuted };
|
|
}
|
|
|
|
const assistantMessage: XMessage = {
|
|
role: "assistant",
|
|
content: [{ type: "text", text: response.text }],
|
|
toolCalls: response.toolCalls,
|
|
};
|
|
const toolMessages: XMessage[] = [];
|
|
|
|
for (const toolCall of response.toolCalls) {
|
|
const toolRunResult = await runToolCall(response.provider, toolCall, executors, strategy);
|
|
if (!toolRunResult.handled || !toolRunResult.message) {
|
|
continue;
|
|
}
|
|
toolMessages.push(toolRunResult.message);
|
|
toolCallsExecuted += 1;
|
|
}
|
|
|
|
currentMessages = [...currentMessages, assistantMessage, ...toolMessages];
|
|
}
|
|
|
|
throw new XllmError({
|
|
code: "PROVIDER_ERROR",
|
|
message: `Tool loop exceeded maxRounds=${maxRounds}`,
|
|
provider: input.provider ?? options.provider ?? "openai-compatible",
|
|
});
|
|
};
|
|
|