Retry truncated Ollama character analysis
This commit is contained in:
@@ -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."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -470,6 +471,17 @@ def _extract_ollama_generated_text(resp_json: dict) -> str:
|
|||||||
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):
|
||||||
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"])
|
||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user