Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Cellm/AddIn/CellmAddIn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Cellm.Models.Behaviors;
using Cellm.Models.Providers;
using Cellm.Models.Providers.Anthropic;
using Cellm.Models.Providers.Azure;
using Cellm.Models.Providers.Cellm;
using Cellm.Models.Providers.DeepSeek;
using Cellm.Models.Providers.Google;
Expand Down Expand Up @@ -68,6 +69,7 @@ private static ServiceCollection ConfigureServices(ServiceCollection services)
services
.Configure<AccountConfiguration>(configuration.GetRequiredSection(nameof(AccountConfiguration)))
.Configure<AnthropicConfiguration>(configuration.GetRequiredSection(nameof(AnthropicConfiguration)))
.Configure<AzureConfiguration>(configuration.GetRequiredSection(nameof(AzureConfiguration)))
.Configure<CellmConfiguration>(configuration.GetRequiredSection(nameof(CellmConfiguration)))
.Configure<GeminiConfiguration>(configuration.GetRequiredSection(nameof(GeminiConfiguration)))
.Configure<DeepSeekConfiguration>(configuration.GetRequiredSection(nameof(DeepSeekConfiguration)))
Expand Down Expand Up @@ -133,6 +135,7 @@ private static ServiceCollection ConfigureServices(ServiceCollection services)
services
.AddSingleton<IChatClientFactory, ChatClientFactory>()
.AddAnthropicChatClient()
.AddAzureChatClient()
.AddCellmChatClient()
.AddDeepSeekChatClient()
.AddGeminiChatClient()
Expand Down
Binary file added src/Cellm/AddIn/UserInterface/Resources/azure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
76 changes: 14 additions & 62 deletions src/Cellm/AddIn/UserInterface/Ribbon/RibbonModelGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Cellm.Models.Providers.OpenAiCompatible;
using Cellm.Users;
using ExcelDna.Integration.CustomUI;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
Expand Down Expand Up @@ -57,12 +58,13 @@ private class ProviderItem
private readonly Dictionary<int, ProviderItem> _providerItems = new()
{
[0] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.Anthropic)}", Image = $"{ResourcesBasePath}/anthropic.png", Label = nameof(Provider.Anthropic), Entitlement = Entitlement.EnableAnthropicProvider },
[1] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.Gemini)}", Image = $"{ResourcesBasePath}/google.png", Label = nameof(Provider.Gemini), Entitlement = Entitlement.EnableGeminiProvider },
[2] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.DeepSeek)}", Image = $"{ResourcesBasePath}/deepseek.png", Label = nameof(Provider.DeepSeek), Entitlement = Entitlement.EnableDeepSeekProvider },
[3] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.Mistral)}", Image = $"{ResourcesBasePath}/mistral.png", Label = nameof(Provider.Mistral), Entitlement = Entitlement.EnableMistralProvider },
[4] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.Ollama)}", Image = $"{ResourcesBasePath}/ollama.png", Label = nameof(Provider.Ollama), Entitlement = Entitlement.EnableOllamaProvider },
[5] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.OpenAi)}", Image = $"{ResourcesBasePath}/openai.png", Label = nameof(Provider.OpenAi), Entitlement = Entitlement.EnableOpenAiProvider },
[6] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.OpenAiCompatible)}", Image = $"{ResourcesBasePath}/openai.png", Label = nameof(Provider.OpenAiCompatible) }
[1] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.Azure)}", Image = $"{ResourcesBasePath}/azure.png", Label = nameof(Provider.Azure), Entitlement = Entitlement.EnableAzureProvider },
[2] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.Gemini)}", Image = $"{ResourcesBasePath}/google.png", Label = nameof(Provider.Gemini), Entitlement = Entitlement.EnableGeminiProvider },
[3] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.DeepSeek)}", Image = $"{ResourcesBasePath}/deepseek.png", Label = nameof(Provider.DeepSeek), Entitlement = Entitlement.EnableDeepSeekProvider },
[4] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.Mistral)}", Image = $"{ResourcesBasePath}/mistral.png", Label = nameof(Provider.Mistral), Entitlement = Entitlement.EnableMistralProvider },
[5] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.Ollama)}", Image = $"{ResourcesBasePath}/ollama.png", Label = nameof(Provider.Ollama), Entitlement = Entitlement.EnableOllamaProvider },
[6] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.OpenAi)}", Image = $"{ResourcesBasePath}/openai.png", Label = nameof(Provider.OpenAi), Entitlement = Entitlement.EnableOpenAiProvider },
[7] = new ProviderItem { Id = $"{nameof(Provider)}.{nameof(Provider.OpenAiCompatible)}", Image = $"{ResourcesBasePath}/openai.png", Label = nameof(Provider.OpenAiCompatible) }
};

