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.
97 lines
2.5 KiB
97 lines
2.5 KiB
import { XllmError } from "../core/errors";
|
|
import type {
|
|
XChatWithToolsOptions,
|
|
XClientOptions,
|
|
XMessage,
|
|
XRequest,
|
|
XToolCall,
|
|
XToolErrorStrategy,
|
|
XToolExecutorMap,
|
|
} from "../core/types";
|
|
import { resolveConfig } from "../runtime/config";
|
|
import { runToolCall } from "./tool-loop-shared";
|
|
|
|
export interface ToolLoopConfig {
|
|
provider: string;
|
|
maxRounds: number;
|
|
strategy: XToolErrorStrategy;
|
|
}
|
|
|
|
export interface ToolLoopAccumulator {
|
|
rounds: number;
|
|
toolCallsExecuted: number;
|
|
currentMessages: XMessage[];
|
|
}
|
|
|
|
export const createToolLoopConfig = (
|
|
input: XChatWithToolsOptions,
|
|
options: XClientOptions,
|
|
): ToolLoopConfig => {
|
|
const resolved = resolveConfig(input, options);
|
|
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: resolved.provider,
|
|
});
|
|
}
|
|
return { provider: resolved.provider, maxRounds, strategy };
|
|
};
|
|
|
|
export const buildRequestBase = (
|
|
input: XChatWithToolsOptions,
|
|
stream: boolean,
|
|
): Omit<XRequest, "messages"> => ({
|
|
tools: input.tools,
|
|
toolChoice: input.toolChoice,
|
|
stream,
|
|
temperature: input.temperature,
|
|
topP: input.topP,
|
|
maxTokens: input.maxTokens,
|
|
metadata: input.metadata,
|
|
provider: input.provider,
|
|
model: input.model,
|
|
apiKey: input.apiKey,
|
|
baseURL: input.baseURL,
|
|
providerExtras: input.providerExtras,
|
|
});
|
|
|
|
export const buildAssistantMessage = (
|
|
text: string,
|
|
toolCalls: XToolCall[],
|
|
reasoning?: string,
|
|
): XMessage => ({
|
|
role: "assistant",
|
|
content: text,
|
|
toolCalls,
|
|
...(reasoning ? { reasoningContent: reasoning } : {}),
|
|
});
|
|
|
|
export const executeToolCalls = async (
|
|
provider: string,
|
|
toolCalls: XToolCall[],
|
|
executors: XToolExecutorMap,
|
|
strategy: XToolErrorStrategy,
|
|
): Promise<{ toolMessages: XMessage[]; toolCallsExecuted: number }> => {
|
|
const toolMessages: XMessage[] = [];
|
|
let toolCallsExecuted = 0;
|
|
for (const toolCall of toolCalls) {
|
|
const toolRunResult = await runToolCall(provider, toolCall, executors, strategy);
|
|
if (!toolRunResult.handled || !toolRunResult.message) {
|
|
continue;
|
|
}
|
|
toolMessages.push(toolRunResult.message);
|
|
toolCallsExecuted += 1;
|
|
}
|
|
return { toolMessages, toolCallsExecuted };
|
|
};
|
|
|
|
export function throwIfMaxRoundsExceeded(config: ToolLoopConfig): never {
|
|
throw new XllmError({
|
|
code: "PROVIDER_ERROR",
|
|
message: `Tool loop exceeded maxRounds=${config.maxRounds}`,
|
|
provider: config.provider,
|
|
});
|
|
}
|
|
|