Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 53 additions & 9 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ SERVICE_PORT=3000
# retry regenerates from scratch and is the longest such window.
# ANTHROPIC_PING_INTERVAL_MS=15000

# Anthropic (/v1/messages) 与 OpenAI (/v1/chat/completions) 两条路径共用:一轮 Agent
# 回合里文本通道工具调用的上限(默认 24,钳在 4..256)。
# 模型在叙述的 [TOOL CALL] 之后失控(同一调用重复上百次、幻想整段 agent 会话)时,
# 第 N 个已放行的调用一到就终止上游,已放行的调用照常交付
# (stop_reason=tool_use / finish_reason=tool_calls)。更早的 delta 里已放行过调用之后,
# 再出现重复调用 / 被拒绝的调用 / 正文或思考同样立刻截断,不受此上限影响。
# Both agent paths (/v1/messages and /v1/chat/completions): cap on text-channel tool
# calls per agent turn (default 24, clamped to 4..256). When the model runs away after a
# narrated [TOOL CALL] (repeats the same call hundreds of times, hallucinates a whole
# agentic session), the upstream is cut right after the N-th admitted call and the
# admitted calls are still delivered (stop_reason=tool_use / finish_reason=tool_calls).
# Once a call was admitted in an earlier delta, a duplicate, a rejected call, or
# prose/thinking after it also cuts the turn, regardless of this cap.
# AGENT_TURN_MAX_TOOL_CALLS=24

# 监听地址(非必填)
# Listen address (optional)
Expand Down Expand Up @@ -169,15 +183,45 @@ QWEN_CLI_PROXY_URL=
# 示例 / Example: PROXY_URL=socks5://127.0.0.1:1080
PROXY_URL=

# ========== Claude Code 兼容配置 / Claude Code Compatibility ==========

# Claude-to-Qwen 模型映射(可选,内置默认映射已覆盖主流 Claude 模型)
# 格式: claude-model-name=qwen-model-id,claude-model-name2=qwen-model-id2
# 未匹配的 claude-* 模型自动回退到 qwen3-coder-plus
# Claude-to-Qwen model mapping (optional; built-in defaults cover mainstream Claude models)
# Format: claude-model-name=qwen-model-id,claude-model-name2=qwen-model-id2
# Unmatched claude-* models fall back to qwen3-coder-plus
# CLAUDE_MODEL_MAP=claude-sonnet-5=qwen3-coder-plus,claude-opus-4=qwen3-max
# ========== 入站模型名映射 / Incoming model name mapping ==========

