Fix cronjob/telegram fallback for keyless server-key providers

Cronjobs and Telegram bots threw a hard error when an agent had no
assigned user API key, even for providers (nvidia/gemini/minimax/
pollinations) whose server-side key ChatEndpoint resolves automatically
via ResolveProviderConfig. The guard blocked every non-empty provider
instead of only "openai", so e.g. the nvidia agent failed on "Jetzt
ausführen" although the normal chat worked.

Mirror ChatEndpoint's three-way handling at all three sites
(ExecuteCronjob, SendCronjobNotification, TelegramService.HandleMessage):
- Anthropic (empty provider): Claude CLI fallback when permitted/available
- "openai": hard error (no server key, no CLI equivalent)
- other providers: pass through with empty token; server key resolved later

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MelekiandClaude Opus 4.8 committed 2026-06-19 13:25:05 +02:00
1 parent f99c3c1db9
commit e3f89043c1
2 files changed
+70 -9

No files matched your search

+58 -2
View File
@@ -91,9 +91,37 @@ public class CronjobService : BackgroundService
// API-Key des Cronjob-Owners für diesen Agent auflösen
var userApiKey = await _db.GetAgentApiKeyRecord(job.UserId, ctx.AgentId);
bool useCliFallback = false;
if (userApiKey == null)
throw new Exception($"Kein API-Key für Agent {ctx.AgentId} zugewiesen (User {job.UserId})");
{
// Kein User-Key – Verhalten exakt wie ChatEndpoint:
// - Anthropic (Provider leer) → Claude-CLI-Fallback (sofern berechtigt/verfügbar)
// - "openai" → harter Fehler (kein Server-Key, kein CLI-Äquivalent)
// - nvidia/gemini/minimax/pollinations → durchlassen: ResolveProviderConfig löst den Server-Key auf
if (string.IsNullOrEmpty(ctx.Provider))
{
var jobOwner = await _db.GetUser(job.UserId);
if (jobOwner != null && !jobOwner.CanUseCli && !jobOwner.IsAdmin)
throw new Exception($"Kein API-Key für Agent {ctx.AgentId} zugewiesen und User {job.UserId} hat keine CLI-Berechtigung.");
if (!_anthropicService.IsCliAvailable)
throw new Exception($"Kein API-Key für Agent {ctx.AgentId} zugewiesen und Claude CLI nicht verfügbar.");
useCliFallback = true;
_logger.LogInformation("Cronjob '{Name}': Kein API-Key für Agent {AgentId}, verwende CLI-Fallback", job.Name, ctx.AgentId);
}
else if (ctx.Provider == "openai")
{
throw new Exception($"Kein API-Key für Agent {ctx.AgentId} zugewiesen (User {job.UserId}). OpenAI benötigt zwingend einen zugewiesenen API-Key.");
}
// sonst: nvidia/gemini/minimax/pollinations → ctx.ApiToken bleibt leer,
// der Server-seitige Provider-Key wird in ResolveProviderConfig aufgelöst.
}
else
{
ctx.ApiToken = userApiKey.ApiKey;
}
// History aus DB laden. includeThinking=true für OpenAI-kompatible Provider →
// <think>...</think>-Prefix wird dem Assistant-Text vorangestellt.
@@ -117,6 +145,8 @@ public class CronjobService : BackgroundService
string responseText;
if (!string.IsNullOrEmpty(ctx.Provider))
responseText = await _openAiService.ChatNonStreaming(ctx, history);
else if (useCliFallback)
responseText = await _anthropicService.ChatNonStreamingViaCli(ctx, history);
else
responseText = await _anthropicService.ChatNonStreaming(ctx, history);
@@ -150,12 +180,36 @@ public class CronjobService : BackgroundService
// API-Key des Cronjob-Owners für Notify-Agent auflösen
var notifyApiKey = await _db.GetAgentApiKeyRecord(job.UserId, notifyCtx.AgentId);
bool notifyUseCliFallback = false;
if (notifyApiKey == null)
{
_logger.LogWarning("Kein API-Key für Agent {AgentId} (Cronjob '{Name}' Notification)", notifyCtx.AgentId, job.Name);
// Kein User-Key – Verhalten wie ExecuteCronjob/ChatEndpoint:
// - Anthropic (Provider leer) → Claude-CLI-Fallback (sofern berechtigt/verfügbar)
// - "openai" → abbrechen (kein Server-Key, kein CLI-Äquivalent)
// - nvidia/gemini/minimax/pollinations → durchlassen: Server-Key via ResolveProviderConfig
if (string.IsNullOrEmpty(notifyCtx.Provider))
{
var notifyOwner = await _db.GetUser(job.UserId);
if ((notifyOwner != null && !notifyOwner.CanUseCli && !notifyOwner.IsAdmin) || !_anthropicService.IsCliAvailable)
{
_logger.LogWarning("Kein API-Key für Agent {AgentId} (Cronjob '{Name}' Notification) und CLI nicht verfügbar/berechtigt", notifyCtx.AgentId, job.Name);
return;
}
notifyUseCliFallback = true;
}
else if (notifyCtx.Provider == "openai")
{
_logger.LogWarning("Kein API-Key für Agent {AgentId} (Cronjob '{Name}' Notification, OpenAI benötigt zwingend einen Key)", notifyCtx.AgentId, job.Name);
return;
}
// sonst: nvidia/gemini/minimax/pollinations → notifyCtx.ApiToken bleibt leer (Server-Key-Auflösung).
}
else
{
notifyCtx.ApiToken = notifyApiKey.ApiKey;
}
// Kurze Zusammenfassung als User-Nachricht in die Notify-Session schreiben
var summary = $"[Cronjob \"{job.Name}\" wurde in Session \"{job.SessionName}\" ausgeführt]\n\nAntwort:\n{Truncate(responseText, 2000)}";
@@ -183,6 +237,8 @@ public class CronjobService : BackgroundService
string notifyResponse;
if (!string.IsNullOrEmpty(notifyCtx.Provider))
notifyResponse = await _openAiService.ChatNonStreaming(notifyCtx, notifyHistory);
else if (notifyUseCliFallback)
notifyResponse = await _anthropicService.ChatNonStreamingViaCli(notifyCtx, notifyHistory);
else
notifyResponse = await _anthropicService.ChatNonStreaming(notifyCtx, notifyHistory);
+12 -7
View File
@@ -177,14 +177,12 @@ public class TelegramService
if (userApiKey == null)
{
// Kein Key → CLI-Fallback prüfen (analog ChatEndpoint).
// Nur für Anthropic-Agents möglich (CLI = Claude). OpenAI-Provider haben kein CLI-Äquivalent.
if (!string.IsNullOrEmpty(ctx.Provider))
// Kein User-Key – Verhalten exakt wie ChatEndpoint:
// - Anthropic (Provider leer) → Claude-CLI-Fallback (sofern berechtigt/verfügbar)
// - "openai" → abbrechen (kein Server-Key, kein CLI-Äquivalent)
// - nvidia/gemini/minimax/pollinations → durchlassen: Server-Key via ResolveProviderConfig
if (string.IsNullOrEmpty(ctx.Provider))
{
await SendMessage(chatId, botToken, "❌ Kein API-Key für diesen Agent zugewiesen. Für OpenAI-kompatible Provider ist kein CLI-Fallback möglich – der Bot-Besitzer muss im Web-UI einen Key zuweisen.");
return;
}
var botOwner = await _db.GetUser(botUserId);
if (botOwner != null && !botOwner.CanUseCli && !botOwner.IsAdmin)
{
@@ -200,6 +198,13 @@ public class TelegramService
useCliFallback = true;
}
else if (ctx.Provider == "openai")
{
await SendMessage(chatId, botToken, "❌ Kein API-Key für diesen Agent zugewiesen. OpenAI benötigt zwingend einen Key – der Bot-Besitzer muss im Web-UI einen Key zuweisen.");
return;
}
// sonst: nvidia/gemini/minimax/pollinations → ctx.ApiToken bleibt leer (Server-Key-Auflösung).
}
else
{
ctx.ApiToken = userApiKey.ApiKey;