invoke.ts•3.06 kB
import axios from "axios";
import { ExecutionContext } from "./types";
import { callToolHandler } from "./handlers";
const OLLAMA_BASE_URL = process.env.OLLAMA_BASE_URL || "http://localhost:11434";
const DEFAULT_TOOL_TIMEOUT_MS = Number(process.env.MCP_TOOL_TIMEOUT_MS || 10000);
export async function invokeOllama(model: string, prompt: string, system?: string): Promise<any> {
const resp = await axios.post(`${OLLAMA_BASE_URL}/api/generate`, {
model,
prompt,
system,
stream: false,
});
if (!resp || !resp.data) throw new Error("No response from Ollama");
// Ollama shape: { response: "..." }
return resp.data.response;
}
const toolRegistry: Record<string, (args: any) => Promise<any>> = {};
export function registerTool(name: string, fn: (args: any) => Promise<any>) {
toolRegistry[name] = fn;
}
function withTimeout<T>(p: Promise<T>, ms: number, name?: string): Promise<T> {
if (ms == null || ms === Infinity) return p;
let timer: NodeJS.Timeout | null = null;
const t = new Promise<never>((_, rej) => {
timer = setTimeout(() => rej(new Error(`Tool invocation timed out after ${ms}ms${name ? `: ${name}` : ""}`)), ms);
});
return Promise.race([p.then((r) => {
if (timer) clearTimeout(timer);
return r;
}), t]) as Promise<T>;
}
export async function invokeTool(name: string, args: any, options?: { timeoutMs?: number }): Promise<any> {
const fn = toolRegistry[name];
const timeoutMs = options && typeof options.timeoutMs === "number" ? options.timeoutMs : DEFAULT_TOOL_TIMEOUT_MS;
if (fn) {
const p = Promise.resolve(fn(args));
return await withTimeout(p, timeoutMs, name);
}
// Fallback: try calling local MCP handlers if available
try {
const p = Promise.resolve(callToolHandler({ name, arguments: args }));
return await withTimeout(p, timeoutMs, name);
} catch (e) {
throw new Error(`Unknown tool or handler failed: ${name} (${(e as Error).message})`);
}
}
function resolvePath(obj: any, path: string): any {
if (!obj) return undefined;
if (!path) return undefined;
const parts = path.split(".");
let cur = obj;
for (const p of parts) {
if (cur == null) return undefined;
cur = cur[p];
}
return cur;
}
export function renderTemplate(template: string, ctx: { memory: Record<string, any>; variables: Record<string, any> }) {
if (!template) return "";
return template.replace(/\${([^}]+)}/g, (_, expr: string) => {
const parts = expr.split(".");
if (parts[0] === "memory") {
return String(resolvePath(ctx.memory, parts.slice(1).join(".")) ?? "");
}
if (parts[0] === "variables" || parts[0] === "$") {
return String(resolvePath(ctx.variables, parts.slice(1).join(".")) ?? "");
}
// fallback: try variables then memory
const v = resolvePath(ctx.variables, expr);
if (v !== undefined) return String(v);
const m = resolvePath(ctx.memory, expr);
if (m !== undefined) return String(m);
return "";
});
}
export default { invokeOllama, registerTool, invokeTool, renderTemplate };