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.
- package/config/recommended-providers.json +28 -9
- package/config/version.json +1 -4
- package/dist/admin/monitor.d.ts +1 -1
- package/dist/admin/monitor.js +4 -2
- package/dist/admin/providers.d.ts +3 -3
- package/dist/admin/providers.js +5 -0
- package/dist/admin/quick-setup.d.ts +3 -3
- package/dist/admin/quick-setup.js +2 -0
- package/dist/admin/routes.d.ts +3 -3
- package/dist/admin/upgrade.js +11 -12
- package/dist/config/recommended.d.ts +2 -1
- package/dist/config/recommended.js +17 -15
- package/dist/core/errors.d.ts +1 -15
- package/dist/core/errors.js +3 -25
- package/dist/core/pino-logger.d.ts +4 -0
- package/dist/core/pino-logger.js +8 -0
- package/dist/core/registry.d.ts +7 -7
- package/dist/core/sse-client-adapter.d.ts +3 -0
- package/dist/core/sse-client-adapter.js +13 -0
- package/dist/core/types.d.ts +1 -6
- package/dist/db/providers.d.ts +3 -1
- package/dist/db/providers.js +3 -3
- package/dist/index.d.ts +3 -4
- package/dist/index.js +12 -11
- package/dist/proxy/handler/proxy-handler-utils.d.ts +2 -2
- package/dist/proxy/handler/proxy-handler.js +6 -2
- package/dist/proxy/orchestration/orchestrator.d.ts +5 -5
- package/dist/proxy/orchestration/scope.d.ts +4 -4
- package/dist/proxy/transport/stream.d.ts +1 -1
- package/dist/proxy/transport/transport-fn.d.ts +1 -1
- package/dist/proxy/transport/transport-fn.js +3 -3
- package/dist/storage/log-file-compressor.js +3 -3
- package/dist/storage/log-file-writer.js +3 -3
- package/dist/storage/types.d.ts +1 -1
- package/dist/storage/types.js +4 -4
- package/dist/upgrade/checker.js +6 -3
- package/package.json +3 -22
- package/README.en.md +0 -319
- package/README.md +0 -319
- package/dist/db/migrations/001_init.sql +0 -37
- package/dist/db/migrations/002_add_request_response_body.sql +0 -2
- package/dist/db/migrations/003_add_full_request_chain_log.sql +0 -4
- package/dist/db/migrations/004_rename_to_providers.sql +0 -9
- package/dist/db/migrations/005_add_api_key_preview.sql +0 -1
- package/dist/db/migrations/006_create_request_metrics.sql +0 -20
- package/dist/db/migrations/007_add_retry_fields.sql +0 -2
- package/dist/db/migrations/008_create_router_keys.sql +0 -17
- package/dist/db/migrations/009_add_request_logs_indexes.sql +0 -2
- package/dist/db/migrations/010_add_key_encrypted.sql +0 -1
- package/dist/db/migrations/011_create_mapping_groups.sql +0 -37
- package/dist/db/migrations/012_add_provider_models.sql +0 -2
- package/dist/db/migrations/013_add_retry_strategy.sql +0 -4
- package/dist/db/migrations/014_create_settings.sql +0 -4
- package/dist/db/migrations/015_add_original_model.sql +0 -1
- package/dist/db/migrations/016_create_session_model_tables.sql +0 -24
- package/dist/db/migrations/017_add_provider_concurrency.sql +0 -3
- package/dist/db/migrations/018_add_failover_field.sql +0 -2
- package/dist/db/migrations/019_create_usage_windows.sql +0 -11
- package/dist/db/migrations/020_drop_log_redundancy.sql +0 -8
- package/dist/db/migrations/021_merge_metrics_columns.sql +0 -28
- package/dist/db/migrations/022_add_session_id_and_incremental_vacuum.sql +0 -5
- package/dist/db/migrations/023_create_provider_model_info.sql +0 -8
- package/dist/db/migrations/024_add_mapping_groups_is_active.sql +0 -4
- package/dist/db/migrations/025_add_client_status_code.sql +0 -3
- package/dist/db/migrations/026_create_schedules_simplify_mappings.sql +0 -64
- package/dist/db/migrations/027_metrics_independent.sql +0 -54
- package/dist/db/migrations/028_ensure_strategy_column.sql +0 -11
- package/dist/db/migrations/029_convert_old_rule_format.sql +0 -7
- package/dist/db/migrations/030_add_input_tokens_estimated.sql +0 -6
- package/dist/db/migrations/031_add_tps_breakdown.sql +0 -13
- package/dist/db/migrations/032_add_non_thinking_tps.sql +0 -3
- package/dist/db/migrations/033_add_adaptive_concurrency.sql +0 -6
- package/dist/db/migrations/034_create_provider_transform_rules.sql +0 -11
- package/dist/db/migrations/035_drop_redundant_log_columns.sql +0 -13
- package/dist/db/migrations/036_add_openai_responses_api_type.sql +0 -68
- package/dist/db/migrations/037_fix_035_data_corruption.sql +0 -54
- package/dist/monitor/request-tracker.d.ts +0 -70
- package/dist/monitor/request-tracker.js +0 -303
- package/dist/monitor/runtime-collector.d.ts +0 -11
- package/dist/monitor/runtime-collector.js +0 -41
- package/dist/monitor/stats-aggregator.d.ts +0 -22
- package/dist/monitor/stats-aggregator.js +0 -167
- package/dist/monitor/stream-content-accumulator.d.ts +0 -14
- package/dist/monitor/stream-content-accumulator.js +0 -58
- package/dist/monitor/stream-extractor.d.ts +0 -11
- package/dist/monitor/stream-extractor.js +0 -72
- package/dist/monitor/types.d.ts +0 -106
- package/dist/monitor/types.js +0 -1
- package/dist/proxy/adaptive-controller.d.ts +0 -45
- package/dist/proxy/adaptive-controller.js +0 -144
- package/dist/proxy/loop-prevention/detectors/detector.d.ts +0 -10
- package/dist/proxy/loop-prevention/detectors/detector.js +0 -1
- package/dist/proxy/loop-prevention/detectors/ngram-detector.d.ts +0 -15
- package/dist/proxy/loop-prevention/detectors/ngram-detector.js +0 -65
- package/dist/proxy/loop-prevention/session-tracker.d.ts +0 -14
- package/dist/proxy/loop-prevention/session-tracker.js +0 -67
- package/dist/proxy/loop-prevention/stream-loop-guard.d.ts +0 -12
- package/dist/proxy/loop-prevention/stream-loop-guard.js +0 -28
- package/dist/proxy/loop-prevention/tool-loop-guard.d.ts +0 -13
- package/dist/proxy/loop-prevention/tool-loop-guard.js +0 -70
- package/dist/proxy/loop-prevention/types.d.ts +0 -38
- package/dist/proxy/loop-prevention/types.js +0 -18
- package/dist/proxy/orchestration/semaphore.d.ts +0 -32
- package/dist/proxy/orchestration/semaphore.js +0 -157
- package/frontend-dist/assets/CardContent-BhMXx-JD.js +0 -1
- package/frontend-dist/assets/CardTitle-DQDjTee3.js +0 -1
- package/frontend-dist/assets/CascadingModelSelect-JBQq3JJt.js +0 -1
- package/frontend-dist/assets/Checkbox-ByxbKP_C.js +0 -1
- package/frontend-dist/assets/CollapsibleContent-GecW2Jk_.js +0 -1
- package/frontend-dist/assets/CollapsibleTrigger-Cib3-OsK.js +0 -1
- package/frontend-dist/assets/Collection-Dbvdpa0m.js +0 -1
- package/frontend-dist/assets/Dashboard-3MJPLflT.js +0 -3
- package/frontend-dist/assets/DialogTitle-Ej_rtfV1.js +0 -1
- package/frontend-dist/assets/Input-tcnrMp1v.js +0 -1
- package/frontend-dist/assets/Label-BwzPFyL-.js +0 -1
- package/frontend-dist/assets/Login-Cdsw2pWC.js +0 -1
- package/frontend-dist/assets/Logs-5_CWiws5.js +0 -1
- package/frontend-dist/assets/MappingList-D8HRph05.js +0 -1
- package/frontend-dist/assets/ModelCard-CZbQcYNn.js +0 -1
- package/frontend-dist/assets/ModelMappings-CJqgl7O8.js +0 -1
- package/frontend-dist/assets/Monitor-B8v5a8fB.js +0 -1
- package/frontend-dist/assets/PopoverTrigger-C88SpJNZ.js +0 -1
- package/frontend-dist/assets/PopperContent-6BXua_FZ.js +0 -1
- package/frontend-dist/assets/Providers-DH0nvlGn.js +0 -1
- package/frontend-dist/assets/ProxyEnhancement-CAH-44W-.js +0 -5
- package/frontend-dist/assets/QuickSetup-CsDO-ZGP.js +0 -1
- package/frontend-dist/assets/RetryRules-8iT9fLsH.js +0 -1
- package/frontend-dist/assets/RouterKeys-BFoEmWgj.js +0 -1
- package/frontend-dist/assets/RovingFocusItem-DdPUFQHC.js +0 -1
- package/frontend-dist/assets/Schedules-B8Se31u4.js +0 -1
- package/frontend-dist/assets/SelectValue-CT2z_-6j.js +0 -1
- package/frontend-dist/assets/Settings-BHvtsJKD.js +0 -6
- package/frontend-dist/assets/Setup-k-l9KDC0.js +0 -1
- package/frontend-dist/assets/Switch-D1NdA4ax.js +0 -1
- package/frontend-dist/assets/TableHeader-CcMyOsUB.js +0 -1
- package/frontend-dist/assets/Teleport-Bmeh33lB.js +0 -3
- package/frontend-dist/assets/TooltipTrigger-LegC_Uvp.js +0 -1
- package/frontend-dist/assets/UnifiedRequestDialog-BVw6W2pk.js +0 -3
- package/frontend-dist/assets/UnifiedRequestDialog-C4MTxb25.css +0 -1
- package/frontend-dist/assets/VisuallyHidden-ogESfc9X.js +0 -1
- package/frontend-dist/assets/VisuallyHiddenInput-BQemVGau.js +0 -1
- package/frontend-dist/assets/alert-dialog-DzKCAoYJ.js +0 -1
- package/frontend-dist/assets/badge-C-9zPTgw.js +0 -1
- package/frontend-dist/assets/button-D27ClX8J.js +0 -14
- package/frontend-dist/assets/check-yTAivq1h.js +0 -1
- package/frontend-dist/assets/common-CWCbKHOK.js +0 -1
- package/frontend-dist/assets/common-D4xnnaqi.js +0 -1
- package/frontend-dist/assets/constants-B-VELBjk.js +0 -1
- package/frontend-dist/assets/copy-DWG9cQPR.js +0 -1
- package/frontend-dist/assets/dashboard-B8eI-t8c.js +0 -1
- package/frontend-dist/assets/dashboard-Dbe6A2lu.js +0 -1
- package/frontend-dist/assets/dialog-BnYR6_dh.js +0 -1
- package/frontend-dist/assets/file-text-D33FJAPX.js +0 -1
- package/frontend-dist/assets/format-BhxQSgt6.js +0 -1
- package/frontend-dist/assets/i18n-CwUfS0tE.js +0 -1
- package/frontend-dist/assets/index-B348nt-T.css +0 -1
- package/frontend-dist/assets/index-C8DKlnvd.js +0 -1
- package/frontend-dist/assets/lib-D0Ek2pPZ.js +0 -1
- package/frontend-dist/assets/loader-circle-EpKC006I.js +0 -1
- package/frontend-dist/assets/login-BTolYxVI.js +0 -1
- package/frontend-dist/assets/login-w_ICpiU5.js +0 -1
- package/frontend-dist/assets/logs-7dT2uyMa.js +0 -1
- package/frontend-dist/assets/logs-_3w8tDQa.js +0 -1
- package/frontend-dist/assets/mappings-Bbn3r2uJ.js +0 -1
- package/frontend-dist/assets/mappings-CTZ-zb1x.js +0 -1
- package/frontend-dist/assets/monitor-DN5m5n_x.js +0 -1
- package/frontend-dist/assets/monitor-DysWEOtt.js +0 -1
- package/frontend-dist/assets/ohash.D__AXeF1-CTo5WcIm.js +0 -1
- package/frontend-dist/assets/providers-C1gQGzwa.js +0 -1
- package/frontend-dist/assets/providers-CCfko___.js +0 -1
- package/frontend-dist/assets/proxyEnhancement-BItabyLo.js +0 -1
- package/frontend-dist/assets/proxyEnhancement-DeMb7wIE.js +0 -1
- package/frontend-dist/assets/quickSetup-C75HMC_z.js +0 -1
- package/frontend-dist/assets/quickSetup-DStZWiuf.js +0 -1
- package/frontend-dist/assets/requestDetail-BoaPEQs-.js +0 -1
- package/frontend-dist/assets/requestDetail-CM5kFgy6.js +0 -1
- package/frontend-dist/assets/retryRules-CIF37gOl.js +0 -1
- package/frontend-dist/assets/retryRules-o_D8S5gy.js +0 -1
- package/frontend-dist/assets/routerKeys-BAvjW0V8.js +0 -1
- package/frontend-dist/assets/routerKeys-mQt2YPuE.js +0 -1
- package/frontend-dist/assets/schedules-BCV2rxK-.js +0 -1
- package/frontend-dist/assets/schedules-Qte9b7b_.js +0 -1
- package/frontend-dist/assets/settings-Bgu2lJfy.js +0 -1
- package/frontend-dist/assets/settings-UCmMSq_F.js +0 -1
- package/frontend-dist/assets/setup-B_fAfMoV.js +0 -1
- package/frontend-dist/assets/setup-Chc246Zi.js +0 -1
- package/frontend-dist/assets/sidebar-B7rejnZA.js +0 -1
- package/frontend-dist/assets/sidebar-CBMItLst.js +0 -1
- package/frontend-dist/assets/sun-BylRZIWt.js +0 -1
- package/frontend-dist/assets/trash-2-QNFff7V4.js +0 -1
- package/frontend-dist/assets/useClipboard-BFt5f-_-.js +0 -1
- package/frontend-dist/assets/useFocusGuards-DQBZKWnu.js +0 -1
- package/frontend-dist/assets/useFormControl-T2RQNBqs.js +0 -1
- package/frontend-dist/assets/useLogRetention-NrrZrpPE.js +0 -1
- package/frontend-dist/assets/useNonce-DR38uny5.js +0 -1
- package/frontend-dist/assets/useTheme-CpTI547G.js +0 -1
- package/frontend-dist/assets/x-DSgLgKC_.js +0 -1
- package/frontend-dist/favicon.svg +0 -1
- package/frontend-dist/icons.svg +0 -24
- 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
|
|
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
|
|
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/
|
|
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
|
|
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
|
+
]
|
package/config/version.json
CHANGED
package/dist/admin/monitor.d.ts
CHANGED
package/dist/admin/monitor.js
CHANGED
|
@@ -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
|
-
|
|
24
|
+
const sseClient = adaptSSEClient(reply.raw);
|
|
25
|
+
tracker.addClient(sseClient);
|
|
24
26
|
request.raw.on("close", () => {
|
|
25
|
-
tracker.removeClient(
|
|
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 {
|
|
5
|
-
import type { RequestTracker } from "
|
|
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?:
|
|
10
|
+
adaptiveController?: AdaptiveController;
|
|
11
11
|
}
|
|
12
12
|
export declare const adminProviderRoutes: FastifyPluginCallback<ProviderRoutesOptions>;
|
|
13
13
|
export {};
|
package/dist/admin/providers.js
CHANGED
|
@@ -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 "
|
|
5
|
-
import type {
|
|
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?:
|
|
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)}`
|
package/dist/admin/routes.d.ts
CHANGED
|
@@ -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 "
|
|
5
|
-
import type {
|
|
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?:
|
|
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;
|
package/dist/admin/upgrade.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
122
|
+
const syncConfigDir = configDir || path.resolve(process.cwd(), 'config');
|
|
121
123
|
try {
|
|
122
|
-
fs.mkdirSync(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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() { }
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -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 的信号量。
|
package/dist/core/errors.js
CHANGED
|
@@ -1,29 +1,7 @@
|
|
|
1
1
|
// src/core/errors.ts
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
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 的信号量。
|
package/dist/core/registry.d.ts
CHANGED
|
@@ -15,13 +15,13 @@ export interface ProviderConcurrencyParams {
|
|
|
15
15
|
export interface StateRegistry {
|
|
16
16
|
/** 刷新重试规则缓存(RetryRuleMatcher.load) */
|
|
17
17
|
refreshRetryRules(): void;
|
|
18
|
-
/** 更新 provider 并发配置(
|
|
18
|
+
/** 更新 provider 并发配置(SemaphoreManager.updateConfig) */
|
|
19
19
|
updateProviderConcurrency(providerId: string, config: ConcurrencyConfig): void;
|
|
20
|
-
/** 移除 provider 的信号量(
|
|
20
|
+
/** 移除 provider 的信号量(SemaphoreManager.remove) */
|
|
21
21
|
removeProvider(providerId: string): void;
|
|
22
|
-
/** 移除所有信号量配置(
|
|
22
|
+
/** 移除所有信号量配置(SemaphoreManager.removeAll) */
|
|
23
23
|
removeAllProviders(): void;
|
|
24
|
-
/** 获取 provider 并发状态(
|
|
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 的自适应并发配置(
|
|
35
|
+
/** 同步 provider 的自适应并发配置(AdaptiveController.syncProvider) */
|
|
36
36
|
syncAdaptiveProvider(providerId: string, params: ProviderConcurrencyParams): void;
|
|
37
|
-
/** 移除 provider 的自适应并发状态(
|
|
37
|
+
/** 移除 provider 的自适应并发状态(AdaptiveController.remove) */
|
|
38
38
|
removeAdaptiveProvider(providerId: string): void;
|
|
39
39
|
/** 获取 provider 的自适应并发状态 */
|
|
40
|
-
getAdaptiveStatus(providerId: string): import("
|
|
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,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
|
+
}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -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;
|
package/dist/db/providers.d.ts
CHANGED
|
@@ -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;
|
package/dist/db/providers.js
CHANGED
|
@@ -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 {
|
|
5
|
-
import {
|
|
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:
|
|
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 {
|
|
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 "
|
|
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 "
|
|
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(
|
|
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
|
|
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
|
-
// 注册
|
|
211
|
+
// 注册 AdaptiveController(依赖已注册的 semaphoreManager)
|
|
211
212
|
container.register(SERVICE_KEYS.adaptiveController, (c) => {
|
|
212
|
-
const ac = new
|
|
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.
|
|
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 "
|
|
2
|
-
import type { ToolCallRecord } from "
|
|
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;
|