@pactor-app/skills 1.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +40 -0
  3. package/package.json +26 -0
  4. package/pactor/SKILL.md +373 -0
  5. package/pactor/references/dsl-authoring.md +448 -0
  6. package/prd-to-app/SKILL.md +75 -0
  7. package/prd-to-app/references/acceptance-checklist.md +47 -0
  8. package/prd-to-app/references/component-mapping.md +41 -0
  9. package/prd-to-app/references/mock-data-routes.md +66 -0
  10. package/prd-to-app/references/page-templates.md +274 -0
  11. package/prd-to-app/references/project-structure.md +89 -0
  12. package/prd-to-app/references/theme-and-style.md +41 -0
  13. package/prd-to-app/template/README.md +26 -0
  14. package/prd-to-app/template/app/[[...slug]]/page.tsx +7 -0
  15. package/prd-to-app/template/app/api/auth/login/route.ts +32 -0
  16. package/prd-to-app/template/app/api/auth/logout/route.ts +22 -0
  17. package/prd-to-app/template/app/api/data/[...parts]/route.ts +118 -0
  18. package/prd-to-app/template/app/api/public/pages/[id]/route.ts +40 -0
  19. package/prd-to-app/template/app/api/sitemap/route.ts +21 -0
  20. package/prd-to-app/template/app/api/users/route.ts +57 -0
  21. package/prd-to-app/template/app/favicon.ico +0 -0
  22. package/prd-to-app/template/app/globals.css +248 -0
  23. package/prd-to-app/template/app/layout.tsx +49 -0
  24. package/prd-to-app/template/app/login.css +122 -0
  25. package/prd-to-app/template/eslint.config.mjs +18 -0
  26. package/prd-to-app/template/next-env.d.ts +7 -0
  27. package/prd-to-app/template/next.config.js +32 -0
  28. package/prd-to-app/template/package.json +34 -0
  29. package/prd-to-app/template/pnpm-lock.yaml +6854 -0
  30. package/prd-to-app/template/pnpm-workspace.yaml +7 -0
  31. package/prd-to-app/template/postcss.config.mjs +7 -0
  32. package/prd-to-app/template/public/file.svg +1 -0
  33. package/prd-to-app/template/public/globe.svg +1 -0
  34. package/prd-to-app/template/public/next.svg +1 -0
  35. package/prd-to-app/template/public/skins/chenwunaifu.css +111 -0
  36. package/prd-to-app/template/public/skins/taijingshenlin.css +109 -0
  37. package/prd-to-app/template/public/skins/xuanzhimoyun.css +110 -0
  38. package/prd-to-app/template/public/vercel.svg +1 -0
  39. package/prd-to-app/template/public/window.svg +1 -0
  40. package/prd-to-app/template/scripts/build.mjs +35 -0
  41. package/prd-to-app/template/src/pactor/App.tsx +55 -0
  42. package/prd-to-app/template/src/pactor/actions.ts +438 -0
  43. package/prd-to-app/template/src/pactor/components/card-links.tsx +86 -0
  44. package/prd-to-app/template/src/pactor/components/chip-filter.tsx +89 -0
  45. package/prd-to-app/template/src/pactor/components/chip-node.tsx +46 -0
  46. package/prd-to-app/template/src/pactor/components/debug-chat.tsx +189 -0
  47. package/prd-to-app/template/src/pactor/components/index.ts +28 -0
  48. package/prd-to-app/template/src/pactor/components/log-status.tsx +64 -0
  49. package/prd-to-app/template/src/pactor/components/status-badge.tsx +46 -0
  50. package/prd-to-app/template/src/pactor/components/theme-switcher.tsx +136 -0
  51. package/prd-to-app/template/src/pactor/config.ts +28 -0
  52. package/prd-to-app/template/src/pactor/mocks/audit-mock.ts +403 -0
  53. package/prd-to-app/template/src/pactor/mocks/call-logs-mock.ts +875 -0
  54. package/prd-to-app/template/src/pactor/mocks/client-debug-mock.ts +188 -0
  55. package/prd-to-app/template/src/pactor/mocks/client-templates-mock.ts +497 -0
  56. package/prd-to-app/template/src/pactor/mocks/clients-mock.ts +483 -0
  57. package/prd-to-app/template/src/pactor/mocks/costs-mock.ts +406 -0
  58. package/prd-to-app/template/src/pactor/mocks/dashboard-mock.ts +192 -0
  59. package/prd-to-app/template/src/pactor/mocks/model-debug-mock.ts +144 -0
  60. package/prd-to-app/template/src/pactor/mocks/models-mock.ts +799 -0
  61. package/prd-to-app/template/src/pactor/mocks/ops-usage-mock.ts +806 -0
  62. package/prd-to-app/template/src/pactor/mocks/providers-mock.ts +424 -0
  63. package/prd-to-app/template/src/pactor/mocks/routes-mock.ts +604 -0
  64. package/prd-to-app/template/src/pactor/services.ts +185 -0
  65. package/prd-to-app/template/src/pactor/skin.ts +52 -0
  66. package/prd-to-app/template/src/server/accounts.ts +63 -0
  67. package/prd-to-app/template/src/server/cors.ts +34 -0
  68. package/prd-to-app/template/src/server/sitemap.ts +176 -0
  69. package/prd-to-app/template/src/server/yaml/common/dashboard.yaml +498 -0
  70. package/prd-to-app/template/src/server/yaml/common/login.yaml +84 -0
  71. package/prd-to-app/template/src/server/yaml/provider/provider-create.yaml +340 -0
  72. package/prd-to-app/template/src/server/yaml/provider/provider-detail.yaml +780 -0
  73. package/prd-to-app/template/src/server/yaml/provider/provider-edit.yaml +366 -0
  74. package/prd-to-app/template/src/server/yaml/provider/provider-list.yaml +1989 -0
  75. package/prd-to-app/template/src/server/yaml/route/route-create.yaml +511 -0
  76. package/prd-to-app/template/src/server/yaml/route/route-detail.yaml +610 -0
  77. package/prd-to-app/template/src/server/yaml/route/route-edit.yaml +809 -0
  78. package/prd-to-app/template/src/server/yaml/route/route-list.yaml +1793 -0
  79. package/prd-to-app/template/src/server/yaml/sitemap.yaml +250 -0
  80. package/prd-to-app/template/tsconfig.json +34 -0
