@chenpu17/cc-gw 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -8
- package/package.json +3 -4
- package/src/server/dist/index.js +172 -83
- package/src/web/dist/assets/{About-DifCcqF-.js → About-BRJQg6-i.js} +1 -1
- package/src/web/dist/assets/{ApiKeys-Cja_7EiV.js → ApiKeys-BcWxL_bz.js} +1 -1
- package/src/web/dist/assets/{Dashboard-BKODF_sa.js → Dashboard-RjvGXnju.js} +1 -1
- package/src/web/dist/assets/{Help-CVYbgGOi.js → Help-C33N-jg2.js} +1 -1
- package/src/web/dist/assets/{Logs-ZqIQEkJH.js → Logs-B-TUIEjX.js} +1 -1
- package/src/web/dist/assets/ModelManagement-Bjnc9-06.js +1 -0
- package/src/web/dist/assets/{Settings-CaTqdHzh.js → Settings-Btgwveb7.js} +1 -1
- package/src/web/dist/assets/index-DTby9g2A.css +1 -0
- package/src/web/dist/assets/{index-DG02tgGK.js → index-DWhAov2p.js} +1 -1
- package/src/web/dist/assets/{index-CDJfhjXI.js → index-fNmJF3BX.js} +10 -10
- package/src/web/dist/assets/{useApiQuery-Bna2BImG.js → useApiQuery-CCi1tkCr.js} +1 -1
- package/src/web/dist/index.html +2 -2
- package/src/web/dist/assets/ModelManagement-CWeSK3Hd.js +0 -1
- package/src/web/dist/assets/index-fgl6ZbLo.css +0 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ cc-gw start --daemon --port 4100
|
|
|
28
28
|
|
|
29
29
|
首启会在 `~/.cc-gw/config.json` 生成配置模板,推荐直接通过 Web UI (`http://127.0.0.1:4100/ui`) 完成所有后续配置与调整。`cc-gw status`、`cc-gw stop`、`cc-gw restart` 可用于日常运维。
|
|
30
30
|
|
|
31
|
-
> ⚠️ **Linux
|
|
31
|
+
> ⚠️ **Linux 安装提示**:本项目依赖 `better-sqlite3`;该库已为 Node 20/22/24 在 glibc/musl(x64/arm64/arm)提供预编译二进制,通常无需额外工具。如果你的环境未命中预编译(例如更早版本的 Node 或稀有架构),请先安装 `build-essential python3 make g++`,再运行 `npm install -g @chenpu17/cc-gw --unsafe-perm --build-from-source`。
|
|
32
32
|
|
|
33
33
|
### 从源码构建(开发者)
|
|
34
34
|
|
|
@@ -67,10 +67,17 @@ UI 支持中英文、深色/浅色主题以及移动端响应式布局,提供
|
|
|
67
67
|
1. 启动 cc-gw 并确认配置中 `host` 为 `127.0.0.1`,`port` 与 CLI 启动一致。
|
|
68
68
|
2. 在安装了 Claude Code 的终端设置环境变量:
|
|
69
69
|
```bash
|
|
70
|
-
export ANTHROPIC_BASE_URL=http://127.0.0.1:4100
|
|
70
|
+
export ANTHROPIC_BASE_URL=http://127.0.0.1:4100/anthropic
|
|
71
71
|
claude "help me review this file"
|
|
72
72
|
```
|
|
73
|
-
3. cc-gw
|
|
73
|
+
3. 如果使用 VS Code 插件(Claude Code),在“自定义 API”中同样填写 `http://127.0.0.1:4100/anthropic`,插件会自动追加 `/v1/messages` 与 `?beta=true`,最后粘贴 cc-gw Web UI 或 CLI 创建的 API Key。
|
|
74
|
+
4. cc-gw 会根据 `modelRoutes`/默认策略将 Claude 请求路由到已配置的目标模型(如 Kimi、火山 DeepSeek、OpenAI 或自建模型)。
|
|
75
|
+
|
|
76
|
+
### 连接 Codex(原 Claude Code for Repo)
|
|
77
|
+
1. 在 Web UI 的“模型管理 → 路由配置”中为 `/openai` 端点选择目标模型,默认会映射至配置中的 `defaults.completion`。
|
|
78
|
+
2. 在 Codex 或其他需要 OpenAI 风格接口的客户端中,将 Base URL 设置为 `http://127.0.0.1:4100/openai/v1`;若需手动指定路径,请调用 `POST /openai/v1/responses`。
|
|
79
|
+
3. 将 API Key 设置为 cc-gw 生成的密钥(支持 Bearer Header 或 `x-api-key` Header)。
|
|
80
|
+
4. 触发一次 `hello` 或最小请求检查 Streaming 是否正常;若遇到 `Unsupported parameter: thinking` 等提示,说明 cc-gw 已自动剥离该字段并兼容上游。
|
|
74
81
|
|
|
75
82
|
### 使用场景 / Usage Scenarios
|
|
76
83
|
|
|
@@ -134,6 +141,7 @@ UI 支持中英文、深色/浅色主题以及移动端响应式布局,提供
|
|
|
134
141
|
- `modelRoutes`:将 Claude 发起的模型名映射到上游模型;未命中时使用 `defaults`。
|
|
135
142
|
- `storePayloads`:是否在 SQLite 中压缩保存原始请求/响应(Brotli),关闭后仅保留元信息。
|
|
136
143
|
- `logLevel`:控制 Fastify/Pino 控制台日志级别(`fatal`/`error`/`warn`/`info`/`debug`/`trace`)。
|
|
144
|
+
- `providers[].authMode`:仅在 `type: "anthropic"` 时生效,可选 `apiKey`(默认,发送 `x-api-key`)或 `authToken`(发送 `Authorization: Bearer`)。配置 Claude Code 使用 `ANTHROPIC_AUTH_TOKEN` 时,请选择 `authToken` 并在 `apiKey` 输入框填入该值。
|
|
137
145
|
- `requestLogging`:是否输出每个 HTTP 请求的进入日志。
|
|
138
146
|
- `responseLogging`:是否输出每个 HTTP 请求完成的日志,可独立于 `requestLogging` 控制。
|
|
139
147
|
- 推荐通过 Web UI 的“模型管理 / 系统设置”在线编辑并热加载,无需手工修改文件。
|
|
@@ -166,7 +174,7 @@ pnpm --filter @cc-gw/cli exec tsx index.ts status
|
|
|
166
174
|
|
|
167
175
|
## 数据与日志
|
|
168
176
|
|
|
169
|
-
- 数据库:`~/.cc-gw/data/gateway.db`(`sqlite3
|
|
177
|
+
- 数据库:`~/.cc-gw/data/gateway.db`(`better-sqlite3` 管理的嵌入式 SQLite)。
|
|
170
178
|
- `request_logs`:请求摘要、路由结果、耗时、TTFT/TPOT。
|
|
171
179
|
- `request_payloads`:压缩的请求/响应正文(Brotli)。
|
|
172
180
|
- `daily_metrics`:按日聚合的调用次数与 Token 统计。
|
|
@@ -190,7 +198,7 @@ cc-gw is a local gateway tailored for Claude Code and similar Anthropic-compatib
|
|
|
190
198
|
| ------- | ------- |
|
|
191
199
|
| Protocol adaptation | Converts Claude-style payloads into OpenAI-, Anthropic-, Kimi-, and DeepSeek-compatible requests while preserving tool calls and reasoning blocks. |
|
|
192
200
|
| Model routing | Maps incoming model IDs to configured upstream providers with fallbacks for long-context and background tasks. |
|
|
193
|
-
| Observability | Persists request logs, token usage (including cache hits), TTFT, TPOT, and daily aggregates
|
|
201
|
+
| Observability | Persists request logs, token usage (including cache hits), TTFT, TPOT, and daily aggregates via better-sqlite3 with Brotli-compressed payloads. |
|
|
194
202
|
| Web console | React + Vite UI with dashboards, filters, provider CRUD, bilingual copy, and responsive layout. |
|
|
195
203
|
| CLI daemon | `cc-gw` command wraps start/stop/restart/status, manages PID/log files, and scaffolds a default config on first launch. |
|
|
196
204
|
|
|
@@ -203,7 +211,7 @@ cc-gw start --daemon --port 4100
|
|
|
203
211
|
|
|
204
212
|
The first launch writes `~/.cc-gw/config.json`. Manage everything through the Web UI at `http://127.0.0.1:4100/ui`. Use `cc-gw status`, `cc-gw stop`, and `cc-gw restart` to control the daemon.
|
|
205
213
|
|
|
206
|
-
> ⚠️ **Linux build note**: `sqlite3
|
|
214
|
+
> ⚠️ **Linux build note**: We now depend on `better-sqlite3`. Prebuilt binaries ship for Node 20/22/24 on glibc & musl (x64/arm64/arm). If you’re targeting an unsupported combo, install `build-essential python3 make g++` first, then rerun `npm install -g @chenpu17/cc-gw --unsafe-perm --build-from-source`.
|
|
207
215
|
|
|
208
216
|
### From Source (contributors)
|
|
209
217
|
|
|
@@ -214,13 +222,24 @@ pnpm --filter @cc-gw/web build
|
|
|
214
222
|
pnpm --filter @cc-gw/cli exec tsx index.ts start --daemon --port 4100
|
|
215
223
|
```
|
|
216
224
|
|
|
217
|
-
Connect Claude Code by pointing `ANTHROPIC_BASE_URL` to
|
|
225
|
+
Connect Claude Code by pointing `ANTHROPIC_BASE_URL` to the Anthropic namespace on cc-gw. Both the CLI and editor clients append `/v1/messages` automatically:
|
|
218
226
|
|
|
219
227
|
```bash
|
|
220
|
-
export ANTHROPIC_BASE_URL=http://127.0.0.1:4100
|
|
228
|
+
export ANTHROPIC_BASE_URL=http://127.0.0.1:4100/anthropic
|
|
221
229
|
claude "help me review this file"
|
|
222
230
|
```
|
|
223
231
|
|
|
232
|
+
Using the Claude Code VS Code extension? Open the extension settings, enable the custom API mode, set the Base URL to the same `http://127.0.0.1:4100/anthropic`, and paste an API key generated from the cc-gw Web UI or CLI—the extension appends `/v1/messages?beta=true` automatically and cc-gw now forwards the query string upstream.
|
|
233
|
+
|
|
234
|
+
Connect Codex (or any OpenAI-compatible IDE integration) by targeting the OpenAI endpoint exposed by cc-gw:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
export OPENAI_BASE_URL=http://127.0.0.1:4100/openai/v1
|
|
238
|
+
export OPENAI_API_KEY="<your cc-gw api key>"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
If the client expects a full path, call `POST /openai/v1/responses`. The gateway strips unsupported fields (such as `thinking`) before forwarding to the upstream provider, so health checks like `hello` should stream back correctly.
|
|
242
|
+
|
|
224
243
|
### Configuration Snapshot
|
|
225
244
|
|
|
226
245
|
- Providers include `type`, `baseUrl`, `apiKey`, and `models` descriptions.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chenpu17/cc-gw",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -51,16 +51,16 @@
|
|
|
51
51
|
],
|
|
52
52
|
"repository": {
|
|
53
53
|
"type": "git",
|
|
54
|
-
"url": "https://github.com/chenpu17/cc-gw.git"
|
|
54
|
+
"url": "git+https://github.com/chenpu17/cc-gw.git"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@fastify/cors": "^9.0.1",
|
|
58
58
|
"@fastify/static": "^7.0.4",
|
|
59
|
+
"better-sqlite3": "^12.4.1",
|
|
59
60
|
"colorette": "^2.0.20",
|
|
60
61
|
"commander": "^12.0.0",
|
|
61
62
|
"fastify": "^4.26.2",
|
|
62
63
|
"open": "^10.1.0",
|
|
63
|
-
"sqlite3": "^5.1.7",
|
|
64
64
|
"tiktoken": "^1.0.21",
|
|
65
65
|
"undici": "^6.11.1"
|
|
66
66
|
},
|
|
@@ -68,7 +68,6 @@
|
|
|
68
68
|
"@eslint/js": "^9.5.0",
|
|
69
69
|
"@playwright/test": "^1.55.1",
|
|
70
70
|
"@types/node": "^20.12.7",
|
|
71
|
-
"@types/sqlite3": "^3.1.11",
|
|
72
71
|
"eslint": "^8.57.0",
|
|
73
72
|
"globals": "^15.0.0",
|
|
74
73
|
"prettier": "^3.2.5",
|
package/src/server/dist/index.js
CHANGED
|
@@ -91,6 +91,16 @@ function parseConfig(raw) {
|
|
|
91
91
|
if (!Array.isArray(data.providers)) {
|
|
92
92
|
data.providers = [];
|
|
93
93
|
}
|
|
94
|
+
data.providers = data.providers.map((provider) => {
|
|
95
|
+
if (!provider || typeof provider !== "object")
|
|
96
|
+
return provider;
|
|
97
|
+
if (provider.type === "anthropic") {
|
|
98
|
+
provider.authMode = provider.authMode === "authToken" ? "authToken" : "apiKey";
|
|
99
|
+
} else if ("authMode" in provider) {
|
|
100
|
+
delete provider.authMode;
|
|
101
|
+
}
|
|
102
|
+
return provider;
|
|
103
|
+
});
|
|
94
104
|
const legacyDefaults = sanitizeDefaults(data.defaults);
|
|
95
105
|
if (typeof data.logRetentionDays !== "number") {
|
|
96
106
|
data.logRetentionDays = 30;
|
|
@@ -572,6 +582,12 @@ function buildAnthropicBody(payload, options = {}) {
|
|
|
572
582
|
if (typeof options.temperature === "number") {
|
|
573
583
|
body.temperature = options.temperature;
|
|
574
584
|
}
|
|
585
|
+
if (payload.original && typeof payload.original === "object") {
|
|
586
|
+
const original = payload.original;
|
|
587
|
+
if (original.metadata && typeof original.metadata === "object") {
|
|
588
|
+
body.metadata = original.metadata;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
575
591
|
const tools = options.overrideTools ?? payload.tools;
|
|
576
592
|
if (tools && tools.length > 0) {
|
|
577
593
|
body.tools = tools.map((tool) => ({
|
|
@@ -590,6 +606,41 @@ function buildAnthropicBody(payload, options = {}) {
|
|
|
590
606
|
// providers/openai.ts
|
|
591
607
|
import { fetch } from "undici";
|
|
592
608
|
import { ReadableStream } from "stream/web";
|
|
609
|
+
|
|
610
|
+
// providers/utils.ts
|
|
611
|
+
function appendQuery(url, query) {
|
|
612
|
+
if (!query)
|
|
613
|
+
return url;
|
|
614
|
+
const baseHasQuery = url.includes("?");
|
|
615
|
+
if (typeof query === "string") {
|
|
616
|
+
const normalized = query.startsWith("?") ? query.slice(1) : query;
|
|
617
|
+
if (!normalized) {
|
|
618
|
+
return baseHasQuery ? url : `${url}?`;
|
|
619
|
+
}
|
|
620
|
+
return baseHasQuery ? `${url}&${normalized}` : `${url}?${normalized}`;
|
|
621
|
+
}
|
|
622
|
+
const params = new URLSearchParams();
|
|
623
|
+
for (const [key, value] of Object.entries(query)) {
|
|
624
|
+
if (value == null)
|
|
625
|
+
continue;
|
|
626
|
+
if (Array.isArray(value)) {
|
|
627
|
+
for (const item of value) {
|
|
628
|
+
if (item == null)
|
|
629
|
+
continue;
|
|
630
|
+
params.append(key, String(item));
|
|
631
|
+
}
|
|
632
|
+
} else {
|
|
633
|
+
params.append(key, String(value));
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
const serialized = params.toString();
|
|
637
|
+
if (!serialized) {
|
|
638
|
+
return baseHasQuery ? url : `${url}?`;
|
|
639
|
+
}
|
|
640
|
+
return baseHasQuery ? `${url}&${serialized}` : `${url}?${serialized}`;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// providers/openai.ts
|
|
593
644
|
var encoder = new TextEncoder();
|
|
594
645
|
function createJsonStream(payload) {
|
|
595
646
|
const text = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
@@ -619,7 +670,7 @@ function resolveEndpoint(config, options) {
|
|
|
619
670
|
}
|
|
620
671
|
function createOpenAIConnector(config, options) {
|
|
621
672
|
const url = resolveEndpoint(config, options);
|
|
622
|
-
const shouldLogEndpoint = process.env.CC_GW_DEBUG_ENDPOINTS === "1";
|
|
673
|
+
const shouldLogEndpoint = process.env.CC_GW_DEBUG_ENDPOINTS === "1" || process.env.CC_GW_DEBUG_OPENAI === "1";
|
|
623
674
|
return {
|
|
624
675
|
id: config.id,
|
|
625
676
|
async send(request) {
|
|
@@ -637,10 +688,11 @@ function createOpenAIConnector(config, options) {
|
|
|
637
688
|
stream: request.stream ?? false
|
|
638
689
|
};
|
|
639
690
|
const payload = options?.mutateBody ? options.mutateBody(body) : body;
|
|
691
|
+
const finalUrl = appendQuery(url, request.query);
|
|
640
692
|
if (shouldLogEndpoint) {
|
|
641
|
-
console.info(`[cc-gw] provider=${config.id} endpoint=${
|
|
693
|
+
console.info(`[cc-gw] provider=${config.id} endpoint=${finalUrl}`);
|
|
642
694
|
}
|
|
643
|
-
const res = await fetch(
|
|
695
|
+
const res = await fetch(finalUrl, {
|
|
644
696
|
method: "POST",
|
|
645
697
|
headers,
|
|
646
698
|
body: JSON.stringify(payload)
|
|
@@ -749,18 +801,26 @@ import { fetch as fetch2 } from "undici";
|
|
|
749
801
|
var DEFAULT_VERSION = "2023-06-01";
|
|
750
802
|
function createAnthropicConnector(config) {
|
|
751
803
|
const baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
804
|
+
const endpoint = resolveAnthropicEndpoint(baseUrl);
|
|
805
|
+
const shouldLogEndpoint = process.env.CC_GW_DEBUG_ENDPOINTS === "1" || process.env.CC_GW_DEBUG_OPENAI === "1";
|
|
752
806
|
return {
|
|
753
807
|
id: config.id,
|
|
754
808
|
async send(request) {
|
|
755
|
-
const headers = {
|
|
756
|
-
"
|
|
809
|
+
const headers = normalizeHeaders({
|
|
810
|
+
"content-type": "application/json",
|
|
757
811
|
"anthropic-version": DEFAULT_VERSION,
|
|
758
812
|
...config.extraHeaders,
|
|
759
813
|
...request.headers
|
|
760
|
-
};
|
|
761
|
-
delete headers.
|
|
814
|
+
});
|
|
815
|
+
delete headers.authorization;
|
|
816
|
+
delete headers["x-api-key"];
|
|
762
817
|
if (config.apiKey) {
|
|
763
|
-
|
|
818
|
+
const mode = config.authMode === "authToken" ? "authToken" : "apiKey";
|
|
819
|
+
if (mode === "authToken") {
|
|
820
|
+
headers.authorization = `Bearer ${config.apiKey}`;
|
|
821
|
+
} else {
|
|
822
|
+
headers["x-api-key"] = config.apiKey;
|
|
823
|
+
}
|
|
764
824
|
}
|
|
765
825
|
if (!headers["anthropic-version"]) {
|
|
766
826
|
headers["anthropic-version"] = DEFAULT_VERSION;
|
|
@@ -770,11 +830,37 @@ function createAnthropicConnector(config) {
|
|
|
770
830
|
model: request.model,
|
|
771
831
|
stream: request.stream ?? false
|
|
772
832
|
};
|
|
773
|
-
const
|
|
833
|
+
const finalUrl = appendQuery(endpoint, request.query);
|
|
834
|
+
if (shouldLogEndpoint) {
|
|
835
|
+
console.info(`[cc-gw] provider=${config.id} endpoint=${finalUrl}`);
|
|
836
|
+
if (process.env.CC_GW_DEBUG_HEADERS === "1") {
|
|
837
|
+
const safeHeaders = {};
|
|
838
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
839
|
+
if (key.toLowerCase().includes("authorization")) {
|
|
840
|
+
safeHeaders[key] = "<redacted>";
|
|
841
|
+
} else {
|
|
842
|
+
safeHeaders[key] = value;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
console.info(`[cc-gw] provider=${config.id} headers`, safeHeaders);
|
|
846
|
+
try {
|
|
847
|
+
console.info(`[cc-gw] provider=${config.id} payload`, JSON.stringify(payload).slice(0, 500));
|
|
848
|
+
} catch {
|
|
849
|
+
console.info(`[cc-gw] provider=${config.id} payload`, "[unserializable payload]");
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
if (headers["content-length"]) {
|
|
854
|
+
delete headers["content-length"];
|
|
855
|
+
}
|
|
856
|
+
const response = await fetch2(finalUrl, {
|
|
774
857
|
method: "POST",
|
|
775
858
|
headers,
|
|
776
859
|
body: JSON.stringify(payload)
|
|
777
860
|
});
|
|
861
|
+
if (shouldLogEndpoint) {
|
|
862
|
+
console.info(`[cc-gw] provider=${config.id} status=${response.status}`);
|
|
863
|
+
}
|
|
778
864
|
return {
|
|
779
865
|
status: response.status,
|
|
780
866
|
headers: response.headers,
|
|
@@ -783,6 +869,39 @@ function createAnthropicConnector(config) {
|
|
|
783
869
|
}
|
|
784
870
|
};
|
|
785
871
|
}
|
|
872
|
+
function normalizeHeaders(source) {
|
|
873
|
+
const result = {};
|
|
874
|
+
for (const [key, value] of Object.entries(source)) {
|
|
875
|
+
if (value == null)
|
|
876
|
+
continue;
|
|
877
|
+
const normalizedKey = key.toLowerCase();
|
|
878
|
+
if (Array.isArray(value)) {
|
|
879
|
+
const candidate = value.find((item) => item != null);
|
|
880
|
+
if (candidate != null) {
|
|
881
|
+
result[normalizedKey] = String(candidate);
|
|
882
|
+
}
|
|
883
|
+
} else {
|
|
884
|
+
result[normalizedKey] = String(value);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return result;
|
|
888
|
+
}
|
|
889
|
+
function resolveAnthropicEndpoint(baseUrl) {
|
|
890
|
+
const normalized = baseUrl.replace(/\/$/, "");
|
|
891
|
+
if (normalized.endsWith("/messages") || normalized.match(/\/v\d+\/messages$/)) {
|
|
892
|
+
return normalized;
|
|
893
|
+
}
|
|
894
|
+
if (normalized.match(/\/v\d+$/)) {
|
|
895
|
+
return `${normalized}/messages`;
|
|
896
|
+
}
|
|
897
|
+
if (normalized.endsWith("/anthropic")) {
|
|
898
|
+
return `${normalized}/v1/messages`;
|
|
899
|
+
}
|
|
900
|
+
if (normalized.endsWith("/anthropic/v1")) {
|
|
901
|
+
return `${normalized}/messages`;
|
|
902
|
+
}
|
|
903
|
+
return `${normalized}/v1/messages`;
|
|
904
|
+
}
|
|
786
905
|
|
|
787
906
|
// providers/registry.ts
|
|
788
907
|
var connectors = /* @__PURE__ */ new Map();
|
|
@@ -823,72 +942,32 @@ import { brotliCompressSync, brotliDecompressSync, constants as zlibConstants }
|
|
|
823
942
|
import fs2 from "fs";
|
|
824
943
|
import os2 from "os";
|
|
825
944
|
import path2 from "path";
|
|
826
|
-
import
|
|
945
|
+
import Database from "better-sqlite3";
|
|
827
946
|
var HOME_OVERRIDE2 = process.env.CC_GW_HOME;
|
|
828
947
|
var HOME_DIR2 = path2.resolve(HOME_OVERRIDE2 ?? path2.join(os2.homedir(), ".cc-gw"));
|
|
829
948
|
var DATA_DIR = path2.join(HOME_DIR2, "data");
|
|
830
949
|
var DB_PATH = path2.join(DATA_DIR, "gateway.db");
|
|
831
|
-
sqlite3.verbose();
|
|
832
950
|
var dbPromise = null;
|
|
833
951
|
var dbInstance = null;
|
|
834
952
|
function exec(db, sql) {
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
if (error) {
|
|
838
|
-
reject(error);
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
resolve();
|
|
842
|
-
});
|
|
843
|
-
});
|
|
953
|
+
db.exec(sql);
|
|
954
|
+
return Promise.resolve();
|
|
844
955
|
}
|
|
845
956
|
function run(db, sql, params = []) {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
return;
|
|
851
|
-
}
|
|
852
|
-
resolve({ lastID: this.lastID, changes: this.changes });
|
|
853
|
-
};
|
|
854
|
-
if (Array.isArray(params)) {
|
|
855
|
-
db.run(sql, params, handler);
|
|
856
|
-
} else {
|
|
857
|
-
db.run(sql, params, handler);
|
|
858
|
-
}
|
|
859
|
-
});
|
|
957
|
+
const statement = db.prepare(sql);
|
|
958
|
+
const result = Array.isArray(params) ? statement.run(...params) : statement.run(params);
|
|
959
|
+
const lastID = typeof result.lastInsertRowid === "bigint" ? Number(result.lastInsertRowid) : result.lastInsertRowid;
|
|
960
|
+
return Promise.resolve({ lastID, changes: result.changes });
|
|
860
961
|
}
|
|
861
962
|
function all(db, sql, params = []) {
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
reject(error);
|
|
866
|
-
return;
|
|
867
|
-
}
|
|
868
|
-
resolve(rows);
|
|
869
|
-
};
|
|
870
|
-
if (Array.isArray(params)) {
|
|
871
|
-
db.all(sql, params, callback);
|
|
872
|
-
} else {
|
|
873
|
-
db.all(sql, params, callback);
|
|
874
|
-
}
|
|
875
|
-
});
|
|
963
|
+
const statement = db.prepare(sql);
|
|
964
|
+
const rows = Array.isArray(params) ? statement.all(...params) : statement.all(params);
|
|
965
|
+
return Promise.resolve(rows);
|
|
876
966
|
}
|
|
877
967
|
function get(db, sql, params = []) {
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
reject(error);
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
resolve(row);
|
|
885
|
-
};
|
|
886
|
-
if (Array.isArray(params)) {
|
|
887
|
-
db.get(sql, params, callback);
|
|
888
|
-
} else {
|
|
889
|
-
db.get(sql, params, callback);
|
|
890
|
-
}
|
|
891
|
-
});
|
|
968
|
+
const statement = db.prepare(sql);
|
|
969
|
+
const row = Array.isArray(params) ? statement.get(...params) : statement.get(params);
|
|
970
|
+
return Promise.resolve(row);
|
|
892
971
|
}
|
|
893
972
|
async function columnExists(db, table, column) {
|
|
894
973
|
const rows = await all(db, `PRAGMA table_info(${table})`);
|
|
@@ -1053,20 +1132,18 @@ async function getDb() {
|
|
|
1053
1132
|
}
|
|
1054
1133
|
if (!dbPromise) {
|
|
1055
1134
|
fs2.mkdirSync(DATA_DIR, { recursive: true });
|
|
1056
|
-
dbPromise =
|
|
1057
|
-
const instance = new
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
});
|
|
1069
|
-
});
|
|
1135
|
+
dbPromise = (async () => {
|
|
1136
|
+
const instance = new Database(DB_PATH);
|
|
1137
|
+
instance.pragma("journal_mode = WAL");
|
|
1138
|
+
try {
|
|
1139
|
+
await ensureSchema(instance);
|
|
1140
|
+
dbInstance = instance;
|
|
1141
|
+
return instance;
|
|
1142
|
+
} catch (error) {
|
|
1143
|
+
instance.close();
|
|
1144
|
+
throw error;
|
|
1145
|
+
}
|
|
1146
|
+
})();
|
|
1070
1147
|
}
|
|
1071
1148
|
return dbPromise;
|
|
1072
1149
|
}
|
|
@@ -1808,13 +1885,18 @@ async function registerMessagesRoute(app) {
|
|
|
1808
1885
|
};
|
|
1809
1886
|
const rawUrl = typeof request.raw?.url === "string" ? request.raw.url : request.url ?? "";
|
|
1810
1887
|
let querySuffix = null;
|
|
1811
|
-
if (typeof rawUrl === "string"
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1888
|
+
if (typeof rawUrl === "string") {
|
|
1889
|
+
const questionIndex = rawUrl.indexOf("?");
|
|
1890
|
+
if (questionIndex !== -1) {
|
|
1891
|
+
querySuffix = rawUrl.slice(questionIndex + 1);
|
|
1892
|
+
}
|
|
1815
1893
|
}
|
|
1816
|
-
if (querySuffix) {
|
|
1817
|
-
|
|
1894
|
+
if (!querySuffix && typeof request.querystring === "string") {
|
|
1895
|
+
querySuffix = request.querystring || null;
|
|
1896
|
+
}
|
|
1897
|
+
if (querySuffix !== null) {
|
|
1898
|
+
const displaySuffix = querySuffix.length > 0 ? `?${querySuffix}` : "?";
|
|
1899
|
+
console.info(`[cc-gw] inbound url ${rawUrl} query ${displaySuffix}`);
|
|
1818
1900
|
}
|
|
1819
1901
|
const normalized = normalizeClaudePayload(payload);
|
|
1820
1902
|
const requestedModel = typeof payload.model === "string" ? payload.model : void 0;
|
|
@@ -2472,6 +2554,7 @@ data: ${JSON.stringify(data)}
|
|
|
2472
2554
|
};
|
|
2473
2555
|
app.post("/v1/messages", handler);
|
|
2474
2556
|
app.post("/anthropic/v1/messages", handler);
|
|
2557
|
+
app.post("/anthropic/v1/v1/messages", handler);
|
|
2475
2558
|
}
|
|
2476
2559
|
|
|
2477
2560
|
// protocol/normalize-openai.ts
|
|
@@ -3742,13 +3825,19 @@ async function createServer() {
|
|
|
3742
3825
|
}
|
|
3743
3826
|
if (responseLogEnabled) {
|
|
3744
3827
|
app.addHook("onResponse", (request, reply, done) => {
|
|
3828
|
+
let elapsedTime;
|
|
3829
|
+
if (typeof reply.elapsedTime === "number") {
|
|
3830
|
+
elapsedTime = reply.elapsedTime;
|
|
3831
|
+
} else if (typeof reply.getResponseTime === "function") {
|
|
3832
|
+
elapsedTime = reply.getResponseTime();
|
|
3833
|
+
}
|
|
3745
3834
|
app.log.info(
|
|
3746
3835
|
{
|
|
3747
3836
|
reqId: request.id,
|
|
3748
3837
|
res: {
|
|
3749
3838
|
statusCode: reply.statusCode
|
|
3750
3839
|
},
|
|
3751
|
-
responseTime:
|
|
3840
|
+
responseTime: elapsedTime
|
|
3752
3841
|
},
|
|
3753
3842
|
"request completed"
|
|
3754
3843
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{u as x,a as p,r as o,j as e}from"./index-
|
|
1
|
+
import{u as x,a as p,r as o,j as e}from"./index-fNmJF3BX.js";import{u as m}from"./useApiQuery-CCi1tkCr.js";const h="0.3.2",v={version:h},g={};function N(){const{t:s}=x(),{pushToast:l}=p(),t=m(["status","gateway"],{url:"/api/status",method:"GET"},{staleTime:6e4});o.useEffect(()=>{t.isError&&t.error&&l({title:s("about.toast.statusError.title"),description:t.error.message,variant:"error"})},[t.isError,t.error,l,s]);const d=v.version,r=o.useMemo(()=>{const a=g,u=a.VITE_BUILD_TIME??"-",b=a.VITE_NODE_VERSION??"-";return{buildTime:u,nodeVersion:b}},[]),i=()=>{l({title:s("about.toast.updatesPlanned"),variant:"info"})},n=[{label:s("about.app.labels.name"),value:"cc-local-gw"},{label:s("about.app.labels.version"),value:d},{label:s("about.app.labels.buildTime"),value:r.buildTime},{label:s("about.app.labels.node"),value:r.nodeVersion}],c=t.data?[{label:s("about.status.labels.host"),value:t.data.host??"127.0.0.1"},{label:s("about.status.labels.port"),value:t.data.port},{label:s("about.status.labels.providers"),value:t.data.providers},{label:s("about.status.labels.active"),value:t.data.activeRequests??0}]:[];return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsxs("header",{className:"flex flex-col gap-2",children:[e.jsx("h1",{className:"text-2xl font-semibold",children:s("about.title")}),e.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:s("about.description")})]}),e.jsxs("section",{className:"grid gap-4 rounded-lg border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900 md:grid-cols-2",children:[e.jsxs("div",{className:"space-y-3",children:[e.jsx("h2",{className:"text-lg font-semibold",children:s("about.app.title")}),e.jsx("dl",{className:"grid grid-cols-[160px_1fr] gap-2 text-sm text-slate-600 dark:text-slate-300",children:n.map(a=>e.jsxs("div",{className:"contents",children:[e.jsx("dt",{className:"font-medium",children:a.label}),e.jsx("dd",{children:a.value})]},a.label))})]}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("h2",{className:"text-lg font-semibold",children:s("about.status.title")}),t.isLoading?e.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:s("about.status.loading")}):t.data?e.jsx("dl",{className:"grid grid-cols-[160px_1fr] gap-2 text-sm text-slate-600 dark:text-slate-300",children:c.map(a=>e.jsxs("div",{className:"contents",children:[e.jsx("dt",{className:"font-medium",children:a.label}),e.jsx("dd",{children:a.value})]},a.label))}):e.jsx("p",{className:"text-sm text-red-500",children:s("about.status.empty")})]})]}),e.jsxs("section",{className:"rounded-lg border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold",children:s("about.support.title")}),e.jsx("p",{className:"text-xs text-slate-500 dark:text-slate-400",children:s("about.support.subtitle")})]}),e.jsx("button",{type:"button",onClick:i,className:"rounded-md border border-slate-200 px-3 py-1 text-sm transition hover:bg-slate-100 dark:border-slate-700 dark:hover:bg-slate-800",children:s("about.support.actions.checkUpdates")})]}),e.jsx("p",{className:"mt-4 text-sm leading-6 text-slate-600 dark:text-slate-300",children:s("about.support.description")})]})]})}export{N as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{c as g,u as R,a as U,r as d,j as e,L as k,K as W}from"./index-
|
|
1
|
+
import{c as g,u as R,a as U,r as d,j as e,L as k,K as W}from"./index-fNmJF3BX.js";import{E as L}from"./index-DWhAov2p.js";import{u as N,a as v}from"./useApiQuery-CCi1tkCr.js";/**
|
|
2
2
|
* @license lucide-react v0.344.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{u as A,a as R,r as h,j as t,L as D}from"./index-CDJfhjXI.js";import{E as O}from"./index-DG02tgGK.js";import{u as v}from"./useApiQuery-Bna2BImG.js";function N(e,a,s){return e==null?"-":`${e.toLocaleString(void 0,s)} ${a}`}function B(e){if(e==null)return"-";if(e<1024)return`${e} B`;const a=["KB","MB","GB","TB"];let s=e/1024,d=0;for(;s>=1024&&d<a.length-1;)s/=1024,d+=1;return`${s.toFixed(s>=100?0:s>=10?1:2)} ${a[d]}`}function _(){var S;const{t:e}=A(),{pushToast:a}=R(),[s,d]=h.useState("all"),l=s==="all"?void 0:s,b=v(["stats","overview",s],{url:"/api/stats/overview",method:"GET",params:l?{endpoint:l}:void 0}),p=v(["stats","daily",14,s],{url:"/api/stats/daily",method:"GET",params:{days:14,...l?{endpoint:l}:{}}}),u=v(["stats","model",7,6,s],{url:"/api/stats/model",method:"GET",params:{days:7,limit:6,...l?{endpoint:l}:{}}}),g=v(["status"],{url:"/api/status",method:"GET"}),f=v(["db","info"],{url:"/api/db/info",method:"GET"}),y=v(["logs","recent",s],{url:"/api/logs",method:"GET",params:{limit:5,...l?{endpoint:l}:{}}},{refetchInterval:3e4});h.useEffect(()=>{b.isError&&b.error&&a({title:e("dashboard.toast.overviewError"),description:b.error.message,variant:"error"})},[b.isError,b.error,a,e]),h.useEffect(()=>{p.isError&&p.error&&a({title:e("dashboard.toast.dailyError"),description:p.error.message,variant:"error"})},[p.isError,p.error,a,e]),h.useEffect(()=>{u.isError&&u.error&&a({title:e("dashboard.toast.modelError"),description:u.error.message,variant:"error"})},[u.isError,u.error,a,e]),h.useEffect(()=>{g.isError&&g.error&&a({title:e("dashboard.toast.statusError"),description:g.error.message,variant:"error"})},[g.isError,g.error,a,e]),h.useEffect(()=>{f.isError&&f.error&&a({title:e("dashboard.toast.dbError"),description:f.error.message,variant:"error"})},[f.isError,f.error,a,e]),h.useEffect(()=>{y.isError&&y.error&&a({title:e("dashboard.toast.recentError"),description:y.error.message,variant:"error"})},[y.isError,y.error,a,e]);const c=b.data,j=p.data??[],n=u.data??[],k=g.data,M=f.data,$=((S=y.data)==null?void 0:S.items)??[],q=h.useMemo(()=>{const i=j.map(o=>o.date),x=e("dashboard.charts.barRequests"),r=e("dashboard.charts.lineInput"),m=e("dashboard.charts.lineOutput");return{tooltip:{trigger:"axis"},legend:{data:[x,r,m]},grid:{left:40,right:20,top:40,bottom:40},xAxis:{type:"category",data:i},yAxis:{type:"value"},series:[{name:x,type:"bar",data:j.map(o=>o.requestCount),itemStyle:{color:"#2563eb"}},{name:r,type:"line",yAxisIndex:0,data:j.map(o=>o.inputTokens),smooth:!0,itemStyle:{color:"#22c55e"}},{name:m,type:"line",yAxisIndex:0,data:j.map(o=>o.outputTokens),smooth:!0,itemStyle:{color:"#f97316"}}]}},[j,e]),w=h.useMemo(()=>{const i=n.map(o=>`${o.provider}/${o.model}`),x=e("dashboard.charts.barRequests"),r=e("dashboard.charts.lineInput"),m=e("dashboard.charts.lineOutput");return{tooltip:{trigger:"axis"},legend:{data:[x,r,m]},grid:{left:50,right:40,top:40,bottom:70},xAxis:{type:"category",data:i,axisLabel:{rotate:30}},yAxis:[{type:"value",name:x},{type:"value",name:e("dashboard.charts.axisTokens"),position:"right"}],series:[{name:x,type:"bar",data:n.map(o=>o.requests),itemStyle:{color:"#6366f1"},yAxisIndex:0},{name:r,type:"line",yAxisIndex:1,smooth:!0,data:n.map(o=>o.inputTokens??0),itemStyle:{color:"#22c55e"}},{name:m,type:"line",yAxisIndex:1,smooth:!0,data:n.map(o=>o.outputTokens??0),itemStyle:{color:"#f97316"}}]}},[n,e]),I=h.useMemo(()=>{const i=n.map(r=>`${r.provider}/${r.model}`),x=e("dashboard.charts.ttftLabel");return{tooltip:{trigger:"axis",formatter(r){var T;if(!Array.isArray(r)||r.length===0)return"";const m=((T=r[0])==null?void 0:T.dataIndex)??0,o=n[m];return o?`<strong>${i[m]}</strong><br/>${x}: ${N(o.avgTtftMs,e("common.units.ms"))}`:""}},grid:{left:50,right:30,top:40,bottom:70},xAxis:{type:"category",data:i,axisLabel:{rotate:30}},yAxis:{type:"value",name:e("dashboard.charts.ttftAxis")},series:[{name:x,type:"bar",data:n.map(r=>r.avgTtftMs??0),itemStyle:{color:"#2563eb"}}]}},[n,e]),P=h.useMemo(()=>{const i=n.map(r=>`${r.provider}/${r.model}`),x=e("dashboard.charts.tpotLabel");return{tooltip:{trigger:"axis",formatter(r){var T;if(!Array.isArray(r)||r.length===0)return"";const m=((T=r[0])==null?void 0:T.dataIndex)??0,o=n[m];return o?`<strong>${i[m]}</strong><br/>${x}: ${N(o.avgTpotMs,e("common.units.msPerToken"),{maximumFractionDigits:2})}`:""}},grid:{left:50,right:30,top:40,bottom:70},xAxis:{type:"category",data:i,axisLabel:{rotate:30}},yAxis:{type:"value",name:e("dashboard.charts.tpotAxis")},series:[{name:x,type:"bar",data:n.map(r=>r.avgTpotMs??0),itemStyle:{color:"#f97316"}}]}},[n,e]);return b.isPending||g.isPending||f.isPending?t.jsx(D,{}):t.jsxs("div",{className:"flex flex-col gap-6",children:[t.jsxs("section",{className:"flex flex-col gap-2",children:[t.jsx("h1",{className:"text-2xl font-semibold",children:e("nav.dashboard")}),t.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:e("dashboard.description")}),k?t.jsxs("div",{className:"flex flex-wrap gap-3 rounded-lg border border-slate-200 bg-white p-4 text-sm shadow-sm dark:border-slate-800 dark:bg-slate-900","aria-live":"polite",children:[t.jsx("span",{className:"font-medium",children:e("dashboard.status.listening",{host:k.host??"0.0.0.0",port:k.port})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.providers",{value:k.providers})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.todayRequests",{value:((c==null?void 0:c.today.requests)??0).toLocaleString()})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.active",{value:(k.activeRequests??0).toLocaleString()})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.dbSize",{value:M?B(M.sizeBytes):"-"})})]}):null,t.jsxs("div",{className:"mt-2 flex flex-wrap items-center gap-2 text-sm text-slate-500 dark:text-slate-400",children:[t.jsx("label",{className:"text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400",children:e("dashboard.filters.endpoint")}),t.jsxs("select",{value:s,onChange:i=>d(i.target.value),className:"h-9 rounded-md border border-slate-200 bg-white px-3 text-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 dark:focus:border-blue-400 dark:focus:ring-blue-400/40",children:[t.jsx("option",{value:"all",children:e("dashboard.filters.endpointAll")}),t.jsx("option",{value:"anthropic",children:e("dashboard.filters.endpointAnthropic")}),t.jsx("option",{value:"openai",children:e("dashboard.filters.endpointOpenAI")})]})]})]}),t.jsxs("section",{className:"grid gap-4 md:grid-cols-2 xl:grid-cols-4",children:[t.jsx(E,{title:e("dashboard.cards.todayRequests"),value:(c==null?void 0:c.today.requests)??0,suffix:e("common.units.request")}),t.jsx(E,{title:e("dashboard.cards.todayInput"),value:(c==null?void 0:c.today.inputTokens)??0,suffix:e("common.units.token")}),t.jsx(E,{title:e("dashboard.cards.todayOutput"),value:(c==null?void 0:c.today.outputTokens)??0,suffix:e("common.units.token")}),t.jsx(E,{title:e("dashboard.cards.avgLatency"),value:(c==null?void 0:c.today.avgLatencyMs)??0,suffix:e("common.units.ms")})]}),t.jsxs("div",{className:"grid gap-6 xl:grid-cols-2",children:[t.jsx(L,{title:e("dashboard.charts.requestsTitle"),description:e("dashboard.charts.requestsDesc"),loading:p.isPending,option:q,empty:!j.length,emptyText:e("dashboard.charts.empty")}),t.jsx(L,{title:e("dashboard.charts.modelTitle"),description:e("dashboard.charts.modelDesc"),loading:u.isPending,option:w,empty:!n.length,emptyText:e("dashboard.charts.empty")})]}),t.jsxs("div",{className:"grid gap-6 xl:grid-cols-2",children:[t.jsx(L,{title:e("dashboard.charts.ttftTitle"),description:e("dashboard.charts.ttftDesc"),loading:u.isPending,option:I,empty:!n.some(i=>i.avgTtftMs!=null&&i.avgTtftMs>0),emptyText:e("dashboard.charts.ttftEmpty")}),t.jsx(L,{title:e("dashboard.charts.tpotTitle"),description:e("dashboard.charts.tpotDesc"),loading:u.isPending,option:P,empty:!n.some(i=>i.avgTpotMs!=null&&i.avgTpotMs>0),emptyText:e("dashboard.charts.tpotEmpty")})]}),t.jsx(G,{models:n,loading:u.isPending}),t.jsx(Q,{loading:y.isPending,records:$})]})}function E({title:e,value:a,suffix:s}){return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:e}),t.jsxs("p",{className:"mt-2 text-2xl font-semibold",children:[a.toLocaleString(),s?t.jsx("span",{className:"ml-1 text-base font-normal text-slate-500 dark:text-slate-400",children:s}):null]})]})}function L({title:e,description:a,option:s,loading:d,empty:l,emptyText:b}){const{t:p}=A();return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsxs("div",{className:"mb-4",children:[t.jsx("p",{className:"text-sm font-semibold",children:e}),t.jsx("p",{className:"text-xs text-slate-500 dark:text-slate-400",children:a})]}),d?t.jsx("div",{className:"flex h-60 items-center justify-center text-sm text-slate-400",children:p("common.loadingShort")}):l?t.jsx("div",{className:"flex h-60 items-center justify-center text-sm text-slate-400",children:b??p("dashboard.charts.empty")}):t.jsx(O,{option:s,style:{height:260},notMerge:!0,lazyUpdate:!0,theme:void 0})]})}function G({models:e,loading:a}){const{t:s}=A(),d=e.length>0;return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsx("div",{className:"flex items-center justify-between border-b border-slate-200 px-4 py-3 dark:border-slate-800",children:t.jsxs("div",{children:[t.jsx("p",{className:"text-sm font-semibold",children:s("dashboard.modelTable.title")}),t.jsx("p",{className:"text-xs text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.description")})]})}),a?t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("common.loadingShort")}):d?t.jsx("div",{className:"max-h-80 overflow-x-auto",children:t.jsxs("table",{className:"min-w-full divide-y divide-slate-200 text-sm dark:divide-slate-800",children:[t.jsx("caption",{className:"sr-only",children:s("dashboard.modelTable.title")}),t.jsx("thead",{className:"bg-slate-100 dark:bg-slate-800/50",children:t.jsxs("tr",{children:[t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.model")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.requests")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.latency")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.ttft")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.tpot")})]})}),t.jsx("tbody",{className:"divide-y divide-slate-200 dark:divide-slate-800",children:e.map(l=>t.jsxs("tr",{className:"hover:bg-slate-50 dark:hover:bg-slate-800/60",children:[t.jsx("td",{className:"px-4 py-2",children:t.jsxs("div",{className:"flex flex-col",children:[t.jsx("span",{className:"font-medium text-slate-700 dark:text-slate-100",children:l.provider}),t.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:l.model})]})}),t.jsx("td",{className:"px-4 py-2 text-right",children:l.requests.toLocaleString()}),t.jsx("td",{className:"px-4 py-2 text-right",children:N(l.avgLatencyMs,s("common.units.ms"))}),t.jsx("td",{className:"px-4 py-2 text-right",children:N(l.avgTtftMs,s("common.units.ms"))}),t.jsx("td",{className:"px-4 py-2 text-right",children:N(l.avgTpotMs,s("common.units.msPerToken"),{maximumFractionDigits:2})})]},`${l.provider}/${l.model}`))})]})}):t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("dashboard.modelTable.empty")})]})}function Q({records:e,loading:a}){const{t:s}=A();return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsxs("div",{className:"flex items-center justify-between border-b border-slate-200 px-4 py-3 dark:border-slate-800",children:[t.jsx("p",{className:"text-sm font-semibold",children:s("dashboard.recent.title")}),t.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:s("dashboard.recent.subtitle",{count:5})})]}),a?t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("dashboard.recent.loading")}):e.length===0?t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("dashboard.recent.empty")}):t.jsx("div",{className:"max-h-80 overflow-auto",children:t.jsxs("table",{className:"min-w-full divide-y divide-slate-200 text-sm dark:divide-slate-800",children:[t.jsx("caption",{className:"sr-only",children:s("dashboard.recent.title")}),t.jsx("thead",{className:"bg-slate-100 dark:bg-slate-800/50",children:t.jsxs("tr",{children:[t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.time")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.endpoint")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.provider")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.route")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.latency")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.status")})]})}),t.jsx("tbody",{className:"divide-y divide-slate-200 dark:divide-slate-800",children:e.map(d=>t.jsxs("tr",{className:"hover:bg-slate-50 dark:hover:bg-slate-800/60",children:[t.jsx("td",{className:"px-4 py-2 text-xs text-slate-500 dark:text-slate-400",children:new Date(d.timestamp).toLocaleString()}),t.jsx("td",{className:"px-4 py-2 text-xs text-slate-500 dark:text-slate-400",children:d.endpoint==="anthropic"?s("logs.table.endpointAnthropic"):d.endpoint==="openai"?s("logs.table.endpointOpenAI"):d.endpoint}),t.jsx("td",{className:"px-4 py-2",children:d.provider}),t.jsxs("td",{className:"px-4 py-2",children:[t.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:d.client_model??s("dashboard.recent.routePlaceholder")}),t.jsx("span",{className:"mx-1 text-slate-400",children:"→"}),t.jsx("span",{className:"font-medium text-slate-700 dark:text-slate-100",children:d.model})]}),t.jsx("td",{className:"px-4 py-2",children:N(d.latency_ms,s("common.units.ms"))}),t.jsx("td",{className:"px-4 py-2",children:t.jsxs("span",{className:`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${d.error?"bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-200":"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-200"}`,children:[(d.status_code??200).toString(),t.jsx("span",{className:"ml-1",children:d.error?s("common.status.error"):s("common.status.success")})]})})]},d.id))})]})})]})}export{_ as default};
|
|
1
|
+
import{u as A,a as R,r as h,j as t,L as D}from"./index-fNmJF3BX.js";import{E as O}from"./index-DWhAov2p.js";import{u as v}from"./useApiQuery-CCi1tkCr.js";function N(e,a,s){return e==null?"-":`${e.toLocaleString(void 0,s)} ${a}`}function B(e){if(e==null)return"-";if(e<1024)return`${e} B`;const a=["KB","MB","GB","TB"];let s=e/1024,d=0;for(;s>=1024&&d<a.length-1;)s/=1024,d+=1;return`${s.toFixed(s>=100?0:s>=10?1:2)} ${a[d]}`}function _(){var S;const{t:e}=A(),{pushToast:a}=R(),[s,d]=h.useState("all"),l=s==="all"?void 0:s,b=v(["stats","overview",s],{url:"/api/stats/overview",method:"GET",params:l?{endpoint:l}:void 0}),p=v(["stats","daily",14,s],{url:"/api/stats/daily",method:"GET",params:{days:14,...l?{endpoint:l}:{}}}),u=v(["stats","model",7,6,s],{url:"/api/stats/model",method:"GET",params:{days:7,limit:6,...l?{endpoint:l}:{}}}),g=v(["status"],{url:"/api/status",method:"GET"}),f=v(["db","info"],{url:"/api/db/info",method:"GET"}),y=v(["logs","recent",s],{url:"/api/logs",method:"GET",params:{limit:5,...l?{endpoint:l}:{}}},{refetchInterval:3e4});h.useEffect(()=>{b.isError&&b.error&&a({title:e("dashboard.toast.overviewError"),description:b.error.message,variant:"error"})},[b.isError,b.error,a,e]),h.useEffect(()=>{p.isError&&p.error&&a({title:e("dashboard.toast.dailyError"),description:p.error.message,variant:"error"})},[p.isError,p.error,a,e]),h.useEffect(()=>{u.isError&&u.error&&a({title:e("dashboard.toast.modelError"),description:u.error.message,variant:"error"})},[u.isError,u.error,a,e]),h.useEffect(()=>{g.isError&&g.error&&a({title:e("dashboard.toast.statusError"),description:g.error.message,variant:"error"})},[g.isError,g.error,a,e]),h.useEffect(()=>{f.isError&&f.error&&a({title:e("dashboard.toast.dbError"),description:f.error.message,variant:"error"})},[f.isError,f.error,a,e]),h.useEffect(()=>{y.isError&&y.error&&a({title:e("dashboard.toast.recentError"),description:y.error.message,variant:"error"})},[y.isError,y.error,a,e]);const c=b.data,j=p.data??[],n=u.data??[],k=g.data,M=f.data,$=((S=y.data)==null?void 0:S.items)??[],q=h.useMemo(()=>{const i=j.map(o=>o.date),x=e("dashboard.charts.barRequests"),r=e("dashboard.charts.lineInput"),m=e("dashboard.charts.lineOutput");return{tooltip:{trigger:"axis"},legend:{data:[x,r,m]},grid:{left:40,right:20,top:40,bottom:40},xAxis:{type:"category",data:i},yAxis:{type:"value"},series:[{name:x,type:"bar",data:j.map(o=>o.requestCount),itemStyle:{color:"#2563eb"}},{name:r,type:"line",yAxisIndex:0,data:j.map(o=>o.inputTokens),smooth:!0,itemStyle:{color:"#22c55e"}},{name:m,type:"line",yAxisIndex:0,data:j.map(o=>o.outputTokens),smooth:!0,itemStyle:{color:"#f97316"}}]}},[j,e]),w=h.useMemo(()=>{const i=n.map(o=>`${o.provider}/${o.model}`),x=e("dashboard.charts.barRequests"),r=e("dashboard.charts.lineInput"),m=e("dashboard.charts.lineOutput");return{tooltip:{trigger:"axis"},legend:{data:[x,r,m]},grid:{left:50,right:40,top:40,bottom:70},xAxis:{type:"category",data:i,axisLabel:{rotate:30}},yAxis:[{type:"value",name:x},{type:"value",name:e("dashboard.charts.axisTokens"),position:"right"}],series:[{name:x,type:"bar",data:n.map(o=>o.requests),itemStyle:{color:"#6366f1"},yAxisIndex:0},{name:r,type:"line",yAxisIndex:1,smooth:!0,data:n.map(o=>o.inputTokens??0),itemStyle:{color:"#22c55e"}},{name:m,type:"line",yAxisIndex:1,smooth:!0,data:n.map(o=>o.outputTokens??0),itemStyle:{color:"#f97316"}}]}},[n,e]),I=h.useMemo(()=>{const i=n.map(r=>`${r.provider}/${r.model}`),x=e("dashboard.charts.ttftLabel");return{tooltip:{trigger:"axis",formatter(r){var T;if(!Array.isArray(r)||r.length===0)return"";const m=((T=r[0])==null?void 0:T.dataIndex)??0,o=n[m];return o?`<strong>${i[m]}</strong><br/>${x}: ${N(o.avgTtftMs,e("common.units.ms"))}`:""}},grid:{left:50,right:30,top:40,bottom:70},xAxis:{type:"category",data:i,axisLabel:{rotate:30}},yAxis:{type:"value",name:e("dashboard.charts.ttftAxis")},series:[{name:x,type:"bar",data:n.map(r=>r.avgTtftMs??0),itemStyle:{color:"#2563eb"}}]}},[n,e]),P=h.useMemo(()=>{const i=n.map(r=>`${r.provider}/${r.model}`),x=e("dashboard.charts.tpotLabel");return{tooltip:{trigger:"axis",formatter(r){var T;if(!Array.isArray(r)||r.length===0)return"";const m=((T=r[0])==null?void 0:T.dataIndex)??0,o=n[m];return o?`<strong>${i[m]}</strong><br/>${x}: ${N(o.avgTpotMs,e("common.units.msPerToken"),{maximumFractionDigits:2})}`:""}},grid:{left:50,right:30,top:40,bottom:70},xAxis:{type:"category",data:i,axisLabel:{rotate:30}},yAxis:{type:"value",name:e("dashboard.charts.tpotAxis")},series:[{name:x,type:"bar",data:n.map(r=>r.avgTpotMs??0),itemStyle:{color:"#f97316"}}]}},[n,e]);return b.isPending||g.isPending||f.isPending?t.jsx(D,{}):t.jsxs("div",{className:"flex flex-col gap-6",children:[t.jsxs("section",{className:"flex flex-col gap-2",children:[t.jsx("h1",{className:"text-2xl font-semibold",children:e("nav.dashboard")}),t.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:e("dashboard.description")}),k?t.jsxs("div",{className:"flex flex-wrap gap-3 rounded-lg border border-slate-200 bg-white p-4 text-sm shadow-sm dark:border-slate-800 dark:bg-slate-900","aria-live":"polite",children:[t.jsx("span",{className:"font-medium",children:e("dashboard.status.listening",{host:k.host??"0.0.0.0",port:k.port})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.providers",{value:k.providers})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.todayRequests",{value:((c==null?void 0:c.today.requests)??0).toLocaleString()})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.active",{value:(k.activeRequests??0).toLocaleString()})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.dbSize",{value:M?B(M.sizeBytes):"-"})})]}):null,t.jsxs("div",{className:"mt-2 flex flex-wrap items-center gap-2 text-sm text-slate-500 dark:text-slate-400",children:[t.jsx("label",{className:"text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400",children:e("dashboard.filters.endpoint")}),t.jsxs("select",{value:s,onChange:i=>d(i.target.value),className:"h-9 rounded-md border border-slate-200 bg-white px-3 text-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 dark:focus:border-blue-400 dark:focus:ring-blue-400/40",children:[t.jsx("option",{value:"all",children:e("dashboard.filters.endpointAll")}),t.jsx("option",{value:"anthropic",children:e("dashboard.filters.endpointAnthropic")}),t.jsx("option",{value:"openai",children:e("dashboard.filters.endpointOpenAI")})]})]})]}),t.jsxs("section",{className:"grid gap-4 md:grid-cols-2 xl:grid-cols-4",children:[t.jsx(E,{title:e("dashboard.cards.todayRequests"),value:(c==null?void 0:c.today.requests)??0,suffix:e("common.units.request")}),t.jsx(E,{title:e("dashboard.cards.todayInput"),value:(c==null?void 0:c.today.inputTokens)??0,suffix:e("common.units.token")}),t.jsx(E,{title:e("dashboard.cards.todayOutput"),value:(c==null?void 0:c.today.outputTokens)??0,suffix:e("common.units.token")}),t.jsx(E,{title:e("dashboard.cards.avgLatency"),value:(c==null?void 0:c.today.avgLatencyMs)??0,suffix:e("common.units.ms")})]}),t.jsxs("div",{className:"grid gap-6 xl:grid-cols-2",children:[t.jsx(L,{title:e("dashboard.charts.requestsTitle"),description:e("dashboard.charts.requestsDesc"),loading:p.isPending,option:q,empty:!j.length,emptyText:e("dashboard.charts.empty")}),t.jsx(L,{title:e("dashboard.charts.modelTitle"),description:e("dashboard.charts.modelDesc"),loading:u.isPending,option:w,empty:!n.length,emptyText:e("dashboard.charts.empty")})]}),t.jsxs("div",{className:"grid gap-6 xl:grid-cols-2",children:[t.jsx(L,{title:e("dashboard.charts.ttftTitle"),description:e("dashboard.charts.ttftDesc"),loading:u.isPending,option:I,empty:!n.some(i=>i.avgTtftMs!=null&&i.avgTtftMs>0),emptyText:e("dashboard.charts.ttftEmpty")}),t.jsx(L,{title:e("dashboard.charts.tpotTitle"),description:e("dashboard.charts.tpotDesc"),loading:u.isPending,option:P,empty:!n.some(i=>i.avgTpotMs!=null&&i.avgTpotMs>0),emptyText:e("dashboard.charts.tpotEmpty")})]}),t.jsx(G,{models:n,loading:u.isPending}),t.jsx(Q,{loading:y.isPending,records:$})]})}function E({title:e,value:a,suffix:s}){return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:e}),t.jsxs("p",{className:"mt-2 text-2xl font-semibold",children:[a.toLocaleString(),s?t.jsx("span",{className:"ml-1 text-base font-normal text-slate-500 dark:text-slate-400",children:s}):null]})]})}function L({title:e,description:a,option:s,loading:d,empty:l,emptyText:b}){const{t:p}=A();return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsxs("div",{className:"mb-4",children:[t.jsx("p",{className:"text-sm font-semibold",children:e}),t.jsx("p",{className:"text-xs text-slate-500 dark:text-slate-400",children:a})]}),d?t.jsx("div",{className:"flex h-60 items-center justify-center text-sm text-slate-400",children:p("common.loadingShort")}):l?t.jsx("div",{className:"flex h-60 items-center justify-center text-sm text-slate-400",children:b??p("dashboard.charts.empty")}):t.jsx(O,{option:s,style:{height:260},notMerge:!0,lazyUpdate:!0,theme:void 0})]})}function G({models:e,loading:a}){const{t:s}=A(),d=e.length>0;return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsx("div",{className:"flex items-center justify-between border-b border-slate-200 px-4 py-3 dark:border-slate-800",children:t.jsxs("div",{children:[t.jsx("p",{className:"text-sm font-semibold",children:s("dashboard.modelTable.title")}),t.jsx("p",{className:"text-xs text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.description")})]})}),a?t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("common.loadingShort")}):d?t.jsx("div",{className:"max-h-80 overflow-x-auto",children:t.jsxs("table",{className:"min-w-full divide-y divide-slate-200 text-sm dark:divide-slate-800",children:[t.jsx("caption",{className:"sr-only",children:s("dashboard.modelTable.title")}),t.jsx("thead",{className:"bg-slate-100 dark:bg-slate-800/50",children:t.jsxs("tr",{children:[t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.model")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.requests")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.latency")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.ttft")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.tpot")})]})}),t.jsx("tbody",{className:"divide-y divide-slate-200 dark:divide-slate-800",children:e.map(l=>t.jsxs("tr",{className:"hover:bg-slate-50 dark:hover:bg-slate-800/60",children:[t.jsx("td",{className:"px-4 py-2",children:t.jsxs("div",{className:"flex flex-col",children:[t.jsx("span",{className:"font-medium text-slate-700 dark:text-slate-100",children:l.provider}),t.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:l.model})]})}),t.jsx("td",{className:"px-4 py-2 text-right",children:l.requests.toLocaleString()}),t.jsx("td",{className:"px-4 py-2 text-right",children:N(l.avgLatencyMs,s("common.units.ms"))}),t.jsx("td",{className:"px-4 py-2 text-right",children:N(l.avgTtftMs,s("common.units.ms"))}),t.jsx("td",{className:"px-4 py-2 text-right",children:N(l.avgTpotMs,s("common.units.msPerToken"),{maximumFractionDigits:2})})]},`${l.provider}/${l.model}`))})]})}):t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("dashboard.modelTable.empty")})]})}function Q({records:e,loading:a}){const{t:s}=A();return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsxs("div",{className:"flex items-center justify-between border-b border-slate-200 px-4 py-3 dark:border-slate-800",children:[t.jsx("p",{className:"text-sm font-semibold",children:s("dashboard.recent.title")}),t.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:s("dashboard.recent.subtitle",{count:5})})]}),a?t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("dashboard.recent.loading")}):e.length===0?t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("dashboard.recent.empty")}):t.jsx("div",{className:"max-h-80 overflow-auto",children:t.jsxs("table",{className:"min-w-full divide-y divide-slate-200 text-sm dark:divide-slate-800",children:[t.jsx("caption",{className:"sr-only",children:s("dashboard.recent.title")}),t.jsx("thead",{className:"bg-slate-100 dark:bg-slate-800/50",children:t.jsxs("tr",{children:[t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.time")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.endpoint")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.provider")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.route")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.latency")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.status")})]})}),t.jsx("tbody",{className:"divide-y divide-slate-200 dark:divide-slate-800",children:e.map(d=>t.jsxs("tr",{className:"hover:bg-slate-50 dark:hover:bg-slate-800/60",children:[t.jsx("td",{className:"px-4 py-2 text-xs text-slate-500 dark:text-slate-400",children:new Date(d.timestamp).toLocaleString()}),t.jsx("td",{className:"px-4 py-2 text-xs text-slate-500 dark:text-slate-400",children:d.endpoint==="anthropic"?s("logs.table.endpointAnthropic"):d.endpoint==="openai"?s("logs.table.endpointOpenAI"):d.endpoint}),t.jsx("td",{className:"px-4 py-2",children:d.provider}),t.jsxs("td",{className:"px-4 py-2",children:[t.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:d.client_model??s("dashboard.recent.routePlaceholder")}),t.jsx("span",{className:"mx-1 text-slate-400",children:"→"}),t.jsx("span",{className:"font-medium text-slate-700 dark:text-slate-100",children:d.model})]}),t.jsx("td",{className:"px-4 py-2",children:N(d.latency_ms,s("common.units.ms"))}),t.jsx("td",{className:"px-4 py-2",children:t.jsxs("span",{className:`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${d.error?"bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-200":"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-200"}`,children:[(d.status_code??200).toString(),t.jsx("span",{className:"ml-1",children:d.error?s("common.status.error"):s("common.status.success")})]})})]},d.id))})]})})]})}export{_ as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{u as c,r as d,j as e}from"./index-
|
|
1
|
+
import{u as c,r as d,j as e}from"./index-fNmJF3BX.js";function o(){const{t:s}=c(),a=d.useMemo(()=>{const t=s("help.sections.configuration.items",{returnObjects:!0}),l=s("help.sections.usage.items",{returnObjects:!0}),i=s("help.sections.tips.items",{returnObjects:!0});return[{title:s("help.sections.configuration.title"),items:t},{title:s("help.sections.usage.title"),items:l},{title:s("help.sections.tips.title"),items:i}]},[s]),r=s("help.faq.items",{returnObjects:!0});return e.jsxs("div",{className:"mx-auto flex max-w-4xl flex-col gap-8",children:[e.jsxs("header",{className:"space-y-3",children:[e.jsx("h1",{className:"text-2xl font-semibold",children:s("help.title")}),e.jsx("p",{className:"text-sm text-slate-600 dark:text-slate-300",children:s("help.intro")}),e.jsx("div",{className:"rounded-md border border-blue-100 bg-blue-50 px-4 py-3 text-xs text-blue-700 dark:border-blue-900/40 dark:bg-blue-950/60 dark:text-blue-200",children:s("help.note")})]}),a.map(t=>e.jsxs("section",{className:"space-y-3 rounded-lg border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[e.jsx("h2",{className:"text-lg font-semibold",children:t.title}),e.jsx("ol",{className:"list-decimal space-y-2 pl-6 text-sm text-slate-700 dark:text-slate-300",children:t.items.map(l=>e.jsx("li",{children:l},l))})]},t.title)),e.jsxs("section",{className:"space-y-3 rounded-lg border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[e.jsx("h2",{className:"text-lg font-semibold",children:s("help.faq.title")}),e.jsx("dl",{className:"space-y-4 text-sm text-slate-700 dark:text-slate-300",children:r.map(t=>e.jsxs("div",{className:"space-y-1",children:[e.jsx("dt",{className:"font-medium text-slate-900 dark:text-slate-100",children:t.q}),e.jsx("dd",{children:t.a})]},t.q))})]})]})}export{o as default};
|