llm-simple-router 0.11.30 → 0.12.0-dev.2464ab6

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.
Files changed (151) hide show
  1. package/config/recommended-providers.json +24 -12
  2. package/config/recommended-retry-rules.json +128 -11
  3. package/dist/admin/providers.js +1 -1
  4. package/dist/admin/quick-setup.js +61 -6
  5. package/dist/admin/usage.js +19 -12
  6. package/dist/config/recommended-providers.json +24 -12
  7. package/dist/config/recommended-retry-rules.json +128 -11
  8. package/dist/config/recommended.d.ts +1 -0
  9. package/dist/core/concurrency/adaptive-controller.d.ts +2 -0
  10. package/dist/core/concurrency/adaptive-controller.js +32 -27
  11. package/dist/core/concurrency/types.d.ts +0 -1
  12. package/dist/core/monitor/types.d.ts +2 -0
  13. package/dist/db/index.js +42 -0
  14. package/dist/proxy/orchestration/orchestrator.js +2 -0
  15. package/frontend-dist/assets/AuthLayout-DIuO8h2M.js +1 -0
  16. package/frontend-dist/assets/Card-LxyKRado.js +1 -0
  17. package/frontend-dist/assets/CardContent-CBWQC-lR.js +1 -0
  18. package/frontend-dist/assets/{CardTitle-Cs6WQYrl.js → CardTitle-BniGcduK.js} +1 -1
  19. package/frontend-dist/assets/CascadingModelSelect-BRTk0YeA.js +1 -0
  20. package/frontend-dist/assets/{Checkbox-ljNZhtRQ.js → Checkbox-DvzjmCJw.js} +1 -1
  21. package/frontend-dist/assets/{CollapsibleContent-Yk-lxVPc.js → CollapsibleContent-A4N8C4mR.js} +1 -1
  22. package/frontend-dist/assets/CollapsibleTrigger-Buc8T4d6.js +1 -0
  23. package/frontend-dist/assets/ConcurrencyControl-BtxXhdj1.js +1 -0
  24. package/frontend-dist/assets/Dashboard-DaAFcQdM.js +3 -0
  25. package/frontend-dist/assets/{Input-BGoap26P.js → Input-tWeV922G.js} +1 -1
  26. package/frontend-dist/assets/{Label-CR9CcQmY.js → Label-DQdfKRz5.js} +1 -1
  27. package/frontend-dist/assets/Login-BBPvb7VJ.js +1 -0
  28. package/frontend-dist/assets/Logs-jvr7JSJh.js +1 -0
  29. package/frontend-dist/assets/ModelMappings-CGQgK074.js +1 -0
  30. package/frontend-dist/assets/Monitor-MVWHc95e.js +1 -0
  31. package/frontend-dist/assets/Providers-CnHeM61N.js +1 -0
  32. package/frontend-dist/assets/ProxyEnhancement-DrqmJrU2.js +1 -0
  33. package/frontend-dist/assets/QuickSetup-D9LIZD0Z.js +1 -0
  34. package/frontend-dist/assets/RetryRules-DrfgjOVb.js +1 -0
  35. package/frontend-dist/assets/RouterKeys-4VVBtoVF.js +1 -0
  36. package/frontend-dist/assets/RouterKeys-DEMKBc7g.css +1 -0
  37. package/frontend-dist/assets/{RovingFocusItem-C31XRuKO.js → RovingFocusItem-CIyUIO9f.js} +1 -1
  38. package/frontend-dist/assets/Schedules-DBjOuZHU.js +1 -0
  39. package/frontend-dist/assets/Settings-DyOJ-Eb7.js +6 -0
  40. package/frontend-dist/assets/Setup-rm2y3ELe.js +1 -0
  41. package/frontend-dist/assets/Skeleton-Du8wawX9.js +1 -0
  42. package/frontend-dist/assets/{Switch-Dxml8U-u.js → Switch-BTsasjo5.js} +1 -1
  43. package/frontend-dist/assets/TableHeader-CpSiR8jr.js +1 -0
  44. package/frontend-dist/assets/TabsTrigger-5U9reGLu.js +1 -0
  45. package/frontend-dist/assets/TooltipTrigger-Q2S80oM5.js +1 -0
  46. package/frontend-dist/assets/UnifiedRequestDialog-8u2nQl-X.js +3 -0
  47. package/frontend-dist/assets/UnifiedRequestDialog-BRmnLLG1.css +1 -0
  48. package/frontend-dist/assets/{VisuallyHiddenInput-D7dv0owV.js → VisuallyHiddenInput-CvakhEOp.js} +1 -1
  49. package/frontend-dist/assets/_plugin-vue_export-helper-BH2M0_bF.js +1 -0
  50. package/frontend-dist/assets/arrow-down-BOahowLg.js +1 -0
  51. package/frontend-dist/assets/badge-D46rxNhL.js +1 -0
  52. package/frontend-dist/assets/{button-CiJBAzrS.js → button-BfiX_W4f.js} +2 -2
  53. package/frontend-dist/assets/chevron-right-Dh4Rn9QQ.js +1 -0
  54. package/frontend-dist/assets/common-ZLJwmI0c.js +1 -0
  55. package/frontend-dist/assets/common-qa3O5YJX.js +1 -0
  56. package/frontend-dist/assets/dashboard-BlCt9ud6.js +1 -0
  57. package/frontend-dist/assets/dashboard-CqwGLWoK.js +1 -0
  58. package/frontend-dist/assets/dialog-DcICdtQQ.js +1 -0
  59. package/frontend-dist/assets/image-DiqV7Ud1.js +1 -0
  60. package/frontend-dist/assets/index-DunPfXQM.css +1 -0
  61. package/frontend-dist/assets/index-mrYZl4Q6.js +3 -0
  62. package/frontend-dist/assets/logs-BI_TLeBe.js +1 -0
  63. package/frontend-dist/assets/logs-DZyb4-Ew.js +1 -0
  64. package/frontend-dist/assets/mappings-Bw39-I5f.js +1 -0
  65. package/frontend-dist/assets/mappings-DBDiQL8p.js +1 -0
  66. package/frontend-dist/assets/model-patches-C5McZb2c.js +1 -0
  67. package/frontend-dist/assets/monitor-Crmj9XSn.js +1 -0
  68. package/frontend-dist/assets/monitor-D5lXaCoi.js +1 -0
  69. package/frontend-dist/assets/pencil-B-R89QBF.js +1 -0
  70. package/frontend-dist/assets/plus-Bk9a7uAj.js +1 -0
  71. package/frontend-dist/assets/providers-Cy6EVDp5.js +1 -0
  72. package/frontend-dist/assets/providers-DTIaw_44.js +1 -0
  73. package/frontend-dist/assets/proxyEnhancement-BEbUoM8Y.js +3 -0
  74. package/frontend-dist/assets/proxyEnhancement-rdWlqMlw.js +3 -0
  75. package/frontend-dist/assets/quickSetup-CjxI1qgV.js +1 -0
  76. package/frontend-dist/assets/quickSetup-zSIVom2e.js +1 -0
  77. package/frontend-dist/assets/requestDetail-BgwV24CW.js +1 -0
  78. package/frontend-dist/assets/requestDetail-zegFIYZs.js +1 -0
  79. package/frontend-dist/assets/routerKeys-B2_9kz1O.js +1 -0
  80. package/frontend-dist/assets/routerKeys-C579ZS8v.js +1 -0
  81. package/frontend-dist/assets/{schedules-ByzpVhl2.js → schedules-DeR-EOsE.js} +1 -1
  82. package/frontend-dist/assets/schedules-u1et4hyF.js +1 -0
  83. package/frontend-dist/assets/search-DKvomb3i.js +1 -0
  84. package/frontend-dist/assets/{sparkles-DcNGAfEa.js → sparkles-B6anpOdw.js} +1 -1
  85. package/frontend-dist/assets/transform-domain-BnnnpP-1.js +1 -0
  86. package/frontend-dist/assets/{trash-2-C_XOOsld.js → trash-2-EHg5iA2L.js} +1 -1
  87. package/frontend-dist/assets/useClipboard-DJ4czDOj.js +1 -0
  88. package/frontend-dist/assets/useLogRetention-Hr_-c1Nt.js +1 -0
  89. package/frontend-dist/assets/useProviderGroups-fkmNjicN.js +1 -0
  90. package/frontend-dist/index.html +4 -4
  91. package/package.json +1 -1
  92. package/frontend-dist/assets/CardContent-BU3fjxiM.js +0 -1
  93. package/frontend-dist/assets/CascadingModelSelect-B_n7DYfi.js +0 -1
  94. package/frontend-dist/assets/CollapsibleTrigger-8_A4LNtD.js +0 -1
  95. package/frontend-dist/assets/Dashboard-C5oqC_3e.js +0 -3
  96. package/frontend-dist/assets/Login-CM-J-SVi.js +0 -1
  97. package/frontend-dist/assets/Logs-CSZi8Fkp.js +0 -1
  98. package/frontend-dist/assets/MappingEntryEditor-Dng2wCdx.js +0 -1
  99. package/frontend-dist/assets/ModelMappings-_ONUTtBQ.js +0 -1
  100. package/frontend-dist/assets/Monitor-o28u2RV9.js +0 -1
  101. package/frontend-dist/assets/Providers-awOg4hbb.js +0 -1
  102. package/frontend-dist/assets/ProxyEnhancement-DV-dfZLk.js +0 -1
  103. package/frontend-dist/assets/QuickSetup-XvyghGod.js +0 -1
  104. package/frontend-dist/assets/RetryRules-EnfS-FTJ.js +0 -1
  105. package/frontend-dist/assets/RouterKeys-BzcUK5x_.js +0 -1
  106. package/frontend-dist/assets/Schedules-BuP-3qX8.js +0 -1
  107. package/frontend-dist/assets/Settings-hBsvQQWG.js +0 -6
  108. package/frontend-dist/assets/Setup-CukhOjX3.js +0 -1
  109. package/frontend-dist/assets/TooltipTrigger-BITX1gT4.js +0 -1
  110. package/frontend-dist/assets/TransformRulesForm-DFW-oDll.js +0 -1
  111. package/frontend-dist/assets/UnifiedRequestDialog-C3sWdkTe.css +0 -1
  112. package/frontend-dist/assets/UnifiedRequestDialog-DChZe4do.js +0 -3
  113. package/frontend-dist/assets/common-BCe8fK5N.js +0 -1
  114. package/frontend-dist/assets/common-CdzdKRsV.js +0 -1
  115. package/frontend-dist/assets/copy-C035pTD9.js +0 -1
  116. package/frontend-dist/assets/dashboard-C0XEgNnb.js +0 -1
  117. package/frontend-dist/assets/dashboard-uBv6TkGn.js +0 -1
  118. package/frontend-dist/assets/dialog-QrJZom9g.js +0 -1
  119. package/frontend-dist/assets/index-B_tA9Si9.css +0 -1
  120. package/frontend-dist/assets/index-bv08h-oy.js +0 -3
  121. package/frontend-dist/assets/logs-C3X3MZjQ.js +0 -1
  122. package/frontend-dist/assets/logs-HY8sUOwb.js +0 -1
  123. package/frontend-dist/assets/mappings-B6NCfNio.js +0 -1
  124. package/frontend-dist/assets/mappings-yo-RxHK6.js +0 -1
  125. package/frontend-dist/assets/model-patches-1yALrtK3.js +0 -1
  126. package/frontend-dist/assets/monitor-CcfwHbJF.js +0 -1
  127. package/frontend-dist/assets/monitor-XNX6LZGC.js +0 -1
  128. package/frontend-dist/assets/plus-7SwZDVUb.js +0 -1
  129. package/frontend-dist/assets/providers-BaiCysMg.js +0 -1
  130. package/frontend-dist/assets/providers-DsC5Qd6m.js +0 -1
  131. package/frontend-dist/assets/proxyEnhancement-BC3eVKDp.js +0 -3
  132. package/frontend-dist/assets/proxyEnhancement-Dq47MAAI.js +0 -3
  133. package/frontend-dist/assets/quickSetup-C8jt6VzA.js +0 -1
  134. package/frontend-dist/assets/quickSetup-tMWsXvo3.js +0 -1
  135. package/frontend-dist/assets/requestDetail-C5px2Mru.js +0 -1
  136. package/frontend-dist/assets/requestDetail-Tozq3jwE.js +0 -1
  137. package/frontend-dist/assets/routerKeys-C4oCPJrT.js +0 -1
  138. package/frontend-dist/assets/routerKeys-CyZ4L-1h.js +0 -1
  139. package/frontend-dist/assets/schedules-tSjne6S-.js +0 -1
  140. package/frontend-dist/assets/useClipboard-DaHSM2Ce.js +0 -1
  141. package/frontend-dist/assets/useLogRetention-D7MYPV9z.js +0 -1
  142. /package/frontend-dist/assets/{login-DXMVxesL.js → login-BjRWJuuH.js} +0 -0
  143. /package/frontend-dist/assets/{login-BozjQrft.js → login-Drdlb6If.js} +0 -0
  144. /package/frontend-dist/assets/{retryRules-DGg26acb.js → retryRules-KY-2u2K-.js} +0 -0
  145. /package/frontend-dist/assets/{retryRules-CR3EIOTr.js → retryRules-v9HTZUGQ.js} +0 -0
  146. /package/frontend-dist/assets/{settings-DQqli04G.js → settings-av7tA8wt.js} +0 -0
  147. /package/frontend-dist/assets/{settings-CveTb9z9.js → settings-dzu7sg6b.js} +0 -0
  148. /package/frontend-dist/assets/{setup-BKw9vkV3.js → setup-B0bSyAH4.js} +0 -0
  149. /package/frontend-dist/assets/{setup-D9yk-VSM.js → setup-Bw-U-YJa.js} +0 -0
  150. /package/frontend-dist/assets/{sidebar-T-fjy0Pb.js → sidebar-BxTRaJDZ.js} +0 -0
  151. /package/frontend-dist/assets/{sidebar-HuUg5Wsk.js → sidebar-Dl1-HY8Q.js} +0 -0
