Files
creator/backend/models.py
2026-06-27 10:05:37 +02:00

298 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from pydantic import BaseModel, Field
from typing import Literal
FormatType = Literal[
"Guide",
"FullGuide",
"Rest",
]
ProviderType = Literal["claude", "minimax", "lokal"]
SourceType = Literal["thema", "projekt", "uni", "link"]
class GuideCreateRequest(BaseModel):
topic: str = Field(min_length=1, max_length=100)
format: FormatType
instructions: str = Field(default="", max_length=2000)
provider: ProviderType = "claude"
ab_step: int | None = Field(default=None, ge=0, le=4) # Re-Run ab Guide-Schritt (0 Gliederung … 4 Lese-Prüfung); None = voll/Resume
class TopicCreateRequest(BaseModel):
name: str = Field(min_length=1, max_length=100)
class BausteineCreateRequest(BaseModel):
topic: str = Field(min_length=1, max_length=100)
instructions: str = Field(default="", max_length=2000)
provider: ProviderType = "claude"
source_type: SourceType = "thema"
source_ort: str = Field(default="", max_length=2000)
ab_phase: int | None = Field(default=None, ge=1, le=9) # Re-Run ab grober Phase (Position in _phasen(topic), 1-based; bis 9: …Gliederung/Fragen/Artefakte); None = Resume/Fortsetzen ohne Löschen
class BausteineStep(BaseModel):
label: str
state: Literal["done", "active", "pending"]
class BausteineStatusResponse(BaseModel):
ready: bool
generating: bool
progress: str | None = None
error: str | None = None
partial: bool = False
steps: list[BausteineStep] = []
class ProjectResponse(BaseModel):
name: str
class FolderResponse(BaseModel):
name: str
ort: str # relativer Pfad ab Repo-Root (z.B. "projects/foo")
class BausteineQuelleUpdate(BaseModel):
topic: str = Field(min_length=1, max_length=100)
type: SourceType = "thema"
ort: str = Field(default="", max_length=2000)
spec: str = Field(default="", max_length=2000)
class BausteineQuelleResponse(BaseModel):
type: SourceType
ort: str
spec: str
class SubbausteinInfo(BaseModel):
titel: str
stufe: Literal["anfaenger", "fortgeschritten", "experte", "einfach", "mittel", "schwer"]
relevanz: Literal["relevant", "rand"] | None = None
class BausteinUebersicht(BaseModel):
num: int
titel: str
beschreibung: str = ""
subbausteine: list[SubbausteinInfo] = []
class ProviderInfo(BaseModel):
id: str
available: bool
class GuideResponse(BaseModel):
id: str
topic: str
format: str
status: str
progress: str | None = None
step: int | None = None
error_msg: str | None = None
created_at: str
updated_at: str
class ChatMessage(BaseModel):
role: Literal["user", "assistant"]
content: str = Field(min_length=1, max_length=8000)
class GuideChatRequest(BaseModel):
section: str = Field(default="", max_length=20000)
outline: str = Field(default="", max_length=8000)
messages: list[ChatMessage] = Field(min_length=1)
provider: ProviderType = "claude"
class GuideChatResponse(BaseModel):
reply: str
class ElementResponse(BaseModel):
id: str
topic: str
title: str
description: str = ""
examples: list[str] = []
hints: list[str] = []
created_at: str
updated_at: str
class ElementCreateRequest(BaseModel):
topic: str = Field(min_length=1, max_length=100)
hint: str = Field(default="", max_length=500)
provider: ProviderType = "claude"
class ElementUpdateRequest(BaseModel):
title: str | None = Field(default=None, max_length=200)
description: str | None = None
examples: list[str] | None = None
hints: list[str] | None = None
class ElementCheckRequest(BaseModel):
provider: ProviderType = "claude"
class ElementSuggestion(BaseModel):
text: str
target: Literal["description", "examples", "hints"]
content: str
class ElementCheckResponse(BaseModel):
suggestions: list[ElementSuggestion]
class ElementStyleChange(BaseModel):
text: str
action: Literal["entfernen", "anpassen", "hinzufuegen"]
target: Literal["title", "description", "examples", "hints"]
index: int | None = None
content: str = ""
class ElementStyleResponse(BaseModel):
changes: list[ElementStyleChange]
class ElementChatRequest(BaseModel):
messages: list[ChatMessage] = Field(min_length=1)
provider: ProviderType = "claude"
class ElementChatResponse(BaseModel):
reply: str
changes: list[ElementStyleChange] = []
class ElementRefineRequest(BaseModel):
suggestion: ElementStyleChange
instruction: str = Field(min_length=1, max_length=2000)
provider: ProviderType = "claude"
class ElementRefineResponse(BaseModel):
change: ElementStyleChange
class ProgressUpdate(BaseModel):
chapter: str = Field(min_length=1, max_length=100)
done: bool
class ProgressResponse(BaseModel):
chapters: list[str]
# --- Baustein-Lernen ---
class BausteinChatRequest(BaseModel):
topic: str = Field(min_length=1, max_length=100)
baustein: str = Field(min_length=1, max_length=200)
section: str = Field(default="", max_length=20000) # ausführliche Fassung
section_kompakt: str = Field(default="", max_length=20000) # kompakte Fassung (Merksätze)
messages: list[ChatMessage] = Field(min_length=1)
provider: ProviderType = "claude"
class BausteinChatResponse(BaseModel):
reply: str
class BausteinPruefungRequest(BaseModel):
topic: str = Field(min_length=1, max_length=100)
baustein: str = Field(min_length=1, max_length=200)
section: str = Field(default="", max_length=20000) # ausführliche Fassung
section_kompakt: str = Field(default="", max_length=20000) # kompakte Fassung (Merksätze)
aktion: Literal[
"frage", "diskussion", "antwort", "antwort_pruefen",
"quiz_frage", "quiz_antwort", "lueck_frage", "lueck_antwort",
] = "frage"
frage: str = Field(default="", max_length=2000) # aktuell geprüfte Frage (für diskussion/antwort); Anker der Basis
auswahl: list[int] = [] # Quiz/Lückentext-Auswahl: vom Lerner gewählte Options-Indizes
korrekt: list[int] = [] # Quiz/Lückentext-Auswahl: korrekte Indizes (Client hält sie aus der Generierung)
loesung: str = Field(default="", max_length=500) # Lückentext frei: erwarteter Begriff
alternativen: list[str] = [] # Lückentext frei: akzeptierte Synonyme
eingabe: str = Field(default="", max_length=500) # Lückentext frei: getippter Begriff
schwer: bool = False # Variante: leicht (+1/1) vs schwer (+3/1)
letzte_bewertung: str = Field(default="", max_length=2000) # Feedback der letzten Bewertung (Kontext für diskussion)
vermeide: list[str] = [] # schon gestellte + vorgemerkte Fragen — sinngemäß nicht wiederholen
nachgefragt: bool = False # für diese Frage wurde nachgefragt → Gewinn auf +1 gedeckelt
begruendung: str = Field(default="", max_length=2000) # „Gründlich prüfen": warum mit der Bewertung unzufrieden
muster: str = Field(default="", max_length=2000) # gezogenes Frage-Muster (Saat); leer → Live-Generierung (Fallback)
# Basis + cap werden serverseitig geführt (Anker bzw. Subs×25) — Client-cap nur Hinweis.
cap: int = Field(default=10, ge=1, le=10000) # Score-Deckel = freigeschaltete Subbausteine × 25
messages: list[ChatMessage] = [] # Dialog bisher; leer = erste Frage
provider: ProviderType = "claude"
gruendlich: bool = False # „Gründlich prüfen": Bewertung mit starkem Modell (role guide)
class QuizOption(BaseModel):
text: str
korrekt: bool
class BausteinPruefungResponse(BaseModel):
frage: str | None = None
reply: str | None = None
feedback: str | None = None
punkte: int | None = None # Punkt-Delta dieser Antwort (2 … +3); schnell = voraussichtlich
bewertung: Literal["gut", "neutral", "schlecht"] | None = None # aus Vorzeichen, fürs Einfärben
optionen: list[QuizOption] | None = None # Quiz: 4 Optionen + Korrekt-Flags
satz: str | None = None # Lückentext: Satz mit Lücke (___)
loesung: str | None = None # Lückentext: erwarteter Begriff
alternativen: list[str] | None = None # Lückentext: akzeptierte Synonyme
gute_antworten: int
streak: int = 0 # aktuelle Serie korrekter Antworten (je Baustein)
cap: int = 10 # cap_final = alle Subs × 25 — Frontend leitet die Lernstufe ab
class BausteinLernstand(BaseModel):
gute_antworten: int
streak: int = 0
cap: int = 0 # cap_final = alle Subbausteine × 25
cap_aktuell: int = 0 # erreichbarer cap der aktuell freigeschalteten Ebene
freie_ebene: int = 1 # 1=A · 2=F · 3=E · 4=V
class BausteinLernstandResponse(BaseModel):
bausteine: dict[str, BausteinLernstand]
# --- Block-Inhalt: einen Abschnitt on-demand prüfen + übernehmen (Fokus, Rechtsklick) ---
class BlockPruefenRequest(BaseModel):
baustein: str = Field(min_length=1, max_length=200)
stelle: str = "ausführlich" # "kompakt" | "ausführlich" (angezeigtes Feld)
block: str = Field(min_length=1, max_length=20000) # roher Markdown-Block
hinweis: str = Field(default="", max_length=2000) # optionaler Zusatz (✏️)
provider: ProviderType = "claude"
class BlockPruefenResponse(BaseModel):
neu: str # korrigierter Block als Markdown
class BlockUebernehmenRequest(BaseModel):
baustein: str = Field(min_length=1, max_length=200)
stelle: str = "ausführlich"
alt: str = Field(min_length=1, max_length=20000)
neu: str = Field(default="", max_length=20000)
provider: ProviderType = "claude"
class BlockUebernehmenResponse(BaseModel):
kompakt: str
md: str
gefunden: bool