Initial commit: ClawdDotNet

Import des bestehenden Projektstands in Git.
- .NET 10 WinForms Anwendung (Multi-Agent / Tool-System)
- .gitignore fuer Build-Artefakte, Secrets und Runtime-Daten ergaenzt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-26 18:21:46 +02:00
co-authored by Claude Opus 4.8
commit 2fed388c99
154 changed files with 29736 additions and 0 deletions
@@ -0,0 +1,120 @@
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using ClawdDotNet.Core.Api.Models;
using Microsoft.Extensions.Logging;
namespace ClawdDotNet.Core.Api;
public sealed class OpenRouterClient : IDisposable
{
private const string BaseUrl = "https://openrouter.ai/api/v1/";
private readonly HttpClient _http;
private readonly ILogger _logger;
private static readonly JsonSerializerOptions JsonOptions = new()
{
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
};
public OpenRouterClient(string apiKey, ILogger logger, HttpClient? httpClient = null)
{
_logger = logger;
_http = httpClient ?? new HttpClient();
_http.BaseAddress = new Uri(BaseUrl);
_http.Timeout = TimeSpan.FromMinutes(5); // LLM-Calls können bei großen Prompts lange dauern
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
_http.DefaultRequestHeaders.Add("HTTP-Referer", "ClawdDotNet");
_http.DefaultRequestHeaders.Add("X-Title", "ClawdDotNet");
}
public async Task<ChatResponse> CompleteAsync(ChatRequest request, CancellationToken ct)
{
request.Stream = false;
var json = JsonSerializer.Serialize(request, JsonOptions);
using var content = new StringContent(json, Encoding.UTF8, "application/json");
_logger.LogDebug("Sending request to OpenRouter: model={Model}, messages={Count}",
request.Model, request.Messages.Count);
using var response = await _http.PostAsync("chat/completions", content, ct);
var responseBody = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
{
_logger.LogError("OpenRouter API error {StatusCode}: {Body}",
(int)response.StatusCode, responseBody);
throw new OpenRouterException(
$"API request failed with status {(int)response.StatusCode}",
(int)response.StatusCode,
responseBody);
}
var result = JsonSerializer.Deserialize<ChatResponse>(responseBody, JsonOptions)
?? throw new OpenRouterException("Empty response from OpenRouter", 0, responseBody);
if (result.Error is not null)
{
_logger.LogError("OpenRouter returned error: {Error}", result.Error.Message);
throw new OpenRouterException(result.Error.Message, result.Error.Code ?? 0, responseBody);
}
_logger.LogDebug("OpenRouter response received: tokens={Tokens}",
result.Usage?.TotalTokens ?? 0);
return result;
}
/// <summary>
/// Ruft die verfügbaren Modelle von OpenRouter ab (/models Endpoint).
/// Gibt eine Liste von Modell-IDs zurück, sortiert nach Name.
/// </summary>
public async Task<List<ModelInfo>> GetAvailableModelsAsync(CancellationToken ct = default)
{
_logger.LogDebug("Fetching available models from OpenRouter...");
using var response = await _http.GetAsync("models", ct);
var body = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
{
_logger.LogError("Failed to fetch models: {StatusCode} {Body}",
(int)response.StatusCode, body);
return [];
}
var doc = JsonDocument.Parse(body);
if (!doc.RootElement.TryGetProperty("data", out var dataArray))
return [];
var models = new List<ModelInfo>();
foreach (var item in dataArray.EnumerateArray())
{
var id = item.GetProperty("id").GetString() ?? "";
var name = item.TryGetProperty("name", out var nameProp) ? nameProp.GetString() ?? id : id;
models.Add(new ModelInfo { Id = id, Name = name });
}
models.Sort((a, b) => string.Compare(a.Id, b.Id, StringComparison.OrdinalIgnoreCase));
_logger.LogDebug("Fetched {Count} models from OpenRouter", models.Count);
return models;
}
public void Dispose()
{
_http.Dispose();
}
}
public sealed class OpenRouterException(string message, int statusCode, string responseBody)
: Exception(message)
{
public int StatusCode { get; } = statusCode;
public string ResponseBody { get; } = responseBody;
}