File size: 3,138 Bytes
8da7101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// Map raw API/runtime errors to friendly Vietnamese messages.
// Returns { title, hint, raw } — UI shows title + hint, raw collapsed in <details>.

export interface FriendlyError {
  title: string
  hint?: string
  raw: string
}

const PATTERNS: Array<{ match: RegExp; title: string; hint?: string }> = [
  {
    match: /string did not match the expected pattern/i,
    title: "Phản hồi không hợp lệ",
    hint: "Model có thể trả về JSON/URL sai định dạng, hoặc API key chứa ký tự lạ. Thử model khác hoặc kiểm tra lại API key trong Cài đặt.",
  },
  {
    match: /quota|rate.?limit|429|exceeded/i,
    title: "Đã vượt quota / rate limit",
    hint: "Model này hết hạn ngạch tạm thời. Đợi vài phút hoặc đổi sang model khác (Groq / GitHub Models có quota riêng).",
  },
  {
    match: /401|unauthor|invalid.?api.?key|api key not valid/i,
    title: "API key không hợp lệ",
    hint: "Kiểm tra lại key tại Cài đặt → API Keys. Đảm bảo key đúng provider và còn hiệu lực.",
  },
  {
    match: /403|forbidden|permission/i,
    title: "Không có quyền truy cập",
    hint: "Model có thể yêu cầu trả phí, vùng địa lý không hỗ trợ, hoặc API key thiếu scope.",
  },
  {
    match: /404|not found.*model|model.*not.*found|is not supported/i,
    title: "Model không khả dụng",
    hint: "Model này không tồn tại hoặc chưa hỗ trợ generateContent. Thử model khác trong danh sách.",
  },
  {
    match: /timeout|timed out|aborted/i,
    title: "Hết thời gian chờ",
    hint: "Server phản hồi quá chậm. Thử lại — nếu lặp lại liên tục, đổi model.",
  },
  {
    match: /failed to fetch|network|ECONNREFUSED|ENOTFOUND|fetch failed/i,
    title: "Lỗi kết nối mạng",
    hint: "Không truy cập được API. Kiểm tra Internet, VPN, hoặc proxy.",
  },
  {
    match: /500|internal server/i,
    title: "Lỗi server",
    hint: "Server provider đang gặp sự cố. Thử lại sau vài giây.",
  },
  {
    match: /context.*length|too many tokens|input.*too long|maximum context/i,
    title: "Vượt quá giới hạn context",
    hint: "Hội thoại quá dài. Bật 'Tự nén context' trong composer hoặc bắt đầu chat mới.",
  },
  {
    match: /content_filter|safety|blocked/i,
    title: "Bị chặn bởi safety filter",
    hint: "Nội dung bị model từ chối. Thử diễn đạt lại câu hỏi.",
  },
  {
    match: /tool.*invalid|invalid.*tool|function call|schema/i,
    title: "Lỗi tool call",
    hint: "Model trả về định dạng tool call không hợp lệ. Thử lại hoặc đổi model có hỗ trợ function calling tốt hơn (Gemini 2.5 Flash, Llama 3.x).",
  },
]

export function friendlyError(err: unknown): FriendlyError {
  const raw = err instanceof Error ? err.message : typeof err === "string" ? err : JSON.stringify(err)
  for (const p of PATTERNS) {
    if (p.match.test(raw)) {
      return { title: p.title, hint: p.hint, raw }
    }
  }
  return { title: "Có lỗi khi gọi AI", raw }
}