198 lines
4.9 KiB
C#
198 lines
4.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using AxCopilot.Models;
|
|
|
|
namespace AxCopilot.Services;
|
|
|
|
public class ModelRouterService
|
|
{
|
|
private readonly SettingsService _settings;
|
|
|
|
public ModelRouterService(SettingsService settings)
|
|
{
|
|
_settings = settings;
|
|
}
|
|
|
|
public ModelRouteResult? Route(string userMessage)
|
|
{
|
|
LlmSettings llm = _settings.Settings.Llm;
|
|
if (!llm.EnableAutoRouter)
|
|
{
|
|
return null;
|
|
}
|
|
var (category, num) = IntentDetector.Detect(userMessage);
|
|
if (num < llm.AutoRouterConfidence)
|
|
{
|
|
return null;
|
|
}
|
|
if (category == "general")
|
|
{
|
|
return null;
|
|
}
|
|
List<ModelCapability> list = GatherCandidates(llm);
|
|
if (list.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
string currentService = llm.Service.ToLowerInvariant();
|
|
string currentModel = ResolveCurrentModel(llm);
|
|
ModelCapability modelCapability = (from c in list
|
|
where c.Scores.ContainsKey(category)
|
|
orderby c.Scores[category] descending
|
|
select c).FirstOrDefault();
|
|
if (modelCapability == null)
|
|
{
|
|
return null;
|
|
}
|
|
ModelCapability modelCapability2 = list.FirstOrDefault((ModelCapability c) => c.Service.Equals(currentService, StringComparison.OrdinalIgnoreCase) && c.Model.Equals(currentModel, StringComparison.OrdinalIgnoreCase));
|
|
if (modelCapability2 != null && modelCapability2.Scores.TryGetValue(category, out var value) && modelCapability.Scores[category] - value < 0.1)
|
|
{
|
|
return null;
|
|
}
|
|
return new ModelRouteResult(modelCapability.Service, modelCapability.Model, modelCapability.Alias, category, num);
|
|
}
|
|
|
|
private List<ModelCapability> GatherCandidates(LlmSettings llm)
|
|
{
|
|
List<ModelCapability> list = new List<ModelCapability>();
|
|
foreach (ModelCapability modelCapability in llm.ModelCapabilities)
|
|
{
|
|
if (modelCapability.Enabled && IsServiceAvailable(llm, modelCapability.Service))
|
|
{
|
|
list.Add(modelCapability);
|
|
}
|
|
}
|
|
List<ModelCapability> defaultCapabilities = GetDefaultCapabilities();
|
|
foreach (ModelCapability def in defaultCapabilities)
|
|
{
|
|
if (IsServiceAvailable(llm, def.Service) && !list.Any((ModelCapability c) => c.Service.Equals(def.Service, StringComparison.OrdinalIgnoreCase) && c.Model.Equals(def.Model, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
list.Add(def);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private static bool IsServiceAvailable(LlmSettings llm, string service)
|
|
{
|
|
string text = service.ToLowerInvariant();
|
|
if (1 == 0)
|
|
{
|
|
}
|
|
bool result = text switch
|
|
{
|
|
"gemini" => !string.IsNullOrEmpty(llm.GeminiApiKey),
|
|
"claude" => !string.IsNullOrEmpty(llm.ClaudeApiKey),
|
|
"ollama" => !string.IsNullOrEmpty(llm.OllamaEndpoint),
|
|
"vllm" => !string.IsNullOrEmpty(llm.VllmEndpoint),
|
|
_ => false,
|
|
};
|
|
if (1 == 0)
|
|
{
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static string ResolveCurrentModel(LlmSettings llm)
|
|
{
|
|
string service = llm.Service;
|
|
bool flag = ((service == "ollama" || service == "vllm") ? true : false);
|
|
if (flag && !string.IsNullOrEmpty(llm.Model))
|
|
{
|
|
return CryptoService.DecryptIfEnabled(llm.Model, llm.EncryptionEnabled);
|
|
}
|
|
string text = llm.Service.ToLowerInvariant();
|
|
if (1 == 0)
|
|
{
|
|
}
|
|
service = ((text == "gemini") ? llm.GeminiModel : ((!(text == "claude")) ? llm.Model : llm.ClaudeModel));
|
|
if (1 == 0)
|
|
{
|
|
}
|
|
return service;
|
|
}
|
|
|
|
public static List<ModelCapability> GetDefaultCapabilities()
|
|
{
|
|
return new List<ModelCapability>
|
|
{
|
|
new ModelCapability
|
|
{
|
|
Service = "gemini",
|
|
Model = "gemini-2.5-flash",
|
|
Alias = "Gemini 2.5 Flash",
|
|
Scores = new Dictionary<string, double>
|
|
{
|
|
["coding"] = 0.7,
|
|
["translation"] = 0.7,
|
|
["analysis"] = 0.85,
|
|
["creative"] = 0.65,
|
|
["document"] = 0.7,
|
|
["math"] = 0.9
|
|
}
|
|
},
|
|
new ModelCapability
|
|
{
|
|
Service = "gemini",
|
|
Model = "gemini-2.5-pro",
|
|
Alias = "Gemini 2.5 Pro",
|
|
Scores = new Dictionary<string, double>
|
|
{
|
|
["coding"] = 0.85,
|
|
["translation"] = 0.8,
|
|
["analysis"] = 0.9,
|
|
["creative"] = 0.75,
|
|
["document"] = 0.8,
|
|
["math"] = 0.95
|
|
}
|
|
},
|
|
new ModelCapability
|
|
{
|
|
Service = "claude",
|
|
Model = "claude-sonnet-4-6",
|
|
Alias = "Claude Sonnet 4.6",
|
|
Scores = new Dictionary<string, double>
|
|
{
|
|
["coding"] = 0.9,
|
|
["translation"] = 0.85,
|
|
["analysis"] = 0.9,
|
|
["creative"] = 0.9,
|
|
["document"] = 0.85,
|
|
["math"] = 0.8
|
|
}
|
|
},
|
|
new ModelCapability
|
|
{
|
|
Service = "claude",
|
|
Model = "claude-opus-4-6",
|
|
Alias = "Claude Opus 4.6",
|
|
Scores = new Dictionary<string, double>
|
|
{
|
|
["coding"] = 0.95,
|
|
["translation"] = 0.9,
|
|
["analysis"] = 0.95,
|
|
["creative"] = 0.95,
|
|
["document"] = 0.9,
|
|
["math"] = 0.85
|
|
}
|
|
},
|
|
new ModelCapability
|
|
{
|
|
Service = "claude",
|
|
Model = "claude-haiku-4-5",
|
|
Alias = "Claude Haiku 4.5",
|
|
Scores = new Dictionary<string, double>
|
|
{
|
|
["coding"] = 0.65,
|
|
["translation"] = 0.7,
|
|
["analysis"] = 0.65,
|
|
["creative"] = 0.6,
|
|
["document"] = 0.6,
|
|
["math"] = 0.6
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}
|