using AuthorBuddy.Web.Models; namespace AuthorBuddy.Web.Services; public class OllamaService : IOllamaService { private readonly IEnumerable _backends; private readonly OllamaSettings _settings; private readonly OperationState _opState; public OllamaService(IEnumerable backends, OllamaSettings settings, OperationState opState) { _backends = backends; _settings = settings; _opState = opState; } private ILLMBackend ActiveBackend => _backends.FirstOrDefault(b => b.Name == _settings.Backend) ?? _backends.First(); public void Reconfigure(string url) { _settings.OllamaUrl = url; } public async Task<(bool Success, long LatencyMs)> TestConnectionAsync() { _opState.SetThought($"Teste Verbindung zu {ActiveBackend.Name}..."); var sw = System.Diagnostics.Stopwatch.StartNew(); var result = await ActiveBackend.TestConnectionAsync(); sw.Stop(); _opState.AppendThought(result ? " OK" : " Fehlgeschlagen"); return (result, result ? sw.ElapsedMilliseconds : 0); } public async Task> GetLocalModelsAsync() { _opState.SetThought($"Lade Modelle von {ActiveBackend.Name}..."); var models = await ActiveBackend.GetModelsAsync(); _opState.AppendThought($" {models.Count()} gefunden"); return models; } public async Task TestEmbeddingAsync(string model) { return ActiveBackend.TestEmbedding(model); } public async Task GetEmbeddingAsync(string text) { return await ActiveBackend.GetEmbeddingAsync(text, _settings.EmbedModel); } public async Task QuickSpellCheckAsync(string text) { return await Chat(_settings.SpellingModel, text, _settings.SpellingPrompt, _settings.SpellingTemp); } public async Task StreamStyleCheckAsync(string text) { return await Chat(_settings.StyleModel, text, _settings.LektorSystemPrompt, _settings.LektorTemp); } public async Task AnswerBasedOnFactsAsync(string question, string facts) { var prompt = _settings.GetFactCheckPrompt(question, facts); return await Chat(_settings.EmbedModel, prompt, temperature: _settings.FactCheckTemp); } public async Task ModelExistsAsync(string model) { return await ActiveBackend.ModelExistsAsync(model); } public async Task<(bool Success, int ContextWindow)> GetModelContextWindowAsync(string model) { return await ActiveBackend.GetModelContextWindowAsync(model); } public async Task MeasureLatencyAsync(string model) { return await ActiveBackend.MeasureLatencyAsync(model); } public async Task Chat(string model, string userMessage, string? systemPrompt = null, double? temperature = null) { _opState.SetThought($"{ActiveBackend.Name}: Sende Anfrage an {model}..."); var result = await ActiveBackend.ChatAsync(model, userMessage, systemPrompt, temperature); _opState.AppendThought(" Fertig"); return result; } public async Task ChatStream(string model, string userMessage, Func onToken, string? systemPrompt = null, double? temperature = null) { _opState.SetThought($"{ActiveBackend.Name}: Verbinde mit Modell {model}..."); int tokenCount = 0; await ActiveBackend.ChatStreamAsync(model, userMessage, async chunk => { tokenCount++; _opState.AppendThought(chunk); await onToken(chunk); }, systemPrompt, temperature); _opState.AppendThought($" Fertig ({tokenCount} Token)"); } public async Task PullModelAsync(string modelName, IProgress progress) { _opState.SetThought($"Lade Modell {modelName}..."); var result = await ActiveBackend.PullModelAsync(modelName, progress); _opState.AppendThought(result ? " Erfolgreich" : " Fehlgeschlagen"); return result; } }