namespace LehrerApp.Core.AiPlanning; /// /// Wire-Vertrag mit dem externen KI-Backend (siehe ai-backend/, TODO 4.5.9). Bewusst getrennt von /// den internen Domänenmodellen in Core/Models: dieser Vertrag muss unabhängig von internen /// Domänen-Refactors abwärtskompatibel zum deployten PHP-Backend bleiben. /// public class AiPlanningRequest { public string Instruction { get; set; } = ""; public AiUnitContext Unit { get; set; } = new(); // Steuert, ob die KI bestehende Lessons inhaltlich ändern darf, oder nur neue Lessons // vorschlagen soll (Einheit umplanen ohne vs. mit Ändern bestehender Stunden). Wird sowohl im // Systemprompt des Backends durchgesetzt als auch client-seitig in ApplyResponse defensiv // geprüft — die KI-Antwort wird dafür nicht blind vertraut. public bool AllowModifyingExistingLessons { get; set; } = true; // Gesetzt, wenn die Anfrage aus dem Editor einer einzelnen Stunde heraus gestartet wurde // (statt aus der Einheiten-Übersicht): die KI soll dann AUSSCHLIESSLICH diese eine Stunde // bearbeiten, keine neuen Stunden vorschlagen und keine andere bestehende Stunde anfassen. // Wie AllowModifyingExistingLessons sowohl im Systemprompt erbeten als auch client-seitig // hart durchgesetzt (siehe AiAssistDialogViewModel.Send/AiPlanningService.ApplyResponse). public Guid? FocusLessonId { get; set; } } public class AiUnitContext { public Guid Id { get; set; } public string Title { get; set; } = ""; public DateOnly? StartDate { get; set; } public DateOnly? EndDate { get; set; } public List Competencies { get; set; } = []; public string? Notes { get; set; } // Nur Kontext für die KI, wird nicht zurückerwartet. public string SubjectName { get; set; } = ""; public int GradeLevel { get; set; } public string GroupName { get; set; } = ""; public List CompetencyCatalog { get; set; } = []; public List AlternativePathCatalog { get; set; } = []; public List Lessons { get; set; } = []; } public class AiCompetencyDomain { public string Name { get; set; } = ""; public List Items { get; set; } = []; } public class AiCompetencyItem { public string Code { get; set; } = ""; public string Description { get; set; } = ""; } public class AiAlternativePath { public Guid Id { get; set; } public string Name { get; set; } = ""; } /// /// ist der einzige Unterscheidungsmechanismus neu/geändert: gesetzt und einer /// tatsächlich im Request gesendeten Lesson zugehörig = Änderung; null oder eine unbekannte Id = /// neue Lesson. Eine unbekannte Id wird beim Import NIE als Update einer bestehenden Lesson /// interpretiert (siehe AiPlanningService.ApplyResponse) — sonst könnte eine halluzinierte Id im /// schlimmsten Fall eine fremde Lesson überschreiben. /// public class AiLesson { public Guid? Id { get; set; } public DateOnly? Date { get; set; } public int? LessonNumber { get; set; } public string Topic { get; set; } = ""; public TimeOnly? StartTime { get; set; } public List Phases { get; set; } = []; public string? Homework { get; set; } public string? Reflection { get; set; } /// Rohentwurf/erste Ideen der Lehrkraft vor der Feinplanung (siehe Lesson.PlanningIdeas) — reiner /// Kontext für die KI wie Homework/Reflection, wird symmetrisch übernommen/zurückgegeben. public string? PlanningIdeas { get; set; } } public class AiPhaseStep { public string Name { get; set; } = ""; public int DurationMinutes { get; set; } public string Activity { get; set; } = ""; public string Material { get; set; } = ""; public string Shorthand { get; set; } = ""; // Name statt Guid: LLMs erfinden/verändern Guids unzuverlässig, ein Name ist beim Import // gegen den Katalog abgleichbar (kein Treffer -> null = Hauptweg, kein Fehler). public string? AlternativePathName { get; set; } // Kurzer Vorschlag für ein konkretes Medium/Material zu dieser Phase (z.B. Tafelbild, // Arbeitsblatt), nur gesetzt, wenn eines wirklich sinnvoll wäre — nicht bei jeder Phase. // Wird NICHT in Lesson.Phases übernommen (nur für die Review-Anzeige/Prompt-Erzeugung // relevant, siehe AiPlanningService.BuildMaterialPrompt), daher kein Feld auf // LessonPhaseStep nötig. public string? MaterialSuggestion { get; set; } } public class AiPlanningResponse { public List Lessons { get; set; } = []; public string? Summary { get; set; } // Vom Backend zusätzlich mitgelieferte, unveränderte Modellantwort. Wird nur benötigt, falls // die typisierte Deserialisierung scheitert; bei einer regulär verarbeiteten Antwort wird sie // weder angezeigt noch gespeichert. public string? RawResponse { get; set; } } /// /// Wire-Vertrag für den "Schattenfeld"-Endpunkt (4.5.21, ai-backend/explain.php): fragt nur auf /// Nachfrage einen didaktischen Hintergrund zu einer bereits vorgeschlagenen/geplanten Lesson ab /// (Warum dieser Aufbau, mögliche Stolpersteine, Differenzierung) — ändert nichts an der Planung, /// eigener Endpunkt statt Zusatzfeld in jeder plan.php-Antwort, damit nur bei tatsächlicher /// Nutzung abgerechnet wird. /// public class AiExplainRequest { public AiUnitContext Unit { get; set; } = new(); public AiLesson Lesson { get; set; } = new(); } public class AiExplainResponse { public string Explanation { get; set; } = ""; } /// /// Wire-Vertrag für den Gefährdungsbeurteilungs-Entwurf (ai-backend/gbu.php, Nutzerwunsch neben /// 4.2 "Anhänge je Stunde"): erkennt aus Thema/Verlaufsplan der Lesson das Experiment und liefert /// dafür einen strukturierten Entwurf. Eigener Endpunkt statt Zusatzfeld in plan.php/explain.php, /// da inhaltlich unabhängig von der Unterrichtsplanung selbst. /// public class AiHazardAssessmentRequest { public AiUnitContext Unit { get; set; } = new(); public AiLesson Lesson { get; set; } = new(); } public class AiHazardAssessmentResponse { /// "Lehrerversuch" | "Schuelerversuch" | "Demonstrationsversuch" (siehe ExperimentKind). public string ExperimentKind { get; set; } = ""; public string Procedure { get; set; } = ""; public List Substances { get; set; } = []; public List Hazards { get; set; } = []; public List ProtectiveMeasures { get; set; } = []; public string FirstAid { get; set; } = ""; public string Disposal { get; set; } = ""; } public class AiHazardSubstance { public string Name { get; set; } = ""; public string Amount { get; set; } = ""; /// GHS-Piktogramm-Codes als Text, z.B. "GHS05" — siehe GhsPictogram für die Zuordnung. public List GhsPictograms { get; set; } = []; public string HStatements { get; set; } = ""; public string PStatements { get; set; } = ""; } /// /// Wire-Vertrag für die Chemikalien-Recherche (ai-backend/substance.php, Nutzerwunsch neben 4.2): /// ein reiner Datenbank-Lookup gegen die lokale RiSU-Stoffliste, kein KI-Aufruf (siehe /// ai-backend/stoffliste.php) — der Name "Ai..." bleibt trotzdem bestehen, da der Aufruf über /// dieselbe Anmeldung/denselben Host wie die echten KI-Endpunkte läuft. Eigener, sehr schmaler /// Wire-Vertrag statt Wiederverwendung von AiHazardSubstance — die Recherche liefert /// zusätzliche, nur hier relevante Felder (CAS, Signalwort, Tätigkeitsbeschränkung, Quelle). /// Enthält bewusst KEIN Entsorgungsfeld — die RiSU-Stoffliste führt keine Entsorgungshinweise. /// public class AiSubstanceResearchResponse { public string Name { get; set; } = ""; public string Cas { get; set; } = ""; public List GhsPictograms { get; set; } = []; public string SignalWord { get; set; } = ""; public string HStatements { get; set; } = ""; public string PStatements { get; set; } = ""; public string ActivityRestriction { get; set; } = ""; public string Source { get; set; } = ""; } /// /// Wire-Vertrag für den Fehlzeiten-Statusvorschlag (ai-backend/untis-status.php, Nutzer-Feedback /// zum Untis-Hub: der aus WebUntis abgeleitete Zielstatus war über den Anzeigetext oft nicht /// nachvollziehbar). Bewusst OHNE jeden Personenbezug - ist eine /// rein technische, für die KI bedeutungslose Kennung, über die der Desktop-Client die Antwort /// zurückordnet; kein Name, keine Klasse, kein Datum verlässt damit die App. Mehrere Zeilen eines /// Abgleichslaufs werden in einer Anfrage gebündelt statt je Zeile einzeln (Kosten/Latenz). /// public class AiUntisStatusRequest { public List Rows { get; set; } = []; } public class AiUntisStatusRow { public string Id { get; set; } = ""; public string ReasonText { get; set; } = ""; public int AbsentMinutes { get; set; } /// True, wenn WebUntis für den Eintrag bereits ein Bearbeitungsdatum führt (unabhängig vom /// tatsächlichen Datum, das aus Datenschutzgründen nicht mitgeschickt wird). public bool HandledOn { get; set; } /// Schulinterne Konvention: Klammerung der Entschuldigungsnummer bedeutet unentschuldigt, ohne /// Klammern entschuldigt; null, wenn keine Nummer hinterlegt ist (siehe UntisDiffService-Analog /// in WebUntisLessonAbsenceComparisonViewModel.MapStatus). public bool? ExternKeyInParentheses { get; set; } /// Bereits regelbasiert ermittelter Status (siehe MapStatus) - der Systemprompt bittet die KI, /// nur bei eindeutigem Widerspruch im Freitext davon abzuweichen, statt bei Unsicherheit zu raten. public string CurrentGuess { get; set; } = ""; } /// /// Enthält absichtlich GENAU eine Zeile je gesendeter - der /// Client verwirft die gesamte Antwort, wenn die zurückgegebene Id-Menge nicht exakt der /// gesendeten entspricht (siehe AiPlanningService.RequestUntisStatusSuggestionsAsync), statt sich /// auf die Reihenfolge zu verlassen. So bleibt eine Verwechslung zwischen Vorschlag und Zeile /// strukturell ausgeschlossen, nicht nur im Regelfall vermieden. /// public class AiUntisStatusResponse { public List Suggestions { get; set; } = []; } public class AiUntisStatusSuggestion { public string Id { get; set; } = ""; /// Einer von "Present"/"Late"/"LeftDuringClass"/"ExcusePending"/"Excused"/"Unexcused" (siehe /// Systemprompt in ai-backend/untis-status.php) - wird client-seitig gegen genau diese Menge /// geprüft, bevor er als AttendanceStatus interpretiert wird. public string Status { get; set; } = ""; }