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:
@@ -0,0 +1,94 @@
|
||||
using System.ComponentModel;
|
||||
using ClawdDotNet.Core.Api;
|
||||
using ClawdDotNet.Core.Api.Models;
|
||||
|
||||
namespace ClawdDotNet.Models;
|
||||
|
||||
/// <summary>
|
||||
/// TypeConverter der im PropertyGrid eine Dropdown-Liste
|
||||
/// mit verfügbaren OpenRouter-Modellen anzeigt.
|
||||
/// Die Modelle werden einmalig per API abgerufen und gecached.
|
||||
/// Freitext-Eingabe bleibt weiterhin möglich (CanConvertFrom = true).
|
||||
/// </summary>
|
||||
public sealed class ModelTypeConverter : StringConverter
|
||||
{
|
||||
private static List<ModelInfo>? _cachedModels;
|
||||
private static bool _fetchInProgress;
|
||||
private static readonly Lock _lock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Wird von außen gesetzt (beim Start der Anwendung), damit
|
||||
/// der Converter Zugriff auf den OpenRouterClient hat.
|
||||
/// </summary>
|
||||
public static OpenRouterClient? Client { get; set; }
|
||||
|
||||
public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true;
|
||||
|
||||
/// <summary>false = Dropdown ist editierbar (Freitext erlaubt)</summary>
|
||||
public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => false;
|
||||
|
||||
public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context)
|
||||
{
|
||||
EnsureModelsLoaded();
|
||||
|
||||
if (_cachedModels is null || _cachedModels.Count == 0)
|
||||
{
|
||||
// Fallback: Einige gängige Modelle
|
||||
return new StandardValuesCollection(new[]
|
||||
{
|
||||
"anthropic/claude-sonnet-4-5",
|
||||
"anthropic/claude-haiku-4",
|
||||
"openai/gpt-4.1",
|
||||
"openai/gpt-4.1-mini",
|
||||
"google/gemini-2.5-pro-preview",
|
||||
"google/gemini-2.5-flash-preview",
|
||||
"deepseek/deepseek-chat-v3-0324",
|
||||
"meta-llama/llama-4-maverick"
|
||||
});
|
||||
}
|
||||
|
||||
var ids = _cachedModels.Select(m => m.Id).ToArray();
|
||||
return new StandardValuesCollection(ids);
|
||||
}
|
||||
|
||||
private static void EnsureModelsLoaded()
|
||||
{
|
||||
if (_cachedModels is not null || Client is null)
|
||||
return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_cachedModels is not null || _fetchInProgress)
|
||||
return;
|
||||
|
||||
_fetchInProgress = true;
|
||||
}
|
||||
|
||||
// Asynchronen Abruf im Hintergrund starten
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var models = await Client.GetAvailableModelsAsync();
|
||||
_cachedModels = models;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_cachedModels = []; // Fehler → Fallback wird verwendet
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_lock) { _fetchInProgress = false; }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kann von außen aufgerufen werden um den Cache zu leeren
|
||||
/// (z.B. wenn sich der API-Key ändert).
|
||||
/// </summary>
|
||||
public static void InvalidateCache()
|
||||
{
|
||||
lock (_lock) { _cachedModels = null; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user