V10.413 protect code review fallback host
All checks were successful
CD Pipeline / deploy (push) Successful in 1m6s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m6s
This commit is contained in:
@@ -227,6 +227,8 @@ CODE_REVIEW_OLLAMA_FALLBACK_MODEL=hermes3:latest
|
||||
CODE_REVIEW_OLLAMA_FALLBACK_TIMEOUT=20
|
||||
CODE_REVIEW_OLLAMA_NUM_PREDICT=384
|
||||
CODE_REVIEW_OLLAMA_KEEP_ALIVE=5m
|
||||
# 預設保護 111:Code Review 這類部署後重分析只跑 GCP-A/GCP-B;需明確救急才設 true。
|
||||
CODE_REVIEW_ALLOW_111_FALLBACK=false
|
||||
CODE_REVIEW_HERMES_TIMEOUT=35
|
||||
CODE_REVIEW_HERMES_PRIMARY_MODEL=qwen2.5-coder:7b
|
||||
CODE_REVIEW_HERMES_PRIMARY_TIMEOUT=15
|
||||
|
||||
@@ -325,7 +325,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.412"
|
||||
SYSTEM_VERSION = "V10.413"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
- PPT vision、PPT 文案 final fallback、MCP 離線 final fallback 等特殊 Ollama 路徑也不得只打單一 host;如需 `/api/generate`,一律透過 `OllamaService.generate()`。
|
||||
- Code Review pipeline 也必須 Ollama-first:Hermes scan 與 OpenClaw assessment 都走 `OllamaService` 三主機 retry;Gemini telemetry 只能以 `code_review_openclaw_gemini` 出現,表示 Ollama/可選 Claude 備援都失敗後才啟用。
|
||||
- Code Review Hermes scan 預設不呼叫 LLM,改用 deterministic fast static scan,避免部署後先卡三段 Ollama timeout;需要 LLM 掃描時才以 `CODE_REVIEW_HERMES_LLM_SCAN_ENABLED=true` 啟用本地矩陣。
|
||||
- Code Review Hermes LLM scan 啟用時才使用本地模型矩陣:GCP-A `qwen2.5-coder:7b`、GCP-B `gemma3:4b`;落到 111 時由 `OllamaService` 降級到 `llama3.2:latest`。不啟用 Gemini 備援,三段本地掃描失敗時只回空 findings 並交由 OpenClaw 本地矩陣續跑。
|
||||
- Code Review OpenClaw assessment 保持主機順序 GCP-A → GCP-B → 111,但可使用主機適配本地模型:GCP-A `qwen2.5-coder:7b`、GCP-B `gemma3:4b`;primary timeout 預設 `15s`、secondary timeout 預設 `60s`,讓 A 掛時快速讓位給 B,且 B 有足夠時間完成審查 prompt。落到 111 時由 `OllamaService` 降級到 `llama3.2:latest`。Code Review 的 Ollama `keep_alive` 預設為 `5m`,不得再用 `24h` 長駐 runner 壓住 GCP-B/111。三段本地 Ollama 全失敗後才允許雲端備援。
|
||||
- Code Review Hermes LLM scan 啟用時才使用本地模型矩陣,且預設只跑 GCP-A `qwen2.5-coder:7b` → GCP-B `gemma3:4b`;`CODE_REVIEW_ALLOW_111_FALLBACK=true` 時才允許落到 111,並由 `OllamaService` 降級到 `llama3.2:latest`。不啟用 Gemini 備援,本地掃描失敗時只回空 findings 並交由 OpenClaw 本地矩陣續跑。
|
||||
- Code Review OpenClaw assessment 預設只跑 GCP-A → GCP-B:GCP-A `qwen2.5-coder:7b`、GCP-B `gemma3:4b`;primary timeout 預設 `15s`、secondary timeout 預設 `60s`,讓 A 掛時快速讓位給 B,且 B 有足夠時間完成審查 prompt。111 是最後救急節點,但部署後重分析預設不打 111;只有 `CODE_REVIEW_ALLOW_111_FALLBACK=true` 才允許 111 接手,並降級到 `llama3.2:latest`。Code Review 的 Ollama `keep_alive` 預設為 `5m`,不得再用 `24h` 長駐 runner 壓住 GCP-B/111。GCP-A/GCP-B 都失敗且 Claude/Gemini 未顯式開啟時,必須回 deterministic 本地降級摘要,不呼叫 Gemini、不落 111、不走其他雲端模型。
|
||||
- OpenClaw Telegram Q&A 主路徑也不得綁單一 host:`_call_qwen3_qa()` 必須透過 `OllamaService` 跑 GCP-A → GCP-B → 111,並把實際落點寫入 `ai_calls.provider`。
|
||||
- OpenClaw Telegram 圖片商品辨識也必須 Ollama-first:`_identify_product_name_with_ollama_vision()` 透過 `OllamaService` 嘗試 GCP-A → GCP-B → 111;Gemini 只允許以 `openclaw_bot_image_gemini` caller 作為失敗後備援。
|
||||
- OpenClaw 週報、月報、Meta analysis、日報洞察、Telegram PPT 分析與 MCP fallback 也必須 Ollama-first;Gemini caller 只能帶 `_gemini_fallback` 或明確 fallback caller 語意,且不得先於 Ollama/NIM 被呼叫。OpenClaw strategy 的 Ollama `keep_alive` 預設為 `5m`,避免報告型任務把 GCP-B/111 runner 長駐 24h。
|
||||
@@ -28,7 +28,7 @@
|
||||
- `docker-compose.yml` 的 `momo-app`、`scheduler`、`telegram-bot` 必須明確設定 `GEMINI_API_HARD_DISABLED=${GEMINI_API_HARD_DISABLED:-true}` 與 `GEMINI_FALLBACK_ENABLED=${GEMINI_FALLBACK_ENABLED:-false}`;`.env` 可保留 `GEMINI_API_KEY`,但不得因 key 存在就讓核心容器產生 Gemini 付費出站。
|
||||
- Gemini 不可被任何狀態面板或 router 推薦為主提供者:`AIProviderService._get_recommended_provider()` 不得回傳 `gemini`,只能顯示為 fallback 狀態;`llm_model_router` 的 `ea_engine` 若收到 `gemini-*` default 必須改回 `hermes3:latest`,需要深推理時才升本地 `deepseek-r1:14b`。
|
||||
- ElephantAlpha prompt / agent registry 不得再把 OpenClaw 描述為 Gemini 主模型;OpenClaw 是 `qwen2.5-coder:7b` / `qwen3:14b` Ollama-first 策略師,Gemini 僅能在 guard 顯式解鎖後作 emergency fallback。
|
||||
- 111 `192.168.0.111` 只是最後一道 Mac fallback,不承接 7B+、vision、long-context 模型長駐;`OllamaService.generate()` 落到 111 時會將 `qwen3`、`deepseek-r1`、`hermes3`、`qwen2.5*`、`gemma3`、`llava`、`minicpm-v` 與 7B+ 模型依 `OLLAMA_111_MODEL_DOWNGRADE_PATTERNS` 降級到 `OLLAMA_111_MODEL_FALLBACK=llama3.2:latest`,並以 `OLLAMA_111_KEEP_ALIVE=5m`、`OLLAMA_111_MAX_TIMEOUT=20`、`OLLAMA_111_NUM_CTX=4096`、`OLLAMA_111_NUM_PREDICT=512` 封頂。Hermes / OpenClaw / Code Review 路徑的業務 keep-alive 也預設 `5m`,避免 16GB RAM 主機與 GCP-B 被長駐 runner、長輸出與 24h keep-alive 壓到高 load。
|
||||
- 111 `192.168.0.111` 只是最後一道 Mac fallback,不承接 7B+、vision、long-context 模型長駐;`OllamaService.generate()` 落到 111 時會將 `qwen3`、`deepseek-r1`、`hermes3`、`qwen2.5*`、`gemma3`、`llava`、`minicpm-v` 與 7B+ 模型依 `OLLAMA_111_MODEL_DOWNGRADE_PATTERNS` 降級到 `OLLAMA_111_MODEL_FALLBACK=llama3.2:latest`,並以 `OLLAMA_111_KEEP_ALIVE=5m`、`OLLAMA_111_MAX_TIMEOUT=20`、`OLLAMA_111_NUM_CTX=4096`、`OLLAMA_111_NUM_PREDICT=512` 封頂。Hermes / OpenClaw 報告型路徑的業務 keep-alive 也預設 `5m`;Code Review 另以 `CODE_REVIEW_ALLOW_111_FALLBACK=false` 預設跳過 111,避免 16GB RAM 主機與 GCP-B 被長駐 runner、長輸出與 24h keep-alive 壓到高 load。
|
||||
- ElephantAlpha 的 `price_drop_alert` / `market_opportunity` Telegram HITL 告警必須把同款證據獨立呈現,至少包含 `match_type`、`price_basis`、`alert_tier` 與 `match_score`;沒有高信心同款與總價可比證據時,不得把 PChome/MOMO 價差寫成可直接跟價建議。
|
||||
|
||||
## 一、四 AI Agent 路由架構
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
## 📅 詳細更新日誌 (考古存檔)
|
||||
|
||||
### 2026-05-24:PChome 近門檻身份回收第二輪
|
||||
- **V10.413 Code Review 預設保護 111 fallback**: production `ai_calls` 顯示 GCP-A 不可達時,Code Review OpenClaw 會先耗掉 primary timeout,再讓 GCP-B 撐到 60s,最後落到 111 `llama3.2` 成功,造成 111 與 GCP-B 高負載。新增 `CODE_REVIEW_ALLOW_111_FALLBACK=false` 預設:Code Review 的 Hermes LLM scan / OpenClaw assessment 只跑 GCP-A → GCP-B;只有明確設 true 才把部署後重分析丟給 111。若 GCP-A/GCP-B 都失敗且 Claude/Gemini 未顯式開啟,改回 deterministic 本地降級摘要,不呼叫 Gemini,也不再用 111 承接非即時重分析。
|
||||
- **V10.412 MCP fetch run package gate**: 新增 `mcp_fetch_run_package` read-only builder、獨立 route extension、GET/POST endpoint、UI run package 審核面板與 deployment readiness smoke target,將已通過的 target review 轉成操作員可覆核的 command argv preview 與 receipt path 契約;API/UI 不執行 CLI、不抓外站、不寫檔、不開 DB、不掛 scheduler,只放行到後續 run readiness review。
|
||||
- **V10.411 rom&nd / Summer’s Eve / Solone 近門檻 review-only 回收**: marketplace matcher 追加三條窄範圍 focused identity:rom&nd 果汁唇釉 2.0 catalog、Summer’s Eve 舒摩兒全肌防護浴潔露 2入、Solone 持久眼線筆;皆只進 `identity_review` / manual-review,不直接價格告警。production pilot 已回刷 3/3,`matched` 1616→1619、`true_low_confidence` 763→760;rom&nd 染眉膏 ZO&FRIENDS 色號、Summer’s Eve 雙天王任選、Lactacyd 清新舒涼 vs 生理呵護、MAC 柔霧 vs 緞光、NIVEA / 曼秀雷敦包數差異仍不自動救回,維持準確率優先。
|
||||
- **V10.410 Code Review timeout 梯度改為保護 111**: 部署後實測顯示 GCP-A 從 188 失聯時,Code Review 仍會先等 primary 45s,GCP-B 完整審查 prompt 又常因 25s 太短而 timeout,最後轉落 111。`CODE_REVIEW_OLLAMA_TIMEOUT` 預設收斂為 `15s`,`CODE_REVIEW_OLLAMA_SECONDARY_TIMEOUT` 放寬為 `60s`;Hermes LLM scan 若啟用則 primary `15s`、secondary `45s`。目標是 A 掛時更快讓位給 B,並給 B 足夠時間完成,避免過早壓到 111。
|
||||
|
||||
@@ -37,6 +37,11 @@ from services.gemini_guard import gemini_disabled_message, get_gemini_api_key
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _env_bool(name: str, default: str = "false") -> bool:
|
||||
return os.getenv(name, default).strip().lower() in {"1", "true", "yes", "on"}
|
||||
|
||||
|
||||
# ── Pipeline 全域狀態(供前端 polling)─────────────────────────────────────
|
||||
_current_pipeline: Dict[str, Any] = {}
|
||||
_pipeline_lock = threading.Lock()
|
||||
@@ -64,6 +69,7 @@ CODE_REVIEW_OLLAMA_FALLBACK_TIMEOUT = int(
|
||||
)
|
||||
CODE_REVIEW_OLLAMA_NUM_PREDICT = int(os.getenv("CODE_REVIEW_OLLAMA_NUM_PREDICT", "384"))
|
||||
CODE_REVIEW_OLLAMA_KEEP_ALIVE = os.getenv("CODE_REVIEW_OLLAMA_KEEP_ALIVE", "5m")
|
||||
CODE_REVIEW_ALLOW_111_FALLBACK = _env_bool("CODE_REVIEW_ALLOW_111_FALLBACK", "false")
|
||||
CODE_REVIEW_HERMES_TIMEOUT = int(os.getenv("CODE_REVIEW_HERMES_TIMEOUT", "35"))
|
||||
CODE_REVIEW_HERMES_PRIMARY_MODEL = os.getenv(
|
||||
"CODE_REVIEW_HERMES_PRIMARY_MODEL",
|
||||
@@ -245,7 +251,7 @@ class CodeReviewPipeline:
|
||||
# ── Step 2:Hermes 掃描 ───────────────────────────────────────────────────
|
||||
|
||||
def _hermes_scan(self, files: Dict[str, str]) -> List[Dict]:
|
||||
"""走 OllamaService 三主機級聯:GCP-A → GCP-B → 111。"""
|
||||
"""走 GCP-A → GCP-B;只有 CODE_REVIEW_ALLOW_111_FALLBACK=true 才落到 111。"""
|
||||
try:
|
||||
if not CODE_REVIEW_HERMES_LLM_SCAN_ENABLED:
|
||||
findings = self._static_code_scan(files)
|
||||
@@ -301,13 +307,14 @@ class CodeReviewPipeline:
|
||||
CODE_REVIEW_HERMES_SECONDARY_MODEL,
|
||||
CODE_REVIEW_HERMES_SECONDARY_TIMEOUT,
|
||||
),
|
||||
(
|
||||
]
|
||||
if CODE_REVIEW_ALLOW_111_FALLBACK:
|
||||
hermes_attempts.append((
|
||||
"lan_111_hermes_scan",
|
||||
OLLAMA_HOST_FALLBACK,
|
||||
CODE_REVIEW_HERMES_FALLBACK_MODEL,
|
||||
CODE_REVIEW_HERMES_FALLBACK_TIMEOUT,
|
||||
),
|
||||
]
|
||||
))
|
||||
findings = None
|
||||
last_error = None
|
||||
|
||||
@@ -437,10 +444,12 @@ class CodeReviewPipeline:
|
||||
def _openclaw_assess(self, files: Dict[str, str], findings: List[Dict]) -> str:
|
||||
"""
|
||||
路由優先序:
|
||||
L1 (預設) → Ollama GCP-A → GCP-B → 111
|
||||
L1 (預設) → Ollama GCP-A → GCP-B
|
||||
L1b (flag CODE_REVIEW_ALLOW_111_FALLBACK=true) → 111 最後備援
|
||||
L2 (flag CODE_REVIEW_USE_CLAUDE=true) → Claude Opus 4.7 雲端備援
|
||||
L3 (Gemini guard 顯式解鎖) → Gemini 雲端備援
|
||||
L4 (降級) → ElephantAlpha via NIM/OpenRouter
|
||||
L4 (雲端未顯式開啟時) → deterministic 本地降級摘要
|
||||
L5 (雲端顯式開啟但失敗時) → ElephantAlpha via NIM/OpenRouter
|
||||
"""
|
||||
sev = self.state["severity_summary"]
|
||||
findings_json = json.dumps(findings[:8], ensure_ascii=False, indent=2)
|
||||
@@ -464,7 +473,7 @@ class CodeReviewPipeline:
|
||||
<b>💡 架構優化方向</b>(1條長期建議)
|
||||
<b>✅ 本次部署亮點</b>"""
|
||||
|
||||
# ── L1:Ollama-first — GCP-A → GCP-B → 111 ──────────────────────────
|
||||
# ── L1:Ollama-first — GCP-A → GCP-B(111 需顯式開 flag)──────────────
|
||||
from services.ollama_service import (
|
||||
OLLAMA_HOST_FALLBACK,
|
||||
OLLAMA_HOST_PRIMARY,
|
||||
@@ -475,9 +484,12 @@ class CodeReviewPipeline:
|
||||
)
|
||||
|
||||
gemini_api_key = get_gemini_api_key("code_review")
|
||||
cloud_fallback_available = CODE_REVIEW_USE_CLAUDE or bool(gemini_api_key)
|
||||
fallback_caller = 'code_review_openclaw' if CODE_REVIEW_USE_CLAUDE else (
|
||||
'code_review_openclaw_gemini' if gemini_api_key else 'code_review_elephant'
|
||||
)
|
||||
if not cloud_fallback_available:
|
||||
fallback_caller = 'code_review_local_degraded'
|
||||
ollama_attempts = [
|
||||
(
|
||||
"primary_code",
|
||||
@@ -491,13 +503,14 @@ class CodeReviewPipeline:
|
||||
CODE_REVIEW_OLLAMA_SECONDARY_MODEL,
|
||||
CODE_REVIEW_OLLAMA_SECONDARY_TIMEOUT,
|
||||
),
|
||||
(
|
||||
]
|
||||
if CODE_REVIEW_ALLOW_111_FALLBACK:
|
||||
ollama_attempts.append((
|
||||
"lan_111_hermes",
|
||||
OLLAMA_HOST_FALLBACK,
|
||||
CODE_REVIEW_OLLAMA_FALLBACK_MODEL,
|
||||
CODE_REVIEW_OLLAMA_FALLBACK_TIMEOUT,
|
||||
),
|
||||
]
|
||||
))
|
||||
last_ollama_error = None
|
||||
|
||||
for attempt_index, (attempt_key, host, model_name, timeout_s) in enumerate(
|
||||
@@ -544,10 +557,20 @@ class CodeReviewPipeline:
|
||||
_ctx.fallback_to_caller(fallback_caller)
|
||||
|
||||
logger.warning(
|
||||
"[CodeReview] OpenClaw 本地 Ollama 鏈全部失敗,才啟用雲端備援: %s",
|
||||
"[CodeReview] OpenClaw 本地 Ollama 鏈全部失敗: %s",
|
||||
last_ollama_error,
|
||||
)
|
||||
|
||||
if not cloud_fallback_available:
|
||||
logger.warning(
|
||||
"[CodeReview] 111/cloud fallback 未啟用,回傳 deterministic 本地降級評估",
|
||||
)
|
||||
return self._build_local_openclaw_degraded_report(
|
||||
files=files,
|
||||
findings=findings,
|
||||
last_error=last_ollama_error,
|
||||
)
|
||||
|
||||
# ── L1:Phase 7 Frontier — Claude Opus 4.7(程式碼能力 #1)────────────
|
||||
# feature flag 預設 OFF;ON 時只作 Ollama 失敗後的雲端備援。
|
||||
if CODE_REVIEW_USE_CLAUDE:
|
||||
@@ -668,6 +691,35 @@ class CodeReviewPipeline:
|
||||
|
||||
return ""
|
||||
|
||||
def _build_local_openclaw_degraded_report(
|
||||
self,
|
||||
*,
|
||||
files: Dict[str, str],
|
||||
findings: List[Dict],
|
||||
last_error: Optional[str],
|
||||
) -> str:
|
||||
sev = self.state["severity_summary"]
|
||||
risk = "低"
|
||||
if sev["critical"]:
|
||||
risk = "嚴重"
|
||||
elif sev["high"]:
|
||||
risk = "高"
|
||||
elif sev["medium"]:
|
||||
risk = "中"
|
||||
top = [f for f in findings[:2] if f.get("description")]
|
||||
top_text = ";".join(
|
||||
f"{f.get('file', 'unknown')}:{f.get('description')}" for f in top
|
||||
) or "本次 deterministic scan 未發現高風險問題"
|
||||
error_text = (last_error or "local Ollama unavailable")[:120]
|
||||
return (
|
||||
f"<b>🔍 整體風險等級</b> {risk}。"
|
||||
f"本地掃描完成 {len(files)} 檔,CRITICAL={sev['critical']}、HIGH={sev['high']}、"
|
||||
f"MEDIUM={sev['medium']}、LOW={sev['low']}。"
|
||||
f"<b>⚠️ 最需關注問題</b> {top_text}。"
|
||||
f"<b>💡 架構優化方向</b> GCP-A/GCP-B OpenClaw 不可用時暫停 111 重分析,避免拖高 fallback 主機負載。"
|
||||
f"<b>✅ 本次部署亮點</b> 已以本地降級報告收斂,未呼叫 Gemini;最後錯誤:{error_text}"
|
||||
)
|
||||
|
||||
# ── Step 4:ElephantAlpha 決策 ─────────────────────────────────────────────
|
||||
|
||||
def _ea_orchestrate(self, findings: List[Dict], openclaw_report: str) -> Dict:
|
||||
|
||||
@@ -201,6 +201,7 @@ def test_code_review_ollama_defaults_use_fast_local_model(monkeypatch):
|
||||
"CODE_REVIEW_OLLAMA_FALLBACK_TIMEOUT",
|
||||
"CODE_REVIEW_OLLAMA_NUM_PREDICT",
|
||||
"CODE_REVIEW_OLLAMA_KEEP_ALIVE",
|
||||
"CODE_REVIEW_ALLOW_111_FALLBACK",
|
||||
"CODE_REVIEW_HERMES_TIMEOUT",
|
||||
"CODE_REVIEW_HERMES_PRIMARY_MODEL",
|
||||
"CODE_REVIEW_HERMES_PRIMARY_TIMEOUT",
|
||||
@@ -225,6 +226,7 @@ def test_code_review_ollama_defaults_use_fast_local_model(monkeypatch):
|
||||
assert svc_mod.CODE_REVIEW_OLLAMA_FALLBACK_TIMEOUT == 20
|
||||
assert svc_mod.CODE_REVIEW_OLLAMA_NUM_PREDICT == 384
|
||||
assert svc_mod.CODE_REVIEW_OLLAMA_KEEP_ALIVE == "5m"
|
||||
assert svc_mod.CODE_REVIEW_ALLOW_111_FALLBACK is False
|
||||
assert svc_mod.CODE_REVIEW_HERMES_TIMEOUT == 35
|
||||
assert svc_mod.CODE_REVIEW_HERMES_PRIMARY_MODEL == "qwen2.5-coder:7b"
|
||||
assert svc_mod.CODE_REVIEW_HERMES_PRIMARY_TIMEOUT == 15
|
||||
@@ -304,6 +306,62 @@ def test_openclaw_uses_secondary_local_model_before_gemini(monkeypatch):
|
||||
fake_elephant.generate.assert_not_called()
|
||||
|
||||
|
||||
def test_openclaw_skips_111_and_cloud_by_default_when_gcp_pair_fails(monkeypatch):
|
||||
"""GCP-A/B 都失敗時,預設不把 Code Review 重分析丟給 111 或雲端。"""
|
||||
monkeypatch.setenv('CODE_REVIEW_USE_CLAUDE', 'false')
|
||||
monkeypatch.delenv('CODE_REVIEW_ALLOW_111_FALLBACK', raising=False)
|
||||
monkeypatch.setenv('GEMINI_API_HARD_DISABLED', 'true')
|
||||
monkeypatch.setenv('GEMINI_FALLBACK_ENABLED', 'false')
|
||||
monkeypatch.delenv('GEMINI_API_KEY', raising=False)
|
||||
_stub_logger(monkeypatch)
|
||||
|
||||
svc_mod = _reload_pipeline()
|
||||
import services.ollama_service as ollama_mod
|
||||
|
||||
calls = []
|
||||
|
||||
class FakeResp:
|
||||
success = False
|
||||
content = ""
|
||||
error = "timeout"
|
||||
input_tokens = 0
|
||||
output_tokens = 0
|
||||
|
||||
def __init__(self, *, host, model):
|
||||
self.host = host
|
||||
self.model = model
|
||||
|
||||
class FakeOllama:
|
||||
def __init__(self, host=None, model=None):
|
||||
self.host = host
|
||||
self.model = model
|
||||
|
||||
def generate(self, **kwargs):
|
||||
calls.append({
|
||||
"host": self.host,
|
||||
"model": kwargs["model"],
|
||||
"timeout": kwargs["timeout"],
|
||||
})
|
||||
return FakeResp(host=self.host, model=kwargs["model"])
|
||||
|
||||
monkeypatch.setattr(ollama_mod, "OllamaService", FakeOllama)
|
||||
fake_claude = _stub_anthropic(monkeypatch, svc_mod, available=True)
|
||||
fake_genai, fake_elephant = _stub_gemini_and_elephant(monkeypatch)
|
||||
|
||||
pipeline = _make_pipeline(svc_mod)
|
||||
result = pipeline._openclaw_assess(
|
||||
files={"services/foo.py": "def x(): pass"},
|
||||
findings=[],
|
||||
)
|
||||
|
||||
assert "本地降級報告" in result
|
||||
assert [call["model"] for call in calls] == ["qwen2.5-coder:7b", "gemma3:4b"]
|
||||
assert not any("192.168.0.111" in call["host"] for call in calls)
|
||||
fake_claude.generate.assert_not_called()
|
||||
fake_genai.GenerativeModel.assert_not_called()
|
||||
fake_elephant.generate.assert_not_called()
|
||||
|
||||
|
||||
def test_hermes_scan_uses_compact_prompt_and_short_timeout(monkeypatch):
|
||||
"""Hermes scan 只送 compact snippet,避免大檔讓三主機各卡 120 秒。"""
|
||||
monkeypatch.setenv("CODE_REVIEW_HERMES_LLM_SCAN_ENABLED", "true")
|
||||
|
||||
Reference in New Issue
Block a user