Harden Ollama MSR analysis parsing
This commit is contained in:
@@ -437,11 +437,37 @@ _PROVIDER_DEFAULTS = {
|
||||
"custom": {"url": "", "model": ""},
|
||||
}
|
||||
|
||||
_ANALYZE_PROMPT = (
|
||||
"Describe the character's physical appearance in two concise sentences. "
|
||||
"Specify their hair color/style, face details, and their clothing type/color. "
|
||||
"Keep the entire response very brief."
|
||||
)
|
||||
_ANALYZE_PROMPT = (
|
||||
"Describe the character's physical appearance in two concise sentences. "
|
||||
"Specify their hair color/style, face details, and their clothing type/color. "
|
||||
"Keep the entire response very brief."
|
||||
)
|
||||
|
||||
|
||||
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):
|
||||
@@ -487,18 +513,36 @@ async def analyze_character_endpoint(request):
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
if provider == "ollama":
|
||||
payload = {
|
||||
"model": model_name, "prompt": _ANALYZE_PROMPT,
|
||||
"images": cleaned_b64_list, "stream": False, "keep_alive": 0,
|
||||
}
|
||||
async with session.post(f"{base_url}/api/generate", json=payload, timeout=120) as response:
|
||||
if response.status != 200:
|
||||
err_txt = await response.text()
|
||||
return web.json_response({"status": "error", "message": f"Ollama HTTP {response.status}: {err_txt}"})
|
||||
resp_json = await response.json()
|
||||
generated_text = (resp_json.get("response") or "").strip()
|
||||
else:
|
||||
if provider == "ollama":
|
||||
payload = {
|
||||
"model": model_name, "prompt": _ANALYZE_PROMPT,
|
||||
"images": cleaned_b64_list, "stream": False, "keep_alive": 0,
|
||||
}
|
||||
async with session.post(f"{base_url}/api/generate", json=payload, timeout=300) as response:
|
||||
if response.status != 200:
|
||||
err_txt = await response.text()
|
||||
return web.json_response({"status": "error", "message": f"Ollama HTTP {response.status}: {err_txt}"})
|
||||
resp_json = await response.json()
|
||||
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:
|
||||
# OpenAI-compatible vision chat (LM Studio / Custom).
|
||||
content = [{"type": "text", "text": _ANALYZE_PROMPT}]
|
||||
for b64 in cleaned_b64_list:
|
||||
@@ -528,11 +572,16 @@ async def analyze_character_endpoint(request):
|
||||
"message": f"Could not connect to {provider} at {base_url}. Make sure the server is running and reachable.",
|
||||
})
|
||||
|
||||
if "<think>" in generated_text:
|
||||
generated_text = generated_text.split("</think>")[-1].strip()
|
||||
|
||||
log.info("[LTXDirector] Analysis complete: %s", generated_text)
|
||||
return web.json_response({"status": "success", "description": generated_text})
|
||||
if "<think>" in generated_text:
|
||||
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)
|
||||
return web.json_response({"status": "success", "description": generated_text})
|
||||
|
||||
except Exception as e:
|
||||
log.error(f"[LTXDirector] Failed to analyze character: {e}")
|
||||
|
||||
Reference in New Issue
Block a user