AuthorBuddy/Services/LlmWikiService.cs

137 lines
4.5 KiB
C#
Raw Normal View History

2026-08-30 08:11:01 +02:00
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<string> 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<string>();
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<string> 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<string> 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;
}
}