Harden Ollama MSR analysis parsing

This commit is contained in:
OpenClaw Agent
2026-07-10 10:17:37 +00:00
parent c2f39a2f0e
commit f0cd0b382d

View File

@@ -444,6 +444,32 @@ _ANALYZE_PROMPT = (
) )
def _extract_ollama_generated_text(resp_json: dict) -> str:
"""Handle Ollama generate/chat variants and reasoning-capable models."""
if not isinstance(resp_json, dict):
return ""
candidates = []
candidates.append(resp_json.get("response"))
candidates.append(resp_json.get("thinking"))
candidates.append(resp_json.get("content"))
message = resp_json.get("message")
if isinstance(message, dict):
candidates.append(message.get("content"))
candidates.append(message.get("reasoning_content"))
candidates.append(message.get("thinking"))
for candidate in candidates:
if isinstance(candidate, str) and candidate.strip():
text = candidate.strip()
if "<think>" in text:
text = text.split("</think>")[-1].strip()
if text:
return text
return ""
def _resolve_provider(data): def _resolve_provider(data):
provider = (data.get("provider") or "ollama").lower() provider = (data.get("provider") or "ollama").lower()
defs = _PROVIDER_DEFAULTS.get(provider, _PROVIDER_DEFAULTS["ollama"]) defs = _PROVIDER_DEFAULTS.get(provider, _PROVIDER_DEFAULTS["ollama"])
@@ -492,12 +518,30 @@ async def analyze_character_endpoint(request):
"model": model_name, "prompt": _ANALYZE_PROMPT, "model": model_name, "prompt": _ANALYZE_PROMPT,
"images": cleaned_b64_list, "stream": False, "keep_alive": 0, "images": cleaned_b64_list, "stream": False, "keep_alive": 0,
} }
async with session.post(f"{base_url}/api/generate", json=payload, timeout=120) as response: async with session.post(f"{base_url}/api/generate", json=payload, timeout=300) as response:
if response.status != 200: if response.status != 200:
err_txt = await response.text() err_txt = await response.text()
return web.json_response({"status": "error", "message": f"Ollama HTTP {response.status}: {err_txt}"}) return web.json_response({"status": "error", "message": f"Ollama HTTP {response.status}: {err_txt}"})
resp_json = await response.json() resp_json = await response.json()
generated_text = (resp_json.get("response") or "").strip() generated_text = _extract_ollama_generated_text(resp_json)
# Some Ollama model variants return an empty `response` from `/api/generate`
# even though the same request succeeds via the chat endpoint.
if not generated_text:
chat_payload = {
"model": model_name,
"messages": [{
"role": "user",
"content": _ANALYZE_PROMPT,
"images": cleaned_b64_list,
}],
"stream": False,
"keep_alive": 0,
}
async with session.post(f"{base_url}/api/chat", json=chat_payload, timeout=300) as response:
if response.status == 200:
resp_json = await response.json()
generated_text = _extract_ollama_generated_text(resp_json)
else: else:
# OpenAI-compatible vision chat (LM Studio / Custom). # OpenAI-compatible vision chat (LM Studio / Custom).
content = [{"type": "text", "text": _ANALYZE_PROMPT}] content = [{"type": "text", "text": _ANALYZE_PROMPT}]
@@ -530,6 +574,11 @@ async def analyze_character_endpoint(request):
if "<think>" in generated_text: if "<think>" in generated_text:
generated_text = generated_text.split("</think>")[-1].strip() generated_text = generated_text.split("</think>")[-1].strip()
if not generated_text:
return web.json_response({
"status": "error",
"message": f"{provider} returned an empty analysis response.",
})
log.info("[LTXDirector] Analysis complete: %s", generated_text) log.info("[LTXDirector] Analysis complete: %s", generated_text)
return web.json_response({"status": "success", "description": generated_text}) return web.json_response({"status": "success", "description": generated_text})