Files
chat-gpz/app/actions/chat.ts
T
Zacharias-Brohn c51b3c3fab changes
2026-01-14 22:51:46 +01:00

141 lines
3.7 KiB
TypeScript

'use server';
import { Tool } from 'ollama';
import ollama from '@/lib/ollama';
export interface ChatMessage {
role: 'user' | 'assistant' | 'system' | 'tool';
content: string;
tool_calls?: any[]; // Ollama tool calls
images?: string[];
}
// --- Tool Definitions ---
const tools: Tool[] = [
{
type: 'function',
function: {
name: 'get_current_time',
description: 'Get the current time',
parameters: {
type: 'object',
properties: {},
required: [],
},
},
},
{
type: 'function',
function: {
name: 'calculate',
description: 'Perform a mathematical calculation',
parameters: {
type: 'object',
properties: {
expression: {
type: 'string',
description: 'The mathematical expression to evaluate (e.g., "2 + 2" or "15 * 7")',
},
},
required: ['expression'],
},
},
},
];
// --- Tool Implementations ---
const availableTools: Record<string, (args: Record<string, unknown>) => string> = {
get_current_time: () => {
return new Date().toLocaleTimeString();
},
calculate: (args) => {
const expression = args.expression as string;
try {
// Safety: simplistic eval for demo purposes.
// In production, use a math parser library like mathjs.
// eslint-disable-next-line no-eval
return eval(expression).toString();
} catch {
return 'Error evaluating expression';
}
},
};
export async function chat(model: string, messages: ChatMessage[]) {
try {
// 1. Initial Call
let response;
try {
response = await ollama.chat({
model,
messages,
tools,
});
} catch (e: unknown) {
// Fallback: If model doesn't support tools, retry without them
const errorMessage = e instanceof Error ? e.message : '';
if (errorMessage.includes('does not support tools')) {
// eslint-disable-next-line no-console
console.warn(`Model ${model} does not support tools. Falling back to standard chat.`);
response = await ollama.chat({
model,
messages,
});
} else {
throw e;
}
}
// 2. Loop to handle tool calls (Ollama might chain multiple calls)
// We limit recursion to avoid infinite loops
let maxTurns = 5;
while (response.message.tool_calls && response.message.tool_calls.length > 0 && maxTurns > 0) {
maxTurns--;
// Append the assistant's message (which contains the tool calls) to history
messages.push(response.message as ChatMessage);
// Execute each tool call
for (const tool of response.message.tool_calls) {
const functionName = tool.function.name;
const functionToCall = availableTools[functionName];
if (functionToCall) {
// eslint-disable-next-line no-console
console.log(`🤖 Tool Call: ${functionName}`, tool.function.arguments);
const functionArgs = tool.function.arguments;
const functionResponse = functionToCall(functionArgs);
// Append the tool result to history
messages.push({
role: 'tool',
content: functionResponse,
});
}
}
// 3. Send the tool results back to the model to get the final answer
response = await ollama.chat({
model,
messages,
tools,
});
}
return {
success: true,
message: response.message,
};
} catch (error: unknown) {
// eslint-disable-next-line no-console
console.error('Chat error:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to generate response',
};
}
}