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>
95 lines
2.9 KiB
C#
95 lines
2.9 KiB
C#
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; }
|
|
}
|
|
}
|