9.3 KiB
Plan: OpenAI als nativer Provider
Übersicht
OpenAI (GPT-4o, GPT-5, o3, etc.) als vollwertigen Provider integrieren – gleichwertig mit Anthropic.
Der Key kommt aus der api_keys-Tabelle (Provider: openai), nicht aus appsettings.json.
Ist-Zustand
Architektur
- AnthropicService – Haupt-Provider, voller Feature-Support (Tools, Review, Preparer, Finalizer, Web-Search, Memory, ask_user_choice, Plan-Mode)
- OpenAiService – "Neben-Provider" für Nvidia, Pollinations, Gemini. Limitiert:
- Nur 6 Tools (list_files, read_file, write_file, edit_file, delete_file, execute)
- Kein Review/Preparer/Finalizer
- Kein Memory, Web-Search, ask_user_choice
- API-Key hardcoded im
Providers-Dictionary (nicht aus DB) maxIterationsfest auf 10
API-Key-System
- User wählt einen Key aus der
api_keys-Tabelle (selected_api_key_id) - Aktuell nur für Anthropic genutzt (
userApiKey.ApiKey→anthropic.StreamChat()) - OpenAi-Provider nutzen eigene Keys aus
Providers-Dict (Gemini ausappsettings.json, Nvidia/Pollinations keyless)
Modell-Auswahl
- Agent hat
Model-Feld, z.B.sonnet-4-6odernvidia:model-id ChatService.ResolveModelId()mapped Anthropic-Kurzformen, OpenAI-Provider werden durchgereicht- Frontend:
<select>mit Anthropic-Optionen + Optgroups für Nvidia/Pollinations/Gemini
Soll-Zustand
Ziel
Ein Agent mit Model openai:gpt-4o nutzt den aktiven OpenAI-Key des Users aus der api_keys-Tabelle.
Voller Tool-Support, alle 17 Tools.
Architektur-Entscheidung
Option A: OpenAI als neuen Provider in OpenAiService einbauen → Einfach, aber OpenAiService hat nur 6 Tools und kein Review/Preparer/Finalizer.
Option B: OpenAI direkt über OpenAiService mit vollem Tool-Support → Alle 17 Tools + Memory + Review etc. erfordert massiven Umbau des OpenAiService.
Option C: OpenAI über AnthropicService (Anthropic-API-kompatibel) → Nicht möglich, OpenAI hat ein anderes API-Format.
→ Empfehlung: Option A (erweitert)
OpenAI als Provider in OpenAiService mit ALLEN Tools (nicht nur 6).
Review/Preparer/Finalizer erstmal nicht für OpenAI – das kann später kommen.
Umsetzungsplan
Phase 1: Backend – OpenAI Provider Registrierung
Datei: Services/OpenAiService.cs
-
"openai"zuProviderKeyshinzufügen:public static readonly HashSet<string> ProviderKeys = new() { "nvidia", "pollinations", "gemini", "openai" }; -
OpenAI Provider-Config NICHT hardcoden, sondern dynamisch aus DB laden. Problem:
Providers-Dict wird im Konstruktor befüllt, aber der API-Key kommt zur Laufzeit ausapi_keys(user-spezifisch).Lösung:
StreamChat()bekommt optionalenapiKey-Parameter. Wenn Provider =openai→ BaseUrlhttps://api.openai.com/v1, Key aus Parameter.// Im Konstruktor: ["openai"] = ("https://api.openai.com/v1", "") // Key kommt zur Laufzeit // In StreamChat(): public async Task StreamChat(..., string apiKey = null, ...) { var providerConfig = Providers[provider]; var effectiveKey = !string.IsNullOrEmpty(apiKey) ? apiKey : providerConfig.ApiKey; var client = CreateClient(effectiveKey); } -
Tools erweitern – ALLE 17 Tools statt nur 6. Problem: Aktuell sind die Tools als statische anonyme Objekte definiert (
OpenAiTools). Besser: Tools dynamisch ausToolService.GetToolDefinitions()konvertieren.// Neue Methode: Anthropic-Tool-Defs → OpenAI-Format konvertieren private static List<object> ConvertToolsToOpenAiFormat(object[] anthropicTools) { // Anthropic: { name, description, input_schema: { type, properties, required } } // OpenAI: { type: "function", function: { name, description, parameters: { type, properties, required } } } } -
maxIterationsaus ChatContext übernehmen statt fest 10.
Phase 2: Backend – API-Key Routing
Datei: Endpoints/ChatEndpoint.cs
Aktuell:
if (!string.IsNullOrEmpty(ctx.Provider))
await openAi.StreamChat(...); // kein API-Key
else
await anthropic.StreamChat(..., apiToken: userApiKey.ApiKey);
Neu: Wenn Provider = openai → Key aus api_keys-Tabelle durchreichen:
if (!string.IsNullOrEmpty(ctx.Provider))
{
// Bei OpenAI: User-API-Key aus DB verwenden
var providerApiKey = ctx.Provider == "openai" ? userApiKey?.ApiKey : null;
await openAi.StreamChat(..., apiKey: providerApiKey, maxIterations: ctx.MaxIterations, ...);
}
Problem: Der User hat aktuell EINEN selected_api_key_id. Wenn er Anthropic nutzt, ist
das ein Anthropic-Key. Wenn er OpenAI nutzt, braucht er einen OpenAI-Key.
Lösung: GetUserSelectedApiKey um Provider-Filterung erweitern:
- ODER: Automatisch den passenden Key basierend auf Agent-Model wählen.
- ODER: Pro User mehrere Keys (einen pro Provider) erlauben.
→ Empfehlung: Automatische Key-Auswahl basierend auf Provider.
Neuer DB-Call: GetActiveApiKeyForProvider(userId, provider) – gibt den ersten
aktiven Key des Users für diesen Provider zurück.
SELECT k.* FROM api_keys k
WHERE k.provider = @provider
AND k.is_active = 1
AND (k.is_public = 1 OR k.key_id = (SELECT selected_api_key_id FROM users WHERE user_id = @userId))
ORDER BY k.is_public ASC -- User-eigener Key bevorzugt
LIMIT 1
Noch einfacher: Der User wählt weiterhin EINEN Key, aber das System erkennt
den Provider anhand des Keys und routet automatisch.
→ User braucht einen OpenAI-Key in der api_keys-Tabelle.
Einfachste Lösung:
// In ChatEndpoint:
ApiKeyRecord resolvedKey;
if (ctx.Provider == "openai")
resolvedKey = await db.GetFirstActiveKeyByProvider("openai");
else
resolvedKey = userApiKey; // Anthropic wie bisher
if (resolvedKey == null) { /* Fehler: Kein Key für Provider */ }
Phase 3: Backend – Modell-Liste
Datei: Services/OpenAiService.cs
OpenAI-Modelle dynamisch laden:
private List<ProviderModel> _openaiModels = new();
// In RefreshModelsFromApis():
var openAiKey = await _db.GetFirstActiveKeyByProvider("openai");
if (openAiKey != null)
_openaiModels = await FetchModels("https://api.openai.com/v1/models", "OpenAI", openAiKey.ApiKey);
Problem: RefreshModelsFromApis() hat keinen Zugriff auf user-spezifische Keys.
→ Den ersten aktiven OpenAI-Key aus der DB nehmen (egal welcher User).
Datei: Endpoints/ModelsEndpoint.cs
Neuen Endpoint:
app.MapGet("/api/models/openai", HandleOpenAiModels);
Phase 4: Frontend – Modell-Auswahl
Datei: wwwroot/index.html
Neue Optgroup im Agent-Model-Select:
<optgroup label="OpenAI" id="openaiOptgroup">
<option value="" disabled>Wird geladen...</option>
</optgroup>
Datei: wwwroot/js/pages/agent-page.js
OpenAI-Modelle laden:
loadProviderModels('/api/models/openai', 'openai', openaiOptgroup)
Phase 5: Frontend – Key-Switch-Modal
Datei: wwwroot/js/pages/chat-page.js
Im Key-Switch-Modal den Provider des Keys anzeigen:
- Anthropic-Keys:
🟤 Anthropic · Pro · ✅ - OpenAI-Keys:
🟢 OpenAI · Tier 5 · ✅
Aktuell zeigt das Modal alle Keys unabhängig vom Provider. → Keys nach Provider gruppieren oder filtern (nur passende Keys zeigen).
Phase 6: Usage-Tracking (optional)
Datei: Services/OpenAiService.cs
Nach jedem OpenAI-API-Call die Usage aus dem Response extrahieren
und in api_keys updaten (analog zu AnthropicService.TryUpdateKeyMeta()).
OpenAI Response-Header enthalten keine Usage-Infos wie Anthropic.
Aber der letzte SSE-Chunk enthält usage: { prompt_tokens, completion_tokens }.
→ Nach dem Stream die Token-Counts aus dem letzten Chunk in die DB schreiben.
Dateien & Aufwand
| Datei | Änderung | Aufwand |
|---|---|---|
Services/OpenAiService.cs |
openai Provider, API-Key-Parameter, alle Tools, maxIterations |
Mittel |
Endpoints/ChatEndpoint.cs |
API-Key-Routing basierend auf Provider | Niedrig |
Endpoints/ModelsEndpoint.cs |
GET /api/models/openai |
Niedrig |
Services/DatabaseService.Integrations.cs |
GetFirstActiveKeyByProvider(provider) |
Niedrig |
wwwroot/index.html |
OpenAI Optgroup | Minimal |
wwwroot/js/pages/agent-page.js |
OpenAI-Modelle laden | Minimal |
wwwroot/js/pages/chat-page.js |
Provider-Info im Key-Switch-Modal | Niedrig |
Gesamt: ~200-250 Zeilen Änderungen, Aufwand mittel
Risiken
-
Tool-Format-Differenzen – OpenAI und Anthropic haben leicht unterschiedliche Tool-Call/Result-Formate. Die Konvertierung in
ConvertMessage()muss stimmen. → Besteht schon für Nvidia/Gemini, sollte auch für OpenAI funktionieren. -
Streaming-Format – OpenAI SSE-Format unterscheidet sich von Anthropic. → Wird bereits im
OpenAiServicekorrekt gehandelt. -
Rate-Limits – OpenAI Rate-Limits sind pro Organisation, nicht pro Key. → Kein spezifisches Handling nötig, 429 wird als Fehler angezeigt.
-
Modell-Liste zu lang – OpenAI hat ~43 Modelle (inkl. Legacy, Embedding, TTS). → Filtern auf Chat-Modelle: nur
gpt-*,o1-*,o3-*,chatgpt-*.
Nicht im Scope (später)
- Review/Preparer/Finalizer für OpenAI-Provider
- ask_user_choice für OpenAI-Provider
- Web-Search für OpenAI-Provider
- Memory-Tools für OpenAI-Provider (aktuell nur 6 Tools) → Diese Features erfordern einen größeren Umbau des OpenAiService