(apiKey, baseURL, model, service = 'openai', puterIPCHandler = null)
| 555 | throw new Error(`Authentication failed: ${error.response.data?.error?.message || 'Invalid API key or insufficient permissions'}`); |
| 556 | } |
| 557 | // Handle no body errors (but not if it's a 429 - that's handled above) |
| 558 | else if (error.message && error.message.includes('no body') && !error.message.includes('429')) { |
| 559 | console.warn(`'No body' error encountered: ${error.message}`); |
| 560 | if (attempt < maxRetries - 1) { |
| 561 | console.log(`Retrying for no body error (attempt ${attempt + 1}/${maxRetries})...`); |
| 562 | attempt++; |
| 563 | await delay(delayMs * (attempt + 1)); // Exponential backoff |
| 564 | continue; |
| 565 | } else { |
| 566 | console.error('No body error persisted after retries, returning empty tags'); |
| 567 | return []; // Return empty tags array instead of throwing |
| 568 | } |
| 569 | } |
| 570 | // Handle network errors |
| 571 | else if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT' || error.code === 'ENOTFOUND') { |
| 572 | if (attempt < maxRetries - 1) { |
| 573 | console.warn(`Network error (${error.code}), retrying attempt ${attempt + 1}...`); |
| 574 | attempt++; |
| 575 | delayMs *= 2; |
| 576 | continue; |
| 577 | } else { |
| 578 | throw new Error(`Network error: Unable to connect to API endpoint. Please check your internet connection and API endpoint configuration.`); |
| 579 | } |
| 580 | } |
| 581 | // Handle other errors |
| 582 | else { |
| 583 | console.error("Error generating tags:", error); |
| 584 | // Provide more user-friendly error messages |
| 585 | if (error.message) { |
| 586 | throw new Error(`Tag generation failed: ${error.message}`); |
| 587 | } else { |
| 588 | throw new Error(`Tag generation failed: Unknown error occurred. Please check your API configuration.`); |
| 589 | } |
| 590 | } |
| 591 | } |
| 592 | } |
| 593 | |
| 594 | throw new Error("Max retries reached. Could not generate tags due to rate limiting."); |
| 595 | } |
| 596 | |
| 597 | // Test AI configuration |
| 598 | async function testAIConfig(apiKey, baseURL, model, service = 'openai', puterIPCHandler = null) { |
| 599 | // Normalize service to lowercase for comparison |
| 600 | let normalizedService = service ? String(service).toLowerCase().trim() : 'openai'; |
| 601 | |
| 602 | // If service is not 'puter' but endpoint contains 'puter.com', treat it as Puter |
| 603 | if (normalizedService !== 'puter' && baseURL && (baseURL.includes('puter.com') || baseURL.includes('js.puter.com'))) { |
| 604 | console.log('[AITagging] Endpoint contains puter.com, forcing service to puter'); |
| 605 | normalizedService = 'puter'; |
| 606 | } |
| 607 | |
| 608 | console.log('[AITagging] testAIConfig called with:', { |
| 609 | service, |
| 610 | normalizedService, |
| 611 | baseURL, |
| 612 | hasPuterHandler: !!puterIPCHandler, |
| 613 | apiKeyLength: apiKey ? apiKey.length : 0 |
| 614 | }); |
nothing calls this directly
no test coverage detected