Provider-Abstraktion: Agents unterstützen jetzt OpenAI + Anthropic
Backend: - ChatEndpoint: Globaler OpenAI-Fallback entfernt, Key-Check pro Provider - TelegramService/CronjobService: Provider-Weiche statt hardcoded Anthropic - AnthropicService: Preparer/Review/Finalizer sind Provider-aware - OpenAiService: Neue ChatNonStreaming()-Methode für agentic loop - ToolService: send_to_session lädt Key des Ziel-Agents - SubAgentContext: Provider-Feld hinzugefügt - ChatService: BuildSubAgentContext setzt Provider Frontend: - Key-Switch-Modal: 'Kein Key'-Option zum Entfernen der Zuweisung - ApiKeysEndpoint: keyId='none' löscht Key-Zuweisung
This commit is contained in:
1 parent
1effc6d00f
commit
02f5fe2faf
11 files changed
+422
-69
No files matched your search
+101
-22
@@ -50,6 +50,12 @@ public class AnthropicService
|
||||
_telegramService = telegramService;
|
||||
}
|
||||
|
||||
private OpenAiService _openAiService;
|
||||
public void SetOpenAiService(OpenAiService openAiService)
|
||||
{
|
||||
_openAiService = openAiService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Liest Usage-Header aus einer Anthropic-Response und aktualisiert die api_keys-Tabelle.
|
||||
/// Fire-and-forget – Fehler werden stillschweigend ignoriert.
|
||||
@@ -474,12 +480,25 @@ public class AnthropicService
|
||||
var preparerMessages = new List<object>(historyMessages);
|
||||
|
||||
var usage = new TokenUsage();
|
||||
var (text, iterations) = await RunAgentChat(
|
||||
response, preparer.ModelId, preparer.SystemPrompt, preparer.AgentId,
|
||||
preparer.Workspace, preparer.UseMemory, preparer.WebSearchVersion,
|
||||
preparer.ToolChoice, preparerMessages, chainId, sessionId, apiToken, usage,
|
||||
0, "preparer", preparer.Agent?.Name ?? "Vorbereiter-Agent",
|
||||
cancellationToken, cancellationToken);
|
||||
string text;
|
||||
int iterations;
|
||||
|
||||
if (!string.IsNullOrEmpty(preparer.Provider) && _openAiService != null)
|
||||
{
|
||||
// OpenAI-kompatibler Preparer
|
||||
var subCtx = BuildSubAgentChatContext(preparer, chainId, sessionId, apiToken);
|
||||
text = await _openAiService.ChatNonStreaming(subCtx, preparerMessages);
|
||||
iterations = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
(text, iterations) = await RunAgentChat(
|
||||
response, preparer.ModelId, preparer.SystemPrompt, preparer.AgentId,
|
||||
preparer.Workspace, preparer.UseMemory, preparer.WebSearchVersion,
|
||||
preparer.ToolChoice, preparerMessages, chainId, sessionId, apiToken, usage,
|
||||
0, "preparer", preparer.Agent?.Name ?? "Vorbereiter-Agent",
|
||||
cancellationToken, cancellationToken);
|
||||
}
|
||||
|
||||
var result = new PreparerResult { IterationCount = iterations, ResponseText = text };
|
||||
|
||||
@@ -566,13 +585,24 @@ public class AnthropicService
|
||||
// Review-Agent bekommt eigene Message-History
|
||||
var reviewMessages = BuildReviewMessages(workerMessages);
|
||||
|
||||
// Review-Agent iteriert mit eigenen Tools
|
||||
var (reviewText, reviewIterations) = await RunAgentChat(
|
||||
response, review.ModelId, review.SystemPrompt, review.AgentId,
|
||||
workspace, review.UseMemory, review.WebSearchVersion,
|
||||
review.ToolChoice, reviewMessages, chainId, sessionId, apiToken, usage,
|
||||
baseIterations, "review", review.Agent?.Name ?? "Review-Agent",
|
||||
workCt, sseCt);
|
||||
string reviewText;
|
||||
int reviewIterations;
|
||||
|
||||
if (!string.IsNullOrEmpty(review.Provider) && _openAiService != null)
|
||||
{
|
||||
var subCtx = BuildSubAgentChatContext(review, chainId, sessionId, apiToken);
|
||||
reviewText = await _openAiService.ChatNonStreaming(subCtx, reviewMessages);
|
||||
reviewIterations = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
(reviewText, reviewIterations) = await RunAgentChat(
|
||||
response, review.ModelId, review.SystemPrompt, review.AgentId,
|
||||
workspace, review.UseMemory, review.WebSearchVersion,
|
||||
review.ToolChoice, reviewMessages, chainId, sessionId, apiToken, usage,
|
||||
baseIterations, "review", review.Agent?.Name ?? "Review-Agent",
|
||||
workCt, sseCt);
|
||||
}
|
||||
|
||||
var result = new ReviewResult { ReviewIterationCount = reviewIterations };
|
||||
|
||||
@@ -636,12 +666,24 @@ public class AnthropicService
|
||||
// Finalizer bekommt die Konversation als Kontext (gleiche Methode wie Review)
|
||||
var finalizerMessages = BuildReviewMessages(workerMessages);
|
||||
|
||||
var (text, iterations) = await RunAgentChat(
|
||||
response, finalizer.ModelId, finalizer.SystemPrompt, finalizer.AgentId,
|
||||
finalizer.Workspace, finalizer.UseMemory, finalizer.WebSearchVersion,
|
||||
finalizer.ToolChoice, finalizerMessages, chainId, sessionId, apiToken, usage,
|
||||
baseIterations, "finalizer", finalizer.Agent?.Name ?? "Finalizer-Agent",
|
||||
workCt, sseCt);
|
||||
string text;
|
||||
int iterations;
|
||||
|
||||
if (!string.IsNullOrEmpty(finalizer.Provider) && _openAiService != null)
|
||||
{
|
||||
var subCtx = BuildSubAgentChatContext(finalizer, chainId, sessionId, apiToken);
|
||||
text = await _openAiService.ChatNonStreaming(subCtx, finalizerMessages);
|
||||
iterations = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
(text, iterations) = await RunAgentChat(
|
||||
response, finalizer.ModelId, finalizer.SystemPrompt, finalizer.AgentId,
|
||||
finalizer.Workspace, finalizer.UseMemory, finalizer.WebSearchVersion,
|
||||
finalizer.ToolChoice, finalizerMessages, chainId, sessionId, apiToken, usage,
|
||||
baseIterations, "finalizer", finalizer.Agent?.Name ?? "Finalizer-Agent",
|
||||
workCt, sseCt);
|
||||
}
|
||||
|
||||
await SseHelper.TryWriteSse(response, "finalizer_done", new
|
||||
{
|
||||
@@ -769,6 +811,43 @@ public class AnthropicService
|
||||
return messages;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Baut einen ChatContext für Sub-Agents (Preparer/Review/Finalizer) auf,
|
||||
/// wenn diese einen OpenAI-kompatiblen Provider nutzen.
|
||||
/// </summary>
|
||||
private ChatContext BuildSubAgentChatContext(SubAgentContext sub, string chainId, string sessionId, string apiToken)
|
||||
{
|
||||
// SystemPrompt als Text extrahieren (SubAgent hat List<object> mit { type, text } Blöcken)
|
||||
var systemText = "";
|
||||
if (sub.SystemPrompt != null)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
foreach (var block in sub.SystemPrompt)
|
||||
{
|
||||
var json = JsonSerializer.Serialize(block);
|
||||
var el = JsonSerializer.Deserialize<JsonElement>(json);
|
||||
if (el.TryGetProperty("text", out var t))
|
||||
parts.Add(t.GetString() ?? "");
|
||||
}
|
||||
systemText = string.Join("\n\n", parts);
|
||||
}
|
||||
|
||||
return new ChatContext
|
||||
{
|
||||
ChainId = chainId,
|
||||
SessionId = sessionId,
|
||||
ModelId = sub.ModelId,
|
||||
Provider = sub.Provider,
|
||||
Workspace = sub.Workspace,
|
||||
AgentId = sub.AgentId,
|
||||
SystemPromptText = systemText,
|
||||
UseMemory = sub.UseMemory,
|
||||
WebSearchVersion = sub.WebSearchVersion,
|
||||
MaxIterations = 20,
|
||||
ApiToken = apiToken
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generischer Agent-Chat (non-streaming für den Agent, aber SSE-Events für den Client).
|
||||
/// Wird für Review-Agent und Finalizer-Agent verwendet.
|
||||
@@ -918,7 +997,7 @@ public class AnthropicService
|
||||
|
||||
string toolResult;
|
||||
bool isError = false;
|
||||
try { toolResult = await _toolService.ExecuteTool(toolName, toolInput, agentWorkspace, _memoryService, agentId, workCt, vectorSearch: _vectorSearch, embeddingBg: _embeddingBg, db: _db, chatService: _chatService, anthropicService: this, telegramService: _telegramService, apiToken: apiToken); }
|
||||
try { toolResult = await _toolService.ExecuteTool(toolName, toolInput, agentWorkspace, _memoryService, agentId, workCt, vectorSearch: _vectorSearch, embeddingBg: _embeddingBg, db: _db, chatService: _chatService, anthropicService: this, openAiService: _openAiService, telegramService: _telegramService, apiToken: apiToken); }
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch (Exception ex) { toolResult = $"Fehler: {ex.Message}"; isError = true; }
|
||||
|
||||
@@ -1126,7 +1205,7 @@ public class AnthropicService
|
||||
|
||||
string toolResult;
|
||||
bool isError = false;
|
||||
try { toolResult = await _toolService.ExecuteTool(toolName, toolInput, ctx.Workspace, _memoryService, ctx.AgentId, vectorSearch: _vectorSearch, embeddingBg: _embeddingBg, db: _db, chatService: _chatService, anthropicService: this, userId: ctx.Session?.UserId, telegramService: _telegramService, apiToken: ctx.ApiToken); }
|
||||
try { toolResult = await _toolService.ExecuteTool(toolName, toolInput, ctx.Workspace, _memoryService, ctx.AgentId, vectorSearch: _vectorSearch, embeddingBg: _embeddingBg, db: _db, chatService: _chatService, anthropicService: this, openAiService: _openAiService, userId: ctx.Session?.UserId, telegramService: _telegramService, apiToken: ctx.ApiToken); }
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch (Exception ex) { toolResult = $"Fehler: {ex.Message}"; isError = true; }
|
||||
|
||||
@@ -1419,7 +1498,7 @@ public class AnthropicService
|
||||
|
||||
string toolResult;
|
||||
bool isError = false;
|
||||
try { toolResult = await _toolService.ExecuteTool(currentToolName, inputEl, workspace, _memoryService, agentId, workCt, remoteMgr: remoteMgr, sessionId: sessionId, vectorSearch: _vectorSearch, embeddingBg: _embeddingBg, db: _db, chatService: _chatService, anthropicService: this, userId: userId, telegramService: _telegramService, apiToken: apiToken); }
|
||||
try { toolResult = await _toolService.ExecuteTool(currentToolName, inputEl, workspace, _memoryService, agentId, workCt, remoteMgr: remoteMgr, sessionId: sessionId, vectorSearch: _vectorSearch, embeddingBg: _embeddingBg, db: _db, chatService: _chatService, anthropicService: this, openAiService: _openAiService, userId: userId, telegramService: _telegramService, apiToken: apiToken); }
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch (Exception ex) { toolResult = $"Fehler: {ex.Message}"; isError = true; }
|
||||
|
||||
|
||||
Reference in new issue
Block a user