ai-error.ts 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940
  1. import OpenAI from 'openai'
  2. /**
  3. * Turns whatever a provider threw into one short line fit for a toast.
  4. *
  5. * A rejected key rarely comes back as tidy JSON: a gateway in front of the
  6. * provider answers with an HTML error page, and the SDK puts that entire page
  7. * in `error.message`. Users were seeing raw markup in the toast.
  8. */
  9. export function describeAiError(error: unknown): string {
  10. const generic = 'The AI provider returned an unexpected response.'
  11. const status = error instanceof OpenAI.APIError ? error.status : undefined
  12. switch (status) {
  13. case 401:
  14. case 403:
  15. return 'The AI provider rejected the API key. Check it in Settings → Integrations.'
  16. case 402:
  17. return 'The AI provider reports no usable credit for this API key.'
  18. case 404:
  19. return 'The AI provider does not offer the selected model. Pick another in Settings → Integrations.'
  20. case 413:
  21. return 'The image was too large for the AI provider.'
  22. case 429:
  23. return 'The AI provider is rate limiting this API key. Try again in a moment.'
  24. }
  25. if (status && status >= 500) {
  26. return 'The AI provider is unavailable right now. Try again in a moment.'
  27. }
  28. const raw = error instanceof Error ? error.message : String(error ?? '')
  29. // An HTML body says nothing a workshop can act on, and stripping the tags
  30. // just leaves the debris of a status page.
  31. if (/<\s*(!doctype|html|head|body|div|p|span|title)\b/i.test(raw)) return generic
  32. const text = raw.replace(/\s+/g, ' ').trim()
  33. if (!text) return generic
  34. return text.length > 200 ? `${text.slice(0, 200)}...` : text
  35. }