@mirodan/ai-insight 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,19 @@ import { messages } from '../i18n/messages'
7
7
 
8
8
  const { t, locale: i18nLocale } = useI18n({ messages, useScope: 'local' })
9
9
 
10
+ const insightErrorMap: Record<string, string> = {
11
+ INSIGHT_UNAUTHORIZED: 'aiInsight.unauthorized',
12
+ INSIGHT_INVALID_KEY: 'aiInsight.invalidKey',
13
+ INSIGHT_KEY_DISABLED: 'aiInsight.keyDisabled',
14
+ INSIGHT_DOMAIN_DENIED: 'aiInsight.domainDenied',
15
+ INSIGHT_DAILY_LIMIT_EXCEEDED: 'aiInsight.dailyLimit',
16
+ INSIGHT_MONTHLY_LIMIT_EXCEEDED: 'aiInsight.monthlyLimit',
17
+ CONFIG_ENDPOINT_MISSING: 'aiInsight.endpointMissing',
18
+ CONFIG_KEY_MISSING: 'aiInsight.keyMissing',
19
+ BACKEND_UNREACHABLE: 'aiInsight.backendUnreachable',
20
+ BACKEND_ERROR: 'aiInsight.backendError'
21
+ }
22
+
10
23
  interface Props {
11
24
  open: boolean
12
25
  data: unknown
@@ -83,7 +96,8 @@ async function loadInsight() {
83
96
  let msg = t('aiInsight.requestError', { status: response.status })
84
97
  try {
85
98
  const json = await response.json()
86
- msg = json.message || json.statusMessage || msg
99
+ const code = json.statusMessage || json.data?.message || json.message || ''
100
+ msg = (code && insightErrorMap[code] ? t(insightErrorMap[code]) : code) || msg
87
101
  } catch {
88
102
  const text = await response.text().catch(() => '')
89
103
  if (text) msg = text
@@ -92,7 +106,7 @@ async function loadInsight() {
92
106
  }
93
107
 
94
108
  let result = ''
95
- for await (const delta of parseUIMessageStream(response)) {
109
+ for await (const delta of parseUIMessageStream(response, t('aiInsight.streamError'))) {
96
110
  result += delta
97
111
  insight.value = result
98
112
  }
@@ -7,7 +7,18 @@ export const messages = {
7
7
  askQuestion: 'Задать вопрос ИИ...',
8
8
  retry: 'Повторить',
9
9
  requestError: 'Ошибка запроса ({status})',
10
- unknownError: 'Неизвестная ошибка'
10
+ unknownError: 'Неизвестная ошибка',
11
+ streamError: 'Ошибка AI-сервиса',
12
+ unauthorized: 'Неверный или отсутствует заголовок Authorization',
13
+ invalidKey: 'Неверный API-ключ',
14
+ keyDisabled: 'API-ключ отключён',
15
+ domainDenied: 'Домен не разрешён',
16
+ dailyLimit: 'Превышен дневной лимит запросов',
17
+ monthlyLimit: 'Превышен месячный лимит запросов',
18
+ endpointMissing: 'Не указан endpoint AI Insight',
19
+ keyMissing: 'Не указан API-ключ AI Insight',
20
+ backendUnreachable: 'Не удалось подключиться к AI Insight',
21
+ backendError: 'Ошибка AI Insight'
11
22
  }
12
23
  },
13
24
  en: {
@@ -18,7 +29,18 @@ export const messages = {
18
29
  askQuestion: 'Ask the AI...',
19
30
  retry: 'Retry',
20
31
  requestError: 'Request failed ({status})',
21
- unknownError: 'Unknown error'
32
+ unknownError: 'Unknown error',
33
+ streamError: 'AI service error',
34
+ unauthorized: 'Invalid or missing Authorization header',
35
+ invalidKey: 'Invalid API key',
36
+ keyDisabled: 'API key is disabled',
37
+ domainDenied: 'Domain is not allowed',
38
+ dailyLimit: 'Daily request limit exceeded',
39
+ monthlyLimit: 'Monthly request limit exceeded',
40
+ endpointMissing: 'AI Insight endpoint is not configured',
41
+ keyMissing: 'AI Insight API key is not configured',
42
+ backendUnreachable: 'Failed to reach AI Insight backend',
43
+ backendError: 'AI Insight error'
22
44
  }
23
45
  }
24
46
  }
@@ -1,23 +1,8 @@
1
1
  import { defineEventHandler, createError, getHeader, setHeaders, readBody } from 'h3'
2
2
  import { useRuntimeConfig } from '#imports'
3
3
 
4
- const tErrorMessages = {
5
- ru: {
6
- CONFIG_ENDPOINT_MISSING: 'Не указан endpoint AI Insight',
7
- CONFIG_KEY_MISSING: 'Не указан API-ключ AI Insight',
8
- BACKEND_UNREACHABLE: 'Не удалось подключиться к AI Insight',
9
- BACKEND_ERROR: 'Ошибка AI Insight'
10
- },
11
- en: {
12
- CONFIG_ENDPOINT_MISSING: 'AI Insight endpoint is not configured',
13
- CONFIG_KEY_MISSING: 'AI Insight API key is not configured',
14
- BACKEND_UNREACHABLE: 'Failed to reach AI Insight backend',
15
- BACKEND_ERROR: 'AI Insight error'
16
- }
17
- }
18
-
19
- function tError(key, lang) {
20
- return tErrorMessages[lang === 'en' ? 'en' : 'ru']?.[key] || key
4
+ function proxyError(statusCode, code) {
5
+ return createError({ statusCode, statusMessage: code, message: code, data: { message: code } })
21
6
  }
22
7
 
23
8
  export default defineEventHandler(async (event) => {
@@ -26,13 +11,12 @@ export default defineEventHandler(async (event) => {
26
11
 
27
12
  const endpoint = config.aiInsight.endpoint
28
13
  const hasKey = !!config.aiInsight.key
29
- const lang = getHeader(event, 'accept-language')?.slice(0, 2) || 'ru'
30
14
 
31
15
  if (!endpoint) {
32
- throw createError({ statusCode: 500, message: tError('CONFIG_ENDPOINT_MISSING', lang) })
16
+ throw proxyError(500, 'CONFIG_ENDPOINT_MISSING')
33
17
  }
34
18
  if (!hasKey) {
35
- throw createError({ statusCode: 500, message: tError('CONFIG_KEY_MISSING', lang) })
19
+ throw proxyError(500, 'CONFIG_KEY_MISSING')
36
20
  }
37
21
 
38
22
  console.log('[ai-insight/proxy] →', endpoint, 'key:', hasKey ? 'yes' : 'NO KEY', 'data:', typeof body?.data, 'prompt:', body?.prompt?.slice(0, 80))
@@ -54,21 +38,21 @@ export default defineEventHandler(async (event) => {
54
38
  })
55
39
  } catch (fetchErr) {
56
40
  console.error('[ai-insight/proxy] fetch error:', fetchErr)
57
- throw createError({ statusCode: 502, message: tError('BACKEND_UNREACHABLE', lang) })
41
+ throw proxyError(502, 'BACKEND_UNREACHABLE')
58
42
  }
59
43
 
60
44
  console.log('[ai-insight/proxy] ←', response.status, response.statusText)
61
45
 
62
46
  if (!response.ok) {
63
47
  const text = await response.text().catch(() => '')
64
- let detail
48
+ let code
65
49
  try {
66
50
  const json = JSON.parse(text)
67
- detail = json.message || json.statusMessage || text
51
+ code = json.statusMessage || json.data?.message || json.message || text
68
52
  } catch {
69
- detail = text || response.statusText
53
+ code = text || response.statusText
70
54
  }
71
- throw createError({ statusCode: response.status, message: detail || tError('BACKEND_ERROR', lang) })
55
+ throw proxyError(response.status, code || 'BACKEND_ERROR')
72
56
  }
73
57
 
74
58
  setHeaders(event, {
@@ -1,4 +1,4 @@
1
- export async function* parseUIMessageStream(response: Response) {
1
+ export async function* parseUIMessageStream(response: Response, fallbackErrorText = 'AI stream error') {
2
2
  const reader = response.body!.getReader()
3
3
  const decoder = new TextDecoder()
4
4
  let buffer = ''
@@ -19,9 +19,18 @@ export async function* parseUIMessageStream(response: Response) {
19
19
  const chunk = JSON.parse(data)
20
20
  if (chunk.type === 'text-delta' && chunk.delta != null) {
21
21
  yield chunk.delta
22
+ } else if (chunk.type === 'error') {
23
+ const message = typeof chunk.errorText === 'string'
24
+ ? chunk.errorText
25
+ : (chunk.error?.message || chunk.error || fallbackErrorText)
26
+ throw new Error(message)
27
+ }
28
+ } catch (err) {
29
+ if (err instanceof SyntaxError) {
30
+ console.warn('[parseUIMessageStream] parse error:', data)
31
+ } else {
32
+ throw err
22
33
  }
23
- } catch {
24
- console.warn('[parseUIMessageStream] parse error:', data)
25
34
  }
26
35
  }
27
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mirodan/ai-insight",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "AI-powered insight tooltip component for Mirodan",
5
5
  "type": "module",
6
6
  "main": "./dist/module.js",