llm-simple-router 0.6.7 → 0.7.1
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/README.md +69 -0
- package/dist/admin/constants.d.ts +1 -1
- package/dist/admin/constants.js +2 -2
- package/dist/admin/logs.d.ts +2 -0
- package/dist/admin/logs.js +17 -1
- package/dist/admin/providers.d.ts +2 -2
- package/dist/admin/providers.js +29 -16
- package/dist/admin/proxy-enhancement.d.ts +2 -0
- package/dist/admin/proxy-enhancement.js +4 -10
- package/dist/admin/retry-rules.d.ts +2 -2
- package/dist/admin/retry-rules.js +4 -8
- package/dist/admin/routes.d.ts +5 -4
- package/dist/admin/routes.js +7 -7
- package/dist/admin/settings-import-export.d.ts +2 -4
- package/dist/admin/settings-import-export.js +9 -19
- package/dist/admin/settings.d.ts +1 -0
- package/dist/admin/settings.js +29 -1
- package/dist/admin/upgrade.d.ts +1 -0
- package/dist/admin/upgrade.js +37 -4
- package/dist/{constants.d.ts → core/constants.d.ts} +4 -1
- package/dist/{constants.js → core/constants.js} +21 -1
- package/dist/core/container.d.ts +31 -0
- package/dist/core/container.js +41 -0
- package/dist/core/errors.d.ts +26 -0
- package/dist/core/errors.js +42 -0
- package/dist/core/registry.d.ts +43 -0
- package/dist/core/registry.js +3 -0
- package/dist/core/types.d.ts +105 -0
- package/dist/core/types.js +3 -0
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/logs.d.ts +11 -24
- package/dist/db/logs.js +37 -38
- package/dist/db/metrics.js +1 -1
- package/dist/db/migrations/033_add_pipeline_snapshot.sql +1 -0
- package/dist/db/migrations/034_drop_redundant_log_columns.sql +13 -0
- package/dist/db/settings.d.ts +2 -0
- package/dist/db/settings.js +9 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +196 -108
- package/dist/metrics/metrics-extractor.d.ts +1 -24
- package/dist/metrics/metrics-extractor.js +1 -1
- package/dist/metrics/sse-metrics-transform.d.ts +1 -1
- package/dist/middleware/admin-auth.js +4 -0
- package/dist/middleware/auth.js +1 -2
- package/dist/monitor/request-tracker.d.ts +3 -4
- package/dist/monitor/request-tracker.js +6 -16
- package/dist/monitor/runtime-collector.js +1 -1
- package/dist/monitor/types.d.ts +8 -0
- package/dist/proxy/adaptive-controller.d.ts +4 -1
- package/dist/proxy/adaptive-controller.js +5 -0
- package/dist/proxy/enhancement/enhancement-handler.d.ts +19 -3
- package/dist/proxy/enhancement/enhancement-handler.js +80 -28
- package/dist/proxy/enhancement/index.d.ts +1 -0
- package/dist/proxy/handler/anthropic.d.ts +7 -0
- package/dist/proxy/{anthropic.js → handler/anthropic.js} +8 -7
- package/dist/proxy/handler/openai.d.ts +7 -0
- package/dist/proxy/{openai.js → handler/openai.js} +10 -9
- package/dist/proxy/handler/proxy-handler-utils.d.ts +9 -0
- package/dist/proxy/handler/proxy-handler-utils.js +63 -0
- package/dist/proxy/handler/proxy-handler.d.ts +13 -0
- package/dist/proxy/{proxy-handler.js → handler/proxy-handler.js} +104 -120
- package/dist/proxy/log-detail-policy.d.ts +12 -0
- package/dist/proxy/log-detail-policy.js +21 -0
- package/dist/proxy/log-helpers.d.ts +8 -0
- package/dist/proxy/log-helpers.js +16 -4
- package/dist/proxy/loop-prevention/tool-loop-guard.d.ts +1 -1
- package/dist/proxy/loop-prevention/tool-loop-guard.js +9 -12
- package/dist/proxy/{orchestrator.d.ts → orchestration/orchestrator.d.ts} +6 -4
- package/dist/proxy/{orchestrator.js → orchestration/orchestrator.js} +2 -1
- package/dist/proxy/{resilience.d.ts → orchestration/resilience.d.ts} +2 -14
- package/dist/proxy/{resilience.js → orchestration/resilience.js} +2 -2
- package/dist/proxy/{retry-rules.d.ts → orchestration/retry-rules.d.ts} +1 -1
- package/dist/proxy/{retry-rules.js → orchestration/retry-rules.js} +1 -1
- package/dist/proxy/{scope.d.ts → orchestration/scope.d.ts} +3 -3
- package/dist/proxy/{semaphore.d.ts → orchestration/semaphore.d.ts} +7 -15
- package/dist/proxy/{semaphore.js → orchestration/semaphore.js} +12 -26
- package/dist/proxy/patch/index.d.ts +8 -2
- package/dist/proxy/patch/index.js +5 -2
- package/dist/proxy/pipeline-snapshot.d.ts +37 -0
- package/dist/proxy/pipeline-snapshot.js +15 -0
- package/dist/proxy/proxy-core.d.ts +1 -1
- package/dist/proxy/proxy-core.js +1 -1
- package/dist/proxy/proxy-logging.d.ts +10 -2
- package/dist/proxy/proxy-logging.js +23 -9
- package/dist/proxy/response-transform.d.ts +7 -0
- package/dist/proxy/response-transform.js +15 -0
- package/dist/proxy/{enhancement-config.js → routing/enhancement-config.js} +1 -1
- package/dist/proxy/{mapping-resolver.d.ts → routing/mapping-resolver.d.ts} +1 -1
- package/dist/proxy/{mapping-resolver.js → routing/mapping-resolver.js} +1 -1
- package/dist/proxy/{model-state.js → routing/model-state.js} +1 -1
- package/dist/proxy/{overflow.d.ts → routing/overflow.d.ts} +1 -1
- package/dist/proxy/{overflow.js → routing/overflow.js} +3 -3
- package/dist/proxy/{usage-window-tracker.js → routing/usage-window-tracker.js} +3 -3
- package/dist/proxy/{transport.d.ts → transport/http.d.ts} +2 -2
- package/dist/proxy/{transport.js → transport/http.js} +3 -3
- package/dist/proxy/{stream-proxy.d.ts → transport/stream.d.ts} +4 -4
- package/dist/proxy/{stream-proxy.js → transport/stream.js} +25 -7
- package/dist/proxy/{transport-fn.d.ts → transport/transport-fn.d.ts} +5 -5
- package/dist/proxy/{transport-fn.js → transport/transport-fn.js} +11 -9
- package/dist/proxy/types.d.ts +3 -64
- package/dist/proxy/types.js +5 -34
- package/dist/storage/log-file-compressor.d.ts +15 -0
- package/dist/storage/log-file-compressor.js +83 -0
- package/dist/storage/log-file-writer.d.ts +21 -0
- package/dist/storage/log-file-writer.js +103 -0
- package/dist/storage/types.d.ts +16 -0
- package/dist/storage/types.js +5 -0
- package/dist/upgrade/deployment.d.ts +13 -0
- package/dist/upgrade/deployment.js +40 -0
- package/frontend-dist/assets/{CardContent-jQcfCC7J.js → CardContent-CxOF1feY.js} +1 -1
- package/frontend-dist/assets/{CardTitle-BrCTvULL.js → CardTitle-BSEFcEOM.js} +1 -1
- package/frontend-dist/assets/{CascadingModelSelect-BFh67j5d.js → CascadingModelSelect-DTwksDPZ.js} +1 -1
- package/frontend-dist/assets/{Checkbox-Bbt7JpdE.js → Checkbox-RfsERG07.js} +1 -1
- package/frontend-dist/assets/{CollapsibleTrigger-DMnEA0qC.js → CollapsibleTrigger-Dsjo7QlC.js} +1 -1
- package/frontend-dist/assets/{Collection-CVk3TPHc.js → Collection-rQ4eIYfa.js} +1 -1
- package/frontend-dist/assets/{Dashboard-Coftbg4B.js → Dashboard-YejfAPiB.js} +1 -1
- package/frontend-dist/assets/{DialogTitle-BbOAZzPQ.js → DialogTitle-DeFTnmgC.js} +1 -1
- package/frontend-dist/assets/{Input-DdHY9q0w.js → Input-CENz_g9t.js} +1 -1
- package/frontend-dist/assets/{Label-DRQv_Dr_.js → Label-BAciBrrd.js} +1 -1
- package/frontend-dist/assets/{Login-SV3ctFnJ.js → Login-DQkYFq7R.js} +1 -1
- package/frontend-dist/assets/{Logs-BG45kX6E.js → Logs-Dol8AX7z.js} +1 -1
- package/frontend-dist/assets/{ModelMappings-DEaBnRU3.js → ModelMappings-VEYW1TrW.js} +1 -1
- package/frontend-dist/assets/{Monitor-ZHOt11n-.js → Monitor-C0r9WefB.js} +1 -1
- package/frontend-dist/assets/{PopoverTrigger-z-Z3EjBk.js → PopoverTrigger-Cyqik5SE.js} +1 -1
- package/frontend-dist/assets/{PopperContent-DPC-6a3n.js → PopperContent-B7IuAHeq.js} +1 -1
- package/frontend-dist/assets/{Providers-DpY6pAcg.js → Providers-D8Z97edN.js} +1 -1
- package/frontend-dist/assets/{ProxyEnhancement-D6KBDXMp.js → ProxyEnhancement-Kn8r2SN6.js} +1 -1
- package/frontend-dist/assets/{RetryRules-DWI7_WLZ.js → RetryRules-F0295m4_.js} +1 -1
- package/frontend-dist/assets/{RouterKeys-CZ1657eX.js → RouterKeys-CFbPtUE_.js} +1 -1
- package/frontend-dist/assets/{RovingFocusItem-BREE2YEV.js → RovingFocusItem-D291Vjh8.js} +1 -1
- package/frontend-dist/assets/{Schedules-BVPsBRPi.js → Schedules-DWhF3uod.js} +1 -1
- package/frontend-dist/assets/{SelectValue-H8hwQwbk.js → SelectValue-BWlgUZa3.js} +1 -1
- package/frontend-dist/assets/Settings-BnIzEF_k.js +6 -0
- package/frontend-dist/assets/{Setup-yOYNKkOG.js → Setup-BglKyQKq.js} +1 -1
- package/frontend-dist/assets/{Switch-CojD3rTH.js → Switch-DyCR-CPu.js} +1 -1
- package/frontend-dist/assets/{TableHeader-awoHTsWN.js → TableHeader-DVUlBL35.js} +1 -1
- package/frontend-dist/assets/{TabsTrigger-DTKSFj85.js → TabsTrigger-BU1DY-C8.js} +1 -1
- package/frontend-dist/assets/{Teleport-DehYAXud.js → Teleport-BQgusr9g.js} +1 -1
- package/frontend-dist/assets/{TooltipTrigger-C2dl_dml.js → TooltipTrigger-Bv_QoBns.js} +1 -1
- package/frontend-dist/assets/{UnifiedRequestDialog-C8A-uSTR.js → UnifiedRequestDialog-f_evI835.js} +2 -2
- package/frontend-dist/assets/{VisuallyHidden-C8oaGi2S.js → VisuallyHidden-Con10z4F.js} +1 -1
- package/frontend-dist/assets/{VisuallyHiddenInput-BMc813t2.js → VisuallyHiddenInput-yrDtxucb.js} +1 -1
- package/frontend-dist/assets/{alert-dialog-C8TZQmU6.js → alert-dialog-2Db6Z7JQ.js} +1 -1
- package/frontend-dist/assets/arrow-down-WyouvE7T.js +1 -0
- package/frontend-dist/assets/{badge-BVh2WpA5.js → badge-DEhZfeI0.js} +1 -1
- package/frontend-dist/assets/button-Cnkbp_6J.js +12 -0
- package/frontend-dist/assets/check-BuqB5Nyb.js +1 -0
- package/frontend-dist/assets/{copy-DTOecxa9.js → copy-CwqZSuIG.js} +1 -1
- package/frontend-dist/assets/{dialog-kA7AUNoc.js → dialog-CVMKSdPr.js} +1 -1
- package/frontend-dist/assets/{file-text-DzZCFO7y.js → file-text-D0K8Hovo.js} +1 -1
- package/frontend-dist/assets/index-Ct718O93.js +1 -0
- package/frontend-dist/assets/{lib-ClDokUbt.js → lib-H3YI7EK4.js} +1 -1
- package/frontend-dist/assets/loader-circle-Be82FnVY.js +1 -0
- package/frontend-dist/assets/{useClipboard-DU1ne-Jw.js → useClipboard-Cd7k-5Yq.js} +1 -1
- package/frontend-dist/assets/{useFocusGuards-Btmdbg_F.js → useFocusGuards-luoLXnwV.js} +1 -1
- package/frontend-dist/assets/useFormControl-Da4ViGZF.js +1 -0
- package/frontend-dist/assets/{useLogRetention--EGNWXig.js → useLogRetention-DB4Iu6o_.js} +1 -1
- package/frontend-dist/assets/useNonce-DvAdQ48J.js +1 -0
- package/frontend-dist/assets/x-DB22csQl.js +1 -0
- package/frontend-dist/index.html +19 -19
- package/package.json +1 -1
- package/dist/proxy/anthropic.d.ts +0 -19
- package/dist/proxy/openai.d.ts +0 -19
- package/dist/proxy/proxy-handler.d.ts +0 -19
- package/dist/proxy/strategy/types.d.ts +0 -21
- package/dist/proxy/strategy/types.js +0 -1
- package/frontend-dist/assets/Settings-DHYaYRgU.js +0 -6
- package/frontend-dist/assets/arrow-down-D-cQXxau.js +0 -1
- package/frontend-dist/assets/button-N59D1BGa.js +0 -12
- package/frontend-dist/assets/check-dDgrw3T3.js +0 -1
- package/frontend-dist/assets/index-DVTeNVaa.js +0 -1
- package/frontend-dist/assets/loader-circle-DVHRL-38.js +0 -1
- package/frontend-dist/assets/useFormControl-C5Kjziuj.js +0 -1
- package/frontend-dist/assets/useNonce-Cp31yRzV.js +0 -1
- package/frontend-dist/assets/x-DMktsI_w.js +0 -1
- /package/dist/{config.d.ts → config/index.d.ts} +0 -0
- /package/dist/{config.js → config/index.js} +0 -0
- /package/dist/proxy/{scope.js → orchestration/scope.js} +0 -0
- /package/dist/proxy/{enhancement-config.d.ts → routing/enhancement-config.d.ts} +0 -0
- /package/dist/proxy/{model-state.d.ts → routing/model-state.d.ts} +0 -0
- /package/dist/proxy/{usage-window-tracker.d.ts → routing/usage-window-tracker.d.ts} +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** 服务键常量 — 避免魔法字符串,编译期类型保护 */
|
|
2
|
+
export declare const SERVICE_KEYS: {
|
|
3
|
+
readonly db: "db";
|
|
4
|
+
readonly matcher: "matcher";
|
|
5
|
+
readonly semaphoreManager: "semaphoreManager";
|
|
6
|
+
readonly tracker: "tracker";
|
|
7
|
+
readonly usageWindowTracker: "usageWindowTracker";
|
|
8
|
+
readonly sessionTracker: "sessionTracker";
|
|
9
|
+
readonly adaptiveController: "adaptiveController";
|
|
10
|
+
readonly logFileWriter: "logFileWriter";
|
|
11
|
+
};
|
|
12
|
+
export type ServiceKey = (typeof SERVICE_KEYS)[keyof typeof SERVICE_KEYS];
|
|
13
|
+
/**
|
|
14
|
+
* 轻量服务容器 — 懒加载单例工厂注册表。
|
|
15
|
+
*
|
|
16
|
+
* 用法:
|
|
17
|
+
* const c = new ServiceContainer();
|
|
18
|
+
* c.register(SERVICE_KEYS.db, () => db);
|
|
19
|
+
* c.register(SERVICE_KEYS.tracker, (c) => new RequestTracker(c.resolve(SERVICE_KEYS.db)));
|
|
20
|
+
* const tracker = c.resolve<RequestTracker>(SERVICE_KEYS.tracker);
|
|
21
|
+
*
|
|
22
|
+
* 注册的工厂最多执行一次(惰性求值 + 缓存)。
|
|
23
|
+
*/
|
|
24
|
+
export declare class ServiceContainer {
|
|
25
|
+
private readonly factories;
|
|
26
|
+
private readonly cache;
|
|
27
|
+
/** 注册服务工厂。重复注册同一 key 会覆盖(但已缓存的实例不会被清除)。 */
|
|
28
|
+
register<T>(key: string, factory: (c: ServiceContainer) => T): void;
|
|
29
|
+
/** 获取服务实例。首次调用时执行工厂并缓存。 */
|
|
30
|
+
resolve<T>(key: string): T;
|
|
31
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** 服务键常量 — 避免魔法字符串,编译期类型保护 */
|
|
2
|
+
export const SERVICE_KEYS = {
|
|
3
|
+
db: "db",
|
|
4
|
+
matcher: "matcher",
|
|
5
|
+
semaphoreManager: "semaphoreManager",
|
|
6
|
+
tracker: "tracker",
|
|
7
|
+
usageWindowTracker: "usageWindowTracker",
|
|
8
|
+
sessionTracker: "sessionTracker",
|
|
9
|
+
adaptiveController: "adaptiveController",
|
|
10
|
+
logFileWriter: "logFileWriter",
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* 轻量服务容器 — 懒加载单例工厂注册表。
|
|
14
|
+
*
|
|
15
|
+
* 用法:
|
|
16
|
+
* const c = new ServiceContainer();
|
|
17
|
+
* c.register(SERVICE_KEYS.db, () => db);
|
|
18
|
+
* c.register(SERVICE_KEYS.tracker, (c) => new RequestTracker(c.resolve(SERVICE_KEYS.db)));
|
|
19
|
+
* const tracker = c.resolve<RequestTracker>(SERVICE_KEYS.tracker);
|
|
20
|
+
*
|
|
21
|
+
* 注册的工厂最多执行一次(惰性求值 + 缓存)。
|
|
22
|
+
*/
|
|
23
|
+
export class ServiceContainer {
|
|
24
|
+
factories = new Map();
|
|
25
|
+
cache = new Map();
|
|
26
|
+
/** 注册服务工厂。重复注册同一 key 会覆盖(但已缓存的实例不会被清除)。 */
|
|
27
|
+
register(key, factory) {
|
|
28
|
+
this.factories.set(key, factory);
|
|
29
|
+
}
|
|
30
|
+
/** 获取服务实例。首次调用时执行工厂并缓存。 */
|
|
31
|
+
resolve(key) {
|
|
32
|
+
if (this.cache.has(key))
|
|
33
|
+
return this.cache.get(key);
|
|
34
|
+
const factory = this.factories.get(key);
|
|
35
|
+
if (!factory)
|
|
36
|
+
throw new Error(`Service not registered: "${key}"`);
|
|
37
|
+
const instance = factory(this);
|
|
38
|
+
this.cache.set(key, instance);
|
|
39
|
+
return instance;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
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
|
+
/**
|
|
18
|
+
* 跨 provider failover 时由 ResilienceLayer 抛出,
|
|
19
|
+
* orchestrator 捕获后释放当前信号量并获取新 provider 的信号量。
|
|
20
|
+
*/
|
|
21
|
+
export declare class ProviderSwitchNeeded extends Error {
|
|
22
|
+
readonly targetProviderId: string;
|
|
23
|
+
readonly attempts?: ResilienceAttempt[] | undefined;
|
|
24
|
+
readonly lastResult?: TransportResult | undefined;
|
|
25
|
+
constructor(targetProviderId: string, attempts?: ResilienceAttempt[] | undefined, lastResult?: TransportResult | undefined);
|
|
26
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 跨 provider failover 时由 ResilienceLayer 抛出,
|
|
29
|
+
* orchestrator 捕获后释放当前信号量并获取新 provider 的信号量。
|
|
30
|
+
*/
|
|
31
|
+
export class ProviderSwitchNeeded extends Error {
|
|
32
|
+
targetProviderId;
|
|
33
|
+
attempts;
|
|
34
|
+
lastResult;
|
|
35
|
+
constructor(targetProviderId, attempts, lastResult) {
|
|
36
|
+
super(`Provider switch needed: ${targetProviderId}`);
|
|
37
|
+
this.targetProviderId = targetProviderId;
|
|
38
|
+
this.attempts = attempts;
|
|
39
|
+
this.lastResult = lastResult;
|
|
40
|
+
this.name = "ProviderSwitchNeeded";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ConcurrencyConfig } from "./types.js";
|
|
2
|
+
export type { ConcurrencyConfig };
|
|
3
|
+
export interface EnhancementConfig {
|
|
4
|
+
claude_code_enabled: boolean;
|
|
5
|
+
tool_call_loop_enabled: boolean;
|
|
6
|
+
stream_loop_enabled: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** Provider 自适应/手动并发配置(DB 字段) */
|
|
9
|
+
export interface ProviderConcurrencyParams {
|
|
10
|
+
adaptive_enabled: number;
|
|
11
|
+
max_concurrency: number;
|
|
12
|
+
queue_timeout_ms: number;
|
|
13
|
+
max_queue_size: number;
|
|
14
|
+
}
|
|
15
|
+
export interface StateRegistry {
|
|
16
|
+
/** 刷新重试规则缓存(RetryRuleMatcher.load) */
|
|
17
|
+
refreshRetryRules(): void;
|
|
18
|
+
/** 更新 provider 并发配置(ProviderSemaphoreManager.updateConfig) */
|
|
19
|
+
updateProviderConcurrency(providerId: string, config: ConcurrencyConfig): void;
|
|
20
|
+
/** 移除 provider 的信号量(ProviderSemaphoreManager.remove) */
|
|
21
|
+
removeProvider(providerId: string): void;
|
|
22
|
+
/** 移除所有信号量配置(ProviderSemaphoreManager.removeAll) */
|
|
23
|
+
removeAllProviders(): void;
|
|
24
|
+
/** 获取 provider 并发状态(ProviderSemaphoreManager.getStatus) */
|
|
25
|
+
getProviderStatus(providerId: string): {
|
|
26
|
+
active: number;
|
|
27
|
+
queued: number;
|
|
28
|
+
};
|
|
29
|
+
/** 清空所有会话模型状态(modelState.clearAll) */
|
|
30
|
+
clearModelState(): void;
|
|
31
|
+
/** 删除指定会话模型状态(modelState.delete) */
|
|
32
|
+
deleteModelState(keyId: string, sessionId: string): void;
|
|
33
|
+
/** 读取 proxy enhancement 配置 */
|
|
34
|
+
getEnhancementConfig(): EnhancementConfig;
|
|
35
|
+
/** 同步 provider 的自适应并发配置(AdaptiveConcurrencyController.syncProvider) */
|
|
36
|
+
syncAdaptiveProvider(providerId: string, params: ProviderConcurrencyParams): void;
|
|
37
|
+
/** 移除 provider 的自适应并发状态(AdaptiveConcurrencyController.remove) */
|
|
38
|
+
removeAdaptiveProvider(providerId: string): void;
|
|
39
|
+
/** 获取 provider 的自适应并发状态 */
|
|
40
|
+
getAdaptiveStatus(providerId: string): import("../proxy/adaptive-controller.js").AdaptiveState | undefined;
|
|
41
|
+
/** 从 DB 重新读取所有 provider 配置,重建信号量/adaptive/tracker 缓存(导入配置后调用) */
|
|
42
|
+
reinitializeProviders(): void;
|
|
43
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export interface Target {
|
|
2
|
+
backend_model: string;
|
|
3
|
+
provider_id: string;
|
|
4
|
+
overflow_provider_id?: string;
|
|
5
|
+
overflow_model?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ResolveContext {
|
|
8
|
+
now: Date;
|
|
9
|
+
excludeTargets?: Target[];
|
|
10
|
+
}
|
|
11
|
+
/** Provider 级并发控制配置(唯一来源,替代 semaphore.ts 和 registry.ts 中的重复定义) */
|
|
12
|
+
export interface ConcurrencyConfig {
|
|
13
|
+
maxConcurrency: number;
|
|
14
|
+
queueTimeoutMs: number;
|
|
15
|
+
maxQueueSize: number;
|
|
16
|
+
}
|
|
17
|
+
export interface ConcurrencyOverride {
|
|
18
|
+
max_concurrency?: number;
|
|
19
|
+
queue_timeout_ms?: number;
|
|
20
|
+
max_queue_size?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface ResolveResult {
|
|
23
|
+
target: Target;
|
|
24
|
+
concurrency_override?: ConcurrencyOverride;
|
|
25
|
+
/** 活跃规则(schedule 或 base)中的 target 总数,用于 failover 判断 */
|
|
26
|
+
targetCount: number;
|
|
27
|
+
}
|
|
28
|
+
export interface MetricsResult {
|
|
29
|
+
input_tokens: number | null;
|
|
30
|
+
output_tokens: number | null;
|
|
31
|
+
cache_creation_tokens: number | null;
|
|
32
|
+
cache_read_tokens: number | null;
|
|
33
|
+
ttft_ms: number | null;
|
|
34
|
+
/** T6 - T0: proxy end-to-end streaming duration */
|
|
35
|
+
total_duration_ms: number | null;
|
|
36
|
+
/** @deprecated Use total_tps instead */
|
|
37
|
+
tokens_per_second: number | null;
|
|
38
|
+
stop_reason: string | null;
|
|
39
|
+
is_complete: number;
|
|
40
|
+
input_tokens_estimated?: number;
|
|
41
|
+
thinking_tokens: number | null;
|
|
42
|
+
/** T3 - T0: request start to last thinking delta */
|
|
43
|
+
thinking_duration_ms: number | null;
|
|
44
|
+
thinking_tps: number | null;
|
|
45
|
+
/** T6 - T3 (thinking) or T6 - T0 (non-thinking) */
|
|
46
|
+
non_thinking_duration_ms: number | null;
|
|
47
|
+
non_thinking_tps: number | null;
|
|
48
|
+
total_tps: number | null;
|
|
49
|
+
text_tokens: number | null;
|
|
50
|
+
tool_use_tokens: number | null;
|
|
51
|
+
}
|
|
52
|
+
export type RawHeaders = Record<string, string | string[] | undefined>;
|
|
53
|
+
export type TransportResult = {
|
|
54
|
+
kind: "success";
|
|
55
|
+
statusCode: number;
|
|
56
|
+
body: string;
|
|
57
|
+
headers: Record<string, string>;
|
|
58
|
+
sentHeaders: Record<string, string>;
|
|
59
|
+
sentBody: string;
|
|
60
|
+
} | {
|
|
61
|
+
kind: "stream_success";
|
|
62
|
+
statusCode: number;
|
|
63
|
+
metrics?: MetricsResult;
|
|
64
|
+
upstreamResponseHeaders?: Record<string, string>;
|
|
65
|
+
sentHeaders: Record<string, string>;
|
|
66
|
+
} | {
|
|
67
|
+
kind: "stream_error";
|
|
68
|
+
statusCode: number;
|
|
69
|
+
body: string;
|
|
70
|
+
headers: Record<string, string>;
|
|
71
|
+
sentHeaders: Record<string, string>;
|
|
72
|
+
headersSent?: boolean;
|
|
73
|
+
} | {
|
|
74
|
+
kind: "stream_abort";
|
|
75
|
+
statusCode: number;
|
|
76
|
+
metrics?: MetricsResult;
|
|
77
|
+
upstreamResponseHeaders?: Record<string, string>;
|
|
78
|
+
sentHeaders: Record<string, string>;
|
|
79
|
+
} | {
|
|
80
|
+
kind: "error";
|
|
81
|
+
statusCode: number;
|
|
82
|
+
body: string;
|
|
83
|
+
headers: Record<string, string>;
|
|
84
|
+
sentHeaders: Record<string, string>;
|
|
85
|
+
sentBody: string;
|
|
86
|
+
} | {
|
|
87
|
+
kind: "throw";
|
|
88
|
+
error: Error;
|
|
89
|
+
headersSent?: boolean;
|
|
90
|
+
};
|
|
91
|
+
/** 单次 resilience 尝试的记录 */
|
|
92
|
+
export interface ResilienceAttempt {
|
|
93
|
+
target: Target;
|
|
94
|
+
attemptIndex: number;
|
|
95
|
+
statusCode: number | null;
|
|
96
|
+
error: string | null;
|
|
97
|
+
latencyMs: number;
|
|
98
|
+
responseBody: string | null;
|
|
99
|
+
/** 上游响应 headers(throw 和 stream_success/stream_abort 时为 null) */
|
|
100
|
+
responseHeaders: Record<string, string> | null;
|
|
101
|
+
/** TransportResult.kind,用于区分 stream_error 等特殊类型 */
|
|
102
|
+
resultKind: TransportResult["kind"];
|
|
103
|
+
}
|
|
104
|
+
/** 流式传输阶段状态 */
|
|
105
|
+
export type StreamState = "BUFFERING" | "STREAMING" | "COMPLETED" | "EARLY_ERROR" | "ABORTED";
|
package/dist/db/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { getMappingGroup, getMappingGroupById, getAllMappingGroups, createMappin
|
|
|
6
6
|
export type { MappingGroup, ProviderModelEntry } from "./mappings.js";
|
|
7
7
|
export { getActiveRetryRules, getAllRetryRules, getRetryRuleById, createRetryRule, updateRetryRule, deleteRetryRule, } from "./retry-rules.js";
|
|
8
8
|
export type { RetryRule } from "./retry-rules.js";
|
|
9
|
-
export { insertRequestLog, getRequestLogs, getRequestLogById, deleteLogsBefore, getRequestLogChildren, getRequestLogsGrouped,
|
|
9
|
+
export { insertRequestLog, getRequestLogs, getRequestLogById, deleteLogsBefore, getRequestLogChildren, getRequestLogsGrouped, updateLogStreamContent, updateLogClientStatus, estimateLogTableSize, deleteOldestLogs, getLogCount, updateLogPipelineSnapshot, } from "./logs.js";
|
|
10
10
|
export type { RequestLog, RequestLogGroupedRow, RequestLogListRow } from "./logs.js";
|
|
11
11
|
export { getRouterKeyByHash, getAllRouterKeys, getRouterKeyById, createRouterKey, updateRouterKey, deleteRouterKey, getAvailableModels, } from "./router-keys.js";
|
|
12
12
|
export type { RouterKey } from "./router-keys.js";
|
package/dist/db/index.js
CHANGED
|
@@ -85,7 +85,7 @@ export function initDatabase(dbPath) {
|
|
|
85
85
|
export { getActiveProviders, getAllProviders, getProviderById, getActiveProviderByName, getActiveProvidersWithModels, createProvider, updateProvider, deleteProvider, PROVIDER_CONCURRENCY_DEFAULTS, } from "./providers.js";
|
|
86
86
|
export { getMappingGroup, getMappingGroupById, getAllMappingGroups, createMappingGroup, updateMappingGroup, deleteMappingGroup, getActiveProviderModels, resolveByProviderModel, } from "./mappings.js";
|
|
87
87
|
export { getActiveRetryRules, getAllRetryRules, getRetryRuleById, createRetryRule, updateRetryRule, deleteRetryRule, } from "./retry-rules.js";
|
|
88
|
-
export { insertRequestLog, getRequestLogs, getRequestLogById, deleteLogsBefore, getRequestLogChildren, getRequestLogsGrouped,
|
|
88
|
+
export { insertRequestLog, getRequestLogs, getRequestLogById, deleteLogsBefore, getRequestLogChildren, getRequestLogsGrouped, updateLogStreamContent, updateLogClientStatus, estimateLogTableSize, deleteOldestLogs, getLogCount, updateLogPipelineSnapshot, } from "./logs.js";
|
|
89
89
|
export { getRouterKeyByHash, getAllRouterKeys, getRouterKeyById, createRouterKey, updateRouterKey, deleteRouterKey, getAvailableModels, } from "./router-keys.js";
|
|
90
90
|
export { getMetricsSummary, getMetricsTimeseries, insertMetrics } from "./metrics.js";
|
|
91
91
|
export { getStats } from "./stats.js";
|
package/dist/db/logs.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import Database from "better-sqlite3";
|
|
2
|
+
import type { LogFileWriter } from "../storage/log-file-writer.js";
|
|
3
|
+
import { type RetryMatcher } from "../proxy/log-detail-policy.js";
|
|
2
4
|
export interface RequestLog {
|
|
3
5
|
id: string;
|
|
4
6
|
api_type: string;
|
|
@@ -17,14 +19,6 @@ export interface RequestLog {
|
|
|
17
19
|
is_failover: number;
|
|
18
20
|
original_request_id: string | null;
|
|
19
21
|
original_model: string | null;
|
|
20
|
-
input_tokens: number | null;
|
|
21
|
-
output_tokens: number | null;
|
|
22
|
-
cache_read_tokens: number | null;
|
|
23
|
-
ttft_ms: number | null;
|
|
24
|
-
tokens_per_second: number | null;
|
|
25
|
-
stop_reason: string | null;
|
|
26
|
-
backend_model: string | null;
|
|
27
|
-
metrics_complete: number;
|
|
28
22
|
stream_text_content: string | null;
|
|
29
23
|
session_id: string | null;
|
|
30
24
|
}
|
|
@@ -53,8 +47,14 @@ export interface RequestLogInsert {
|
|
|
53
47
|
original_model?: string | null;
|
|
54
48
|
session_id?: string | null;
|
|
55
49
|
client_status_code?: number | null;
|
|
50
|
+
pipeline_snapshot?: string | null;
|
|
56
51
|
}
|
|
57
|
-
export
|
|
52
|
+
export interface LogWriteContext {
|
|
53
|
+
matcher?: RetryMatcher | null;
|
|
54
|
+
logFileWriter?: LogFileWriter | null;
|
|
55
|
+
responseBody?: string | null;
|
|
56
|
+
}
|
|
57
|
+
export declare function insertRequestLog(db: Database.Database, log: RequestLogInsert, writeContext?: LogWriteContext): void;
|
|
58
58
|
export declare function getRequestLogs(db: Database.Database, options: {
|
|
59
59
|
page: number;
|
|
60
60
|
limit: number;
|
|
@@ -69,24 +69,10 @@ export declare function getRequestLogs(db: Database.Database, options: {
|
|
|
69
69
|
total: number;
|
|
70
70
|
};
|
|
71
71
|
export declare function getRequestLogById(db: Database.Database, id: string): RequestLogListRow | undefined;
|
|
72
|
-
type MetricsUpdate = {
|
|
73
|
-
input_tokens?: number | null;
|
|
74
|
-
output_tokens?: number | null;
|
|
75
|
-
cache_read_tokens?: number | null;
|
|
76
|
-
ttft_ms?: number | null;
|
|
77
|
-
tokens_per_second?: number | null;
|
|
78
|
-
stop_reason?: string | null;
|
|
79
|
-
is_complete?: number;
|
|
80
|
-
input_tokens_estimated?: number;
|
|
81
|
-
};
|
|
82
|
-
/** 双写:collectTransportMetrics 写 request_metrics 的同时,更新 request_logs 的冗余列 */
|
|
83
|
-
export declare function updateLogMetrics(db: Database.Database, logId: string, m: MetricsUpdate): void;
|
|
84
72
|
/** 流式请求完成后,将 tracker 中累积的文本内容写入 request_logs */
|
|
85
73
|
export declare function updateLogStreamContent(db: Database.Database, logId: string, textContent: string): void;
|
|
86
74
|
/** 当 router 返回给客户端的 status code 与上游不同时,记录实际发送的 status */
|
|
87
75
|
export declare function updateLogClientStatus(db: Database.Database, logId: string, clientStatusCode: number): void;
|
|
88
|
-
/** 启动时回填:从 request_metrics 补齐 metrics_complete = 0 但实际有指标的行 */
|
|
89
|
-
export declare function backfillMetricsFromRequestMetrics(db: Database.Database): number;
|
|
90
76
|
export declare function deleteLogsBefore(db: Database.Database, beforeDate: string): number;
|
|
91
77
|
/** 估算 request_logs 表占用字节数 */
|
|
92
78
|
export declare function estimateLogTableSize(db: Database.Database): number;
|
|
@@ -113,4 +99,5 @@ export declare function getRequestLogsGrouped(db: Database.Database, options: {
|
|
|
113
99
|
data: RequestLogGroupedRow[];
|
|
114
100
|
total: number;
|
|
115
101
|
};
|
|
116
|
-
|
|
102
|
+
/** 后续 pipeline 阶段完成后,回写 snapshot 到已有日志 */
|
|
103
|
+
export declare function updateLogPipelineSnapshot(db: Database.Database, logId: string, snapshot: string): void;
|
package/dist/db/logs.js
CHANGED
|
@@ -1,17 +1,35 @@
|
|
|
1
|
+
import { shouldPreserveDetail } from "../proxy/log-detail-policy.js";
|
|
1
2
|
// --- request_logs ---
|
|
2
|
-
/** 日志列表查询共享的 SELECT 列 + JOIN 子句(metrics 已冗余到 request_logs,无需 JOIN request_metrics) */
|
|
3
3
|
const LOG_LIST_SELECT = `rl.id, rl.api_type, rl.model, rl.provider_id, rl.status_code, rl.client_status_code, rl.latency_ms,
|
|
4
4
|
rl.is_stream, rl.error_message, rl.created_at, rl.is_retry, rl.is_failover, rl.original_request_id, rl.original_model,
|
|
5
5
|
CASE WHEN rl.provider_id = 'router' THEN rl.upstream_request ELSE NULL END AS upstream_request,
|
|
6
|
-
rl.
|
|
7
|
-
|
|
6
|
+
rl.session_id, rl.pipeline_snapshot,
|
|
7
|
+
rm.input_tokens, rm.output_tokens, rm.cache_read_tokens, rm.ttft_ms,
|
|
8
|
+
rm.tokens_per_second, rm.stop_reason, rm.backend_model, rm.is_complete AS metrics_complete,
|
|
9
|
+
rm.input_tokens_estimated,
|
|
8
10
|
COALESCE(p.name, rl.provider_id) AS provider_name`;
|
|
9
|
-
const LOG_LIST_JOIN = `LEFT JOIN providers p ON p.id = rl.provider_id`;
|
|
10
|
-
export function insertRequestLog(db, log) {
|
|
11
|
+
const LOG_LIST_JOIN = `LEFT JOIN providers p ON p.id = rl.provider_id LEFT JOIN request_metrics rm ON rm.request_log_id = rl.id`;
|
|
12
|
+
export function insertRequestLog(db, log, writeContext) {
|
|
13
|
+
// 文件写入:始终写入全文
|
|
14
|
+
if (writeContext?.logFileWriter) {
|
|
15
|
+
writeContext.logFileWriter.write({
|
|
16
|
+
id: log.id,
|
|
17
|
+
created_at: log.created_at,
|
|
18
|
+
api_type: log.api_type,
|
|
19
|
+
status_code: log.status_code,
|
|
20
|
+
client_request: log.client_request ?? null,
|
|
21
|
+
upstream_request: log.upstream_request ?? null,
|
|
22
|
+
upstream_response: log.upstream_response ?? null,
|
|
23
|
+
stream_text_content: null,
|
|
24
|
+
pipeline_snapshot: log.pipeline_snapshot ?? null,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
// 详情保留判定
|
|
28
|
+
const preserveDetail = shouldPreserveDetail(log.status_code, writeContext?.responseBody ?? null, writeContext?.matcher ?? null, !!writeContext?.logFileWriter);
|
|
11
29
|
db.prepare(`INSERT INTO request_logs (id, api_type, model, provider_id, status_code, client_status_code, latency_ms,
|
|
12
30
|
is_stream, error_message, created_at, client_request, upstream_request, upstream_response,
|
|
13
|
-
is_retry, is_failover, original_request_id, router_key_id, original_model, session_id)
|
|
14
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(log.id, log.api_type, log.model, log.provider_id, log.status_code, log.client_status_code ?? null, log.latency_ms, log.is_stream, log.error_message, log.created_at, log.client_request ?? null, log.upstream_request ?? null, log.upstream_response ?? null, log.is_retry ?? 0, log.is_failover ?? 0, log.original_request_id ?? null, log.router_key_id ?? null, log.original_model ?? null, log.session_id ?? null);
|
|
31
|
+
is_retry, is_failover, original_request_id, router_key_id, original_model, session_id, pipeline_snapshot)
|
|
32
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(log.id, log.api_type, log.model, log.provider_id, log.status_code, log.client_status_code ?? null, log.latency_ms, log.is_stream, log.error_message, log.created_at, preserveDetail ? (log.client_request ?? null) : null, preserveDetail ? (log.upstream_request ?? null) : null, preserveDetail ? (log.upstream_response ?? null) : null, log.is_retry ?? 0, log.is_failover ?? 0, log.original_request_id ?? null, log.router_key_id ?? null, log.original_model ?? null, log.session_id ?? null, log.pipeline_snapshot ?? null);
|
|
15
33
|
}
|
|
16
34
|
function buildLogWhereClause(options, baseCondition) {
|
|
17
35
|
let where = baseCondition;
|
|
@@ -63,19 +81,15 @@ export function getRequestLogs(db, options) {
|
|
|
63
81
|
return { data, total };
|
|
64
82
|
}
|
|
65
83
|
export function getRequestLogById(db, id) {
|
|
66
|
-
return db.prepare(`SELECT rl.*,
|
|
67
|
-
|
|
84
|
+
return db.prepare(`SELECT rl.*, rm.input_tokens, rm.output_tokens, rm.cache_read_tokens, rm.ttft_ms,
|
|
85
|
+
rm.tokens_per_second, rm.stop_reason, rm.backend_model, rm.is_complete AS metrics_complete,
|
|
86
|
+
rm.input_tokens_estimated,
|
|
87
|
+
COALESCE(p.name, rl.provider_id) AS provider_name
|
|
88
|
+
FROM request_logs rl
|
|
89
|
+
LEFT JOIN providers p ON p.id = rl.provider_id
|
|
90
|
+
LEFT JOIN request_metrics rm ON rm.request_log_id = rl.id
|
|
68
91
|
WHERE rl.id = ?`).get(id);
|
|
69
92
|
}
|
|
70
|
-
/** 双写:collectTransportMetrics 写 request_metrics 的同时,更新 request_logs 的冗余列 */
|
|
71
|
-
export function updateLogMetrics(db, logId, m) {
|
|
72
|
-
db.prepare(`UPDATE request_logs SET
|
|
73
|
-
input_tokens = ?, output_tokens = ?, cache_read_tokens = ?,
|
|
74
|
-
ttft_ms = ?, tokens_per_second = ?, stop_reason = ?,
|
|
75
|
-
backend_model = (SELECT backend_model FROM request_metrics WHERE request_log_id = ?),
|
|
76
|
-
metrics_complete = ?, input_tokens_estimated = ?
|
|
77
|
-
WHERE id = ?`).run(m.input_tokens ?? null, m.output_tokens ?? null, m.cache_read_tokens ?? null, m.ttft_ms ?? null, m.tokens_per_second ?? null, m.stop_reason ?? null, logId, m.is_complete ?? 1, m.input_tokens_estimated ?? 0, logId);
|
|
78
|
-
}
|
|
79
93
|
/** 流式请求完成后,将 tracker 中累积的文本内容写入 request_logs */
|
|
80
94
|
export function updateLogStreamContent(db, logId, textContent) {
|
|
81
95
|
db.prepare("UPDATE request_logs SET stream_text_content = ? WHERE id = ?").run(textContent, logId);
|
|
@@ -84,25 +98,6 @@ export function updateLogStreamContent(db, logId, textContent) {
|
|
|
84
98
|
export function updateLogClientStatus(db, logId, clientStatusCode) {
|
|
85
99
|
db.prepare("UPDATE request_logs SET client_status_code = ? WHERE id = ?").run(clientStatusCode, logId);
|
|
86
100
|
}
|
|
87
|
-
/** 启动时回填:从 request_metrics 补齐 metrics_complete = 0 但实际有指标的行 */
|
|
88
|
-
export function backfillMetricsFromRequestMetrics(db) {
|
|
89
|
-
return db.prepare(`
|
|
90
|
-
UPDATE request_logs
|
|
91
|
-
SET
|
|
92
|
-
input_tokens = rm.input_tokens,
|
|
93
|
-
output_tokens = rm.output_tokens,
|
|
94
|
-
cache_read_tokens = rm.cache_read_tokens,
|
|
95
|
-
ttft_ms = rm.ttft_ms,
|
|
96
|
-
tokens_per_second = rm.tokens_per_second,
|
|
97
|
-
stop_reason = rm.stop_reason,
|
|
98
|
-
backend_model = rm.backend_model,
|
|
99
|
-
metrics_complete = rm.is_complete,
|
|
100
|
-
input_tokens_estimated = rm.input_tokens_estimated
|
|
101
|
-
FROM request_metrics rm
|
|
102
|
-
WHERE rm.request_log_id = request_logs.id
|
|
103
|
-
AND request_logs.metrics_complete = 0
|
|
104
|
-
`).run().changes;
|
|
105
|
-
}
|
|
106
101
|
export function deleteLogsBefore(db, beforeDate) {
|
|
107
102
|
const changes = db.prepare("DELETE FROM request_logs WHERE created_at < ?").run(beforeDate).changes;
|
|
108
103
|
if (changes > 0) {
|
|
@@ -118,7 +113,7 @@ export function estimateLogTableSize(db) {
|
|
|
118
113
|
SELECT COALESCE(SUM(
|
|
119
114
|
COALESCE(length(client_request), 0) + COALESCE(length(upstream_request), 0) +
|
|
120
115
|
COALESCE(length(upstream_response), 0) + COALESCE(length(stream_text_content), 0) +
|
|
121
|
-
COALESCE(length(error_message), 0) + ?
|
|
116
|
+
COALESCE(length(error_message), 0) + COALESCE(length(pipeline_snapshot), 0) + ?
|
|
122
117
|
), 0) as size
|
|
123
118
|
FROM request_logs
|
|
124
119
|
`).get(ROW_METADATA_BYTES);
|
|
@@ -177,3 +172,7 @@ export function getRequestLogsGrouped(db, options) {
|
|
|
177
172
|
.all(...params, options.limit, offset);
|
|
178
173
|
return { data, total };
|
|
179
174
|
}
|
|
175
|
+
/** 后续 pipeline 阶段完成后,回写 snapshot 到已有日志 */
|
|
176
|
+
export function updateLogPipelineSnapshot(db, logId, snapshot) {
|
|
177
|
+
db.prepare("UPDATE request_logs SET pipeline_snapshot = ? WHERE id = ?").run(snapshot, logId);
|
|
178
|
+
}
|
package/dist/db/metrics.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
|
-
import { MS_PER_SECOND } from "../constants.js";
|
|
2
|
+
import { MS_PER_SECOND } from "../core/constants.js";
|
|
3
3
|
export function insertMetrics(db, m) {
|
|
4
4
|
const id = randomUUID();
|
|
5
5
|
db.prepare(`INSERT INTO request_metrics (id, request_log_id, provider_id, backend_model, api_type, router_key_id, status_code,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE request_logs ADD COLUMN pipeline_snapshot TEXT;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- 034_drop_redundant_log_columns.sql
|
|
2
|
+
-- request_logs 与 request_metrics 双写冗余清理:
|
|
3
|
+
-- metrics 字段统一由 request_metrics 承载,日志列表查询改用 LEFT JOIN。
|
|
4
|
+
|
|
5
|
+
ALTER TABLE request_logs DROP COLUMN input_tokens;
|
|
6
|
+
ALTER TABLE request_logs DROP COLUMN output_tokens;
|
|
7
|
+
ALTER TABLE request_logs DROP COLUMN cache_read_tokens;
|
|
8
|
+
ALTER TABLE request_logs DROP COLUMN ttft_ms;
|
|
9
|
+
ALTER TABLE request_logs DROP COLUMN tokens_per_second;
|
|
10
|
+
ALTER TABLE request_logs DROP COLUMN stop_reason;
|
|
11
|
+
ALTER TABLE request_logs DROP COLUMN backend_model;
|
|
12
|
+
ALTER TABLE request_logs DROP COLUMN metrics_complete;
|
|
13
|
+
ALTER TABLE request_logs DROP COLUMN input_tokens_estimated;
|
package/dist/db/settings.d.ts
CHANGED
|
@@ -10,3 +10,5 @@ export declare function getLogTableMaxSizeMb(db: Database.Database): number;
|
|
|
10
10
|
export declare function setLogTableMaxSizeMb(db: Database.Database, mb: number): void;
|
|
11
11
|
export declare function getConfigSyncSource(db: Database.Database): "github" | "gitee";
|
|
12
12
|
export declare function setConfigSyncSource(db: Database.Database, source: "github" | "gitee"): void;
|
|
13
|
+
export declare function getDetailLogEnabled(db: Database.Database): boolean;
|
|
14
|
+
export declare function getLogFileRetentionDays(db: Database.Database): number;
|
package/dist/db/settings.js
CHANGED
|
@@ -39,3 +39,12 @@ export function getConfigSyncSource(db) {
|
|
|
39
39
|
export function setConfigSyncSource(db, source) {
|
|
40
40
|
setSetting(db, "config_sync_source", source);
|
|
41
41
|
}
|
|
42
|
+
export function getDetailLogEnabled(db) {
|
|
43
|
+
const row = db.prepare("SELECT value FROM settings WHERE key = ?").get("detail_log_enabled");
|
|
44
|
+
return row ? row.value !== "0" : true;
|
|
45
|
+
}
|
|
46
|
+
const DEFAULT_LOG_FILE_RETENTION_DAYS = 3;
|
|
47
|
+
export function getLogFileRetentionDays(db) {
|
|
48
|
+
const row = db.prepare("SELECT value FROM settings WHERE key = ?").get("log_file_retention_days");
|
|
49
|
+
return row ? parseInt(row.value, 10) : DEFAULT_LOG_FILE_RETENTION_DAYS;
|
|
50
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { FastifyInstance } from "fastify";
|
|
3
|
-
import { Config } from "./config.js";
|
|
4
|
-
import {
|
|
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";
|
|
7
|
+
import { UsageWindowTracker } from "./proxy/routing/usage-window-tracker.js";
|
|
5
8
|
import { CheckerOptions } from "./upgrade/checker.js";
|
|
6
9
|
import Database from "better-sqlite3";
|
|
7
10
|
export interface AppOptions {
|
|
@@ -9,6 +12,11 @@ export interface AppOptions {
|
|
|
9
12
|
db?: Database.Database;
|
|
10
13
|
upgradeCheckerOptions?: CheckerOptions;
|
|
11
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* 共享初始化逻辑 — 启动时和导入配置后都需要调用。
|
|
17
|
+
* 从 DB 读取所有 provider,初始化信号量/自适应并发/tracker 缓存。
|
|
18
|
+
*/
|
|
19
|
+
export declare function initializeProviderState(db: Database.Database, semaphoreManager: ProviderSemaphoreManager, adaptiveController: AdaptiveConcurrencyController, tracker: RequestTracker): void;
|
|
12
20
|
export declare function buildApp(options?: AppOptions): Promise<{
|
|
13
21
|
app: FastifyInstance;
|
|
14
22
|
db: Database.Database;
|