[V10.282] 補齊 Code Review Hermes 本地模型矩陣 | code_review_pipeline_service.py
All checks were successful
CD Pipeline / deploy (push) Successful in 1m1s

This commit is contained in:
OoO
2026-05-19 22:26:09 +08:00
parent 40ddf4eee0
commit 350022f2ea
6 changed files with 130 additions and 37 deletions

View File

@@ -65,6 +65,24 @@ 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", "24h")
CODE_REVIEW_HERMES_TIMEOUT = int(os.getenv("CODE_REVIEW_HERMES_TIMEOUT", "35"))
CODE_REVIEW_HERMES_PRIMARY_MODEL = os.getenv(
"CODE_REVIEW_HERMES_PRIMARY_MODEL",
CODE_REVIEW_OLLAMA_MODEL,
)
CODE_REVIEW_HERMES_SECONDARY_MODEL = os.getenv(
"CODE_REVIEW_HERMES_SECONDARY_MODEL",
CODE_REVIEW_OLLAMA_SECONDARY_MODEL,
)
CODE_REVIEW_HERMES_FALLBACK_MODEL = os.getenv(
"CODE_REVIEW_HERMES_FALLBACK_MODEL",
CODE_REVIEW_OLLAMA_FALLBACK_MODEL,
)
CODE_REVIEW_HERMES_PRIMARY_TIMEOUT = int(
os.getenv("CODE_REVIEW_HERMES_PRIMARY_TIMEOUT", os.getenv("CODE_REVIEW_HERMES_TIMEOUT", "35"))
)
CODE_REVIEW_HERMES_SECONDARY_TIMEOUT = int(os.getenv("CODE_REVIEW_HERMES_SECONDARY_TIMEOUT", "25"))
CODE_REVIEW_HERMES_FALLBACK_TIMEOUT = int(os.getenv("CODE_REVIEW_HERMES_FALLBACK_TIMEOUT", "20"))
CODE_REVIEW_HERMES_NUM_PREDICT = int(os.getenv("CODE_REVIEW_HERMES_NUM_PREDICT", "768"))
CODE_REVIEW_HERMES_MAX_FILES = int(os.getenv("CODE_REVIEW_HERMES_MAX_FILES", "3"))
CODE_REVIEW_HERMES_MAX_CHARS = int(os.getenv("CODE_REVIEW_HERMES_MAX_CHARS", "2500"))
INTERNAL_TOKEN = os.getenv("INTERNAL_WEBHOOK_TOKEN", "")
@@ -248,44 +266,94 @@ class CodeReviewPipeline:
只輸出 JSON 陣列,不含其他文字。無問題時輸出 []"""
from services.ollama_service import OllamaService, get_host_label, get_provider_tag
from services.ollama_service import (
OLLAMA_HOST_FALLBACK,
OLLAMA_HOST_PRIMARY,
OLLAMA_HOST_SECONDARY,
OllamaService,
get_host_label,
get_provider_tag,
)
with log_ai_call(
caller='code_review_hermes',
provider='gcp_ollama',
model=_HERMES_MODEL,
request_id=f"cr-{self.commit_sha[:8]}",
meta={'commit': self.commit_sha[:8], 'branch': self.branch,
'files': len(files), 'route': 'ollama_first',
'max_files': CODE_REVIEW_HERMES_MAX_FILES,
'max_chars': CODE_REVIEW_HERMES_MAX_CHARS},
) as _ctx:
ollama = OllamaService(model=_HERMES_MODEL)
resp = ollama.generate(
prompt=prompt,
model=_HERMES_MODEL,
temperature=0.1,
timeout=CODE_REVIEW_HERMES_TIMEOUT,
)
actual_provider = get_provider_tag(resp.host or '')
_ctx.set_provider(actual_provider)
_ctx.set_tokens(
input=resp.input_tokens,
output=resp.output_tokens,
)
_ctx.add_meta('host', resp.host)
_ctx.add_meta('host_label', get_host_label(resp.host or ''))
if not resp.success:
_ctx.set_error(resp.error or 'ollama generate failed')
logger.warning("[CodeReview] Hermes Ollama 掃描失敗: %s", resp.error)
return []
raw = (resp.content or "").strip()
hermes_attempts = [
(
"primary_code_scan",
OLLAMA_HOST_PRIMARY,
CODE_REVIEW_HERMES_PRIMARY_MODEL,
CODE_REVIEW_HERMES_PRIMARY_TIMEOUT,
),
(
"secondary_fast_scan",
OLLAMA_HOST_SECONDARY,
CODE_REVIEW_HERMES_SECONDARY_MODEL,
CODE_REVIEW_HERMES_SECONDARY_TIMEOUT,
),
(
"lan_111_hermes_scan",
OLLAMA_HOST_FALLBACK,
CODE_REVIEW_HERMES_FALLBACK_MODEL,
CODE_REVIEW_HERMES_FALLBACK_TIMEOUT,
),
]
findings = None
last_error = None
match = re.search(r"\[.*\]", raw, re.DOTALL)
if not match:
logger.warning("[CodeReview] Hermes 回應無 JSON: %s", raw[:200])
for attempt_index, (attempt_key, host, model_name, timeout_s) in enumerate(
hermes_attempts,
start=1,
):
with log_ai_call(
caller='code_review_hermes',
provider=get_provider_tag(host),
model=model_name,
request_id=f"cr-{self.commit_sha[:8]}",
meta={'commit': self.commit_sha[:8], 'branch': self.branch,
'files': len(files), 'route': 'ollama_first',
'attempt': attempt_index,
'attempt_key': attempt_key,
'max_files': CODE_REVIEW_HERMES_MAX_FILES,
'max_chars': CODE_REVIEW_HERMES_MAX_CHARS,
'timeout_s': timeout_s},
) as _ctx:
ollama = OllamaService(host=host, model=model_name)
resp = ollama.generate(
prompt=prompt,
model=model_name,
temperature=0.1,
timeout=timeout_s,
keep_alive=CODE_REVIEW_OLLAMA_KEEP_ALIVE,
options={"num_predict": CODE_REVIEW_HERMES_NUM_PREDICT},
)
actual_host = resp.host or host
_ctx.set_provider(get_provider_tag(actual_host))
_ctx.set_tokens(
input=resp.input_tokens,
output=resp.output_tokens,
)
_ctx.add_meta('host', actual_host)
_ctx.add_meta('host_label', get_host_label(actual_host))
if not resp.success:
last_error = resp.error or 'ollama generate failed'
_ctx.set_error(last_error)
continue
raw = (resp.content or "").strip()
match = re.search(r"\[.*\]", raw, re.DOTALL)
if not match:
last_error = f"missing JSON array: {raw[:120]}"
_ctx.set_error(last_error)
logger.warning("[CodeReview] Hermes 回應無 JSON: %s", raw[:200])
continue
try:
findings = json.loads(match.group())
except Exception as exc:
last_error = f"json parse failed: {type(exc).__name__}: {exc}"
_ctx.set_error(last_error)
continue
break
if findings is None:
logger.warning("[CodeReview] Hermes 本地掃描全部失敗: %s", last_error)
return []
findings = json.loads(match.group())
for f in findings:
sev = f.get("severity", "LOW").lower()