@unified-llm/core
Version:
Unified LLM interface (in-memory).
111 lines • 4.55 kB
JavaScript
import { sanitizeToolCallResult } from "../mcp-utils.js";
import { logTimed, toModelSafeError } from "../logging.js";
import { createDefaultClock } from "../timing.js";
export const NOOP_LOGGER = {
debug: () => { },
info: () => { },
warn: () => { },
error: () => { },
child: () => NOOP_LOGGER,
};
function safeJsonParse(value, fallback) {
if (!value)
return fallback;
try {
return JSON.parse(value);
}
catch (_a) {
return fallback;
}
}
/**
* tool call を実行して結果を文字列として返す(プロバイダー非依存)。
*
* - localToolHandlers が該当すれば優先
* - それ以外は MCP に委譲
* - 例外は握りつぶして `{ ok:false, error }` を output に詰める(LLMループを止めない)
* - sanitizeToolCallResult を適用して安全化
*/
export async function executeToolCalls(toolCalls, toolNameToClient, localToolHandlers, options) {
var _a, _b;
const logger = (_a = options === null || options === void 0 ? void 0 : options.logger) !== null && _a !== void 0 ? _a : NOOP_LOGGER;
const clock = (_b = options === null || options === void 0 ? void 0 : options.clock) !== null && _b !== void 0 ? _b : createDefaultClock();
const tasks = toolCalls.map(async (call) => {
var _a;
if (!call.callId) {
throw new Error(`Missing callId for tool call: ${call.name}`);
}
const args = typeof call.arguments === "string"
? safeJsonParse(call.arguments, {})
: (_a = call.arguments) !== null && _a !== void 0 ? _a : {};
try {
const localHandler = localToolHandlers === null || localToolHandlers === void 0 ? void 0 : localToolHandlers.get(call.name);
if (localHandler) {
logger.info("tool.call.request", {
tool: call.name,
call_id: call.callId,
kind: "local",
args: JSON.stringify(args),
});
const result = await logTimed(logger, clock, "tool.call.completed", {
tool: call.name,
call_id: call.callId,
kind: "local",
args_keys_count: Object.keys(args).length,
}, async () => localHandler(args), "info");
const outputText = typeof result === "string"
? result
: JSON.stringify(result !== null && result !== void 0 ? result : { ok: true });
logger.info("tool.call.result", {
tool: call.name,
call_id: call.callId,
kind: "local",
result: outputText,
});
return { name: call.name, callId: call.callId, output: outputText };
}
const mcpClient = toolNameToClient.get(call.name);
if (!mcpClient) {
throw new Error(`No MCP client registered for tool: ${call.name}`);
}
logger.info("tool.call.request", {
tool: call.name,
call_id: call.callId,
kind: "mcp",
args: JSON.stringify(args),
});
const result = await logTimed(logger, clock, "tool.call.completed", {
tool: call.name,
call_id: call.callId,
kind: "mcp",
args_keys_count: Object.keys(args).length,
}, async () => mcpClient.callTool({ name: call.name, arguments: args }), "info");
const { sanitizedResult } = sanitizeToolCallResult(result);
logger.info("tool.call.result", {
tool: call.name,
call_id: call.callId,
kind: "mcp",
result: JSON.stringify(sanitizedResult),
});
return {
name: call.name,
callId: call.callId,
output: JSON.stringify(sanitizedResult),
};
}
catch (err) {
logger.error("tool.call.failed", {
tool: call.name,
call_id: call.callId,
error: toModelSafeError(err),
});
return {
name: call.name,
callId: call.callId,
output: JSON.stringify({ ok: false, error: toModelSafeError(err) }),
};
}
});
return Promise.all(tasks);
}
//# sourceMappingURL=execute-tool-calls.js.map