@@ -0,0 +1,806 @@
1
+ /**
2
+ * 运行管理 mock:模型/供应商/客户端用量 + 供应商可用性(可靠性)。
3
+ * 行数据以原始数值存储(基准口径 = 7d),查询时按时间范围因子缩放
4
+ * (请求数 / Token / 费用随范围线性缩放,比率与延迟不变),并在 mock 层
5
+ * 预计算全部展示文案(Token 紧凑 K/M/B 与数字两种格式、$ 金额、百分比、
6
+ * ms、配额用量)——DSL 表达式引擎不支持函数调用与数组方法,单元格只做
7
+ * 字段拼接与 compact/raw 三元切换。
8
+ * 模型 / 供应商 / 客户端名称均取自 models-mock / providers-mock /
9
+ * clients-mock 中真实存在的记录,路由组取自 routes-mock 的路由标识。
10
+ */
11
+
12
+ export interface OpsUsageQuery {
13
+ /** ISO 日期对(DatePicker range 值,同仪表盘) */
14
+ start?: string;
15
+ end?: string;
16
+ }
17
+
18
+ /** 一天毫秒数与 ISO 日期解析(yyyy-MM-dd;非法返回 undefined) */
19
+ const DAY_MS = 86400000;
20
+
21
+ function parseDay(s?: string): number | undefined {
22
+ if (!s) return undefined;
23
+ const t = Date.parse(`${s}T00:00:00`);
24
+ return Number.isFinite(t) ? t : undefined;
25
+ }
26
+
27
+ /** 用量缩放因子:区间天数 / 7(基准 7d = 1;缺省或非法按 7d 计) */
28
+ function rangeFactor(q: OpsUsageQuery): number {
29
+ const start = parseDay(q.start);
30
+ const end = parseDay(q.end);
31
+ if (start === undefined || end === undefined || end < start) return 1;
32
+ const days = Math.round((end - start) / DAY_MS) + 1;
33
+ return days / 7;
34
+ }
35
+
36
+ // ── 格式化(mock 层预计算,DSL 侧不调用函数) ─────────────────────
37
+
38
+ const fmtInt = (n: number) => Math.round(n).toLocaleString("en-US");
39
+
40
+ function fmtCompact(n: number): string {
41
+ if (n >= 1e9) return `${(n / 1e9).toFixed(1)}B`;
42
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
43
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
44
+ return String(Math.round(n));
45
+ }
46
+
47
+ const fmtMoney = (n: number) => `$${n.toFixed(2)}`;
48
+ const fmtMoney4 = (n: number) => `$${n.toFixed(4)}`;
49
+ const fmtPct = (x: number) => `${(x * 100).toFixed(1)}%`;
50
+ const fmtMs = (n: number) => `${Math.round(n)}ms`;
51
+ const fmtAttempts = (n: number) => n.toFixed(2);
52
+
53
+ // ── 原始指标行(7d 基准) ────────────────────────────────────────
54
+
55
+ interface RawMetrics {
56
+ requests: number;
57
+ inputTokens: number;
58
+ outputTokens: number;
59
+ cacheHitRate: number;
60
+ /** 标准价合计(USD) */
61
+ standard: number;
62
+ /** 对用户计费合计(USD) */
63
+ billed: number;
64
+ /** 供应侧成本合计(USD) */
65
+ cost: number;
66
+ successRate: number;
67
+ latencyMs: number;
68
+ ttftMs: number;
69
+ upstreamMs: number;
70
+ /** Token/秒 吞吐 */
71
+ tps: number;
72
+ failoverRate: number;
73
+ /** 含重试的平均尝试次数 */
74
+ attempts: number;
75
+ }
76
+
77
+ /** 用量宽表展示行(全部为预计算文案) */
78
+ export interface UsageDisplay {
79
+ requests: string;
80
+ inputCompact: string;
81
+ inputRaw: string;
82
+ outputCompact: string;
83
+ outputRaw: string;
84
+ cacheHit: string;
85
+ standard: string;
86
+ billed: string;
87
+ cost: string;
88
+ avgBilled: string;
89
+ successRate: string;
90
+ avgLatency: string;
91
+ ttft: string;
92
+ avgUpstream: string;
93
+ tps: string;
94
+ failoverRate: string;
95
+ avgAttempts: string;
96
+ }
97
+
98
+ /** 原始指标 → 展示行:量纲字段按范围因子缩放,比率 / 延迟不变 */
99
+ function toUsageDisplay(r: RawMetrics, f: number): UsageDisplay {
100
+ const requests = r.requests * f;
101
+ const input = r.inputTokens * f;
102
+ const output = r.outputTokens * f;
103
+ const billed = r.billed * f;
104
+ return {
105
+ requests: fmtCompact(requests),
106
+ inputCompact: fmtCompact(input),
107
+ inputRaw: fmtInt(input),
108
+ outputCompact: fmtCompact(output),
109
+ outputRaw: fmtInt(output),
110
+ cacheHit: fmtPct(r.cacheHitRate),
111
+ standard: fmtMoney(r.standard * f),
112
+ billed: fmtMoney(billed),
113
+ cost: fmtMoney(r.cost * f),
114
+ avgBilled: fmtMoney4(r.billed / r.requests),
115
+ successRate: fmtPct(r.successRate),
116
+ avgLatency: fmtMs(r.latencyMs),
117
+ ttft: fmtMs(r.ttftMs),
118
+ avgUpstream: fmtMs(r.upstreamMs),
119
+ tps: fmtCompact(r.tps),
120
+ failoverRate: fmtPct(r.failoverRate),
121
+ avgAttempts: fmtAttempts(r.attempts),
122
+ };
123
+ }
124
+
125
+ /** 可靠性展示行(无 Token / 缓存列,错误数随范围缩放) */
126
+ interface QualityDisplay {
127
+ requests: string;
128
+ successRate: string;
129
+ avgLatency: string;
130
+ avgUpstream: string;
131
+ failoverRate: string;
132
+ avgAttempts: string;
133
+ standard: string;
134
+ billed: string;
135
+ cost: string;
136
+ }
137
+
138
+ function toQualityDisplay(r: RawMetrics, f: number): QualityDisplay {
139
+ return {
140
+ requests: fmtCompact(r.requests * f),
141
+ successRate: fmtPct(r.successRate),
142
+ avgLatency: fmtMs(r.latencyMs),
143
+ avgUpstream: fmtMs(r.upstreamMs),
144
+ failoverRate: fmtPct(r.failoverRate),
145
+ avgAttempts: fmtAttempts(r.attempts),
146
+ standard: fmtMoney(r.standard * f),
147
+ billed: fmtMoney(r.billed * f),
148
+ cost: fmtMoney(r.cost * f),
149
+ };
150
+ }
151
+
152
+ export interface UsageSummary {
153
+ standard: string;
154
+ billed: string;
155
+ cost: string;
156
+ }
157
+
158
+ function summarize(rows: RawMetrics[], f: number): UsageSummary {
159
+ const sum = (pick: (r: RawMetrics) => number) =>
160
+ rows.reduce((acc, r) => acc + pick(r), 0) * f;
161
+ return {
162
+ standard: fmtMoney(sum((r) => r.standard)),
163
+ billed: fmtMoney(sum((r) => r.billed)),
164
+ cost: fmtMoney(sum((r) => r.cost)),
165
+ };
166
+ }
167
+
168
+ // ── 模型用量(模型 × 路由组) ────────────────────────────────────
169
+
170
+ interface RawModelUsage extends RawMetrics {
171
+ key: string;
172
+ model: string;
173
+ route: string;
174
+ }
175
+
176
+ const MODEL_USAGE_RAW: RawModelUsage[] = [
177
+ {
178
+ key: "gpt-5/reasoning-default",
179
+ model: "GPT-5",
180
+ route: "reasoning-default",
181
+ requests: 12400,
182
+ inputTokens: 2_100_000,
183
+ outputTokens: 1_800_000,
184
+ cacheHitRate: 0.325,
185
+ standard: 186.0,
186
+ billed: 223.2,
187
+ cost: 148.8,
188
+ successRate: 0.992,
189
+ latencyMs: 1820,
190
+ ttftMs: 340,
191
+ upstreamMs: 1650,
192
+ tps: 1280,
193
+ failoverRate: 0.008,
194
+ attempts: 1.02,
195
+ },
196
+ {
197
+ key: "qwen3.5-flash/general-chat",
198
+ model: "Qwen3.5 Flash",
199
+ route: "general-chat",
200
+ requests: 45200,
201
+ inputTokens: 8_600_000,
202
+ outputTokens: 5_200_000,
203
+ cacheHitRate: 0.412,
204
+ standard: 52.4,
205
+ billed: 68.1,
206
+ cost: 41.3,
207
+ successRate: 0.997,
208
+ latencyMs: 640,
209
+ ttftMs: 180,
210
+ upstreamMs: 560,
211
+ tps: 3420,
212
+ failoverRate: 0.004,
213
+ attempts: 1.01,
214
+ },
215
+ {
216
+ key: "gpt-5/coding-assistant",
217
+ model: "GPT-5",
218
+ route: "coding-assistant",
219
+ requests: 8400,
220
+ inputTokens: 5_800_000,
221
+ outputTokens: 3_900_000,
222
+ cacheHitRate: 0.354,
223
+ standard: 210.5,
224
+ billed: 252.6,
225
+ cost: 168.4,
226
+ successRate: 0.988,
227
+ latencyMs: 2460,
228
+ ttftMs: 480,
229
+ upstreamMs: 2280,
230
+ tps: 1120,
231
+ failoverRate: 0.015,
232
+ attempts: 1.05,
233
+ },
234
+ {
235
+ key: "gemini-3-flash/mobile-lite",
236
+ model: "Gemini 3 Flash",
237
+ route: "mobile-lite",
238
+ requests: 23800,
239
+ inputTokens: 4_100_000,
240
+ outputTokens: 2_600_000,
241
+ cacheHitRate: 0.286,
242
+ standard: 38.2,
243
+ billed: 45.9,
244
+ cost: 30.6,
245
+ successRate: 0.991,
246
+ latencyMs: 720,
247
+ ttftMs: 210,
248
+ upstreamMs: 640,
249
+ tps: 2650,
250
+ failoverRate: 0.012,
251
+ attempts: 1.04,
252
+ },
253
+ {
254
+ key: "qwen3-vl-plus/vision-router",
255
+ model: "Qwen3-VL Plus",
256
+ route: "vision-router",
257
+ requests: 6800,
258
+ inputTokens: 3_400_000,
259
+ outputTokens: 890_000,
260
+ cacheHitRate: 0.128,
261
+ standard: 96.8,
262
+ billed: 116.2,
263
+ cost: 77.4,
264
+ successRate: 0.985,
265
+ latencyMs: 2350,
266
+ ttftMs: 520,
267
+ upstreamMs: 2140,
268
+ tps: 860,
269
+ failoverRate: 0.021,
270
+ attempts: 1.08,
271
+ },
272
+ {
273
+ key: "text-embedding-3-large/embeddings-std",
274
+ model: "Text Embedding 3 Large",
275
+ route: "embeddings-std",
276
+ requests: 96400,
277
+ inputTokens: 12_400_000,
278
+ outputTokens: 0,
279
+ cacheHitRate: 0.052,
280
+ standard: 12.8,
281
+ billed: 15.4,
282
+ cost: 10.2,
283
+ successRate: 0.999,
284
+ latencyMs: 180,
285
+ ttftMs: 90,
286
+ upstreamMs: 150,
287
+ tps: 18500,
288
+ failoverRate: 0.001,
289
+ attempts: 1.0,
290
+ },
291
+ ];
292
+
293
+ export interface ModelUsageRow extends UsageDisplay {
294
+ key: string;
295
+ model: string;
296
+ route: string;
297
+ }
298
+
299
+ export function queryModelUsage(q: OpsUsageQuery): {
300
+ items: ModelUsageRow[];
301
+ summary: UsageSummary;
302
+ total: number;
303
+ } {
304
+ const f = rangeFactor(q);
305
+ const items = MODEL_USAGE_RAW.map((r) => ({
306
+ key: r.key,
307
+ model: r.model,
308
+ route: r.route,
309
+ ...toUsageDisplay(r, f),
310
+ }));
311
+ return { items, summary: summarize(MODEL_USAGE_RAW, f), total: items.length };
312
+ }
313
+
314
+ // ── 供应商用量 ───────────────────────────────────────────────────
315
+
316
+ interface RawProviderUsage extends RawMetrics {
317
+ key: string;
318
+ provider: string;
319
+ }
320
+
321
+ const PROVIDER_USAGE_RAW: RawProviderUsage[] = [
322
+ {
323
+ key: "openai",
324
+ provider: "OpenAI",
325
+ requests: 48200,
326
+ inputTokens: 9_800_000,
327
+ outputTokens: 6_400_000,
328
+ cacheHitRate: 0.301,
329
+ standard: 312.4,
330
+ billed: 374.9,
331
+ cost: 249.9,
332
+ successRate: 0.993,
333
+ latencyMs: 1240,
334
+ ttftMs: 310,
335
+ upstreamMs: 1120,
336
+ tps: 1420,
337
+ failoverRate: 0.006,
338
+ attempts: 1.02,
339
+ },
340
+ {
341
+ key: "dashscope",
342
+ provider: "阿里云百炼",
343
+ requests: 52400,
344
+ inputTokens: 12_600_000,
345
+ outputTokens: 7_800_000,
346
+ cacheHitRate: 0.386,
347
+ standard: 86.4,
348
+ billed: 103.7,
349
+ cost: 69.1,
350
+ successRate: 0.988,
351
+ latencyMs: 580,
352
+ ttftMs: 190,
353
+ upstreamMs: 520,
354
+ tps: 3240,
355
+ failoverRate: 0.018,
356
+ attempts: 1.06,
357
+ },
358
+ {
359
+ key: "deepseek",
360
+ provider: "DeepSeek",
361
+ requests: 31200,
362
+ inputTokens: 7_400_000,
363
+ outputTokens: 4_600_000,
364
+ cacheHitRate: 0.294,
365
+ standard: 42.8,
366
+ billed: 51.4,
367
+ cost: 34.2,
368
+ successRate: 0.984,
369
+ latencyMs: 890,
370
+ ttftMs: 260,
371
+ upstreamMs: 810,
372
+ tps: 2180,
373
+ failoverRate: 0.022,
374
+ attempts: 1.09,
375
+ },
376
+ {
377
+ key: "gemini",
378
+ provider: "Google Gemini",
379
+ requests: 24800,
380
+ inputTokens: 4_400_000,
381
+ outputTokens: 2_800_000,
382
+ cacheHitRate: 0.272,
383
+ standard: 44.6,
384
+ billed: 53.5,
385
+ cost: 35.7,
386
+ successRate: 0.99,
387
+ latencyMs: 740,
388
+ ttftMs: 220,
389
+ upstreamMs: 660,
390
+ tps: 2560,
391
+ failoverRate: 0.011,
392
+ attempts: 1.03,
393
+ },
394
+ {
395
+ key: "anthropic",
396
+ provider: "Anthropic",
397
+ requests: 18400,
398
+ inputTokens: 6_800_000,
399
+ outputTokens: 4_200_000,
400
+ cacheHitRate: 0.338,
401
+ standard: 186.2,
402
+ billed: 223.4,
403
+ cost: 148.9,
404
+ successRate: 0.996,
405
+ latencyMs: 1420,
406
+ ttftMs: 380,
407
+ upstreamMs: 1300,
408
+ tps: 1180,
409
+ failoverRate: 0.003,
410
+ attempts: 1.01,
411
+ },
412
+ {
413
+ key: "azure-openai",
414
+ provider: "Azure OpenAI",
415
+ requests: 12600,
416
+ inputTokens: 3_200_000,
417
+ outputTokens: 2_100_000,
418
+ cacheHitRate: 0.264,
419
+ standard: 98.6,
420
+ billed: 118.3,
421
+ cost: 78.9,
422
+ successRate: 0.971,
423
+ latencyMs: 1580,
424
+ ttftMs: 420,
425
+ upstreamMs: 1460,
426
+ tps: 980,
427
+ failoverRate: 0.034,
428
+ attempts: 1.12,
429
+ },
430
+ ];
431
+
432
+ export interface ProviderUsageRow extends UsageDisplay {
433
+ key: string;
434
+ provider: string;
435
+ }
436
+
437
+ export function queryProviderUsage(q: OpsUsageQuery): {
438
+ items: ProviderUsageRow[];
439
+ summary: UsageSummary;
440
+ total: number;
441
+ } {
442
+ const f = rangeFactor(q);
443
+ const items = PROVIDER_USAGE_RAW.map((r) => ({
444
+ key: r.key,
445
+ provider: r.provider,
446
+ ...toUsageDisplay(r, f),
447
+ }));
448
+ return {
449
+ items,
450
+ summary: summarize(PROVIDER_USAGE_RAW, f),
451
+ total: items.length,
452
+ };
453
+ }
454
+
455
+ // ── 客户端用量(客户端 × AppKey,含配额用量) ────────────────────
456
+
457
+ interface RawClientUsage extends RawMetrics {
458
+ key: string;
459
+ name: string;
460
+ code: string;
461
+ appKeyId: string;
462
+ /** 配额用量(当前周期 用量/上限;周期口径,不随时间范围缩放) */
463
+ quotaUsage: string;
464
+ }
465
+
466
+ const CLIENT_USAGE_RAW: RawClientUsage[] = [
467
+ {
468
+ key: "agent-app",
469
+ name: "agent-app",
470
+ code: "agent-app",
471
+ appKeyId: "gw-ak-7f3a2b1c",
472
+ quotaUsage: "520K/1M",
473
+ requests: 32400,
474
+ inputTokens: 5_200_000,
475
+ outputTokens: 3_100_000,
476
+ cacheHitRate: 0.22,
477
+ standard: 52.6,
478
+ billed: 68.4,
479
+ cost: 41.2,
480
+ successRate: 0.991,
481
+ latencyMs: 960,
482
+ ttftMs: 280,
483
+ upstreamMs: 860,
484
+ tps: 1640,
485
+ failoverRate: 0.009,
486
+ attempts: 1.03,
487
+ },
488
+ {
489
+ key: "web-portal",
490
+ name: "web-portal",
491
+ code: "web-portal",
492
+ appKeyId: "gw-ak-9c1d4e5f",
493
+ quotaUsage: "1.2M/2M",
494
+ requests: 58200,
495
+ inputTokens: 9_600_000,
496
+ outputTokens: 6_200_000,
497
+ cacheHitRate: 0.348,
498
+ standard: 96.2,
499
+ billed: 115.4,
500
+ cost: 76.9,
501
+ successRate: 0.994,
502
+ latencyMs: 820,
503
+ ttftMs: 240,
504
+ upstreamMs: 740,
505
+ tps: 2260,
506
+ failoverRate: 0.007,
507
+ attempts: 1.02,
508
+ },
509
+ {
510
+ key: "internal-batch",
511
+ name: "internal-batch",
512
+ code: "internal-batch",
513
+ appKeyId: "gw-ak-2b8e6a0d",
514
+ quotaUsage: "860K/5M",
515
+ requests: 12800,
516
+ inputTokens: 18_400_000,
517
+ outputTokens: 2_200_000,
518
+ cacheHitRate: 0.086,
519
+ standard: 64.8,
520
+ billed: 77.8,
521
+ cost: 51.8,
522
+ successRate: 0.982,
523
+ latencyMs: 2340,
524
+ ttftMs: 460,
525
+ upstreamMs: 2180,
526
+ tps: 1480,
527
+ failoverRate: 0.026,
528
+ attempts: 1.11,
529
+ },
530
+ {
531
+ key: "legacy-mobile",
532
+ name: "legacy-mobile",
533
+ code: "legacy-mobile",
534
+ appKeyId: "gw-ak-5d7c9b2e",
535
+ quotaUsage: "96K/500K",
536
+ requests: 8600,
537
+ inputTokens: 1_200_000,
538
+ outputTokens: 680_000,
539
+ cacheHitRate: 0.194,
540
+ standard: 12.4,
541
+ billed: 14.9,
542
+ cost: 9.9,
543
+ successRate: 0.968,
544
+ latencyMs: 1120,
545
+ ttftMs: 340,
546
+ upstreamMs: 1020,
547
+ tps: 620,
548
+ failoverRate: 0.041,
549
+ attempts: 1.16,
550
+ },
551
+ {
552
+ key: "expired-trial",
553
+ name: "expired-trial",
554
+ code: "expired-trial",
555
+ appKeyId: "gw-ak-0a4f8c6b",
556
+ quotaUsage: "100K/100K",
557
+ requests: 2400,
558
+ inputTokens: 380_000,
559
+ outputTokens: 210_000,
560
+ cacheHitRate: 0.122,
561
+ standard: 4.2,
562
+ billed: 5.0,
563
+ cost: 3.4,
564
+ successRate: 0.955,
565
+ latencyMs: 1380,
566
+ ttftMs: 390,
567
+ upstreamMs: 1260,
568
+ tps: 340,
569
+ failoverRate: 0.058,
570
+ attempts: 1.21,
571
+ },
572
+ ];
573
+
574
+ export interface ClientUsageRow extends UsageDisplay {
575
+ key: string;
576
+ name: string;
577
+ code: string;
578
+ appKeyId: string;
579
+ quotaUsage: string;
580
+ }
581
+
582
+ export function queryClientUsage(q: OpsUsageQuery): {
583
+ items: ClientUsageRow[];
584
+ summary: UsageSummary;
585
+ total: number;
586
+ } {
587
+ const f = rangeFactor(q);
588
+ const items = CLIENT_USAGE_RAW.map((r) => ({
589
+ key: r.key,
590
+ name: r.name,
591
+ code: r.code,
592
+ appKeyId: r.appKeyId,
593
+ quotaUsage: r.quotaUsage,
594
+ ...toUsageDisplay(r, f),
595
+ }));
596
+ return { items, summary: summarize(CLIENT_USAGE_RAW, f), total: items.length };
597
+ }
598
+
599
+ // ── 供应商可用性(可靠性) ───────────────────────────────────────
600
+
601
+ export interface ProviderQualityRow extends QualityDisplay {
602
+ key: string;
603
+ provider: string;
604
+ /** 失败请求数(随范围缩放) */
605
+ errors: string;
606
+ }
607
+
608
+ export interface ModelProviderCompareRow extends QualityDisplay {
609
+ key: string;
610
+ model: string;
611
+ provider: string;
612
+ }
613
+
614
+ export interface RecentErrorRow {
615
+ id: string;
616
+ /** UTC 时间(MM-DD HH:mm:ss) */
617
+ time: string;
618
+ /** 模型 / 供应商 */
619
+ target: string;
620
+ error: string;
621
+ }
622
+
623
+ interface RawCompare extends RawMetrics {
624
+ key: string;
625
+ model: string;
626
+ provider: string;
627
+ }
628
+
629
+ const COMPARE_RAW: RawCompare[] = [
630
+ {
631
+ key: "gpt-5/openai",
632
+ model: "GPT-5",
633
+ provider: "OpenAI",
634
+ requests: 9200,
635
+ inputTokens: 0,
636
+ outputTokens: 0,
637
+ cacheHitRate: 0,
638
+ standard: 142.6,
639
+ billed: 171.1,
640
+ cost: 114.1,
641
+ successRate: 0.995,
642
+ latencyMs: 1680,
643
+ ttftMs: 0,
644
+ upstreamMs: 1520,
645
+ tps: 0,
646
+ failoverRate: 0.005,
647
+ attempts: 1.01,
648
+ },
649
+ {
650
+ key: "gpt-5/azure-openai",
651
+ model: "GPT-5",
652
+ provider: "Azure OpenAI",
653
+ requests: 3200,
654
+ inputTokens: 0,
655
+ outputTokens: 0,
656
+ cacheHitRate: 0,
657
+ standard: 68.4,
658
+ billed: 82.1,
659
+ cost: 54.7,
660
+ successRate: 0.972,
661
+ latencyMs: 2120,
662
+ ttftMs: 0,
663
+ upstreamMs: 1960,
664
+ tps: 0,
665
+ failoverRate: 0.032,
666
+ attempts: 1.11,
667
+ },
668
+ {
669
+ key: "qwen3.5-flash/dashscope",
670
+ model: "Qwen3.5 Flash",
671
+ provider: "阿里云百炼",
672
+ requests: 38600,
673
+ inputTokens: 0,
674
+ outputTokens: 0,
675
+ cacheHitRate: 0,
676
+ standard: 44.2,
677
+ billed: 53.0,
678
+ cost: 35.4,
679
+ successRate: 0.998,
680
+ latencyMs: 560,
681
+ ttftMs: 0,
682
+ upstreamMs: 500,
683
+ tps: 0,
684
+ failoverRate: 0.002,
685
+ attempts: 1.0,
686
+ },
687
+ {
688
+ key: "qwen3-vl-plus/dashscope",
689
+ model: "Qwen3-VL Plus",
690
+ provider: "阿里云百炼",
691
+ requests: 6800,
692
+ inputTokens: 0,
693
+ outputTokens: 0,
694
+ cacheHitRate: 0,
695
+ standard: 96.8,
696
+ billed: 116.2,
697
+ cost: 77.4,
698
+ successRate: 0.986,
699
+ latencyMs: 2280,
700
+ ttftMs: 0,
701
+ upstreamMs: 2080,
702
+ tps: 0,
703
+ failoverRate: 0.019,
704
+ attempts: 1.07,
705
+ },
706
+ {
707
+ key: "gemini-3-flash/gemini",
708
+ model: "Gemini 3 Flash",
709
+ provider: "Google Gemini",
710
+ requests: 23800,
711
+ inputTokens: 0,
712
+ outputTokens: 0,
713
+ cacheHitRate: 0,
714
+ standard: 38.2,
715
+ billed: 45.9,
716
+ cost: 30.6,
717
+ successRate: 0.991,
718
+ latencyMs: 720,
719
+ ttftMs: 0,
720
+ upstreamMs: 640,
721
+ tps: 0,
722
+ failoverRate: 0.012,
723
+ attempts: 1.04,
724
+ },
725
+ {
726
+ key: "text-embedding-3-large/openai",
727
+ model: "Text Embedding 3 Large",
728
+ provider: "OpenAI",
729
+ requests: 96400,
730
+ inputTokens: 0,
731
+ outputTokens: 0,
732
+ cacheHitRate: 0,
733
+ standard: 12.8,
734
+ billed: 15.4,
735
+ cost: 10.2,
736
+ successRate: 0.999,
737
+ latencyMs: 180,
738
+ ttftMs: 0,
739
+ upstreamMs: 150,
740
+ tps: 0,
741
+ failoverRate: 0.001,
742
+ attempts: 1.0,
743
+ },
744
+ ];
745
+
746
+ const RECENT_ERRORS: RecentErrorRow[] = [
747
+ {
748
+ id: "e1",
749
+ time: "09-19 08:42:18",
750
+ target: "GPT-5 / Azure OpenAI",
751
+ error: "upstream timeout (504)",
752
+ },
753
+ {
754
+ id: "e2",
755
+ time: "09-19 08:31:05",
756
+ target: "Qwen3-VL Plus / 阿里云百炼",
757
+ error: "rate limit exceeded (429)",
758
+ },
759
+ {
760
+ id: "e3",
761
+ time: "09-19 07:58:44",
762
+ target: "GPT-5 / OpenAI",
763
+ error: "connection reset by peer",
764
+ },
765
+ {
766
+ id: "e4",
767
+ time: "09-19 07:12:30",
768
+ target: "Gemini 3 Flash / Google Gemini",
769
+ error: "upstream timeout (504)",
770
+ },
771
+ {
772
+ id: "e5",
773
+ time: "09-19 06:45:02",
774
+ target: "Qwen3.5 Flash / 阿里云百炼",
775
+ error: "context length exceeded (400)",
776
+ },
777
+ {
778
+ id: "e6",
779
+ time: "09-19 05:20:37",
780
+ target: "GPT-5 / Azure OpenAI",
781
+ error: "service unavailable (503)",
782
+ },
783
+ ];
784
+
785
+ export function queryAvailability(q: OpsUsageQuery): {
786
+ providers: ProviderQualityRow[];
787
+ compare: ModelProviderCompareRow[];
788
+ errors: RecentErrorRow[];
789
+ } {
790
+ const f = rangeFactor(q);
791
+ const providers = PROVIDER_USAGE_RAW.map((r) => ({
792
+ key: r.key,
793
+ provider: r.provider,
794
+ errors: fmtInt(r.requests * f * (1 - r.successRate)),
795
+ ...toQualityDisplay(r, f),
796
+ }));
797
+ const compare = COMPARE_RAW.map((r) => ({
798
+ key: r.key,
799
+ model: r.model,
800
+ provider: r.provider,
801
+ ...toQualityDisplay(r, f),
802
+ }));
803
+ // 极短范围仅保留最近几条错误(演示)
804
+ const errors = f < 0.1 ? RECENT_ERRORS.slice(0, 2) : RECENT_ERRORS;
805
+ return { providers, compare, errors };
806
+ }