llm-simple-router 1.0.20 → 1.0.22
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/dist/admin/groups.js +1 -1
- package/dist/admin/logs.js +1 -1
- package/dist/admin/mappings.js +1 -1
- package/dist/admin/monitor.js +1 -1
- package/dist/admin/providers.d.ts +4 -2
- package/dist/admin/providers.js +7 -16
- package/dist/admin/proxy-enhancement.d.ts +2 -0
- package/dist/admin/proxy-enhancement.js +2 -3
- package/dist/admin/quick-setup.js +2 -2
- package/dist/admin/retry-rules.js +1 -1
- package/dist/admin/router-keys.js +1 -1
- package/dist/admin/routes.d.ts +4 -2
- package/dist/admin/routes.js +3 -4
- package/dist/admin/schedules.js +3 -40
- package/dist/admin/settings.js +1 -1
- package/dist/admin/setup.js +1 -1
- package/dist/admin/utils.d.ts +11 -0
- package/dist/admin/utils.js +57 -0
- package/dist/core/log-detail-policy.d.ts +12 -0
- package/dist/core/log-detail-policy.js +21 -0
- package/dist/core/provider-connectivity.d.ts +22 -0
- package/dist/core/provider-connectivity.js +5 -0
- package/dist/core/proxy-agent-types.d.ts +9 -0
- package/dist/core/proxy-agent-types.js +6 -0
- package/dist/core/registry.d.ts +7 -0
- package/dist/db/logs.d.ts +1 -1
- package/dist/db/logs.js +1 -1
- package/dist/index.js +7 -1
- package/dist/proxy/log-detail-policy.d.ts +1 -12
- package/dist/proxy/log-detail-policy.js +2 -20
- package/dist/proxy/transport/provider-connectivity.d.ts +11 -0
- package/dist/proxy/transport/provider-connectivity.js +11 -0
- package/dist/proxy/transport/proxy-agent.d.ts +2 -1
- package/frontend-dist/assets/{AuthLayout-BwI_T7qz.js → AuthLayout-BvYyVNay.js} +1 -1
- package/frontend-dist/assets/{Card-C5jCkE1m.js → Card-CrAceTFb.js} +1 -1
- package/frontend-dist/assets/{CardContent-Dozca7Pu.js → CardContent-otK_vTWx.js} +1 -1
- package/frontend-dist/assets/{CardTitle-DqrzLNUU.js → CardTitle-BEMKGuDs.js} +1 -1
- package/frontend-dist/assets/{CascadingModelSelect-D2DRpM28.js → CascadingModelSelect-TP3nDN4K.js} +1 -1
- package/frontend-dist/assets/{Checkbox-B-BvqDQI.js → Checkbox-KBtwJU8Q.js} +1 -1
- package/frontend-dist/assets/{CollapsibleContent-BV1VGpMl.js → CollapsibleContent-CK4MEdEk.js} +1 -1
- package/frontend-dist/assets/{CollapsibleTrigger-BNuoyr3g.js → CollapsibleTrigger-C1YuylWt.js} +1 -1
- package/frontend-dist/assets/{ConcurrencyControl-XSeHDo1-.js → ConcurrencyControl-naUxnJl6.js} +1 -1
- package/frontend-dist/assets/{Dashboard-CsgO9KeQ.js → Dashboard-CsMzPzyB.js} +1 -1
- package/frontend-dist/assets/{Input-C5OLhaEA.js → Input-CISfShtH.js} +1 -1
- package/frontend-dist/assets/{Label-B5FJLcPy.js → Label-CpOxo1sn.js} +1 -1
- package/frontend-dist/assets/{Login-wZ8_XeqQ.js → Login-DWIJrfGr.js} +1 -1
- package/frontend-dist/assets/{Logs-CiC8wTHb.js → Logs-Do6F6xpO.js} +1 -1
- package/frontend-dist/assets/{ModelMappings-CZcMgWbM.js → ModelMappings-BeCtJid-.js} +1 -1
- package/frontend-dist/assets/{Monitor-Cb4RhaXB.js → Monitor-BkKdwtEO.js} +1 -1
- package/frontend-dist/assets/{Providers-DGwBmDka.js → Providers-DZDMM3Ly.js} +1 -1
- package/frontend-dist/assets/{ProxyEnhancement-Duyvg2Pu.js → ProxyEnhancement-Bf9reTLr.js} +1 -1
- package/frontend-dist/assets/{QuickSetup-MgM8jncN.js → QuickSetup-D05Pbvny.js} +1 -1
- package/frontend-dist/assets/{RetryRules-CucoGQb3.js → RetryRules-ZYSMISJv.js} +1 -1
- package/frontend-dist/assets/{RouterKeys-Cj46NlnE.js → RouterKeys-DyCXVjSV.js} +1 -1
- package/frontend-dist/assets/{RovingFocusItem-zXj-QlAl.js → RovingFocusItem-DyThG4Ao.js} +1 -1
- package/frontend-dist/assets/{Schedules-BxtgE_jQ.js → Schedules-BMdkLY7a.js} +1 -1
- package/frontend-dist/assets/{Separator-Cmw60D6s.js → Separator-Cge3lk25.js} +1 -1
- package/frontend-dist/assets/{Settings-CSjzJJ1L.js → Settings-CqA17rFr.js} +1 -1
- package/frontend-dist/assets/{Setup-P3Amua1-.js → Setup-C03USty0.js} +1 -1
- package/frontend-dist/assets/{Skeleton-Bb6JdseU.js → Skeleton-DBYFTs7B.js} +1 -1
- package/frontend-dist/assets/{Switch-G855OsUp.js → Switch-CLdGo10G.js} +1 -1
- package/frontend-dist/assets/{TableHeader-CFMCLKRy.js → TableHeader-BmXchBD9.js} +1 -1
- package/frontend-dist/assets/{TabsTrigger-P5C-goq6.js → TabsTrigger-URUZbzCb.js} +1 -1
- package/frontend-dist/assets/{UnifiedRequestDialog-b0YkEvxq.js → UnifiedRequestDialog-ZW5x63E9.js} +1 -1
- package/frontend-dist/assets/{VisuallyHiddenInput-De_zFaJT.js → VisuallyHiddenInput-Bi3I1T8I.js} +1 -1
- package/frontend-dist/assets/arrow-down-BCM-l0Jl.js +1 -0
- package/frontend-dist/assets/{badge-BKjuTJ01.js → badge-eqb4qgGp.js} +1 -1
- package/frontend-dist/assets/{button-DpPvjx0f.js → button-mHNAa2aF.js} +2 -2
- package/frontend-dist/assets/chevron-right-D0wqOqtp.js +1 -0
- package/frontend-dist/assets/{dialog-BxDRyeVd.js → dialog-BBMCB4Zc.js} +1 -1
- package/frontend-dist/assets/{image-Bp59tVen.js → image-DTqw64FB.js} +1 -1
- package/frontend-dist/assets/{index-Di7f6Crg.js → index-Ddg5hheZ.js} +2 -2
- package/frontend-dist/assets/{model-patches-CGEItLFI.js → model-patches-Bru1xRNm.js} +1 -1
- package/frontend-dist/assets/{pencil-d2ST8J0H.js → pencil-B6uEYOlv.js} +1 -1
- package/frontend-dist/assets/plus-B2f9w-px.js +1 -0
- package/frontend-dist/assets/search-CczkyuG-.js +1 -0
- package/frontend-dist/assets/{sparkles-C1RBYUoM.js → sparkles-DyakAPX_.js} +1 -1
- package/frontend-dist/assets/{transform-domain-CaOlRFL4.js → transform-domain-BYHRTHa-.js} +1 -1
- package/frontend-dist/assets/{trash-2-C1YCkSOe.js → trash-2-KJKHmeJP.js} +1 -1
- package/frontend-dist/assets/{useClipboard-DfhNcDMZ.js → useClipboard-2juraaSb.js} +1 -1
- package/frontend-dist/assets/{useLogRetention-Ck3INr4l.js → useLogRetention-CYfHJhhs.js} +1 -1
- package/frontend-dist/assets/{useProviderGroups-IgmEpHao.js → useProviderGroups-Cg-m09or.js} +1 -1
- package/frontend-dist/index.html +2 -2
- package/package.json +1 -1
- package/dist/admin/constants.d.ts +0 -1
- package/dist/admin/constants.js +0 -2
- package/frontend-dist/assets/arrow-down-C0j8UmLw.js +0 -1
- package/frontend-dist/assets/chevron-right-QdaibhaO.js +0 -1
- package/frontend-dist/assets/plus-DPWmbUOf.js +0 -1
- package/frontend-dist/assets/search-BNr8kuWX.js +0 -1
package/dist/admin/groups.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Type } from "@sinclair/typebox";
|
|
|
2
2
|
import { getAllMappingGroups, createMappingGroup, updateMappingGroup, deleteMappingGroup, getProviderById, getMappingGroupById, getAllProviders, } from "../db/index.js";
|
|
3
3
|
import { getSetting } from "../db/settings.js";
|
|
4
4
|
import { serializeProviders } from "./providers.js";
|
|
5
|
-
import { HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_CONFLICT, HTTP_NOT_FOUND } from "
|
|
5
|
+
import { HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_CONFLICT, HTTP_NOT_FOUND } from "../core/constants.js";
|
|
6
6
|
import { parseModels } from "../config/model-context.js";
|
|
7
7
|
import { API_CODE, apiError } from "./api-response.js";
|
|
8
8
|
const CreateGroupSchema = Type.Object({
|
package/dist/admin/logs.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import { getRequestLogs, getRequestLogsGrouped, getRequestLogById, getRequestLogChildren, deleteLogsBefore, extractThinkingLevel, getAllProviders, getAllRouterKeys, getAllMappingGroups } from "../db/index.js";
|
|
3
3
|
import { getLogRetentionDays } from "../db/settings.js";
|
|
4
|
-
import { HTTP_NOT_FOUND } from "
|
|
4
|
+
import { HTTP_NOT_FOUND } from "../core/constants.js";
|
|
5
5
|
import { API_CODE, apiError } from "./api-response.js";
|
|
6
6
|
const LogQuerySchema = Type.Object({
|
|
7
7
|
page: Type.Optional(Type.String()),
|
package/dist/admin/mappings.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import { getAllMappingGroups, createMappingGroup, updateMappingGroup, deleteMappingGroup, getProviderById, getMappingGroupById, getMappingGroup, } from "../db/index.js";
|
|
3
|
-
import { HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_NOT_FOUND, HTTP_CONFLICT } from "
|
|
3
|
+
import { HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_NOT_FOUND, HTTP_CONFLICT } from "../core/constants.js";
|
|
4
4
|
import { API_CODE, apiError } from "./api-response.js";
|
|
5
5
|
const CreateMappingSchema = Type.Object({
|
|
6
6
|
client_model: Type.String({ minLength: 1 }),
|
package/dist/admin/monitor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { adaptSSEClient } from "../core/sse-client-adapter.js";
|
|
2
|
-
import { HTTP_NOT_FOUND } from "
|
|
2
|
+
import { HTTP_NOT_FOUND } from "../core/constants.js";
|
|
3
3
|
import { API_CODE, apiError } from "./api-response.js";
|
|
4
4
|
const HTTP_OK = 200;
|
|
5
5
|
export const adminMonitorRoutes = (app, options, done) => {
|
|
@@ -4,13 +4,15 @@ import type { Provider } from "../db/index.js";
|
|
|
4
4
|
import type { StateRegistry } from "../core/registry.js";
|
|
5
5
|
import type { AdaptiveController } from "../core/concurrency/index.js";
|
|
6
6
|
import type { RequestTracker } from "../core/monitor/index.js";
|
|
7
|
-
import type {
|
|
7
|
+
import type { ProviderConnectivityChecker } from "../core/provider-connectivity.js";
|
|
8
|
+
import type { IProxyAgentInvalidator } from "../core/proxy-agent-types.js";
|
|
8
9
|
interface ProviderRoutesOptions {
|
|
9
10
|
db: Database.Database;
|
|
10
11
|
stateRegistry?: StateRegistry;
|
|
11
12
|
tracker?: RequestTracker;
|
|
12
13
|
adaptiveController?: AdaptiveController;
|
|
13
|
-
proxyAgentFactory?:
|
|
14
|
+
proxyAgentFactory?: IProxyAgentInvalidator;
|
|
15
|
+
connectivityChecker?: ProviderConnectivityChecker;
|
|
14
16
|
}
|
|
15
17
|
/** 序列化 provider 列表,解密敏感字段、展开 models/endpoints。供多个端点复用 */
|
|
16
18
|
export declare function serializeProviders(db: Database.Database, providers: Provider[], encryptionKey: string, concurrencyStatus?: (id: string) => {
|
package/dist/admin/providers.js
CHANGED
|
@@ -4,12 +4,10 @@ import { parseEndpoints, serializeEndpoints } from "../db/providers.js";
|
|
|
4
4
|
import { getRecommendedProviders } from "../config/recommended.js";
|
|
5
5
|
import { encrypt, decrypt } from "../utils/crypto.js";
|
|
6
6
|
import { getSetting } from "../db/settings.js";
|
|
7
|
-
import { HTTP_CREATED, HTTP_NOT_FOUND, HTTP_CONFLICT, HTTP_BAD_REQUEST, HTTP_OK } from "
|
|
7
|
+
import { HTTP_CREATED, HTTP_NOT_FOUND, HTTP_CONFLICT, HTTP_BAD_REQUEST, HTTP_OK, HTTP_SERVICE_UNAVAILABLE } from "../core/constants.js";
|
|
8
8
|
import { API_CODE, apiError } from "./api-response.js";
|
|
9
9
|
import { parseModels, buildModelInfoList, normalizePatchName, lookupCapabilities } from "../config/model-context.js";
|
|
10
10
|
import { getModelInfoForProvider, setModelInfoForProvider, deleteAllModelInfoForProvider } from "../db/model-info.js";
|
|
11
|
-
import { buildUpstreamHeaders } from "../proxy/proxy-core.js";
|
|
12
|
-
import { callGet } from "../proxy/transport/http.js";
|
|
13
11
|
const API_KEY_PREVIEW_MIN_LENGTH = 8;
|
|
14
12
|
const FETCH_MODELS_BODY_PREVIEW_LENGTH = 200;
|
|
15
13
|
function cascadeProviderDisable(db, providerId) {
|
|
@@ -87,17 +85,7 @@ function extractModelOverrides(models) {
|
|
|
87
85
|
return { entries, overrides };
|
|
88
86
|
}
|
|
89
87
|
const API_KEY_PREVIEW_PREFIX_LEN = 4;
|
|
90
|
-
|
|
91
|
-
/** 校验 base_url 是否为合法的 HTTP(S) URL */
|
|
92
|
-
function isValidHttpUrl(str) {
|
|
93
|
-
try {
|
|
94
|
-
const url = new URL(str);
|
|
95
|
-
return url.protocol === "http:" || url.protocol === "https:";
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
88
|
+
import { PROVIDER_NAME_RE, isValidHttpUrl } from "./utils.js";
|
|
101
89
|
/** 校验 endpoints 数组并加密 api_key。成功返回处理后的数组,失败返回错误信息 */
|
|
102
90
|
function validateAndEncryptEndpoints(endpoints, encryptionKey) {
|
|
103
91
|
const apiTypes = endpoints.map(e => e.api_type);
|
|
@@ -304,7 +292,7 @@ export function serializeProviders(db, providers, encryptionKey, concurrencyStat
|
|
|
304
292
|
});
|
|
305
293
|
}
|
|
306
294
|
export const adminProviderRoutes = (app, options, done) => {
|
|
307
|
-
const { db, stateRegistry, tracker, adaptiveController, proxyAgentFactory } = options;
|
|
295
|
+
const { db, stateRegistry, tracker, adaptiveController, proxyAgentFactory, connectivityChecker } = options;
|
|
308
296
|
app.get("/admin/api/providers", async (_request, reply) => {
|
|
309
297
|
const encryptionKey = getSetting(db, "encryption_key");
|
|
310
298
|
const providers = getAllProviders(db);
|
|
@@ -523,8 +511,11 @@ export const adminProviderRoutes = (app, options, done) => {
|
|
|
523
511
|
const { base_url, models_endpoint, api_key, api_type } = request.body;
|
|
524
512
|
const backend = { base_url };
|
|
525
513
|
const clientHeaders = {};
|
|
514
|
+
if (!connectivityChecker) {
|
|
515
|
+
return reply.code(HTTP_SERVICE_UNAVAILABLE).send(apiError(API_CODE.BAD_REQUEST, "Connectivity checker not available"));
|
|
516
|
+
}
|
|
526
517
|
try {
|
|
527
|
-
const result = await
|
|
518
|
+
const result = await connectivityChecker.fetchModels(backend, api_key, clientHeaders, models_endpoint, api_type);
|
|
528
519
|
if (result.statusCode !== HTTP_OK) {
|
|
529
520
|
return reply.code(HTTP_BAD_REQUEST).send(apiError(API_CODE.BAD_REQUEST, `上游返回 HTTP ${result.statusCode}: ${result.body.substring(0, FETCH_MODELS_BODY_PREVIEW_LENGTH)}`));
|
|
530
521
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { FastifyPluginCallback } from "fastify";
|
|
2
2
|
import Database from "better-sqlite3";
|
|
3
|
+
import type { StateRegistry } from "../core/registry.js";
|
|
3
4
|
interface ProxyEnhancementOptions {
|
|
4
5
|
db: Database.Database;
|
|
6
|
+
stateRegistry?: StateRegistry;
|
|
5
7
|
}
|
|
6
8
|
export declare const adminProxyEnhancementRoutes: FastifyPluginCallback<ProxyEnhancementOptions>;
|
|
7
9
|
export {};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import { getSetting, setSetting } from "../db/settings.js";
|
|
3
|
-
import { clearEnhancementConfigCache } from "../proxy/routing/enhancement-config.js";
|
|
4
3
|
import { getAllProviders } from "../db/index.js";
|
|
5
4
|
import { serializeProviders } from "./providers.js";
|
|
6
5
|
const UpdateProxyEnhancementSchema = Type.Object({
|
|
@@ -14,7 +13,7 @@ const UpdateProxyEnhancementSchema = Type.Object({
|
|
|
14
13
|
])),
|
|
15
14
|
});
|
|
16
15
|
export const adminProxyEnhancementRoutes = (app, options, done) => {
|
|
17
|
-
const { db } = options;
|
|
16
|
+
const { db, stateRegistry } = options;
|
|
18
17
|
app.get("/admin/api/proxy-enhancement", async (_request, reply) => {
|
|
19
18
|
const raw = getSetting(db, "proxy_enhancement");
|
|
20
19
|
const defaults = { tool_call_loop_enabled: false, stream_loop_enabled: false, tool_round_limit_enabled: true, tool_error_logging_enabled: false };
|
|
@@ -54,7 +53,7 @@ export const adminProxyEnhancementRoutes = (app, options, done) => {
|
|
|
54
53
|
tool_error_logging_enabled: enhancementFields.tool_error_logging_enabled,
|
|
55
54
|
};
|
|
56
55
|
setSetting(db, "proxy_enhancement", JSON.stringify(config));
|
|
57
|
-
|
|
56
|
+
stateRegistry?.clearEnhancementCache();
|
|
58
57
|
// ai_retry_config is stored in a separate settings key
|
|
59
58
|
if (ai_retry_config !== undefined) {
|
|
60
59
|
setSetting(db, "ai_retry_config", ai_retry_config ? JSON.stringify(ai_retry_config) : "");
|
|
@@ -11,9 +11,9 @@ import { getRecommendedProviders, getRecommendedRetryRules } from "../config/rec
|
|
|
11
11
|
import { lookupCapabilities } from "../config/model-context.js";
|
|
12
12
|
import { getAllMappingGroups, getAllProviders } from "../db/index.js";
|
|
13
13
|
import { serializeProviders } from "./providers.js";
|
|
14
|
-
import { HTTP_CREATED, HTTP_BAD_REQUEST, HTTP_BAD_GATEWAY, HTTP_CONFLICT } from "
|
|
14
|
+
import { HTTP_CREATED, HTTP_BAD_REQUEST, HTTP_BAD_GATEWAY, HTTP_CONFLICT } from "../core/constants.js";
|
|
15
15
|
import { API_CODE, apiError } from "./api-response.js";
|
|
16
|
-
|
|
16
|
+
import { PROVIDER_NAME_RE } from "./utils.js";
|
|
17
17
|
const API_KEY_PREVIEW_MIN_LENGTH = 8;
|
|
18
18
|
const API_KEY_PREVIEW_PREFIX_LEN = 4;
|
|
19
19
|
const NEW_PROVIDER_ID = "__new__";
|
|
@@ -7,7 +7,7 @@ import { getRequestLogById } from "../db/logs.js";
|
|
|
7
7
|
import { getProviderById } from "../db/providers.js";
|
|
8
8
|
import { getSetting } from "../db/settings.js";
|
|
9
9
|
import { decrypt } from "../utils/crypto.js";
|
|
10
|
-
import { HTTP_OK, HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_NOT_FOUND } from "
|
|
10
|
+
import { HTTP_OK, HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_NOT_FOUND } from "../core/constants.js";
|
|
11
11
|
import { API_CODE, apiError } from "./api-response.js";
|
|
12
12
|
// AI 重试规则的 system prompt 模板(内联避免运行时文件依赖)
|
|
13
13
|
const AI_RETRY_PROMPT_TEMPLATE = `You are an API retry rule expert. Your ONLY job is to analyze the error response and output a JSON retry rule.
|
|
@@ -3,7 +3,7 @@ import { Type } from "@sinclair/typebox";
|
|
|
3
3
|
import { encrypt, decrypt } from "../utils/crypto.js";
|
|
4
4
|
import { getAllRouterKeys, getRouterKeyById, createRouterKey, updateRouterKey, deleteRouterKey, getAvailableModels, } from "../db/index.js";
|
|
5
5
|
import { getSetting } from "../db/settings.js";
|
|
6
|
-
import { HTTP_CREATED, HTTP_NOT_FOUND } from "
|
|
6
|
+
import { HTTP_CREATED, HTTP_NOT_FOUND } from "../core/constants.js";
|
|
7
7
|
import { API_CODE, apiError } from "./api-response.js";
|
|
8
8
|
const KEY_RANDOM_BYTES = 32;
|
|
9
9
|
const KEY_PREFIX_LENGTH = 8;
|
package/dist/admin/routes.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ import Database from "better-sqlite3";
|
|
|
3
3
|
import type { StateRegistry } from "../core/registry.js";
|
|
4
4
|
import type { RequestTracker } from "../core/monitor/index.js";
|
|
5
5
|
import type { AdaptiveController } from "../core/concurrency/index.js";
|
|
6
|
-
import type {
|
|
6
|
+
import type { IProxyAgentInvalidator } from "../core/proxy-agent-types.js";
|
|
7
|
+
import type { ProviderConnectivityChecker } from "../core/provider-connectivity.js";
|
|
7
8
|
interface AdminRoutesOptions {
|
|
8
9
|
db: Database.Database;
|
|
9
10
|
stateRegistry: StateRegistry;
|
|
@@ -13,7 +14,8 @@ interface AdminRoutesOptions {
|
|
|
13
14
|
logsDir?: string;
|
|
14
15
|
pluginRegistry?: import("../proxy/transform/plugin-registry.js").PluginRegistry;
|
|
15
16
|
closeFn?: () => Promise<void>;
|
|
16
|
-
proxyAgentFactory?:
|
|
17
|
+
proxyAgentFactory?: IProxyAgentInvalidator;
|
|
18
|
+
connectivityChecker?: ProviderConnectivityChecker;
|
|
17
19
|
}
|
|
18
20
|
export declare const adminRoutes: FastifyPluginCallback<AdminRoutesOptions>;
|
|
19
21
|
export {};
|
package/dist/admin/routes.js
CHANGED
|
@@ -19,13 +19,12 @@ import { adminImportExportRoutes } from "./settings-import-export.js";
|
|
|
19
19
|
import { adminTransformRuleRoutes } from "./transform-rules.js";
|
|
20
20
|
import { adminDashboardRoutes } from "./dashboard.js";
|
|
21
21
|
import { adminScheduleRoutes } from "./schedules.js";
|
|
22
|
-
import { hookRegistry } from "../proxy/pipeline/hook-registry.js";
|
|
23
22
|
export const adminRoutes = (app, options, done) => {
|
|
24
23
|
// Setup 路由不需要 auth
|
|
25
24
|
app.register(adminSetupRoutes, { db: options.db });
|
|
26
25
|
app.register(adminAuthPlugin, { db: options.db });
|
|
27
26
|
app.register(adminLoginRoutes, { db: options.db });
|
|
28
|
-
app.register(adminProviderRoutes, { db: options.db, stateRegistry: options.stateRegistry, tracker: options.tracker, adaptiveController: options.adaptiveController, proxyAgentFactory: options.proxyAgentFactory });
|
|
27
|
+
app.register(adminProviderRoutes, { db: options.db, stateRegistry: options.stateRegistry, tracker: options.tracker, adaptiveController: options.adaptiveController, proxyAgentFactory: options.proxyAgentFactory, connectivityChecker: options.connectivityChecker });
|
|
29
28
|
app.register(adminMappingRoutes, { db: options.db });
|
|
30
29
|
app.register(adminGroupRoutes, { db: options.db });
|
|
31
30
|
app.register(adminScheduleRoutes, { db: options.db });
|
|
@@ -34,7 +33,7 @@ export const adminRoutes = (app, options, done) => {
|
|
|
34
33
|
app.register(adminRouterKeyRoutes, { db: options.db });
|
|
35
34
|
app.register(adminStatsRoutes, { db: options.db });
|
|
36
35
|
app.register(adminMetricsRoutes, { db: options.db });
|
|
37
|
-
app.register(adminProxyEnhancementRoutes, { db: options.db });
|
|
36
|
+
app.register(adminProxyEnhancementRoutes, { db: options.db, stateRegistry: options.stateRegistry });
|
|
38
37
|
app.register(adminMonitorRoutes, { tracker: options.tracker });
|
|
39
38
|
app.register(adminSettingsRoutes, { db: options.db, logsDir: options.logsDir });
|
|
40
39
|
app.register(adminImportExportRoutes, { db: options.db, stateRegistry: options.stateRegistry, pluginRegistry: options.pluginRegistry });
|
|
@@ -46,7 +45,7 @@ export const adminRoutes = (app, options, done) => {
|
|
|
46
45
|
app.register(adminTransformRuleRoutes, { db: options.db, pluginRegistry: options.pluginRegistry });
|
|
47
46
|
// Pipeline hooks 查询
|
|
48
47
|
app.get("/admin/api/pipeline/hooks", async () => {
|
|
49
|
-
return { hooks:
|
|
48
|
+
return { hooks: options.stateRegistry.getPipelineHooks() };
|
|
50
49
|
});
|
|
51
50
|
done();
|
|
52
51
|
};
|
package/dist/admin/schedules.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import { getSchedulesByGroup, getAllSchedules, getScheduleById, createSchedule, updateSchedule, deleteSchedule, getAllMappingGroups, getAllProviders, } from "../db/index.js";
|
|
3
|
-
import { getMappingGroupById
|
|
3
|
+
import { getMappingGroupById } from "../db/index.js";
|
|
4
4
|
import { serializeProviders } from "./providers.js";
|
|
5
5
|
import { getSetting } from "../db/settings.js";
|
|
6
|
-
import { HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_NOT_FOUND } from "
|
|
6
|
+
import { HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_NOT_FOUND } from "../core/constants.js";
|
|
7
7
|
import { API_CODE, apiError } from "./api-response.js";
|
|
8
8
|
const CreateScheduleSchema = Type.Object({
|
|
9
9
|
mapping_group_id: Type.String({ minLength: 1 }),
|
|
@@ -25,44 +25,7 @@ const UpdateScheduleSchema = Type.Object({
|
|
|
25
25
|
concurrency_rule: Type.Optional(Type.String()),
|
|
26
26
|
transform_rule: Type.Optional(Type.String()),
|
|
27
27
|
});
|
|
28
|
-
|
|
29
|
-
let rule;
|
|
30
|
-
try {
|
|
31
|
-
rule = JSON.parse(ruleJson);
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
return "Invalid mapping_rule JSON";
|
|
35
|
-
}
|
|
36
|
-
if (typeof rule !== "object" || rule === null)
|
|
37
|
-
return "Invalid mapping_rule";
|
|
38
|
-
const r = rule;
|
|
39
|
-
if (!Array.isArray(r.targets) || r.targets.length === 0) {
|
|
40
|
-
return "mapping_rule.targets must be a non-empty array";
|
|
41
|
-
}
|
|
42
|
-
for (let i = 0; i < r.targets.length; i++) {
|
|
43
|
-
const t = r.targets[i];
|
|
44
|
-
if (!t.backend_model || !t.provider_id) {
|
|
45
|
-
return `targets[${i}] missing backend_model or provider_id`;
|
|
46
|
-
}
|
|
47
|
-
const p = getProviderById(db, t.provider_id);
|
|
48
|
-
if (!p)
|
|
49
|
-
return `targets[${i}] provider_id '${t.provider_id}' not found`;
|
|
50
|
-
const hasOverflowProvider = !!t.overflow_provider_id;
|
|
51
|
-
const hasOverflowModel = !!t.overflow_model;
|
|
52
|
-
if (hasOverflowProvider && !hasOverflowModel) {
|
|
53
|
-
return `targets[${i}]: overflow_provider_id requires overflow_model`;
|
|
54
|
-
}
|
|
55
|
-
if (hasOverflowModel && !hasOverflowProvider) {
|
|
56
|
-
return `targets[${i}]: overflow_model requires overflow_provider_id`;
|
|
57
|
-
}
|
|
58
|
-
if (hasOverflowProvider) {
|
|
59
|
-
const op = getProviderById(db, t.overflow_provider_id);
|
|
60
|
-
if (!op)
|
|
61
|
-
return `targets[${i}]: overflow_provider_id '${t.overflow_provider_id}' not found`;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
28
|
+
import { validateMappingRule } from "./utils.js";
|
|
66
29
|
/** 解析 week JSON 为数字数组,失败返回 null */
|
|
67
30
|
const MAX_WEEK_DAY = 6;
|
|
68
31
|
function parseWeekSafe(weekJson) {
|
package/dist/admin/settings.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { statSync, readdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { getLogRetentionDays, setLogRetentionDays, getDbMaxSizeMb, setDbMaxSizeMb, getLogTableMaxSizeMb, setLogTableMaxSizeMb, getSetting, getTokenEstimationEnabled, setTokenEstimationEnabled, getClientSessionHeaders, setClientSessionHeaders, getMetricsDetailDays, setMetricsDetailDays, } from "../db/settings.js";
|
|
4
|
-
import { HTTP_BAD_REQUEST } from "
|
|
4
|
+
import { HTTP_BAD_REQUEST } from "../core/constants.js";
|
|
5
5
|
import { API_CODE, apiError } from "./api-response.js";
|
|
6
6
|
export const adminSettingsRoutes = (app, options, done) => {
|
|
7
7
|
const { db, logsDir } = options;
|
package/dist/admin/setup.js
CHANGED
|
@@ -3,7 +3,7 @@ import jwt from "jsonwebtoken";
|
|
|
3
3
|
import { getSetting, setSetting, isInitialized } from "../db/settings.js";
|
|
4
4
|
import { hashPassword } from "../utils/password.js";
|
|
5
5
|
import { isForwardedProtoHttps } from "../utils/cookie-secure.js";
|
|
6
|
-
import { HTTP_BAD_REQUEST, HTTP_CONFLICT } from "
|
|
6
|
+
import { HTTP_BAD_REQUEST, HTTP_CONFLICT } from "../core/constants.js";
|
|
7
7
|
import { API_CODE, apiError } from "./api-response.js";
|
|
8
8
|
const CRYPTO_BYTES_LENGTH = 32;
|
|
9
9
|
const MIN_PASSWORD_LENGTH = 6;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* admin 层共享校验工具函数和常量。
|
|
3
|
+
* 各 admin 文件统一从此模块导入,消除重复定义。
|
|
4
|
+
*/
|
|
5
|
+
import type Database from "better-sqlite3";
|
|
6
|
+
/** Provider 名称合法字符:英文、数字、横线、下划线 */
|
|
7
|
+
export declare const PROVIDER_NAME_RE: RegExp;
|
|
8
|
+
/** 校验 base_url 是否为合法的 HTTP(S) URL */
|
|
9
|
+
export declare function isValidHttpUrl(str: string): boolean;
|
|
10
|
+
/** 校验 mapping_rule JSON 结构和引用完整性 */
|
|
11
|
+
export declare function validateMappingRule(db: Database.Database, ruleJson: string): string | undefined;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* admin 层共享校验工具函数和常量。
|
|
3
|
+
* 各 admin 文件统一从此模块导入,消除重复定义。
|
|
4
|
+
*/
|
|
5
|
+
import { getProviderById } from "../db/index.js";
|
|
6
|
+
/** Provider 名称合法字符:英文、数字、横线、下划线 */
|
|
7
|
+
export const PROVIDER_NAME_RE = /^[a-zA-Z0-9_-]+$/;
|
|
8
|
+
/** 校验 base_url 是否为合法的 HTTP(S) URL */
|
|
9
|
+
export function isValidHttpUrl(str) {
|
|
10
|
+
try {
|
|
11
|
+
const url = new URL(str);
|
|
12
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** 校验 mapping_rule JSON 结构和引用完整性 */
|
|
19
|
+
export function validateMappingRule(db, ruleJson) {
|
|
20
|
+
let rule;
|
|
21
|
+
try {
|
|
22
|
+
rule = JSON.parse(ruleJson);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return "Invalid mapping_rule JSON";
|
|
26
|
+
}
|
|
27
|
+
if (typeof rule !== "object" || rule === null)
|
|
28
|
+
return "Invalid mapping_rule";
|
|
29
|
+
const r = rule;
|
|
30
|
+
if (!Array.isArray(r.targets) || r.targets.length === 0) {
|
|
31
|
+
return "mapping_rule.targets must be a non-empty array";
|
|
32
|
+
}
|
|
33
|
+
for (let i = 0; i < r.targets.length; i++) {
|
|
34
|
+
const t = r.targets[i];
|
|
35
|
+
if (!t.backend_model || !t.provider_id) {
|
|
36
|
+
return `targets[${i}] missing backend_model or provider_id`;
|
|
37
|
+
}
|
|
38
|
+
const p = getProviderById(db, t.provider_id);
|
|
39
|
+
if (!p)
|
|
40
|
+
return `targets[${i}] provider_id '${t.provider_id}' not found`;
|
|
41
|
+
const hasOverflowProvider = !!t.overflow_provider_id;
|
|
42
|
+
const hasOverflowModel = !!t.overflow_model;
|
|
43
|
+
if (hasOverflowProvider && !hasOverflowModel) {
|
|
44
|
+
return `targets[${i}]: overflow_provider_id requires overflow_model`;
|
|
45
|
+
}
|
|
46
|
+
if (hasOverflowModel && !hasOverflowProvider) {
|
|
47
|
+
return `targets[${i}]: overflow_model requires overflow_provider_id`;
|
|
48
|
+
}
|
|
49
|
+
if (hasOverflowProvider) {
|
|
50
|
+
const op = getProviderById(db, t.overflow_provider_id);
|
|
51
|
+
if (!op) {
|
|
52
|
+
return `targets[${i}]: overflow_provider_id '${t.overflow_provider_id}' not found`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface RetryMatcher {
|
|
2
|
+
test: (statusCode: number, body: string) => boolean;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* 判断一条日志是否需要保留全文详情到 DB。
|
|
6
|
+
* - hasFileWriter=false 时保守保留全文(避免数据丢失)
|
|
7
|
+
* - status >= 400 → 保留
|
|
8
|
+
* - matcher 为 null → 保守保留
|
|
9
|
+
* - matcher 命中 → 保留
|
|
10
|
+
* - 否则 → 只存摘要(文件已有全文备份)
|
|
11
|
+
*/
|
|
12
|
+
export declare function shouldPreserveDetail(statusCode: number | null, responseBody: string | null, matcher: RetryMatcher | null, hasFileWriter?: boolean): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// src/core/log-detail-policy.ts
|
|
2
|
+
const HTTP_ERROR_THRESHOLD = 400;
|
|
3
|
+
/**
|
|
4
|
+
* 判断一条日志是否需要保留全文详情到 DB。
|
|
5
|
+
* - hasFileWriter=false 时保守保留全文(避免数据丢失)
|
|
6
|
+
* - status >= 400 → 保留
|
|
7
|
+
* - matcher 为 null → 保守保留
|
|
8
|
+
* - matcher 命中 → 保留
|
|
9
|
+
* - 否则 → 只存摘要(文件已有全文备份)
|
|
10
|
+
*/
|
|
11
|
+
export function shouldPreserveDetail(statusCode, responseBody, matcher, hasFileWriter = true) {
|
|
12
|
+
if (!hasFileWriter)
|
|
13
|
+
return true;
|
|
14
|
+
if (statusCode !== null && statusCode >= HTTP_ERROR_THRESHOLD)
|
|
15
|
+
return true;
|
|
16
|
+
if (!matcher)
|
|
17
|
+
return true;
|
|
18
|
+
if (responseBody && matcher.test(statusCode ?? 0, responseBody))
|
|
19
|
+
return true;
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider 连通性检查接口。
|
|
3
|
+
* admin 层通过此接口检查上游 provider 的模型列表,解耦对 proxy 层的直接依赖。
|
|
4
|
+
*/
|
|
5
|
+
import type { RawHeaders } from "./types.js";
|
|
6
|
+
export interface ProviderConnectivityCheckResult {
|
|
7
|
+
statusCode: number;
|
|
8
|
+
body: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ProviderConnectivityChecker {
|
|
11
|
+
/**
|
|
12
|
+
* 向上游 provider 发起 GET 请求获取模型列表。
|
|
13
|
+
* @param backend 包含 base_url 的后端信息
|
|
14
|
+
* @param apiKey API 密钥
|
|
15
|
+
* @param clientHeaders 客户端原始请求头
|
|
16
|
+
* @param upstreamPath 上游路径(如 /v1/models)
|
|
17
|
+
* @param apiType API 类型,决定 header 构建方式
|
|
18
|
+
*/
|
|
19
|
+
fetchModels(backend: {
|
|
20
|
+
base_url: string;
|
|
21
|
+
}, apiKey: string, clientHeaders: RawHeaders, upstreamPath: string, apiType: "openai" | "openai-responses" | "anthropic"): Promise<ProviderConnectivityCheckResult>;
|
|
22
|
+
}
|
package/dist/core/registry.d.ts
CHANGED
|
@@ -29,4 +29,11 @@ export interface StateRegistry {
|
|
|
29
29
|
getAdaptiveStatus(providerId: string): import("./concurrency/types.js").AdaptiveState | undefined;
|
|
30
30
|
/** 从 DB 重新读取所有 provider 配置,重建信号量/adaptive/tracker 缓存(导入配置后调用) */
|
|
31
31
|
reinitializeProviders(): void;
|
|
32
|
+
/** 清除代理增强配置缓存(enhancement-config) */
|
|
33
|
+
clearEnhancementCache(): void;
|
|
34
|
+
/** 获取已注册的 pipeline hooks(按 phase 分组) */
|
|
35
|
+
getPipelineHooks(): Record<string, Array<{
|
|
36
|
+
name: string;
|
|
37
|
+
priority: number;
|
|
38
|
+
}>>;
|
|
32
39
|
}
|
package/dist/db/logs.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Database from "better-sqlite3";
|
|
2
2
|
import type { LogFileWriter } from "../storage/log-file-writer.js";
|
|
3
|
-
import { type RetryMatcher } from "../
|
|
3
|
+
import { type RetryMatcher } from "../core/log-detail-policy.js";
|
|
4
4
|
/** 从 client_request JSON 中提取 thinking_level,按 api_type 分支处理 */
|
|
5
5
|
export declare function extractThinkingLevel(apiType: string, clientRequest: string | null): string;
|
|
6
6
|
export interface RequestLog {
|
package/dist/db/logs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shouldPreserveDetail } from "../
|
|
1
|
+
import { shouldPreserveDetail } from "../core/log-detail-policy.js";
|
|
2
2
|
import { getCachedStmt } from "./helpers.js";
|
|
3
3
|
/** 从 client_request JSON 中提取 thinking_level,按 api_type 分支处理 */
|
|
4
4
|
export function extractThinkingLevel(apiType, clientRequest) {
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,9 @@ import fastifyStatic from "@fastify/static";
|
|
|
42
42
|
import { ServiceContainer, SERVICE_KEYS } from "./core/container.js";
|
|
43
43
|
import { LogFileWriter } from "./storage/log-file-writer.js";
|
|
44
44
|
import { ProxyAgentFactory } from "./proxy/transport/proxy-agent.js";
|
|
45
|
+
import { ProxyConnectivityChecker } from "./proxy/transport/provider-connectivity.js";
|
|
46
|
+
import { hookRegistry } from "./proxy/pipeline/hook-registry.js";
|
|
47
|
+
import { clearEnhancementConfigCache } from "./proxy/routing/enhancement-config.js";
|
|
45
48
|
import { registerBuiltinHooks } from "./proxy/pipeline/register-hooks.js";
|
|
46
49
|
import { scheduleLogFileMaintenance } from "./storage/log-file-compressor.js";
|
|
47
50
|
import { getDetailLogEnabled, getLogFileRetentionDays } from "./db/settings.js";
|
|
@@ -274,6 +277,7 @@ export async function buildApp(options) {
|
|
|
274
277
|
container.register(SERVICE_KEYS.formatRegistry, () => formatRegistry);
|
|
275
278
|
// 注册 ProxyAgentFactory
|
|
276
279
|
container.register(SERVICE_KEYS.proxyAgentFactory, () => new ProxyAgentFactory());
|
|
280
|
+
const connectivityChecker = new ProxyConnectivityChecker();
|
|
277
281
|
// 从容器解析所有服务
|
|
278
282
|
const matcher = container.resolve(SERVICE_KEYS.matcher);
|
|
279
283
|
const semaphoreManager = container.resolve(SERVICE_KEYS.semaphoreManager);
|
|
@@ -318,11 +322,13 @@ export async function buildApp(options) {
|
|
|
318
322
|
adaptiveController.removeAll();
|
|
319
323
|
initializeProviderState(db, semaphoreManager, adaptiveController, tracker);
|
|
320
324
|
},
|
|
325
|
+
clearEnhancementCache: () => clearEnhancementConfigCache(),
|
|
326
|
+
getPipelineHooks: () => hookRegistry.getAll(),
|
|
321
327
|
};
|
|
322
328
|
// Late-bound close ref — close 函数在 adminRoutes 注册之后才定义,
|
|
323
329
|
// 但 restart API 需要在运行时调用它
|
|
324
330
|
const closeRef = { fn: async () => { } };
|
|
325
|
-
app.register(adminRoutes, { db, stateRegistry, tracker, adaptiveController, logFileWriter, logsDir, closeFn: () => closeRef.fn(), pluginRegistry, proxyAgentFactory });
|
|
331
|
+
app.register(adminRoutes, { db, stateRegistry, tracker, adaptiveController, logFileWriter, logsDir, closeFn: () => closeRef.fn(), pluginRegistry, proxyAgentFactory, connectivityChecker });
|
|
326
332
|
// 前端静态文件服务(生产环境)
|
|
327
333
|
const frontendDist = path.resolve(process.env.FRONTEND_DIST || path.join(__dirname, "../frontend-dist"));
|
|
328
334
|
if (existsSync(frontendDist)) {
|
|
@@ -1,12 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
test: (statusCode: number, body: string) => boolean;
|
|
3
|
-
}
|
|
4
|
-
/**
|
|
5
|
-
* 判断一条日志是否需要保留全文详情到 DB。
|
|
6
|
-
* - hasFileWriter=false 时保守保留全文(避免数据丢失)
|
|
7
|
-
* - status >= 400 → 保留
|
|
8
|
-
* - matcher 为 null → 保守保留
|
|
9
|
-
* - matcher 命中 → 保留
|
|
10
|
-
* - 否则 → 只存摘要(文件已有全文备份)
|
|
11
|
-
*/
|
|
12
|
-
export declare function shouldPreserveDetail(statusCode: number | null, responseBody: string | null, matcher: RetryMatcher | null, hasFileWriter?: boolean): boolean;
|
|
1
|
+
export { shouldPreserveDetail, type RetryMatcher } from "../core/log-detail-policy.js";
|
|
@@ -1,21 +1,3 @@
|
|
|
1
1
|
// src/proxy/log-detail-policy.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* 判断一条日志是否需要保留全文详情到 DB。
|
|
5
|
-
* - hasFileWriter=false 时保守保留全文(避免数据丢失)
|
|
6
|
-
* - status >= 400 → 保留
|
|
7
|
-
* - matcher 为 null → 保守保留
|
|
8
|
-
* - matcher 命中 → 保留
|
|
9
|
-
* - 否则 → 只存摘要(文件已有全文备份)
|
|
10
|
-
*/
|
|
11
|
-
export function shouldPreserveDetail(statusCode, responseBody, matcher, hasFileWriter = true) {
|
|
12
|
-
if (!hasFileWriter)
|
|
13
|
-
return true;
|
|
14
|
-
if (statusCode !== null && statusCode >= HTTP_ERROR_THRESHOLD)
|
|
15
|
-
return true;
|
|
16
|
-
if (!matcher)
|
|
17
|
-
return true;
|
|
18
|
-
if (responseBody && matcher.test(statusCode ?? 0, responseBody))
|
|
19
|
-
return true;
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
2
|
+
// 策略逻辑已提升到 core 层,此文件保留 re-export 兼容
|
|
3
|
+
export { shouldPreserveDetail } from "../core/log-detail-policy.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProviderConnectivityChecker 的 proxy 层实现。
|
|
3
|
+
* 组合 proxy 层的 callGet + buildUpstreamHeaders,通过接口暴露给 admin 层。
|
|
4
|
+
*/
|
|
5
|
+
import type { ProviderConnectivityChecker, ProviderConnectivityCheckResult } from "../../core/provider-connectivity.js";
|
|
6
|
+
import type { RawHeaders } from "../../core/types.js";
|
|
7
|
+
export declare class ProxyConnectivityChecker implements ProviderConnectivityChecker {
|
|
8
|
+
fetchModels(backend: {
|
|
9
|
+
base_url: string;
|
|
10
|
+
}, apiKey: string, clientHeaders: RawHeaders, upstreamPath: string, apiType: "openai" | "openai-responses" | "anthropic"): Promise<ProviderConnectivityCheckResult>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProviderConnectivityChecker 的 proxy 层实现。
|
|
3
|
+
* 组合 proxy 层的 callGet + buildUpstreamHeaders,通过接口暴露给 admin 层。
|
|
4
|
+
*/
|
|
5
|
+
import { callGet } from "./http.js";
|
|
6
|
+
import { buildUpstreamHeaders } from "../proxy-core.js";
|
|
7
|
+
export class ProxyConnectivityChecker {
|
|
8
|
+
fetchModels(backend, apiKey, clientHeaders, upstreamPath, apiType) {
|
|
9
|
+
return callGet(backend, apiKey, clientHeaders, upstreamPath, (cliHdrs, key) => buildUpstreamHeaders(cliHdrs, key, undefined, apiType));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Agent } from "http";
|
|
2
|
+
import type { IProxyAgentInvalidator } from "../../core/proxy-agent-types.js";
|
|
2
3
|
export interface ProxyConfig {
|
|
3
4
|
id: string;
|
|
4
5
|
proxy_type: string | null;
|
|
@@ -6,7 +7,7 @@ export interface ProxyConfig {
|
|
|
6
7
|
proxy_username: string | null;
|
|
7
8
|
proxy_password: string | null;
|
|
8
9
|
}
|
|
9
|
-
export declare class ProxyAgentFactory {
|
|
10
|
+
export declare class ProxyAgentFactory implements IProxyAgentInvalidator {
|
|
10
11
|
private readonly cache;
|
|
11
12
|
private keepAliveHttpAgent;
|
|
12
13
|
private keepAliveHttpsAgent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{$ as e,Qt as t,at as n,et as r,jt as i,n as a,nt as o,ot as s,qt as c,vt as l,xt as u}from"./button-
|
|
1
|
+
import{$ as e,Qt as t,at as n,et as r,jt as i,n as a,nt as o,ot as s,qt as c,vt as l,xt as u}from"./button-mHNAa2aF.js";import{dt as d,ft as f,r as p}from"./index-Ddg5hheZ.js";import{t as m}from"./Card-CrAceTFb.js";import{t as h}from"./CardContent-otK_vTWx.js";var g={class:`min-h-screen flex items-center justify-center bg-background relative`},_={class:`text-center mb-6`},v={class:`text-sm text-muted-foreground mt-1`},y=s({__name:`AuthLayout`,props:{subtitle:{}},setup(s){let{isDark:y,toggleTheme:b}=p();return(p,x)=>(l(),o(`div`,g,[n(c(a),{variant:`ghost`,size:`icon`,class:`absolute top-4 right-4 text-muted-foreground hover:text-foreground`,onClick:c(b)},{default:i(()=>[c(y)?(l(),r(c(d),{key:1,class:`w-4 h-4`})):(l(),r(c(f),{key:0,class:`w-4 h-4`}))]),_:1},8,[`onClick`]),n(c(m),{class:`w-full max-w-sm shadow-lg`},{default:i(()=>[n(c(h),{class:`pt-6`},{default:i(()=>[e(`div`,_,[x[0]||=e(`div`,{class:`w-12 h-12 bg-primary rounded-lg mx-auto mb-3 flex items-center justify-center`},[e(`svg`,{class:`w-7 h-7 text-white`,fill:`none`,stroke:`currentColor`,viewBox:`0 0 24 24`},[e(`path`,{"stroke-linecap":`round`,"stroke-linejoin":`round`,"stroke-width":`2`,d:`M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z`})])],-1),x[1]||=e(`h1`,{class:`text-xl font-semibold text-foreground`},` LLM Simple Router `,-1),e(`p`,v,t(s.subtitle),1)]),u(p.$slots,`default`)]),_:3})]),_:3})]))}});export{y as t};
|