dsh-router-core 0.1.0 → 0.3.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 (42) hide show
  1. package/README.md +58 -13
  2. package/lib/client-registry.js +824 -153
  3. package/lib/client.js +824 -153
  4. package/lib/index.js +1606 -227
  5. package/lib/types/llm/adapter.d.ts +2 -0
  6. package/lib/types/router/account-pool.d.ts +41 -0
  7. package/lib/types/router/index.d.ts +72 -4
  8. package/lib/types/router/types.d.ts +12 -10
  9. package/lib/types/router/usage-store.d.ts +93 -0
  10. package/lib/types/router/usage-tokens.d.ts +118 -0
  11. package/lib/types/shared.d.ts +69 -0
  12. package/lib/types/supplier-config.d.ts +15 -4
  13. package/lib/types/suppliers/contract.d.ts +70 -21
  14. package/lib/types/suppliers/registry.d.ts +4 -3
  15. package/package.json +2 -1
  16. package/src/client/CombosTab.tsx +28 -16
  17. package/src/client/RouterView.tsx +15 -2
  18. package/src/client/StatsTab.tsx +521 -0
  19. package/src/client/SupplierDetail.tsx +58 -34
  20. package/src/client/router.css +406 -0
  21. package/src/client/workspace-mount.tsx +2 -9
  22. package/src/index.ts +52 -4
  23. package/src/llm/adapter.test.ts +108 -0
  24. package/src/llm/adapter.ts +18 -6
  25. package/src/router/account-pool.test.ts +120 -0
  26. package/src/router/account-pool.ts +161 -0
  27. package/src/router/index.ts +652 -26
  28. package/src/router/models-cache.test.ts +455 -0
  29. package/src/router/precise-dispatch.test.ts +198 -0
  30. package/src/router/types.ts +12 -7
  31. package/src/router/usage-store.ts +499 -0
  32. package/src/router/usage-tokens.ts +327 -0
  33. package/src/router/usage.test.ts +558 -0
  34. package/src/shared.ts +77 -0
  35. package/src/supplier-config.ts +31 -14
  36. package/src/suppliers/contract.ts +71 -8
  37. package/src/suppliers/loader.ts +21 -13
  38. package/src/suppliers/nvidia/plugin.ts +161 -119
  39. package/src/suppliers/opencode/plugin.ts +47 -95
  40. package/src/suppliers/openrouter/plugin.ts +67 -122
  41. package/src/suppliers/registry.test.ts +194 -0
  42. package/src/suppliers/registry.ts +79 -59
package/README.md CHANGED
@@ -17,6 +17,10 @@ OpenAI 兼容端点,把请求路由到内部供应商。装好即用,不用多
17
17
  无需改核心代码、无需重编译;
18
18
  - **模型不内置**:供应商只实现差异化能力(列模型/调上游/登录),模型拉取与
19
19
  缓存由核心统一管,不写死、不过时;
20
+ - **策略只写一次**:组合回退、账号池(选号/冷却/禁用/连续错误累计)、响应写入、
21
+ 凭证存储、模型管理都由核心提供。供应商 js 只对**单个账号**调一次上游并报告
22
+ 成败,不自己遍历账号、不维护冷却表——否则每个插件都会长出一份互相不一致的
23
+ 实现,而核心也就无从判断「该不该换号」;
20
24
  - **凭证 SQLite 单库**:`auths/credentials.sqlite`,供应商凭证不透明 blob,
21
25
  核心统一生命周期,干净可备份;
22
26
  - **复用 9router 思路**:面板布局、组合 fallback、连接池/账号池、API key
@@ -28,13 +32,37 @@ OpenAI 兼容端点,把请求路由到内部供应商。装好即用,不用多
28
32
  左侧边栏「记忆系统」上方有 **路由系统** 入口,点击打开中心栏面板:
29
33
 
30
34
  - **返回会话** — 左侧按钮,关闭面板回到聊天;
