Files
openclaw-ollama-toolcall-proxy/src/proxy/response-rewriter.ts

77 lines
2.5 KiB
TypeScript
Executable File

import { OllamaChatResponse, ToolCall } from '../types/ollama';
import { parseXmlToolCalls } from '../parsers';
import { logger } from '../utils/logger';
import { sanitizeContent } from '../utils/content-sanitizer';
function buildStandardToolCalls(parsedCalls: ReturnType<typeof parseXmlToolCalls>): ToolCall[] {
return parsedCalls.map((call, index) => ({
id: `call_${Date.now()}_${index}`,
type: 'function',
function: {
name: call.name,
arguments: call.args || {},
}
}));
}
/**
* Rewrites the Ollama response to include structured tool calls if missing
* but present in XML tags within the content.
*/
export function rewriteResponse(response: OllamaChatResponse): OllamaChatResponse {
// If the response isn't properly formed or has no message, return as is
if (!response || !response.message) {
return response;
}
// If the response already has tool_calls, normalize them (add missing fields)
if (response.message.tool_calls && response.message.tool_calls.length > 0) {
response.message.tool_calls = response.message.tool_calls.map((call: any) => {
const normalized: any = { ...call };
// Ensure 'type' field exists
if (!normalized.type) {
normalized.type = 'function';
}
// Remove Ollama-specific 'function.index' if present (not part of OpenAI spec)
if (normalized.function && normalized.function.index !== undefined) {
delete normalized.function.index;
}
return normalized;
});
return response;
}
const content = response.message.content || '';
const thinking = response.message.thinking || '';
const parsedCalls = [
...parseXmlToolCalls(content),
...parseXmlToolCalls(thinking),
];
if (parsedCalls.length > 0) {
logger.info(`Rewriting response: found ${parsedCalls.length} tool calls in content/thinking`);
response.message.tool_calls = buildStandardToolCalls(parsedCalls);
response.message.content = '';
if (response.message.thinking) {
response.message.thinking = sanitizeContent(
response.message.thinking
.replace(/<function=([^>]+)>([\s\S]*?)<\/function>/g, '')
.replace(/<tool_call>([\s\S]*?)<\/tool_call>/g, '')
);
}
return response;
}
// Sanitize plain text responses too
if (response.message.content) {
response.message.content = sanitizeContent(response.message.content);
}
if (response.message.thinking) {
response.message.thinking = sanitizeContent(response.message.thinking);
}
return response;
}