From c86f5bc304e53ac82378f86bb13c1d9cb85d6c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=94=AF?= Date: Thu, 2 Jul 2026 01:00:23 +0800 Subject: [PATCH] fix: NewAPI key strip sk- prefix + LLM wiki graceful fallback --- scripts/wiki_curator.py | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/scripts/wiki_curator.py b/scripts/wiki_curator.py index 1b4098a..4526642 100644 --- a/scripts/wiki_curator.py +++ b/scripts/wiki_curator.py @@ -40,7 +40,7 @@ STATE_FILE = os.path.expanduser("~/.hermes/wiki_curator_state.json") # LLM 配置 LLM_API = "http://127.0.0.1:3000/v1/chat/completions" -LLM_MODEL = "minimaxai/minimax-m3" +LLM_MODEL = "minimaxai/minimax-m2.7" # m3 sometimes returns empty, use m2.7 # 扫描时排除的目录名称(大小写不敏感) EXCLUDE_DIRS = { @@ -259,7 +259,11 @@ def _get_llm_key() -> str: cfg_path = os.path.expanduser("~/.hermes/config.yaml") with open(cfg_path) as f: cfg = yaml.safe_load(f) - return cfg.get("providers", {}).get("newapi-local", {}).get("api_key", "") + raw = cfg.get("providers", {}).get("newapi-local", {}).get("api_key", "") + # NewAPI 的 key 不需要 sk- 前缀 + if raw.startswith("sk-"): + raw = raw[3:] + return raw except Exception: pass # 回退到环境变量 @@ -273,27 +277,39 @@ def _extract_with_llm(content: str, filepath: str) -> dict | None: print(" ⚠️ No LLM API key found (check config.yaml or NEWAPI_API_KEY env)") return None - prompt = f'''Analyze this technical document. Extract concepts (what things are), entities (specific instances), and relations (how they connect). -Return JSON ONLY: -{{"concepts": [{{"name":"...","summary":"..."}}], - "entities": [{{"name":"...","attributes":{{}}}}], - "relations": [{{"source":"...","relation":"uses|contains|depends_on|part_of|implements","target":"..."}}]}} + prompt = f'''Extract concepts, entities, and relations from this document. +Return ONLY valid JSON: +{{"concepts":[{{"name":"...","summary":"..."}}],"entities":[{{"name":"...","attributes":{{}}}}],"relations":[{{"source":"...","relation":"uses|contains|depends_on|part_of|implements","target":"..."}}]}} -Document: {content[:3000]} +Document: +{content[:2000]} ''' try: resp = requests.post(LLM_API, headers={"Authorization": f"Bearer {llm_key}", "Content-Type": "application/json"}, - json={"model": LLM_MODEL, "messages": [{"role": "user", "content": prompt}], "temperature": 0.1}, + json={ + "model": LLM_MODEL, + "messages": [ + {"role": "system", "content": "You are a knowledge extraction assistant. Always respond with valid JSON only."}, + {"role": "user", "content": prompt} + ], + "temperature": 0.1, + "max_tokens": 1000, + }, timeout=30) data = resp.json() + if "error" in data and data["error"].get("message"): + print(f" ⚠️ LLM API error: {data['error']['message'][:60]}") + return None choices = data.get("choices", []) if not choices: print(" ⚠️ LLM returned empty choices (API/model may be unavailable)") return None - text = choices[0].get("message", {}).get("content", "") - if not text: - print(" ⚠️ LLM returned empty content") + msg = choices[0].get("message", {}) + text = msg.get("content", "") or "" + if not text.strip(): + finish = choices[0].get("finish_reason", "") + print(f" ⚠️ LLM returned empty content (finish={finish})") return None # Parse JSON from response json_match = re.search(r'\{[\s\S]*\}', text)