diff --git a/ltx_director.py b/ltx_director.py
index 8cf2eee..f861426 100644
--- a/ltx_director.py
+++ b/ltx_director.py
@@ -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 "" in text:
+ text = text.split("")[-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 "" in generated_text:
- generated_text = generated_text.split("")[-1].strip()
-
- log.info("[LTXDirector] Analysis complete: %s", generated_text)
- return web.json_response({"status": "success", "description": generated_text})
+ if "" in generated_text:
+ generated_text = generated_text.split("")[-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}")