This commit is contained in:
Zacharias-Brohn
2026-01-14 23:59:13 +01:00
parent cb773bda91
commit 51aead70b6
2 changed files with 73 additions and 14 deletions
+29
View File
@@ -125,6 +125,9 @@ export async function POST(request: NextRequest) {
: [...messages]; : [...messages];
let iterations = 0; let iterations = 0;
// Track tool calls to detect loops (same tool with same/similar args)
const toolCallHistory: string[] = [];
while (iterations < MAX_TOOL_ITERATIONS) { while (iterations < MAX_TOOL_ITERATIONS) {
iterations++; iterations++;
@@ -203,6 +206,32 @@ export async function POST(request: NextRequest) {
break; break;
} }
// Check for duplicate/similar tool calls (sign of a loop)
// If the model is calling the same tool with the same base argument, stop
let hasDuplicateCall = false;
for (const tc of toolCalls) {
// Create a simple signature for the tool call
const mainArg = Object.values(tc.arguments)[0]?.toString().toLowerCase() || '';
const signature = `${tc.name}:${mainArg.split(',')[0].split(' ')[0]}`; // e.g., "get_weather:phoenix"
if (toolCallHistory.includes(signature)) {
hasDuplicateCall = true;
break;
}
toolCallHistory.push(signature);
}
// If duplicate detected, force a response on next iteration
if (hasDuplicateCall && iterations < MAX_TOOL_ITERATIONS - 1) {
// Skip to last iteration to force a response
iterations = MAX_TOOL_ITERATIONS - 1;
workingMessages.push({
role: 'system',
content:
'You are repeating the same tool call. Stop calling tools and provide a response using the information you already have.',
});
}
// Process tool calls // Process tool calls
controller.enqueue(encoder.encode('\n\n')); controller.enqueue(encoder.encode('\n\n'));
+44 -14
View File
@@ -11,13 +11,14 @@ export const weatherTool: Tool = {
function: { function: {
name: 'get_weather', name: 'get_weather',
description: description:
'Get current weather information for a location. Provides temperature, conditions, humidity, wind speed, and more.', 'Get current weather information for a location. Provides temperature, conditions, humidity, wind speed, and more. Use simple city names for best results (e.g., "Phoenix" not "Phoenix, AZ").',
parameters: { parameters: {
type: 'object', type: 'object',
properties: { properties: {
location: { location: {
type: 'string', type: 'string',
description: 'City name or location (e.g., "New York", "London, UK", "Tokyo, Japan")', description:
'City name, optionally with country (e.g., "New York", "London", "Tokyo, Japan"). Avoid state abbreviations like "NY" or "AZ".',
}, },
units: { units: {
type: 'string', type: 'string',
@@ -104,6 +105,43 @@ function getWindDirection(degrees: number): string {
return directions[index]; return directions[index];
} }
/**
* Try to geocode a location, with fallback attempts for common formats
*/
async function geocodeLocation(location: string): Promise<GeocodingResult | null> {
// List of location variations to try
const variations = [
location,
// Remove state abbreviations like ", NY" or ", AZ"
location.replace(/,\s*[A-Z]{2}$/i, ''),
// Remove country/state suffixes after comma
location.split(',')[0].trim(),
// Remove "USA", "United States", etc.
location.replace(/,?\s*(USA|United States|US)$/i, '').trim(),
];
// Remove duplicates while preserving order
const uniqueVariations = Array.from(new Set(variations.map((v) => v.trim()).filter(Boolean)));
for (const query of uniqueVariations) {
try {
const geoUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(query)}&count=1&language=en&format=json`;
const geoResponse = await fetch(geoUrl, { signal: AbortSignal.timeout(10000) });
if (!geoResponse.ok) continue;
const geoData = await geoResponse.json();
if (geoData.results && geoData.results.length > 0) {
return geoData.results[0];
}
} catch {
// Try next variation
}
}
return null;
}
export const weatherHandler: ToolHandler = async (args): Promise<ToolResult> => { export const weatherHandler: ToolHandler = async (args): Promise<ToolResult> => {
const location = args.location as string; const location = args.location as string;
const units = (args.units as 'metric' | 'imperial') || 'metric'; const units = (args.units as 'metric' | 'imperial') || 'metric';
@@ -116,24 +154,16 @@ export const weatherHandler: ToolHandler = async (args): Promise<ToolResult> =>
} }
try { try {
// First, geocode the location // Geocode the location with fallback attempts
const geoUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(location)}&count=1&language=en&format=json`; const geo = await geocodeLocation(location);
const geoResponse = await fetch(geoUrl, { signal: AbortSignal.timeout(10000) });
if (!geoResponse.ok) { if (!geo) {
throw new Error('Failed to geocode location');
}
const geoData = await geoResponse.json();
if (!geoData.results || geoData.results.length === 0) {
return { return {
success: false, success: false,
error: `Location not found: "${location}"`, error: `Location not found: "${location}". Try using just the city name (e.g., "Phoenix" instead of "Phoenix, AZ")`,
}; };
} }
const geo: GeocodingResult = geoData.results[0];
// Fetch weather data // Fetch weather data
const tempUnit = units === 'imperial' ? 'fahrenheit' : 'celsius'; const tempUnit = units === 'imperial' ? 'fahrenheit' : 'celsius';
const windUnit = units === 'imperial' ? 'mph' : 'kmh'; const windUnit = units === 'imperial' ? 'mph' : 'kmh';