This commit is contained in:
team3
2026-06-30 00:14:18 +02:00
parent 3e3559aa8f
commit c794fcaccf
152 changed files with 9485 additions and 9583 deletions

View File

@@ -17,47 +17,47 @@ class GuideCreateRequest(BaseModel):
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
ab_step: int | None = Field(default=None, ge=0, le=4) # re-run from guide step (0 outline … 4 read-exam); None = full/resume
class TopicCreateRequest(BaseModel):
name: str = Field(min_length=1, max_length=100)
class BausteineCreateRequest(BaseModel):
class BlocksCreateRequest(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
ab_step: int | None = Field(default=None, ge=0) # Re-Run ab feinem Teilschritt (0-basierter Index in _bausteine_steps); hat Vorrang vor ab_phase
source_location: str = Field(default="", max_length=2000)
ab_phase: int | None = Field(default=None, ge=1, le=9) # re-run from a coarse phase (position in _phasen(topic), 1-based; up to 9: …outline/questions/artifacts); None = resume/continue without deleting
ab_step: int | None = Field(default=None, ge=0) # re-run from a fine sub-step (0-based index into _blocks_steps); takes precedence over ab_phase
class BausteineResetStepRequest(BaseModel):
class BlocksResetStepRequest(BaseModel):
topic: str = Field(min_length=1, max_length=100)
ab_step: int = Field(ge=0) # NUR zurücksetzen ab diesem Teilschritt (kein Neu-Generieren)
ab_step: int = Field(ge=0) # ONLY reset from this sub-step (no regeneration)
class BausteineStep(BaseModel):
class BlocksStep(BaseModel):
label: str
state: Literal["done", "active", "pending"]
class BausteineFeinStep(BaseModel):
class BlocksFineStep(BaseModel):
label: str
phase: str = ""
state: Literal["done", "active", "pending"]
class BausteineStatusResponse(BaseModel):
class BlocksStatusResponse(BaseModel):
ready: bool
generating: bool
progress: str | None = None
error: str | None = None
partial: bool = False
steps: list[BausteineStep] = []
feine_steps: list[BausteineFeinStep] = []
steps: list[BlocksStep] = []
feine_steps: list[BlocksFineStep] = []
class ProjectResponse(BaseModel):
@@ -66,33 +66,33 @@ class ProjectResponse(BaseModel):
class FolderResponse(BaseModel):
name: str
ort: str # relativer Pfad ab Repo-Root (z.B. "projects/foo")
location: str # path relative to the repo root (e.g. "projects/foo")
class BausteineQuelleUpdate(BaseModel):
class BlocksSourceUpdate(BaseModel):
topic: str = Field(min_length=1, max_length=100)
type: SourceType = "thema"
ort: str = Field(default="", max_length=2000)
location: str = Field(default="", max_length=2000)
spec: str = Field(default="", max_length=2000)
class BausteineQuelleResponse(BaseModel):
class BlocksSourceResponse(BaseModel):
type: SourceType
ort: str
location: str
spec: str
class SubbausteinInfo(BaseModel):
titel: str
stufe: Literal["anfaenger", "fortgeschritten", "experte", "einfach", "mittel", "schwer"]
relevanz: Literal["relevant", "rand"] | None = None
class SubblockInfo(BaseModel):
title: str
level: Literal["beginner", "advanced", "expert", "easy", "medium", "hard"]
relevance: Literal["relevant", "peripheral"] | None = None
class BausteinUebersicht(BaseModel):
class BlockOverview(BaseModel):
num: int
titel: str
beschreibung: str = ""
subbausteine: list[SubbausteinInfo] = []
title: str
description: str = ""
subblocks: list[SubblockInfo] = []
class ProviderInfo(BaseModel):
@@ -168,7 +168,7 @@ class ElementCheckResponse(BaseModel):
class ElementStyleChange(BaseModel):
text: str
action: Literal["entfernen", "anpassen", "hinzufuegen"]
action: Literal["remove", "adjust", "add"]
target: Literal["title", "description", "examples", "hints"]
index: int | None = None
content: str = ""
@@ -207,104 +207,104 @@ class ProgressResponse(BaseModel):
chapters: list[str]
# --- Baustein-Lernen ---
# --- Block learning ---
class BausteinChatRequest(BaseModel):
class BlockChatRequest(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)
block: str = Field(min_length=1, max_length=200)
section: str = Field(default="", max_length=20000) # detailed version
section_compact: str = Field(default="", max_length=20000) # compact version (mnemonics)
messages: list[ChatMessage] = Field(min_length=1)
provider: ProviderType = "claude"
class BausteinChatResponse(BaseModel):
class BlockChatResponse(BaseModel):
reply: str
class BausteinPruefungRequest(BaseModel):
class BlockExamRequest(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); leerLive-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
block: str = Field(min_length=1, max_length=200)
section: str = Field(default="", max_length=20000) # detailed version
section_compact: str = Field(default="", max_length=20000) # compact version (mnemonics)
action: Literal[
"question", "discussion", "answer", "answer_check",
"quiz_question", "quiz_answer", "gap_question", "gap_answer",
] = "question"
question: str = Field(default="", max_length=2000) # currently checked question (for discussion/answer); base anchor
selection: list[int] = [] # quiz/gap-text choice: option indices picked by the learner
correct: list[int] = [] # quiz/gap-text choice: correct indices (the client keeps them from generation)
solution: str = Field(default="", max_length=500) # gap text free: expected term
alternatives: list[str] = [] # gap text free: accepted synonyms
input: str = Field(default="", max_length=500) # gap text free: typed term
schwer: bool = False # variant: easy (+1/1) vs hard (+3/1)
last_rating: str = Field(default="", max_length=2000) # feedback of the last rating (context for discussion)
avoid: list[str] = [] # already-asked + earmarked questions — don't repeat them in substance
asked_again: bool = False # asked_again was used for this question → gain capped at +1
reason: str = Field(default="", max_length=2000) # "thorough check": why dissatisfied with the rating
pattern: str = Field(default="", max_length=2000) # drawn question pattern (seed); emptylive generation (fallback)
# Base + cap are kept server-side (anchor / subs×25) — the client cap is only a hint.
cap: int = Field(default=10, ge=1, le=10000) # score cap = unlocked subblocks × 25
messages: list[ChatMessage] = [] # dialog so far; empty = first question
provider: ProviderType = "claude"
gruendlich: bool = False # „Gründlich prüfen": Bewertung mit starkem Modell (role guide)
thorough: bool = False # "thorough check": rating with a strong model (role guide)
class QuizOption(BaseModel):
text: str
korrekt: bool
correct: bool
class BausteinPruefungResponse(BaseModel):
frage: str | None = None
class BlockExamResponse(BaseModel):
question: 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
points: int | None = None # points delta of this answer (2 … +3); fast = expected
rating: Literal["gut", "neutral", "schlecht"] | None = None # from the sign, for coloring
options: list[QuizOption] | None = None # quiz: 4 options + correct flags
sentence: str | None = None # gap text: sentence with a gap (___)
solution: str | None = None # gap text: expected term
alternatives: list[str] | None = None # gap text: accepted synonyms
good_answers: int
streak: int = 0 # current run of correct answers (per block)
cap: int = 10 # cap_final = all subs × 25 — the frontend derives the learning level
class BausteinLernstand(BaseModel):
gute_antworten: int
class BlockLearnState(BaseModel):
good_answers: 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
cap: int = 0 # cap_final = all subblocks × 25
cap_aktuell: int = 0 # reachable cap of the currently unlocked level
freie_level: int = 1 # 1=A · 2=F · 3=E · 4=V
class BausteinLernstandResponse(BaseModel):
bausteine: dict[str, BausteinLernstand]
class BlockLearnStateResponse(BaseModel):
blocks: dict[str, BlockLearnState]
# --- Block-Inhalt: einen Abschnitt on-demand prüfen + übernehmen (Fokus, Rechtsklick) ---
# --- Block content: check + apply one section on demand (focus, right-click) ---
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 (✏️)
block: str = Field(min_length=1, max_length=200)
spot: str = "ausführlich" # "compact" | "ausführlich" (displayed field)
snippet: str = Field(min_length=1, max_length=20000) # raw markdown block
hint: str = Field(default="", max_length=2000) # optional addition (✏️)
provider: ProviderType = "claude"
class BlockPruefenResponse(BaseModel):
neu: str # korrigierter Block als Markdown
revised: str # corrected block as markdown
class BlockUebernehmenRequest(BaseModel):
baustein: str = Field(min_length=1, max_length=200)
stelle: str = "ausführlich"
block: str = Field(min_length=1, max_length=200)
spot: str = "ausführlich"
alt: str = Field(min_length=1, max_length=20000)
neu: str = Field(default="", max_length=20000)
revised: str = Field(default="", max_length=20000)
provider: ProviderType = "claude"
class BlockUebernehmenResponse(BaseModel):
kompakt: str
compact: str
md: str
gefunden: bool
found: bool