103 lines
5.6 KiB
C#
103 lines
5.6 KiB
C#
using System.Text.Json;
|
|
using Microsoft.Extensions.Logging;
|
|
using Volcanic.AgenticCore.Abstractions.Extensions;
|
|
using Volcanic.AgenticCore.Abstractions.Extensions.Events;
|
|
using Volcanic.AgenticCore.Abstractions.Messages;
|
|
using Volcanic.AgenticCore.Abstractions.Providers;
|
|
using Volcanic.AgenticCore.Abstractions.Tools;
|
|
using Volcanic.AgenticCore.Extensions.Anthropic;
|
|
using Volcanic.AgenticCore.Features.Models;
|
|
|
|
namespace Volcanic.AgenticCore.Extensions.Tests.Anthropic;
|
|
|
|
public class AnthropicExtensionTests
|
|
{
|
|
[Fact]
|
|
public async Task Activate_registers_the_anthropic_provider_with_the_default_catalogue()
|
|
{
|
|
var api = new FakeExtensionApi();
|
|
var extension = new AnthropicProviderExtension();
|
|
|
|
Assert.Equal("anthropic-provider", extension.Name);
|
|
await extension.ActivateAsync(api, CancellationToken.None);
|
|
|
|
var registration = Assert.Single(api.Providers);
|
|
Assert.Equal("anthropic", registration.Id);
|
|
Assert.Equal("Anthropic", registration.Name);
|
|
Assert.Equal("anthropic-messages", registration.Api);
|
|
Assert.Equal("https://api.anthropic.com", registration.BaseUrl);
|
|
Assert.Equal("$ANTHROPIC_API_KEY", registration.ApiKey);
|
|
Assert.True(registration.RequiresApiKey);
|
|
Assert.NotNull(registration.Stream);
|
|
Assert.Equal(["claude-opus-5", "claude-sonnet-5", "claude-fable-5-1", "claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"], registration.Models.Select(m => m.Id).ToArray());
|
|
Assert.All(registration.Models, m =>
|
|
{
|
|
Assert.Equal("anthropic", m.Provider);
|
|
Assert.Equal("anthropic-messages", m.Api);
|
|
Assert.True(m.Reasoning);
|
|
Assert.True(m.SupportsImages);
|
|
Assert.True(m.ContextWindow >= 200_000);
|
|
Assert.NotNull(m.ThinkingLevelMap![ThinkingLevel.Medium]);
|
|
Assert.NotNull(m.ThinkingLevelMap[ThinkingLevel.High]);
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Activate_honours_custom_models_and_api_key_spec()
|
|
{
|
|
var api = new FakeExtensionApi();
|
|
var custom = AnthropicModels.Haiku45 with { Id = "claude-custom", Name = "Custom" };
|
|
var extension = new AnthropicProviderExtension(models: [custom], apiKeySpec: "!op read secret");
|
|
|
|
await extension.ActivateAsync(api, CancellationToken.None);
|
|
|
|
var registration = Assert.Single(api.Providers);
|
|
Assert.Equal("!op read secret", registration.ApiKey);
|
|
Assert.Equal("claude-custom", Assert.Single(registration.Models).Id);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Registration_resolves_through_the_model_registry()
|
|
{
|
|
var registry = new ModelRegistry(new ApiKeyResolver(null, _ => null));
|
|
var api = new FakeExtensionApi();
|
|
await new AnthropicProviderExtension().ActivateAsync(api, CancellationToken.None);
|
|
registry.Register(api.Providers[0]);
|
|
|
|
var model = registry.Find("anthropic/claude-opus-5");
|
|
Assert.NotNull(model);
|
|
Assert.Equal("Claude Opus 5", model!.Name);
|
|
Assert.Equal("anthropic-messages", model.Api);
|
|
Assert.Equal("https://api.anthropic.com", model.BaseUrl);
|
|
Assert.Equal(6, registry.ListModels().Count);
|
|
}
|
|
|
|
/// <summary>Only <see cref="RegisterProvider"/> is supported; everything else throws.</summary>
|
|
private sealed class FakeExtensionApi : IExtensionApi
|
|
{
|
|
public List<ProviderRegistration> Providers { get; } = [];
|
|
|
|
public string ExtensionName => "test";
|
|
public ILogger Logger => throw new NotSupportedException();
|
|
public IEventBus Events => throw new NotSupportedException();
|
|
public IDisposable On<TEvent>(ExtensionHandler<TEvent> handler) where TEvent : ExtensionEvent => throw new NotSupportedException();
|
|
public void RegisterTool(ITool tool) => throw new NotSupportedException();
|
|
public void RegisterCommand(string name, CommandDefinition command) => throw new NotSupportedException();
|
|
public void RegisterProvider(ProviderRegistration provider) => Providers.Add(provider);
|
|
public void UnregisterProvider(string providerId) => throw new NotSupportedException();
|
|
public void SendMessage(CustomMessage message, MessageDelivery delivery = MessageDelivery.Steer, bool triggerTurn = true) => throw new NotSupportedException();
|
|
public void SendUserMessage(string text, IReadOnlyList<ImageContent>? images = null, MessageDelivery delivery = MessageDelivery.Steer) => throw new NotSupportedException();
|
|
public void AppendEntry(string customType, JsonElement? data = null) => throw new NotSupportedException();
|
|
public Task<bool> SetModelAsync(Model model, CancellationToken cancellationToken = default) => throw new NotSupportedException();
|
|
public ThinkingLevel GetThinkingLevel() => throw new NotSupportedException();
|
|
public void SetThinkingLevel(ThinkingLevel level) => throw new NotSupportedException();
|
|
public IReadOnlyList<string> GetActiveTools() => throw new NotSupportedException();
|
|
public void SetActiveTools(IEnumerable<string> names) => throw new NotSupportedException();
|
|
public IReadOnlyList<ToolInfo> GetAllTools() => throw new NotSupportedException();
|
|
public IReadOnlyList<CommandInfo> GetCommands() => throw new NotSupportedException();
|
|
public string? GetSessionName() => throw new NotSupportedException();
|
|
public void SetSessionName(string? name) => throw new NotSupportedException();
|
|
public Task<ExecResult> ExecAsync(string command, IReadOnlyList<string> args, ExecOptions? options = null, CancellationToken cancellationToken = default) => throw new NotSupportedException();
|
|
}
|
|
}
|