Reduce Ollama analysis image context
This commit is contained in:
@@ -482,6 +482,30 @@ def _analysis_text_is_usable(text: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _compress_analysis_image_b64(b64_payload: str, max_dim: int = 768, quality: int = 82) -> str:
|
||||||
|
"""Shrink analysis images so multimodal providers do not burn their full context on pixels."""
|
||||||
|
try:
|
||||||
|
raw = base64.b64decode(_normalise_b64_payload(b64_payload))
|
||||||
|
with Image.open(_io.BytesIO(raw)) as img:
|
||||||
|
img = img.convert("RGB")
|
||||||
|
w, h = img.size
|
||||||
|
if max(w, h) > max_dim:
|
||||||
|
scale = max_dim / float(max(w, h))
|
||||||
|
img = img.resize((max(1, int(round(w * scale))), max(1, int(round(h * scale)))), Image.LANCZOS)
|
||||||
|
out = _io.BytesIO()
|
||||||
|
img.save(out, format="JPEG", quality=quality, optimize=True)
|
||||||
|
return base64.b64encode(out.getvalue()).decode("ascii")
|
||||||
|
except Exception:
|
||||||
|
return _normalise_b64_payload(b64_payload)
|
||||||
|
|
||||||
|
|
||||||
|
def _prepare_analysis_images(cleaned_b64_list: list[str], max_dim: int = 768, quality: int = 82) -> list[str]:
|
||||||
|
return [
|
||||||
|
_compress_analysis_image_b64(b64, max_dim=max_dim, quality=quality)
|
||||||
|
for b64 in cleaned_b64_list
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
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"])
|
||||||
@@ -513,6 +537,7 @@ async def analyze_character_endpoint(request):
|
|||||||
cleaned_b64_list.append(b64)
|
cleaned_b64_list.append(b64)
|
||||||
if not cleaned_b64_list:
|
if not cleaned_b64_list:
|
||||||
return web.json_response({"status": "error", "message": "No valid base64 images decoded."})
|
return web.json_response({"status": "error", "message": "No valid base64 images decoded."})
|
||||||
|
analysis_images = _prepare_analysis_images(cleaned_b64_list, max_dim=768, quality=82)
|
||||||
|
|
||||||
if provider in ("lmstudio", "custom") and not model_name:
|
if provider in ("lmstudio", "custom") and not model_name:
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
@@ -526,10 +551,11 @@ async def analyze_character_endpoint(request):
|
|||||||
try:
|
try:
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
if provider == "ollama":
|
if provider == "ollama":
|
||||||
|
generated_text = ""
|
||||||
payload = {
|
payload = {
|
||||||
"model": model_name,
|
"model": model_name,
|
||||||
"prompt": _ANALYZE_PROMPT,
|
"prompt": _ANALYZE_PROMPT,
|
||||||
"images": cleaned_b64_list,
|
"images": analysis_images,
|
||||||
"stream": False,
|
"stream": False,
|
||||||
"keep_alive": 0,
|
"keep_alive": 0,
|
||||||
"options": {
|
"options": {
|
||||||
@@ -538,6 +564,17 @@ async def analyze_character_endpoint(request):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
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:
|
||||||
|
err_txt = await response.text()
|
||||||
|
if response.status == 400 and "exceeds the available context size" in err_txt:
|
||||||
|
analysis_images = _prepare_analysis_images(cleaned_b64_list, max_dim=512, quality=70)
|
||||||
|
else:
|
||||||
|
return web.json_response({"status": "error", "message": f"Ollama HTTP {response.status}: {err_txt}"})
|
||||||
|
else:
|
||||||
|
resp_json = await response.json()
|
||||||
|
generated_text = _extract_ollama_generated_text(resp_json)
|
||||||
|
if not generated_text:
|
||||||
|
async with session.post(f"{base_url}/api/generate", json={**payload, "images": analysis_images}, 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}"})
|
||||||
@@ -552,7 +589,7 @@ async def analyze_character_endpoint(request):
|
|||||||
"messages": [{
|
"messages": [{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": _ANALYZE_PROMPT,
|
"content": _ANALYZE_PROMPT,
|
||||||
"images": cleaned_b64_list,
|
"images": analysis_images,
|
||||||
}],
|
}],
|
||||||
"stream": False,
|
"stream": False,
|
||||||
"keep_alive": 0,
|
"keep_alive": 0,
|
||||||
@@ -565,10 +602,31 @@ async def analyze_character_endpoint(request):
|
|||||||
if response.status == 200:
|
if response.status == 200:
|
||||||
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)
|
||||||
|
else:
|
||||||
|
err_txt = await response.text()
|
||||||
|
if response.status == 400 and "exceeds the available context size" in err_txt:
|
||||||
|
smaller_images = _prepare_analysis_images(cleaned_b64_list, max_dim=384, quality=60)
|
||||||
|
retry_payload = {
|
||||||
|
**chat_payload,
|
||||||
|
"messages": [{
|
||||||
|
"role": "user",
|
||||||
|
"content": _ANALYZE_PROMPT,
|
||||||
|
"images": smaller_images,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
async with session.post(f"{base_url}/api/chat", json=retry_payload, timeout=300) as retry_response:
|
||||||
|
if retry_response.status == 200:
|
||||||
|
resp_json = await retry_response.json()
|
||||||
|
generated_text = _extract_ollama_generated_text(resp_json)
|
||||||
|
else:
|
||||||
|
err_txt = await retry_response.text()
|
||||||
|
return web.json_response({"status": "error", "message": f"Ollama HTTP {retry_response.status}: {err_txt}"})
|
||||||
|
else:
|
||||||
|
return web.json_response({"status": "error", "message": f"Ollama HTTP {response.status}: {err_txt}"})
|
||||||
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}]
|
||||||
for b64 in cleaned_b64_list:
|
for b64 in analysis_images:
|
||||||
content.append({"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}})
|
content.append({"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}})
|
||||||
payload = {
|
payload = {
|
||||||
"model": model_name,
|
"model": model_name,
|
||||||
|
|||||||
Reference in New Issue
Block a user