mirror of
https://github.com/Jeuners/astra-vision.git
synced 2026-09-09 15:02:35 +02:00
Zwei neue, sauber getrennte Fähigkeiten, jede in ihrem eigenen Modul: - astra/comfyui.py: async HTTP-Client für einen lokalen ComfyUI-Server (z-image-turbo-Workflow), kennt nichts von der Pipeline. - astra/documents.py: PDF-Textextraktion via pypdf, keine Netzwerkzugriffe. - astra/tools.py: verdrahtet generate_image als natives Ollama-Tool-Call — Qwen 3.5 unterstützt Tools und Vision bereits nativ laut `ollama show`. Dafür wurde astra/services.py so erweitert, dass NativeOllamaService Ollamas native tool_calls im Streaming-Response erkennt und als ChatCompletionChunk-Deltas an Pipecats bereits vorhandene, generische Function-Calling-Maschinerie (_process_context/run_function_calls) weiterreicht — die musste dafür nicht angefasst werden. trim_messages in core.py bewahrt jetzt Tool-Roundtrips und Bild-Anhänge vollständig statt sie auf role/content zu reduzieren. Neuer Upload-Button im UI (Bild oder PDF, während eines laufenden Gesprächs): PDFs gehen als Text, Bilder als Base64 über Qwens Vision in den Gesprächskontext ein. Generierte Bilder werden über /api/media/<id> ausgeliefert und per Datenkanal im Transkript angezeigt. Kompletter Function-Calling-Roundtrip end-to-end gegen echtes Ollama und echtes ComfyUI verifiziert (Modell ruft generate_image korrekt auf, Bild wird erzeugt und im media_store abgelegt). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVgSHNHdRx3UNTBodFmhRA
60 lines
2 KiB
Python
60 lines
2 KiB
Python
import pytest
|
|
|
|
import astra.tools as tools_module
|
|
from astra.comfyui import ComfyUIError
|
|
from astra.core import Settings
|
|
|
|
|
|
class FakeFunctionCallParams:
|
|
"""Stand-in for pipecat's FunctionCallParams: only what the handler touches."""
|
|
|
|
def __init__(self, arguments):
|
|
self.arguments = arguments
|
|
self.results = []
|
|
|
|
async def result_callback(self, result, *, properties=None):
|
|
self.results.append(result)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_image_tool_stores_media_and_notifies(monkeypatch):
|
|
async def fake_generate_image(endpoint, prompt, **kwargs):
|
|
assert endpoint == "http://fake-comfyui"
|
|
assert prompt == "a cat"
|
|
return b"png-bytes"
|
|
|
|
monkeypatch.setattr(tools_module, "generate_image", fake_generate_image)
|
|
|
|
notifications = []
|
|
media_store: dict[str, bytes] = {}
|
|
schema = tools_module.build_tools(
|
|
Settings(comfyui_url="http://fake-comfyui"), notifications.append, media_store
|
|
).standard_tools[0]
|
|
|
|
params = FakeFunctionCallParams({"prompt": "a cat"})
|
|
await schema.handler(params)
|
|
|
|
assert len(media_store) == 1
|
|
image_id, image_bytes = next(iter(media_store.items()))
|
|
assert image_bytes == b"png-bytes"
|
|
assert notifications == [{"type": "image", "url": f"/api/media/{image_id}"}]
|
|
assert params.results[0]["status"] == "ok"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_image_tool_reports_comfyui_errors_without_storing_media(monkeypatch):
|
|
async def failing_generate_image(endpoint, prompt, **kwargs):
|
|
raise ComfyUIError("ComfyUI ist nicht erreichbar.")
|
|
|
|
monkeypatch.setattr(tools_module, "generate_image", failing_generate_image)
|
|
|
|
media_store: dict[str, bytes] = {}
|
|
schema = tools_module.build_tools(
|
|
Settings(comfyui_url="http://fake-comfyui"), lambda _: None, media_store
|
|
).standard_tools[0]
|
|
|
|
params = FakeFunctionCallParams({"prompt": "a cat"})
|
|
await schema.handler(params)
|
|
|
|
assert media_store == {}
|
|
assert "error" in params.results[0]
|