llm-simple-router 0.9.5 → 0.9.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.
Files changed (200) hide show
  1. package/config/recommended-providers.json +28 -9
  2. package/config/version.json +1 -4
  3. package/dist/admin/monitor.d.ts +1 -1
  4. package/dist/admin/monitor.js +4 -2
  5. package/dist/admin/providers.d.ts +3 -3
  6. package/dist/admin/providers.js +5 -0
  7. package/dist/admin/quick-setup.d.ts +3 -3
  8. package/dist/admin/quick-setup.js +2 -0
  9. package/dist/admin/routes.d.ts +3 -3
  10. package/dist/admin/upgrade.js +11 -12
  11. package/dist/config/recommended.d.ts +2 -1
  12. package/dist/config/recommended.js +17 -15
  13. package/dist/core/errors.d.ts +1 -15
  14. package/dist/core/errors.js +3 -25
  15. package/dist/core/pino-logger.d.ts +4 -0
  16. package/dist/core/pino-logger.js +8 -0
  17. package/dist/core/registry.d.ts +7 -7
  18. package/dist/core/sse-client-adapter.d.ts +3 -0
  19. package/dist/core/sse-client-adapter.js +13 -0
  20. package/dist/core/types.d.ts +1 -6
  21. package/dist/db/providers.d.ts +3 -1
  22. package/dist/db/providers.js +3 -3
  23. package/dist/index.d.ts +3 -4
  24. package/dist/index.js +12 -11
  25. package/dist/proxy/handler/proxy-handler-utils.d.ts +2 -2
  26. package/dist/proxy/handler/proxy-handler.js +6 -2
  27. package/dist/proxy/orchestration/orchestrator.d.ts +5 -5
  28. package/dist/proxy/orchestration/scope.d.ts +4 -4
  29. package/dist/proxy/transport/stream.d.ts +1 -1
  30. package/dist/proxy/transport/transport-fn.d.ts +1 -1
  31. package/dist/proxy/transport/transport-fn.js +3 -3
  32. package/dist/storage/log-file-compressor.js +3 -3
  33. package/dist/storage/log-file-writer.js +3 -3
  34. package/dist/storage/types.d.ts +1 -1
  35. package/dist/storage/types.js +4 -4
  36. package/dist/upgrade/checker.js +6 -3
  37. package/package.json +3 -22
  38. package/README.en.md +0 -319
  39. package/README.md +0 -319
  40. package/dist/db/migrations/001_init.sql +0 -37
  41. package/dist/db/migrations/002_add_request_response_body.sql +0 -2
  42. package/dist/db/migrations/003_add_full_request_chain_log.sql +0 -4
  43. package/dist/db/migrations/004_rename_to_providers.sql +0 -9
  44. package/dist/db/migrations/005_add_api_key_preview.sql +0 -1
  45. package/dist/db/migrations/006_create_request_metrics.sql +0 -20
  46. package/dist/db/migrations/007_add_retry_fields.sql +0 -2
  47. package/dist/db/migrations/008_create_router_keys.sql +0 -17
  48. package/dist/db/migrations/009_add_request_logs_indexes.sql +0 -2
  49. package/dist/db/migrations/010_add_key_encrypted.sql +0 -1
  50. package/dist/db/migrations/011_create_mapping_groups.sql +0 -37
  51. package/dist/db/migrations/012_add_provider_models.sql +0 -2
  52. package/dist/db/migrations/013_add_retry_strategy.sql +0 -4
  53. package/dist/db/migrations/014_create_settings.sql +0 -4
  54. package/dist/db/migrations/015_add_original_model.sql +0 -1
  55. package/dist/db/migrations/016_create_session_model_tables.sql +0 -24
  56. package/dist/db/migrations/017_add_provider_concurrency.sql +0 -3
  57. package/dist/db/migrations/018_add_failover_field.sql +0 -2
  58. package/dist/db/migrations/019_create_usage_windows.sql +0 -11
  59. package/dist/db/migrations/020_drop_log_redundancy.sql +0 -8
  60. package/dist/db/migrations/021_merge_metrics_columns.sql +0 -28
  61. package/dist/db/migrations/022_add_session_id_and_incremental_vacuum.sql +0 -5
  62. package/dist/db/migrations/023_create_provider_model_info.sql +0 -8
  63. package/dist/db/migrations/024_add_mapping_groups_is_active.sql +0 -4
  64. package/dist/db/migrations/025_add_client_status_code.sql +0 -3
  65. package/dist/db/migrations/026_create_schedules_simplify_mappings.sql +0 -64
  66. package/dist/db/migrations/027_metrics_independent.sql +0 -54
  67. package/dist/db/migrations/028_ensure_strategy_column.sql +0 -11
  68. package/dist/db/migrations/029_convert_old_rule_format.sql +0 -7
  69. package/dist/db/migrations/030_add_input_tokens_estimated.sql +0 -6
  70. package/dist/db/migrations/031_add_tps_breakdown.sql +0 -13
  71. package/dist/db/migrations/032_add_non_thinking_tps.sql +0 -3
  72. package/dist/db/migrations/033_add_adaptive_concurrency.sql +0 -6
  73. package/dist/db/migrations/034_create_provider_transform_rules.sql +0 -11
  74. package/dist/db/migrations/035_drop_redundant_log_columns.sql +0 -13
  75. package/dist/db/migrations/036_add_openai_responses_api_type.sql +0 -68
  76. package/dist/db/migrations/037_fix_035_data_corruption.sql +0 -54
  77. package/dist/monitor/request-tracker.d.ts +0 -70
  78. package/dist/monitor/request-tracker.js +0 -303
  79. package/dist/monitor/runtime-collector.d.ts +0 -11
  80. package/dist/monitor/runtime-collector.js +0 -41
  81. package/dist/monitor/stats-aggregator.d.ts +0 -22
  82. package/dist/monitor/stats-aggregator.js +0 -167
  83. package/dist/monitor/stream-content-accumulator.d.ts +0 -14
  84. package/dist/monitor/stream-content-accumulator.js +0 -58
  85. package/dist/monitor/stream-extractor.d.ts +0 -11
  86. package/dist/monitor/stream-extractor.js +0 -72
  87. package/dist/monitor/types.d.ts +0 -106
  88. package/dist/monitor/types.js +0 -1
  89. package/dist/proxy/adaptive-controller.d.ts +0 -45
  90. package/dist/proxy/adaptive-controller.js +0 -144
  91. package/dist/proxy/loop-prevention/detectors/detector.d.ts +0 -10
  92. package/dist/proxy/loop-prevention/detectors/detector.js +0 -1
  93. package/dist/proxy/loop-prevention/detectors/ngram-detector.d.ts +0 -15
  94. package/dist/proxy/loop-prevention/detectors/ngram-detector.js +0 -65
  95. package/dist/proxy/loop-prevention/session-tracker.d.ts +0 -14
  96. package/dist/proxy/loop-prevention/session-tracker.js +0 -67
  97. package/dist/proxy/loop-prevention/stream-loop-guard.d.ts +0 -12
  98. package/dist/proxy/loop-prevention/stream-loop-guard.js +0 -28
  99. package/dist/proxy/loop-prevention/tool-loop-guard.d.ts +0 -13
  100. package/dist/proxy/loop-prevention/tool-loop-guard.js +0 -70
  101. package/dist/proxy/loop-prevention/types.d.ts +0 -38
  102. package/dist/proxy/loop-prevention/types.js +0 -18
  103. package/dist/proxy/orchestration/semaphore.d.ts +0 -32
  104. package/dist/proxy/orchestration/semaphore.js +0 -157
  105. package/frontend-dist/assets/CardContent-BhMXx-JD.js +0 -1
  106. package/frontend-dist/assets/CardTitle-DQDjTee3.js +0 -1
  107. package/frontend-dist/assets/CascadingModelSelect-JBQq3JJt.js +0 -1
  108. package/frontend-dist/assets/Checkbox-ByxbKP_C.js +0 -1
  109. package/frontend-dist/assets/CollapsibleContent-GecW2Jk_.js +0 -1
  110. package/frontend-dist/assets/CollapsibleTrigger-Cib3-OsK.js +0 -1
  111. package/frontend-dist/assets/Collection-Dbvdpa0m.js +0 -1
  112. package/frontend-dist/assets/Dashboard-3MJPLflT.js +0 -3
  113. package/frontend-dist/assets/DialogTitle-Ej_rtfV1.js +0 -1
  114. package/frontend-dist/assets/Input-tcnrMp1v.js +0 -1
  115. package/frontend-dist/assets/Label-BwzPFyL-.js +0 -1
  116. package/frontend-dist/assets/Login-Cdsw2pWC.js +0 -1
  117. package/frontend-dist/assets/Logs-5_CWiws5.js +0 -1
  118. package/frontend-dist/assets/MappingList-D8HRph05.js +0 -1
  119. package/frontend-dist/assets/ModelCard-CZbQcYNn.js +0 -1
  120. package/frontend-dist/assets/ModelMappings-CJqgl7O8.js +0 -1
  121. package/frontend-dist/assets/Monitor-B8v5a8fB.js +0 -1
  122. package/frontend-dist/assets/PopoverTrigger-C88SpJNZ.js +0 -1
  123. package/frontend-dist/assets/PopperContent-6BXua_FZ.js +0 -1
  124. package/frontend-dist/assets/Providers-DH0nvlGn.js +0 -1
  125. package/frontend-dist/assets/ProxyEnhancement-CAH-44W-.js +0 -5
  126. package/frontend-dist/assets/QuickSetup-CsDO-ZGP.js +0 -1
  127. package/frontend-dist/assets/RetryRules-8iT9fLsH.js +0 -1
  128. package/frontend-dist/assets/RouterKeys-BFoEmWgj.js +0 -1
  129. package/frontend-dist/assets/RovingFocusItem-DdPUFQHC.js +0 -1
  130. package/frontend-dist/assets/Schedules-B8Se31u4.js +0 -1
  131. package/frontend-dist/assets/SelectValue-CT2z_-6j.js +0 -1
  132. package/frontend-dist/assets/Settings-BHvtsJKD.js +0 -6
  133. package/frontend-dist/assets/Setup-k-l9KDC0.js +0 -1
  134. package/frontend-dist/assets/Switch-D1NdA4ax.js +0 -1
  135. package/frontend-dist/assets/TableHeader-CcMyOsUB.js +0 -1
  136. package/frontend-dist/assets/Teleport-Bmeh33lB.js +0 -3
  137. package/frontend-dist/assets/TooltipTrigger-LegC_Uvp.js +0 -1
  138. package/frontend-dist/assets/UnifiedRequestDialog-BVw6W2pk.js +0 -3
  139. package/frontend-dist/assets/UnifiedRequestDialog-C4MTxb25.css +0 -1
  140. package/frontend-dist/assets/VisuallyHidden-ogESfc9X.js +0 -1
  141. package/frontend-dist/assets/VisuallyHiddenInput-BQemVGau.js +0 -1
  142. package/frontend-dist/assets/alert-dialog-DzKCAoYJ.js +0 -1
  143. package/frontend-dist/assets/badge-C-9zPTgw.js +0 -1
  144. package/frontend-dist/assets/button-D27ClX8J.js +0 -14
  145. package/frontend-dist/assets/check-yTAivq1h.js +0 -1
  146. package/frontend-dist/assets/common-CWCbKHOK.js +0 -1
  147. package/frontend-dist/assets/common-D4xnnaqi.js +0 -1
  148. package/frontend-dist/assets/constants-B-VELBjk.js +0 -1
  149. package/frontend-dist/assets/copy-DWG9cQPR.js +0 -1
  150. package/frontend-dist/assets/dashboard-B8eI-t8c.js +0 -1
  151. package/frontend-dist/assets/dashboard-Dbe6A2lu.js +0 -1
  152. package/frontend-dist/assets/dialog-BnYR6_dh.js +0 -1
  153. package/frontend-dist/assets/file-text-D33FJAPX.js +0 -1
  154. package/frontend-dist/assets/format-BhxQSgt6.js +0 -1
  155. package/frontend-dist/assets/i18n-CwUfS0tE.js +0 -1
  156. package/frontend-dist/assets/index-B348nt-T.css +0 -1
  157. package/frontend-dist/assets/index-C8DKlnvd.js +0 -1
  158. package/frontend-dist/assets/lib-D0Ek2pPZ.js +0 -1
  159. package/frontend-dist/assets/loader-circle-EpKC006I.js +0 -1
  160. package/frontend-dist/assets/login-BTolYxVI.js +0 -1
  161. package/frontend-dist/assets/login-w_ICpiU5.js +0 -1
  162. package/frontend-dist/assets/logs-7dT2uyMa.js +0 -1
  163. package/frontend-dist/assets/logs-_3w8tDQa.js +0 -1
  164. package/frontend-dist/assets/mappings-Bbn3r2uJ.js +0 -1
  165. package/frontend-dist/assets/mappings-CTZ-zb1x.js +0 -1
  166. package/frontend-dist/assets/monitor-DN5m5n_x.js +0 -1
  167. package/frontend-dist/assets/monitor-DysWEOtt.js +0 -1
  168. package/frontend-dist/assets/ohash.D__AXeF1-CTo5WcIm.js +0 -1
  169. package/frontend-dist/assets/providers-C1gQGzwa.js +0 -1
  170. package/frontend-dist/assets/providers-CCfko___.js +0 -1
  171. package/frontend-dist/assets/proxyEnhancement-BItabyLo.js +0 -1
  172. package/frontend-dist/assets/proxyEnhancement-DeMb7wIE.js +0 -1
  173. package/frontend-dist/assets/quickSetup-C75HMC_z.js +0 -1
  174. package/frontend-dist/assets/quickSetup-DStZWiuf.js +0 -1
  175. package/frontend-dist/assets/requestDetail-BoaPEQs-.js +0 -1
  176. package/frontend-dist/assets/requestDetail-CM5kFgy6.js +0 -1
  177. package/frontend-dist/assets/retryRules-CIF37gOl.js +0 -1
  178. package/frontend-dist/assets/retryRules-o_D8S5gy.js +0 -1
  179. package/frontend-dist/assets/routerKeys-BAvjW0V8.js +0 -1
  180. package/frontend-dist/assets/routerKeys-mQt2YPuE.js +0 -1
  181. package/frontend-dist/assets/schedules-BCV2rxK-.js +0 -1
  182. package/frontend-dist/assets/schedules-Qte9b7b_.js +0 -1
  183. package/frontend-dist/assets/settings-Bgu2lJfy.js +0 -1
  184. package/frontend-dist/assets/settings-UCmMSq_F.js +0 -1
  185. package/frontend-dist/assets/setup-B_fAfMoV.js +0 -1
  186. package/frontend-dist/assets/setup-Chc246Zi.js +0 -1
  187. package/frontend-dist/assets/sidebar-B7rejnZA.js +0 -1
  188. package/frontend-dist/assets/sidebar-CBMItLst.js +0 -1
  189. package/frontend-dist/assets/sun-BylRZIWt.js +0 -1
  190. package/frontend-dist/assets/trash-2-QNFff7V4.js +0 -1
  191. package/frontend-dist/assets/useClipboard-BFt5f-_-.js +0 -1
  192. package/frontend-dist/assets/useFocusGuards-DQBZKWnu.js +0 -1
  193. package/frontend-dist/assets/useFormControl-T2RQNBqs.js +0 -1
  194. package/frontend-dist/assets/useLogRetention-NrrZrpPE.js +0 -1
  195. package/frontend-dist/assets/useNonce-DR38uny5.js +0 -1
  196. package/frontend-dist/assets/useTheme-CpTI547G.js +0 -1
  197. package/frontend-dist/assets/x-DSgLgKC_.js +0 -1
  198. package/frontend-dist/favicon.svg +0 -1
  199. package/frontend-dist/icons.svg +0 -24
  200. package/frontend-dist/index.html +0 -37
