refactor: Backend-Deduplizierung – Parameter-Objekte, Shared Helpers, KeyCheckDispatcher

- ToolDefinitionOptions + ToolExecutionContext ersetzen lange Parameterlisten in ToolService
- AppendToLastMessage-Helper in ChatService eliminiert 5x duplizierte Logik
- OpenAiService: ResolveProviderConfig + BuildApiMessages + ConvertToOpenAiTools statt doppeltem Code
- Tool-Definitionen aus einer Quelle (Anthropic→OpenAI Konvertierung statt hardcodierter Liste)
- KeyCheckDispatcher zentralisiert Key-Check-Dispatch (ApiKeysEndpoint, KeyScanner, GitHubEventScanner)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MelekiandClaude Opus 4.6 committed 2026-03-19 08:01:49 +01:00
1 parent f3069cedf7
commit 3e19da4d48
12 files changed
+428 -487

No files matched your search

+42 -89
View File
@@ -182,72 +182,56 @@ public static class ApiKeysEndpoint
return;
}
switch (existing.Provider)
var check = await KeyCheckDispatcher.CheckByProvider(httpFactory, existing.Provider, existing.ApiKey);
if (check != null)
{
case "anthropic":
await db.UpdateApiKeyMeta(keyId, check.DbStatus, check.Plan, check.Usage7d, check.Usage5h);
// Provider-spezifische Response-Details
if (check.ProviderResult is KeyCheckResult anthropic)
{
var result = await AnthropicKeyChecker.CheckKeyAsync(httpFactory, existing.ApiKey);
var dbStatus = (result.Status == "ok" || result.Status == "usage_limited") ? "valid" : result.Status;
await db.UpdateApiKeyMeta(keyId, dbStatus, result.Plan, result.Usage7d, result.Usage5h);
await ctx.Response.WriteAsJsonAsync(new
{
status = dbStatus,
message = result.Message,
plan = result.Plan,
result.IsSubscription,
result.Usage7d,
result.Usage5h,
result.Overage,
status7d = result.Status7d,
status5h = result.Status5h,
statusOverage = result.StatusOverage
status = check.DbStatus,
message = check.Message,
plan = check.Plan,
anthropic.IsSubscription,
anthropic.Usage7d,
anthropic.Usage5h,
anthropic.Overage,
status7d = anthropic.Status7d,
status5h = anthropic.Status5h,
statusOverage = anthropic.StatusOverage
});
break;
}
case "openai":
else if (check.ProviderResult is StripeCheckResult stripe)
{
var result = await OpenAiKeyChecker.CheckKeyAsync(httpFactory, existing.ApiKey);
var dbStatus = result.Status == "ok" ? "valid" : result.Status;
await db.UpdateApiKeyMeta(keyId, dbStatus, result.Message, null, null);
await ctx.Response.WriteAsJsonAsync(new { status = dbStatus, message = result.Message, plan = result.Message });
break;
}
case "google":
{
var result = await GoogleKeyChecker.CheckKeyAsync(httpFactory, existing.ApiKey);
var dbStatus = result.Status == "ok" ? "valid" : result.Status;
await db.UpdateApiKeyMeta(keyId, dbStatus, result.Message, null, null);
await ctx.Response.WriteAsJsonAsync(new { status = dbStatus, message = result.Message, plan = result.Message });
break;
}
case "stripe":
{
var result = await StripeKeyChecker.CheckKeyAsync(httpFactory, existing.ApiKey);
var dbStatus = result.Status == "ok" ? "valid" : result.Status;
await db.UpdateApiKeyMeta(keyId, dbStatus, result.Message, null, null);
await ctx.Response.WriteAsJsonAsync(new {
status = dbStatus,
message = result.Message,
plan = result.IsLiveKey ? "Live" : "Test",
result.AccountId,
result.AccountName,
result.AccountEmail,
result.AccountCountry,
result.Currency,
result.BalanceAvailable,
result.BalancePending,
result.ChargesEnabled,
result.PayoutsEnabled
await ctx.Response.WriteAsJsonAsync(new
{
status = check.DbStatus,
message = check.Message,
plan = check.Plan,
stripe.AccountId,
stripe.AccountName,
stripe.AccountEmail,
stripe.AccountCountry,
stripe.Currency,
stripe.BalanceAvailable,
stripe.BalancePending,
stripe.ChargesEnabled,
stripe.PayoutsEnabled
});
break;
}
default:
else
{
ctx.Response.StatusCode = 400;
await ctx.Response.WriteAsJsonAsync(new { error = $"Check für Provider '{existing.Provider}' nicht unterstützt" });
break;
await ctx.Response.WriteAsJsonAsync(new { status = check.DbStatus, message = check.Message, plan = check.Plan });
}
}
else
{
ctx.Response.StatusCode = 400;
await ctx.Response.WriteAsJsonAsync(new { error = $"Check für Provider '{existing.Provider}' nicht unterstützt" });
}
}
private static async Task HandleCheckAll(HttpContext ctx, IHttpClientFactory httpFactory, DatabaseService db)
@@ -259,41 +243,10 @@ public static class ApiKeysEndpoint
foreach (var key in allKeys)
{
switch (key.Provider)
{
case "anthropic":
{
var result = await AnthropicKeyChecker.CheckKeyAsync(httpFactory, key.ApiKey);
var dbStatus = (result.Status == "ok" || result.Status == "usage_limited") ? "valid" : result.Status;
await db.UpdateApiKeyMeta(key.KeyId, dbStatus, result.Plan, result.Usage7d, result.Usage5h);
results.Add(new { key.KeyId, key.Name, status = dbStatus, result.Plan, result.Usage5h, result.Usage7d });
break;
}
case "openai":
{
var result = await OpenAiKeyChecker.CheckKeyAsync(httpFactory, key.ApiKey);
var dbStatus = result.Status == "ok" ? "valid" : result.Status;
await db.UpdateApiKeyMeta(key.KeyId, dbStatus, result.Message, null, null);
results.Add(new { key.KeyId, key.Name, status = dbStatus, Plan = result.Message, Usage5h = (double?)null, Usage7d = (double?)null });
break;
}
case "google":
{
var result = await GoogleKeyChecker.CheckKeyAsync(httpFactory, key.ApiKey);
var dbStatus = result.Status == "ok" ? "valid" : result.Status;
await db.UpdateApiKeyMeta(key.KeyId, dbStatus, result.Message, null, null);
results.Add(new { key.KeyId, key.Name, status = dbStatus, Plan = result.Message, Usage5h = (double?)null, Usage7d = (double?)null });
break;
}
case "stripe":
{
var result = await StripeKeyChecker.CheckKeyAsync(httpFactory, key.ApiKey);
var dbStatus = result.Status == "ok" ? "valid" : result.Status;
await db.UpdateApiKeyMeta(key.KeyId, dbStatus, result.Message, null, null);
results.Add(new { key.KeyId, key.Name, status = dbStatus, Plan = result.IsLiveKey ? "Live" : "Test", Usage5h = (double?)null, Usage7d = (double?)null });
break;
}
}
var check = await KeyCheckDispatcher.CheckByProvider(httpFactory, key.Provider, key.ApiKey);
if (check == null) continue;
await db.UpdateApiKeyMeta(key.KeyId, check.DbStatus, check.Plan, check.Usage7d, check.Usage5h);
results.Add(new { key.KeyId, key.Name, status = check.DbStatus, check.Plan, check.Usage5h, check.Usage7d });
}
await ctx.Response.WriteAsJsonAsync(new { checked_ = results.Count, results });
+6 -6
View File
@@ -298,12 +298,12 @@ public static class SessionEndpoint
var tools = new List<object>();
if (hasTools)
{
var allTools = toolService.GetToolDefinitions(
includeFileTools: hasWorkspace,
includeMemory: includeMemory,
includeUserChoice: agent.AllowUserChoice,
includeGitHubSearch: includeGitHubSearch,
includeSerperSearch: includeSerperSearch);
var allTools = toolService.GetToolDefinitions(new ToolDefinitionOptions
{
IncludeFileTools = hasWorkspace, IncludeMemory = includeMemory,
IncludeUserChoice = agent.AllowUserChoice,
IncludeGitHubSearch = includeGitHubSearch, IncludeSerperSearch = includeSerperSearch
});
if (planMode)
allTools = toolService.FilterReadOnlyTools(allTools);
tools.AddRange(allTools);