landvex: Fixar och tester klara för alla komponenter
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
quiXzoom Academy — Gemini Bridge via AAMOS
|
||||
Använder AAMOS Academy API för att generera bilder med Gemini
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
import base64
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
# AAMOS API-konfiguration
|
||||
AAMOS_BASE_URL = "http://localhost:3100"
|
||||
ACADEMY_API = f"{AAMOS_BASE_URL}/api/aamos/academy"
|
||||
|
||||
# S3-konfiguration
|
||||
S3_BUCKET = "quixzoom-landing-prod"
|
||||
ACADEMY_IMAGES_PATH = "academy/images"
|
||||
|
||||
# Lektionsbilder som ska genereras
|
||||
LESSON_IMAGES = {
|
||||
"m1l1": {
|
||||
"title": "Välkommen till quiXzoom",
|
||||
"prompt": """Generera en vänlig illustration för en online-utbildning om crowdsourcing-fotografering.
|
||||
|
||||
Scen: En person med smartphone fotograferar en bro i solnedgång. Personen ser glad och professionell ut.
|
||||
Stil: Modern, ren illustration (inte fotorealistisk)
|
||||
Färger: Blå (#0066FF), grön (#00C853), svart (#0A0A0A), vita accenter
|
||||
Format: 16:9 bredbild
|
||||
Ingen text i bilden
|
||||
Pedagogisk och inspirerande känsla"""
|
||||
},
|
||||
"m2l1": {
|
||||
"title": "AI-granskning av bilder",
|
||||
"prompt": """Generera en teknisk illustration som visar AI-analys av en fotografi.
|
||||
|
||||
Scen: Dela bilden i två delar. Vänster: Originalbild på en bro. Höger: Samma bild med AI-analys-overlay som markerar skärpa, ljus, komposition med färgkodade indikatorer.
|
||||
Stil: Futuristisk men pedagogisk
|
||||
Färger: Blå (#0066FF), grön (#00C853), orange (#FF6B35) för markeringar
|
||||
Format: 16:9
|
||||
Ingen text"""
|
||||
},
|
||||
"m2l2": {
|
||||
"title": "Ljus och exponering",
|
||||
"prompt": """Generera en pedagogisk jämförelsebild för fotoutbildning.
|
||||
|
||||
Scen: Tre bilder i rad som visar samma bro från samma vinkel:
|
||||
1. Vänster: Underexponerad (för mörk)
|
||||
2. Mitten: Perfekt exponerad (gyllene timmen, varmt ljus)
|
||||
3. Höger: Överexponerad (för ljus, utbrända detaljer)
|
||||
Stil: Fotorealistisk
|
||||
Format: 16:9 (tre paneler)
|
||||
Ingen text, men tydlig visuell skillnad"""
|
||||
},
|
||||
"m2l3": {
|
||||
"title": "Fokus och skärpa",
|
||||
"prompt": """Generera en pedagogisk bild som visar skillnaden mellan oskarp och skarp fotografi.
|
||||
|
||||
Scen: Två bilder som visar en vägskylt:
|
||||
1. Vänster: Suddig, oskarp bild
|
||||
2. Höger: Knivskarp bild med tydliga detaljer
|
||||
Lägg till ett förstoringsglas-effekt som visar detaljerna tydligare på den skarpa bilden.
|
||||
Stil: Fotorealistisk med grafiska element
|
||||
Format: 16:9
|
||||
Ingen text"""
|
||||
},
|
||||
"m2l4": {
|
||||
"title": "Framing och komposition",
|
||||
"prompt": """Generera en pedagogisk diagram-bild för fotokomposition.
|
||||
|
||||
Scen: En bro fotograferad med korrekt komposition. Overlay som visar:
|
||||
- Rule of thirds grid (3x3 linjer)
|
||||
- Huvudobjektet centrerat vid skärningspunkter
|
||||
- Pil som pekar på huvudobjektet
|
||||
- Grön markering för "bra" komposition
|
||||
Stil: Fotorealistisk med pedagogiska overlay-element
|
||||
Format: 16:9
|
||||
Ingen text"""
|
||||
},
|
||||
"m3l1": {
|
||||
"title": "GPS-accuracy",
|
||||
"prompt": """Generera en teknisk illustration om GPS-teknik.
|
||||
|
||||
Scen: En karta över en stad sett uppifrån. En smartphone i mitten som tar emot signaler från 4 GPS-satelliter ovanför (visa som linjer från satelliter till telefon). Färgkodad accuracy-cirkel runt telefonen:
|
||||
- Grön cirkel: Noggrann position (liten cirkel)
|
||||
- Gul cirkel: Medel (större cirkel)
|
||||
- Röd cirkel: Låg accuracy (stor cirkel)
|
||||
Stil: Modern, teknisk illustration
|
||||
Format: 16:9
|
||||
Ingen text"""
|
||||
},
|
||||
"m4l1": {
|
||||
"title": "Personlig säkerhet i fält",
|
||||
"prompt": """Generera en illustration om säker fotografering.
|
||||
|
||||
Scen: En person (Zoomer) med reflexväst och smartphone fotograferar en bro från ett säkert avstånd. Visa:
|
||||
- Reflexväst på personen
|
||||
- Trafikkegel som markerar säker zon
|
||||
- Säkerhetsavstånd markerat med streckad linje
|
||||
- Bil som passerar på håll
|
||||
Stil: Modern, vänlig illustration
|
||||
Färger: Orange varselväst, blå (#0066FF), grön (#00C853)
|
||||
Format: 16:9
|
||||
Ingen text"""
|
||||
},
|
||||
"m5l1": {
|
||||
"title": "Batch-fotografering",
|
||||
"prompt": """Generera en karta-baserad illustration för effektiv fotografering.
|
||||
|
||||
Scen: En stiliserad karta med flera fotopunkter (A, B, C, D, E) som är kopplade med en optimerad rutt (grön linje). En person med smartphone och cykel/bil följer rutten. Visa att personen fotograferar flera platser effektivt.
|
||||
Stil: Modern karta/illustration
|
||||
Färger: Grön rutt (#00C853), blå punkter (#0066FF), neutral bakgrund
|
||||
Format: 16:9
|
||||
Ingen text"""
|
||||
},
|
||||
"m6l1": {
|
||||
"title": "Utbetalningsflöde",
|
||||
"prompt": """Generera ett flödesdiagram för utbetalningsprocess.
|
||||
|
||||
Scen: Ett horisontellt flöde som visar:
|
||||
1. Godkänd fotografi (grön bock)
|
||||
2. AI-granskning (robot-ikon)
|
||||
3. Godkännande (stämpel)
|
||||
4. Utbetalning (pengar/bank)
|
||||
5. Pengar på kontot (glad person)
|
||||
Stil: Modern, ren illustration med ikoner
|
||||
Färger: Grön (#00C853) för framsteg, blå (#0066FF) för process
|
||||
Format: 16:9
|
||||
Ingen text, bara ikoner och pilar"""
|
||||
}
|
||||
}
|
||||
|
||||
def generate_with_aamos(prompt: str, provider: str = "gemini") -> dict:
|
||||
"""Generera innehåll via AAMOS Academy API"""
|
||||
|
||||
url = f"{ACADEMY_API}/ai/generate"
|
||||
|
||||
payload = {
|
||||
"prompt": prompt,
|
||||
"provider": provider,
|
||||
"maxTokens": 2000
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(url, json=payload, timeout=30)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
print(f" ⚠️ AAMOS API fel: {response.status_code}")
|
||||
return {"error": f"HTTP {response.status_code}"}
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(f" ⚠️ Kunde inte ansluta till AAMOS på {AAMOS_BASE_URL}")
|
||||
return {"error": "Connection failed"}
|
||||
except Exception as e:
|
||||
print(f" ⚠️ Fel: {e}")
|
||||
return {"error": str(e)}
|
||||
|
||||
def generate_image_description(lesson_id: str) -> str:
|
||||
"""Generera bildbeskrivning via AAMOS Gemini"""
|
||||
|
||||
lesson = LESSON_IMAGES.get(lesson_id)
|
||||
if not lesson:
|
||||
return None
|
||||
|
||||
print(f"🎨 Genererar bild för: {lesson['title']}")
|
||||
|
||||
# Använd AAMOS för att generera en detaljerad bildbeskrivning
|
||||
# (Gemini text-generering, inte bildgenerering ännu)
|
||||
result = generate_with_aamos(lesson['prompt'], "gemini")
|
||||
|
||||
if 'text' in result:
|
||||
return result['text']
|
||||
else:
|
||||
return None
|
||||
|
||||
def save_image_metadata(lesson_id: str, description: str):
|
||||
"""Spara bild-metadata för senare generering"""
|
||||
|
||||
metadata = {
|
||||
"lesson_id": lesson_id,
|
||||
"title": LESSON_IMAGES[lesson_id]['title'],
|
||||
"description": description,
|
||||
"prompt": LESSON_IMAGES[lesson_id]['prompt'],
|
||||
"generated_at": datetime.utcnow().isoformat(),
|
||||
"status": "description_ready",
|
||||
"image_url": None
|
||||
}
|
||||
|
||||
# Spara till lokal fil
|
||||
output_dir = Path("academy-generated")
|
||||
output_dir.mkdir(exist_ok=True)
|
||||
|
||||
with open(output_dir / f"{lesson_id}.json", "w") as f:
|
||||
json.dump(metadata, f, indent=2, ensure_ascii=False)
|
||||
|
||||
return metadata
|
||||
|
||||
def generate_all_images():
|
||||
"""Generera alla bildbeskrivningar för Academy"""
|
||||
|
||||
print("🚀 quiXzoom Academy — Bildgenerering via AAMOS Gemini")
|
||||
print("=" * 60)
|
||||
|
||||
results = []
|
||||
|
||||
for lesson_id, lesson in LESSON_IMAGES.items():
|
||||
print(f"\n📚 {lesson['title']}")
|
||||
|
||||
# Generera beskrivning via AAMOS
|
||||
description = generate_image_description(lesson_id)
|
||||
|
||||
if description:
|
||||
# Spara metadata
|
||||
metadata = save_image_metadata(lesson_id, description)
|
||||
results.append(metadata)
|
||||
print(f" ✅ Beskrivning genererad")
|
||||
else:
|
||||
print(f" ❌ Kunde inte generera")
|
||||
|
||||
# Spara sammanfattning
|
||||
summary = {
|
||||
"generated_at": datetime.utcnow().isoformat(),
|
||||
"total_images": len(LESSON_IMAGES),
|
||||
"successful": len(results),
|
||||
"failed": len(LESSON_IMAGES) - len(results),
|
||||
"images": results
|
||||
}
|
||||
|
||||
with open("academy-generated/summary.json", "w") as f:
|
||||
json.dump(summary, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f"✅ {len(results)}/{len(LESSON_IMAGES)} bildbeskrivningar genererade")
|
||||
print(f"📄 Sparade i academy-generated/")
|
||||
|
||||
return summary
|
||||
|
||||
def upload_to_s3(image_path: str, s3_key: str) -> str:
|
||||
"""Ladda upp bild till S3"""
|
||||
import subprocess
|
||||
|
||||
cmd = [
|
||||
"aws", "s3", "cp", image_path,
|
||||
f"s3://{S3_BUCKET}/{s3_key}",
|
||||
"--acl", "public-read",
|
||||
"--content-type", "image/png"
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
url = f"https://{S3_BUCKET}.s3.eu-north-1.amazonaws.com/{s3_key}"
|
||||
print(f" ✅ Uppladdad: {url}")
|
||||
return url
|
||||
else:
|
||||
print(f" ❌ Fel: {result.stderr}")
|
||||
return None
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "--generate":
|
||||
generate_all_images()
|
||||
else:
|
||||
print("Användning:")
|
||||
print(" python academy-gemini-bridge.py --generate")
|
||||
print("")
|
||||
print("Detta kommer att:")
|
||||
print(" 1. Ansluta till AAMOS Academy API (localhost:3100)")
|
||||
print(" 2. Generera bildbeskrivningar med Gemini")
|
||||
print(" 3. Spara metadata för varje bild")
|
||||
print("")
|
||||
print("När bildbeskrivningarna är klara kan du:")
|
||||
print(" - Använda dem för att generera riktiga bilder")
|
||||
print(" - Kopiera in dem i Academy-lektionerna")
|
||||
Reference in New Issue
Block a user