@@ -24,7 +24,8 @@
24
24
  "deepseek-v4-pro"
25
25
  ]
26
26
  }
27
- ]
27
+ ],
28
+ "shortname": "deepseek"
28
29
  },
29
30
  {
30
31
  "group": "百度千帆",
@@ -47,7 +48,8 @@
47
48
  ],
48
49
  "upstreamPath": "/chat/completions"
49
50
  }
50
- ]
51
+ ],
52
+ "shortname": "qianfan"
51
53
  },
52
54
  {
53
55
  "group": "科大讯飞",
@@ -67,7 +69,8 @@
67
69
  "lite"
68
70
  ]
69
71
  }
70
- ]
72
+ ],
73
+ "shortname": "iflytek"
71
74
  },
72
75
  {
73
76
  "group": "硅基流动",
@@ -88,7 +91,8 @@
88
91
  "moonshotai/Kimi-K2.5"
89
92
  ]
90
93
  }
91
- ]
94
+ ],
95
+ "shortname": "siliconflow"
92
96
  },
93
97
  {
94
98
  "group": "智谱",
@@ -139,7 +143,8 @@
139
143
  "glm-4.6"
140
144
  ]
141
145
  }
142
- ]
146
+ ],
147
+ "shortname": "zhipu"
143
148
  },
