Retry truncated Ollama character analysis

This commit is contained in:
OpenClaw Agent
2026-07-10 10:22:47 +00:00
parent f0cd0b382d
commit c5a4aecdd7

View File

@@ -438,9 +438,10 @@ _PROVIDER_DEFAULTS = {
} }
_ANALYZE_PROMPT = ( _ANALYZE_PROMPT = (
"Describe the character's physical appearance in two concise sentences. " "Describe only the visible character in exactly two complete sentences. "
"Specify their hair color/style, face details, and their clothing type/color. " "Sentence 1 must cover hair, face, age impression, and any standout physical traits. "
"Keep the entire response very brief." "Sentence 2 must cover clothing, accessories, and overall silhouette. "
"Do not start with fragments like 'The'. Do not mention image quality, background, framing, or emotions."
) )
@@ -468,6 +469,17 @@ def _extract_ollama_generated_text(resp_json: dict) -> str:
if text: if text:
return text return text
return "" return ""
def _analysis_text_is_usable(text: str) -> bool:
text = (text or "").strip()
if not text:
return False
if len(text) < 24:
return False
if len(text.split()) < 6:
return False
return True
def _resolve_provider(data): def _resolve_provider(data):
@@ -515,8 +527,15 @@ async def analyze_character_endpoint(request):
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
if provider == "ollama": if provider == "ollama":
payload = { payload = {
"model": model_name, "prompt": _ANALYZE_PROMPT, "model": model_name,
"images": cleaned_b64_list, "stream": False, "keep_alive": 0, "prompt": _ANALYZE_PROMPT,
"images": cleaned_b64_list,
"stream": False,
"keep_alive": 0,
"options": {
"temperature": 0.2,
"num_predict": 120,
},
} }
async with session.post(f"{base_url}/api/generate", json=payload, timeout=300) 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:
@@ -525,9 +544,9 @@ async def analyze_character_endpoint(request):
resp_json = await response.json() resp_json = await response.json()
generated_text = _extract_ollama_generated_text(resp_json) generated_text = _extract_ollama_generated_text(resp_json)
# Some Ollama model variants return an empty `response` from `/api/generate` # Some Ollama model variants return an empty or truncated response from
# even though the same request succeeds via the chat endpoint. # `/api/generate` even though the same request succeeds via the chat endpoint.
if not generated_text: if not _analysis_text_is_usable(generated_text):
chat_payload = { chat_payload = {
"model": model_name, "model": model_name,
"messages": [{ "messages": [{
@@ -537,6 +556,10 @@ async def analyze_character_endpoint(request):
}], }],
"stream": False, "stream": False,
"keep_alive": 0, "keep_alive": 0,
"options": {
"temperature": 0.2,
"num_predict": 120,
},
} }
async with session.post(f"{base_url}/api/chat", json=chat_payload, timeout=300) as response: async with session.post(f"{base_url}/api/chat", json=chat_payload, timeout=300) as response:
if response.status == 200: if response.status == 200:
@@ -574,10 +597,10 @@ 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: if not _analysis_text_is_usable(generated_text):
return web.json_response({ return web.json_response({
"status": "error", "status": "error",
"message": f"{provider} returned an empty analysis response.", "message": f"{provider} returned an empty or truncated analysis response.",
}) })
log.info("[LTXDirector] Analysis complete: %s", generated_text) log.info("[LTXDirector] Analysis complete: %s", generated_text)