@@ -41,7 +41,8 @@
41
41
  "ernie-x1-32k-preview",
42
42
  "deepseek-v3",
43
43
  "deepseek-r1"
44
- ]
44
+ ],
45
+ "upstreamPath": "/chat/completions"
45
46
  }
46
47
  ]
47
48
  },
@@ -52,7 +53,7 @@
52
53
  "plan": "API",
53
54
  "presetName": "iflytek-spark",
54
55
  "apiType": "openai",
55
- "baseUrl": "https://spark-api-open.xf-yun.com/v1",
56
+ "baseUrl": "https://spark-api-open.xf-yun.com",
56
57
  "models": [
57
58
  "4.0Ultra",
58
59
  "generalv3.5",
@@ -71,7 +72,7 @@
71
72
  "plan": "API",
72
73
  "presetName": "siliconflow",
73
74
  "apiType": "openai",
74
- "baseUrl": "https://api.siliconflow.cn/v1",
75
+ "baseUrl": "https://api.siliconflow.cn",
75
76
  "models": [
76
77
  "deepseek-ai/DeepSeek-V3.2-Exp",
77
78
  "deepseek-ai/DeepSeek-R1",
@@ -178,7 +179,7 @@
178
179
  "plan": "Coding Plan",
179
180
  "presetName": "volcengine-coding-plan",
180
181
  "apiType": "anthropic",
181
- "baseUrl": "https://ark.cn-beijing.volces.com/api/compatible",
182
+ "baseUrl": "https://ark.cn-beijing.volces.com/api/coding",
182
183
  "models": [
183
184
  "ark-code-latest",
184
185
  "doubao-seed-2.0-code",
@@ -267,16 +268,34 @@
267
268
  ]
268
269
  },
269
270
  {
270
- "group": "OpenCode Go",
271
+ "group": "OpenCode",
271
272
  "presets": [
272
273
  {
273
- "plan": "Go",
274
+ "plan": "Go OpenAI",
275
+ "presetName": "opencode-go-openai",
276
+ "apiType": "openai",
277
+ "baseUrl": "https://opencode.ai/zen/go/v1/chat/completions",
278
+ "models": [
279
+ "glm-5.1",
280
+ "glm-5",
281
+ "kimi-k2.5",
282
+ "kimi-k2.6",
283
+ "deepseek-v4-pro",
284
+ "deepseek-v4-flash",
285
+ "mimo-v2-pro",
286
+ "mimo-v2-omni",
287
+ "mimo-v2.5-pro",
288
+ "mimo-v2.5",
289
+ "qwen3.6-plus",
290
+ "qwen3.5-plus"
291
+ ]
292
+ },
293
+ {
294
+ "plan": "Go Anthropic",
274
295
  "presetName": "opencode-go-anthropic",
275
296
  "apiType": "anthropic",
276
297
  "baseUrl": "https://opencode.ai/zen/go/v1/messages",
277
298
  "models": [
278
- "deepseek-v4-pro",
279
- "deepseek-v4-flash",
280
299
  "minimax-m2.7",
281
300
  "minimax-m2.5"
282
301
  ]
@@ -312,4 +331,4 @@
312
331
  }
313
332
  ]
314
333
  }
315
- ]
334
+ ]
@@ -1,4 +1 @@
1
- {
2
- "providers": 1,
3
- "retryRules": 1
4
- }
1
+ {"providers":1,"retryRules":1}
@@ -1,5 +1,5 @@
1
1
  import { FastifyPluginCallback } from "fastify";