internal int _selectedProviderIndex = 3; // Default to Ollama
Expand Down Expand Up @@ -276,7 +278,7 @@ public string GetSelectedProviderLabel(IRibbonControl control)
}
catch (KeyNotFoundException)
{
currentModel = string.Empty; // Explicitly null
currentModel = string.Empty;
}
catch (Exception ex)
{
Expand Down Expand Up @@ -416,67 +418,17 @@ private List<string> GetAvailableModelNamesForProvider(Provider provider)
if (!string.IsNullOrEmpty(big) && !big.StartsWith("No ")) modelNames.Add(big);
if (!string.IsNullOrEmpty(thinking) && !thinking.StartsWith("No ")) modelNames.Add(thinking);

// Remove duplicates if Small/Big/Thinking are configured to the same model
// Remove duplicates
return modelNames.Distinct().ToList();
}

private string GetModelNameForProvider(Provider provider, string modelType)
{
try
{
return provider switch
{
Provider.Cellm => modelType switch
{
"SmallModel" => GetProviderConfiguration<CellmConfiguration>()?.SmallModel ?? "No small model",
"MediumModel" => GetProviderConfiguration<CellmConfiguration>()?.MediumModel ?? "No big model",
"LargeModel" => GetProviderConfiguration<CellmConfiguration>()?.LargeModel ?? "No thinking model",
_ => "N/A"
},
Provider.Anthropic => modelType switch
{
"SmallModel" => GetProviderConfiguration<AnthropicConfiguration>()?.SmallModel ?? "No small model",
"MediumModel" => GetProviderConfiguration<AnthropicConfiguration>()?.MediumModel ?? "No big model",
"LargeModel" => GetProviderConfiguration<AnthropicConfiguration>()?.LargeModel ?? "No thinking model",
_ => "N/A"
},
Provider.DeepSeek => modelType switch
{
"SmallModel" => GetProviderConfiguration<DeepSeekConfiguration>()?.SmallModel ?? "No small model",
"MediumModel" => GetProviderConfiguration<DeepSeekConfiguration>()?.MediumModel ?? "No big model",
"LargeModel" => GetProviderConfiguration<DeepSeekConfiguration>()?.LargeModel ?? "No thinking model",
_ => "N/A"
},
Provider.Mistral => modelType switch
{
"SmallModel" => GetProviderConfiguration<MistralConfiguration>()?.SmallModel ?? "No small model",
"MediumModel" => GetProviderConfiguration<MistralConfiguration>()?.MediumModel ?? "No big model",
"LargeModel" => GetProviderConfiguration<MistralConfiguration>()?.LargeModel ?? "No thinking model",
_ => "N/A"
},
Provider.Ollama => modelType switch
{
"SmallModel" => GetProviderConfiguration<OllamaConfiguration>()?.SmallModel ?? "No small model",
"MediumModel" => GetProviderConfiguration<OllamaConfiguration>()?.MediumModel ?? "No big model",
"LargeModel" => GetProviderConfiguration<OllamaConfiguration>()?.LargeModel ?? "No thinking model",
_ => "N/A"
},
Provider.OpenAi => modelType switch
{
"SmallModel" => GetProviderConfiguration<OpenAiConfiguration>()?.SmallModel ?? "No small model",
"MediumModel" => GetProviderConfiguration<OpenAiConfiguration>()?.MediumModel ?? "No big model",
"LargeModel" => GetProviderConfiguration<OpenAiConfiguration>()?.LargeModel ?? "No thinking model",
_ => "N/A"
},
Provider.OpenAiCompatible => modelType switch
{
"SmallModel" => GetProviderConfiguration<OpenAiCompatibleConfiguration>()?.SmallModel ?? "No small model",
"MediumModel" => GetProviderConfiguration<OpenAiCompatibleConfiguration>()?.MediumModel ?? "No big model",
"LargeModel" => GetProviderConfiguration<OpenAiCompatibleConfiguration>()?.LargeModel ?? "No thinking model",
_ => "N/A"
},
_ => "N/A" // Default case for unhandled providers
};
var configuration = CellmAddIn.Services.GetRequiredService<IConfiguration>();
var key = $"{provider}Configuration:{modelType}";
return configuration[key] ?? string.Empty;
}
catch (Exception ex)
{
Expand Down Expand Up @@ -709,7 +661,7 @@ static internal bool IsBaseAddressEditable(Provider provider)
{
return provider switch
{
Provider.Ollama or Provider.OpenAiCompatible => true,
Provider.Azure or Provider.Gemini or Provider.Ollama or Provider.OpenAiCompatible => true,
_ => false
};
}
Expand Down
3 changes: 3 additions & 0 deletions src/Cellm/Cellm.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

<ItemGroup>
<None Remove="AddIn\UserInterface\Resources\anthropic.png" />
<None Remove="AddIn\UserInterface\Resources\azure.png" />
<None Remove="AddIn\UserInterface\Resources\deepseek.png" />
<None Remove="AddIn\UserInterface\Resources\external-link.svg" />
<None Remove="AddIn\UserInterface\Resources\google.png" />
Expand All @@ -37,6 +38,7 @@
<PackageReference Include="MediatR" Version="12.5.0" />
<PackageReference Include="Microsoft.Extensions.AI" Version="9.5.0" />
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="9.5.0" />
<PackageReference Include="Microsoft.Extensions.AI.AzureAIInference" Version="9.5.0-preview.1.25265.7" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.4.4-preview.1.25259.16" />
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.5.0" />
<PackageReference Include="Microsoft.Extensions.Compliance.Redaction" Version="9.5.0" />
Expand Down Expand Up @@ -70,6 +72,7 @@

<ItemGroup>
<EmbeddedResource Include="AddIn\UserInterface\Resources\anthropic.png" />
<EmbeddedResource Include="AddIn\UserInterface\Resources\azure.png" />
<EmbeddedResource Include="AddIn\UserInterface\Resources\deepseek.png" />
<EmbeddedResource Include="AddIn\UserInterface\Resources\external-link.svg" />
<EmbeddedResource Include="AddIn\UserInterface\Resources\google.png" />
Expand Down
10 changes: 10 additions & 0 deletions src/Cellm/Models/Providers/Azure/AzureConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Cellm.Models.Providers.Azure;

internal class AzureConfiguration
{
public Uri BaseAddress { get; set; } = default!;

public string DefaultModel { get; init; } = string.Empty;

public string ApiKey { get; init; } = string.Empty;
}
1 change: 1 addition & 0 deletions src/Cellm/Models/Providers/Provider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public enum Provider
{
Anthropic,
Azure,
Cellm,
DeepSeek,
Gemini,
Expand Down
26 changes: 25 additions & 1 deletion src/Cellm/Models/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@
using System.Net.Http.Headers;
using System.Threading.RateLimiting;
using Anthropic.SDK;
using Azure;
using Azure.AI.Inference;
using Cellm.Models.Prompts;
using Cellm.Models.Providers;
using Cellm.Models.Providers.Anthropic;
using Cellm.Models.Providers.Azure;
using Cellm.Models.Providers.Cellm;
using Cellm.Models.Providers.DeepSeek;
using Cellm.Models.Providers.Google;
Expand Down Expand Up @@ -145,7 +148,7 @@ public static IServiceCollection AddAnthropicChatClient(this IServiceCollection
.AddKeyedChatClient(Provider.Anthropic, serviceProvider =>
{
var account = serviceProvider.GetRequiredService<Account>();
account.ThrowIfNotEntitled(Entitlement.EnableAnthropicProvider);
account.ThrowIfNotEntitled(Entitlement.EnableAzureProvider);

var anthropicConfiguration = serviceProvider.GetRequiredService<IOptionsMonitor<AnthropicConfiguration>>();
var resilientHttpClient = serviceProvider.GetKeyedService<HttpClient>("ResilientHttpClient") ?? throw new NullReferenceException("ResilientHttpClient");
Expand All @@ -160,6 +163,27 @@ public static IServiceCollection AddAnthropicChatClient(this IServiceCollection
return services;
}

public static IServiceCollection AddAzureChatClient(this IServiceCollection services)
{
services
.AddKeyedChatClient(Provider.Azure, serviceProvider =>
{
var account = serviceProvider.GetRequiredService<Account>();
account.ThrowIfNotEntitled(Entitlement.EnableAzureProvider);

var azureConfiguration = serviceProvider.GetRequiredService<IOptionsMonitor<AzureConfiguration>>();
var resilientHttpClient = serviceProvider.GetKeyedService<HttpClient>("ResilientHttpClient") ?? throw new NullReferenceException("ResilientHttpClient");

return new ChatCompletionsClient(
azureConfiguration.CurrentValue.BaseAddress,
new AzureKeyCredential(azureConfiguration.CurrentValue.ApiKey))
.AsIChatClient(azureConfiguration.CurrentValue.DefaultModel);
}, ServiceLifetime.Transient)
.UseFunctionInvocation();

return services;
}

public static IServiceCollection AddCellmChatClient(this IServiceCollection services)
{
services
Expand Down
3 changes: 2 additions & 1 deletion src/Cellm/Users/Entitlement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public enum Entitlement
{
EnableAnthropicProvider,
EnableAzureProvider,
EnableCellmProvider,
EnableDeepSeekProvider,
EnableGeminiProvider,
Expand All @@ -11,6 +12,6 @@ public enum Entitlement
EnableOpenAiProvider,
EnableOpenAiCompatibleProvider,
EnableModelContextProtocol,
DisableTelemetry,
DisableTelemetry
}

1 change: 1 addition & 0 deletions src/Cellm/Users/Exceptions/PermissionDeniedException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ private static string GetMessage(Entitlement entitlement)
return entitlement switch
{
Entitlement.EnableAnthropicProvider => $"You do not have permission to use models hosted by Anthropic.",
Entitlement.EnableAzureProvider => $"You do not have permission to use models hosted by Azure.",
Entitlement.EnableCellmProvider => $"You do not have permission to use models hosted by Cellm.",
Entitlement.EnableDeepSeekProvider => $"You do not have permission to use models hosted by DeepSeek.",
Entitlement.EnableMistralProvider => $"You do not have permission to use models hosted by Mistral.",
Expand Down
4 changes: 4 additions & 0 deletions src/Cellm/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
"MediumModel": "claude-3-7-sonnet-latest",
"LargeModel": "claude-3-opus-latest"
},
"AzureConfiguration": {
"DefaultModel": "",
"ApiKey": ""
},
"CellmAddInConfiguration": {
"DefaultProvider": "Ollama",
"DefaultModel": "gemma3:4b-it-qat",
Expand Down
71 changes: 65 additions & 6 deletions src/Cellm/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@
"resolved": "9.5.0",
"contentHash": "Y46j1S4zOPtLAHCUzhHIm1IhCMr7764754bLYupyc1JZcvLNXjUk+VC7H3Bou20S2hHgDJnfox7X/H/3BYEvzA=="
},
"Microsoft.Extensions.AI.AzureAIInference": {
"type": "Direct",
"requested": "[9.5.0-preview.1.25265.7, )",
"resolved": "9.5.0-preview.1.25265.7",
"contentHash": "RSC/jDyRotlCdqokJlyIv617jxP9TNUMeD87W5RP8vaYdwr0ZE0cd2wkCs/1QQ6J93vOuNcBkyCxPxWI2na28g==",
"dependencies": {
"Azure.AI.Inference": "1.0.0-beta.4",
"Microsoft.Extensions.AI.Abstractions": "9.5.0",
"System.Memory.Data": "9.0.5",
"System.Text.Json": "9.0.5"
}
},
"Microsoft.Extensions.AI.OpenAI": {
"type": "Direct",
"requested": "[9.4.4-preview.1.25259.16, )",
Expand Down Expand Up @@ -298,6 +310,30 @@
"resolved": "9.0.5",
"contentHash": "WobqAA+zMpzxJ6EkSNkHrjltyx+s+yTM+BoBJt74aqjBDyrsLE+buHvFyp49sjAyzRZ/ywJ25T4LMDX5b+zL3Q=="
},
"Azure.AI.Inference": {
"type": "Transitive",
"resolved": "1.0.0-beta.4",
"contentHash": "HNiPXhBoCDLTq1IX9wrqXKYwy8ON+U0nSHkba7qxFRXjMKCqA9WiQIHbnoO5i6Oet2/qbDkRDWjjnmQQJKdqsw==",
"dependencies": {
"Azure.Core": "1.44.1",
"System.ClientModel": "1.4.0-beta.1"
}
},
"Azure.Core": {
"type": "Transitive",
"resolved": "1.44.1",
"contentHash": "YyznXLQZCregzHvioip07/BkzjuWNXogJEVz9T5W6TwjNr17ax41YGzYMptlo2G10oLCuVPoyva62y0SIRDixg==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "6.0.0",
"System.ClientModel": "1.1.0",
"System.Diagnostics.DiagnosticSource": "6.0.1",
"System.Memory.Data": "6.0.0",
"System.Numerics.Vectors": "4.5.0",
"System.Text.Encodings.Web": "6.0.0",
"System.Text.Json": "6.0.10",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"ExcelDna.Integration": {
"type": "Transitive",
"resolved": "1.9.0-beta2",
Expand Down Expand Up @@ -589,11 +625,11 @@
},
"System.ClientModel": {
"type": "Transitive",
"resolved": "1.2.1",
"contentHash": "s9+M5El+DXdCRRLzxak8uGBKWT8H/eIssGpFtpaMKdJULrQbBDPH/zFbVyHX+NDczhS5EvjHFbBH9/L+0UhmcA==",
"resolved": "1.4.0-beta.1",
"contentHash": "ZR0fKC94VS4P80vmxjk7l13/jPBXV0GMoE4jQfkYk8m2YV+dlw8jSC+b6eAfyBz0u+soN4CjhT3OdOC5KHaXxg==",
"dependencies": {
"System.Memory.Data": "6.0.0",
"System.Text.Json": "6.0.10"
"Microsoft.Extensions.Logging.Abstractions": "6.0.0",
"System.Memory.Data": "6.0.1"
}
},
"System.Diagnostics.DiagnosticSource": {
Expand All @@ -616,14 +652,32 @@
},
"System.Memory.Data": {
"type": "Transitive",
"resolved": "9.0.4",
"contentHash": "m9v4fYRP2cvatcM/WJUpqyfmQc9c1aFhDrOcJs7+nmuAG96kk9jXipDTBsS6fUYdDcy2lR4Q4QNDanSefHaUrA=="
"resolved": "9.0.5",
"contentHash": "qK9dBegf3wdw2+mqeIXmTJjlMq4sn39wUN+JSagMuCmp/05ZyQLYIZsxlCopChxcgRwqu/+tHXKRaKtzHYlITw=="
},
"System.Net.ServerSentEvents": {
"type": "Transitive",
"resolved": "10.0.0-preview.2.25163.2",
"contentHash": "XHyvtQSgco0Sv0kz9yNBv93k3QOoAVzIVd5XbQoTqjV9sqkzWHsToNknyxtNjcXQwb+O9TfzSlNobsBWwnKD3Q=="
},
"System.Numerics.Vectors": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Text.Json": {
"type": "Transitive",
"resolved": "9.0.5",
Expand All @@ -638,6 +692,11 @@
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q=="
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.5.4",
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg=="
}
}
}
Expand Down
Loading