using System.Text; using AuthorBuddy.Web.Models; namespace AuthorBuddy.Web.Services; public class LlmWikiService : ILlmWikiService { private readonly IOllamaService _ollama; private readonly OllamaSettings _settings; private readonly IProjectService _projectService; public LlmWikiService(IOllamaService ollama, OllamaSettings settings, IProjectService projectService) { _ollama = ollama; _settings = settings; _projectService = projectService; } public bool WikiExists(int projectId, string rootPath) { var wikiDir = GetWikiDir(rootPath); return Directory.Exists(wikiDir) && Directory.GetFiles(wikiDir, "*.md").Length > 0; } public async Task CompileWikiAsync(int projectId, string rootPath, IProgress progress) { var wikiDir = GetWikiDir(rootPath); Directory.CreateDirectory(wikiDir); var files = Directory.GetFiles(rootPath, "*.md", SearchOption.AllDirectories) .Where(f => !f.Contains("\\wiki\\") && !f.Contains("/wiki/")) .ToList(); if (files.Count == 0) { progress?.Report("Keine Markdown-Dateien gefunden."); return; } var indexEntries = new List(); foreach (var file in files) { var fileName = Path.GetFileNameWithoutExtension(file); var safeName = SanitizeName(fileName); progress?.Report($"Komprimiere: {fileName}..."); var content = await File.ReadAllTextAsync(file); if (string.IsNullOrWhiteSpace(content)) { progress?.Report(" ↳ leer, übersprungen"); continue; } var wikiContent = await CompilePage(content, fileName); var wikiPath = Path.Combine(wikiDir, $"{safeName}.md"); await File.WriteAllTextAsync(wikiPath, wikiContent); indexEntries.Add($"- [{fileName}]({safeName}.md)"); progress?.Report(" ↳ ✓ Wiki-Seite erstellt"); } var indexContent = "# Wiki-Index\n\n" + $"Erstellt am {DateTime.Now:dd.MM.yyyy HH:mm} aus {files.Count} Quellen.\n\n" + string.Join("\n", indexEntries) + "\n"; await File.WriteAllTextAsync(Path.Combine(wikiDir, "index.md"), indexContent); progress?.Report($"Wiki kompiliert: {indexEntries.Count} Seiten."); } public async Task QueryWikiAsync(int projectId, string question) { var project = await _projectService.GetProjectAsync(projectId); if (project == null) return "Projekt nicht gefunden."; var wikiDir = GetWikiDir(project.RootPath); if (!Directory.Exists(wikiDir)) return "Wiki noch nicht kompiliert. Bitte zuerst das Wiki erstellen."; var wikiFiles = Directory.GetFiles(wikiDir, "*.md"); var wikiContent = new StringBuilder(); foreach (var file in wikiFiles.OrderBy(f => f)) { var content = await File.ReadAllTextAsync(file); wikiContent.AppendLine($"--- {Path.GetFileName(file)} ---"); wikiContent.AppendLine(content); wikiContent.AppendLine(); } var prompt = $@"Du bist ein Wiki-Assistent. Beantworte die folgende Frage ausschließlich basierend auf dem untenstehenden Wiki. Wenn die Antwort nicht im Wiki zu finden ist, sage das deutlich. WIKI: {wikiContent} FRAGE: {question}"; return await _ollama.Chat(_settings.StyleModel, prompt, temperature: 0.3, backendName: _settings.StyleBackend); } private async Task CompilePage(string content, string title) { var prompt = $@"Du bist ein Wiki-Redakteur. Erstelle eine strukturierte Wiki-Seite aus dem folgenden Dokument. FORMAT: # {title} ## Zusammenfassung Kurze Zusammenfassung (2-3 Sätze) ## Kernkonzepte - Konzept 1: Beschreibung - Konzept 2: Beschreibung ## Details Wichtige Fakten und Details aus dem Dokument ## Verknüpfungen Verweise auf verwandte Themen (falls erkennbar) DOKUMENT: {content}"; return await _ollama.Chat(_settings.StyleModel, prompt, temperature: 0.3, backendName: _settings.StyleBackend); } private static string GetWikiDir(string rootPath) => Path.Combine(rootPath, "wiki"); private static string SanitizeName(string name) { var invalid = Path.GetInvalidFileNameChars(); var sanitized = new string(name.Where(c => !invalid.Contains(c)).ToArray()); return string.IsNullOrWhiteSpace(sanitized) ? "unnamed" : sanitized; } }