# 把客户端发来的模型名映射成 Qwen 模型 id,只作用于 /v1/chat/completions 和 /v1/messages
# (图片、视频、CLI 端点不走这里)。Claude Code 的子代理会发 claude-opus-5 / claude-haiku-*
# (除非客户端自己设置了 ANTHROPIC_DEFAULT_OPUS/SONNET/HAIKU_MODEL 或 CLAUDE_CODE_SUBAGENT_MODEL;
# 服务端映射不需要任何客户端配置),OpenAI 风格客户端会发 gpt-*;不映射时上游返回 "Model not found"。
# 规则(两个端点相同):
# 1. 精确匹配优先,不区分大小写,末尾的 [..] 后缀先去掉(claude-opus-5[1m] 按 claude-opus-5 处理)
# 2. 上游已存在的 Qwen id(含 -thinking 等变体)原样透传,不受 * 影响
# 3. 其余名字用 * 条目;没有 * 时用上游第一个 t2t 模型并打印一条 warn。
# 仅在上游模型列表可用时生效:列表取不到时名字原样转发(打印一条 warn),不套用 *
# 目标 id 可带 -thinking 等后缀,后缀照常生效(会打开思考)。响应里的 model 字段回显解析后的
# Qwen id,不是别名。别名不需要出现在 /v1/models 里。落到回退目标的名字记录在进程内存中
# (每个 PM2 worker 一份,最多 100 个)。
# Maps incoming model names to Qwen model ids; applies to /v1/chat/completions and /v1/messages
# only (not images/videos/cli). Claude Code subagents send claude-opus-5 / claude-haiku-* (unless
# the client sets ANTHROPIC_DEFAULT_OPUS/SONNET/HAIKU_MODEL or CLAUDE_CODE_SUBAGENT_MODEL; the
# server-side map needs no client config), OpenAI-style clients send gpt-*; without a map the
# upstream answers "Model not found". Rule (same on both endpoints):
# 1. exact entry wins, case-insensitive; a trailing [..] suffix is stripped first (claude-opus-5[1m] = claude-opus-5)
# 2. names that already exist upstream (incl. -thinking variants) pass through, even with *
# 3. everything else uses the * entry; with no * the first upstream t2t model is used with a warn.
# Only while the upstream model list is available: if it cannot be fetched the name is
# forwarded unchanged (one warn) and * is not applied
# Targets may carry suffixes such as -thinking; they apply as usual (thinking switches on). The
# response `model` field echoes the resolved Qwen id, not the alias. Aliases work without being
# listed in /v1/models. Names that fell to the fallback are recorded in process memory (one list
# per PM2 worker, 100 max).
# Dashboard:系统设置里的「模型映射」卡片可在线编辑。dashboard 保存过的映射优先于本变量(重启后仍生效);
# DATA_SAVE_MODE=none 时 dashboard 的修改只在内存里生效,重启即丢;「恢复 env 映射」会清掉保存的映射,
# 本变量重新生效。PM2 多 worker 时,保存的映射只在处理请求的 worker 立即生效,其他 worker 重启后才读到。
# Dashboard: the "Model mapping" card in Settings edits this at runtime. A dashboard-saved map takes
# precedence over this variable (and survives restarts); with DATA_SAVE_MODE=none dashboard changes
# live in memory only and are lost on restart; "Restore env map" clears the saved map so this variable
# applies again. With several PM2 workers a saved map applies at once only in the worker that handled
# the save; the others pick it up at their next restart.
# 格式 / Format: alias=qwen-model-id,alias2=qwen-model-id2,*=fallback-qwen-model-id
# 示例 / Example: MODEL_MAP=claude-opus-5=qwen3.8-max,*=qwen3.8-max-thinking
MODEL_MAP=

# ========== CLI 配置 / CLI Configuration ==========

Expand Down
3 changes: 3 additions & 0 deletions README-en.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ OUTPUT_THINK=true # Whether to output thinking process (true/false)
LEGACY_REASONING_IN_CONTENT=false # Reasoning format, false=reasoning_content field, true=legacy <think> inside content (true/false)
SIMPLE_MODEL_MAP=false # Simplify model mapping (true/false)
MODELS_CACHE_TTL=3600 # Model list cache TTL in seconds, 0=never expires
AGENT_TURN_MAX_TOOL_CALLS=24 # Anthropic path: text-channel tool_use cap per agent turn (4-256), upstream cut after it

