255 lines
7.3 KiB
Python
255 lines
7.3 KiB
Python
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)
|
||
ab_phase: int | None = Field(default=None, ge=1, le=5) # Re-Run ab grober Phase (1 Inventar … 5 Fragen); 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["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"] = "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
|
||
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 wird 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
|
||
punkte: int | None = None # Punkt-Delta dieser Antwort (−1 … +3); schnell = voraussichtlich
|
||
bewertung: Literal["gut", "neutral", "schlecht"] | None = None # aus Vorzeichen, fürs Einfärben
|
||
gute_antworten: int
|
||
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]
|