Files
lingma-openai-gateway/README.md
GitHub Actions 4e08d1af36 feat: session bundle import/export to skip Playwright auto-login
Adds a lightweight way to pre-seed a Lingma workDir with an existing
logged-in session:

- New module session_bundle.py packs/unpacks only the four cache files
  that make up a Lingma login (id, user, quota, config.json). Everything
  else (db, logs, index, diagnosis) stays local so bundles stay tiny
  and never leak session-specific artefacts.
- Safety: path-traversal/symlink members are rejected; size is capped;
  refuses to export from a workDir that isn't actually logged in;
  sensitive cache/user is chmod'd 0600 on restore.
- LingmaAccount gains optional session_bundle_b64 / session_bundle_file;
  LINGMA_SESSION_BUNDLE[_FILE] env provide the singleton fallback.
  Credentials become optional when a bundle is supplied.
- LingmaPool.start() restores the bundle into each instance workDir
  only if it isn't already logged in, so persistent volumes aren't
  clobbered and a corrupt bundle falls back to Playwright gracefully.
- POST /internal/session/export returns the bundle as base64; ?instance=
  selects a specific pool instance. Requires an authed, already-logged-in
  instance to prevent exporting empties.
- README + .env.example document the end-to-end flow.

Made-with: Cursor
2026-04-18 09:39:58 +08:00

