astra-vision/astra/comfyui_workflow.json
Jeuner 16786fd3fd feat: image generation via ComfyUI and PDF/image upload
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
2026-09-07 15:56:18 +02:00

59 lines
No EOL
1.6 KiB
JSON

{
"9": {
"inputs": { "filename_prefix": "z+krea2", "images": ["57:8", 0] },
"class_type": "SaveImage"
},
"57:30": {
"inputs": { "clip_name": "qwen_3_4b.safetensors", "type": "lumina2", "device": "default" },
"class_type": "CLIPLoader"
},
"57:29": {
"inputs": { "vae_name": "ae.safetensors" },
"class_type": "VAELoader"
},
"57:33": {
"inputs": { "conditioning": ["57:27", 0] },
"class_type": "ConditioningZeroOut"
},
"57:8": {
"inputs": { "samples": ["57:3", 0], "vae": ["57:29", 0] },
"class_type": "VAEDecode"
},
"57:28": {
"inputs": { "unet_name": "z_image_turbo_bf16.safetensors", "weight_dtype": "default" },
"class_type": "UNETLoader"
},
"57:51": {
"inputs": {
"model": ["57:28", 0],
"clip": ["57:30", 0],
"lora_name": "krea2_turbo_lora_rank_64_bf16.safetensors",
"strength_model": 0.85,
"strength_clip": 0.85
},
"class_type": "LoraLoader"
},
"57:27": {
"inputs": { "text": "__PROMPT__", "clip": ["57:51", 1] },
"class_type": "CLIPTextEncode"
},
"57:13": {
"inputs": { "width": 1024, "height": 1024, "batch_size": 1 },
"class_type": "EmptySD3LatentImage"
},
"57:11": {
"inputs": { "shift": 3, "model": ["57:51", 0] },
"class_type": "ModelSamplingAuraFlow"
},
"57:3": {
"inputs": {
"seed": 0, "steps": 8, "cfg": 1,
"sampler_name": "res_multistep", "scheduler": "simple", "denoise": 1,
"model": ["57:11", 0],
"positive": ["57:27", 0],
"negative": ["57:33", 0],
"latent_image": ["57:13", 0]
},
"class_type": "KSampler"
}
}