Sanitize leaked reasoning in character analysis
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import base64
|
import base64
|
||||||
import io as _io
|
import io as _io
|
||||||
import math
|
import math
|
||||||
|
import re
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
@@ -482,6 +483,65 @@ def _analysis_text_is_usable(text: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _sanitize_analysis_text(text: str) -> str:
|
||||||
|
"""Strip prompt-echo / reasoning junk and keep the shortest usable visual description."""
|
||||||
|
text = (text or "").strip()
|
||||||
|
if not text:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
if "<think>" in text:
|
||||||
|
text = text.split("</think>")[-1].strip()
|
||||||
|
|
||||||
|
# Drop markdown emphasis and common prompt-echo scaffolding.
|
||||||
|
lines = []
|
||||||
|
for raw_line in text.splitlines():
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
lower = line.lower()
|
||||||
|
if lower.startswith("the user wants"):
|
||||||
|
continue
|
||||||
|
if lower.startswith("sentence 1 requirements"):
|
||||||
|
continue
|
||||||
|
if lower.startswith("sentence 2 requirements"):
|
||||||
|
continue
|
||||||
|
if lower.startswith("requirements:"):
|
||||||
|
continue
|
||||||
|
if lower.startswith("let's interpret"):
|
||||||
|
continue
|
||||||
|
if lower.startswith("wait, the prompt says"):
|
||||||
|
continue
|
||||||
|
if lower.startswith("do not "):
|
||||||
|
continue
|
||||||
|
if line.startswith("- ") or line.startswith("* "):
|
||||||
|
continue
|
||||||
|
cleaned = line.replace("**", "").strip()
|
||||||
|
if cleaned:
|
||||||
|
lines.append(cleaned)
|
||||||
|
|
||||||
|
text = " ".join(lines).strip()
|
||||||
|
text = re.sub(r"\s+", " ", text)
|
||||||
|
if not text:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# If the model echoed instructions and then described the subject, keep the last descriptive sentences.
|
||||||
|
sentences = re.split(r"(?<=[.!?])\s+", text)
|
||||||
|
descriptive = []
|
||||||
|
for sentence in sentences:
|
||||||
|
sentence = sentence.strip()
|
||||||
|
if not sentence:
|
||||||
|
continue
|
||||||
|
lower = sentence.lower()
|
||||||
|
if "prompt says" in lower or "requirements" in lower or "the user wants" in lower:
|
||||||
|
continue
|
||||||
|
descriptive.append(sentence)
|
||||||
|
|
||||||
|
if descriptive:
|
||||||
|
text = " ".join(descriptive[-2:]).strip()
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
def _compress_analysis_image_b64(b64_payload: str, max_dim: int = 768, quality: int = 82) -> str:
|
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."""
|
"""Shrink analysis images so multimodal providers do not burn their full context on pixels."""
|
||||||
try:
|
try:
|
||||||
@@ -653,8 +713,7 @@ async def analyze_character_endpoint(request):
|
|||||||
"message": f"Could not connect to {provider} at {base_url}. Make sure the server is running and reachable.",
|
"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 = _sanitize_analysis_text(generated_text)
|
||||||
generated_text = generated_text.split("</think>")[-1].strip()
|
|
||||||
if not _analysis_text_is_usable(generated_text):
|
if not _analysis_text_is_usable(generated_text):
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
"status": "error",
|
"status": "error",
|
||||||
|
|||||||
Reference in New Issue
Block a user