Feat: Bildschirm-Sicht Multi-Monitor — beide Screens an das Vision-Modell
ChatIn.images (Liste, 1 data-URL je Monitor); _describe_images schickt alle Screenshots in EINER Nachricht ans VL-Modell -> Lucy sieht beide Bildschirme. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+22
-16
@@ -28,18 +28,23 @@ router = APIRouter(prefix="/api")
|
||||
VISION_MODEL = os.environ.get("MC_VISION_MODEL", "vision")
|
||||
|
||||
|
||||
async def _describe_image(image_url: str, hint: str) -> str:
|
||||
"""Lässt das Vision-Modell den Screenshot knapp beschreiben (Deutsch). Leerer String bei Fehler."""
|
||||
prompt = ("Beschreibe knapp und präzise auf Deutsch, was auf diesem Screenshot zu sehen ist "
|
||||
"(App/Fenster, wichtige Inhalte, sichtbarer Text/Code). Frage des Nutzers dazu: " + hint)
|
||||
async def _describe_images(image_urls: list[str], hint: str) -> str:
|
||||
"""Lässt das Vision-Modell die Screenshots (1 je Monitor) knapp beschreiben (Deutsch).
|
||||
Mehrere Bilder gehen in EINER Nachricht ans VL-Modell. Leerer String bei Fehler."""
|
||||
multi = len(image_urls) > 1
|
||||
intro = (f"Hier sind {len(image_urls)} Screenshots (je ein Monitor). Beschreibe knapp und präzise auf Deutsch, "
|
||||
"was auf den Bildschirmen zu sehen ist (pro Monitor: App/Fenster, wichtige Inhalte, sichtbarer Text/Code). "
|
||||
if multi else
|
||||
"Beschreibe knapp und präzise auf Deutsch, was auf diesem Screenshot zu sehen ist "
|
||||
"(App/Fenster, wichtige Inhalte, sichtbarer Text/Code). ")
|
||||
content: list = [{"type": "text", "text": intro + "Frage des Nutzers dazu: " + hint}]
|
||||
for u in image_urls:
|
||||
content.append({"type": "image_url", "image_url": {"url": u}})
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=httpx.Timeout(90.0, connect=5.0)) as client:
|
||||
async with httpx.AsyncClient(timeout=httpx.Timeout(120.0, connect=5.0)) as client:
|
||||
r = await client.post(f"{LLAMA_SWAP_URL}/v1/chat/completions", json={
|
||||
"model": VISION_MODEL, "max_tokens": 450, "stream": False,
|
||||
"messages": [{"role": "user", "content": [
|
||||
{"type": "text", "text": prompt},
|
||||
{"type": "image_url", "image_url": {"url": image_url}},
|
||||
]}],
|
||||
"model": VISION_MODEL, "max_tokens": 600, "stream": False,
|
||||
"messages": [{"role": "user", "content": content}],
|
||||
})
|
||||
r.raise_for_status()
|
||||
return (r.json().get("choices") or [{}])[0].get("message", {}).get("content", "").strip()
|
||||
@@ -64,7 +69,7 @@ class ChatIn(BaseModel):
|
||||
session_key: str = "" # optional: Langzeit-Memory-Scope
|
||||
system: str = "" # optionaler ephemerer System-Prompt (z.B. „antworte knapp/gesprochen")
|
||||
model: str = ""
|
||||
image: str = "" # optionale Bildschirm-Sicht: Bild als data:-URL (Lucys „Augen")
|
||||
images: list[str] = [] # optionale Bildschirm-Sicht: ein data:-URL je Monitor (Lucys „Augen")
|
||||
|
||||
|
||||
@router.get("/voice/health")
|
||||
@@ -167,12 +172,13 @@ async def voice_chat(body: ChatIn) -> StreamingResponse:
|
||||
if body.system:
|
||||
messages.append({"role": "system", "content": body.system})
|
||||
user_text = body.text
|
||||
if body.image:
|
||||
# Bildschirm-Sicht: erst das Vision-Modell beschreiben lassen, dann die Beschreibung als TEXT-
|
||||
# Kontext an Hermes (Lucy antwortet mit vollem Hirn/Gedächtnis, sieht aber via besserem VL-Modell).
|
||||
desc = await _describe_image(body.image, body.text)
|
||||
imgs = [u for u in (body.images or []) if u]
|
||||
if imgs:
|
||||
# Bildschirm-Sicht: erst das Vision-Modell die Monitore beschreiben lassen, dann die Beschreibung
|
||||
# als TEXT-Kontext an Hermes (Lucy antwortet mit vollem Hirn/Gedächtnis, sieht via besserem VL-Modell).
|
||||
desc = await _describe_images(imgs, body.text)
|
||||
if desc:
|
||||
user_text = f"[Bildschirm-Sicht — das ist gerade auf dem Schirm zu sehen:\n{desc}\n]\n\n{body.text}"
|
||||
user_text = f"[Bildschirm-Sicht — das ist gerade auf dem/den Schirm(en) zu sehen:\n{desc}\n]\n\n{body.text}"
|
||||
messages.append({"role": "user", "content": user_text})
|
||||
payload = {"model": body.model or HERMES_API_MODEL, "messages": messages, "stream": True}
|
||||
headers = {
|
||||
|
||||
Reference in New Issue
Block a user