# 🌐 Proxy and Reverse Proxy Configuration
QWEN_CHAT_PROXY_URL= # Custom Chat API reverse proxy URL (default: https://chat.qwen.ai)
Expand Down Expand Up @@ -130,7 +131,9 @@ CACHE_MODE=default # Image cache mode (default/file)
| `OUTPUT_THINK` | Whether to show AI thinking process | `true` or `false` |
| `LEGACY_REASONING_IN_CONTENT` | Reasoning output format. Default `false` = reasoning goes to a separate `reasoning_content` field; `true` = legacy behavior (`<think>` inside `content`) | `true` or `false` |
| `SIMPLE_MODEL_MAP` | Simplify model mapping, return basic models without variants only | `true` or `false` |
| `MODEL_MAP` | Incoming model name mapping: `alias=qwen-id,...,*=fallback`. Exact entry wins (trailing `[..]` stripped, case-insensitive), existing Qwen ids pass through, everything else uses `*`; applies to `/v1/chat/completions` and `/v1/messages` only. Also editable at runtime in the dashboard (Settings → Model mapping); a dashboard-saved map overrides this variable, see `.env.example` | `*=qwen3.8-max-thinking` |
| `MODELS_CACHE_TTL` | Model list cache TTL in seconds; after expiry the next request refreshes it from upstream; `0` = never expires | `3600` |
| `AGENT_TURN_MAX_TOOL_CALLS` | Anthropic path: cap on text-channel `tool_use` blocks per agent turn (4–256). When the model runs away after a narrated `[TOOL CALL]` (repeats the same call hundreds of times, hallucinates a whole session), the upstream is cut right after the N-th admitted call and the admitted calls are delivered with `stop_reason=tool_use`; once a call was admitted in an earlier delta, a duplicate, a rejected call or prose/thinking also cuts the turn | `24` |
| `AGENT_CONTEXT_FILE_THRESHOLD_BYTES` | Externalize complete Agent tool definitions and history as a Qwen text document when the request body exceeds this size, avoiding the roughly 128 KiB WAF limit | `92160` (90 KiB) |
| `AGENT_CONTEXT_LIVE_PROMPT_BYTES` | Maximum size of the tool protocol and current turn kept in the live request after context externalization | `49152` (48 KiB) |
| `QWEN_CHAT_PROXY_URL` | Custom Chat API reverse proxy address | `https://your-proxy.com` |
Expand Down
3 changes: 3 additions & 0 deletions README-ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ OUTPUT_THINK=true # Выводить ли процесс размы
LEGACY_REASONING_IN_CONTENT=false # Формат рассуждений: false=поле reasoning_content, true=старый режим <think> внутри content (true/false)
SIMPLE_MODEL_MAP=false # Упрощенное сопоставление моделей (true/false)
MODELS_CACHE_TTL=3600 # Срок жизни кэша списка моделей (сек), 0=бессрочно
AGENT_TURN_MAX_TOOL_CALLS=24 # Anthropic: лимит tool_use по текстовому каналу за ход агента (4-256), дальше обрыв upstream

