diff --git a/Endpoints/ModelsEndpoint.cs b/Endpoints/ModelsEndpoint.cs new file mode 100644 index 0000000..7f4d7a3 --- /dev/null +++ b/Endpoints/ModelsEndpoint.cs @@ -0,0 +1,32 @@ +using System.Linq; +using Gott.Services; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; + +namespace Gott.Endpoints; + +public static class ModelsEndpoint +{ + public static void MapModelsEndpoint(this IEndpointRouteBuilder app) + { + app.MapGet("/api/models/nvidia", HandleNvidiaModels); + app.MapGet("/api/models/pollinations", HandlePollinationsModels); + } + + private static IResult HandleNvidiaModels(OpenAiService openAiService) + { + var models = openAiService.GetNvidiaModels() + .Select(m => new { id = m.Id, displayName = m.DisplayName }) + .ToList(); + return Results.Json(new { models }); + } + + private static IResult HandlePollinationsModels(OpenAiService openAiService) + { + var models = openAiService.GetPollinationsModels() + .Select(m => new { id = m.Id, displayName = m.DisplayName }) + .ToList(); + return Results.Json(new { models }); + } +} diff --git a/Program.cs b/Program.cs index c70656d..318e7f1 100644 --- a/Program.cs +++ b/Program.cs @@ -92,6 +92,7 @@ builder.Services.AddHostedService(sp => sp.GetRequiredService().SetTelegramService(app.Services.GetRequiredService()); +await app.Services.GetRequiredService().LoadModelsAsync(); app.UseWebSockets(); app.UseDefaultFiles(); app.UseStaticFiles(); @@ -104,6 +105,7 @@ app.MapSessionEndpoints(); app.MapAgentEndpoints(); app.MapWorkspaceEndpoint(); app.MapInfoEndpoint(); +app.MapModelsEndpoint(); app.MapBackupEndpoint(); app.MapSkillEndpoints(); app.MapProfileEndpoints(); diff --git a/Services/OpenAiService.cs b/Services/OpenAiService.cs index 4ab229b..b82be79 100644 --- a/Services/OpenAiService.cs +++ b/Services/OpenAiService.cs @@ -25,7 +25,8 @@ public class OpenAiService // Provider-Definitionen private static readonly Dictionary Providers = new() { - ["nvidia"] = ("https://g4f.space/api/nvidia/v1", "") + ["nvidia"] = ("https://g4f.space/api/nvidia/v1", ""), + ["pollinations"] = ("https://text.pollinations.ai/openai", "") }; // Tools die für OpenAI-kompatible Provider verfügbar sind @@ -146,6 +147,11 @@ public class OpenAiService } }; + public record ProviderModel(string Id, string DisplayName); + + private List _nvidiaModels = new List(); + private List _pollinationsModels = new List(); + public OpenAiService(IHttpClientFactory httpFactory, DatabaseService db, ToolService toolService) { _httpFactory = httpFactory; @@ -153,6 +159,61 @@ public class OpenAiService _toolService = toolService; } + /// + /// Lädt verfügbare Modelle aller Provider beim Server-Start und cached sie. + /// + public async Task LoadModelsAsync() + { + _nvidiaModels = await FetchModels("https://g4f.space/api/nvidia/models", "Nvidia"); + _pollinationsModels = await FetchModels("https://text.pollinations.ai/models", "Pollinations"); + } + + private async Task> FetchModels(string url, string providerName) + { + try + { + var client = CreateClient(""); + var resp = await client.GetAsync(url); + if (!resp.IsSuccessStatusCode) return new List(); + + var json = await resp.Content.ReadAsStringAsync(); + var root = JsonSerializer.Deserialize(json); + + // Pollinations: root is array [{ name, ... }] + // Nvidia/OpenAI: root is { data: [{ id, ... }] } + JsonElement arr; + if (root.ValueKind == JsonValueKind.Array) + arr = root; + else if (root.TryGetProperty("data", out var data)) + arr = data; + else + return new List(); + + var models = new List(); + foreach (var item in arr.EnumerateArray()) + { + // try "id" first (Nvidia), fallback to "name" (Pollinations) + string id = null; + if (item.TryGetProperty("id", out var idProp) && idProp.ValueKind == JsonValueKind.String) id = idProp.GetString(); + if (id == null && item.TryGetProperty("name", out var nameProp) && nameProp.ValueKind == JsonValueKind.String) id = nameProp.GetString(); + if (string.IsNullOrEmpty(id)) continue; + var display = id.Contains('/') ? id[(id.LastIndexOf('/') + 1)..] : id; + models.Add(new ProviderModel(id, display)); + } + + Console.WriteLine($"[OpenAiService] {models.Count} {providerName}-Modelle geladen."); + return models; + } + catch (Exception ex) + { + Console.WriteLine($"[OpenAiService] Fehler beim Laden der {providerName}-Modelle: {ex.Message}"); + return new List(); + } + } + + public List GetNvidiaModels() => _nvidiaModels; + public List GetPollinationsModels() => _pollinationsModels; + /// /// Gibt den Provider und das eigentliche Modell-ID aus einem kombinierten Model-String zurück. /// Format: "nvidia:moonshotai/kimi-k2.5" → ("nvidia", "moonshotai/kimi-k2.5") diff --git a/wwwroot/index.html b/wwwroot/index.html index d74bc00..edf1db4 100644 --- a/wwwroot/index.html +++ b/wwwroot/index.html @@ -191,8 +191,11 @@ - - + + + + +
diff --git a/wwwroot/js/pages/agent-page.js b/wwwroot/js/pages/agent-page.js index 6b8f775..917f4a1 100644 --- a/wwwroot/js/pages/agent-page.js +++ b/wwwroot/js/pages/agent-page.js @@ -18,6 +18,32 @@ registerPage('agent', { const nameEl = document.getElementById('agentName'); const descEl = document.getElementById('agentDescription'); const modelEl = document.getElementById('agentModel'); + const nvidiaOptgroup = document.getElementById('nvidiaOptgroup'); + const pollinationsOptgroup = document.getElementById('pollinationsOptgroup'); + + // Modelle dynamisch laden + const loadProviderModels = async (url, prefix, optgroup) => { + try { + const resp = await fetch(url); + const data = await resp.json(); + if (optgroup && data.models && data.models.length > 0) { + optgroup.innerHTML = ''; + for (const m of data.models) { + const opt = document.createElement('option'); + opt.value = prefix + ':' + m.id; + opt.textContent = m.displayName; + optgroup.appendChild(opt); + } + } + } catch (e) { + if (optgroup) optgroup.innerHTML = ''; + } + }; + + await Promise.all([ + loadProviderModels('/api/models/nvidia', 'nvidia', nvidiaOptgroup), + loadProviderModels('/api/models/pollinations', 'pollinations', pollinationsOptgroup) + ]); const allowUserChoiceEl = document.getElementById('agentAllowUserChoice'); const autoCommitEl = document.getElementById('agentAutoCommit'); const useSoulEl = document.getElementById('agentUseSoul');