280 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Lingma OpenAI Gateway
把本地 Lingma 能力封装为 OpenAI 兼容接口,支持:
- `GET /v1/models`
- `POST /v1/chat/completions`
- `stream=true`SSE
- Bearer API Key 鉴权
## 1. 准备目录
```bash
mkdir -p data
```
说明:
- 启动时会自动获取最新插件并提取 `Lingma``data/bin`
- 默认通过 VSCode Marketplace 查询最新版本,再下载对应 VSIX。
- 登录态与运行数据统一写入 `data/.lingma`
## 2. 配置环境变量
```bash
cp .env.example .env
```
至少修改:
- `API_KEYS`
- `LINGMA_USERNAME`
- `LINGMA_PASSWORD`
如果你的 Lingma 路径不同,修改:
- `LINGMA_BIN`
可选(企业专属域):
- `DEDICATED_DOMAIN_URL`
### `.env` 字段说明(简版)
- `HOST`:网关监听地址(通常 `0.0.0.0`
- `PORT`:网关监听端口(外部调用端口)
- `API_KEYS`Bearer Key多个用逗号分隔
- `LINGMA_BIN`:容器内 Lingma 路径
- `LINGMA_SOURCE_TYPE`:二进制来源(`marketplace`/`vsix`
- `LINGMA_MARKETPLACE_PUBLISHER`Marketplace 发布者
- `LINGMA_MARKETPLACE_EXTENSION`Marketplace 扩展名
- `LINGMA_VSIX_URL`VSIX 下载地址(最新优先)
- `LINGMA_BOOTSTRAP_ALWAYS`:启动时是否总尝试刷新 Lingma
- `LINGMA_FORCE_REFRESH`:是否强制刷新(忽略本地缓存)
- `LINGMA_WORK_DIR`Lingma 工作目录(登录与会话数据)
- `LINGMA_SOCKET_PORT`Lingma 本地 WS 端口
- `LINGMA_STARTUP_TIMEOUT`Lingma 启动等待秒数
- `LINGMA_RPC_TIMEOUT`:单次 RPC 超时秒数
- `DEFAULT_MODEL`:默认模型(无法映射时兜底)
- `DEFAULT_ASK_MODE`:默认模式(`chat`/`agent`
- `DEDICATED_DOMAIN_URL`:企业专属域(可留空)
- `AUTO_LOGIN_ENABLED`:未登录时自动登录开关
- `AUTO_LOGIN_HEADLESS`:自动登录是否无头浏览器
- `AUTO_LOGIN_TIMEOUT`:自动登录超时秒数
- `AUTO_LOGIN_MAX_RETRY`:自动登录重试次数
- `LINGMA_USERNAME`Lingma 登录用户名
- `LINGMA_PASSWORD`Lingma 登录密码
- `METRICS_TOKEN``/metrics` 独立鉴权 token留空则 `API_KEYS` 也可访问;两者都留空时 `/metrics` 为公开)
- `LOG_LEVEL`:日志级别(默认 `INFO`,输出结构化 JSON包含 `request_id`
- `GATEWAY_MAX_IN_FLIGHT``/v1/chat/completions` 并发上限(默认 4`<=0` 表示不限流)
- `GATEWAY_QUEUE_TIMEOUT_SEC`:排队等待超时秒数(默认 30超过后直接 429 + `Retry-After`
- `LINGMA_ACCOUNTS`:多账号实例池,格式 `u1:p1,u2:p2` 或 JSON 数组;配置后每个账号起一个独立 Lingma 子进程
- `LINGMA_INSTANCE_COUNT`:实例数(默认等于账号数;显式指定且不足时账号会循环复用)
- `SESSION_REUSE_ENABLED`:多轮对话复用上游 sessionId默认 `true`)。命中时只把最新一条 user 消息发给 Lingma命中上游 KV cache显著降低第 2 轮及以后的首 token 延迟
- `SESSION_CACHE_MAX_ENTRIES`会话缓存容量LRU默认 256
- `SESSION_CACHE_TTL_SEC`:会话缓存 TTL 秒数(默认 1800超时自动失效避免复用到已被 Lingma 回收的 session
- `LINGMA_SESSION_BUNDLE` / `LINGMA_SESSION_BUNDLE_FILE`:已登录态注入,跳过 Playwright。具体见下方"跳过自动登录"章节
### `.env` 最小必填示例
```env
PORT=8317
API_KEYS=sk-your-api-key
LINGMA_USERNAME=your-username
LINGMA_PASSWORD=your-password
LINGMA_BIN=/app/data/bin/Lingma
LINGMA_WORK_DIR=/app/data/.lingma/vscode/sharedClientCache
LINGMA_SOURCE_TYPE=marketplace
LINGMA_MARKETPLACE_PUBLISHER=Alibaba-Cloud
LINGMA_MARKETPLACE_EXTENSION=tongyi-lingma
LINGMA_VSIX_URL=https://tongyi-code.oss-cn-hangzhou.aliyuncs.com/vscode/tongyi-lingma-latest.vsix
DEDICATED_DOMAIN_URL=
```
### 数据目录说明
- 本项目所有持久化数据都在 `./data`
- `data/bin/Lingma`:自动提取的 Lingma 二进制
- `data/.lingma/...`Lingma 登录态、缓存、日志(单实例模式)
- `data/.lingma/pool/inst-<i>/...`:多实例模式下每个实例独立的登录态/缓存
### 多实例池(方案乙:多账号)
启用方式:在 `.env` 里配置 `LINGMA_ACCOUNTS=u1:p1,u2:p2`,重启容器即可。
- 每个账号对应一个独立 Lingma 子进程,各自独立登录、独立 workDir。
- 路由策略:同一 `user` 字段或同一 system prompt 的请求粘性路由到同一实例;其余按 least-in-flight 分配。
- 一个实例挂了/断连不影响整体,`/healthz` 汇报 `pool_ready` 计数。
- `/internal/stats.pool` 按实例粒度暴露状态,`/metrics` 增加 `gateway_pool_instance_in_flight{name}` / `gateway_pool_instance_ready{name}`
- 未配置 `LINGMA_ACCOUNTS` 时自动退化为单实例模式(沿用 `LINGMA_USERNAME/LINGMA_PASSWORD`),向下兼容。
### 跳过自动登录session bundle 注入)
Playwright 登录偶尔会因为反爬虫策略失败。如果你已经有一个登录好的 Lingma
workDir本项目任意历史部署、或 VSCode 插件的 shared cache可以直接把那
份登录态导出成 "bundle" 注入到新的部署,完全跳过浏览器。
**导出**(在一个已登录的 gateway 上执行):
```bash
curl -sS -X POST \
-H "Authorization: Bearer $API_KEY" \
"http://host:port/internal/session/export" \
| jq -r '.bundle_b64' > lingma-session.b64
```
可选 `?instance=inst-0` 指定从哪个实例导出(多实例场景)。
**注入**(新部署的 `.env` 三选一):
```env
# 方式 1直接塞 base64
LINGMA_SESSION_BUNDLE=H4sIAAAA...
# 方式 2从文件读推荐避免 env 过长)
LINGMA_SESSION_BUNDLE_FILE=/secrets/lingma-session.b64
# 方式 3多账号模式每个账号独立 bundleJSON 模式)
LINGMA_ACCOUNTS=[
{"username":"u1","password":"p1","session_bundle_file":"/secrets/u1.b64"},
{"username":"u2","password":"p2","session_bundle":"H4sIAAAA..."}
]
```
**行为**
- bundle 只在目标 workDir **没有** 已有登录态(即 `cache/user` 为空或不存在)时才注入,不会覆盖活跃的登录。
- 注入成功后 Lingma 启动时直接 ready`auth_status()` 命中已登录,跳过
Playwright。
- 注入失败bundle 损坏、权限不足等)自动 fallback 到原有 Playwright 流程。
- bundle 只打包 `cache/{id,user,quota,config.json}` 四个文件,不包含
db/logs/index体积通常 < 10 KB。
- bundle 含敏感 token**按密钥保管**,不要写入公共仓库。
## 3. Docker 运行
```bash
docker compose up -d --build
```
说明:
- 构建阶段已默认使用腾讯云 PyPI 镜像(`mirrors.cloud.tencent.com`)。
- 如需自定义镜像源,可在 `docker-compose.yml``build.args` 中修改。
查看日志:
```bash
docker compose logs -f
```
## 4. 调用示例
### 模型列表
```bash
curl -s http://127.0.0.1:8317/v1/models \
-H "Authorization: Bearer sk-your-api-key"
```
说明:
- `id` 保持 Lingma 原始模型 key兼容 OpenAI 客户端)
- `name` 提供可读名称(如 `qwen3.6-plus`
- 调用 `/v1/chat/completions` 时,`model` 既可传 `id`,也可直接传 `name`
### 非流式聊天
```bash
curl -s http://127.0.0.1:8317/v1/chat/completions \
-H "Authorization: Bearer sk-your-api-key" \
-H "Content-Type: application/json" \
-d '{
"model": "dashscope_qmodel",
"messages": [
{"role": "user", "content": "写一个 python hello world"}
]
}'
```
### 流式聊天
```bash
curl -N http://127.0.0.1:8317/v1/chat/completions \
-H "Authorization: Bearer sk-your-api-key" \
-H "Content-Type: application/json" \
-d '{
"model": "dashscope_qmodel",
"stream": true,
"messages": [
{"role": "user", "content": "介绍一下你自己"}
]
}'
```
## 5. 统计与监控
支持调用次数与 token估算值统计
- `GET /internal/stats`(需 Bearer
- `GET /metrics`Prometheus 文本格式)
示例:
```bash
curl -s http://127.0.0.1:8317/internal/stats \
-H "Authorization: Bearer sk-xxx"
```
```bash
curl -s http://127.0.0.1:8317/metrics \
-H "Authorization: Bearer ${METRICS_TOKEN:-sk-your-api-key}"
```
说明:
- `usage.prompt_tokens/completion_tokens` 为估算值(按字节近似换算)。
- 非流式响应里会附带 `usage` 字段。
- 流式响应可传 `stream_options: {"include_usage": true}` 让最后一帧返回 `usage`
- `/metrics` 默认需要 Bearer 鉴权:优先匹配 `METRICS_TOKEN`,否则接受 `API_KEYS` 里任意一个;两者都未配置时保持公开。
## 6. 容器内自动登录
已内置自动登录能力Playwright + Chromium
你可以主动触发:
```bash
curl -s -X POST http://127.0.0.1:8317/internal/auto-login/start \
-H "Authorization: Bearer sk-your-api-key"
```
查看状态:
```bash
curl -s http://127.0.0.1:8317/internal/auto-login/status \
-H "Authorization: Bearer sk-your-api-key"
```
说明:
- 若未登录,`/v1/models``/v1/chat/completions` 也会尝试自动登录。
- 账号密码来自 `.env``LINGMA_USERNAME` / `LINGMA_PASSWORD`)。
- 建议仅在受控环境使用,并妥善保护 `.env`
## 7. agent 模式
在 v1 中,若 `model``agent``lingma-agent`,会走 agent 模式。
```bash
curl -s http://127.0.0.1:8317/v1/chat/completions \
-H "Authorization: Bearer sk-xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "agent",
"messages": [
{"role": "user", "content": "分析这个项目目录结构"}
]
}'
```