35
+ - **概览** — 用量看板(默认页):
36
+ - 周期切换 **今日 / 24 小时 / 7 天 / 30 天**;
37
+ - 汇总卡:总请求(含成功率)、输入 Tokens、输出 Tokens、缓存 Tokens、平均耗时(含首字节);
38
+ - Token 趋势折线图:鼠标悬停 / 触摸点选 / 键盘 `←` `→`(`Home` `End` 到两端,`Esc` 取消)
39
+ 看每个时段;读数和峰值用 K/M 缩写,精确值在悬停提示里;
40
+ - Top 榜:按供应商 / 按模型(请求数带失败计数);
41
+ - 最近请求:时间 / 模型 / 供应商 / in↑ out↓ / 耗时,显示最近 10 条;
42
+ - **清空** — 清掉全部用量统计(不影响供应商、账号、组合配置);
43
+ - 数据落盘 `data/usage.json`(按天聚合 + 最近 500 条明细 + 累计计数)。
44
+ token 口径:上游返回 `usage` 就用真值(分散在多帧时按字段取最大值合并);
45
+ 上游不发时按 ~4 字符/token 估算,面板上标 `~`。**失败请求不估算**——
46
+ 它没到上游,编造输入 token 只会把总量灌水;
47
+ 缓存口径:OpenAI 系 `prompt_tokens` **含**缓存,Claude 系不含(单报
48
+ `cache_read_input_tokens`),归一时统一折成「prompt 含缓存」,
49
+ 所以「缓存 Tokens」是「输入 Tokens」的**子集**,不是并列的第三种;
31
50
  - **供应商** — 供应商卡片(内置 / 插件分组),点击进入详情:
32
- - **链接池** — 账号列表(冷却/禁用/健康数),支持删除;
51
+ - **链接池** — 账号列表(冷却/禁用/健康数/积分),支持删除;
33
52
  - **加链接** — 按供应商能力弹出不同流程:URL 登录(生成链接 → 浏览器登录 → 回调)、
34
53
  API key 弹窗(填名字 + key)、轮询登录(登录后自动取凭证);
54
+ - **签到** — 供应商实现了签到的才显示(如 codebuddy:每日 100 积分,连续第 7 天
55
+ 1000)。核心遍历所有链接逐个签,汇总「N/M 成功 · X 今日已签」;上游「今日已
56
+ 签到」按成功处理(幂等),账号额度或凭证失效会单独标出;
57
+ - **刷新** — 刷所有链接的积分,并跑一次最简会话探测该供应商是否还有活着的链接
58
+ (走真实对话路径 + 账号池回退,能分清是账号额度没了还是供应商真挂了);
35
59
  - **可用模型** — 模型列表,逐个启用/禁用 + 自定义模型(通用能力,持久化到
36
- `data/supplier-config.json`,`/v1/models` 与 chat 只接受启用的模型);
37
- - **组合** — fallback 链(免费优先),可自定义;
60
+ `data/supplier-config.json`,`/v1/models` 与 chat 只接受启用的模型);单个模型可
61
+ 「测试」,走真实对话路径并按账号池依次回退,所以能分清是这个账号额度没了还是
62
+ 该模型真的不支持;
63
+ - **组合** — fallback 链(免费优先),可自定义。**组合即模型**:建好的组合会**自动带出**
64
+ 为 DSH 模型目录里的 `router` provider 选项(设置 → 模型直接选组合名即可用),请求
65
+ 按组合策略命中其中一个供应商模型;
38
66
  - **端点与密钥** — 端点核心(无隧道/Tailscale):
39
67
  - API 端点 URL(`http://localhost:3080/v1`,可复制);
40
68
  - 鉴权设置 `requireApiKey` 开关;
@@ -71,6 +99,9 @@ curl -X POST http://localhost:3080/v1/chat/completions \
71
99
  | `/keys/toggle` | POST | `{id, isActive}` |
72
100
  | `/keys/delete` | POST | `{id}` |
73
101
  | `/settings` | GET/PATCH | `{requireApiKey}` |
102
+ | `/stats` | GET | 用量统计 `?period=today\|24h\|7d\|30d`(汇总 + Top 榜 + 最近请求 20 条) |
103
+ | `/stats/chart` | GET | 趋势图数据 `?period=…`(today/24h = 24 小时桶,7d/30d = 天桶) |
104
+ | `/stats/clear` | POST | 清空全部用量统计 |
74
105
  | `/suppliers/:id/login` | POST | 生成登录链接 |
75
106
  | `/suppliers/:id/login/callback` | POST | `{callbackUrl}` → 加账号 |
76
107
  | `/suppliers/:id/models` | GET | 模型 + 启用状态 |
