@lakphy/local-router 0.0.1

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 ADDED
@@ -0,0 +1,178 @@
1
+ # local-router
2
+
3
+ `local-router` 是一个面向本地使用的 AI 请求网关。
4
+ 你把请求发给它,它会按你的配置自动转发到 OpenAI/Anthropic 兼容上游,并统一提供日志和管理界面。
5
+
6
+ ## 这是什么
7
+
8
+ 适合这几类场景:
9
+
10
+ - 你希望统一一个本地入口来切换不同模型/供应商
11
+ - 你不想在每个客户端里直接暴露上游 API Key
12
+ - 你需要查看请求日志、会话轨迹和基础统计
13
+
14
+ 支持的协议入口:
15
+
16
+ - `openai-completions`
17
+ - `openai-responses`
18
+ - `anthropic-messages`
19
+
20
+ ## 5 分钟上手
21
+
22
+ ### 1) 安装依赖
23
+
24
+ ```sh
25
+ bun install
26
+ ```
27
+
28
+ ### 2) 初始化配置
29
+
30
+ ```sh
31
+ local-router init
32
+ ```
33
+
34
+ 默认会创建配置文件(优先当前目录 `config.json5`,否则 `~/.local-router/config.json5`)。
35
+
36
+ ### 3) 启动服务
37
+
38
+ ```sh
39
+ local-router start
40
+ ```
41
+
42
+ 默认地址:
43
+
44
+ - 服务:`http://127.0.0.1:4099`
45
+ - 管理面板:`http://127.0.0.1:4099/admin`
46
+ - API 文档:`http://127.0.0.1:4099/api/docs`
47
+
48
+ ## 配置示例(最小可用)
49
+
50
+ ```json5
51
+ {
52
+ providers: {
53
+ openai: {
54
+ type: "openai-completions",
55
+ base: "https://api.openai.com/v1",
56
+ apiKey: "sk-xxxx",
57
+ models: {
58
+ "gpt-4o-mini": {}
59
+ }
60
+ }
61
+ },
62
+ routes: {
63
+ "openai-completions": {
64
+ "*": { provider: "openai", model: "gpt-4o-mini" }
65
+ }
66
+ }
67
+ }
68
+ ```
69
+
70
+ 配置要点:
71
+
72
+ - `providers` 定义上游地址、类型和密钥
73
+ - `routes` 定义“你传入的 model”映射到“实际上游 model”
74
+ - 每个入口必须有 `*` 兜底规则
75
+ - `log` 是可选配置,不写就不记录日志
76
+
77
+ 完整 schema 见 `config.schema.json`。
78
+
79
+ ## 如何调用
80
+
81
+ 你只需要把客户端请求地址改到 local-router。
82
+
83
+ ### OpenAI Chat Completions
84
+
85
+ ```sh
86
+ curl -X POST "http://127.0.0.1:4099/openai-completions/v1/chat/completions" \
87
+ -H "Content-Type: application/json" \
88
+ -d '{
89
+ "model": "gpt-4o-mini",
90
+ "messages": [{"role":"user","content":"请回复 ok"}]
91
+ }'
92
+ ```
93
+
94
+ ### OpenAI Responses
95
+
96
+ ```sh
97
+ curl -X POST "http://127.0.0.1:4099/openai-responses/v1/responses" \
98
+ -H "Content-Type: application/json" \
99
+ -d '{
100
+ "model": "gpt-4o-mini",
101
+ "input": "请回复 ok"
102
+ }'
103
+ ```
104
+
105
+ ### Anthropic Messages
106
+
107
+ ```sh
108
+ curl -X POST "http://127.0.0.1:4099/anthropic-messages/v1/messages" \
109
+ -H "Content-Type: application/json" \
110
+ -d '{
111
+ "model": "sonnet",
112
+ "max_tokens": 64,
113
+ "messages": [{"role":"user","content":"请回复 ok"}]
114
+ }'
115
+ ```
116
+
117
+ ## 常用 CLI 命令
118
+
119
+ ```sh
120
+ local-router init
121
+ local-router start
122
+ local-router start --daemon
123
+ local-router status --json
124
+ local-router logs --follow
125
+ local-router stop
126
+ local-router restart --daemon
127
+ local-router health
128
+ local-router version
129
+ ```
130
+
131
+ ## 日志与管理面板
132
+
133
+ - 管理面板:`/admin`
134
+ - 健康检查:`GET /api/health`
135
+ - 日志查询:`GET /api/logs/events`
136
+ - 日志导出:`GET /api/logs/export?format=json|csv`
137
+ - 实时 tail:`GET /api/logs/tail`(SSE)
138
+
139
+ 默认日志目录:`~/.local-router/logs`
140
+
141
+ - 事件日志:`events/YYYY-MM-DD.jsonl`
142
+ - 流式原文:`streams/YYYY-MM-DD/<request_id>.sse.raw`
143
+
144
+ ## 常见问题
145
+
146
+ ### Q1: 客户端还需要带上游 API Key 吗?
147
+
148
+ 一般不需要。local-router 会按你在 `providers.*.apiKey` 配置的密钥进行转发鉴权。
149
+
150
+ ### Q2: 为什么启动失败?
151
+
152
+ 优先检查:
153
+
154
+ - 端口 `4099` 是否被占用(可改 `--port`)
155
+ - 配置中是否缺少 `routes.<type>."*"` 兜底
156
+ - `routes` 引用的 provider 是否在 `providers` 中存在
157
+
158
+ ### Q3: 管理面板打不开?
159
+
160
+ 如果是生产/本地打包后运行,先执行:
161
+
162
+ ```sh
163
+ bun run build
164
+ ```
165
+
166
+ 开发态可通过 `bun run dev` 启动(包含 Web 开发服务器)。
167
+
168
+ ## 运行要求
169
+
170
+ - Bun `>=1.2.0`
171
+
172
+ ## 进阶文档
173
+
174
+ - `docs/cli-development-and-release.md`
175
+ - `docs/logging-architecture.md`
176
+ - `docs/react-csr-integration.md`
177
+ - `docs/application-performance-analysis.md`
178
+ - `docs/application-performance-high-risk-remediation.md`
@@ -0,0 +1,249 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "./config.schema.json",
4
+ "title": "Local Router Config",
5
+ "description": "local-router 的主配置文件。用于把不同协议入口的请求路由到具体 provider,并定义 provider 的模型能力与鉴权信息。",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["routes", "providers"],
9
+ "properties": {
10
+ "$schema": {
11
+ "type": "string",
12
+ "description": "可选。指向本文件的 schema 路径,便于编辑器提供自动补全与静态校验。",
13
+ "examples": ["./config.schema.json", "https://example.com/config.schema.json"]
14
+ },
15
+ "routes": {
16
+ "description": "路由映射:按入口协议分组,再按模型别名(或 * 通配)映射到目标 provider 与目标模型。",
17
+ "markdownDescription": "路由映射:按入口协议分组,再按模型别名(或 `*` 通配)映射到目标 provider 与目标模型。\n\n- 第一层 key:协议入口(例如 `openai-completions`、`openai-responses`、`anthropic-messages`)\n- 第二层 key:模型匹配规则(如 `opus`、`sonnet`、`*`)\n- value:`{ provider, model }`,表示最终转发到哪个 provider、使用哪个上游模型",
18
+ "type": "object",
19
+ "minProperties": 1,
20
+ "propertyNames": {
21
+ "type": "string",
22
+ "pattern": "^[a-z0-9][a-z0-9-]*$",
23
+ "description": "协议入口名称,建议使用 kebab-case。"
24
+ },
25
+ "additionalProperties": {
26
+ "$ref": "#/$defs/routeModelMap"
27
+ },
28
+ "examples": [
29
+ {
30
+ "openai-completions": {
31
+ "*": {
32
+ "provider": "dashscope-openai-completions",
33
+ "model": "qwen3.5-plus"
34
+ }
35
+ },
36
+ "anthropic-messages": {
37
+ "opus": {
38
+ "provider": "dashscope-anthropic-messages",
39
+ "model": "qwen3.5-plus"
40
+ },
41
+ "*": {
42
+ "provider": "dashscope-anthropic-messages",
43
+ "model": "qwen3.5-plus"
44
+ }
45
+ }
46
+ }
47
+ ]
48
+ },
49
+ "providers": {
50
+ "description": "provider 定义集合。key 为 provider 名称,value 为 provider 的协议类型、基础地址、鉴权与模型能力。",
51
+ "markdownDescription": "provider 定义集合。`routes.*.*.provider` 应引用这里的某个 key。\n\n每个 provider 包含:\n- `type`:协议类型(决定路由层如何拼接上游地址)\n- `base`:上游 API 基础地址(不含末尾具体路径)\n- `apiKey`:访问上游的密钥\n- `models`:上游模型能力开关(如是否支持推理、图像输入)",
52
+ "type": "object",
53
+ "minProperties": 1,
54
+ "propertyNames": {
55
+ "type": "string",
56
+ "pattern": "^[a-z0-9][a-z0-9-]*$",
57
+ "description": "provider 名称,建议使用 kebab-case,并在 routes 中通过该名称引用。"
58
+ },
59
+ "additionalProperties": {
60
+ "$ref": "#/$defs/providerConfig"
61
+ }
62
+ },
63
+ "log": {
64
+ "$ref": "#/$defs/logConfig",
65
+ "description": "日志配置。省略此字段或不配置时,日志系统不会启用。配置后默认启用,可通过 enabled: false 暂停。",
66
+ "markdownDescription": "日志配置。省略此字段时日志系统不启用。\n\n配置后,日志会写入配置文件同目录下的 `logs/` 子目录(如 `~/.local-router/logs/`),包括:\n- `events/YYYY-MM-DD.jsonl`:按天分片的结构化事件日志\n- `streams/YYYY-MM-DD/<request_id>.sse.raw`:流式响应原始 SSE 文本"
67
+ }
68
+ },
69
+ "$defs": {
70
+ "routeModelMap": {
71
+ "type": "object",
72
+ "description": "某个协议入口下的模型匹配表。key 是模型别名(或 *),value 是路由目标。",
73
+ "minProperties": 1,
74
+ "required": ["*"],
75
+ "propertyNames": {
76
+ "type": "string",
77
+ "minLength": 1,
78
+ "description": "模型匹配键。可使用 * 作为兜底通配规则。"
79
+ },
80
+ "additionalProperties": {
81
+ "$ref": "#/$defs/routeTarget"
82
+ },
83
+ "examples": [
84
+ {
85
+ "opus": {
86
+ "provider": "dashscope-anthropic-messages",
87
+ "model": "qwen3.5-plus"
88
+ },
89
+ "*": {
90
+ "provider": "dashscope-anthropic-messages",
91
+ "model": "qwen3.5-plus"
92
+ }
93
+ }
94
+ ]
95
+ },
96
+ "routeTarget": {
97
+ "type": "object",
98
+ "description": "路由命中后的转发目标。",
99
+ "additionalProperties": false,
100
+ "required": ["provider", "model"],
101
+ "properties": {
102
+ "provider": {
103
+ "type": "string",
104
+ "minLength": 1,
105
+ "description": "目标 provider 名称,必须与 providers 下某个 key 一致(跨字段引用关系由运行时或额外校验保证)。",
106
+ "examples": ["dashscope-openai-completions", "dashscope-anthropic-messages"]
107
+ },
108
+ "model": {
109
+ "type": "string",
110
+ "minLength": 1,
111
+ "description": "转发到上游时写入请求体的模型名。",
112
+ "examples": ["qwen3.5-plus", "qwen3-max", "qwen3.5-flash"]
113
+ }
114
+ }
115
+ },
116
+ "providerConfig": {
117
+ "type": "object",
118
+ "description": "单个 provider 的配置。",
119
+ "additionalProperties": false,
120
+ "required": ["type", "base", "apiKey", "models"],
121
+ "properties": {
122
+ "type": {
123
+ "type": "string",
124
+ "enum": ["openai-completions", "openai-responses", "anthropic-messages"],
125
+ "description": "provider 协议类型。应与其服务的入口协议/接口形态一致。"
126
+ },
127
+ "base": {
128
+ "type": "string",
129
+ "format": "uri",
130
+ "minLength": 1,
131
+ "description": "上游 API 的基础 URL。建议不带末尾斜杠,避免路径拼接出现重复斜杠。",
132
+ "examples": [
133
+ "https://dashscope.aliyuncs.com/compatible-mode",
134
+ "https://dashscope.aliyuncs.com/response-mode",
135
+ "https://dashscope.aliyuncs.com/anthropic"
136
+ ]
137
+ },
138
+ "apiKey": {
139
+ "type": "string",
140
+ "minLength": 1,
141
+ "description": "访问上游服务的密钥。建议通过环境变量注入后再生成配置文件,避免在仓库中明文保存。"
142
+ },
143
+ "models": {
144
+ "type": "object",
145
+ "minProperties": 1,
146
+ "description": "模型能力定义。key 为模型名,value 为该模型的能力标记(例如 reasoning、image-input)。",
147
+ "propertyNames": {
148
+ "type": "string",
149
+ "minLength": 1,
150
+ "description": "模型标识,例如 qwen3.5-plus。"
151
+ },
152
+ "additionalProperties": {
153
+ "$ref": "#/$defs/modelCapabilities"
154
+ }
155
+ }
156
+ }
157
+ },
158
+ "modelCapabilities": {
159
+ "type": "object",
160
+ "description": "模型能力开关。当前已知能力字段为 image-input 与 reasoning;未声明字段默认视为 false 或由上游默认行为决定。",
161
+ "additionalProperties": false,
162
+ "properties": {
163
+ "image-input": {
164
+ "type": "boolean",
165
+ "description": "是否支持图像输入能力。"
166
+ },
167
+ "reasoning": {
168
+ "type": "boolean",
169
+ "description": "是否支持推理/思维链类能力(按你的业务语义定义)。"
170
+ }
171
+ },
172
+ "examples": [
173
+ {
174
+ "image-input": false,
175
+ "reasoning": true
176
+ }
177
+ ]
178
+ },
179
+ "logConfig": {
180
+ "type": "object",
181
+ "description": "日志系统配置。控制请求/响应日志的记录方式、存储路径和隐私脱敏策略。",
182
+ "additionalProperties": false,
183
+ "properties": {
184
+ "enabled": {
185
+ "type": "boolean",
186
+ "default": true,
187
+ "description": "是否启用日志记录。设为 false 可在保留配置的同时暂停日志写入。默认为 true(配置了 log 段即启用)。"
188
+ },
189
+ "baseDir": {
190
+ "type": "string",
191
+ "description": "日志文件根目录的绝对路径。默认为 ~/.local-router/logs/。仅在需要自定义存储位置时指定。",
192
+ "examples": ["/home/user/.local-router/logs", "/var/log/local-router"]
193
+ },
194
+ "events": {
195
+ "$ref": "#/$defs/logEventsConfig",
196
+ "description": "事件日志(JSONL 格式)相关配置。"
197
+ },
198
+ "streams": {
199
+ "$ref": "#/$defs/logStreamsConfig",
200
+ "description": "流式响应原文(SSE raw)相关配置。"
201
+ },
202
+ "bodyPolicy": {
203
+ "type": "string",
204
+ "enum": ["off", "masked", "full"],
205
+ "default": "off",
206
+ "description": "请求体与响应体的记录策略。off:不记录 body 内容(推荐日常使用);masked:脱敏后记录(当前版本行为同 full,后续将完善字段级脱敏);full:完整记录所有内容(仅建议调试时临时开启,注意隐私风险)。",
207
+ "markdownDescription": "请求体与响应体的记录策略:\n- `off`:不记录 body 内容(推荐日常使用)\n- `masked`:脱敏后记录(当前版本行为同 `full`,后续完善)\n- `full`:完整记录所有内容(仅调试时使用)"
208
+ }
209
+ }
210
+ },
211
+ "logEventsConfig": {
212
+ "type": "object",
213
+ "description": "事件日志(JSONL 格式)相关配置。每个转发请求生成一条 JSON 事件,按天写入独立文件(如 events/2026-02-28.jsonl)。",
214
+ "additionalProperties": false,
215
+ "properties": {
216
+ "retainDays": {
217
+ "type": "integer",
218
+ "minimum": 1,
219
+ "default": 14,
220
+ "description": "事件日志文件保留天数。超过此天数的 .jsonl 文件将在清理时被删除。默认保留 14 天。"
221
+ }
222
+ }
223
+ },
224
+ "logStreamsConfig": {
225
+ "type": "object",
226
+ "description": "流式响应原文(SSE raw)相关配置。每个流式请求的完整 SSE 文本会保存为独立的 .sse.raw 文件(如 streams/2026-02-28/<request_id>.sse.raw)。",
227
+ "additionalProperties": false,
228
+ "properties": {
229
+ "enabled": {
230
+ "type": "boolean",
231
+ "default": true,
232
+ "description": "是否记录流式响应的原始 SSE 文本到独立文件。关闭后仅在事件日志中记录元数据,不保存原始流内容。默认开启。"
233
+ },
234
+ "retainDays": {
235
+ "type": "integer",
236
+ "minimum": 1,
237
+ "default": 7,
238
+ "description": "流式原文文件保留天数。超过此天数的 .sse.raw 文件将在清理时被删除。默认保留 7 天。"
239
+ },
240
+ "maxBytesPerRequest": {
241
+ "type": "integer",
242
+ "minimum": 1,
243
+ "default": 10485760,
244
+ "description": "单个流式请求最大记录字节数(默认约 10MB = 10485760 字节)。超出部分将被截断并在文件末尾追加 [TRUNCATED] 标记。设置合理上限可防止异常长响应撑爆磁盘。"
245
+ }
246
+ }
247
+ }
248
+ }
249
+ }