144
149
  {
145
150
  "group": "月之暗面",
@@ -169,7 +174,8 @@
169
174
  "moonshot-v1-128k"
170
175
  ]
171
176
  }
172
- ]
177
+ ],
178
+ "shortname": "moonshot"
173
179
  },
174
180
  {
175
181
  "group": "Minimax",
@@ -199,7 +205,8 @@
199
205
  "MiniMax-M2"
200
206
  ]
201
207
  }
202
- ]
208
+ ],
209
+ "shortname": "minimax"
203
210
  },
204
211
  {
205
212
  "group": "火山引擎",
@@ -230,7 +237,8 @@
230
237
  "doubao-seed-code-preview-251028"
231
238
  ]
232
239
  }
233
- ]
240
+ ],
241
+ "shortname": "volcengine"
234
242
  },
235
243
  {
236
244
  "group": "阿里云",
@@ -264,7 +272,8 @@
264
272
  "qwen3-coder-next"
265
273
  ]
266
274
  }
267
- ]
275
+ ],
276
+ "shortname": "aliyun"
268
277
  },
269
278
  {
270
279
  "group": "腾讯云",
@@ -299,7 +308,8 @@
299
308
  "hunyuan-turbos-latest"
300
309
  ]
301
310
  }
302
- ]
311
+ ],
312
+ "shortname": "tencent"
303
313
  },
