llm-simple-router 1.0.11 → 1.0.13
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/dashboard.d.ts +2 -0
- package/dist/admin/dashboard.js +112 -70
- package/dist/admin/groups.js +10 -1
- package/dist/admin/logs.js +30 -1
- package/dist/admin/monitor.js +16 -0
- package/dist/admin/providers.d.ts +35 -0
- package/dist/admin/providers.js +53 -32
- package/dist/admin/proxy-enhancement.js +37 -0
- package/dist/admin/quick-setup.js +33 -0
- package/dist/admin/retry-rules.js +13 -1
- package/dist/admin/router-keys.js +6 -0
- package/dist/admin/routes.js +1 -1
- package/dist/admin/schedules.js +11 -1
- package/dist/admin/settings.js +33 -0
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/stats.d.ts +6 -0
- package/dist/db/stats.js +1 -1
- package/frontend-dist/assets/{AuthLayout-BNvGjJv3.js → AuthLayout-C7E43a6p.js} +1 -1
- package/frontend-dist/assets/{Card-BNz2IkLE.js → Card-L1MI4nan.js} +1 -1
- package/frontend-dist/assets/{CardContent-DszVPJgx.js → CardContent-DUzyQmbU.js} +1 -1
- package/frontend-dist/assets/{CardTitle-BeBkx0ns.js → CardTitle-oL3GJ7xA.js} +1 -1
- package/frontend-dist/assets/{CascadingModelSelect-Z7-ur9_p.js → CascadingModelSelect-C_Ydm0fg.js} +1 -1
- package/frontend-dist/assets/{Checkbox-CAQnc0qV.js → Checkbox-OkYUPlFe.js} +1 -1
- package/frontend-dist/assets/{CollapsibleContent-CHlSINCS.js → CollapsibleContent-4ZwwyZPz.js} +1 -1
- package/frontend-dist/assets/{CollapsibleTrigger-CJMUoPRa.js → CollapsibleTrigger-Cg651F6-.js} +1 -1
- package/frontend-dist/assets/{ConcurrencyControl-BcUA816m.js → ConcurrencyControl-LFw4H_oP.js} +1 -1
- package/frontend-dist/assets/{Dashboard-DXvcAfAx.js → Dashboard-Bqups5aa.js} +2 -2
- package/frontend-dist/assets/{Input-Dr2XqfSh.js → Input-CLu4bS6g.js} +1 -1
- package/frontend-dist/assets/{Label-Ct_DR7Ui.js → Label-DVu1uVI7.js} +1 -1
- package/frontend-dist/assets/{Login-C8_p8684.js → Login--CFSqs7j.js} +1 -1
- package/frontend-dist/assets/Logs-CJPid8DO.js +1 -0
- package/frontend-dist/assets/ModelMappings-BTM9yiL6.js +1 -0
- package/frontend-dist/assets/Monitor-CBLKclHj.js +1 -0
- package/frontend-dist/assets/Providers-5RjiOHfn.js +1 -0
- package/frontend-dist/assets/ProxyEnhancement-XKgw6IoB.js +1 -0
- package/frontend-dist/assets/QuickSetup-CISVMd51.js +1 -0
- package/frontend-dist/assets/RetryRules-DVSEfmo2.js +1 -0
- package/frontend-dist/assets/RouterKeys-Cx2n1D0b.js +1 -0
- package/frontend-dist/assets/{RouterKeys-B0enpkQK.css → RouterKeys-D8tI3eHr.css} +1 -1
- package/frontend-dist/assets/{RovingFocusItem-DlOsi-lb.js → RovingFocusItem-CB_XB24W.js} +1 -1
- package/frontend-dist/assets/Schedules-BMD8vrVd.js +1 -0
- package/frontend-dist/assets/{Separator-BO94Jk9p.js → Separator-DHTeoMaB.js} +1 -1
- package/frontend-dist/assets/Settings-C3yzrB1_.js +6 -0
- package/frontend-dist/assets/{Setup-mAyd7FhD.js → Setup-Cgf37mQl.js} +1 -1
- package/frontend-dist/assets/{Skeleton-Di0WMQf-.js → Skeleton-DcA4Afm_.js} +1 -1
- package/frontend-dist/assets/{Switch-BS1G55M2.js → Switch-BDu-VJJd.js} +1 -1
- package/frontend-dist/assets/{TableHeader-jCTjghgy.js → TableHeader-ncK52pod.js} +1 -1
- package/frontend-dist/assets/{TabsTrigger-B5HyH2xO.js → TabsTrigger-DeTfRTma.js} +1 -1
- package/frontend-dist/assets/{UnifiedRequestDialog-CQ5Rzs11.js → UnifiedRequestDialog-CLHb7t0f.js} +3 -3
- package/frontend-dist/assets/{VisuallyHiddenInput-DiW4Si0N.js → VisuallyHiddenInput-DcH9Mxb9.js} +1 -1
- package/frontend-dist/assets/arrow-down-CuC5m7VK.js +1 -0
- package/frontend-dist/assets/{badge-FksLt30f.js → badge-DhXKxnV5.js} +1 -1
- package/frontend-dist/assets/{button-BCxYWEaM.js → button-BuzJlj9X.js} +2 -2
- package/frontend-dist/assets/chevron-right-LdKvgE7W.js +1 -0
- package/frontend-dist/assets/{dialog-B0wgNzud.js → dialog-CtPySx34.js} +1 -1
- package/frontend-dist/assets/{image-DlPnda0z.js → image-BkW18kNK.js} +1 -1
- package/frontend-dist/assets/{index-CwjudU09.js → index-BjnFvPid.js} +2 -2
- package/frontend-dist/assets/{model-patches-Dv2jfKSp.js → model-patches-DQFF0Cuh.js} +1 -1
- package/frontend-dist/assets/{pencil-Y6MJERvc.js → pencil-i0P0IRz5.js} +1 -1
- package/frontend-dist/assets/plus-fHe1ysx9.js +1 -0
- package/frontend-dist/assets/search-Dast7lfE.js +1 -0
- package/frontend-dist/assets/{sparkles-C44_iMrx.js → sparkles-BGb3coPL.js} +1 -1
- package/frontend-dist/assets/{transform-domain-FnCfUKrh.js → transform-domain-pYyIPtkg.js} +1 -1
- package/frontend-dist/assets/{trash-2-BEOdZk69.js → trash-2-DhD7GboJ.js} +1 -1
- package/frontend-dist/assets/{useClipboard-Bw6GLR6I.js → useClipboard-tJg52v7m.js} +1 -1
- package/frontend-dist/assets/{useLogRetention-CQHD99Fy.js → useLogRetention-D7ZaYfCW.js} +1 -1
- package/frontend-dist/assets/{useProviderGroups-Bz7Yz1b7.js → useProviderGroups-DMuF_-28.js} +1 -1
- package/frontend-dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend-dist/assets/Logs-BDrXnYb1.js +0 -1
- package/frontend-dist/assets/ModelMappings-CeLps4LM.js +0 -1
- package/frontend-dist/assets/Monitor-By1ywNWD.js +0 -1
- package/frontend-dist/assets/Providers-Cp-gomQY.js +0 -1
- package/frontend-dist/assets/ProxyEnhancement-D5jsMnL3.js +0 -1
- package/frontend-dist/assets/QuickSetup-1ADLBexY.js +0 -1
- package/frontend-dist/assets/RetryRules-Dk0OxVLs.js +0 -1
- package/frontend-dist/assets/RouterKeys-BvD4POUA.js +0 -1
- package/frontend-dist/assets/Schedules-CVJcpHlE.js +0 -1
- package/frontend-dist/assets/Settings-MYcQoEZw.js +0 -6
- package/frontend-dist/assets/arrow-down-BsHwq97B.js +0 -1
- package/frontend-dist/assets/chevron-right-C3U2yjrf.js +0 -1
- package/frontend-dist/assets/plus-FNO9X5JG.js +0 -1
- package/frontend-dist/assets/search-DpWQ2wvO.js +0 -1
|
@@ -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 DashboardRoutesOptions {
|
|
4
5
|
db: Database.Database;
|
|
6
|
+
stateRegistry?: StateRegistry;
|
|
5
7
|
}
|
|
6
8
|
export declare const adminDashboardRoutes: FastifyPluginCallback<DashboardRoutesOptions>;
|
|
7
9
|
export {};
|
package/dist/admin/dashboard.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import { getStats, getMetricsSummary, getMetricsTimeseries, getClientTypeBreakdown } from "../db/index.js";
|
|
2
|
+
import { getStats, getMetricsSummary, getMetricsTimeseries, getClientTypeBreakdown, getAllProviders, getAllRouterKeys, computeBucketBoundary } from "../db/index.js";
|
|
3
|
+
import { getSetting } from "../db/settings.js";
|
|
4
|
+
import { serializeProviders } from "./providers.js";
|
|
3
5
|
const OverviewQuerySchema = Type.Object({
|
|
4
6
|
provider_id: Type.Optional(Type.String()),
|
|
5
7
|
backend_model: Type.Optional(Type.String()),
|
|
@@ -10,84 +12,124 @@ const OverviewQuerySchema = Type.Object({
|
|
|
10
12
|
});
|
|
11
13
|
const PERCENT_MULTIPLIER = 100;
|
|
12
14
|
const PCT_ROUND_DIGITS = 10;
|
|
13
|
-
|
|
15
|
+
/** Compute overview stats (reused by both /overview and /init) */
|
|
16
|
+
async function computeOverview(db, query) {
|
|
17
|
+
const startTime = query.start_time;
|
|
18
|
+
const endTime = query.end_time;
|
|
19
|
+
const providerId = query.provider_id || undefined;
|
|
20
|
+
const backendModel = query.backend_model || undefined;
|
|
21
|
+
const routerKeyId = query.router_key_id || undefined;
|
|
22
|
+
const clientType = query.client_type || undefined;
|
|
23
|
+
// 1. Current period stats
|
|
24
|
+
const stats = getStats(db, startTime, endTime, routerKeyId, providerId, backendModel, clientType);
|
|
25
|
+
// 2. Previous period stats (same duration, immediately before current window)
|
|
26
|
+
const durationMs = new Date(endTime).getTime() - new Date(startTime).getTime();
|
|
27
|
+
const prevEnd = startTime;
|
|
28
|
+
const prevStart = new Date(new Date(startTime).getTime() - durationMs).toISOString();
|
|
29
|
+
const prevStats = durationMs > 0
|
|
30
|
+
? getStats(db, prevStart, prevEnd, routerKeyId, providerId, backendModel, clientType)
|
|
31
|
+
: null;
|
|
32
|
+
// 3. Timeseries (tps, input_tokens, output_tokens)
|
|
33
|
+
const legacyPeriod = "30d";
|
|
34
|
+
const [tpsRes, inputRes, outputRes] = await Promise.allSettled([
|
|
35
|
+
getMetricsTimeseries(db, legacyPeriod, "total_tps", providerId, backendModel, routerKeyId, startTime, endTime, clientType),
|
|
36
|
+
getMetricsTimeseries(db, legacyPeriod, "input_tokens", providerId, backendModel, routerKeyId, startTime, endTime, clientType),
|
|
37
|
+
getMetricsTimeseries(db, legacyPeriod, "output_tokens", providerId, backendModel, routerKeyId, startTime, endTime, clientType),
|
|
38
|
+
]);
|
|
39
|
+
const tpsRes_ = tpsRes.status === "fulfilled" ? tpsRes.value : [];
|
|
40
|
+
const inputRes_ = inputRes.status === "fulfilled" ? inputRes.value : [];
|
|
41
|
+
const outputRes_ = outputRes.status === "fulfilled" ? outputRes.value : [];
|
|
42
|
+
// 4. Cache hit rate + client type breakdown
|
|
43
|
+
const summary = getMetricsSummary(db, legacyPeriod, providerId, backendModel, routerKeyId, startTime, endTime, clientType);
|
|
44
|
+
const totalInputTokens = summary.reduce((sum, r) => sum + r.total_input_tokens, 0);
|
|
45
|
+
const totalCacheHitTokens = summary.reduce((sum, r) => sum + r.total_cache_hit_tokens, 0);
|
|
46
|
+
const cacheHitRate = totalInputTokens > 0
|
|
47
|
+
? Math.round(totalCacheHitTokens * PERCENT_MULTIPLIER / totalInputTokens * PCT_ROUND_DIGITS) / PCT_ROUND_DIGITS
|
|
48
|
+
: 0;
|
|
49
|
+
const breakdown = getClientTypeBreakdown(db, legacyPeriod, providerId, backendModel, routerKeyId, startTime, endTime);
|
|
50
|
+
// 5. Provider token summary (same time range as overview)
|
|
51
|
+
const providerTokenSummary = getProviderTokenSummary(db, startTime, endTime);
|
|
52
|
+
return {
|
|
53
|
+
stats: {
|
|
54
|
+
totalRequests: stats.totalRequests,
|
|
55
|
+
successRate: stats.successRate,
|
|
56
|
+
avgTps: stats.avgTps,
|
|
57
|
+
totalInputTokens: stats.totalInputTokens,
|
|
58
|
+
totalOutputTokens: stats.totalOutputTokens,
|
|
59
|
+
startTime,
|
|
60
|
+
endTime,
|
|
61
|
+
},
|
|
62
|
+
prev_stats: prevStats ? {
|
|
63
|
+
totalRequests: prevStats.totalRequests,
|
|
64
|
+
successRate: prevStats.successRate,
|
|
65
|
+
avgTps: prevStats.avgTps,
|
|
66
|
+
totalInputTokens: prevStats.totalInputTokens,
|
|
67
|
+
totalOutputTokens: prevStats.totalOutputTokens,
|
|
68
|
+
} : null,
|
|
69
|
+
cache_hit_rate: cacheHitRate,
|
|
70
|
+
client_type_breakdown: breakdown,
|
|
71
|
+
timeseries: {
|
|
72
|
+
tps: tpsRes_,
|
|
73
|
+
input_tokens: inputRes_,
|
|
74
|
+
output_tokens: outputRes_,
|
|
75
|
+
},
|
|
76
|
+
provider_token_summary: providerTokenSummary,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
14
79
|
export const adminDashboardRoutes = (app, options, done) => {
|
|
15
|
-
const { db } = options;
|
|
16
|
-
app.get("/admin/api/dashboard/
|
|
80
|
+
const { db, stateRegistry } = options;
|
|
81
|
+
app.get("/admin/api/dashboard/init", { schema: { querystring: OverviewQuerySchema } }, async (request, reply) => {
|
|
17
82
|
const query = request.query;
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// 2. Previous period stats (same duration, immediately before current window)
|
|
27
|
-
const durationMs = new Date(endTime).getTime() - new Date(startTime).getTime();
|
|
28
|
-
const prevEnd = startTime;
|
|
29
|
-
const prevStart = new Date(new Date(startTime).getTime() - durationMs).toISOString();
|
|
30
|
-
const prevStats = durationMs > 0
|
|
31
|
-
? getStats(db, prevStart, prevEnd, routerKeyId, providerId, backendModel, clientType)
|
|
32
|
-
: null;
|
|
33
|
-
// 3. Timeseries (tps, input_tokens, output_tokens)
|
|
34
|
-
const legacyPeriod = "30d";
|
|
35
|
-
const [tpsRes, inputRes, outputRes] = await Promise.allSettled([
|
|
36
|
-
getMetricsTimeseries(db, legacyPeriod, "total_tps", providerId, backendModel, routerKeyId, startTime, endTime, clientType),
|
|
37
|
-
getMetricsTimeseries(db, legacyPeriod, "input_tokens", providerId, backendModel, routerKeyId, startTime, endTime, clientType),
|
|
38
|
-
getMetricsTimeseries(db, legacyPeriod, "output_tokens", providerId, backendModel, routerKeyId, startTime, endTime, clientType),
|
|
83
|
+
const [providersResult, routerKeysResult, overviewResult] = await Promise.allSettled([
|
|
84
|
+
(async () => {
|
|
85
|
+
const encryptionKey = getSetting(db, "encryption_key");
|
|
86
|
+
const providers = getAllProviders(db);
|
|
87
|
+
return serializeProviders(db, providers, encryptionKey, (id) => stateRegistry?.getProviderStatus(id) ?? { active: 0, queued: 0 });
|
|
88
|
+
})(),
|
|
89
|
+
Promise.resolve(getAllRouterKeys(db).map(rk => ({ id: rk.id, name: rk.name }))),
|
|
90
|
+
(async () => computeOverview(db, query))(),
|
|
39
91
|
]);
|
|
40
|
-
const tpsRes_ = tpsRes.status === "fulfilled" ? tpsRes.value : [];
|
|
41
|
-
const inputRes_ = inputRes.status === "fulfilled" ? inputRes.value : [];
|
|
42
|
-
const outputRes_ = outputRes.status === "fulfilled" ? outputRes.value : [];
|
|
43
|
-
// 4. Cache hit rate + client type breakdown
|
|
44
|
-
const summary = getMetricsSummary(db, legacyPeriod, providerId, backendModel, routerKeyId, startTime, endTime, clientType);
|
|
45
|
-
const totalInputTokens = summary.reduce((sum, r) => sum + r.total_input_tokens, 0);
|
|
46
|
-
const totalCacheHitTokens = summary.reduce((sum, r) => sum + r.total_cache_hit_tokens, 0);
|
|
47
|
-
const cacheHitRate = totalInputTokens > 0
|
|
48
|
-
? Math.round(totalCacheHitTokens * PERCENT_MULTIPLIER / totalInputTokens * PCT_ROUND_DIGITS) / PCT_ROUND_DIGITS
|
|
49
|
-
: 0;
|
|
50
|
-
const breakdown = getClientTypeBreakdown(db, legacyPeriod, providerId, backendModel, routerKeyId, startTime, endTime);
|
|
51
|
-
// 5. Provider token summary (for provider sorting/labels in frontend)
|
|
52
|
-
const providerTokenSummary = getProviderTokenSummary(db);
|
|
53
92
|
return reply.send({
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
startTime,
|
|
61
|
-
endTime,
|
|
62
|
-
},
|
|
63
|
-
prev_stats: prevStats ? {
|
|
64
|
-
totalRequests: prevStats.totalRequests,
|
|
65
|
-
successRate: prevStats.successRate,
|
|
66
|
-
avgTps: prevStats.avgTps,
|
|
67
|
-
totalInputTokens: prevStats.totalInputTokens,
|
|
68
|
-
totalOutputTokens: prevStats.totalOutputTokens,
|
|
69
|
-
} : null,
|
|
70
|
-
cache_hit_rate: cacheHitRate,
|
|
71
|
-
client_type_breakdown: breakdown,
|
|
72
|
-
timeseries: {
|
|
73
|
-
tps: tpsRes_,
|
|
74
|
-
input_tokens: inputRes_,
|
|
75
|
-
output_tokens: outputRes_,
|
|
76
|
-
},
|
|
77
|
-
provider_token_summary: providerTokenSummary,
|
|
93
|
+
providers: providersResult.status === "fulfilled" ? providersResult.value : null,
|
|
94
|
+
router_keys: routerKeysResult.status === "fulfilled" ? routerKeysResult.value : null,
|
|
95
|
+
...(overviewResult.status === "fulfilled" ? overviewResult.value : {
|
|
96
|
+
stats: null, prev_stats: null, cache_hit_rate: null,
|
|
97
|
+
client_type_breakdown: null, timeseries: null, provider_token_summary: null,
|
|
98
|
+
}),
|
|
78
99
|
});
|
|
79
100
|
});
|
|
101
|
+
app.get("/admin/api/dashboard/overview", { schema: { querystring: OverviewQuerySchema } }, async (request, reply) => {
|
|
102
|
+
const query = request.query;
|
|
103
|
+
return reply.send(await computeOverview(db, query));
|
|
104
|
+
});
|
|
80
105
|
done();
|
|
81
106
|
};
|
|
82
|
-
/** Get per-provider total input tokens for
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
WHERE bucket_time >= datetime('now', '-' || ? || ' days')
|
|
87
|
-
GROUP BY provider_id`).all(PROVIDER_TOKEN_LOOKBACK_DAYS);
|
|
107
|
+
/** Get per-provider total input tokens for the given time range (for sorting/labels).
|
|
108
|
+
* Uses the same cross-boundary merge as getStats(): metrics_10min (settled) + request_metrics (current bucket).
|
|
109
|
+
*/
|
|
110
|
+
function getProviderTokenSummary(db, startTime, endTime) {
|
|
88
111
|
const result = {};
|
|
89
|
-
|
|
90
|
-
|
|
112
|
+
const boundary = computeBucketBoundary();
|
|
113
|
+
// 1. Aggregated data from metrics_10min (everything before the current bucket)
|
|
114
|
+
const aggEnd = endTime <= boundary ? endTime : boundary;
|
|
115
|
+
if (startTime < aggEnd) {
|
|
116
|
+
const rows = db.prepare(`SELECT provider_id, COALESCE(SUM(sum_input_tokens), 0) AS total_input_tokens
|
|
117
|
+
FROM metrics_10min
|
|
118
|
+
WHERE bucket_time >= datetime(?) AND bucket_time < datetime(?)
|
|
119
|
+
GROUP BY provider_id`).all(startTime, aggEnd);
|
|
120
|
+
for (const r of rows) {
|
|
121
|
+
result[r.provider_id] = (result[r.provider_id] ?? 0) + r.total_input_tokens;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// 2. Real-time data from request_metrics (current bucket to now)
|
|
125
|
+
if (endTime > boundary && boundary > startTime) {
|
|
126
|
+
const detailRows = db.prepare(`SELECT provider_id, COALESCE(SUM(input_tokens), 0) AS total_input_tokens
|
|
127
|
+
FROM request_metrics
|
|
128
|
+
WHERE created_at >= datetime(?) AND created_at < datetime(?)
|
|
129
|
+
GROUP BY provider_id`).all(boundary, endTime);
|
|
130
|
+
for (const r of detailRows) {
|
|
131
|
+
result[r.provider_id] = (result[r.provider_id] ?? 0) + r.total_input_tokens;
|
|
132
|
+
}
|
|
91
133
|
}
|
|
92
134
|
return result;
|
|
93
135
|
}
|
package/dist/admin/groups.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import { getAllMappingGroups, createMappingGroup, updateMappingGroup, deleteMappingGroup, getProviderById, getMappingGroupById, } from "../db/index.js";
|
|
2
|
+
import { getAllMappingGroups, createMappingGroup, updateMappingGroup, deleteMappingGroup, getProviderById, getMappingGroupById, getAllProviders, } from "../db/index.js";
|
|
3
|
+
import { getSetting } from "../db/settings.js";
|
|
4
|
+
import { serializeProviders } from "./providers.js";
|
|
3
5
|
import { HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_CONFLICT, HTTP_NOT_FOUND } from "./constants.js";
|
|
4
6
|
import { parseModels } from "../config/model-context.js";
|
|
5
7
|
import { API_CODE, apiError } from "./api-response.js";
|
|
@@ -87,6 +89,13 @@ export const adminGroupRoutes = (app, options, done) => {
|
|
|
87
89
|
const groups = getAllMappingGroups(db);
|
|
88
90
|
return reply.send(groups);
|
|
89
91
|
});
|
|
92
|
+
app.get("/admin/api/mapping-groups/init", async (_request, reply) => {
|
|
93
|
+
const groups = getAllMappingGroups(db);
|
|
94
|
+
const encryptionKey = getSetting(db, "encryption_key");
|
|
95
|
+
const providers = getAllProviders(db);
|
|
96
|
+
const serialized = serializeProviders(db, providers, encryptionKey);
|
|
97
|
+
return reply.send({ groups, providers: serialized });
|
|
98
|
+
});
|
|
90
99
|
app.post("/admin/api/mapping-groups", { schema: { body: CreateGroupSchema } }, async (request, reply) => {
|
|
91
100
|
const body = request.body;
|
|
92
101
|
const validationError = validateRule(db, body.rule);
|
package/dist/admin/logs.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import { getRequestLogs, getRequestLogsGrouped, getRequestLogById, getRequestLogChildren, deleteLogsBefore, extractThinkingLevel } from "../db/index.js";
|
|
2
|
+
import { getRequestLogs, getRequestLogsGrouped, getRequestLogById, getRequestLogChildren, deleteLogsBefore, extractThinkingLevel, getAllProviders, getAllRouterKeys, getAllMappingGroups } from "../db/index.js";
|
|
3
|
+
import { getLogRetentionDays } from "../db/settings.js";
|
|
3
4
|
import { HTTP_NOT_FOUND } from "./constants.js";
|
|
4
5
|
import { API_CODE, apiError } from "./api-response.js";
|
|
5
6
|
const LogQuerySchema = Type.Object({
|
|
@@ -22,6 +23,34 @@ const DeleteLogsBeforeSchema = Type.Object({
|
|
|
22
23
|
const DEFAULT_LOG_VIEW = "flat";
|
|
23
24
|
export const adminLogRoutes = (app, options, done) => {
|
|
24
25
|
const { db, logFileWriter } = options;
|
|
26
|
+
app.get("/admin/api/logs/init", async () => {
|
|
27
|
+
const [providersResult, routerKeysResult, groupsResult, retentionResult] = await Promise.allSettled([
|
|
28
|
+
Promise.resolve(getAllProviders(db).map(p => ({ id: p.id, name: p.name }))),
|
|
29
|
+
Promise.resolve(getAllRouterKeys(db).map(rk => ({ id: rk.id, name: rk.name }))),
|
|
30
|
+
(async () => {
|
|
31
|
+
const groups = getAllMappingGroups(db);
|
|
32
|
+
const clientModels = [...new Set(groups.filter(g => g.is_active).map(g => g.client_model))].sort();
|
|
33
|
+
const backendModels = [...new Set(groups.flatMap(g => {
|
|
34
|
+
try {
|
|
35
|
+
const rule = JSON.parse(g.rule);
|
|
36
|
+
return (Array.isArray(rule.targets) ? rule.targets : []).map((t) => t.backend_model);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
}).filter(Boolean))].sort();
|
|
42
|
+
return { client_models: clientModels, backend_models: backendModels };
|
|
43
|
+
})(),
|
|
44
|
+
Promise.resolve(getLogRetentionDays(db)),
|
|
45
|
+
]);
|
|
46
|
+
return {
|
|
47
|
+
providers: providersResult.status === "fulfilled" ? providersResult.value : null,
|
|
48
|
+
router_keys: routerKeysResult.status === "fulfilled" ? routerKeysResult.value : null,
|
|
49
|
+
client_models: groupsResult.status === "fulfilled" ? groupsResult.value.client_models : null,
|
|
50
|
+
backend_models: groupsResult.status === "fulfilled" ? groupsResult.value.backend_models : null,
|
|
51
|
+
log_retention_days: retentionResult.status === "fulfilled" ? retentionResult.value : null,
|
|
52
|
+
};
|
|
53
|
+
});
|
|
25
54
|
app.get("/admin/api/logs", { schema: { querystring: LogQuerySchema } }, async (request, reply) => {
|
|
26
55
|
const query = request.query;
|
|
27
56
|
const page = parseInt(query.page || "1", 10);
|
package/dist/admin/monitor.js
CHANGED
|
@@ -8,6 +8,22 @@ export const adminMonitorRoutes = (app, options, done) => {
|
|
|
8
8
|
done();
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
+
app.get("/admin/api/monitor/init", async () => {
|
|
12
|
+
const [activeResult, recentResult, statsResult, concurrencyResult, runtimeResult] = await Promise.allSettled([
|
|
13
|
+
tracker.getActive(),
|
|
14
|
+
tracker.getRecent(),
|
|
15
|
+
tracker.getStats(),
|
|
16
|
+
tracker.getConcurrency(),
|
|
17
|
+
tracker.getRuntime(),
|
|
18
|
+
]);
|
|
19
|
+
return {
|
|
20
|
+
active: activeResult.status === "fulfilled" ? activeResult.value : null,
|
|
21
|
+
recent: recentResult.status === "fulfilled" ? recentResult.value : null,
|
|
22
|
+
stats: statsResult.status === "fulfilled" ? statsResult.value : null,
|
|
23
|
+
concurrency: concurrencyResult.status === "fulfilled" ? concurrencyResult.value : null,
|
|
24
|
+
runtime: runtimeResult.status === "fulfilled" ? runtimeResult.value : null,
|
|
25
|
+
};
|
|
26
|
+
});
|
|
11
27
|
app.get("/admin/api/monitor/active", async () => tracker.getActive());
|
|
12
28
|
app.get("/admin/api/monitor/recent", async () => tracker.getRecent());
|
|
13
29
|
app.get("/admin/api/monitor/stats", async () => tracker.getStats());
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FastifyPluginCallback } from "fastify";
|
|
2
2
|
import Database from "better-sqlite3";
|
|
3
|
+
import type { Provider } from "../db/index.js";
|
|
3
4
|
import type { StateRegistry } from "../core/registry.js";
|
|
4
5
|
import type { AdaptiveController } from "../core/concurrency/index.js";
|
|
5
6
|
import type { RequestTracker } from "../core/monitor/index.js";
|
|
@@ -11,5 +12,39 @@ interface ProviderRoutesOptions {
|
|
|
11
12
|
adaptiveController?: AdaptiveController;
|
|
12
13
|
proxyAgentFactory?: ProxyAgentFactory;
|
|
13
14
|
}
|
|
15
|
+
/** 序列化 provider 列表,解密敏感字段、展开 models/endpoints。供多个端点复用 */
|
|
16
|
+
export declare function serializeProviders(db: Database.Database, providers: Provider[], encryptionKey: string, concurrencyStatus?: (id: string) => {
|
|
17
|
+
active: number;
|
|
18
|
+
queued: number;
|
|
19
|
+
}): {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
api_type: "anthropic" | "openai" | "openai-responses";
|
|
23
|
+
base_url: string;
|
|
24
|
+
upstream_path: string | null;
|
|
25
|
+
api_key: string;
|
|
26
|
+
models: import("../config/model-context.js").ModelInfo[];
|
|
27
|
+
is_active: number;
|
|
28
|
+
max_concurrency: number;
|
|
29
|
+
queue_timeout_ms: number;
|
|
30
|
+
max_queue_size: number;
|
|
31
|
+
adaptive_enabled: number;
|
|
32
|
+
proxy_type: string | null;
|
|
33
|
+
proxy_url: string | null;
|
|
34
|
+
proxy_username: string | null;
|
|
35
|
+
proxy_password: string | null;
|
|
36
|
+
endpoints: {
|
|
37
|
+
api_type: import("../core/types.js").ApiType;
|
|
38
|
+
base_url: string;
|
|
39
|
+
upstream_path: string | null;
|
|
40
|
+
api_key: string;
|
|
41
|
+
}[];
|
|
42
|
+
concurrency_status: {
|
|
43
|
+
active: number;
|
|
44
|
+
queued: number;
|
|
45
|
+
};
|
|
46
|
+
created_at: string;
|
|
47
|
+
updated_at: string;
|
|
48
|
+
}[];
|
|
14
49
|
export declare const adminProviderRoutes: FastifyPluginCallback<ProviderRoutesOptions>;
|
|
15
50
|
export {};
|
package/dist/admin/providers.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import { getAllProviders, getProviderById, createProvider, updateProvider, deleteProvider, getAllMappingGroups, updateMappingGroup, PROVIDER_CONCURRENCY_DEFAULTS } from "../db/index.js";
|
|
3
3
|
import { parseEndpoints, serializeEndpoints } from "../db/providers.js";
|
|
4
|
+
import { getRecommendedProviders } from "../config/recommended.js";
|
|
4
5
|
import { encrypt, decrypt } from "../utils/crypto.js";
|
|
5
6
|
import { getSetting } from "../db/settings.js";
|
|
6
7
|
import { HTTP_CREATED, HTTP_NOT_FOUND, HTTP_CONFLICT, HTTP_BAD_REQUEST, HTTP_OK } from "./constants.js";
|
|
7
8
|
import { API_CODE, apiError } from "./api-response.js";
|
|
8
|
-
import { parseModels, buildModelInfoList, normalizePatchName } from "../config/model-context.js";
|
|
9
|
+
import { parseModels, buildModelInfoList, normalizePatchName, lookupCapabilities } from "../config/model-context.js";
|
|
9
10
|
import { getModelInfoForProvider, setModelInfoForProvider, deleteAllModelInfoForProvider } from "../db/model-info.js";
|
|
10
11
|
import { buildUpstreamHeaders } from "../proxy/proxy-core.js";
|
|
11
12
|
import { callGet } from "../proxy/transport/http.js";
|
|
@@ -268,42 +269,62 @@ async function handleCreateProvider(body, reply, deps) {
|
|
|
268
269
|
});
|
|
269
270
|
return reply.code(HTTP_CREATED).send({ id });
|
|
270
271
|
}
|
|
272
|
+
/** 序列化 provider 列表,解密敏感字段、展开 models/endpoints。供多个端点复用 */
|
|
273
|
+
export function serializeProviders(db, providers, encryptionKey, concurrencyStatus) {
|
|
274
|
+
return providers.map((s) => {
|
|
275
|
+
const modelEntries = parseModels(s.models || "[]");
|
|
276
|
+
const overrides = new Map(getModelInfoForProvider(db, s.id).map(m => [m.model_name, m.context_window]));
|
|
277
|
+
return {
|
|
278
|
+
id: s.id,
|
|
279
|
+
name: s.name,
|
|
280
|
+
api_type: s.api_type,
|
|
281
|
+
base_url: s.base_url,
|
|
282
|
+
upstream_path: s.upstream_path,
|
|
283
|
+
api_key: s.api_key ? decrypt(s.api_key, encryptionKey) : "",
|
|
284
|
+
models: buildModelInfoList(modelEntries, overrides),
|
|
285
|
+
is_active: s.is_active,
|
|
286
|
+
max_concurrency: s.max_concurrency,
|
|
287
|
+
queue_timeout_ms: s.queue_timeout_ms,
|
|
288
|
+
max_queue_size: s.max_queue_size,
|
|
289
|
+
adaptive_enabled: s.adaptive_enabled,
|
|
290
|
+
proxy_type: s.proxy_type,
|
|
291
|
+
proxy_url: s.proxy_url,
|
|
292
|
+
proxy_username: s.proxy_username ? decrypt(s.proxy_username, encryptionKey) : null,
|
|
293
|
+
proxy_password: s.proxy_password ? decrypt(s.proxy_password, encryptionKey) : null,
|
|
294
|
+
endpoints: parseEndpoints(s.endpoints).map(ep => ({
|
|
295
|
+
api_type: ep.api_type,
|
|
296
|
+
base_url: ep.base_url,
|
|
297
|
+
upstream_path: ep.upstream_path ?? null,
|
|
298
|
+
api_key: ep.api_key ? decrypt(ep.api_key, encryptionKey) : "",
|
|
299
|
+
})),
|
|
300
|
+
concurrency_status: concurrencyStatus?.(s.id) ?? { active: 0, queued: 0 },
|
|
301
|
+
created_at: s.created_at,
|
|
302
|
+
updated_at: s.updated_at,
|
|
303
|
+
};
|
|
304
|
+
});
|
|
305
|
+
}
|
|
271
306
|
export const adminProviderRoutes = (app, options, done) => {
|
|
272
307
|
const { db, stateRegistry, tracker, adaptiveController, proxyAgentFactory } = options;
|
|
273
308
|
app.get("/admin/api/providers", async (_request, reply) => {
|
|
274
309
|
const encryptionKey = getSetting(db, "encryption_key");
|
|
275
310
|
const providers = getAllProviders(db);
|
|
276
|
-
return reply.send(providers
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
proxy_url: s.proxy_url,
|
|
294
|
-
proxy_username: s.proxy_username ? decrypt(s.proxy_username, encryptionKey) : null,
|
|
295
|
-
proxy_password: s.proxy_password ? decrypt(s.proxy_password, encryptionKey) : null,
|
|
296
|
-
endpoints: parseEndpoints(s.endpoints).map(ep => ({
|
|
297
|
-
api_type: ep.api_type,
|
|
298
|
-
base_url: ep.base_url,
|
|
299
|
-
upstream_path: ep.upstream_path ?? null,
|
|
300
|
-
api_key: ep.api_key ? decrypt(ep.api_key, encryptionKey) : "",
|
|
301
|
-
})),
|
|
302
|
-
concurrency_status: stateRegistry?.getProviderStatus(s.id) ?? { active: 0, queued: 0 },
|
|
303
|
-
created_at: s.created_at,
|
|
304
|
-
updated_at: s.updated_at,
|
|
305
|
-
};
|
|
306
|
-
}));
|
|
311
|
+
return reply.send(serializeProviders(db, providers, encryptionKey, (id) => stateRegistry?.getProviderStatus(id) ?? { active: 0, queued: 0 }));
|
|
312
|
+
});
|
|
313
|
+
app.get("/admin/api/providers/init", async (_request, reply) => {
|
|
314
|
+
const encryptionKey = getSetting(db, "encryption_key");
|
|
315
|
+
const providers = getAllProviders(db);
|
|
316
|
+
const serialized = serializeProviders(db, providers, encryptionKey, (id) => stateRegistry?.getProviderStatus(id) ?? { active: 0, queued: 0 });
|
|
317
|
+
const recommended = getRecommendedProviders();
|
|
318
|
+
for (const group of recommended) {
|
|
319
|
+
for (const preset of group.presets) {
|
|
320
|
+
const capMap = {};
|
|
321
|
+
for (const m of preset.models) {
|
|
322
|
+
capMap[m] = lookupCapabilities(m);
|
|
323
|
+
}
|
|
324
|
+
preset.modelCapabilities = capMap;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return reply.send({ providers: serialized, recommended });
|
|
307
328
|
});
|
|
308
329
|
app.post("/admin/api/providers", { schema: { body: CreateProviderSchema } }, async (request, reply) => {
|
|
309
330
|
return handleCreateProvider(request.body, reply, { db, stateRegistry, tracker, adaptiveController });
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import { getSetting, setSetting } from "../db/settings.js";
|
|
3
3
|
import { clearEnhancementConfigCache } from "../proxy/routing/enhancement-config.js";
|
|
4
|
+
import { getAllProviders } from "../db/index.js";
|
|
5
|
+
import { serializeProviders } from "./providers.js";
|
|
4
6
|
const UpdateProxyEnhancementSchema = Type.Object({
|
|
5
7
|
tool_call_loop_enabled: Type.Boolean(),
|
|
6
8
|
stream_loop_enabled: Type.Boolean(),
|
|
@@ -59,5 +61,40 @@ export const adminProxyEnhancementRoutes = (app, options, done) => {
|
|
|
59
61
|
}
|
|
60
62
|
return reply.send({ success: true });
|
|
61
63
|
});
|
|
64
|
+
app.get("/admin/api/proxy-enhancement/init", async (_request, reply) => {
|
|
65
|
+
// config
|
|
66
|
+
const raw = getSetting(db, "proxy_enhancement");
|
|
67
|
+
const defaults = { tool_call_loop_enabled: false, stream_loop_enabled: false, tool_round_limit_enabled: true, tool_error_logging_enabled: false };
|
|
68
|
+
let config = defaults;
|
|
69
|
+
if (raw) {
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(raw);
|
|
72
|
+
config = {
|
|
73
|
+
tool_call_loop_enabled: parsed.tool_call_loop_enabled ?? false,
|
|
74
|
+
stream_loop_enabled: parsed.stream_loop_enabled ?? false,
|
|
75
|
+
tool_round_limit_enabled: parsed.tool_round_limit_enabled ?? true,
|
|
76
|
+
tool_error_logging_enabled: parsed.tool_error_logging_enabled ?? false,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch { /* eslint-disable-line taste/no-silent-catch -- 损坏的 JSON,回退默认 */ }
|
|
80
|
+
}
|
|
81
|
+
const aiConfigRaw = getSetting(db, "ai_retry_config");
|
|
82
|
+
let aiRetryConfig = null;
|
|
83
|
+
if (aiConfigRaw) {
|
|
84
|
+
try {
|
|
85
|
+
aiRetryConfig = JSON.parse(aiConfigRaw);
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
console.error('proxyEnhancementInit.parseAiConfig:', e);
|
|
89
|
+
aiRetryConfig = null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const fullConfig = { ...config, ai_retry_config: aiRetryConfig };
|
|
93
|
+
// providers — simplified list with id, name, models for AI Retry selection
|
|
94
|
+
const encryptionKey = getSetting(db, "encryption_key");
|
|
95
|
+
const providers = getAllProviders(db);
|
|
96
|
+
const serializedProviders = serializeProviders(db, providers, encryptionKey);
|
|
97
|
+
return reply.send({ config: fullConfig, providers: serializedProviders });
|
|
98
|
+
});
|
|
62
99
|
done();
|
|
63
100
|
};
|
|
@@ -7,6 +7,10 @@ import { createRetryRule } from "../db/retry-rules.js";
|
|
|
7
7
|
import { upsertTransformRule } from "../db/transform-rules.js";
|
|
8
8
|
import { encrypt } from "../utils/crypto.js";
|
|
9
9
|
import { getSetting } from "../db/settings.js";
|
|
10
|
+
import { getRecommendedProviders, getRecommendedRetryRules } from "../config/recommended.js";
|
|
11
|
+
import { lookupCapabilities } from "../config/model-context.js";
|
|
12
|
+
import { getAllMappingGroups, getAllProviders } from "../db/index.js";
|
|
13
|
+
import { serializeProviders } from "./providers.js";
|
|
10
14
|
import { HTTP_CREATED, HTTP_BAD_REQUEST, HTTP_BAD_GATEWAY, HTTP_CONFLICT } from "./constants.js";
|
|
11
15
|
import { API_CODE, apiError } from "./api-response.js";
|
|
12
16
|
const PROVIDER_NAME_RE = /^[a-zA-Z0-9_-]+$/;
|
|
@@ -224,6 +228,35 @@ export const adminQuickSetupRoutes = (app, options, done) => {
|
|
|
224
228
|
});
|
|
225
229
|
return reply.code(HTTP_CREATED).send({ success: true, provider_id: providerId });
|
|
226
230
|
});
|
|
231
|
+
app.get("/admin/api/quick-setup/init", async (_request, reply) => {
|
|
232
|
+
// provider_groups (recommended providers with capabilities)
|
|
233
|
+
const groups = getRecommendedProviders();
|
|
234
|
+
for (const group of groups) {
|
|
235
|
+
for (const preset of group.presets) {
|
|
236
|
+
const capMap = {};
|
|
237
|
+
for (const m of preset.models) {
|
|
238
|
+
capMap[m] = lookupCapabilities(m);
|
|
239
|
+
}
|
|
240
|
+
preset.modelCapabilities = capMap;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// recommended_rules (with exists flag)
|
|
244
|
+
const rules = getRecommendedRetryRules();
|
|
245
|
+
const existing = new Set(db.prepare("SELECT name FROM retry_rules").all().map((r) => r.name));
|
|
246
|
+
const recommendedRules = rules.map(r => ({ ...r, exists: existing.has(r.name) }));
|
|
247
|
+
// existing_mappings
|
|
248
|
+
const existingMappings = getAllMappingGroups(db);
|
|
249
|
+
// existing_providers
|
|
250
|
+
const encryptionKey = getSetting(db, "encryption_key");
|
|
251
|
+
const providers = getAllProviders(db);
|
|
252
|
+
const serializedProviders = serializeProviders(db, providers, encryptionKey);
|
|
253
|
+
return reply.send({
|
|
254
|
+
provider_groups: groups,
|
|
255
|
+
recommended_rules: recommendedRules,
|
|
256
|
+
existing_mappings: existingMappings,
|
|
257
|
+
existing_providers: serializedProviders,
|
|
258
|
+
});
|
|
259
|
+
});
|
|
227
260
|
// ---------- Test Connection ----------
|
|
228
261
|
const TestConnectionSchema = Type.Object({
|
|
229
262
|
api_type: Type.Union([Type.Literal("openai"), Type.Literal("openai-responses"), Type.Literal("anthropic")]),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import { getAllRetryRules, getRetryRuleById, createRetryRule, updateRetryRule, deleteRetryRule, } from "../db/index.js";
|
|
2
|
+
import { getAllRetryRules, getRetryRuleById, createRetryRule, updateRetryRule, deleteRetryRule, getAllProviders, } from "../db/index.js";
|
|
3
|
+
import { getRecommendedRetryRules } from "../config/recommended.js";
|
|
3
4
|
import { callLLM } from "../utils/llm-client.js";
|
|
4
5
|
import { getActiveRetryRules } from "../db/retry-rules.js";
|
|
5
6
|
import { getRequestLogById } from "../db/logs.js";
|
|
@@ -270,6 +271,17 @@ export const adminRetryRuleRoutes = (app, options, done) => {
|
|
|
270
271
|
const rules = getAllRetryRules(db);
|
|
271
272
|
return reply.send(rules);
|
|
272
273
|
});
|
|
274
|
+
app.get("/admin/api/retry-rules/init", async (_request, reply) => {
|
|
275
|
+
const rules = getAllRetryRules(db);
|
|
276
|
+
const providers = getAllProviders(db).map(p => ({ id: p.id, name: p.name }));
|
|
277
|
+
const recommendedRaw = getRecommendedRetryRules();
|
|
278
|
+
const existing = new Set(db.prepare("SELECT name FROM retry_rules").all().map(r => r.name));
|
|
279
|
+
const recommended_rules = recommendedRaw.map(r => ({
|
|
280
|
+
...r,
|
|
281
|
+
exists: existing.has(r.name),
|
|
282
|
+
}));
|
|
283
|
+
return reply.send({ rules, providers, recommended_rules });
|
|
284
|
+
});
|
|
273
285
|
app.post("/admin/api/retry-rules", { schema: { body: CreateRetryRuleSchema } }, async (request, reply) => {
|
|
274
286
|
const body = request.body;
|
|
275
287
|
const regexError = validateBodyPattern(body.body_pattern);
|
|
@@ -89,6 +89,12 @@ export const adminRouterKeyRoutes = (app, options, done) => {
|
|
|
89
89
|
deleteRouterKey(db, id);
|
|
90
90
|
return reply.send({ success: true });
|
|
91
91
|
});
|
|
92
|
+
app.get("/admin/api/router-keys/init", async (_request, reply) => {
|
|
93
|
+
const keys = getAllRouterKeys(db);
|
|
94
|
+
const serialized = keys.map((rk) => toPublicRouterKey(rk, db));
|
|
95
|
+
const availableModels = getAvailableModels(db);
|
|
96
|
+
return reply.send({ keys: serialized, available_models: availableModels });
|
|
97
|
+
});
|
|
92
98
|
app.get("/admin/api/models/available", async (_request, reply) => {
|
|
93
99
|
const models = getAvailableModels(db);
|
|
94
100
|
return reply.send(models);
|
package/dist/admin/routes.js
CHANGED
|
@@ -42,7 +42,7 @@ export const adminRoutes = (app, options, done) => {
|
|
|
42
42
|
app.register(adminUsageRoutes, { db: options.db });
|
|
43
43
|
app.register(adminQuickSetupRoutes, { db: options.db, stateRegistry: options.stateRegistry, tracker: options.tracker, adaptiveController: options.adaptiveController });
|
|
44
44
|
app.register(adminUpgradeRoutes, { db: options.db, closeFn: options.closeFn ?? (async () => { }) });
|
|
45
|
-
app.register(adminDashboardRoutes, { db: options.db });
|
|
45
|
+
app.register(adminDashboardRoutes, { db: options.db, stateRegistry: options.stateRegistry });
|
|
46
46
|
app.register(adminTransformRuleRoutes, { db: options.db, pluginRegistry: options.pluginRegistry });
|
|
47
47
|
// Pipeline hooks 查询
|
|
48
48
|
app.get("/admin/api/pipeline/hooks", async () => {
|
package/dist/admin/schedules.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import { getSchedulesByGroup, getAllSchedules, getScheduleById, createSchedule, updateSchedule, deleteSchedule, } from "../db/index.js";
|
|
2
|
+
import { getSchedulesByGroup, getAllSchedules, getScheduleById, createSchedule, updateSchedule, deleteSchedule, getAllMappingGroups, getAllProviders, } from "../db/index.js";
|
|
3
3
|
import { getMappingGroupById, getProviderById } from "../db/index.js";
|
|
4
|
+
import { serializeProviders } from "./providers.js";
|
|
5
|
+
import { getSetting } from "../db/settings.js";
|
|
4
6
|
import { HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_NOT_FOUND } from "./constants.js";
|
|
5
7
|
import { API_CODE, apiError } from "./api-response.js";
|
|
6
8
|
const CreateScheduleSchema = Type.Object({
|
|
@@ -227,5 +229,13 @@ export const adminScheduleRoutes = (app, options, done) => {
|
|
|
227
229
|
updateSchedule(db, id, { enabled: newEnabled });
|
|
228
230
|
return reply.send({ success: true, enabled: newEnabled });
|
|
229
231
|
});
|
|
232
|
+
app.get("/admin/api/schedules/init", async (_request, reply) => {
|
|
233
|
+
const schedules = getAllSchedules(db);
|
|
234
|
+
const mappingGroups = getAllMappingGroups(db);
|
|
235
|
+
const encryptionKey = getSetting(db, "encryption_key");
|
|
236
|
+
const providers = getAllProviders(db);
|
|
237
|
+
const serializedProviders = serializeProviders(db, providers, encryptionKey);
|
|
238
|
+
return reply.send({ schedules, mapping_groups: mappingGroups, providers: serializedProviders });
|
|
239
|
+
});
|
|
230
240
|
done();
|
|
231
241
|
};
|