Skip to content

Commit 475dd43

Browse files
committed
fix(review-feedback-1224): address latest review comments
1 parent 66b4322 commit 475dd43

4 files changed

Lines changed: 9 additions & 0 deletions

File tree

docs/LLM_CONFIG_GUIDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ LITELLM_MODEL=ollama/qwen3:8b
9898

9999
- 预设里的 provider / Base URL / 示例模型只用于**初始化表单**;真正落盘时仍是你当前输入的 `LLM_{CHANNEL}_PROTOCOL``LLM_{CHANNEL}_BASE_URL``LLM_{CHANNEL}_MODELS``LLM_{CHANNEL}_API_KEY(S)`,不会在后台偷偷改成别的 provider 名或 URL。
100100
- 设置页的“获取模型”只对 `OpenAI Compatible` / `DeepSeek` 渠道调用 `{base_url}/models`;“测试连接”默认只对模型列表首项发起一次最小聊天请求,并在结果中展示后端规范化后的 `resolved_model`。若返回 `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 检测。上述检测返回的 `stage / error_code / details / latency_ms / capability_results` 仅用于结构化诊断提示,**不会写回** `.env`,也不会阻止保存。
101+
- 对于上游拦截类文案,本次修复会将 `Your request was blocked.``Request was blocked.``request was blocked by policy``moderation_blocked` 等(来源可见 [Issue #1223](https://github.com/ZhuLinsen/daily_stock_analysis/issues/1223))归入 `request_blocked + provider_blocked`;包含 `local firewall` 字样的本地网络拦截会保留为网络诊断分支,行为不触发配置清理或迁移。
101102
- 运行时能力检测会产生真实 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 形状。
102103
- 相关外部来源: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>
103104
- 保存渠道时,只会更新这次提交的 key;不会因为切换渠道模式而静默迁移整个旧配置。唯一会被**同步清理**的是运行时模型引用:如果 `LITELLM_MODEL``AGENT_LITELLM_MODEL``VISION_MODEL``LITELLM_FALLBACK_MODELS` 指向了当前已启用渠道里已经不存在的模型,设置页会在保存前把这些失效引用清空/移除,避免运行时继续指向无效模型;即使当前启用渠道没有任何可选模型,也会清理缺少 legacy Key 支撑的托管 provider 旧值。`cohere/*``google/*``xai/*` 这类直连模型仅用于说明历史 `direct-env` 兼容保留语义,不等于可用性承诺,是否可用请按各厂商官方模型/API 文档再做实际验证。

docs/LLM_CONFIG_GUIDE_EN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ The backend exposes a read-only status endpoint at `GET /api/v1/system/config/se
9898

9999
- The preset provider / Base URL / sample models are **form defaults only**. What gets persisted is still exactly what you submit in `LLM_{CHANNEL}_PROTOCOL`, `LLM_{CHANNEL}_BASE_URL`, `LLM_{CHANNEL}_MODELS`, and `LLM_{CHANNEL}_API_KEY(S)`; the editor does not silently rewrite them to a different provider name or URL.
100100
- "Discover models" only calls `{base_url}/models` for `OpenAI Compatible` / `DeepSeek` channels, and the default "Test connection" action sends one minimal chat completion request against the first model in the list and shows the backend-normalized `resolved_model` in the result. 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 model availability diagnostic based on provider wording: first confirm that the tested model is enabled for the current account/key, then adjust the model order or remove unavailable models before retrying. Provider messages not covered by this conservative rule, or provider messages with different semantics, continue to use the fallback diagnostic path. Optional runtime capability checks must be explicitly selected by the user and send additional JSON / tools / stream / vision smoke requests; the result only represents a best-effort check for the current account, model, and endpoint at that moment. The returned `stage / error_code / details / latency_ms / capability_results` fields are for structured diagnostics only, are **never persisted** back into `.env`, and do not block saving.
101+
- For upstream block responses, this release maps `Your request was blocked.`, `Request was blocked.`, `request was blocked by policy`, and `moderation_blocked` to `request_blocked + provider_blocked` based on Issue #1223. Firewall-block messages that include `local firewall` stay on the generic network branch, and this change still does not trigger config migration or cleanup.
101102
- 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.
102103
- 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>.
103104
- 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.

src/services/system_config_service.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ class SystemConfigService:
9191
# 该分类只用于诊断展示,不作为配置迁移或清理触发条件。
9292
_LLM_PROVIDER_BLOCKED_TOKENS: Tuple[str, ...] = (
9393
"your request was blocked",
94+
"request was blocked",
9495
"request was blocked by safety",
9596
"request was blocked by policy",
9697
"request has been blocked by provider safety",
@@ -2683,6 +2684,8 @@ def _has_provider_blocked_signal(text: str) -> bool:
26832684
in runtime checks and do not mutate or migrate persisted user config.
26842685
"""
26852686
lowered = text.lower()
2687+
if "request was blocked" in lowered and "local firewall" in lowered:
2688+
return False
26862689
return any(token in lowered for token in SystemConfigService._LLM_PROVIDER_BLOCKED_TOKENS)
26872690

26882691
@staticmethod

tests/test_system_config_service.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,6 +1411,7 @@ class RateLimitError(Exception):
14111411
(Exception("model gpt-4o is not authorized for this account"), "model_not_found", "model_access_denied"),
14121412
(Exception("litellm.APIError: APIError: OpenAIException - Model disabled."), "model_not_found", "model_access_denied"),
14131413
(Exception("Model is disabled for this account"), "model_not_found", "model_access_denied"),
1414+
(Exception("Request was blocked."), "request_blocked", "provider_blocked"),
14141415
(Exception("litellm.APIError: APIError: OpenAIException - Your request was blocked."), "request_blocked", "provider_blocked"),
14151416
(Exception("request was blocked by policy"), "request_blocked", "provider_blocked"),
14161417
(Exception("This request has been blocked by provider safety policy."), "request_blocked", "provider_blocked"),
@@ -1503,6 +1504,8 @@ def test_discover_llm_channel_models_classifies_error_scenarios(self, mock_get)
15031504
rate_limit_response.json.return_value = {"error": {"message": "too many requests"}}
15041505
blocked_response = Mock(ok=False, status_code=403, text="Your request was blocked.")
15051506
blocked_response.json.return_value = {"error": {"message": "Your request was blocked."}}
1507+
blocked_plain_response = Mock(ok=False, status_code=403, text="Request was blocked.")
1508+
blocked_plain_response.json.return_value = {"error": {"message": "Request was blocked."}}
15061509
blocked_by_policy_response = Mock(ok=False, status_code=403, text="Request has been blocked by policy filter.")
15071510
blocked_by_policy_response.json.return_value = {"error": {"message": "Request has been blocked by policy filter."}}
15081511
blocked_by_firewall_response = Mock(
@@ -1522,6 +1525,7 @@ def test_discover_llm_channel_models_classifies_error_scenarios(self, mock_get)
15221525
(quota_exceeded_response, "quota", "model_discovery", True, "quota_exceeded"),
15231526
(rate_limit_response, "quota", "model_discovery", True, "rate_limit"),
15241527
(blocked_response, "request_blocked", "model_discovery", False, "provider_blocked"),
1528+
(blocked_plain_response, "request_blocked", "model_discovery", False, "provider_blocked"),
15251529
(blocked_by_policy_response, "request_blocked", "model_discovery", False, "provider_blocked"),
15261530
(
15271531
blocked_by_firewall_response,

0 commit comments

Comments
 (0)