omniroute / src /lib /skills /interception.ts
igorkurgin's picture
Deploy OmniRoute to Hugging Face Space
35743bd verified
Raw
History Blame Contribute Delete
8.14 kB
import { skillExecutor } from "./executor";
import { builtinSkills } from "./builtins";
import { detectProvider } from "./injection";
import { OMNIROUTE_WEB_SEARCH_FALLBACK_TOOL_NAME } from "@omniroute/open-sse/services/webSearchFallback.ts";
import { logger } from "../../../open-sse/utils/logger.ts";
const log = logger("SKILLS_INTERCEPTION");
interface ToolCall {
id: string;
name: string;
arguments: Record<string, unknown>;
}
interface ExecutionContext {
apiKeyId: string;
sessionId: string;
requestId: string;
builtinToolNames?: string[];
customSkillExecutionEnabled?: boolean;
}
const BUILTIN_TOOL_ALIASES: Record<string, string> = {
[OMNIROUTE_WEB_SEARCH_FALLBACK_TOOL_NAME]: "web_search",
};
function resolveBuiltinHandlerName(
toolName: string,
context: ExecutionContext
): keyof typeof builtinSkills | null {
const [rawName] = toolName.includes("@") ? toolName.split("@") : [toolName];
const canonicalName = BUILTIN_TOOL_ALIASES[rawName] || rawName;
const allowed = new Set(
(context.builtinToolNames || []).map((name) => BUILTIN_TOOL_ALIASES[name] || name)
);
if (!allowed.has(canonicalName)) {
return null;
}
return canonicalName in builtinSkills ? (canonicalName as keyof typeof builtinSkills) : null;
}
function getResponsesOutputContainer(response: Record<string, unknown> | null | undefined): {
root: Record<string, unknown>;
responseRoot: Record<string, unknown>;
output: unknown[];
} | null {
if (!response || typeof response !== "object") return null;
if (Array.isArray(response.output)) {
return {
root: response,
responseRoot: response,
output: response.output,
};
}
if (
response.response &&
typeof response.response === "object" &&
!Array.isArray(response.response) &&
Array.isArray((response.response as Record<string, unknown>).output)
) {
return {
root: response,
responseRoot: response.response as Record<string, unknown>,
output: (response.response as Record<string, unknown>).output as unknown[],
};
}
return null;
}
export async function interceptToolCalls(
toolCalls: ToolCall[],
context: ExecutionContext
): Promise<{ id: string; result: unknown }[]> {
const results = await Promise.all(
toolCalls.map(async (call) => {
try {
const builtinHandlerName = resolveBuiltinHandlerName(call.name, context);
if (builtinHandlerName) {
log.info("skills.interception.builtin_tool_detected", {
toolName: call.name,
builtinHandler: builtinHandlerName,
callId: call.id,
});
const result = await builtinSkills[builtinHandlerName](call.arguments, {
apiKeyId: context.apiKeyId,
sessionId: context.sessionId,
});
log.info("skills.interception.execution_complete", {
toolName: call.name,
callId: call.id,
});
return {
id: call.id,
result,
};
}
const [name, version] = call.name.includes("@")
? call.name.split("@")
: [call.name, "latest"];
const skillName = version === "latest" ? name : `${name}@${version}`;
log.info("skills.interception.tool_call_detected", {
toolName: call.name,
callId: call.id,
});
const execution = await skillExecutor.execute(skillName, call.arguments, {
apiKeyId: context.apiKeyId,
sessionId: context.sessionId,
});
const result =
execution.output ??
(execution.errorMessage
? { error: execution.errorMessage }
: { error: "Skill execution returned no output" });
log.info("skills.interception.execution_complete", {
toolName: call.name,
callId: call.id,
});
return {
id: call.id,
result,
};
} catch (err) {
log.error("skills.interception.execution_failed", {
toolName: call.name,
callId: call.id,
err: err instanceof Error ? err.message : String(err),
});
return {
id: call.id,
result: { error: err instanceof Error ? err.message : String(err) },
};
}
})
);
return results;
}
export function extractToolCalls(response: any, modelId: string): ToolCall[] {
const provider = detectProvider(modelId);
switch (provider) {
case "openai": {
const rootToolCalls = Array.isArray(response?.tool_calls) ? response.tool_calls : [];
const choiceToolCalls = Array.isArray(response?.choices)
? response.choices.flatMap((choice: any) =>
Array.isArray(choice?.message?.tool_calls) ? choice.message.tool_calls : []
)
: [];
const responsesOutput = getResponsesOutputContainer(response);
const responsesToolCalls = responsesOutput
? responsesOutput.output
.map((item: unknown) => (item && typeof item === "object" ? (item as any) : null))
.filter((item: any) => item?.type === "function_call")
: [];
const toolCalls =
rootToolCalls.length > 0
? rootToolCalls
: choiceToolCalls.length > 0
? choiceToolCalls
: responsesToolCalls;
return toolCalls.map((tc: any) => ({
id: tc.call_id || tc.id || `call_${Date.now()}`,
name: tc.function?.name || tc.name || "",
arguments: parseArguments(tc.function?.arguments || tc.arguments || "{}"),
}));
}
case "anthropic":
return (response.content || [])
.filter((c: any) => c.type === "tool_use")
.map((tc: any) => ({
id: tc.id,
name: tc.name,
arguments: tc.input || {},
}));
case "google":
return (response.functionCalls || []).map((fc: any) => ({
id: `call_${Date.now()}_${Math.random().toString(36).slice(2)}`,
name: fc.name,
arguments: fc.args || {},
}));
default:
return [];
}
}
function parseArguments(args: string | Record<string, unknown>): Record<string, unknown> {
if (typeof args === "object") {
return args;
}
try {
return JSON.parse(args);
} catch {
return {};
}
}
export async function handleToolCallExecution(
response: any,
modelId: string,
context: ExecutionContext
): Promise<any> {
const toolCalls = extractToolCalls(response, modelId).filter((call) => {
const builtinHandlerName = resolveBuiltinHandlerName(call.name, context);
if (builtinHandlerName) {
return true;
}
return context.customSkillExecutionEnabled !== false;
});
if (toolCalls.length === 0) {
return response;
}
const results = await interceptToolCalls(toolCalls, context);
const provider = detectProvider(modelId);
switch (provider) {
case "openai": {
const responsesOutput = getResponsesOutputContainer(response);
if (responsesOutput) {
const functionOutputs = results.map((result) => ({
type: "function_call_output",
call_id: result.id,
output: JSON.stringify(result.result),
}));
if (responsesOutput.root === responsesOutput.responseRoot) {
return {
...response,
output: [...responsesOutput.output, ...functionOutputs],
};
}
return {
...response,
response: {
...responsesOutput.responseRoot,
output: [...responsesOutput.output, ...functionOutputs],
},
};
}
return {
...response,
tool_results: results.map((r) => ({
tool_call_id: r.id,
output: JSON.stringify(r.result),
})),
};
}
case "anthropic":
return {
...response,
content: [
...response.content,
...results.map((r) => ({
type: "tool_result",
tool_use_id: r.id,
content: JSON.stringify(r.result),
})),
],
};
default:
return response;
}
}