# 🌐 Прокси и обратный прокси
QWEN_CHAT_PROXY_URL= # Пользовательский URL обратного прокси Chat API (по умолчанию: https://chat.qwen.ai)
Expand Down Expand Up @@ -150,7 +151,9 @@ CACHE_MODE=default # Режим кэширования изображ
| `OUTPUT_THINK` | Отображать ли процесс размышления AI | `true` или `false` |
| `LEGACY_REASONING_IN_CONTENT` | Формат вывода рассуждений. По умолчанию `false` = рассуждения в отдельном поле `reasoning_content`; `true` = старый режим (`<think>` внутри `content`) | `true` или `false` |
| `SIMPLE_MODEL_MAP` | Упрощенное сопоставление моделей, возвращает только базовые модели без вариантов | `true` или `false` |
| `MODEL_MAP` | Сопоставление входящих имён моделей: `alias=qwen-id,...,*=fallback`. Точное совпадение в приоритете (хвостовой `[..]` отбрасывается, без учёта регистра), существующие id Qwen проходят без изменений, остальное идёт в `*`; действует только для `/v1/chat/completions` и `/v1/messages`. Редактируется и в панели (Настройки → Сопоставление моделей); сохранённое в панели сопоставление имеет приоритет над этой переменной, см. `.env.example` | `*=qwen3.8-max-thinking` |
| `MODELS_CACHE_TTL` | Срок жизни кэша списка моделей (в секундах); по истечении следующий запрос обновит список; `0` = бессрочный кэш | `3600` |
| `AGENT_TURN_MAX_TOOL_CALLS` | Путь Anthropic: лимит блоков `tool_use` по текстовому каналу за один ход агента (4–256). Если модель «идёт вразнос» после нарративного `[TOOL CALL]` (сотни повторов одного вызова, выдуманная сессия целиком), upstream обрывается сразу после N-го принятого вызова, а принятые вызовы отдаются со `stop_reason=tool_use`; после уже принятого в более раннем delta вызова дубликат, отклонённый вызов или проза/размышление также обрывают ход | `24` |
| `QWEN_CHAT_PROXY_URL` | Пользовательский адрес обратного прокси Chat API | `https://your-proxy.com` |
| `QWEN_CLI_PROXY_URL` | Пользовательский адрес обратного прокси CLI API | `https://your-cli-proxy.com` |
| `PROXY_URL` | Адрес прокси для исходящих запросов, поддержка HTTP/HTTPS/SOCKS5 | `http://127.0.0.1:7890` |
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ LEGACY_REASONING_IN_CONTENT=false # 推理输出格式,false=reasoning_content
SIMPLE_MODEL_MAP=false # 简化模型映射 (true/false)
MODELS_CACHE_TTL=3600 # 模型列表缓存有效期(秒),0=永不过期
AGENT_TURN_MAX_ATTEMPTS=3 # 单个 Agent 回合生成有效工具调用/最终态的最大尝试数(2-6)
AGENT_TURN_MAX_TOOL_CALLS=24 # Anthropic 路径单轮文本通道 tool_use 上限(4-256),到数即截断上游
AGENT_TURN_ALLOW_PROSE_WITH_TOOLS=false # 允许工具调用回合同时带可见正文(Anthropic 客户端)
AGENT_TURN_ACCEPT_BARE_FINAL=false # 允许没有 <agent_final> 包装的可见正文作为正常结束
AGENT_CONTEXT_FILE_THRESHOLD_BYTES=92160 # 超过阈值时外置完整 Agent 上下文
Expand Down Expand Up @@ -135,10 +136,12 @@ CACHE_MODE=default # 图片缓存模式 (default/file)
| `OUTPUT_THINK` | 是否显示 AI 思考过程 | `true` 或 `false` |
| `LEGACY_REASONING_IN_CONTENT` | 推理输出格式。默认 `false`=推理走独立的 `reasoning_content` 字段;`true`=旧版行为(`<think>` 并入 `content`) | `true` 或 `false` |
| `SIMPLE_MODEL_MAP` | 简化模型映射,只返回基础模型不包含变体 | `true` 或 `false` |
| `MODEL_MAP` | 入站模型名映射:`alias=qwen-id,...,*=fallback`。精确匹配优先(末尾 `[..]` 先去掉、不区分大小写),上游已有的 Qwen id 原样透传,其余走 `*`;只作用于 `/v1/chat/completions` 与 `/v1/messages`。也可在管理面板「系统设置 → 模型映射」里在线编辑,面板保存的映射优先于本变量;详见 `.env.example` | `*=qwen3.8-max-thinking` |
| `MODELS_CACHE_TTL` | 模型列表缓存有效期(秒),过期后下次请求自动向上游刷新;`0` 表示永不过期 | `3600` |
| `AGENT_TURN_ALLOW_PROSE_WITH_TOOLS` | 放宽回合门禁:允许同一回合既有有效工具调用又有可见正文。Anthropic Messages API 允许 `text` 与 `tool_use` 共存,Claude Code 等客户端因此会被严格模式反复判为 `invalid_tool_call` | `false` |
| `AGENT_TURN_ACCEPT_BARE_FINAL` | 放宽回合门禁:把有可见正文但缺少 `<agent_final>` 包装的回合按 `finish_reason=stop` 接受,而不是判为 `bare` 并重试 | `false` |
| `AGENT_TURN_MAX_ATTEMPTS` | 工具请求在一次 HTTP 回合内生成有效 `tool_calls`、明确完成态或阻塞态的最大尝试数;范围 2–6,耗尽后非流式请求返回 HTTP 429/503,SSE 请求返回显式错误帧,绝不伪装成正常 `stop` | `3` |
| `AGENT_TURN_MAX_TOOL_CALLS` | Anthropic 路径:一轮 Agent 回合里文本通道 `tool_use` 的上限(4–256)。模型在叙述的 `[TOOL CALL]` 之后失控(同一调用重复上百次、幻想整段会话)时,第 N 个已放行的调用之后立刻终止上游,已放行的调用以 `stop_reason=tool_use` 交付;更早的 delta 里已放行过调用之后再出现重复、被拒绝的调用或正文/思考同样截断 | `24` |
| `AGENT_CONTEXT_FILE_THRESHOLD_BYTES` | Agent 请求体超过此大小时,将完整工具定义和历史自动外置为 Qwen 文本文档,避免触发约 128 KiB 的 WAF 限制 | `92160`(90 KiB) |
| `AGENT_CONTEXT_LIVE_PROMPT_BYTES` | 上下文外置后,实时请求中保留的工具协议、system/developer 指令、原始任务、最近工具进度和当前结果的最大大小 | `49152`(48 KiB) |
| `QWEN_CHAT_PROXY_URL` | 自定义 Chat API 反代地址 | `https://your-proxy.com` |
Expand Down
Loading