304
314
  {
305
315
  "group": "OpenCode",
@@ -335,7 +345,8 @@
335
345
  "minimax-m2.5"
336
346
  ]
337
347
  }
338
- ]
348
+ ],
349
+ "shortname": "opencode"
339
350
  },
340
351
  {
341
352
  "group": "阶跃星辰",
@@ -366,6 +377,7 @@
366
377
  "step-1-32k"
367
378
  ]
368
379
  }
369
- ]
380
+ ],
381
+ "shortname": "stepfun"
370
382
  }
371
383
  ]
@@ -1,13 +1,130 @@
1
1
  [
2
- { "name": "429 Too Many Requests", "status_code": 429, "body_pattern": ".*", "retry_strategy": "exponential", "retry_delay_ms": 5000, "max_retries": 10, "max_delay_ms": 60000, "providers": [] },
3
- { "name": "503 Service Unavailable", "status_code": 503, "body_pattern": ".*", "retry_strategy": "exponential", "retry_delay_ms": 5000, "max_retries": 10, "max_delay_ms": 60000, "providers": [] },
4
- { "name": "ZAI 网络错误 (code 1234)", "status_code": 400, "body_pattern": "\"type\"\\s*:\\s*\"error\".*\"code\"\\s*:\\s*\"1234\"", "retry_strategy": "exponential", "retry_delay_ms": 5000, "max_retries": 10, "max_delay_ms": 60000, "providers": ["智谱"] },
5
- { "name": "ZAI 临时不可用", "status_code": 400, "body_pattern": "\"type\"\\s*:\\s*\"error\".*请稍后重试", "retry_strategy": "exponential", "retry_delay_ms": 5000, "max_retries": 10, "max_delay_ms": 60000, "providers": ["智谱"] },
6
- { "name": "ZAI 操作失败 (code 500)", "status_code": 400, "body_pattern": "\"type\"\\s*:\\s*\"error\".*\"code\"\\s*:\\s*\"500\"", "retry_strategy": "exponential", "retry_delay_ms": 5000, "max_retries": 10, "max_delay_ms": 60000, "providers": ["智谱"] },
7
- { "name": "ZAI 速率限制 (HTTP 200, code 1302)", "status_code": 200, "body_pattern": "\"error\".*\"code\"\\s*:\\s*\"1302\"", "retry_strategy": "exponential", "retry_delay_ms": 5000, "max_retries": 10, "max_delay_ms": 60000, "providers": ["智谱"] },
8
- { "name": "ZAI SSE 错误 (HTTP 200, code 500)", "status_code": 200, "body_pattern": "\"error\".*\"code\"\\s*:\\s*\"500\"", "retry_strategy": "exponential", "retry_delay_ms": 5000, "max_retries": 10, "max_delay_ms": 60000, "providers": ["智谱"] },
9
- { "name": "ZAI SSE 错误 (HTTP 200, code 1234)", "status_code": 200, "body_pattern": "\"error\".*\"code\"\\s*:\\s*\"1234\"", "retry_strategy": "exponential", "retry_delay_ms": 5000, "max_retries": 10, "max_delay_ms": 60000, "providers": ["智谱"] },
10
- { "name": "ZAI 模型过载 (HTTP 200, code 1305)", "status_code": 200, "body_pattern": "\"error\".*\"code\"\\s*:\\s*\"1305\"", "retry_strategy": "exponential", "retry_delay_ms": 5000, "max_retries": 10, "max_delay_ms": 60000, "providers": ["智谱"] },
11
- { "name": "KIMI 401 认证错误", "status_code": 401, "body_pattern": ".*authentication_error.*", "retry_strategy": "exponential", "retry_delay_ms": 5000, "max_retries": 3, "max_delay_ms": 60000, "providers": ["月之暗面"] },
12
- { "name": "DeepSeek 并发限流 (429)", "status_code": 429, "body_pattern": "Too many requests.*concurrency", "retry_strategy": "exponential", "retry_delay_ms": 2000, "max_retries": 5, "max_delay_ms": 120000, "providers": ["DeepSeek", "OpenCode"] }
2
+ {
3
+ "name": "429 Too Many Requests",
4
+ "status_code": 429,
5
+ "body_pattern": ".*",
6
+ "retry_strategy": "exponential",
7
+ "retry_delay_ms": 5000,
8
+ "max_retries": 10,
9
+ "max_delay_ms": 60000,
10
+ "providers": []
11
+ },
12
+ {
13
+ "name": "503 Service Unavailable",
14
+ "status_code": 503,
15
+ "body_pattern": ".*",
16
+ "retry_strategy": "exponential",
17
+ "retry_delay_ms": 5000,
18
+ "max_retries": 10,
19
+ "max_delay_ms": 60000,
20
+ "providers": []
21
+ },
22
+ {
23
+ "name": "ZAI 网络错误 (code 1234)",
24
+ "status_code": 400,
25
+ "body_pattern": "\"type\"\\s*:\\s*\"error\".*\"code\"\\s*:\\s*\"1234\"",
26
+ "retry_strategy": "exponential",
27
+ "retry_delay_ms": 5000,
28
+ "max_retries": 10,
29
+ "max_delay_ms": 60000,
30
+ "providers": [
31
+ "zhipu"
32
+ ]
33
+ },
34
+ {
35
+ "name": "ZAI 临时不可用",
36
+ "status_code": 400,
37
+ "body_pattern": "\"type\"\\s*:\\s*\"error\".*请稍后重试",
38
+ "retry_strategy": "exponential",
39
+ "retry_delay_ms": 5000,
40
+ "max_retries": 10,
41
+ "max_delay_ms": 60000,
42
+ "providers": [
43
+ "zhipu"
44
+ ]
45
+ },
46
+ {
47
+ "name": "ZAI 操作失败 (code 500)",
48
+ "status_code": 400,
49
+ "body_pattern": "\"type\"\\s*:\\s*\"error\".*\"code\"\\s*:\\s*\"500\"",
50
+ "retry_strategy": "exponential",
51
+ "retry_delay_ms": 5000,
52
+ "max_retries": 10,
53
+ "max_delay_ms": 60000,
54
+ "providers": [
55
+ "zhipu"
56
+ ]
57
+ },
58
+ {
59
+ "name": "ZAI 速率限制 (HTTP 200, code 1302)",
60
+ "status_code": 200,
61
+ "body_pattern": "\"error\".*\"code\"\\s*:\\s*\"1302\"",
62
+ "retry_strategy": "exponential",
63
+ "retry_delay_ms": 5000,
64
+ "max_retries": 10,
65
+ "max_delay_ms": 60000,
66
+ "providers": [
67
+ "zhipu"
68
+ ]
69
+ },
70
+ {
71
+ "name": "ZAI SSE 错误 (HTTP 200, code 500)",
72
+ "status_code": 200,
73
+ "body_pattern": "\"error\".*\"code\"\\s*:\\s*\"500\"",
74
+ "retry_strategy": "exponential",
75
+ "retry_delay_ms": 5000,
76
+ "max_retries": 10,
77
+ "max_delay_ms": 60000,
78
+ "providers": [
79
+ "zhipu"
80
+ ]
81
+ },
82
+ {
83
+ "name": "ZAI SSE 错误 (HTTP 200, code 1234)",
84
+ "status_code": 200,
85
+ "body_pattern": "\"error\".*\"code\"\\s*:\\s*\"1234\"",
86
+ "retry_strategy": "exponential",
87
+ "retry_delay_ms": 5000,
88
+ "max_retries": 10,
89
+ "max_delay_ms": 60000,
90
+ "providers": [
91
+ "zhipu"
92
+ ]
93
+ },
94
+ {
95
+ "name": "ZAI 模型过载 (HTTP 200, code 1305)",
96
+ "status_code": 200,
97
+ "body_pattern": "\"error\".*\"code\"\\s*:\\s*\"1305\"",
98
+ "retry_strategy": "exponential",
99
+ "retry_delay_ms": 5000,
100
+ "max_retries": 10,
101
+ "max_delay_ms": 60000,
102
+ "providers": [
103
+ "zhipu"
104
+ ]
105
+ },
106
+ {
107
+ "name": "KIMI 401 认证错误",
108
+ "status_code": 401,
109
+ "body_pattern": ".*authentication_error.*",
110
+ "retry_strategy": "exponential",
111
+ "retry_delay_ms": 5000,
112
+ "max_retries": 3,
113
+ "max_delay_ms": 60000,
114
+ "providers": [
115
+ "moonshot"
116
+ ]
117
+ },
118
+ {
119
+ "name": "DeepSeek 并发限流 (429)",
120
+ "status_code": 429,
121
+ "body_pattern": "Too many requests.*concurrency",
122
+ "retry_strategy": "exponential",
123
+ "retry_delay_ms": 2000,
124
+ "max_retries": 5,
125
+ "max_delay_ms": 120000,
126
+ "providers": [
127
+ "opencode"
128
+ ]
129
+ }
13
130
  ]
