From ed6e78de5648cc248af588d606b12ecbfd37f559 Mon Sep 17 00:00:00 2001 From: Meleki Date: Thu, 12 Mar 2026 08:57:47 +0100 Subject: [PATCH] Cache Provider-Modelle in models_cache.json statt bei jedem Start von APIs zu laden MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LoadModelsAsync() prüft zuerst data/models_cache.json - Bei Cache-Hit: kein API-Call, sofortiger Start - RefreshModelsFromApis() lädt von APIs + speichert Cache - POST /api/models/refresh Endpoint für manuellen Refresh - dataDir an OpenAiService-Konstruktor übergeben --- Endpoints/ModelsEndpoint.cs | 8 ++++++ Program.cs | 2 +- Services/OpenAiService.cs | 57 ++++++++++++++++++++++++++++++++++--- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/Endpoints/ModelsEndpoint.cs b/Endpoints/ModelsEndpoint.cs index ce6ba86..a71257a 100644 --- a/Endpoints/ModelsEndpoint.cs +++ b/Endpoints/ModelsEndpoint.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Threading.Tasks; using Gott.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -13,6 +14,7 @@ public static class ModelsEndpoint app.MapGet("/api/models/nvidia", HandleNvidiaModels); app.MapGet("/api/models/pollinations", HandlePollinationsModels); app.MapGet("/api/models/gemini", HandleGeminiModels); + app.MapPost("/api/models/refresh", HandleRefresh); } private static IResult HandleNvidiaModels(OpenAiService openAiService) @@ -38,4 +40,10 @@ public static class ModelsEndpoint .ToList(); return Results.Json(new { models }); } + + private static async Task HandleRefresh(OpenAiService openAiService) + { + await openAiService.RefreshModelsFromApis(); + return Results.Json(new { success = true, message = "Modelle neu geladen und gecacht." }); + } } diff --git a/Program.cs b/Program.cs index 159fcf2..a237986 100644 --- a/Program.cs +++ b/Program.cs @@ -70,7 +70,7 @@ builder.Services.AddSingleton(sp => // OpenAI-kompatibler Service (Nvidia etc.) builder.Services.AddSingleton(sp => - new OpenAiService(sp.GetRequiredService(), dbService, sp.GetRequiredService(), builder.Configuration)); + new OpenAiService(sp.GetRequiredService(), dbService, sp.GetRequiredService(), builder.Configuration, dataDir)); // Telegram Bot (pro Session – Token aus Session-Config) var maxIterations = builder.Configuration.GetValue("MaxIterations", 100); diff --git a/Services/OpenAiService.cs b/Services/OpenAiService.cs index 7439943..4aa7f53 100644 --- a/Services/OpenAiService.cs +++ b/Services/OpenAiService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Net.Http; using System.Net.Http.Headers; using System.Text; @@ -151,12 +152,14 @@ public class OpenAiService private List _nvidiaModels = new List(); private List _pollinationsModels = new List(); private List _geminiModels = new List(); + private readonly string _cacheFile; - public OpenAiService(IHttpClientFactory httpFactory, DatabaseService db, ToolService toolService, Microsoft.Extensions.Configuration.IConfiguration configuration) + public OpenAiService(IHttpClientFactory httpFactory, DatabaseService db, ToolService toolService, Microsoft.Extensions.Configuration.IConfiguration configuration, string dataDir) { _httpFactory = httpFactory; _db = db; _toolService = toolService; + _cacheFile = Path.Combine(dataDir, "models_cache.json"); Providers = new Dictionary { ["nvidia"] = ("https://g4f.space/api/nvidia/v1", ""), @@ -166,19 +169,65 @@ public class OpenAiService } /// - /// Lädt verfügbare Modelle aller Provider beim Server-Start und cached sie. + /// Lädt verfügbare Modelle – aus lokalem Cache (models_cache.json) oder von den APIs. /// - public async Task LoadModelsAsync() + public async Task LoadModelsAsync(bool forceRefresh = false) + { + if (!forceRefresh && File.Exists(_cacheFile)) + { + try + { + var json = await File.ReadAllTextAsync(_cacheFile); + var cache = JsonSerializer.Deserialize>>(json); + if (cache != null) + { + _nvidiaModels = cache.ContainsKey("nvidia") ? cache["nvidia"] : new List(); + _pollinationsModels = cache.ContainsKey("pollinations") ? cache["pollinations"] : new List(); + _geminiModels = cache.ContainsKey("gemini") ? cache["gemini"] : new List(); + Console.WriteLine($"[OpenAiService] Modelle aus Cache geladen: {_nvidiaModels.Count} Nvidia, {_pollinationsModels.Count} Pollinations, {_geminiModels.Count} Gemini"); + return; + } + } + catch (Exception ex) + { + Console.WriteLine($"[OpenAiService] Cache-Datei fehlerhaft, lade von APIs: {ex.Message}"); + } + } + + await RefreshModelsFromApis(); + } + + /// + /// Lädt Modelle von allen Provider-APIs und speichert sie im Cache. + /// + public async Task RefreshModelsFromApis() { _nvidiaModels = await FetchModels("https://g4f.space/api/nvidia/models", "Nvidia"); _pollinationsModels = await FetchModels("https://text.pollinations.ai/models", "Pollinations"); - // Gemini: dynamisch von der API laden (Bearer-Token erforderlich) var geminiApiKey = Providers["gemini"].ApiKey; if (!string.IsNullOrEmpty(geminiApiKey)) _geminiModels = await FetchModels("https://generativelanguage.googleapis.com/v1beta/openai/models", "Gemini", geminiApiKey); else Console.WriteLine("[OpenAiService] GeminiApiKey nicht konfiguriert – Gemini-Modelle nicht geladen."); + + // Cache speichern + try + { + var cache = new Dictionary> + { + ["nvidia"] = _nvidiaModels, + ["pollinations"] = _pollinationsModels, + ["gemini"] = _geminiModels + }; + var json = JsonSerializer.Serialize(cache, new JsonSerializerOptions { WriteIndented = true }); + await File.WriteAllTextAsync(_cacheFile, json); + Console.WriteLine($"[OpenAiService] Modelle-Cache gespeichert: {_cacheFile}"); + } + catch (Exception ex) + { + Console.WriteLine($"[OpenAiService] Fehler beim Speichern des Modelle-Cache: {ex.Message}"); + } } private async Task> FetchModels(string url, string providerName, string apiKey = "")