@@ -81,12 +112,15 @@ curl -X POST http://localhost:3080/v1/chat/completions \
81
112
  ```
82
113
  浏览器(client 半)
83
114
  └─ 侧边栏「路由系统」+ 中心栏面板(RouterView, 含返回会话按钮)
84
- ├─ RouterView tab: 供应商 / 组合 / 端点与密钥
115
+ ├─ StatsTab 概览:用量看板(周期切换 + 汇总卡 + 折线趋势 + Top 榜 + 最近请求)
116
+ ├─ RouterView tab: 概览 / 供应商 / 组合 / 端点与密钥
85
117
  ├─ SupplierDetail 供应商详情:链接池 + 加链接 + 可用模型
86
118
  ├─ EndpointTab 端点 URL + requireApiKey + 密钥管理
87
119
  └─ fetch /router/api/* (同源,无 CORS)
88
120
  └─ host 半(src/index.ts)
89
121
  ├─ /v1/models + /v1/chat/completions (OpenAI 兼容, KeysStore 鉴权)
122
+ │ └─ RouterAdapter(src/llm/adapter.ts) OpenAI SSE → DSH StreamChunk
123
+ │ (usage 经 toTokenUsage 转 DSH 契约,见 docs/suppliers.md)
90
124
  ├─ KeysStore(src/keys.ts) 密钥库 + requireApiKey
91
125
  └─ Router(路由器) → suppliers[]
92
126
  ├─ OpenCodeSupplier(lib/suppliers/opencode.js) 无账号免费直连
@@ -95,9 +129,10 @@ curl -X POST http://localhost:3080/v1/chat/completions \
95
129
  └─ 外部插件供应商(经 router.suppliers service 注册)
96
130
  ```
97
131
 
98
- - **供应商抽象**:可插拔 js 模块只提供**差异化能力**(`status/listModels/getAlias/chatCompletions`
99
- + 可选登录/签到/测模型);**通用能力**(连接池排序/策略、模型启用/自定义、别名、凭证)
100
- 由核心统一管。
132
+ - **供应商抽象**:可插拔 js 模块只提供**差异化能力**(`status/listModels/getAlias/chatOnce`
133
+ + 可选登录/签到/加 key);**策略与通用能力**(组合回退、账号池选号/冷却/禁用、
134
+ 连接池排序、模型启用/自定义、别名、凭证、响应写入)由核心统一管。
135
+ `chatOnce(uid, req)` 一次只服务一个账号,返回成功/失败 + 语义状态,换号由核心决定。
101
136
  - **供应商加载**(三来源,见 [`docs/suppliers.md`](docs/suppliers.md)):
102
137
  1. 内置:`lib/suppliers/*.js`(随插件分发,如 opencode)
103
138
  2. 用户:`~/.dsh/profiles/web/suppliers/*.js`
@@ -127,14 +162,24 @@ pnpm build # lib/index.js(host) + lib/client.js / lib/client-registry.js(
127
162
  pnpm typecheck
128
163
  ```
129
164
 
130
- ## 挂载
165
+ ## 安装(DSH)
131
166
 
132
- 1. `~/.dsh/profiles/web/package.json` 的 `dsh.profile.bundles` 加 `"dsh-router"`,
133
- `dependencies` 加 `"dsh-router-core": "link:/Users/carvin/Desktop/dsh-plugins/dsh-router"`;
134
- 2. `cd ~/.dsh/profiles/web && pnpm install`;
135
- 3. 重启 `dsh web`。
167
+ 用 dsh CLI 装到 profile(web 是 DSH 插件宿主):
136
168
 
137
- > 发布版:`npm i dsh-router-core`(见 [npm](https://www.npmjs.com/package/dsh-router-core))。
169
+ ```bash
170
+ dsh plugin --profile web add dsh-router-core
171
+ ```
172
+
173
+ 该命令会在 profile 里 `pnpm add`,并自动把 `dsh-router-core` 加入
174
+ `dsh.profile.bundles`(包声明了 `dsh.bundle.patch`,即 `cordis.patch.yml`)。
175
+
176
+ 然后**重启 `dsh web`**。侧边栏出现「路由系统」入口,打开即面板。
177
+
178
+ > 更多供应商:DSH 插件形态的供应商各自发 npm 包,同样 `dsh plugin --profile web add <包名>`
179
+ > 即可;供应商接入与开发见 [`docs/suppliers.md`](docs/suppliers.md)。
180
+ >
181
+ > 本地开发版:不用 npm,直接 `dependencies` 加
182
+ > `"dsh-router-core": "link:/path/to/dsh-router"` 指向本地仓库。
138
183
 
139
184
  ## 前提
140
185