using System.ClientModel; using System.Diagnostics.CodeAnalysis; using DysonNetwork.Insight.Thought.Plugins; using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Registry; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.Ollama; using Microsoft.SemanticKernel.Connectors.OpenAI; using OpenAI; using Microsoft.SemanticKernel.Plugins.Web; using Microsoft.SemanticKernel.Plugins.Web.Bing; using Microsoft.SemanticKernel.Plugins.Web.Google; namespace DysonNetwork.Insight.Thought; public class ThoughtServiceModel { public string ServiceId { get; set; } = null!; public string? Provider { get; set; } public string? Model { get; set; } public double BillingMultiplier { get; set; } public int PerkLevel { get; set; } } public class ThoughtProvider { private readonly PostService.PostServiceClient _postClient; private readonly AccountService.AccountServiceClient _accountClient; private readonly IConfiguration _configuration; private readonly ILogger _logger; private readonly Dictionary _kernels = new(); private readonly Dictionary _serviceProviders = new(); private readonly Dictionary _serviceModels = new(); private readonly string _defaultServiceId; [Experimental("SKEXP0050")] public ThoughtProvider( IConfiguration configuration, PostService.PostServiceClient postServiceClient, AccountService.AccountServiceClient accountServiceClient, ILogger logger ) { _logger = logger; _postClient = postServiceClient; _accountClient = accountServiceClient; _configuration = configuration; var cfg = configuration.GetSection("Thinking"); _defaultServiceId = cfg.GetValue("DefaultService")!; var services = cfg.GetSection("Services").GetChildren(); foreach (var service in services) { var serviceId = service.Key; var serviceModel = new ThoughtServiceModel { ServiceId = serviceId, Provider = service.GetValue("Provider"), Model = service.GetValue("Model"), BillingMultiplier = service.GetValue("BillingMultiplier", 1.0), PerkLevel = service.GetValue("PerkLevel", 0) }; _serviceModels[serviceId] = serviceModel; var providerType = service.GetValue("Provider")?.ToLower(); if (providerType is null) continue; var kernel = InitializeThinkingService(service); InitializeHelperFunctions(kernel); _kernels[serviceId] = kernel; _serviceProviders[serviceId] = providerType; } } private Kernel InitializeThinkingService(IConfigurationSection serviceConfig) { var providerType = serviceConfig.GetValue("Provider")?.ToLower(); var model = serviceConfig.GetValue("Model"); var endpoint = serviceConfig.GetValue("Endpoint"); var apiKey = serviceConfig.GetValue("ApiKey"); var builder = Kernel.CreateBuilder(); switch (providerType) { case "ollama": builder.AddOllamaChatCompletion( model!, new Uri(endpoint ?? "http://localhost:11434/api") ); break; case "deepseek": var client = new OpenAIClient( new ApiKeyCredential(apiKey!), new OpenAIClientOptions { Endpoint = new Uri(endpoint ?? "https://api.deepseek.com/v1") } ); builder.AddOpenAIChatCompletion(model!, client); break; default: throw new IndexOutOfRangeException("Unknown thinking provider: " + providerType); } // Add gRPC clients for Thought Plugins builder.Services.AddServiceDiscoveryCore(); builder.Services.AddServiceDiscovery(); builder.Services.AddAccountService(); builder.Services.AddSphereService(); builder.Plugins.AddFromObject(new SnAccountKernelPlugin(_accountClient)); builder.Plugins.AddFromObject(new SnPostKernelPlugin(_postClient)); return builder.Build(); } [Experimental("SKEXP0050")] private void InitializeHelperFunctions(Kernel kernel) { // Add web search plugins if configured var bingApiKey = _configuration.GetValue("Thinking:BingApiKey"); if (!string.IsNullOrEmpty(bingApiKey)) { var bingConnector = new BingConnector(bingApiKey); var bing = new WebSearchEnginePlugin(bingConnector); kernel.ImportPluginFromObject(bing, "bing"); } var googleApiKey = _configuration.GetValue("Thinking:GoogleApiKey"); var googleCx = _configuration.GetValue("Thinking:GoogleCx"); if (!string.IsNullOrEmpty(googleApiKey) && !string.IsNullOrEmpty(googleCx)) { var googleConnector = new GoogleConnector( apiKey: googleApiKey, searchEngineId: googleCx); var google = new WebSearchEnginePlugin(googleConnector); kernel.ImportPluginFromObject(google, "google"); } } public Kernel? GetKernel(string? serviceId = null) { serviceId ??= _defaultServiceId; return _kernels.GetValueOrDefault(serviceId); } public string GetServiceId(string? serviceId = null) { return serviceId ?? _defaultServiceId; } public IEnumerable GetAvailableServices() { return _kernels.Keys; } public IEnumerable GetAvailableServicesInfo() { return _serviceModels.Values; } public ThoughtServiceModel? GetServiceInfo(string? serviceId) { serviceId ??= _defaultServiceId; return _serviceModels.GetValueOrDefault(serviceId); } public string GetDefaultServiceId() { return _defaultServiceId; } public PromptExecutionSettings CreatePromptExecutionSettings(string? serviceId = null) { serviceId ??= _defaultServiceId; var providerType = _serviceProviders.GetValueOrDefault(serviceId); return providerType switch { "ollama" => new OllamaPromptExecutionSettings { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(autoInvoke: false) }, "deepseek" => new OpenAIPromptExecutionSettings { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(autoInvoke: false), ModelId = serviceId }, _ => throw new InvalidOperationException("Unknown provider for service: " + serviceId) }; } }