Skip to content

Commit 39e260c

Browse files
committed
fix(review-feedback-933): address latest review comments
1 parent b9fea9a commit 39e260c

9 files changed

Lines changed: 139 additions & 27 deletions

docs/CHANGELOG.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7070
- [改进] 🔎 **SerpAPI 正文补抓范围收敛** — 自然搜索结果不再逐条同步抓取网页正文;现在仅对极少数高位且摘要明显不足的结果,在更短超时预算内做延迟补抓,并优先复用 SerpAPI 已返回的结构化摘要,降低搜索链路尾延迟与慢站点放大风险。
7171
- [修复] A 股和中文股票名称场景下的相关资讯搜索恢复中文优先策略:`search_stock_news()` 现在会在首个 provider 主要返回英文资讯时继续尝试后续引擎,并将同批结果中的中文资讯排到前面;同时非美股查询不再默认沿用 Brave 的 `en/US` 区域语言偏好,避免更新后被英文新闻结果占满。
7272
- [修复] 飞书群机器人通知现在支持 `FEISHU_WEBHOOK_SECRET` / `FEISHU_WEBHOOK_KEYWORD`,并在 Web 设置与文档中明确区分 Webhook 推送和 `FEISHU_APP_ID` / `FEISHU_APP_SECRET` 应用模式,降低误配导致的推送失败。
73-
- [修复] `LLM_CHANNELS` 现在会在常见 provider 渠道缺少 `LLM_{NAME}_API_KEY(S)` 时,安全回退读取匹配的 legacy Secrets(如 `DEEPSEEK_API_KEY``AIHUBMIX_KEY``OPENAI_API_KEY``GEMINI_API_KEY``ANTHROPIC_API_KEY`),让 GitHub Actions 可继续使用默认 `.env` 的非敏感渠道结构。
74-
- [修复] `LLM_CHANNELS` 现在会在常见 provider 渠道缺少 `LLM_{NAME}_API_KEY(S)` 时,安全回退读取匹配的 legacy Secrets(如 `DEEPSEEK_API_KEY``AIHUBMIX_KEY``OPENAI_API_KEY``GEMINI_API_KEY``ANTHROPIC_API_KEY`),让 GitHub Actions 可继续使用默认 `.env` 的非敏感渠道结构。当渠道配置了自定义 `base_url` 时,仅通过严格域名校验匹配 legacy 密钥,防止凭据泄露到非官方端点。
75-
- [修复] `LLM_CHANNELS` 现在会在常见 provider 渠道缺少 `LLM_{NAME}_API_KEY(S)` 时,安全回退读取匹配的 legacy Secrets(如 `DEEPSEEK_API_KEY``AIHUBMIX_KEY``OPENAI_API_KEY``GEMINI_API_KEY``ANTHROPIC_API_KEY`),让 GitHub Actions 可继续使用默认 `.env` 的非敏感渠道结构。当渠道配置了自定义 `base_url` 时,仅当 URL 使用 HTTPS 协议且通过严格域名校验时才匹配 legacy 密钥,防止凭据通过明文 HTTP 或非官方端点泄露。
76-
- [文档] 更新中英文 LLM 配置指南与 FAQ,补充 GitHub Actions 下渠道模式复用 legacy Secrets 的用法,并明确自定义渠道仍建议使用 `LITELLM_CONFIG` + `LITELLM_CONFIG_YAML`
73+
- [修复] `LLM_CHANNELS` 现在会在常见 provider 渠道缺少 `LLM_{NAME}_API_KEY(S)` 时,按 provider/protocol 严格匹配并回退读取 legacy Secrets(如 `DEEPSEEK_API_KEY``AIHUBMIX_KEY``OPENAI_API_KEY``GEMINI_API_KEY``ANTHROPIC_API_KEY`)。仅当 `base_url` 为 HTTPS 且通过域名白名单校验时,或未配置自定义 `base_url` 且与 `LLM_{NAME}_PROTOCOL` 一致时才回退,避免密钥落到不可信端点,兼容 GitHub Actions 复用既有 Secrets。
74+
- [文档] 更新中英文 LLM 配置指南与 FAQ,补充 GitHub Actions 下渠道模式复用 legacy Secrets 的用法,并明确自定义渠道建议改用 `LITELLM_CONFIG`(高级模型路由配置文件路径)承载自定义配置。
7775

