fix: 将 litellm 依赖窗口收敛到已验证上界(排除 1.99.0 回归) (#2320)

* Exclude litellm 1.99.0 from allowed versions

litellm 1.99.0 (released 2026-09-01T00:42:41Z) now forwards the
prompt_cache_key argument to the OpenAI provider. The provider cache
contract test asserts the key is not passed through without verified
capture, so CI backend-tests (3/3) and the downstream backend-gate
went red on every branch right after the release, unrelated to any
code change.

Local capture server reproduction: with 1.98.0 the captured request
body carries no prompt_cache_key; with 1.99.0 it carries
prompt_cache_key=cache-key. This one-line exclusion follows the
existing quarantine pattern for 1.82.7 / 1.82.8 and keeps the tested
behaviour until the contract is adapted deliberately.

* Bound litellm below the 1.99.0 behavioral change

Pin the dependency window to a verified upper bound (latest 1.98.x)
instead of quarantining only 1.99.0, and sync every doc that hardcodes
the litellm constraint string so docs cannot drift from requirements.txt
again.

* Sync .env.example runtime constraint comment to the litellm upper bound
This commit is contained in:
subaoyan16
2026-09-01 19:22:45 +08:00
committed by GitHub
parent ac8bd29b0f
commit 3d890a0731
11 changed files with 37 additions and 35 deletions

View File

@@ -201,7 +201,7 @@ GEMINI_API_KEY=
# - Moonshot 官方模型卡(评测默认 temperature = 1.0https://huggingface.co/moonshotai/Kimi-K2.6
# - OpenAI Chat Completions 规范https://platform.openai.com/docs/api-reference/chat/create
# - LiteLLM OpenAI-Compatible 规范https://docs.litellm.ai/docs/providers/openai_compatible
# 当前仓库运行时依赖约束litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0(显式排除 PyPI 事故版本,见 requirements.txt
# 当前仓库运行时依赖约束litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0(显式排除 PyPI 事故版本并将上界收敛到已验证版本,见 requirements.txt
# 因此 kimi-k2.6 会自动改用 1.0/0.6GPT-5 / o 系列等默认温度模型会省略 temperature避免 API 拒绝请求。
# 若兼容平台返回明确的参数不支持错误,运行时会在当前请求内修正参数并重试一次;成功策略只做进程内缓存。
# top_p、presence_penalty、frequency_penalty、seed 若返回“不支持参数”,同样会触发本次请求级别的修正与重试(不改写 LLM_TEMPERATURE

View File

@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
> For user-friendly release highlights, see the [GitHub Releases](https://github.com/ZhuLinsen/daily_stock_analysis/releases) page.
## [Unreleased]
- [修复] 将 litellm 依赖窗口上界收敛到 `<1.99.0`1.99.0 起把 `prompt_cache_key` 透传给 OpenAI provider破坏 provider 缓存测试对不透传行为的既有断言CI backend-tests 3/3 与 backend-gate 失败);保留历史最低版本与 `!=1.82.7`/`!=1.82.8` 事故排除,同时同步更新各 LLM 兼容文档中写死的依赖约束表述,避免文档与 requirements.txt 漂移
- [新功能] 新增 `SEARXNG_TIMEOUT_SECONDS` 配置自建 SearXNG 单次搜索超时(默认 10 秒),已接线全部 SearchService 构造入口(含题材搜索子进程重建)与默认 GitHub Actions 工作流
- [修复] 美股日线路由现按各数据源当前优先级排序,单项 `*_PRIORITY` 配置(如 `YFINANCE_PRIORITY=0`)对美股即时生效;指数固定首选与 Longbridge preferred 语义保持不变

View File

@@ -7,7 +7,7 @@
如果你正在选择具体服务商、配置 GitHub Actions Secrets / Variables、排查 `details.reason` 错误或准备回滚配置,请优先查看 [LLM 服务商配置指南](./llm-providers.md)。该文档集中维护 provider 预设、Actions 变量对照、运行时能力检测边界和常见错误处理建议。
> 本页的 provider/model/Base URL 说明本次未新增外部兼容语义,仅用于同步现网约定;实际兼容判断仍按当前仓库锁定依赖与运行时实现执行:
> - 依赖边界:`litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`(与 `requirements.txt` 一致)。
> - 依赖边界:`litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`(与 `requirements.txt` 一致)。
> - 兼容验证入口:`tests/test_system_config_service.py`、`tests/test_system_config_api.py` 以及现有前端模型配置页回归用例。
> - 回退路径:优先使用 `.env` 配置备份 + `POST /api/v1/system/config/import` 恢复;也可在重启前手动回填旧 `LITELLM_MODEL` / `LLM_*` / `AGENT_LITELLM_MODEL` / `VISION_MODEL` / `LLM_TEMPERATURE` / `LLM_USAGE_HMAC_*`。
@@ -156,7 +156,7 @@ OLLAMA_API_BASE=http://localhost:11434
LITELLM_MODEL=ollama/qwen3:8b
```
> **重要**Ollama 必须使用 `OLLAMA_API_BASE` 配置,**不要**使用 `OPENAI_BASE_URL`,否则系统会错误拼接 URL如 404、`api/generate/api/show`)。远程 Ollama 时,将 `OLLAMA_API_BASE` 设为实际地址(如 `http://192.168.1.100:11434`)。当前依赖约束为 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`(与 requirements.txt 一致)。
> **重要**Ollama 必须使用 `OLLAMA_API_BASE` 配置,**不要**使用 `OPENAI_BASE_URL`,否则系统会错误拼接 URL如 404、`api/generate/api/show`)。远程 Ollama 时,将 `OLLAMA_API_BASE` 设为实际地址(如 `http://192.168.1.100:11434`)。当前依赖约束为 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`(与 requirements.txt 一致)。
> **恭喜!小白读到这里就可以去运行程序了!**
> 想测测看通没通?在主目录打开命令行输入:`python scripts/check_env.py --llm`
@@ -180,18 +180,18 @@ LITELLM_MODEL=ollama/qwen3:8b
- 预设里的 provider / API Surface / Base URL / 示例模型只用于**初始化表单**;真正落盘时仍是你当前输入的 `LLM_{CHANNEL}_PROTOCOL``LLM_{CHANNEL}_API_SURFACE``LLM_{CHANNEL}_BASE_URL``LLM_{CHANNEL}_MODELS``LLM_{CHANNEL}_API_KEY(S)`,不会在后台偷偷改成别的 provider 名、Surface 或 URL。
- `LLM_{CHANNEL}_API_SURFACE` 可选 `chat_completions`(默认)或 `responses`Responses 当前只支持 OpenAI-compatible 协议,而且渠道内每个模型的实际 LiteLLM provider 都必须是 `openai`。系统从当前安装的 LiteLLM provider registry 识别直连前缀,并通过 `GET /api/v1/system/config``llm_model_providers` 返回给 Web 编辑器;前后端不再各自维护 provider 表。因此 `anthropic/claude-*``xai/grok-*` 以及未来 LiteLLM 新增的直连 provider 不会因本地白名单滞后而被错误包装成 OpenAI route冲突配置会在保存、启动、状态诊断和筛选入口一致拒绝不会生成 `anthropic/responses/...``deepseek-ai/DeepSeek-V3``Qwen/...` 这类不在 registry 中的网关自有模型 ID 会规范化为 `openai/<网关模型 ID>`。同一渠道中的模型必须使用同一种 Surface并且同一个规范化 route alias 不能跨渠道混用 Chat 与 Responses否则 Router 可能把同一次公开 alias 调度到错误 endpoint需要两种 Surface 时必须使用不同别名。设置页的“获取模型”仍只调用 `{base_url}/models`;“测试连接”会按选中的 Surface 对模型列表首项发起一次最小请求,并展示 `resolved_model``resolved_api_surface`,不会在失败后静默重试另一 endpoint。若返回 `details.reason=model_access_denied`(例如 Issue #1208 中已观测到的 SiliconFlow / OpenAI Compatible 经 LiteLLM 返回 `Model disabled`),请把它视为基于 provider 文案的 best-effort 模型可用性诊断,优先确认该模型是否已在当前账号/key 下开通,必要时调整模型顺序或移除不可用模型后重试;未覆盖或语义不同的 provider 文案会继续走兜底诊断。可选的“运行时能力检测”必须由用户显式选择后触发,会额外发起 JSON / tools / stream / vision smoke 请求,结果仅代表当前账号、模型和 endpoint 的一次 best-effort 检测。上述检测返回的结构化诊断字段**不会写回** `.env`,也不会阻止保存。
- 若返回 `details.reason=provider_blocked`,表示服务商或中转网关明确拦截了本次请求;它区别于本地网络 / TLS 异常和 `model_access_denied`,应优先检查账号风控、地域或请求来源限制、模型权限、代理商网关策略和内容安全策略。
- 运行时能力检测会产生真实 LLM 请求,可能带来 token / 图像输入费用、RPM/TPM 限流、余额不足或超时。检测失败可能来自账号权限、模型未开通、endpoint 区域、余额、服务商兼容层或 LiteLLM 转换路径,不等于该 provider 全局不支持对应能力。P3 未对所有真实 provider 做在线 smoke兼容依据来自当前依赖约束 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` 下的 LiteLLM `completion()` / OpenAI I/O format / streaming / exception mapping以及 OpenAI Chat Completions 的 JSON mode、tool calling、streaming 和 vision input 形状。
- 运行时能力检测会产生真实 LLM 请求,可能带来 token / 图像输入费用、RPM/TPM 限流、余额不足或超时。检测失败可能来自账号权限、模型未开通、endpoint 区域、余额、服务商兼容层或 LiteLLM 转换路径,不等于该 provider 全局不支持对应能力。P3 未对所有真实 provider 做在线 smoke兼容依据来自当前依赖约束 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` 下的 LiteLLM `completion()` / OpenAI I/O format / streaming / exception mapping以及 OpenAI Chat Completions 的 JSON mode、tool calling、streaming 和 vision input 形状。
- 相关外部来源LiteLLM Python SDK / OpenAI I/O format / streaming / exception mapping<https://docs.litellm.ai/>LiteLLM OpenAI-compatible 路由:<https://docs.litellm.ai/docs/providers/openai_compatible>OpenAI Chat Completions<https://platform.openai.com/docs/api-reference/chat/create>JSON mode<https://platform.openai.com/docs/guides/structured-outputs?api-mode=chat>tool calling<https://platform.openai.com/docs/guides/function-calling?api-mode=chat>streaming<https://platform.openai.com/docs/guides/streaming-responses?api-mode=chat>vision input<https://platform.openai.com/docs/guides/images-vision?api-mode=chat>
- 保存渠道时,只会更新这次提交的 key不会因为切换渠道模式而静默迁移整个旧配置。唯一会被**同步清理**的是运行时模型引用:如果 `LITELLM_MODEL``AGENT_LITELLM_MODEL``VISION_MODEL``LITELLM_FALLBACK_MODELS` 指向了当前已启用渠道里已经不存在的模型,设置页会在保存前把这些失效引用清空/移除,避免运行时继续指向无效模型;即使当前启用渠道没有任何可选模型,也会清理缺少 legacy Key 支撑的托管 provider 旧值。`cohere/*``google/*``xai/*` 这类直连模型仅用于说明历史 `direct-env` 兼容保留语义,不等于可用性承诺,是否可用请按各厂商官方模型/API 文档再做实际验证。
- 后端一致性依据:配置校验链路在 `SystemConfigService._validate_llm_runtime_selection``src/services/system_config_service.py`)中通过 `_uses_direct_env_provider``src/config.py`)判断运行时来源;当前仅 `gemini``vertex_ai``anthropic``openai``deepseek` 属于托管 key provider`cohere``google``xai` 不在该白名单中,因此会保留为直连模型。
- 回退方式也保持最小:把对应渠道模型列表改回去后重新选择主模型 / fallback或直接用桌面端导出备份 / 手动 `.env` 还原之前的 `LLM_*``LITELLM_MODEL``AGENT_LITELLM_MODEL``VISION_MODEL``LLM_TEMPERATURE``LLM_USAGE_HMAC_*` 即可不需要额外跑迁移脚本。Web 端如需恢复配置,也可在启用管理员鉴权(`ADMIN_AUTH_ENABLED=true`)后通过 `POST /api/v1/system/config/import` 回滚。
- 当前仓库对此链路的依赖约束是 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`(见 `requirements.txt`);回归覆盖包括 `tests/test_system_config_service.py``tests/test_system_config_api.py``apps/dsa-web/src/components/settings/__tests__/LLMChannelEditor.test.tsx`
- 当前仓库对此链路的依赖约束是 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`(见 `requirements.txt`);回归覆盖包括 `tests/test_system_config_service.py``tests/test_system_config_api.py``apps/dsa-web/src/components/settings/__tests__/LLMChannelEditor.test.tsx`
> **外部 provider 示例模型说明**`cohere/*`、`google/*`、`xai/*` 等 provider 前缀值仅用于说明当前保存清理语义,**不代表该依赖约束内的逐型号可用性保证**。文档或测试中的具体模型名都是配置保留行为样例,不是生产推荐;实际可用性请以对应官方模型文档为准,并结合仓库依赖约束 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` 复核。
> **外部 provider 示例模型说明**`cohere/*`、`google/*`、`xai/*` 等 provider 前缀值仅用于说明当前保存清理语义,**不代表该依赖约束内的逐型号可用性保证**。文档或测试中的具体模型名都是配置保留行为样例,不是生产推荐;实际可用性请以对应官方模型文档为准,并结合仓库依赖约束 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` 复核。
### 回退与兼容性证据
- 依赖约束与静默清理范围:在 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` 下,保存仅清理失效的 runtime 模型引用(`LITELLM_MODEL``AGENT_LITELLM_MODEL``VISION_MODEL``LITELLM_FALLBACK_MODELS``cohere/*``google/*``xai/*` 等非渠道直连模型会被保留。
- 依赖约束与静默清理范围:在 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` 下,保存仅清理失效的 runtime 模型引用(`LITELLM_MODEL``AGENT_LITELLM_MODEL``VISION_MODEL``LITELLM_FALLBACK_MODELS``cohere/*``google/*``xai/*` 等非渠道直连模型会被保留。
- 回退方式:可直接用桌面端导出备份后通过 `POST /api/v1/system/config/import` 恢复;也可手动把 `.env` 中历史 `LITELLM_* / AGENT_LITELLM_MODEL / VISION_MODEL / LLM_TEMPERATURE / LLM_USAGE_HMAC_*` 回填后重启生效。Web 端执行导入前请先开启管理员鉴权(`ADMIN_AUTH_ENABLED=true`)。
- 回退回归证据:`tests/test_system_config_service.py::test_import_desktop_env_restores_runtime_models_after_cleanup` 覆盖“清理后用桌面导出备份恢复 runtime 引用”。
- 直连 provider 回归证据:`tests/test_system_config_service.py::SystemConfigServiceTestCase::test_validate_accepts_minimax_model_as_direct_env_provider``test_validate_accepts_cohere_model_as_direct_env_provider``test_validate_accepts_google_model_as_direct_env_provider``test_validate_accepts_xai_model_as_direct_env_provider` 覆盖直连 provider 保留语义。
@@ -314,7 +314,7 @@ AGENT_CONTEXT_PROTECTED_TURNS=
压缩只处理 `session_id` 下用户可见的 `user` / `assistant` 文本历史,不处理 provider trace、thinking blocks、tool calls 或 tool results也不会改变同轮工具调用透传。三档 preset 分别是 `cost`6000 tokens / 保护 2 轮)、`balanced`12000 / 4`long_context_raw_first`24000 / 6trigger / protected 留空时跟随当前 profile显式填写时覆盖 profile。
问股 single-agent 路径会额外维护一条 provider-aware trace 分轨,用于 DeepSeek V4 thinking + tool-call 的跨轮协议回放:只有同一轮同时出现 `tool_calls``reasoning_content` 时才会按当前 `session_id + provider + model` 保存最近 3 条最小协议材料,并在下一轮按原始时序插回对应可见 assistant 回复之前。该 trace 只能原样保留或整段丢弃,不参与摘要、不写入 Web 会话消息、不新增 `.env` 配置model/provider 不匹配、锚点已被 summary 覆盖或预算不足时会整段跳过。Claude extended thinking 本轮只覆盖 adapter/storage 级 opaque `thinking` / `redacted_thinking` / `signature` blocks plumbing 与离线 fixture不声明生产端到端支持multi-agent trace 注入仍是 follow-up。外部协议依据包括 DeepSeek thinking mode 文档(<https://api-docs.deepseek.com/guides/thinking_mode>)和 Anthropic Claude extended thinking 文档(<https://platform.claude.com/docs/en/docs/build-with-claude/extended-thinking>LiteLLM 兼容窗口仍以 `requirements.txt``litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` 为准。
问股 single-agent 路径会额外维护一条 provider-aware trace 分轨,用于 DeepSeek V4 thinking + tool-call 的跨轮协议回放:只有同一轮同时出现 `tool_calls``reasoning_content` 时才会按当前 `session_id + provider + model` 保存最近 3 条最小协议材料,并在下一轮按原始时序插回对应可见 assistant 回复之前。该 trace 只能原样保留或整段丢弃,不参与摘要、不写入 Web 会话消息、不新增 `.env` 配置model/provider 不匹配、锚点已被 summary 覆盖或预算不足时会整段跳过。Claude extended thinking 本轮只覆盖 adapter/storage 级 opaque `thinking` / `redacted_thinking` / `signature` blocks plumbing 与离线 fixture不声明生产端到端支持multi-agent trace 注入仍是 follow-up。外部协议依据包括 DeepSeek thinking mode 文档(<https://api-docs.deepseek.com/guides/thinking_mode>)和 Anthropic Claude extended thinking 文档(<https://platform.claude.com/docs/en/docs/build-with-claude/extended-thinking>LiteLLM 兼容窗口仍以 `requirements.txt``litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` 为准。
### 严格 temperature 模型兼容说明
@@ -322,7 +322,7 @@ AGENT_CONTEXT_PROTECTED_TURNS=
- LiteLLM 官方要求 OpenAI Compatible 渠道模型名使用 `openai/` 前缀:<https://docs.litellm.ai/docs/providers/openai_compatible>
- Moonshot 官方兼容性文档区分两种固定值:**thinking 模式固定 `1.0`non-thinking 模式固定 `0.6`**;传其它值会被接口拒绝:<https://platform.moonshot.ai/docs/guide/compatibility#parameters-differences-in-request-body>
- OpenAI Chat Completions 规范中 `temperature` 是可选参数;对 GPT-5 / o 系列等只接受默认温度的模型,本项目会在请求层省略 `temperature`,让服务端使用默认值,而不是改写你的 `LLM_TEMPERATURE`<https://platform.openai.com/docs/api-reference/chat/create>
- 当前仓库的运行时依赖约束是 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`(见 `requirements.txt`本次兼容逻辑按该约束回归验证了主分析、大盘复盘、Agent 直连 LiteLLM以及系统设置页的渠道连通性测试。
- 当前仓库的运行时依赖约束是 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`(见 `requirements.txt`本次兼容逻辑按该约束回归验证了主分析、大盘复盘、Agent 直连 LiteLLM以及系统设置页的渠道连通性测试。
- 因此本项目会在请求发出前按**实际请求模式**归一化 `kimi-k2.6` 及其 `kimi-k2.6-*` 变体:默认 / thinking 路径使用 `temperature=1.0`;如果你的 LiteLLM YAML 路由别名里显式写了 `litellm_params.extra_body.thinking.type: disabled`(或等价 non-thinking 配置),则自动切到 `temperature=0.6`。你在 `.env` 或 Web 设置里保存的 `LLM_TEMPERATURE` 不会被改写。
- 如果兼容平台对未收录的新模型返回明确的参数错误(例如 `temperature` 不支持、只能使用默认 `1.0``top_p` 不支持),运行时会对**当前请求**做一次参数修正并重试;只有重试成功后才把该策略缓存在当前进程内。该缓存不会写回 `.env`,服务重启后会重新按配置与适配规则判断。
- 对已经产生部分内容的流式响应,系统不会在半截输出后切换参数;仍沿用原有“同模型非流式重试 / fallback 模型”的稳定路径,避免拼接出不一致的回答。
@@ -333,7 +333,7 @@ AGENT_CONTEXT_PROTECTED_TURNS=
### 兼容性与回退复核清单(按 PR 审核口径)
- 运行时依赖约束:`litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`(与 `requirements.txt` 一致)。
- 运行时依赖约束:`litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`(与 `requirements.txt` 一致)。
- 回归验证入口:
- 渠道模型发现与连接:`tests/test_llm_channel_config.py`
- 运行时源清理与恢复(含桌面导出备份链路):`tests/test_system_config_service.py`
@@ -346,7 +346,7 @@ AGENT_CONTEXT_PROTECTED_TURNS=
### 兼容依据与回退审计说明(本次 PR 适配说明)
- 官方与运行时兼容依据采用两层第一层为官方接口语义LiteLLM OpenAI-compatible 路由、OpenAI Chat Completions、Moonshot/Kimi 文档与官方模型说明);第二层为本仓库当前运行时语义(`litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`)下的实际错误归类。
- 官方与运行时兼容依据采用两层第一层为官方接口语义LiteLLM OpenAI-compatible 路由、OpenAI Chat Completions、Moonshot/Kimi 文档与官方模型说明);第二层为本仓库当前运行时语义(`litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`)下的实际错误归类。
- 本次兼容恢复只使用“本地运行时错误归类 + 单请求修正重试 + 进程内缓存”策略,不写入 `.env`、不做配置迁移,仅在执行路径上动态规避不支持参数(`temperature``top_p``presence_penalty``frequency_penalty``seed`)。若要回退,不需要额外迁移命令,恢复旧值即可。
- 回归与证据:`tests/test_llm_param_recovery.py``tests/test_system_config_service.py``tests/test_llm_channel_config.py``tests/test_system_config_api.py``tests/test_market_analyzer_generate_text.py``tests/test_agent_pipeline.py`;桌面导入与运行时清理回退另有 `test_import_desktop_env_restores_runtime_models_after_cleanup` 直接覆盖。
@@ -368,10 +368,10 @@ Cache token 归一化只做 allowlisted best-effort normalization。外部字段
| --- | --- | --- | --- |
| OpenAI | `usage.prompt_tokens_details.cached_tokens` | 官方 Prompt Caching 文档说明 1024 tokens 以下也会返回 `cached_tokens=0`<https://developers.openai.com/api/docs/guides/prompt-caching> | unit/mock 覆盖;本 PR 未做 OpenAI live smoke |
| Anthropic | `cache_creation_input_tokens` / `cache_read_input_tokens` / `input_tokens` | 官方 Prompt Caching 文档定义 `total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens`<https://platform.claude.com/docs/en/build-with-claude/prompt-caching> | unit/mock 覆盖;本 PR 未做 Anthropic live smoke |
| Gemini / Vertex AI | 官方字段为 `UsageMetadata.cachedContentTokenCount`;运行时消费 LiteLLM 暴露的 snake_case / normalized 字段,如 `cached_content_token_count``cache_read_input_tokens``prompt_tokens_details.cached_tokens` | Gemini `UsageMetadata` 官方字段见 <https://ai.google.dev/api/generate-content#UsageMetadata>;本仓库不新增 native camelCase runtime fallback运行时边界以 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` 为准 | unit/mock 覆盖;本 PR 未做 Gemini / Vertex live smoke |
| Gemini / Vertex AI | 官方字段为 `UsageMetadata.cachedContentTokenCount`;运行时消费 LiteLLM 暴露的 snake_case / normalized 字段,如 `cached_content_token_count``cache_read_input_tokens``prompt_tokens_details.cached_tokens` | Gemini `UsageMetadata` 官方字段见 <https://ai.google.dev/api/generate-content#UsageMetadata>;本仓库不新增 native camelCase runtime fallback运行时边界以 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` 为准 | unit/mock 覆盖;本 PR 未做 Gemini / Vertex live smoke |
| DeepSeek | `prompt_cache_hit_tokens` / `prompt_cache_miss_tokens` | DeepSeek Chat Completion 文档说明 `prompt_tokens = prompt_cache_hit_tokens + prompt_cache_miss_tokens`<https://api-docs.deepseek.com/api/create-chat-completion> | unit/mock 覆盖;本 PR 只做一次脱敏 DeepSeek smoke不保存完整响应 |
| GLM / OpenAI-compatible / StepFun 等兼容平台 | 已建模 token/cache count allowlist 中能映射到统一字段的值 | 不声明官方稳定 cache telemetry contract仅表示在当前 LiteLLM / OpenAI-compatible shape 下做 best-effort normalization未建模 metadata 不持久化 | unit/fixture/mock 覆盖;本 PR 未做这些 provider 的 live smoke |
| LiteLLM public response shape | `usage` / `usage_metadata` | 按当前依赖窗口 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` 的 response / `Usage` object shape 消费;不作为 LiteLLM 2.x 兼容承诺 | Analyzer / Agent / usage tests 覆盖 |
| LiteLLM public response shape | `usage` / `usage_metadata` | 按当前依赖窗口 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` 的 response / `Usage` object shape 消费;不作为 LiteLLM 2.x 兼容承诺 | Analyzer / Agent / usage tests 覆盖 |
| LiteLLM private fallback | `_hidden_params["usage"]` | private/internal best-effort fallback不是 LiteLLM 稳定公共契约;仅在 public usage zero-only/no-signal 等窄场景补足 streaming usage不改变 provider 请求参数 | unit/mock 覆盖;缺失时只影响 telemetry 完整性,不代表模型请求失败 |
```env

View File

@@ -149,7 +149,7 @@ OLLAMA_API_BASE=http://localhost:11434
LITELLM_MODEL=ollama/qwen3:8b
```
> **Important**: Ollama must be configured with `OLLAMA_API_BASE`. **Do not** use `OPENAI_BASE_URL`, or the system will concatenate URLs incorrectly (e.g. 404, `api/generate/api/show`). For remote Ollama, set `OLLAMA_API_BASE` to the actual address (e.g. `http://192.168.1.100:11434`). Current dependency constraint is `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` (matches requirements.txt).
> **Important**: Ollama must be configured with `OLLAMA_API_BASE`. **Do not** use `OPENAI_BASE_URL`, or the system will concatenate URLs incorrectly (e.g. 404, `api/generate/api/show`). For remote Ollama, set `OLLAMA_API_BASE` to the actual address (e.g. `http://192.168.1.100:11434`). Current dependency constraint is `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` (matches requirements.txt).
> **Congratulations! If you're a beginner, you can stop reading here and run the program!**
> Want to test the connection? Open your terminal in the root directory and run: `python scripts/check_env.py --llm`
@@ -173,18 +173,18 @@ The backend exposes a read-only status endpoint at `GET /api/v1/system/config/se
- The preset provider / API surface / Base URL / sample models are **form defaults only**. What gets persisted is exactly what you submit in `LLM_{CHANNEL}_PROTOCOL`, `LLM_{CHANNEL}_API_SURFACE`, `LLM_{CHANNEL}_BASE_URL`, `LLM_{CHANNEL}_MODELS`, and `LLM_{CHANNEL}_API_KEY(S)`; the editor does not silently rewrite them to a different provider, surface, or URL.
- `LLM_{CHANNEL}_API_SURFACE` accepts `chat_completions` (default) or `responses`; Responses currently requires the OpenAI-compatible protocol, and every model's actual LiteLLM provider must be `openai`. Direct prefixes come from the installed LiteLLM provider registry and are returned to the Web editor as `llm_model_providers` by `GET /api/v1/system/config`, so the frontend and backend do not maintain separate provider tables. This prevents `anthropic/claude-*`, `xai/grok-*`, and providers added by future LiteLLM versions from being miswrapped as OpenAI routes because of a stale client allow-list. Conflicts are rejected consistently by save validation, runtime loading, status diagnostics, and screening instead of producing `anthropic/responses/...`. Gateway-owned IDs such as `deepseek-ai/DeepSeek-V3` or `Qwen/...` that are absent from the registry are normalized under `openai/<gateway model ID>`. All models in one channel must use the same surface, and one normalized route alias cannot mix Chat and Responses across channels because Router load balancing could select the wrong endpoint; use distinct aliases when both surfaces are required. "Discover models" still calls `{base_url}/models`; "Test connection" sends one minimal request through the selected surface and reports `resolved_model` plus `resolved_api_surface`. It never retries another endpoint after failure. If the response includes `details.reason=model_access_denied` (for example, the observed Issue #1208 SiliconFlow / OpenAI Compatible sample returned `Model disabled` through LiteLLM), treat it as a best-effort availability diagnostic: confirm model entitlement for the current key, then adjust the model order or remove unavailable models. Optional capability checks send additional real requests and remain best-effort. Their structured diagnostic fields are **never persisted** into `.env` and do not block saving.
- If the response includes `details.reason=provider_blocked`, the provider or relay gateway explicitly blocked this request. This is distinct from local network / TLS failures and `model_access_denied`; first check account risk controls, region or request-source restrictions, model entitlement, relay gateway policy, and content-safety policy.
- Runtime capability checks send real LLM requests and may incur token / image-input cost, RPM/TPM rate limiting, insufficient balance errors, or timeouts. A failed check may come from account permissions, model entitlement, endpoint region, balance, provider compatibility layers, or LiteLLM translation behavior; it does not prove that the provider globally lacks that capability. P3 does not include online smoke coverage for every real provider. Its compatibility basis is the repository dependency constraint `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`, LiteLLM `completion()` / OpenAI I/O format / streaming / exception mapping, and the OpenAI Chat Completions shapes for JSON mode, tool calling, streaming, and vision input.
- Runtime capability checks send real LLM requests and may incur token / image-input cost, RPM/TPM rate limiting, insufficient balance errors, or timeouts. A failed check may come from account permissions, model entitlement, endpoint region, balance, provider compatibility layers, or LiteLLM translation behavior; it does not prove that the provider globally lacks that capability. P3 does not include online smoke coverage for every real provider. Its compatibility basis is the repository dependency constraint `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`, LiteLLM `completion()` / OpenAI I/O format / streaming / exception mapping, and the OpenAI Chat Completions shapes for JSON mode, tool calling, streaming, and vision input.
- External references: LiteLLM Python SDK / OpenAI I/O format / streaming / exception mapping: <https://docs.litellm.ai/>; LiteLLM OpenAI-compatible routing: <https://docs.litellm.ai/docs/providers/openai_compatible>; OpenAI Chat Completions: <https://platform.openai.com/docs/api-reference/chat/create>; JSON mode: <https://platform.openai.com/docs/guides/structured-outputs?api-mode=chat>; tool calling: <https://platform.openai.com/docs/guides/function-calling?api-mode=chat>; streaming: <https://platform.openai.com/docs/guides/streaming-responses?api-mode=chat>; vision input: <https://platform.openai.com/docs/guides/images-vision?api-mode=chat>.
- Saving channels only updates the keys submitted in that save operation; there is no whole-config silent migration when you switch channel settings. The one deliberate cleanup is runtime model references: if `LITELLM_MODEL`, `AGENT_LITELLM_MODEL`, `VISION_MODEL`, or `LITELLM_FALLBACK_MODELS` point to models that no longer exist in the currently enabled channels, the editor clears/removes those stale references before saving so runtime calls do not keep targeting invalid models. Even when enabled channels expose no selectable models, stale managed-provider values without a matching legacy key are cleaned. `cohere/*`, `google/*`, and `xai/*` are kept as explicit direct-env compatibility examples for legacy retention behavior only, and are not a runtime availability guarantee.
- Backend consistency basis: runtime validation in `SystemConfigService._validate_llm_runtime_selection` (`src/services/system_config_service.py`) relies on `_uses_direct_env_provider` (`src/config.py`). Only `gemini`, `vertex_ai`, `anthropic`, `openai`, and `deepseek` are treated as managed key-backed providers; `cohere`, `google`, and `xai` are not in that allowlist, so they remain valid direct provider runtime entries.
- Rollback stays minimal: restore the previous channel model list and re-select the runtime models, or restore the previous `LLM_*`, `LITELLM_MODEL`, `AGENT_LITELLM_MODEL`, `VISION_MODEL`, `LLM_TEMPERATURE`, and `LLM_USAGE_HMAC_*` values from your desktop export / manual `.env` backup. No extra migration script is required.
- The current dependency constraint for this flow in the repository is `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` (see `requirements.txt`). Regression coverage for it lives in `tests/test_system_config_service.py`, `tests/test_system_config_api.py`, and `apps/dsa-web/src/components/settings/__tests__/LLMChannelEditor.test.tsx`.
- The current dependency constraint for this flow in the repository is `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` (see `requirements.txt`). Regression coverage for it lives in `tests/test_system_config_service.py`, `tests/test_system_config_api.py`, and `apps/dsa-web/src/components/settings/__tests__/LLMChannelEditor.test.tsx`.
> **External provider model examples notice**: `cohere/*`, `google/*`, and `xai/*` provider-prefixed values are included here only to describe current runtime retention behavior and are **not** a global availability guarantee. Specific model names in docs or tests are configuration-retention examples, not production recommendations. Check the provider's official model/API docs and validate against the repository dependency constraint `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` before production use.
> **External provider model examples notice**: `cohere/*`, `google/*`, and `xai/*` provider-prefixed values are included here only to describe current runtime retention behavior and are **not** a global availability guarantee. Specific model names in docs or tests are configuration-retention examples, not production recommendations. Check the provider's official model/API docs and validate against the repository dependency constraint `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` before production use.
### Rollback & compatibility evidence
- Scope and cleanup behavior under `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`: only runtime references (`LITELLM_MODEL`, `AGENT_LITELLM_MODEL`, `VISION_MODEL`, `LITELLM_FALLBACK_MODELS`) are sanitized during save; non-channel direct providers such as `cohere/*`, `google/*`, and `xai/*` are preserved.
- Scope and cleanup behavior under `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`: only runtime references (`LITELLM_MODEL`, `AGENT_LITELLM_MODEL`, `VISION_MODEL`, `LITELLM_FALLBACK_MODELS`) are sanitized during save; non-channel direct providers such as `cohere/*`, `google/*`, and `xai/*` are preserved.
- Rollback path: export desktop config, then restore the backup through `POST /api/v1/system/config/import`; or manually restore historical `.env` entries (`LITELLM_*`, `AGENT_LITELLM_MODEL`, `VISION_MODEL`, `LLM_TEMPERATURE`, `LLM_USAGE_HMAC_*`) and restart.
- Rollback evidence: `tests/test_system_config_service.py::test_import_desktop_env_restores_runtime_models_after_cleanup` covers restore from exported desktop backup after runtime cleanup.
- Direct-provider evidence: `tests/test_system_config_service.py::SystemConfigServiceTestCase::test_validate_accepts_minimax_model_as_direct_env_provider`, `test_validate_accepts_cohere_model_as_direct_env_provider`, `test_validate_accepts_google_model_as_direct_env_provider`, and `test_validate_accepts_xai_model_as_direct_env_provider` cover the preserved direct-provider behavior.
@@ -307,7 +307,7 @@ AGENT_CONTEXT_PROTECTED_TURNS=
Compression processes only visible `user` / `assistant` text under the same `session_id`. It does not process provider traces, thinking blocks, tool calls, or tool results, and it does not change same-turn tool passthrough. The presets are `cost` (6000 tokens / 2 protected turns), `balanced` (12000 / 4), and `long_context_raw_first` (24000 / 6). Blank trigger/protected values follow the selected profile; explicit values override it.
For the single-agent ask-stock path, the backend also keeps a provider-aware trace track for DeepSeek V4 thinking + tool-call roundtrip. A trace is persisted only when the same run has both `tool_calls` and `reasoning_content`; the last 3 minimal protocol slices per `session_id + provider + model` are spliced back into the next request before the anchored visible assistant reply. Provider trace is either preserved exactly or dropped as a whole; it is never summarized, never returned by Web session-history APIs, and adds no `.env` setting. Model/provider mismatch, summarized anchors, or insufficient budget drop the whole trace. Claude extended thinking is limited in this PR to adapter/storage-level opaque `thinking` / `redacted_thinking` / `signature` block plumbing with offline fixtures; production end-to-end Claude and multi-agent trace injection remain follow-ups. Protocol references: DeepSeek thinking mode (<https://api-docs.deepseek.com/guides/thinking_mode>) and Anthropic Claude extended thinking (<https://platform.claude.com/docs/en/docs/build-with-claude/extended-thinking>). The LiteLLM compatibility window remains `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` from `requirements.txt`.
For the single-agent ask-stock path, the backend also keeps a provider-aware trace track for DeepSeek V4 thinking + tool-call roundtrip. A trace is persisted only when the same run has both `tool_calls` and `reasoning_content`; the last 3 minimal protocol slices per `session_id + provider + model` are spliced back into the next request before the anchored visible assistant reply. Provider trace is either preserved exactly or dropped as a whole; it is never summarized, never returned by Web session-history APIs, and adds no `.env` setting. Model/provider mismatch, summarized anchors, or insufficient budget drop the whole trace. Claude extended thinking is limited in this PR to adapter/storage-level opaque `thinking` / `redacted_thinking` / `signature` block plumbing with offline fixtures; production end-to-end Claude and multi-agent trace injection remain follow-ups. Protocol references: DeepSeek thinking mode (<https://api-docs.deepseek.com/guides/thinking_mode>) and Anthropic Claude extended thinking (<https://platform.claude.com/docs/en/docs/build-with-claude/extended-thinking>). The LiteLLM compatibility window remains `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` from `requirements.txt`.
### Strict Temperature Model Compatibility Notes
@@ -315,7 +315,7 @@ For the single-agent ask-stock path, the backend also keeps a provider-aware tra
- LiteLLM officially requires the `openai/` prefix for OpenAI-compatible model routing: <https://docs.litellm.ai/docs/providers/openai_compatible>
- Moonshot's compatibility docs distinguish two fixed values: **thinking mode must use `1.0`, while non-thinking mode must use `0.6`**; other values are rejected by the API: <https://platform.moonshot.ai/docs/guide/compatibility#parameters-differences-in-request-body>
- The OpenAI Chat Completions API treats `temperature` as optional. For GPT-5 / o-series style models that only accept the provider default temperature, this project omits `temperature` at request time instead of rewriting your saved `LLM_TEMPERATURE`: <https://platform.openai.com/docs/api-reference/chat/create>
- The current runtime dependency constraint in this repository is `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` (see `requirements.txt`); this compatibility fix is regression-covered under that constraint across the main analyzer, market review, direct Agent LiteLLM calls, and the system-settings channel connectivity test path.
- The current runtime dependency constraint in this repository is `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` (see `requirements.txt`); this compatibility fix is regression-covered under that constraint across the main analyzer, market review, direct Agent LiteLLM calls, and the system-settings channel connectivity test path.
- This repository therefore normalizes `kimi-k2.6` and `kimi-k2.6-*` right before dispatch based on the **actual request mode**: default / thinking requests use `temperature=1.0`; if your LiteLLM YAML route alias explicitly sets `litellm_params.extra_body.thinking.type: disabled` (or an equivalent non-thinking override), it automatically switches to `temperature=0.6`. Your saved `LLM_TEMPERATURE` value in `.env` or the Web settings is not rewritten.
- If a compatible platform returns an explicit parameter error for a not-yet-profiled model, such as unsupported `temperature`, default-only `1.0`, or unsupported `top_p`, the runtime repairs the **current request** and retries once. The strategy is cached only in the current process after the retry succeeds; it is never written back to `.env`, and a service restart re-evaluates the configured rules normally.
- For streaming responses that already produced partial content, the runtime does not switch parameters mid-output. It keeps the existing same-model non-stream retry / fallback-model path to avoid stitching inconsistent answers together.
@@ -329,7 +329,7 @@ For the single-agent ask-stock path, the backend also keeps a provider-aware tra
### Compatibility evidence and rollback audit notes (for this recovery change)
- Compatibility is validated in two layers: first-party provider/API contract references (LiteLLM OpenAI-compatible routing, OpenAI Chat Completions, Moonshot/Kimi docs and model notes), and second the current runtime implementation in this repository under `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`.
- Compatibility is validated in two layers: first-party provider/API contract references (LiteLLM OpenAI-compatible routing, OpenAI Chat Completions, Moonshot/Kimi docs and model notes), and second the current runtime implementation in this repository under `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`.
- This recovery path is runtime-only and intentionally local: exception classification + one in-request repair retry + in-process cache. It does not rewrite `.env`, migrate saved config keys, or alter legacy values; it only omits/adjusts request parameters (`temperature`, `top_p`, `presence_penalty`, `frequency_penalty`, `seed`) for the current call. Rolling back requires no migration; restore previous settings and model/provider selection.
- Regression evidence for this path is in `tests/test_llm_param_recovery.py`, `tests/test_system_config_service.py`, `tests/test_llm_channel_config.py`, `tests/test_system_config_api.py`, `tests/test_market_analyzer_generate_text.py`, `tests/test_agent_pipeline.py`; desktop backup import restore is directly covered by `test_import_desktop_env_restores_runtime_models_after_cleanup`.
@@ -381,10 +381,10 @@ Cache-token normalization is allowlisted best-effort normalization only. The ext
| --- | --- | --- | --- |
| OpenAI | `usage.prompt_tokens_details.cached_tokens` | The official Prompt Caching docs state that requests below 1024 tokens still expose `cached_tokens=0`: <https://developers.openai.com/api/docs/guides/prompt-caching> | Covered by unit/mock tests; this PR does not include OpenAI live smoke |
| Anthropic | `cache_creation_input_tokens` / `cache_read_input_tokens` / `input_tokens` | The official Prompt Caching docs define `total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens`: <https://platform.claude.com/docs/en/build-with-claude/prompt-caching> | Covered by unit/mock tests; this PR does not include Anthropic live smoke |
| Gemini / Vertex AI | Official source field: `UsageMetadata.cachedContentTokenCount`; runtime consumes LiteLLM-exposed snake_case / normalized fields such as `cached_content_token_count`, `cache_read_input_tokens`, or `prompt_tokens_details.cached_tokens` | Gemini `UsageMetadata` official field: <https://ai.google.dev/api/generate-content#UsageMetadata>. This repository does not add native camelCase runtime fallback; runtime compatibility is bounded to `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` | Covered by unit/mock tests; this PR does not include Gemini / Vertex live smoke |
| Gemini / Vertex AI | Official source field: `UsageMetadata.cachedContentTokenCount`; runtime consumes LiteLLM-exposed snake_case / normalized fields such as `cached_content_token_count`, `cache_read_input_tokens`, or `prompt_tokens_details.cached_tokens` | Gemini `UsageMetadata` official field: <https://ai.google.dev/api/generate-content#UsageMetadata>. This repository does not add native camelCase runtime fallback; runtime compatibility is bounded to `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` | Covered by unit/mock tests; this PR does not include Gemini / Vertex live smoke |
| DeepSeek | `prompt_cache_hit_tokens` / `prompt_cache_miss_tokens` | DeepSeek Chat Completion docs state that `prompt_tokens = prompt_cache_hit_tokens + prompt_cache_miss_tokens`: <https://api-docs.deepseek.com/api/create-chat-completion> | Covered by unit/mock tests; this PR includes one redacted DeepSeek smoke only and does not store the full response |
| GLM / OpenAI-compatible / StepFun and similar compatible platforms | Values from the modeled token/cache count allowlist that can be normalized to common fields | No stable official cache telemetry contract is claimed here; this is best-effort normalization under the current LiteLLM / OpenAI-compatible shape. Unmodeled metadata is not persisted | Covered by unit/fixture/mock tests; this PR does not include live smoke for these providers |
| LiteLLM public response shape | `usage` / `usage_metadata` | Consumed according to the response / `Usage` object shape in the current dependency window `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`; this is not a LiteLLM 2.x compatibility guarantee | Covered by Analyzer / Agent / usage tests |
| LiteLLM public response shape | `usage` / `usage_metadata` | Consumed according to the response / `Usage` object shape in the current dependency window `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`; this is not a LiteLLM 2.x compatibility guarantee | Covered by Analyzer / Agent / usage tests |
| LiteLLM private fallback | `_hidden_params["usage"]` | Private/internal best-effort fallback, not a stable LiteLLM public contract. It only fills narrow streaming telemetry gaps such as public zero-only/no-signal usage and does not change provider request parameters | Covered by unit/mock tests; absence only affects telemetry completeness, not model request success |
```env

View File

@@ -204,7 +204,7 @@ class CommandDispatcher:
- `LLM_CHANNELS`
- legacy provider 键(`GEMINI_API_KEY` / `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / `DEEPSEEK_API_KEY`
- 当主模型(`LITELLM_MODEL``AGENT_LITELLM_MODEL`在当前激活层无可用来源时会展示“AI 服务未配置”,并保留用户可见原因行。
- 本仓库 `requirements.txt` 的运行时依赖约束为 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`,该约束内本链路以现有兼容行为为准。
- 本仓库 `requirements.txt` 的运行时依赖约束为 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`,该约束内本链路以现有兼容行为为准。
- 该诊断规则与 `GET /api/v1/system/config/setup/status` 的 LLM 检查保持一致:`LITELLM_CONFIG`/`LLM_CHANNELS` 为高优先级;模式切换时不会做静默迁移,切回旧模式由用户显式恢复历史值或回滚。
### 回退与迁移边界

View File

@@ -173,7 +173,7 @@ class BotCommand(ABC):
- `LLM_CHANNELS`
- legacy provider keys (`GEMINI_API_KEY` / `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / `DEEPSEEK_API_KEY`)
- If the primary model (`LITELLM_MODEL` or `AGENT_LITELLM_MODEL`) has no configured source in the active layer, `/status` shows `AI 服务未配置` and keeps the explicit reason line.
- Runtime dependency constraint in this repository is `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`; current status semantics are aligned with this constraint.
- Runtime dependency constraint in this repository is `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`; current status semantics are aligned with this constraint.
- This diagnostic follows the same readiness rules as `GET /api/v1/system/config/setup/status` for LLM checks: channels/yaml are active higher priority than legacy keys, and no silent migration is performed when toggling modes.
### Fallback and migration boundary

View File

@@ -852,7 +852,7 @@ docker run -e SCHEDULE_ENABLED=true -e SCHEDULE_RUN_IMMEDIATELY=false ...
> 兼容说明Issue #1815`MARKET_REVIEW_REGION=cn|hk|us|jp|kr|both` 仅扩展大盘复盘输入集合JP/KR 仅供复盘上下文消费,不会放开 Market Light 告警。
> - `src/config.py`、`src/core/config_registry.py`、`src/services/system_config_service.py` 的改动仅是配置语义扩展,不改 `provider`/`model`/`base_url` 的运行时路由,也不触发 provider/model/base URL 迁移或清理逻辑。
> - 本轮实际受控配置项:`MARKET_REVIEW_REGION`、`MARKET_REVIEW_COLOR_SCHEME``LITELLM_MODEL`、`AGENT_LITELLM_MODEL`、`LITELLM_FALLBACK_MODELS`、`VISION_MODEL`、`OPENAI_BASE_URL` 等旧值保持原子 upsert 语义,不会在更新其他字段时被静默清空或覆盖。
> - 可核验证据摘要:官方 provider / Base URL / 模型命名来源沿用 [LLM 配置指南](LLM_CONFIG_GUIDE.md#常用官方文档来源用于核对预设-provider--base-url--模型命名),当前运行时依赖窗口沿用 `requirements.txt` 中的 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`;本轮不新增配置迁移脚本或清理分支,保存/导入仍只写本次提交键。`tests/test_system_config_service.py::SystemConfigServiceTestCase::test_update_market_review_region_does_not_trigger_runtime_model_cleanup` 覆盖只保存 `MARKET_REVIEW_REGION` 时不清空或改写 `LITELLM_CONFIG`、`LLM_CHANNELS`、`LLM_OPENAI_*`、`LITELLM_MODEL`、`AGENT_LITELLM_MODEL`、`LITELLM_FALLBACK_MODELS`、`VISION_MODEL`、`OPENAI_*` 等旧配置。
> - 可核验证据摘要:官方 provider / Base URL / 模型命名来源沿用 [LLM 配置指南](LLM_CONFIG_GUIDE.md#常用官方文档来源用于核对预设-provider--base-url--模型命名),当前运行时依赖窗口沿用 `requirements.txt` 中的 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`;本轮不新增配置迁移脚本或清理分支,保存/导入仍只写本次提交键。`tests/test_system_config_service.py::SystemConfigServiceTestCase::test_update_market_review_region_does_not_trigger_runtime_model_cleanup` 覆盖只保存 `MARKET_REVIEW_REGION` 时不清空或改写 `LITELLM_CONFIG`、`LLM_CHANNELS`、`LLM_OPENAI_*`、`LITELLM_MODEL`、`AGENT_LITELLM_MODEL`、`LITELLM_FALLBACK_MODELS`、`VISION_MODEL`、`OPENAI_*` 等旧配置。
> - 旧值回退策略:先恢复备份 `MARKET_REVIEW_REGION` 与配置文件即可回到旧边界,未提交的模型/路由键保留原值;必要时 `revert` PR 并按 `.env` 备份完成回退。
> - 可回滚路径:恢复提交前 `.env` / 配置备份中的 `MARKET_REVIEW_REGION` 与相关运行时变量,或直接 revert 本 PR。
@@ -1691,7 +1691,7 @@ FastAPI 提供 RESTful API 服务,支持配置管理和触发分析。
> 兼容性审计证据:
> - 官方来源LiteLLM OpenAI-compatible provider 文档 <https://docs.litellm.ai/docs/providers/openai_compatible>OpenAI Chat API 文档 <https://platform.openai.com/docs/api-reference/chat/create>DeepSeek API 文档 <https://api-docs.deepseek.com/>。
> - 依赖版本:项目约束为 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`(见 `requirements.txt`),以上兼容语义回归测试在该版本窗口内执行。
> - 依赖版本:项目约束为 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`(见 `requirements.txt`),以上兼容语义回归测试在该版本窗口内执行。
> - 可复核测试:
> - `tests/test_llm_channel_config.py`(配置源优先级与 provider/base url 映射)
> - `tests/test_market_review_runtime.py``build_market_review_runtime` 复用装配路径)

View File

@@ -438,7 +438,7 @@ For the notification baseline, diagnostics, and deployment notes, see [Notificat
> Compatibility note (Issue #1815): `MARKET_REVIEW_REGION=cn|hk|us|jp|kr|both` only expands the market set used by market review; `jp`/`kr` are for recap scope and do not open JP/KR for Market Light alerts.
> - Changes in `src/config.py`, `src/core/config_registry.py`, and `src/services/system_config_service.py` are configuration-contract updates only, and do not alter runtime provider/model/base URL routing semantics or trigger provider migration/cleanup logic.
> - Affected config keys are `MARKET_REVIEW_REGION` and `MARKET_REVIEW_COLOR_SCHEME`; existing model/runtime keys (`LITELLM_MODEL`, `AGENT_LITELLM_MODEL`, `LITELLM_FALLBACK_MODELS`, `VISION_MODEL`, `OPENAI_BASE_URL`, etc.) remain unchanged under the existing atomic upsert semantics and are not silently cleared when this scope is changed.
> - Verifiable evidence summary: official provider / Base URL / model-name sources remain the [LLM Config Guide](LLM_CONFIG_GUIDE_EN.md#official-references-for-provider-presets--base-urls--model-naming), and the locked runtime dependency window remains `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` in `requirements.txt`; this scope adds no migration script or cleanup branch, and save/import still writes only submitted keys. `tests/test_system_config_service.py::SystemConfigServiceTestCase::test_update_market_review_region_does_not_trigger_runtime_model_cleanup` covers saving `MARKET_REVIEW_REGION` without clearing or rewriting existing `LITELLM_CONFIG`, `LLM_CHANNELS`, `LLM_OPENAI_*`, `LITELLM_MODEL`, `AGENT_LITELLM_MODEL`, `LITELLM_FALLBACK_MODELS`, `VISION_MODEL`, `OPENAI_*`, and related runtime settings.
> - Verifiable evidence summary: official provider / Base URL / model-name sources remain the [LLM Config Guide](LLM_CONFIG_GUIDE_EN.md#official-references-for-provider-presets--base-urls--model-naming), and the locked runtime dependency window remains `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` in `requirements.txt`; this scope adds no migration script or cleanup branch, and save/import still writes only submitted keys. `tests/test_system_config_service.py::SystemConfigServiceTestCase::test_update_market_review_region_does_not_trigger_runtime_model_cleanup` covers saving `MARKET_REVIEW_REGION` without clearing or rewriting existing `LITELLM_CONFIG`, `LLM_CHANNELS`, `LLM_OPENAI_*`, `LITELLM_MODEL`, `AGENT_LITELLM_MODEL`, `LITELLM_FALLBACK_MODELS`, `VISION_MODEL`, `OPENAI_*`, and related runtime settings.
> - Rollback is a restore-and-recover path: apply pre-PR `.env` / config backup for the above keys, restore `MARKET_REVIEW_REGION`, and restart the runtime; or revert this PR directly.
> - CN market review reports now use a post-market workstation layout with market signal, index detail, sector Top tables, news catalysts, next-session plan, and risk sections. The market signal uses a plain-text score such as `66/100 (constructive, risk-on)` instead of block bars so it renders consistently across terminals and notification clients. News catalysts list only headline, source, and link instead of search snippets to reduce mixed-language noise. Missing data sources degrade by omitting or simplifying only the affected block.
> - Per-stock analysis, realtime quote priority, and sector rankings fallback remain unchanged.
@@ -1524,7 +1524,7 @@ For this feature, the product behavior is:
> Compatibility audit evidence:
> - Official references: LiteLLM OpenAI-compatible provider documentation <https://docs.litellm.ai/docs/providers/openai_compatible>, OpenAI Chat API <https://platform.openai.com/docs/api-reference/chat/create>, and DeepSeek API docs <https://api-docs.deepseek.com/>.
> - Dependency boundary: this repo currently pins `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` (see `requirements.txt`); the compatibility regressions for this path were verified under that dependency window.
> - Dependency boundary: this repo currently pins `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` (see `requirements.txt`); the compatibility regressions for this path were verified under that dependency window.
> - Verifiable tests:
> - `tests/test_llm_channel_config.py` (configuration priority and provider/base URL mapping)
> - `tests/test_market_review_runtime.py` (`build_market_review_runtime` shared assembly path)

View File

@@ -4,7 +4,7 @@
> 本页未引入新的外部 provider、模型名或 Base URL 兼容行为,仅整理配置参考与官方来源;实际兼容性仍以仓库当前运行时依赖与测试结论为准。
> - 运行时基础:`requirements.txt` 当前锁定 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`,兼容语义以该版本约束下实现为准。
> - 运行时基础:`requirements.txt` 当前锁定 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`,兼容语义以该版本约束下实现为准。
> - 验证闭环:系统配置链路回归见 `tests/test_system_config_service.py` 与 `tests/test_system_config_api.py``Web` 侧配置页交互回归见现有组件测试用例。
> - 回退路径:保留旧变量不做自动迁移;可通过 Web/桌面导出备份后 `POST /api/v1/system/config/import` 回滚,或手动恢复历史 `LLM_*` / `LITELLM_*` / `AGENT_*` / `VISION_MODEL` 配置。
@@ -141,7 +141,7 @@ LITELLM_FALLBACK_MODELS=openai/gpt-5.6-terra,openai/gpt-5.6-luna
| OpenRouter | [Models API](https://openrouter.ai/docs/api/api-reference/models/get-models) | OpenRouter 支持 `~anthropic/claude-sonnet-latest``~openai/gpt-latest` 等 latest router alias2026-05-03 的一次手动 live smoke 以 Claude Sonnet latest 作为默认示例通过GPT latest 保留为可按账号权限切换的备选。 |
| LiteLLM | [OpenAI-Compatible Endpoints](https://docs.litellm.ai/docs/providers/openai_compatible) | OpenAI-compatible 端点需要把运行时模型写成 `openai/<model>`Base URL 只填到服务商兼容入口,不额外拼接 `/chat/completions`。 |
本页预设只保证配置形状与当前依赖的 OpenAI-compatible 路由规则一致;实际连通性仍取决于服务商账号权限、地域、额度和模型开通状态。当前 LiteLLM 版本约束为 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`(见 `requirements.txt`),保留历史最低版本、显式排除 PyPI 事故版本,并避免未来大版本自动进入。
本页预设只保证配置形状与当前依赖的 OpenAI-compatible 路由规则一致;实际连通性仍取决于服务商账号权限、地域、额度和模型开通状态。当前 LiteLLM 版本约束为 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`(见 `requirements.txt`),保留历史最低版本、显式排除 PyPI 事故版本,并将上界收敛到已验证的 `<1.99.0`避免未来大版本自动进入。
## OpenAI-compatible 与 LiteLLM 规则
@@ -226,7 +226,7 @@ Phase 3 只支持普通分析 / JSON generation不支持 stream/SSE、tools
- SiliconFlow 官方错误处理文档要求接口错误排查时记录 HTTP 错误码和 `message`,说明 403 表示余额不足或权限不够,其他情况参考报错 `message`,并建议换一个模型确认问题是否仍存在(中文:<https://docs.siliconflow.cn/cn/faqs/error-code>;英文:<https://docs.siliconflow.cn/en/faqs/error-code>)。
- Issue #1208 中真实脱敏样例来自 SiliconFlow / OpenAI Compatible 渠道测试,经 LiteLLM 返回 `litellm.APIError: APIError: OpenAIException - Model disabled.`
- 线上复核记录2026-05-06T16:21:21Z`litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` 约束下,本地验证环境为 Python `3.13.12`、LiteLLM `1.82.3`、Base URL `https://api.siliconflow.cn/v1`、模型 `Qwen/Qwen3-235B-A22B-Thinking-2507`。直连 SiliconFlow Chat Completions 返回 HTTP `403`,响应体为 `{"code":30003,"message":"Model disabled.","data":null}`;同一模型通过 LiteLLM `completion(model="openai/Qwen/Qwen3-235B-A22B-Thinking-2507")` 返回 `APIError: OpenAIException - Model disabled.`
- 线上复核记录2026-05-06T16:21:21Z`litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` 约束下,本地验证环境为 Python `3.13.12`、LiteLLM `1.82.3`、Base URL `https://api.siliconflow.cn/v1`、模型 `Qwen/Qwen3-235B-A22B-Thinking-2507`。直连 SiliconFlow Chat Completions 返回 HTTP `403`,响应体为 `{"code":30003,"message":"Model disabled.","data":null}`;同一模型通过 LiteLLM `completion(model="openai/Qwen/Qwen3-235B-A22B-Thinking-2507")` 返回 `APIError: OpenAIException - Model disabled.`
因此当前运行时把该已观测 provider `message` 作为 best-effort 模型可用性诊断,而不是把它声明为官方跨 provider 错误码。实现仅在错误文本同时包含 `model` 和明确权限、禁用或不可用信号时进入该诊断;未覆盖或语义不同的 provider 文案会继续走既有兜底诊断。`provider_blocked` 同样是基于明确拦截文案的 best-effort 诊断,用于区分服务商/网关策略拦截与本地网络、TLS 或模型不可用问题。
@@ -237,7 +237,7 @@ Phase 3 只支持普通分析 / JSON generation不支持 stream/SSE、tools
- 检测结果只代表当前账号、模型和 endpoint 的一次 best-effort 运行时结果。
- 检测结果不会写回 `.env`,也不会阻止保存配置。
- 能力检测失败不等于 provider 全局不支持失败可能来自账号权限、模型未开通、endpoint 区域、余额、服务商兼容层或 LiteLLM 转换路径。
- 当前实现未对所有真实 provider 做在线 smoke兼容依据是 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0`(见 `requirements.txt`)、[LiteLLM Python SDK / OpenAI I/O format](https://docs.litellm.ai/)、[LiteLLM OpenAI-compatible 路由](https://docs.litellm.ai/docs/providers/openai_compatible),以及 OpenAI Chat Completions 的 [JSON mode](https://platform.openai.com/docs/guides/structured-outputs?api-mode=chat)、[tool calling](https://platform.openai.com/docs/guides/function-calling?api-mode=chat)、[streaming](https://platform.openai.com/docs/guides/streaming-responses?api-mode=chat) 和 [vision input](https://platform.openai.com/docs/guides/images-vision?api-mode=chat) 请求形状。
- 当前实现未对所有真实 provider 做在线 smoke兼容依据是 `litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0`(见 `requirements.txt`)、[LiteLLM Python SDK / OpenAI I/O format](https://docs.litellm.ai/)、[LiteLLM OpenAI-compatible 路由](https://docs.litellm.ai/docs/providers/openai_compatible),以及 OpenAI Chat Completions 的 [JSON mode](https://platform.openai.com/docs/guides/structured-outputs?api-mode=chat)、[tool calling](https://platform.openai.com/docs/guides/function-calling?api-mode=chat)、[streaming](https://platform.openai.com/docs/guides/streaming-responses?api-mode=chat) 和 [vision input](https://platform.openai.com/docs/guides/images-vision?api-mode=chat) 请求形状。
## 回滚方式

View File

@@ -132,7 +132,7 @@ Portfolio 允许 JP/KR 账户、交易和持仓快照进入现有链路,但会
- Web 设置页中 `MARKET_REVIEW_REGION` 从固定枚举下拉收敛为自由文本输入,用于保存 `cn,us,jp``cn,hk,us` 等逗号分隔子集;该 UI 变化只影响大盘复盘配置,不影响 Market Light 告警市场枚举。
- `MARKET_REVIEW_REGION` 既有 `cn``hk``us` 可原样保留;若用户希望维持 JP/KR 扩展前 `both` 对应的三市场复盘边界,应改为 `cn,hk,us`;只有希望纳入五市场复盘时才继续使用 `both` 或显式配置 `cn,hk,us,jp,kr`
- 该轮边界收敛不改动 LLM Provider / Model / Base URL 的持久化语义,也不执行默认模型、运行时配置清理或回写;配置更新仍是**原子 upsert**`ConfigManager.apply_updates`),保存/导入只写入提交的键,未提交的 `LITELLM_MODEL``LITELLM_FALLBACK_MODELS``AGENT_LITELLM_MODEL``VISION_MODEL``OPENAI_BASE_URL` 等旧值保留不清空。
- 可直接核验的配置兼容证据:本轮未新增或替换外部 provider/model/Base URL仍沿用 LiteLLM OpenAI-compatible 路由(<https://docs.litellm.ai/docs/providers/openai_compatible>、OpenAI Chat Completions 请求形状(<https://platform.openai.com/docs/api-reference/chat/create>),以及 [LLM 服务商配置指南](llm-providers.md#官方来源与兼容性) 中集中维护的各 provider 官方来源链接。当前运行时依赖窗口以 `requirements.txt``litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0` 为准;旧配置没有迁移脚本或清理分支,保存/导入仍只通过 `ConfigManager.apply_updates` 写入本次提交键。回退路径是恢复变更前 `.env`/配置备份中的 `MARKET_REVIEW_REGION`,或直接 revert 本 PR未提交的 `LITELLM_CONFIG``LLM_CHANNELS``LLM_OPENAI_*``LITELLM_MODEL``AGENT_LITELLM_MODEL``LITELLM_FALLBACK_MODELS``VISION_MODEL``OPENAI_*` 等既有运行时配置不需要迁移。回归证据为 `tests/test_system_config_service.py::SystemConfigServiceTestCase::test_update_market_review_region_does_not_trigger_runtime_model_cleanup``tests/test_config_env_compat.py::test_market_review_region_updates_do_not_change_llm_provider_model_contract`
- 可直接核验的配置兼容证据:本轮未新增或替换外部 provider/model/Base URL仍沿用 LiteLLM OpenAI-compatible 路由(<https://docs.litellm.ai/docs/providers/openai_compatible>、OpenAI Chat Completions 请求形状(<https://platform.openai.com/docs/api-reference/chat/create>),以及 [LLM 服务商配置指南](llm-providers.md#官方来源与兼容性) 中集中维护的各 provider 官方来源链接。当前运行时依赖窗口以 `requirements.txt``litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0` 为准;旧配置没有迁移脚本或清理分支,保存/导入仍只通过 `ConfigManager.apply_updates` 写入本次提交键。回退路径是恢复变更前 `.env`/配置备份中的 `MARKET_REVIEW_REGION`,或直接 revert 本 PR未提交的 `LITELLM_CONFIG``LLM_CHANNELS``LLM_OPENAI_*``LITELLM_MODEL``AGENT_LITELLM_MODEL``LITELLM_FALLBACK_MODELS``VISION_MODEL``OPENAI_*` 等既有运行时配置不需要迁移。回归证据为 `tests/test_system_config_service.py::SystemConfigServiceTestCase::test_update_market_review_region_does_not_trigger_runtime_model_cleanup``tests/test_config_env_compat.py::test_market_review_region_updates_do_not_change_llm_provider_model_contract`
- Web UI 可视证据口径Market Light 告警目标范围切到“大盘市场”时,市场区域下拉只显示 A 股、港股、美股,不显示日股/韩股;设置页 `MARKET_REVIEW_REGION` 渲染为可输入逗号分隔值的文本框。当前仓库不保存一次性截图证据,可替代证据为 `apps/dsa-web/src/components/alerts/__tests__/AlertRuleForm.test.tsx``apps/dsa-web/src/components/settings/__tests__/SettingsField.test.tsx``apps/dsa-web/tests/system_config_i18n.test.ts` 的断言。
回滚方式:移除 Portfolio snapshot 的 `data_quality` / `limitations` 扩展,恢复告警前端/后端对市场枚举的旧边界说明;如需整体回滚,移除 `jp/kr` 市场识别、交易日历注册、YFinance 路由扩展、Web/API 类型放行、`scripts/stock_index_seeds/` 日韩种子索引,并删除本文档中的能力声明。

View File

@@ -38,7 +38,7 @@ orjson>=3.10,<4 # LiteLLM runtime JSON dependency; explicitly bundle
# AI analysis
# Keep the historical minimum version while excluding quarantined builds and avoiding future major breaks.
litellm>=1.80.10,!=1.82.7,!=1.82.8,<2.0.0 # Unified LLM client (Gemini/Anthropic/OpenAI/DeepSeek etc.)
litellm>=1.80.10,!=1.82.7,!=1.82.8,<1.99.0 # Unified LLM client (Gemini/Anthropic/OpenAI/DeepSeek etc.)
tiktoken>=0.8.0,<0.12.0 # BPE tokenizer for LLM token counting (pin <0.12 to avoid plugin registration issues, #537)
openai>=1.0.0 # OpenAI SDK (transitive dependency of litellm, kept explicit)
PyYAML>=6.0 # YAML parser for LITELLM_CONFIG support