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; }
|
||||
|
||||
|
||||
@@ -806,10 +806,12 @@ Für welche Plattform soll die App entwickelt werden? Web, Mobile oder Desktop?
|
||||
if (!string.IsNullOrWhiteSpace(systemText))
|
||||
prompt.Add(new { type = "text", text = systemText });
|
||||
|
||||
var (provider, _) = OpenAiService.ParseModelString(modelId);
|
||||
var hasTools = !string.IsNullOrEmpty(workspace) || agent.UseMemory;
|
||||
return new SubAgentContext
|
||||
{
|
||||
ModelId = modelId,
|
||||
Provider = provider,
|
||||
SystemPrompt = prompt,
|
||||
AgentId = agent.AgentId,
|
||||
Workspace = workspace,
|
||||
|
||||
@@ -15,6 +15,7 @@ public class CronjobService : BackgroundService
|
||||
private readonly DatabaseService _db;
|
||||
private readonly ChatService _chatService;
|
||||
private readonly AnthropicService _anthropicService;
|
||||
private readonly OpenAiService _openAiService;
|
||||
private readonly TelegramService _telegramService;
|
||||
private readonly ILogger<CronjobService> _logger;
|
||||
|
||||
@@ -22,12 +23,14 @@ public class CronjobService : BackgroundService
|
||||
DatabaseService db,
|
||||
ChatService chatService,
|
||||
AnthropicService anthropicService,
|
||||
OpenAiService openAiService,
|
||||
TelegramService telegramService,
|
||||
ILogger<CronjobService> logger)
|
||||
{
|
||||
_db = db;
|
||||
_chatService = chatService;
|
||||
_anthropicService = anthropicService;
|
||||
_openAiService = openAiService;
|
||||
_telegramService = telegramService;
|
||||
_logger = logger;
|
||||
}
|
||||
@@ -109,8 +112,12 @@ public class CronjobService : BackgroundService
|
||||
Content = job.Prompt
|
||||
});
|
||||
|
||||
// Non-Streaming Chat ausführen
|
||||
var responseText = await _anthropicService.ChatNonStreaming(ctx, history);
|
||||
// Non-Streaming Chat ausführen – Provider-Weiche
|
||||
string responseText;
|
||||
if (!string.IsNullOrEmpty(ctx.Provider))
|
||||
responseText = await _openAiService.ChatNonStreaming(ctx, history);
|
||||
else
|
||||
responseText = await _anthropicService.ChatNonStreaming(ctx, history);
|
||||
|
||||
// Session-Timestamp aktualisieren
|
||||
await _db.UpdateSessionTimestamp(job.SessionId);
|
||||
@@ -172,7 +179,11 @@ public class CronjobService : BackgroundService
|
||||
["content"] = new List<object> { new { type = "text", text = summary } }
|
||||
}
|
||||
};
|
||||
var notifyResponse = await _anthropicService.ChatNonStreaming(notifyCtx, notifyHistory);
|
||||
string notifyResponse;
|
||||
if (!string.IsNullOrEmpty(notifyCtx.Provider))
|
||||
notifyResponse = await _openAiService.ChatNonStreaming(notifyCtx, notifyHistory);
|
||||
else
|
||||
notifyResponse = await _anthropicService.ChatNonStreaming(notifyCtx, notifyHistory);
|
||||
|
||||
await _db.UpdateSessionTimestamp(job.NotifySessionId);
|
||||
|
||||
|
||||
@@ -732,6 +732,204 @@ public class OpenAiService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Non-Streaming Chat für Telegram, Cronjobs und send_to_session.
|
||||
/// Gibt den finalen Antworttext zurück. Unterstützt Tool-Calling (agentic loop).
|
||||
/// </summary>
|
||||
public async Task<string> ChatNonStreaming(
|
||||
ChatContext ctx,
|
||||
List<object> messages,
|
||||
Func<int, Task> onIteration = null)
|
||||
{
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
var usage = new TokenUsage();
|
||||
int iterations = 0;
|
||||
var finalText = new StringBuilder();
|
||||
|
||||
var (provider, modelId) = ParseModelString(ctx.ModelId);
|
||||
if (!Providers.TryGetValue(provider, out var providerConfig))
|
||||
throw new Exception($"Unbekannter Provider: '{provider}'");
|
||||
|
||||
// API-Key Override: User-Key hat Vorrang
|
||||
if (!string.IsNullOrEmpty(ctx.ApiToken))
|
||||
providerConfig = (providerConfig.BaseUrl, ctx.ApiToken);
|
||||
|
||||
// Gemini: Key dynamisch aus ServerSettings
|
||||
if (provider == "gemini" && string.IsNullOrEmpty(providerConfig.ApiKey))
|
||||
providerConfig = (providerConfig.BaseUrl, _settings.Get("GeminiApiKey"));
|
||||
|
||||
var chain = new ChainRecord
|
||||
{
|
||||
ChainId = ctx.ChainId,
|
||||
SessionId = ctx.SessionId,
|
||||
Model = ctx.ModelId
|
||||
};
|
||||
|
||||
// Messages in OpenAI-Format konvertieren
|
||||
var apiMessages = new List<object>();
|
||||
if (!string.IsNullOrEmpty(ctx.SystemPromptText))
|
||||
apiMessages.Add(new { role = "system", content = ctx.SystemPromptText });
|
||||
|
||||
foreach (var msg in messages)
|
||||
{
|
||||
var converted = ConvertMessage(msg);
|
||||
if (converted == null) continue;
|
||||
if (converted is List<object> toolResultList)
|
||||
{
|
||||
foreach (var tr in toolResultList)
|
||||
apiMessages.Add(tr);
|
||||
continue;
|
||||
}
|
||||
apiMessages.Add(converted);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
while (iterations < ctx.MaxIterations)
|
||||
{
|
||||
iterations++;
|
||||
onIteration?.Invoke(iterations);
|
||||
|
||||
var client = CreateClient(providerConfig.ApiKey);
|
||||
var payload = new Dictionary<string, object>
|
||||
{
|
||||
["model"] = modelId,
|
||||
["messages"] = apiMessages,
|
||||
["stream"] = false,
|
||||
["max_tokens"] = 16384,
|
||||
["tools"] = OpenAiTools,
|
||||
["tool_choice"] = "auto"
|
||||
};
|
||||
|
||||
var jsonPayload = JsonSerializer.Serialize(payload);
|
||||
var httpContent = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
|
||||
|
||||
using var httpResp = await client.SendAsync(
|
||||
new HttpRequestMessage(HttpMethod.Post, $"{providerConfig.BaseUrl}/chat/completions") { Content = httpContent });
|
||||
|
||||
var respBody = await httpResp.Content.ReadAsStringAsync();
|
||||
|
||||
if (!httpResp.IsSuccessStatusCode)
|
||||
{
|
||||
var errMsg = $"API {(int)httpResp.StatusCode}: {respBody}";
|
||||
await _db.SaveMessage(new MessageRecord
|
||||
{
|
||||
ChainId = ctx.ChainId, Iteration = iterations,
|
||||
Role = "system", Type = "error",
|
||||
Content = errMsg, IsError = true
|
||||
});
|
||||
await FinalizeChain(chain, usage, iterations, "error", sw);
|
||||
throw new Exception($"OpenAI API Fehler: {(int)httpResp.StatusCode}");
|
||||
}
|
||||
|
||||
var resp = JsonSerializer.Deserialize<JsonElement>(respBody);
|
||||
|
||||
// Usage tracken
|
||||
if (resp.TryGetProperty("usage", out var usageProp))
|
||||
{
|
||||
if (usageProp.TryGetProperty("prompt_tokens", out var pt))
|
||||
usage.InputTokens = pt.GetInt64();
|
||||
if (usageProp.TryGetProperty("completion_tokens", out var ct2))
|
||||
usage.OutputTokens = ct2.GetInt64();
|
||||
}
|
||||
|
||||
// Erste Choice auswerten
|
||||
var choices = resp.GetProperty("choices");
|
||||
if (choices.GetArrayLength() == 0) break;
|
||||
|
||||
var choice = choices[0];
|
||||
var message = choice.GetProperty("message");
|
||||
var finishReason = choice.TryGetProperty("finish_reason", out var frProp) ? frProp.GetString() ?? "" : "";
|
||||
|
||||
// Text extrahieren
|
||||
if (message.TryGetProperty("content", out var contentProp) && contentProp.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var text = contentProp.GetString() ?? "";
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
finalText.Append(text);
|
||||
await _db.SaveMessage(new MessageRecord
|
||||
{
|
||||
ChainId = ctx.ChainId, Iteration = iterations,
|
||||
Role = "assistant", Type = "text",
|
||||
Content = text
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Tool-Calls prüfen
|
||||
if (!message.TryGetProperty("tool_calls", out var toolCallsProp) || toolCallsProp.GetArrayLength() == 0)
|
||||
{
|
||||
await FinalizeChain(chain, usage, iterations, "end_turn", sw);
|
||||
break;
|
||||
}
|
||||
|
||||
// Assistant-Message mit tool_calls in History
|
||||
var assistantMsg = JsonSerializer.Deserialize<JsonElement>(message.GetRawText());
|
||||
apiMessages.Add(new
|
||||
{
|
||||
role = "assistant",
|
||||
content = message.TryGetProperty("content", out var c) && c.ValueKind == JsonValueKind.String ? c.GetString() ?? "" : "",
|
||||
tool_calls = JsonSerializer.Deserialize<List<object>>(toolCallsProp.GetRawText())
|
||||
});
|
||||
|
||||
// Tool-Calls ausführen
|
||||
var toolResults = new List<object>();
|
||||
foreach (var tc in toolCallsProp.EnumerateArray())
|
||||
{
|
||||
var tcId = tc.GetProperty("id").GetString() ?? "";
|
||||
var func = tc.GetProperty("function");
|
||||
var tcName = func.GetProperty("name").GetString() ?? "";
|
||||
var tcArgs = func.TryGetProperty("arguments", out var argsProp) ? argsProp.GetString() ?? "{}" : "{}";
|
||||
|
||||
await _db.SaveMessage(new MessageRecord
|
||||
{
|
||||
ChainId = ctx.ChainId, Iteration = iterations,
|
||||
Role = "assistant", Type = "tool_use",
|
||||
Content = tcArgs,
|
||||
ToolCallId = tcId, ToolName = tcName
|
||||
});
|
||||
|
||||
JsonElement inputEl;
|
||||
try { inputEl = JsonSerializer.Deserialize<JsonElement>(tcArgs); }
|
||||
catch { inputEl = JsonSerializer.Deserialize<JsonElement>("{}"); }
|
||||
|
||||
string toolResult;
|
||||
bool isError = false;
|
||||
try { toolResult = await _toolService.ExecuteTool(tcName, inputEl, ctx.Workspace); }
|
||||
catch (Exception ex) { toolResult = $"Fehler: {ex.Message}"; isError = true; }
|
||||
|
||||
toolResults.Add(new
|
||||
{
|
||||
role = "tool",
|
||||
tool_call_id = tcId,
|
||||
content = toolResult
|
||||
});
|
||||
|
||||
await _db.SaveMessage(new MessageRecord
|
||||
{
|
||||
ChainId = ctx.ChainId, Iteration = iterations,
|
||||
Role = "tool", Type = "tool_result",
|
||||
Content = toolResult,
|
||||
ToolCallId = tcId, ToolName = tcName,
|
||||
IsError = isError
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var tr in toolResults)
|
||||
apiMessages.Add(tr);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (iterations > 0)
|
||||
await FinalizeChain(chain, usage, iterations, "error", sw);
|
||||
throw;
|
||||
}
|
||||
|
||||
return finalText.Length > 0 ? finalText.ToString() : "(Keine Textantwort)";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Konvertiert eine Anthropic-format Message in das OpenAI-format.
|
||||
/// </summary>
|
||||
|
||||
@@ -15,6 +15,7 @@ public class TelegramService
|
||||
private readonly DatabaseService _db;
|
||||
private readonly ChatService _chatService;
|
||||
private readonly AnthropicService _anthropic;
|
||||
private readonly OpenAiService _openAi;
|
||||
private readonly ServerSettingsService _settings;
|
||||
|
||||
public TelegramService(
|
||||
@@ -22,12 +23,14 @@ public class TelegramService
|
||||
DatabaseService db,
|
||||
ChatService chatService,
|
||||
AnthropicService anthropic,
|
||||
OpenAiService openAi,
|
||||
ServerSettingsService settings)
|
||||
{
|
||||
_httpFactory = httpFactory;
|
||||
_db = db;
|
||||
_chatService = chatService;
|
||||
_anthropic = anthropic;
|
||||
_openAi = openAi;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
@@ -197,11 +200,23 @@ public class TelegramService
|
||||
Content = userText
|
||||
});
|
||||
|
||||
var reply = await _anthropic.ChatNonStreaming(ctx, history, onIteration: async (iteration) =>
|
||||
string reply;
|
||||
if (!string.IsNullOrEmpty(ctx.Provider))
|
||||
{
|
||||
if (iteration % 3 == 0)
|
||||
await SendChatAction(chatId, botToken, "typing");
|
||||
});
|
||||
reply = await _openAi.ChatNonStreaming(ctx, history, onIteration: async (iteration) =>
|
||||
{
|
||||
if (iteration % 3 == 0)
|
||||
await SendChatAction(chatId, botToken, "typing");
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
reply = await _anthropic.ChatNonStreaming(ctx, history, onIteration: async (iteration) =>
|
||||
{
|
||||
if (iteration % 3 == 0)
|
||||
await SendChatAction(chatId, botToken, "typing");
|
||||
});
|
||||
}
|
||||
|
||||
await SendLongMessage(chatId, botToken, reply);
|
||||
}
|
||||
|
||||
+10
-4
@@ -390,7 +390,7 @@ public class ToolService
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<string> ExecuteTool(string name, JsonElement input, string workspace, MemoryService memoryService = null, string agentId = null, CancellationToken cancellationToken = default, RemoteConnectionManager remoteMgr = null, string sessionId = null, VectorSearchService vectorSearch = null, EmbeddingBackgroundService embeddingBg = null, DatabaseService db = null, ChatService chatService = null, AnthropicService anthropicService = null, string userId = null, TelegramService telegramService = null, string apiToken = null)
|
||||
public async Task<string> ExecuteTool(string name, JsonElement input, string workspace, MemoryService memoryService = null, string agentId = null, CancellationToken cancellationToken = default, RemoteConnectionManager remoteMgr = null, string sessionId = null, VectorSearchService vectorSearch = null, EmbeddingBackgroundService embeddingBg = null, DatabaseService db = null, ChatService chatService = null, AnthropicService anthropicService = null, OpenAiService openAiService = null, string userId = null, TelegramService telegramService = null, string apiToken = null)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
@@ -742,7 +742,7 @@ public class ToolService
|
||||
}
|
||||
case "send_to_session":
|
||||
{
|
||||
if (db == null || chatService == null || anthropicService == null)
|
||||
if (db == null || chatService == null || (anthropicService == null && openAiService == null))
|
||||
return "Fehler: send_to_session nicht verfügbar";
|
||||
|
||||
var sessionIdOrName = input.GetProperty("session_id_or_name").GetString() ?? "";
|
||||
@@ -767,7 +767,10 @@ public class ToolService
|
||||
var ctx = await chatService.PrepareContext(targetSessionId, userId: userId);
|
||||
if (ctx == null)
|
||||
return $"Fehler: Session '{targetSession.Name}' ist nicht konfiguriert (kein Agent zugewiesen?)";
|
||||
ctx.ApiToken = apiToken;
|
||||
|
||||
// API-Key des Ziel-Agents auflösen (nicht den Key des aufrufenden Agents nutzen)
|
||||
var targetApiKey = await db.GetAgentApiKeyRecord(userId, ctx.AgentId);
|
||||
ctx.ApiToken = targetApiKey?.ApiKey ?? apiToken;
|
||||
|
||||
// History aufbauen – User-Message NICHT in DB speichern (wie Cronjob)
|
||||
// Stattdessen als kontextualisierte Nachricht in die History injizieren
|
||||
@@ -780,7 +783,10 @@ public class ToolService
|
||||
|
||||
try
|
||||
{
|
||||
await anthropicService.ChatNonStreaming(ctx, history);
|
||||
if (!string.IsNullOrEmpty(ctx.Provider) && openAiService != null)
|
||||
await openAiService.ChatNonStreaming(ctx, history);
|
||||
else
|
||||
await anthropicService.ChatNonStreaming(ctx, history);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user