7876
## [3.11.0] - 2026-03-27
7977

@@ -415,7 +413,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
415413
- 🤖 **Agent background execution** (#495) — analysis continues when switching pages; badge notification on completion; auto-cancel in-progress stream on session switch
416414
- 📝 **Report Engine P0** — Pydantic schema validation for LLM JSON; Jinja2 templates (markdown/wechat/brief) with legacy fallback; content integrity checks with retry; brief mode (`REPORT_TYPE=brief`); history signal comparison
417415
- 📦 **Smart import** — multi-source import from image/CSV/Excel/clipboard; Vision LLM extracts code+name+confidence; name→code resolver (local map + pinyin + AkShare); confidence-tiered confirmation
418-
- ⚙️ **GitHub Actions LiteLLM config** — workflow supports `LITELLM_CONFIG`/`LITELLM_CONFIG_YAML` for flexible AI provider configuration
416+
- ⚙️ **GitHub Actions LiteLLM config** — workflow supports `LITELLM_CONFIG` as the custom routing configuration file path for AI provider setup
419417
- ⚙️ **Config engine refactor & system API** (#602) — unified config registry, validation and API exposure
420418
- 📖 **LLM configuration guide** — new `docs/LLM_CONFIG_GUIDE.md` covering 3-tier config, quick start, Vision/Agent/troubleshooting
421419

docs/FAQ.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@
9191
- `REPORT_TYPE`
9292
- `LLM_CHANNELS``LLM_{NAME}_BASE_URL``LLM_{NAME}_MODELS`
9393

94-
如果你在 GitHub Actions 使用 `LLM_CHANNELS=deepseek,aihubmix` 这类常见 provider 渠道,不必把真实 `LLM_DEEPSEEK_API_KEY` / `LLM_AIHUBMIX_API_KEY` 写进默认 `.env`;把真实 Key 放在 `DEEPSEEK_API_KEY``AIHUBMIX_KEY`(或 `OPENAI_API_KEY`)即可。自定义渠道名仍建议改用 `LITELLM_CONFIG` + `LITELLM_CONFIG_YAML`
94+
如果你在 GitHub Actions 使用 `LLM_CHANNELS=deepseek,aihubmix` 这类常见 provider 渠道,不必把真实 `LLM_DEEPSEEK_API_KEY` / `LLM_AIHUBMIX_API_KEY` 写进默认 `.env`;把真实 Key 放在 `DEEPSEEK_API_KEY``AIHUBMIX_KEY`(或 `OPENAI_API_KEY`)即可。自定义渠道名仍建议改用 `LITELLM_CONFIG` 指向 YAML 配置文件路径
9595

9696
---
9797

docs/FAQ_EN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ This document compiles common issues encountered by users and their solutions.
8989
- `REPORT_TYPE`
9090
- `LLM_CHANNELS`, `LLM_{NAME}_BASE_URL`, `LLM_{NAME}_MODELS`
9191

92-
If you run `LLM_CHANNELS=deepseek,aihubmix` in GitHub Actions, you do not need to put real `LLM_DEEPSEEK_API_KEY` / `LLM_AIHUBMIX_API_KEY` values into the default `.env`. Put the real keys in `DEEPSEEK_API_KEY` and `AIHUBMIX_KEY` (or `OPENAI_API_KEY`) instead. Custom channel names should still use `LITELLM_CONFIG` + `LITELLM_CONFIG_YAML`.
92+
If you run `LLM_CHANNELS=deepseek,aihubmix` in GitHub Actions, you do not need to put real `LLM_DEEPSEEK_API_KEY` / `LLM_AIHUBMIX_API_KEY` values into the default `.env`. Put the real keys in `DEEPSEEK_API_KEY` and `AIHUBMIX_KEY` (or `OPENAI_API_KEY`) instead. Custom channel names should use `LITELLM_CONFIG` (path to YAML config).
9393

9494
---
9595

docs/LLM_CONFIG_GUIDE.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ LITELLM_MODEL=ollama/qwen3:8b
121121
122122
> **GitHub Actions 兼容说明**:渠道模式仍优先于 legacy 模式,但常见 provider 渠道在缺少 `LLM_{NAME}_API_KEY(S)` 时,会按渠道名/官方域名安全回退读取已有 Secrets:`deepseek -> DEEPSEEK_API_KEY(S)``aihubmix -> AIHUBMIX_KEY / OPENAI_API_KEY(S)``openai -> OPENAI_API_KEY(S)``gemini/vertex -> GEMINI_API_KEY(S)``anthropic/claude -> ANTHROPIC_API_KEY(S)`。这意味着你可以继续使用默认 `.env` 中的非敏感渠道结构,把真实 Key 放在 GitHub Secrets 里,而不用把真实 `LLM_DEEPSEEK_API_KEY``LLM_AIHUBMIX_API_KEY` 写进仓库配置。
123123
>
124-
> **边界说明**:这个回退只覆盖常见 provider 渠道名;像 `my_proxy``corp_gateway` 这类自定义渠道名在 GitHub Actions 中仍建议改用 `LITELLM_CONFIG` + `LITELLM_CONFIG_YAML`,避免 secret 名与渠道名无法自动对齐。
124+
> **边界说明**:这个回退只覆盖常见 provider 渠道名;像 `my_proxy``corp_gateway` 这类自定义渠道名在 GitHub Actions 中仍建议改用 `LITELLM_CONFIG`(指向 YAML 配置文件),避免 secret 名与渠道名无法自动对齐。
125125
126126
---
127127

@@ -189,7 +189,6 @@ LITELLM_FALLBACK_MODELS=openai/gpt-4o-mini,anthropic/claude-3-5-sonnet
189189
|------------|------|:----:|
190190
| `LITELLM_CONFIG` | 高级模型路由配置文件路径,通常配置 `./litellm_config.yaml` | 必填 |
191191
| `LITELLM_MODEL` | 默认主模型名称或路由别名 | 必填 |
192-
| `LITELLM_CONFIG_YAML` | 存放 YAML 配置文件内容,可不在仓库中提交实体文件 | 可选 |
193192
| `LITELLM_API_KEY` | 用于存储API Key,可在配置文件中引用(环境变量引用方式)。由于GitHub Actions必须要指定导入的环境变量,因此你不能像本地运行模式那样自由命名环境变量 | 可选,必须配置到repository secret中 |
194193
| `ANTHROPIC_API_KEY` | 如果要多个API Key,这个变量名称也能拿来用 | 可选,必须配置到repository secret中 |
195194
| `OPENAI_API_KEY` | 同上,可以用来存储API Key | 可选,必须配置到repository secret中 |
@@ -230,7 +229,7 @@ VISION_PROVIDER_PRIORITY=gemini,anthropic,openai
230229
| 遇到了什么诡异报错? | 罪魁祸首可能是啥? | 该怎么收拾它? |
231230
|----------------------|----------------------|------------------|
232231
| **界面提示主模型未配置** | 系统不知道你到底想用哪家的哪个模型 | 在 `.env` 中写上一句明白话:`LITELLM_MODEL=provider/你的模型名`。比如 `openai/gpt-4o-mini` |
233-
| **我写了好几家的Key,为什么死活只有一个生效?修改还没用?** | 大概率是模式混用,或渠道名和 Secret 名没对齐。 | 本地 / Docker 请优先直接写 `LLM_{NAME}_API_KEY(S)`;GitHub Actions 下,`deepseek` / `aihubmix` / `openai` / `gemini` / `anthropic` 等常见渠道可复用对应 legacy Secrets;自定义渠道请改用 `LITELLM_CONFIG` + `LITELLM_CONFIG_YAML`。 |
232+
| **我写了好几家的Key,为什么死活只有一个生效?修改还没用?** | 大概率是模式混用,或渠道名和 Secret 名没对齐。 | 本地 / Docker 请优先直接写 `LLM_{NAME}_API_KEY(S)`;GitHub Actions 下,`deepseek` / `aihubmix` / `openai` / `gemini` / `anthropic` 等常见渠道可复用对应 legacy Secrets;自定义渠道请改用 `LITELLM_CONFIG`(指向 YAML 配置文件)。 |
234233
| **错误码报 400 或 401 或 Invalid API Key** | API Key 填错、少复制了一截、账号充值没到账、或者模型名字敲错(极度常见)。 | 1. 检查复制的 Key 前后是否有误填空格。<br> 2. 检查 Base URL 最后是不是少了一个 `/v1`。<br> 3. 检查模型名是否少写了 `openai/` 之类的前缀! |
235234
| **转圈转不停,最后报 Timeout / ConnectionRefused 等** | 1. 在国内使用国外原版(像 Google、OpenAI),没开代理被墙了。<br>2. 你买的云服务器压根不能出境。 | 非常推荐使用**国内官方**(如DeepSeek、阿里)或者各种**兼容 OpenAI 的聚合中转接口**。因为中转站把网络问题帮你解决好了。 |
236235
| **Ollama 报 404、`Could not get model info` 或 `api/generate/api/show`** | 误用 `OPENAI_BASE_URL` 配置 Ollama,系统会错误拼接 URL | 改用 `OLLAMA_API_BASE=http://localhost:11434` 或渠道模式(`LLM_CHANNELS=ollama` + `LLM_OLLAMA_BASE_URL`) |

docs/LLM_CONFIG_GUIDE_EN.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ LITELLM_MODEL=ollama/qwen3:8b
121121
122122
> **GitHub Actions Compatibility**: Channels mode still wins over legacy mode, but well-known provider channels can now safely reuse existing Secrets when `LLM_{NAME}_API_KEY(S)` is absent: `deepseek -> DEEPSEEK_API_KEY(S)`, `aihubmix -> AIHUBMIX_KEY / OPENAI_API_KEY(S)`, `openai -> OPENAI_API_KEY(S)`, `gemini/vertex -> GEMINI_API_KEY(S)`, `anthropic/claude -> ANTHROPIC_API_KEY(S)`. This lets you keep only the non-sensitive channel structure in the default `.env` and store real keys in GitHub Secrets instead of writing real `LLM_DEEPSEEK_API_KEY` / `LLM_AIHUBMIX_API_KEY` values into repo config.
123123
>
124-
> **Boundary**: This fallback only applies to common provider channel names / official hosts. Custom channel names such as `my_proxy` or `corp_gateway` should still use `LITELLM_CONFIG` + `LITELLM_CONFIG_YAML` in GitHub Actions.
124+
> **Boundary**: This fallback only applies to common provider channel names / official hosts. Custom channel names such as `my_proxy` or `corp_gateway` should still use `LITELLM_CONFIG` (YAML config path) in GitHub Actions.
125125
126126
---
127127

@@ -181,7 +181,7 @@ Store the real keys in GitHub Secrets:
181181
- `GEMINI_API_KEY`
182182
- `ANTHROPIC_API_KEY`
183183

184-
This works without changing the workflow and avoids writing real `LLM_DEEPSEEK_API_KEY` / `LLM_AIHUBMIX_API_KEY` values into the default `.env`. For custom channel names, prefer `LITELLM_CONFIG` + `LITELLM_CONFIG_YAML`.
184+
This works without changing the workflow and avoids writing real `LLM_DEEPSEEK_API_KEY` / `LLM_AIHUBMIX_API_KEY` values into the default `.env`. For custom channel names, prefer `LITELLM_CONFIG` (YAML config path).
185185

186186
---
187187

@@ -216,7 +216,7 @@ Afraid you got the config wrong? Type the following commands in your terminal to
216216
| Weird Error You Got? | Likely Culprit | How to Fix It? |
217217
|----------------------|----------------|----------------|
218218
| **The UI says the primary model is not configured** | The system doesn't know which provider/model you want to use. | Add a clear instruction in `.env`: `LITELLM_MODEL=provider/your_model_name`. Example: `openai/gpt-4o-mini`. |
219-
| **I added multiple provider Keys, why is only one working?** | Usually you mixed config modes, or your channel name does not match a supported fallback mapping. | For local / Docker, prefer explicit `LLM_{NAME}_API_KEY(S)`. In GitHub Actions, common provider channel names such as `deepseek` / `aihubmix` / `openai` / `gemini` / `anthropic` can reuse matching legacy Secrets. Custom channel names should switch to `LITELLM_CONFIG` + `LITELLM_CONFIG_YAML`. |
219+
| **I added multiple provider Keys, why is only one working?** | Usually you mixed config modes, or your channel name does not match a supported fallback mapping. | For local / Docker, prefer explicit `LLM_{NAME}_API_KEY(S)`. In GitHub Actions, common provider channel names such as `deepseek` / `aihubmix` / `openai` / `gemini` / `anthropic` can reuse matching legacy Secrets. Custom channel names should switch to `LITELLM_CONFIG` (YAML config path). |
220220
| **Returns 400, 401, or Invalid API Key** | The API Key is wrong, copied incompletely, account lacks credits, or you mistyped the model name (extremely common). | 1. Ensure there are no spaces at the start/end of your Key.<br> 2. Ensure your Base URL ends with `/v1`.<br> 3. Check if you forgot the `openai/` prefix on the model name! |
221221
| **Spins endlessly, eventually hits Timeout/ConnectionRefused** | You are using restricted APIs (like Google/OpenAI) in a blocked region without a proxy, or your cloud server lacks external internet access. | Highly recommend using **official regional APIs** (like DeepSeek) or **OpenAI-compatible relay platforms**. Third-party platforms bypass these network constraints. |
222222
| **Ollama returns 404, `Could not get model info`, or `api/generate/api/show`** | Using `OPENAI_BASE_URL` for Ollama makes the system concatenate URLs incorrectly | Use `OLLAMA_API_BASE=http://localhost:11434` or channel mode (`LLM_CHANNELS=ollama` + `LLM_OLLAMA_BASE_URL`) instead |

src/config.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,27 @@ def _is_provider_host(host: str, domain: str) -> bool:
9090
return host == domain or host.endswith('.' + domain)
9191

9292

93-
def resolve_channel_legacy_api_keys(channel_name: str, base_url: Optional[str]) -> Tuple[List[str], Optional[str]]:
93+
def resolve_channel_legacy_api_keys(
94+
channel_name: str,
95+
base_url: Optional[str],
96+
resolved_protocol: Optional[str] = None,
97+
) -> Tuple[List[str], Optional[str]]:
9498
"""Map well-known channel names/hosts to legacy provider secrets.
9599
96100
This keeps local/Docker channel-specific env vars as the primary source, but
97101
allows common provider channels in GitHub Actions to reuse existing legacy
98102
secret names without writing real keys into the default `.env`.
103+
104+
When *resolved_protocol* is supplied it takes precedence over *channel_name*
105+
for name-based matching, so that an explicit ``LLM_{NAME}_PROTOCOL`` override
106+
selects the correct provider's legacy key instead of blindly trusting the
107+
channel name.
99108
"""
100-
normalized_name = canonicalize_llm_channel_protocol(channel_name)
101-
raw_name = (channel_name or "").strip().lower()
109+
# Use the resolved protocol for name-based matching when available,
110+
# falling back to the channel name for backward compatibility.
111+
effective_name = resolved_protocol if resolved_protocol else channel_name
112+
normalized_name = canonicalize_llm_channel_protocol(effective_name)
113+
raw_name = (effective_name or "").strip().lower()
102114
parsed_url = urlparse(base_url or "")
103115
host = (parsed_url.hostname or "").lower()
104116
scheme = (parsed_url.scheme or "").lower()
@@ -1562,7 +1574,7 @@ def _parse_llm_channels(cls, channels_str: str) -> List[Dict[str, Any]]:
15621574
if not api_keys and channel_allows_empty_api_key(protocol, base_url):
15631575
api_keys = [""]
15641576
if not api_keys:
1565-
api_keys, legacy_source = resolve_channel_legacy_api_keys(ch_name, base_url)
1577+
api_keys, legacy_source = resolve_channel_legacy_api_keys(ch_name, base_url, resolved_protocol=protocol)
15661578
if api_keys and legacy_source:
15671579
_logger.info(
15681580
"LLM channel '%s': using legacy API key fallback from %s",

0 commit comments

Comments
 (0)