feat: Dynamisches Modell-Laden für Nvidia & Pollinations Provider

- OpenAiService: FetchModels() generisch für beide Provider (OpenAI-Format & Pollinations-Array-Format)
- Pollinations Base-URL direkt auf text.pollinations.ai/openai (kein g4f.space Proxy → kein 402)
- Pollinations Modelle-URL: text.pollinations.ai/models
- ModelsEndpoint: /api/models/nvidia + /api/models/pollinations
- agent-page.js: loadProviderModels() generisch, Promise.all für beide Provider parallel
- index.html: statische Nvidia-Option entfernt, beide Optgroups mit 'Wird geladen...'
This commit is contained in:
Meleki committed 2026-03-11 16:43:08 +01:00
1 parent 7972ca6a3a
commit 5ed910edcd
5 files changed
+127 -3

No files matched your search

+32
View File
@@ -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 });
}
}
+2
View File
@@ -92,6 +92,7 @@ builder.Services.AddHostedService<CronjobService>(sp => sp.GetRequiredService<Cr
var app = builder.Build(); var app = builder.Build();
app.Services.GetRequiredService<AnthropicService>().SetTelegramService(app.Services.GetRequiredService<TelegramService>()); app.Services.GetRequiredService<AnthropicService>().SetTelegramService(app.Services.GetRequiredService<TelegramService>());
await app.Services.GetRequiredService<OpenAiService>().LoadModelsAsync();
app.UseWebSockets(); app.UseWebSockets();
app.UseDefaultFiles(); app.UseDefaultFiles();
app.UseStaticFiles(); app.UseStaticFiles();
@@ -104,6 +105,7 @@ app.MapSessionEndpoints();
app.MapAgentEndpoints(); app.MapAgentEndpoints();
app.MapWorkspaceEndpoint(); app.MapWorkspaceEndpoint();
app.MapInfoEndpoint(); app.MapInfoEndpoint();
app.MapModelsEndpoint();
app.MapBackupEndpoint(); app.MapBackupEndpoint();
app.MapSkillEndpoints(); app.MapSkillEndpoints();
app.MapProfileEndpoints(); app.MapProfileEndpoints();
+62 -1
View File
@@ -25,7 +25,8 @@ public class OpenAiService
// Provider-Definitionen // Provider-Definitionen
private static readonly Dictionary<string, (string BaseUrl, string ApiKey)> Providers = new() private static readonly Dictionary<string, (string BaseUrl, string ApiKey)> 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 // 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<ProviderModel> _nvidiaModels = new List<ProviderModel>();
private List<ProviderModel> _pollinationsModels = new List<ProviderModel>();
public OpenAiService(IHttpClientFactory httpFactory, DatabaseService db, ToolService toolService) public OpenAiService(IHttpClientFactory httpFactory, DatabaseService db, ToolService toolService)
{ {
_httpFactory = httpFactory; _httpFactory = httpFactory;
@@ -153,6 +159,61 @@ public class OpenAiService
_toolService = toolService; _toolService = toolService;
} }
/// <summary>
/// Lädt verfügbare Modelle aller Provider beim Server-Start und cached sie.
/// </summary>
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<List<ProviderModel>> FetchModels(string url, string providerName)
{
try
{
var client = CreateClient("");
var resp = await client.GetAsync(url);
if (!resp.IsSuccessStatusCode) return new List<ProviderModel>();
var json = await resp.Content.ReadAsStringAsync();
var root = JsonSerializer.Deserialize<JsonElement>(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<ProviderModel>();
var models = new List<ProviderModel>();
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<ProviderModel>();
}
}
public List<ProviderModel> GetNvidiaModels() => _nvidiaModels;
public List<ProviderModel> GetPollinationsModels() => _pollinationsModels;
/// <summary> /// <summary>
/// Gibt den Provider und das eigentliche Modell-ID aus einem kombinierten Model-String zurück. /// 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") /// Format: "nvidia:moonshotai/kimi-k2.5" → ("nvidia", "moonshotai/kimi-k2.5")
+5 -2
View File
@@ -191,8 +191,11 @@
<option value="opus-4-5">Opus 4.5</option> <option value="opus-4-5">Opus 4.5</option>
<option value="opus-4-6" selected>Opus 4.6</option> <option value="opus-4-6" selected>Opus 4.6</option>
<option value="haiku-4-5">Haiku 4.5</option> <option value="haiku-4-5">Haiku 4.5</option>
<optgroup label="Nvidia (via g4f.space)"> <optgroup label="Pollinations (via g4f.space)" id="pollinationsOptgroup">
<option value="nvidia:moonshotai/kimi-k2.5">Kimi K2.5 (Moonshot)</option> <option value="" disabled>Wird geladen...</option>
</optgroup>
<optgroup label="Nvidia (via g4f.space)" id="nvidiaOptgroup">
<option value="" disabled>Wird geladen...</option>
</optgroup> </optgroup>
</select> </select>
<div class="more"> <div class="more">
+26
View File
@@ -18,6 +18,32 @@ registerPage('agent', {
const nameEl = document.getElementById('agentName'); const nameEl = document.getElementById('agentName');
const descEl = document.getElementById('agentDescription'); const descEl = document.getElementById('agentDescription');
const modelEl = document.getElementById('agentModel'); 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 = '<option value="" disabled>Fehler beim Laden</option>';
}
};
await Promise.all([
loadProviderModels('/api/models/nvidia', 'nvidia', nvidiaOptgroup),
loadProviderModels('/api/models/pollinations', 'pollinations', pollinationsOptgroup)
]);
const allowUserChoiceEl = document.getElementById('agentAllowUserChoice'); const allowUserChoiceEl = document.getElementById('agentAllowUserChoice');
const autoCommitEl = document.getElementById('agentAutoCommit'); const autoCommitEl = document.getElementById('agentAutoCommit');
const useSoulEl = document.getElementById('agentUseSoul'); const useSoulEl = document.getElementById('agentUseSoul');