2
- import type { RequestTracker } from "../monitor/request-tracker.js";
2
+ import type { RequestTracker } from "@llm-router/core/monitor";
3
3
  interface MonitorRoutesOptions {
4
4
  tracker?: RequestTracker;
5
5
  }
@@ -1,3 +1,4 @@
1
+ import { adaptSSEClient } from "../core/sse-client-adapter.js";
1
2
  import { HTTP_NOT_FOUND } from "./constants.js";
2
3
  import { API_CODE, apiError } from "./api-response.js";
3
4
  const HTTP_OK = 200;
@@ -20,9 +21,10 @@ export const adminMonitorRoutes = (app, options, done) => {
20
21
  "Cache-Control": "no-cache",
21
22
  Connection: "keep-alive",
22
23
  });
23
- tracker.addClient(reply.raw);
24
+ const sseClient = adaptSSEClient(reply.raw);
25
+ tracker.addClient(sseClient);
24
26
  request.raw.on("close", () => {
25
- tracker.removeClient(reply.raw);
27
+ tracker.removeClient(sseClient);
26
28
  });
27
29
  });
28
30
  app.get("/admin/api/monitor/request/:id", async (request, reply) => {
@@ -1,13 +1,13 @@
1
1
  import { FastifyPluginCallback } from "fastify";
2
2
  import Database from "better-sqlite3";
3
3
  import type { StateRegistry } from "../core/registry.js";
4
- import type { AdaptiveConcurrencyController } from "../proxy/adaptive-controller.js";
5
- import type { RequestTracker } from "../monitor/request-tracker.js";
4
+ import type { AdaptiveController } from "@llm-router/core/concurrency";
5
+ import type { RequestTracker } from "@llm-router/core/monitor";
6
6
  interface ProviderRoutesOptions {
7
7
  db: Database.Database;
8
8
  stateRegistry?: StateRegistry;
9
9
  tracker?: RequestTracker;
10
- adaptiveController?: AdaptiveConcurrencyController;
10
+ adaptiveController?: AdaptiveController;
11
11
  }
12
12
  export declare const adminProviderRoutes: FastifyPluginCallback<ProviderRoutesOptions>;
13
13
  export {};
@@ -70,6 +70,7 @@ const CreateProviderSchema = Type.Object({
70
70
  name: Type.String({ minLength: 1 }),
71
71
  api_type: Type.Union([Type.Literal("openai"), Type.Literal("anthropic")]),
72
72
  base_url: Type.String({ minLength: 1 }),
73
+ upstream_path: Type.Optional(Type.String({ minLength: 1 })),
73
74
  api_key: Type.String({ minLength: 1 }),
74
75
  models: Type.Optional(Type.Array(Type.Union([
75
76
  Type.String(),
@@ -85,6 +86,7 @@ const UpdateProviderSchema = Type.Object({
85
86
  name: Type.Optional(Type.String({ minLength: 1 })),
86
87
  api_type: Type.Optional(Type.Union([Type.Literal("openai"), Type.Literal("anthropic")])),
87
88
  base_url: Type.Optional(Type.String({ minLength: 1 })),
89
+ upstream_path: Type.Optional(Type.String({ minLength: 1 })),
88
90
  api_key: Type.Optional(Type.String({ minLength: 1 })),
89
91
  models: Type.Optional(Type.Array(Type.Union([
90
92
  Type.String(),
@@ -138,6 +140,7 @@ export const adminProviderRoutes = (app, options, done) => {
138
140
  name: body.name,
139
141
  api_type: body.api_type,
140
142
  base_url: body.base_url,
143
+ upstream_path: body.upstream_path ?? null,
141
144
  api_key: encryptedKey,
142
145
  api_key_preview: body.api_key.length > API_KEY_PREVIEW_MIN_LENGTH ? `${body.api_key.slice(0, API_KEY_PREVIEW_PREFIX_LEN)}...${body.api_key.slice(-API_KEY_PREVIEW_PREFIX_LEN)}` : "****",
143
146
  models: JSON.stringify(normalizedModels),
@@ -189,6 +192,8 @@ export const adminProviderRoutes = (app, options, done) => {
189
192
  fields.api_type = body.api_type;
190
193
  if (body.base_url !== undefined)
191
194
  fields.base_url = body.base_url;
195
+ if (body.upstream_path !== undefined)
196
+ fields.upstream_path = body.upstream_path || null;
192
197
  if (body.is_active !== undefined)
193
198
  fields.is_active = body.is_active;
194
199
  if (body.models !== undefined) {
@@ -1,13 +1,13 @@
1
1
  import { FastifyPluginCallback } from "fastify";
2
2
  import Database from "better-sqlite3";
3
3
  import type { StateRegistry } from "../core/registry.js";
4
- import type { RequestTracker } from "../monitor/request-tracker.js";
5
- import type { AdaptiveConcurrencyController } from "../proxy/adaptive-controller.js";
4
+ import type { RequestTracker } from "@llm-router/core/monitor";
5
+ import type { AdaptiveController } from "@llm-router/core/concurrency";
6
6
  interface QuickSetupRoutesOptions {
7
7
  db: Database.Database;
8
8
  stateRegistry?: StateRegistry;
9
9
  tracker?: RequestTracker;
10
- adaptiveController?: AdaptiveConcurrencyController;
10
+ adaptiveController?: AdaptiveController;
11
11
  }
12
12
  export declare const adminQuickSetupRoutes: FastifyPluginCallback<QuickSetupRoutesOptions>;
13
13
  export {};
@@ -15,6 +15,7 @@ const QuickSetupProviderSchema = Type.Object({
15
15
  name: Type.String({ minLength: 1 }),
16
16
  api_type: Type.Union([Type.Literal("openai"), Type.Literal("openai-responses"), Type.Literal("anthropic")]),
17
17
  base_url: Type.String({ minLength: 1 }),
18
+ upstream_path: Type.Optional(Type.String({ minLength: 1 })),
18
19
  api_key: Type.String({ minLength: 1 }),
19
20
  models: Type.Array(Type.Object({
20
21
  name: Type.String(),
@@ -90,6 +91,7 @@ export const adminQuickSetupRoutes = (app, options, done) => {
90
91
  name: body.provider.name,
91
92
  api_type: body.provider.api_type,
92
93
  base_url: body.provider.base_url,
94
+ upstream_path: body.provider.upstream_path ?? null,
93
95
  api_key: encryptedKey,
94
96
  api_key_preview: body.provider.api_key.length > API_KEY_PREVIEW_MIN_LENGTH
95
97
  ? `${body.provider.api_key.slice(0, API_KEY_PREVIEW_PREFIX_LEN)}...${body.provider.api_key.slice(-API_KEY_PREVIEW_PREFIX_LEN)}`
@@ -1,13 +1,13 @@
1
1
  import { FastifyPluginCallback } from "fastify";
2
2
  import Database from "better-sqlite3";
3
3
  import type { StateRegistry } from "../core/registry.js";
4
- import type { RequestTracker } from "../monitor/request-tracker.js";
5
- import type { AdaptiveConcurrencyController } from "../proxy/adaptive-controller.js";
4
+ import type { RequestTracker } from "@llm-router/core/monitor";
5
+ import type { AdaptiveController } from "@llm-router/core/concurrency";
6
6
  interface AdminRoutesOptions {
7
7
  db: Database.Database;
8
8
  stateRegistry: StateRegistry;
9
9
  tracker?: RequestTracker;
10
- adaptiveController?: AdaptiveConcurrencyController;
10
+ adaptiveController?: AdaptiveController;
11
11
  logFileWriter?: import("../storage/log-file-writer.js").LogFileWriter | null;
12
12
  logsDir?: string;
13
13
  pluginRegistry?: import("../proxy/transform/plugin-registry.js").PluginRegistry;
@@ -7,17 +7,19 @@ import fs from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import { HTTP_BAD_REQUEST, HTTP_INTERNAL_ERROR } from '../core/constants.js';
9
9
  import { API_CODE, apiError } from './api-response.js';
10
- const GITHUB_CONFIG_BASE = 'https://raw.githubusercontent.com/zhushanwen321/llm-simple-router/main/config';
11
- const GITEE_CONFIG_BASE = 'https://gitee.com/zzzzswszzzz/llm-simple-router/raw/main/config';
10
+ const GITHUB_CONFIG_BASE = 'https://raw.githubusercontent.com/zhushanwen321/llm-simple-router/main/router/config';
11
+ const GITEE_CONFIG_BASE = 'https://gitee.com/zzzzswszzzz/llm-simple-router/raw/main/router/config';
12
12
  const CHECK_INTERVAL_MS = 60 * 60 * 1000; // eslint-disable-line no-magic-numbers
13
13
  const JSON_INDENT = 2;
14
- // 模块级单例:checker 和定时器
14
+ // 模块级单例:checker、configDir 和定时器
15
15
  let checker = null;
16
+ let configDir = '';
16
17
  let intervalId = null;
17
18
  export function startUpgradeChecker(opts) {
18
19
  if (checker)
19
20
  return checker;
20
- checker = createUpgradeChecker(opts);
21
+ configDir = opts?.configDir ?? path.resolve(process.cwd(), 'config');
22
+ checker = createUpgradeChecker({ ...opts, configDir });
21
23
  // 启动时检查一次,之后每小时
22
24
  checker.check();
23
25
  intervalId = setInterval(() => checker.check(), CHECK_INTERVAL_MS);
@@ -117,29 +119,26 @@ export const adminUpgradeRoutes = (app, options, done) => {
117
119
  return reply.code(HTTP_BAD_REQUEST).send(apiError(API_CODE.BAD_REQUEST, 'source must be github or gitee'));
118
120
  }
119
121
  const base = getConfigBaseUrl(source);
120
- const configDir = path.resolve(process.cwd(), 'config');
122
+ const syncConfigDir = configDir || path.resolve(process.cwd(), 'config');
121
123
  try {
122
- fs.mkdirSync(configDir, { recursive: true });
124
+ fs.mkdirSync(syncConfigDir, { recursive: true });
123
125
  const [providersResult, rulesResult, versionResult] = await Promise.allSettled([
124
126
  fetchJson(`${base}/recommended-providers.json`),
125
127
  fetchJson(`${base}/recommended-retry-rules.json`),
126
128
  fetchJson(`${base}/version.json`),
127
129
  ]);
128
130
  if (providersResult.status === 'fulfilled') {
129
- fs.writeFileSync(path.join(configDir, 'recommended-providers.json'), JSON.stringify(providersResult.value, null, JSON_INDENT));
131
+ fs.writeFileSync(path.join(syncConfigDir, 'recommended-providers.json'), JSON.stringify(providersResult.value, null, JSON_INDENT));
130
132
  }
131
133
  if (rulesResult.status === 'fulfilled') {
132
- fs.writeFileSync(path.join(configDir, 'recommended-retry-rules.json'), JSON.stringify(rulesResult.value, null, JSON_INDENT));
134
+ fs.writeFileSync(path.join(syncConfigDir, 'recommended-retry-rules.json'), JSON.stringify(rulesResult.value, null, JSON_INDENT));
133
135
  }
134
136
  if (versionResult.status === 'fulfilled') {
135
- fs.writeFileSync(path.join(configDir, 'version.json'), JSON.stringify(versionResult.value, null, JSON_INDENT));
137
+ fs.writeFileSync(path.join(syncConfigDir, 'version.json'), JSON.stringify(versionResult.value, null, JSON_INDENT));
136
138
  }
137
139
  if (providersResult.status === 'rejected' && rulesResult.status === 'rejected') {
138
140
  throw new Error('同步失败: 无法获取 providers 和 retry-rules 配置');
139
141
  }
140
- if (versionResult.status === 'rejected') {
141
- process.stderr.write('[upgrade] warning: version.json sync failed, providers/rules synced without version\n');
142
- }
143
142
  reloadConfig();
144
143
  if (checker)
145
144
  await checker.check(getConfigBaseUrl(source));
@@ -3,6 +3,7 @@ export interface ProviderPreset {
3
3
  presetName: string;
4
4
  apiType: 'openai' | 'openai-responses' | 'anthropic';
5
5
  baseUrl: string;
6
+ upstreamPath?: string;
6
7
  models: string[];
7
8
  }
8
9
  export interface ProviderGroup {
@@ -26,6 +27,6 @@ export interface ConfigVersions {
26
27
  export declare function loadRecommendedConfig(dir?: string): void;
27
28
  export declare function getRecommendedProviders(): ProviderGroup[];
28
29
  export declare function getRecommendedRetryRules(): RecommendedRetryRule[];
29
- export declare function reloadConfig(): void;
30
30
  /** 读取推荐配置的版本号(来自独立 version.json,历史版本代码不会读取此文件) */
31
31
  export declare function getConfigVersions(): ConfigVersions;
32
+ export declare function reloadConfig(): void;
@@ -4,15 +4,24 @@ let configDir = '';
4
4
  export function loadRecommendedConfig(dir) {
5
5
  configDir = dir ?? path.resolve(process.cwd(), 'config');
6
6
  }
7
+ function loadJson(filename) {
8
+ const filePath = path.join(configDir, filename);
9
+ try {
10
+ if (!fs.existsSync(filePath))
11
+ return [];
12
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
13
+ }
14
+ catch (err) {
15
+ process.stderr.write(`[recommended] 加载 ${filename} 失败: ${err instanceof Error ? err.message : String(err)}\n`);
16
+ return [];
17
+ }
18
+ }
7
19
  export function getRecommendedProviders() {
8
20
  return loadJson('recommended-providers.json');
9
21
  }
10
22
  export function getRecommendedRetryRules() {
11
23
  return loadJson('recommended-retry-rules.json');
12
24
  }
13
- // No-op: kept for backward compat (reload endpoint, upgrade flow)
14
- // Config is now always read from disk, no caching.
15
- export function reloadConfig() { }
16
25
  /** 读取推荐配置的版本号(来自独立 version.json,历史版本代码不会读取此文件) */
17
26
  export function getConfigVersions() {
18
27
  const filePath = path.join(configDir, 'version.json');
@@ -21,18 +30,11 @@ export function getConfigVersions() {
21
30
  return { providers: 0, retryRules: 0 };
22
31
  return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
23
32
  }
24
- catch {
33
+ catch (err) {
34
+ process.stderr.write(`[recommended] 加载 version.json 失败: ${err instanceof Error ? err.message : String(err)}\n`);
25
35
  return { providers: 0, retryRules: 0 };
26
36
  }
27
37
  }
28
- function loadJson(filename) {
29
- const filePath = path.join(configDir, filename);
30
- try {
31
- if (!fs.existsSync(filePath))
32
- return [];
33
- return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
34
- }
35
- catch {
36
- return [];
37
- }
38
- }
38
+ // No-op: kept for backward compat (reload endpoint, upgrade flow)
39
+ // Config is now always read from disk, no caching.
40
+ export function reloadConfig() { }
@@ -1,19 +1,5 @@
1
+ export { SemaphoreQueueFullError, SemaphoreTimeoutError } from "@llm-router/core";
1
2
  import type { TransportResult, ResilienceAttempt } from "./types.js";
2
- /**
3
- * Provider 并发队列已满时抛出。
4
- */
5
- export declare class SemaphoreQueueFullError extends Error {
6
- readonly providerId: string;
7
- constructor(providerId: string);
8
- }
9
- /**
10
- * Provider 并发等待超时时抛出。
11
- */
12
- export declare class SemaphoreTimeoutError extends Error {
13
- readonly providerId: string;
14
- readonly timeoutMs: number;
15
- constructor(providerId: string, timeoutMs: number);
16
- }
17
3
  /**
18
4
  * 跨 provider failover 时由 ResilienceLayer 抛出,
19
5
  * orchestrator 捕获后释放当前信号量并获取新 provider 的信号量。
@@ -1,29 +1,7 @@
1
1
  // src/core/errors.ts
2
- // 被多目录共享的错误类型(从 proxy/semaphore.ts proxy/types.ts 移出)
3
- /**
4
- * Provider 并发队列已满时抛出。
5
- */
6
- export class SemaphoreQueueFullError extends Error {
7
- providerId;
8
- constructor(providerId) {
9
- super(`Provider '${providerId}' concurrency queue is full`);
10
- this.providerId = providerId;
11
- this.name = "SemaphoreQueueFullError";
12
- }
13
- }
14
- /**
15
- * Provider 并发等待超时时抛出。
16
- */
17
- export class SemaphoreTimeoutError extends Error {
18
- providerId;
19
- timeoutMs;
20
- constructor(providerId, timeoutMs) {
21
- super(`Provider '${providerId}' concurrency wait timeout (${timeoutMs}ms)`);
22
- this.providerId = providerId;
23
- this.timeoutMs = timeoutMs;
24
- this.name = "SemaphoreTimeoutError";
25
- }
26
- }
2
+ // Re-export core errors + router-specific errors
3
+ // Re-export errors that have been migrated to @llm-router/core
4
+ export { SemaphoreQueueFullError, SemaphoreTimeoutError } from "@llm-router/core";
27
5
  /**
28
6
  * 跨 provider failover 时由 ResilienceLayer 抛出,
29
7
  * orchestrator 捕获后释放当前信号量并获取新 provider 的信号量。
@@ -0,0 +1,4 @@
1
+ import type { Logger } from "@llm-router/core";
2
+ import type { FastifyBaseLogger } from "fastify";
3
+ /** Adapt fastify/pino logger to core Logger interface. */
4
+ export declare function adaptLogger(log: FastifyBaseLogger): Logger;
@@ -0,0 +1,8 @@
1
+ /** Adapt fastify/pino logger to core Logger interface. */
2
+ export function adaptLogger(log) {
3
+ return {
4
+ debug: (obj, msg) => log.debug(obj, msg),
5
+ warn: (obj, msg) => log.warn(obj, msg),
6
+ error: (obj, msg) => log.error(obj, msg),
7
+ };
8
+ }
@@ -15,13 +15,13 @@ export interface ProviderConcurrencyParams {
15
15
  export interface StateRegistry {
16
16
  /** 刷新重试规则缓存(RetryRuleMatcher.load) */
17
17
  refreshRetryRules(): void;
18
- /** 更新 provider 并发配置(ProviderSemaphoreManager.updateConfig) */
18
+ /** 更新 provider 并发配置(SemaphoreManager.updateConfig) */
19
19
  updateProviderConcurrency(providerId: string, config: ConcurrencyConfig): void;
20
- /** 移除 provider 的信号量(ProviderSemaphoreManager.remove) */
20
+ /** 移除 provider 的信号量(SemaphoreManager.remove) */
21
21
  removeProvider(providerId: string): void;
22
- /** 移除所有信号量配置(ProviderSemaphoreManager.removeAll) */
22
+ /** 移除所有信号量配置(SemaphoreManager.removeAll) */
23
23
  removeAllProviders(): void;
24
- /** 获取 provider 并发状态(ProviderSemaphoreManager.getStatus) */
24
+ /** 获取 provider 并发状态(SemaphoreManager.getStatus) */
25
25
  getProviderStatus(providerId: string): {
26
26
  active: number;
27
27
  queued: number;
@@ -32,12 +32,12 @@ export interface StateRegistry {
32
32
  deleteModelState(keyId: string, sessionId: string): void;
33
33
  /** 读取 proxy enhancement 配置 */
34
34
  getEnhancementConfig(): EnhancementConfig;
35
- /** 同步 provider 的自适应并发配置(AdaptiveConcurrencyController.syncProvider) */
35
+ /** 同步 provider 的自适应并发配置(AdaptiveController.syncProvider) */
36
36
  syncAdaptiveProvider(providerId: string, params: ProviderConcurrencyParams): void;
37
- /** 移除 provider 的自适应并发状态(AdaptiveConcurrencyController.remove) */
37
+ /** 移除 provider 的自适应并发状态(AdaptiveController.remove) */
38
38
  removeAdaptiveProvider(providerId: string): void;
39
39
  /** 获取 provider 的自适应并发状态 */
40
- getAdaptiveStatus(providerId: string): import("../proxy/adaptive-controller.js").AdaptiveState | undefined;
40
+ getAdaptiveStatus(providerId: string): import("@llm-router/core/concurrency").AdaptiveState | undefined;
41
41
  /** 从 DB 重新读取所有 provider 配置,重建信号量/adaptive/tracker 缓存(导入配置后调用) */
42
42
  reinitializeProviders(): void;
43
43
  }
@@ -0,0 +1,3 @@
1
+ import type { SSEClient } from "@llm-router/core/monitor";
2
+ /** Adapt Node.js ServerResponse to core SSEClient interface. */
3
+ export declare function adaptSSEClient(res: import("node:http").ServerResponse): SSEClient;
@@ -0,0 +1,13 @@
1
+ /** Adapt Node.js ServerResponse to core SSEClient interface. */
2
+ export function adaptSSEClient(res) {
3
+ return {
4
+ write(data) { res.write(data); },
5
+ end() { res.end(); },
6
+ get writableEnded() { return res.writableEnded; },
7
+ on(event, callback) {
8
+ if (event === "close") {
9
+ res.on("close", callback);
10
+ }
11
+ },
12
+ };
13
+ }
@@ -1,3 +1,4 @@
1
+ export type { ConcurrencyConfig } from "@llm-router/core/concurrency";
1
2
  export interface Target {
2
3
  backend_model: string;
3
4
  provider_id: string;
@@ -8,12 +9,6 @@ export interface ResolveContext {
8
9
  now: Date;
9
10
  excludeTargets?: Target[];
10
11
  }
11
- /** Provider 级并发控制配置(唯一来源,替代 semaphore.ts 和 registry.ts 中的重复定义) */
12
- export interface ConcurrencyConfig {
13
- maxConcurrency: number;
14
- queueTimeoutMs: number;
15
- maxQueueSize: number;
16
- }
17
12
  export interface ConcurrencyOverride {
18
13
  max_concurrency?: number;
19
14
  queue_timeout_ms?: number;
@@ -4,6 +4,7 @@ export interface Provider {
4
4
  name: string;
5
5
  api_type: "openai" | "openai-responses" | "anthropic";
6
6
  base_url: string;
7
+ upstream_path: string | null;
7
8
  api_key: string;
8
9
  api_key_preview?: string;
9
10
  models: string;
@@ -27,6 +28,7 @@ export declare function createProvider(db: Database.Database, provider: {
27
28
  name: string;
28
29
  api_type: "openai" | "openai-responses" | "anthropic";
29
30
  base_url: string;
31
+ upstream_path?: string | null;
30
32
  api_key: string;
31
33
  api_key_preview?: string;
32
34
  models?: string;
@@ -36,7 +38,7 @@ export declare function createProvider(db: Database.Database, provider: {
36
38
  max_queue_size?: number;
37
39
  adaptive_enabled?: number;
38
40
  }): string;
39
- export declare function updateProvider(db: Database.Database, id: string, fields: Partial<Pick<Provider, "name" | "api_type" | "base_url" | "api_key" | "api_key_preview" | "models" | "is_active" | "max_concurrency" | "queue_timeout_ms" | "max_queue_size" | "adaptive_enabled">>): void;
41
+ export declare function updateProvider(db: Database.Database, id: string, fields: Partial<Pick<Provider, "name" | "api_type" | "base_url" | "upstream_path" | "api_key" | "api_key_preview" | "models" | "is_active" | "max_concurrency" | "queue_timeout_ms" | "max_queue_size" | "adaptive_enabled">>): void;
40
42
  export declare function deleteProvider(db: Database.Database, id: string): void;
41
43
  export declare function getActiveProviderByName(db: Database.Database, name: string): {
42
44
  id: string;
@@ -6,7 +6,7 @@ export const PROVIDER_CONCURRENCY_DEFAULTS = {
6
6
  max_queue_size: 100,
7
7
  };
8
8
  const PROVIDER_FIELDS = new Set([
9
- "name", "api_type", "base_url", "api_key", "api_key_preview", "models", "is_active", "max_concurrency", "queue_timeout_ms", "max_queue_size", "adaptive_enabled",
9
+ "name", "api_type", "base_url", "upstream_path", "api_key", "api_key_preview", "models", "is_active", "max_concurrency", "queue_timeout_ms", "max_queue_size", "adaptive_enabled",
10
10
  ]);
11
11
  export function getActiveProviders(db, apiType) {
12
12
  return db
@@ -22,8 +22,8 @@ export function getProviderById(db, id) {
22
22
  export function createProvider(db, provider) {
23
23
  const id = randomUUID();
24
24
  const now = new Date().toISOString();
25
- db.prepare(`INSERT INTO providers (id, name, api_type, base_url, api_key, api_key_preview, models, is_active, max_concurrency, queue_timeout_ms, max_queue_size, adaptive_enabled, created_at, updated_at)
26
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, provider.name, provider.api_type, provider.base_url, provider.api_key, provider.api_key_preview ?? null, provider.models ?? "[]", provider.is_active ?? 1, provider.max_concurrency ?? PROVIDER_CONCURRENCY_DEFAULTS.max_concurrency, provider.queue_timeout_ms ?? PROVIDER_CONCURRENCY_DEFAULTS.queue_timeout_ms, provider.max_queue_size ?? PROVIDER_CONCURRENCY_DEFAULTS.max_queue_size, provider.adaptive_enabled ?? 0, now, now);
25
+ db.prepare(`INSERT INTO providers (id, name, api_type, base_url, upstream_path, api_key, api_key_preview, models, is_active, max_concurrency, queue_timeout_ms, max_queue_size, adaptive_enabled, created_at, updated_at)
26
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, provider.name, provider.api_type, provider.base_url, provider.upstream_path ?? null, provider.api_key, provider.api_key_preview ?? null, provider.models ?? "[]", provider.is_active ?? 1, provider.max_concurrency ?? PROVIDER_CONCURRENCY_DEFAULTS.max_concurrency, provider.queue_timeout_ms ?? PROVIDER_CONCURRENCY_DEFAULTS.queue_timeout_ms, provider.max_queue_size ?? PROVIDER_CONCURRENCY_DEFAULTS.max_queue_size, provider.adaptive_enabled ?? 0, now, now);
27
27
  return id;
28
28
  }
29
29
  export function updateProvider(db, id, fields) {
package/dist/index.d.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { FastifyInstance } from "fastify";
3
3
  import { Config } from "./config/index.js";
4
- import { ProviderSemaphoreManager } from "./proxy/orchestration/semaphore.js";
5
- import { AdaptiveConcurrencyController } from "./proxy/adaptive-controller.js";
6
- import { RequestTracker } from "./monitor/request-tracker.js";
4
+ import { SemaphoreManager, AdaptiveController } from "@llm-router/core/concurrency";
5
+ import { RequestTracker } from "@llm-router/core/monitor";
7
6
  import { UsageWindowTracker } from "./proxy/routing/usage-window-tracker.js";
8
7
  import { CheckerOptions } from "./upgrade/checker.js";
9
8
  import Database from "better-sqlite3";
@@ -16,7 +15,7 @@ export interface AppOptions {
16
15
  * 共享初始化逻辑 — 启动时和导入配置后都需要调用。
17
16
  * 从 DB 读取所有 provider,初始化信号量/自适应并发/tracker 缓存。
18
17
  */
19
- export declare function initializeProviderState(db: Database.Database, semaphoreManager: ProviderSemaphoreManager, adaptiveController: AdaptiveConcurrencyController, tracker: RequestTracker): void;
18
+ export declare function initializeProviderState(db: Database.Database, semaphoreManager: SemaphoreManager, adaptiveController: AdaptiveController, tracker: RequestTracker): void;
20
19
  export declare function buildApp(options?: AppOptions): Promise<{
21
20
  app: FastifyInstance;
22
21
  db: Database.Database;
package/dist/index.js CHANGED
@@ -21,14 +21,12 @@ import { responsesProxy } from "./proxy/handler/responses.js";
21
21
  import { adminRoutes } from "./admin/routes.js";
22
22
  import { RetryRuleMatcher } from "./proxy/orchestration/retry-rules.js";
23
23
  import { PluginRegistry } from "./proxy/transform/plugin-registry.js";
24
- import { ProviderSemaphoreManager } from "./proxy/orchestration/semaphore.js";
25
- import { AdaptiveConcurrencyController } from "./proxy/adaptive-controller.js";
24
+ import { SemaphoreManager, AdaptiveController } from "@llm-router/core/concurrency";
26
25
  import { loadEnhancementConfig } from "./proxy/routing/enhancement-config.js";
27
- import { RequestTracker } from "./monitor/request-tracker.js";
26
+ import { RequestTracker } from "@llm-router/core/monitor";
28
27
  import { modelState } from "./proxy/routing/model-state.js";
29
28
  import { UsageWindowTracker } from "./proxy/routing/usage-window-tracker.js";
30
- import { SessionTracker } from "./proxy/loop-prevention/session-tracker.js";
31
- import { DEFAULT_LOOP_PREVENTION_CONFIG } from "./proxy/loop-prevention/types.js";
29
+ import { SessionTracker, DEFAULT_LOOP_PREVENTION_CONFIG } from "@llm-router/core/loop-prevention";
32
30
  import { scheduleLogCleanup } from "./db/log-cleaner.js";
33
31
  import { scheduleDbSizeMonitor } from "./db/db-size-monitor.js";
34
32
  import { startUpgradeChecker, stopUpgradeChecker } from "./admin/upgrade.js";
@@ -180,12 +178,15 @@ export async function buildApp(options) {
180
178
  }
181
179
  return payload;
182
180
  });
183
- loadRecommendedConfig();
184
- startUpgradeChecker(options?.upgradeCheckerOptions);
181
+ loadRecommendedConfig(path.resolve(__dirname, '../config'));
182
+ startUpgradeChecker({
183
+ ...options?.upgradeCheckerOptions,
184
+ configDir: path.resolve(__dirname, '../config'),
185
+ });
185
186
  const container = new ServiceContainer();
186
187
  container.register(SERVICE_KEYS.db, () => db);
187
188
  container.register(SERVICE_KEYS.matcher, (c) => { const m = new RetryRuleMatcher(); m.load(c.resolve(SERVICE_KEYS.db)); return m; });
188
- container.register(SERVICE_KEYS.semaphoreManager, () => new ProviderSemaphoreManager());
189
+ container.register(SERVICE_KEYS.semaphoreManager, () => new SemaphoreManager());
189
190
  container.register(SERVICE_KEYS.tracker, (c) => {
190
191
  const t = new RequestTracker({ semaphoreManager: c.resolve(SERVICE_KEYS.semaphoreManager), logger: app.log });
191
192
  t.startPushInterval();
@@ -207,9 +208,9 @@ export async function buildApp(options) {
207
208
  container.register(SERVICE_KEYS.logFileWriter, () => logFileWriter);
208
209
  // 注入 DB 到 modelState 单例,启用会话级持久化
209
210
  modelState.init(db);
210
- // 注册 AdaptiveConcurrencyController(依赖已注册的 semaphoreManager)
211
+ // 注册 AdaptiveController(依赖已注册的 semaphoreManager)
211
212
  container.register(SERVICE_KEYS.adaptiveController, (c) => {
212
- const ac = new AdaptiveConcurrencyController(c.resolve(SERVICE_KEYS.semaphoreManager), app.log);
213
+ const ac = new AdaptiveController(c.resolve(SERVICE_KEYS.semaphoreManager), app.log);
213
214
  return ac;
214
215
  });
215
216
  // 注册 PluginRegistry(从 DB 和 plugins 目录加载转换插件)
@@ -225,7 +226,7 @@ export async function buildApp(options) {
225
226
  const usageWindowTracker = container.resolve(SERVICE_KEYS.usageWindowTracker);
226
227
  const adaptiveController = container.resolve(SERVICE_KEYS.adaptiveController);
227
228
  // Wire adaptive controller to tracker
228
- tracker.setAdaptiveController(adaptiveController);
229
+ tracker.setAdaptiveStatusProvider(adaptiveController);
229
230
  // 从 DB 读取已有 provider 的并发配置,初始化信号量/adaptive/tracker(共享逻辑)
230
231
  initializeProviderState(db, semaphoreManager, adaptiveController, tracker);
231
232
  app.register(authMiddleware, { db });
@@ -1,5 +1,5 @@
1
- import type { ContentBlock } from "../../monitor/types.js";
2
- import type { ToolCallRecord } from "../loop-prevention/types.js";
1
+ import type { ContentBlock } from "@llm-router/core/monitor";
2
+ import type { ToolCallRecord } from "@llm-router/core/loop-prevention";
3
3
  import type { TransportResult } from "../types.js";
4
4
  /** 从 TransportResult 中提取最终 HTTP status code */
5
5
  export declare function getTransportStatusCode(result: TransportResult): number | null;