@@ -496,7 +496,7 @@ export const adminProviderRoutes = (app, options, done) => {
496
496
  base_url: Type.String({ minLength: 1 }),
497
497
  models_endpoint: Type.String({ minLength: 1 }),
498
498
  api_key: Type.String({ minLength: 1 }),
499
- api_type: Type.Union([Type.Literal("openai"), Type.Literal("anthropic")]),
499
+ api_type: Type.Union([Type.Literal("openai"), Type.Literal("openai-responses"), Type.Literal("anthropic")]),
500
500
  });
501
501
  app.post("/admin/api/providers/fetch-models", { schema: { body: FetchModelsSchema } }, async (request, reply) => {
502
502
  const { base_url, models_endpoint, api_key, api_type } = request.body;
@@ -1,5 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
- import { createProvider } from "../db/providers.js";
2
+ import { createProvider, PROVIDER_CONCURRENCY_DEFAULTS } from "../db/providers.js";
3
3
  import { createMappingGroup, updateMappingGroup } from "../db/mappings.js";
4
4
  import { createRetryRule } from "../db/retry-rules.js";
5
5
  import { upsertTransformRule } from "../db/transform-rules.js";
@@ -7,10 +7,36 @@ import { encrypt } from "../utils/crypto.js";
7
7
  import { getSetting } from "../db/settings.js";
8
8
  import { HTTP_CREATED, HTTP_BAD_REQUEST, HTTP_CONFLICT } from "./constants.js";
9
9
  import { API_CODE, apiError } from "./api-response.js";
10
- import { PROVIDER_CONCURRENCY_DEFAULTS } from "../db/providers.js";
11
10
  const PROVIDER_NAME_RE = /^[a-zA-Z0-9_-]+$/;
12
11
  const API_KEY_PREVIEW_MIN_LENGTH = 8;
13
12
  const API_KEY_PREVIEW_PREFIX_LEN = 4;
13
+ const NEW_PROVIDER_ID = "__new__";
14
+ /** Recursively replace "__new__" provider_id values with the actual provider ID */
15
+ function replaceProviderIds(obj, providerId) {
16
+ if (Array.isArray(obj)) {
17
+ return obj.map((item) => replaceProviderIds(item, providerId));
18
+ }
19
+ if (obj !== null && typeof obj === "object") {
20
+ const result = {};
21
+ for (const [key, value] of Object.entries(obj)) {
22
+ if ((key === "provider_id" || key === "overflow_provider_id") &&
23
+ (value === NEW_PROVIDER_ID || value === "")) {
24
+ result[key] = providerId;
25
+ }
26
+ else {
27
+ result[key] = replaceProviderIds(value, providerId);
28
+ }
29
+ }
30
+ return result;
31
+ }
32
+ return obj;
33
+ }
34
+ const QuickSetupEndpointSchema = Type.Object({
35
+ api_type: Type.Union([Type.Literal("openai"), Type.Literal("openai-responses"), Type.Literal("anthropic")]),
36
+ base_url: Type.String({ minLength: 1 }),
37
+ upstream_path: Type.Optional(Type.Union([Type.String({ minLength: 1 }), Type.Null()])),
38
+ api_key: Type.Optional(Type.Union([Type.String({ minLength: 1 }), Type.Null()])),
39
+ });
14
40
  const QuickSetupProviderSchema = Type.Object({
15
41
  name: Type.String({ minLength: 1 }),
16
42
  api_type: Type.Union([Type.Literal("openai"), Type.Literal("openai-responses"), Type.Literal("anthropic")]),
@@ -21,7 +47,10 @@ const QuickSetupProviderSchema = Type.Object({
21
47
  name: Type.String(),
22
48
  context_window: Type.Optional(Type.Number()),
23
49
  patches: Type.Optional(Type.Array(Type.String())),
50
+ stream_timeout_ms: Type.Optional(Type.Number()),
51
+ capabilities: Type.Optional(Type.Array(Type.String())),
24
52
  })),
53
+ endpoints: Type.Optional(Type.Array(QuickSetupEndpointSchema, { minItems: 1 })),
25
54
  concurrency_mode: Type.Optional(Type.Union([Type.Literal("auto"), Type.Literal("manual"), Type.Literal("none")])),
26
55
  max_concurrency: Type.Optional(Type.Number()),
27
56
  queue_timeout_ms: Type.Optional(Type.Number()),
@@ -30,6 +59,9 @@ const QuickSetupProviderSchema = Type.Object({
30
59
  const QuickSetupMappingSchema = Type.Object({
31
60
  client_model: Type.String({ minLength: 1 }),
32
61
  backend_model: Type.String({ minLength: 1 }),
62
+ /** Optional pre-built rule JSON (targets, overflow, multimodal_fallback).
63
+ * If provided, provider_id fields are replaced with the newly created provider's ID. */
64
+ rule: Type.Optional(Type.String({ minLength: 1 })),
33
65
  });
34
66
  const QuickSetupRetryRuleSchema = Type.Object({
35
67
  name: Type.String({ minLength: 1 }),
@@ -39,6 +71,7 @@ const QuickSetupRetryRuleSchema = Type.Object({
39
71
  retry_delay_ms: Type.Number({ minimum: 100 }),
40
72
  max_retries: Type.Number({ minimum: 0, maximum: 100 }),
41
73
  max_delay_ms: Type.Number({ minimum: 100 }),
74
+ provider_shortname: Type.Optional(Type.Union([Type.String(), Type.Null()])),
42
75
  });
43
76
  const QuickSetupTransformSchema = Type.Object({
44
77
  inject_headers: Type.Optional(Type.Record(Type.String(), Type.String())),
@@ -82,6 +115,8 @@ export const adminQuickSetupRoutes = (app, options, done) => {
82
115
  name: m.name,
83
116
  ...(m.context_window != null ? { context_window: m.context_window } : {}),
84
117
  ...(m.patches && m.patches.length > 0 ? { patches: m.patches } : {}),
118
+ ...(m.stream_timeout_ms != null ? { stream_timeout_ms: m.stream_timeout_ms } : {}),
119
+ ...(m.capabilities && m.capabilities.length > 0 ? { capabilities: m.capabilities } : {}),
85
120
  }));
86
121
  const adaptiveEnabled = body.provider.concurrency_mode === 'auto' ? 1 : 0;
87
122
  const maxConcurrency = body.provider.max_concurrency ?? PROVIDER_CONCURRENCY_DEFAULTS.max_concurrency;
@@ -102,13 +137,31 @@ export const adminQuickSetupRoutes = (app, options, done) => {
102
137
  queue_timeout_ms: queueTimeoutMs,
103
138
  max_queue_size: maxQueueSize,
104
139
  adaptive_enabled: adaptiveEnabled,
140
+ ...(body.provider.endpoints && body.provider.endpoints.length > 0
141
+ ? {
142
+ endpoints: JSON.stringify(body.provider.endpoints.map(ep => ({
143
+ api_type: ep.api_type,
144
+ base_url: ep.base_url,
145
+ upstream_path: ep.upstream_path ?? null,
146
+ api_key: ep.api_key ? encrypt(ep.api_key, encryptionKey) : (body.provider.api_key ? encrypt(body.provider.api_key, encryptionKey) : null),
147
+ }))),
148
+ }
149
+ : {}),
105
150
  });
106
151
  // 6. Upsert mapping groups
107
152
  for (const m of body.mappings) {
108
153
  const existing = db.prepare('SELECT id FROM mapping_groups WHERE client_model = ?').get(m.client_model);
109
- const ruleJson = JSON.stringify({
110
- targets: [{ backend_model: m.backend_model, provider_id: providerId }],
111
- });
154
+ let ruleJson;
155
+ if (m.rule) {
156
+ // Replace provider_id placeholders with the newly created provider's ID
157
+ const ruleObj = JSON.parse(m.rule);
158
+ ruleJson = JSON.stringify(replaceProviderIds(ruleObj, providerId));
159
+ }
160
+ else {
161
+ ruleJson = JSON.stringify({
162
+ targets: [{ backend_model: m.backend_model, provider_id: providerId }],
163
+ });
164
+ }
112
165
  if (existing) {
113
166
  updateMappingGroup(db, existing.id, {
114
167
  client_model: m.client_model,
@@ -122,8 +175,9 @@ export const adminQuickSetupRoutes = (app, options, done) => {
122
175
  });
123
176
  }
124
177
  }
125
- // 7. Create retry rules
178
+ // 7. Create retry rules (bind to newly created provider if shortname matches)
126
179
  for (const r of body.retry_rules) {
180
+ const ruleProviderId = r.provider_shortname ? providerId : null;
127
181
  createRetryRule(db, {
128
182
  name: r.name,
129
183
  status_code: r.status_code,
@@ -133,6 +187,7 @@ export const adminQuickSetupRoutes = (app, options, done) => {
133
187
  retry_delay_ms: r.retry_delay_ms,
134
188
  max_retries: r.max_retries,
135
189
  max_delay_ms: r.max_delay_ms,
190
+ provider_id: ruleProviderId,
136
191
  });
137
192
  }
138
193
  // 8. Create transform rules
@@ -5,6 +5,8 @@ import { resolveTimeRange } from "../utils/time-range.js";
5
5
  const UsageQuerySchema = Type.Object({
6
6
  router_key_id: Type.Optional(Type.String()),
7
7
  provider_id: Type.Optional(Type.String()),
8
+ start_time: Type.Optional(Type.String()),
9
+ end_time: Type.Optional(Type.String()),
8
10
  });
9
11
  function getDailyUsage(db, startTime, endTime, routerKeyId, providerId) {
10
12
  const conditions = [
@@ -41,23 +43,28 @@ export const adminUsageRoutes = (app, options, done) => {
41
43
  const { db } = options;
42
44
  app.get("/admin/api/usage/windows", { schema: { querystring: UsageQuerySchema } }, async (request) => {
43
45
  const query = request.query;
44
- if (query.provider_id) {
46
+ let startTime;
47
+ let endTime;
48
+ if (query.start_time && query.end_time) {
49
+ startTime = query.start_time;
50
+ endTime = query.end_time;
51
+ }
52
+ else if (query.provider_id) {
45
53
  const range = resolveTimeRange("window", db, query.router_key_id, query.provider_id);
46
- const windows = getWindowsInRange(db, range.startTime, range.endTime, query.router_key_id, query.provider_id);
47
- if (windows.length === 0)
48
- return [];
49
- return windows.map(w => ({
50
- window: { ...w, provider_name: resolveProviderName(db, w.provider_id) },
51
- usage: getWindowUsage(db, w.start_time, w.end_time, query.router_key_id, query.provider_id),
52
- }));
54
+ startTime = range.startTime;
55
+ endTime = range.endTime;
56
+ }
57
+ else {
58
+ startTime = "1970-01-01";
59
+ endTime = "2099-12-31";
53
60
  }
54
- const allWindows = getWindowsInRange(db, "1970-01-01", "2099-12-31", query.router_key_id)
61
+ const windows = getWindowsInRange(db, startTime, endTime, query.router_key_id, query.provider_id)
55
62
  .filter((w) => w.provider_id !== null);
56
- if (allWindows.length === 0)
63
+ if (windows.length === 0)
57
64
  return [];
58
- return allWindows.map(w => ({
65
+ return windows.map(w => ({
59
66
  window: { ...w, provider_name: resolveProviderName(db, w.provider_id) },
60
- usage: getWindowUsage(db, w.start_time, w.end_time, query.router_key_id),
67
+ usage: getWindowUsage(db, w.start_time, w.end_time, query.router_key_id, w.provider_id),
61
68
  }));
62
69
  });
63
70
  app.get("/admin/api/usage/weekly", { schema: { querystring: UsageQuerySchema } }, async (request) => {
@@ -24,7 +24,8 @@
24
24
  "deepseek-v4-pro"
25
25
  ]
26
26
  }
27
- ]
27
+ ],
28
+ "shortname": "deepseek"
28
29
  },
29
30
  {
30
31
  "group": "百度千帆",
@@ -47,7 +48,8 @@
47
48
  ],
48
49
  "upstreamPath": "/chat/completions"
49
50
  }
50
- ]
51
+ ],
52
+ "shortname": "qianfan"
51
53
  },
52
54
  {
53
55
  "group": "科大讯飞",
@@ -67,7 +69,8 @@
67
69
  "lite"
68
70
  ]
69
71
  }
70
- ]
72
+ ],
73
+ "shortname": "iflytek"
71
74
  },
72
75
  {
73
76
  "group": "硅基流动",
@@ -88,7 +91,8 @@
88
91
  "moonshotai/Kimi-K2.5"
89
92
  ]
90
93
  }
91
- ]
94
+ ],
95
+ "shortname": "siliconflow"
92
96
  },
