#!/usr/bin/env tsx /** * Test script to validate fix for issue #55 * Tests that Gemini proxy properly strips exclusiveMinimum/exclusiveMaximum from tool schemas */ import Anthropic from '@anthropic-ai/sdk'; const GEMINI_PROXY_URL = process.env.GEMINI_PROXY_URL || 'http://localhost:3000'; const GOOGLE_GEMINI_API_KEY = process.env.GOOGLE_GEMINI_API_KEY; if (!GOOGLE_GEMINI_API_KEY) { console.error('โŒ GOOGLE_GEMINI_API_KEY not set in environment'); process.exit(1); } console.log('๐Ÿงช Testing Gemini Proxy - exclusiveMinimum/exclusiveMaximum Fix\n'); console.log(`Proxy URL: ${GEMINI_PROXY_URL}`); console.log(`API Key: ${GOOGLE_GEMINI_API_KEY.substring(0, 10)}...\n`); // Test tool definition with exclusiveMinimum (like Claude Code uses) const testTool: Anthropic.Tool = { name: 'test_tool_with_exclusive_minimum', description: 'Test tool that includes exclusiveMinimum in schema', input_schema: { type: 'object', properties: { limit: { type: 'number', exclusiveMinimum: 0, // This should be stripped by cleanSchema description: 'Limit parameter (must be > 0)' }, offset: { type: 'number', exclusiveMinimum: 0, exclusiveMaximum: 1000, // This should also be stripped description: 'Offset parameter' }, name: { type: 'string', description: 'Name parameter (should be preserved)' } }, required: ['limit'] } }; async function testGeminiProxy() { try { console.log('๐Ÿ“‹ Test Tool Schema (BEFORE cleanSchema):'); console.log(JSON.stringify(testTool.input_schema, null, 2)); console.log('\n'); // Create Anthropic client pointing to Gemini proxy const client = new Anthropic({ apiKey: GOOGLE_GEMINI_API_KEY, baseURL: GEMINI_PROXY_URL }); console.log('๐Ÿš€ Sending request to Gemini proxy with tool definition...\n'); const response = await client.messages.create({ model: 'claude-3-5-sonnet-20241022', max_tokens: 1024, messages: [ { role: 'user', content: 'Can you tell me what tools you have available? Just list them briefly.' } ], tools: [testTool] }); console.log('โœ… SUCCESS: Request completed without errors!\n'); console.log('Response:'); console.log(JSON.stringify(response, null, 2)); console.log('\n'); // Verify the response if (response.content && response.content.length > 0) { console.log('โœ… Response received successfully'); console.log('โœ… Tool schema with exclusiveMinimum/exclusiveMaximum was accepted'); console.log('โœ… Fix for issue #55 is WORKING!\n'); console.log('๐Ÿ“Š Test Results:'); console.log(' - Tool definition sent: โœ…'); console.log(' - exclusiveMinimum handled: โœ…'); console.log(' - exclusiveMaximum handled: โœ…'); console.log(' - No 400 errors: โœ…'); console.log(' - Valid response received: โœ…'); return true; } else { console.error('โŒ FAIL: Response content is empty'); return false; } } catch (error: any) { console.error('โŒ ERROR occurred during test:\n'); if (error.status === 400 && error.message?.includes('exclusiveMinimum')) { console.error('โŒ FAIL: Gemini API still rejecting exclusiveMinimum'); console.error(' This means the fix is NOT working correctly\n'); } console.error('Error details:'); console.error(` Status: ${error.status}`); console.error(` Message: ${error.message}`); if (error.error) { console.error(` Error object: ${JSON.stringify(error.error, null, 2)}`); } console.error('\n'); return false; } } async function main() { console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); console.log(' GEMINI PROXY - EXCLUSIVE MINIMUM FIX VALIDATION'); console.log(' Testing fix for GitHub issue #55'); console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'); const success = await testGeminiProxy(); console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); if (success) { console.log('โœ… ALL TESTS PASSED - Fix is working correctly!'); console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'); process.exit(0); } else { console.log('โŒ TESTS FAILED - Fix needs more work'); console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'); process.exit(1); } } main().catch(err => { console.error('Fatal error:', err); process.exit(1); });