Files
creator/backend/models.py
2026-06-19 12:59:13 +02:00

251 lines
6.7 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[
"OnePager",
"MiniGuide",
"Guide",
"ProGuide",
"FullGuide",
]
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"
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)
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["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_pruefen"] = "frage"
frage: str = Field(default="", max_length=2000) # aktuell geprüfte Frage (für diskussion/antwort); Anker der Basis
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
# Basis/Streak werden serverseitig je Frage geführt (offene_frage-Anker) — nicht mehr vom Client.
tier2: bool = False # ganzer Guide absolviert (alle ≥3) → 1 bei falsch, Deckel 10
tier3: bool = False # ganzer Guide verstanden (alle ≥10) → Meisterpfad, 2 bei falsch, Deckel 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 BausteinPruefungResponse(BaseModel):
frage: str | None = None
reply: str | None = None
feedback: str | None = None
bewertung: Literal["gut", "schlecht"] | None = None
gute_antworten: int
streak: int | None = None # nur antwort_pruefen setzt die persistierte Streak
absolviert: bool
verstanden: bool = False
gemeistert: bool = False
class BausteinLernstand(BaseModel):
gute_antworten: int
streak: int = 0
absolviert: bool
verstanden: bool
gemeistert: bool
class BausteinLernstandResponse(BaseModel):
bausteine: dict[str, BausteinLernstand]