Spaces:
Sleeping
Sleeping
First commit
Browse files- __init__.py +0 -0
- app/card_renderer.py +26 -0
- app/constraints.py +12 -0
- app/generator.py +41 -0
- app/hf_api.py +2 -0
- app/lora_config.py +2 -0
- app/main.py +19 -0
- app/model_loader.py +2 -0
- app/qr_utils.py +0 -0
- assets/fonts/hand.ttf +0 -0
- requirements.txt +7 -0
- templates/de/base.txt +11 -0
- templates/en/base.txt +11 -0
__init__.py
ADDED
|
File without changes
|
app/card_renderer.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from PIL import Image, ImageDraw, ImageFont
|
| 2 |
+
import uuid
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
def generate_card(tarot_id: int, symbol_ids: list[int], text: str) -> str:
|
| 6 |
+
base_path = Path(__file__).parent
|
| 7 |
+
tarot_img = Image.open(base_path / f"assets/images/base/{tarot_id}.png").convert("RGBA")
|
| 8 |
+
|
| 9 |
+
# Symbole hinzufügen
|
| 10 |
+
for sid in symbol_ids:
|
| 11 |
+
symbol = Image.open(base_path / f"assets/images/symbols/{sid}.png").convert("RGBA")
|
| 12 |
+
tarot_img.paste(symbol, (50 + sid*10, 400), symbol) # Positionierung frei wählbar
|
| 13 |
+
|
| 14 |
+
# Text hinzufügen
|
| 15 |
+
draw = ImageDraw.Draw(tarot_img)
|
| 16 |
+
font = ImageFont.truetype(str(base_path / "assets/fonts/hand.ttf"), 20)
|
| 17 |
+
draw.text((50, tarot_img.height - 150), text, font=font, fill="black")
|
| 18 |
+
|
| 19 |
+
# Speichern
|
| 20 |
+
output_dir = base_path / "assets/images/generated"
|
| 21 |
+
output_dir.mkdir(exist_ok=True)
|
| 22 |
+
file_id = str(uuid.uuid4())
|
| 23 |
+
output_path = output_dir / f"{file_id}.png"
|
| 24 |
+
tarot_img.save(output_path)
|
| 25 |
+
|
| 26 |
+
return file_id # nur ID zurückgeben
|
app/constraints.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# constraints.py
|
| 2 |
+
# Constraints: Begriffe prüfen, Filter
|
| 3 |
+
|
| 4 |
+
def check_constraints(output: str, terms: list[str]) -> bool:
|
| 5 |
+
return all(term.lower() in output.lower() for term in terms)
|
| 6 |
+
|
| 7 |
+
def generate_with_retry(prompt, generator, terms, max_retries=3):
|
| 8 |
+
for _ in range(max_retries):
|
| 9 |
+
response = generator(prompt)[0]["generated_text"]
|
| 10 |
+
if check_constraints(response, terms):
|
| 11 |
+
return response
|
| 12 |
+
return "Leider konnte kein gültiger Text erzeugt werden."
|
app/generator.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
from jinja2 import Template
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
import datetime
|
| 8 |
+
|
| 9 |
+
""" MMDD → MM = Monat, DD = Tag
|
| 10 |
+
120 = 01.20. → Ende Steinbock
|
| 11 |
+
218 = 02.18. → Ende Wassermann
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
def get_zodiac(birthdate: str) -> str:
|
| 15 |
+
"""Leitet das Sternzeichen aus dem Geburtsdatum ab."""
|
| 16 |
+
date = datetime.datetime.strptime(birthdate, "%Y-%m-%d").date()
|
| 17 |
+
zodiacs = [
|
| 18 |
+
(120, "Steinbock"), (218, "Wassermann"), (320, "Fische"), (420, "Widder"),
|
| 19 |
+
(521, "Stier"), (621, "Zwillinge"), (722, "Krebs"), (823, "Löwe"),
|
| 20 |
+
(923, "Jungfrau"), (1023, "Waage"), (1122, "Skorpion"), (1222, "Schütze"), (1231, "Steinbock")
|
| 21 |
+
]
|
| 22 |
+
date_as_number = int(f"{date.month:02d}{date.day:02d}")
|
| 23 |
+
for boundary, sign in zodiacs:
|
| 24 |
+
if date_as_number <= boundary:
|
| 25 |
+
return sign
|
| 26 |
+
return "Steinbock"
|
| 27 |
+
|
| 28 |
+
def build_prompt(lang: str, birthdate: str, terms: list[str]) -> str:
|
| 29 |
+
template_path = Path(__file__).parent / "templates" / lang / "base.txt"
|
| 30 |
+
if not template_path.exists():
|
| 31 |
+
raise FileNotFoundError(f"Template nicht gefunden: {template_path}")
|
| 32 |
+
|
| 33 |
+
template = Template(template_path.read_text(encoding="utf-8"))
|
| 34 |
+
sternzeichen = get_zodiac(birthdate)
|
| 35 |
+
|
| 36 |
+
prompt = template.render(
|
| 37 |
+
birthdate=birthdate,
|
| 38 |
+
terms=', '.join(terms),
|
| 39 |
+
sternzeichen=sternzeichen
|
| 40 |
+
)
|
| 41 |
+
return prompt
|
app/hf_api.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# hf_api.py
|
| 2 |
+
# Optional: HuggingFace API-Wrapper
|
app/lora_config.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# lora_config.py
|
| 2 |
+
# LoRA-Config (separat gehalten für Training)
|
app/main.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from fastapi import FastAPI, Request
|
| 3 |
+
from pydantic import BaseModel
|
| 4 |
+
from generator import build_prompt, generate_with_retry
|
| 5 |
+
from model_loader import generator
|
| 6 |
+
|
| 7 |
+
SECRET_KEY = os.getenv("SECRET_KEY")
|
| 8 |
+
|
| 9 |
+
app = FastAPI()
|
| 10 |
+
|
| 11 |
+
class GenRequest(BaseModel):
|
| 12 |
+
birthdate: str
|
| 13 |
+
terms: list[str]
|
| 14 |
+
|
| 15 |
+
@app.post("/generate")
|
| 16 |
+
def generate_text(req: GenRequest):
|
| 17 |
+
prompt = build_prompt(req.birthdate, req.terms)
|
| 18 |
+
output = generate_with_retry(prompt, generator, req.terms)
|
| 19 |
+
return {"output": output}
|
app/model_loader.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# model_loader.py
|
| 2 |
+
# LLM laden (lokal oder remote)
|
app/qr_utils.py
ADDED
|
File without changes
|
assets/fonts/hand.ttf
ADDED
|
Binary file (72.3 kB). View file
|
|
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn[standard]
|
| 3 |
+
jinja2
|
| 4 |
+
qrcode
|
| 5 |
+
pillow
|
| 6 |
+
transformers
|
| 7 |
+
peft
|
templates/de/base.txt
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Erstelle ein humorvolles, sex-positives Horoskop im Tarot-Stil.
|
| 2 |
+
|
| 3 |
+
Eingaben:
|
| 4 |
+
- Geburtsdatum: {{birthdate}}
|
| 5 |
+
- Begriffe: {{terms}}
|
| 6 |
+
|
| 7 |
+
Format:
|
| 8 |
+
Titel: [Kreativer Titel]
|
| 9 |
+
Sternzeichen: [aus Geburtsdatum ableiten]
|
| 10 |
+
Text:
|
| 11 |
+
[3–5 Sätze, die alle Begriffe enthalten, lustig, doppeldeutig, aber niemals beleidigend oder vulgär]
|
templates/en/base.txt
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Erstelle ein humorvolles, sex-positives Horoskop im Tarot-Stil.
|
| 2 |
+
|
| 3 |
+
Eingaben:
|
| 4 |
+
- Geburtsdatum: {{birthdate}}
|
| 5 |
+
- Begriffe: {{terms}}
|
| 6 |
+
|
| 7 |
+
Format:
|
| 8 |
+
Titel: [Kreativer Titel]
|
| 9 |
+
Sternzeichen: [aus Geburtsdatum ableiten]
|
| 10 |
+
Text:
|
| 11 |
+
[3–5 Sätze, die alle Begriffe enthalten, lustig, doppeldeutig, aber niemals beleidigend oder vulgär]
|