Files
LehrerApp/LehrerApp.Core/AiPlanning/AiPlanningDtos.cs
T
adminandClaude Sonnet 5 10d82e5e2c KI-Feature 4.5.22: KI-Unterstützung direkt aus dem Stunden-Editor starten
Bisher musste man aus dem Stunden-Editor raus, die KI-Hilfe für die ganze
Einheit aufrufen und die gewünschte Stunde in der freien Anweisung erst
benennen. Neuer Button "KI-Unterstützung für diese Stunde" im LessonDialog
öffnet denselben AiAssistDialog im neuen Fokus-Modus.

AiPlanningRequest.FocusLessonId weist die KI im Systemprompt an, sich auf
genau diese Stunde zu beschränken; zusätzlich wie beim Umfangs-Umschalter
client-seitig hart durchgesetzt in Send() und ApplyResponse, statt der
KI-Antwort blind zu vertrauen.

Da "Übernehmen" direkt ins Repository speichert, wären die noch offenen
Feldwerte des ursprünglichen LessonDialog danach veraltet gewesen - der
Dialog schließt sich deshalb nach einer angewendeten KI-Änderung automatisch
mit dem frischen Stand statt ihn mit alten Werten zu überschreiben.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-16 22:48:00 +02:00

122 lines
5.0 KiB
C#

namespace LehrerApp.Core.AiPlanning;
/// <summary>
/// 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.
/// </summary>
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<string> 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<AiCompetencyDomain> CompetencyCatalog { get; set; } = [];
public List<AiAlternativePath> AlternativePathCatalog { get; set; } = [];
public List<AiLesson> Lessons { get; set; } = [];
}
public class AiCompetencyDomain
{
public string Name { get; set; } = "";
public List<AiCompetencyItem> 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; } = "";
}
/// <summary>
/// <see cref="Id"/> 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.
/// </summary>
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<AiPhaseStep> Phases { get; set; } = [];
public string? Homework { get; set; }
public string? Reflection { 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<AiLesson> Lessons { get; set; } = [];
public string? Summary { get; set; }
}
/// <summary>
/// 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.
/// </summary>
public class AiExplainRequest
{
public AiUnitContext Unit { get; set; } = new();
public AiLesson Lesson { get; set; } = new();
}
public class AiExplainResponse
{
public string Explanation { get; set; } = "";
}