93
97
  {
94
98
  "group": "智谱",
@@ -139,7 +143,8 @@
139
143
  "glm-4.6"
140
144
  ]
141
145
  }
142
- ]
146
+ ],
147
+ "shortname": "zhipu"
143
148
  },
144
149
  {
145
150
  "group": "月之暗面",
@@ -169,7 +174,8 @@
169
174
  "moonshot-v1-128k"
170
175
  ]
171
176
  }
172
- ]
177
+ ],
178
+ "shortname": "moonshot"
173
179
  },
174
180
  {
175
181
  "group": "Minimax",
@@ -199,7 +205,8 @@
199
205
  "MiniMax-M2"
200
206
  ]
201
207
  }
202
- ]
208
+ ],
209
+ "shortname": "minimax"
203
210
  },
204
211
  {
205
212
  "group": "火山引擎",
@@ -230,7 +237,8 @@
230
237
  "doubao-seed-code-preview-251028"
231
238
  ]
232
239
  }
233
- ]
240
+ ],
241
+ "shortname": "volcengine"
234
242
  },
235
243
  {
236
244
  "group": "阿里云",
@@ -264,7 +272,8 @@
264
272
  "qwen3-coder-next"
265
273
  ]
266
274
  }
267
- ]
275
+ ],
276
+ "shortname": "aliyun"
268
277
  },
269
278
  {
270
279
  "group": "腾讯云",
@@ -299,7 +308,8 @@
299
308
  "hunyuan-turbos-latest"
300
309
  ]
301
310
  }
302
- ]
311
+ ],
312
+ "shortname": "tencent"
303
313
  },
304
314
  {
305
315
  "group": "OpenCode",
@@ -335,7 +345,8 @@
335
345
  "minimax-m2.5"
336
346
  ]
337
347
  }
338
- ]
348
+ ],
349
+ "shortname": "opencode"
339
350
  },
340
351
  {
341
352
  "group": "阶跃星辰",
@@ -366,6 +377,7 @@
366
377
  "step-1-32k"
367
378
  ]
368
379
  }
369
- ]
380
+ ],
381
+ "shortname": "stepfun"
370
382
  }
371
383
  ]