diff --git a/utils/aiGatewayClient.ts b/utils/aiGatewayClient.ts index 9d439a7..8b8379e 100644 --- a/utils/aiGatewayClient.ts +++ b/utils/aiGatewayClient.ts @@ -30,66 +30,91 @@ export interface ToolUseBlock { input: Record; } +const MAX_RETRIES = 3; + export async function makeRequest( prompt: string, toolDef: T ): Promise> { - try { - const completion = await getClient().chat.completions.create({ - model: 'Meta-Llama-3_3-70B-Instruct', - temperature: 1, - max_tokens: 16000, - tools: [ - { - type: 'function', - function: { - name: toolDef.name, - description: toolDef.input_schema.description || '', - parameters: { - type: 'object', - properties: toolDef.input_schema.properties, - required: toolDef.input_schema.required - ? [...toolDef.input_schema.required] - : undefined + const requiredFields = toolDef.input_schema.required + ? [...toolDef.input_schema.required] + : Object.keys(toolDef.input_schema.properties); + + let lastError: Error | null = null; + + for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) { + try { + console.log(`AI request attempt ${attempt}/${MAX_RETRIES}`); + + const completion = await getClient().chat.completions.create({ + model: 'Meta-Llama-3_3-70B-Instruct', + temperature: 0.7, + max_tokens: 16000, + tools: [ + { + type: 'function', + function: { + name: toolDef.name, + description: toolDef.input_schema.description || '', + parameters: { + type: 'object', + properties: toolDef.input_schema.properties, + required: requiredFields + } } } - } - ], - tool_choice: { - type: 'function', - function: { name: toolDef.name } - }, - messages: [ - { - role: 'system', - content: - 'You are a data generation assistant. Generate realistic, diverse synthetic data. You must respond ONLY with the function call. Do not include any text outside the function call.' + ], + tool_choice: { + type: 'function', + function: { name: toolDef.name } }, - { - role: 'user', - content: prompt - } - ] - }); + messages: [ + { + role: 'system', + content: `You are a data generation assistant that creates realistic synthetic data. - const message = completion.choices[0]?.message; +CRITICAL REQUIREMENTS: +1. You MUST call the function with ALL required fields populated +2. Required top-level fields: ${requiredFields.join(', ')} +3. Every nested object and array must be fully populated with realistic data +4. Do NOT leave any field as null, undefined, or empty +5. Generate diverse, realistic Italian consumer data` + }, + { + role: 'user', + content: prompt + } + ] + }); - if (!message?.tool_calls || message.tool_calls.length === 0) { - throw new Error('No function call found in response'); + const message = completion.choices[0]?.message; + + if (!message?.tool_calls || message.tool_calls.length === 0) { + throw new Error('No function call found in response'); + } + + const toolCall = message.tool_calls[0]; + + if (toolCall.function.name !== toolDef.name) { + throw new Error( + `Expected tool ${toolDef.name} but got ${toolCall.function.name}` + ); + } + + const result = JSON.parse(toolCall.function.arguments); + return result; + } catch (error) { + console.error(`Attempt ${attempt} failed:`, error); + lastError = error as Error; + + if (attempt < MAX_RETRIES) { + const delay = 1000 * attempt; + console.log(`Retrying in ${delay}ms...`); + await new Promise(resolve => setTimeout(resolve, delay)); + } } - - const toolCall = message.tool_calls[0]; - - if (toolCall.function.name !== toolDef.name) { - throw new Error( - `Expected tool ${toolDef.name} but got ${toolCall.function.name}` - ); - } - - const result = JSON.parse(toolCall.function.arguments); - return result; - } catch (error) { - console.error('Error making request:', error); - throw Error('OVH AI Endpoints client error.'); } + + console.error('All retry attempts failed'); + throw lastError || new Error('OVH AI Endpoints client error.'); }