-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathAnthropicProvider.cs
More file actions
128 lines (112 loc) · 4.36 KB
/
Copy pathAnthropicProvider.cs
File metadata and controls
128 lines (112 loc) · 4.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
using Anthropic;
using Anthropic.Core;
using Anthropic.Models.Messages;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SharpClaw.Code.Infrastructure.Abstractions;
using SharpClaw.Code.Providers.Abstractions;
using SharpClaw.Code.Providers.Configuration;
using SharpClaw.Code.Providers.Internal;
using SharpClaw.Code.Providers.Models;
using SharpClaw.Code.Protocol.Models;
namespace SharpClaw.Code.Providers;
/// <summary>
/// Streams responses from Anthropic's Messages API using the official Anthropic C# SDK.
/// </summary>
public sealed class AnthropicProvider(
IOptions<AnthropicProviderOptions> options,
ISystemClock systemClock,
ILogger<AnthropicProvider> logger) : IModelProvider
{
private readonly AnthropicProviderOptions _options = options.Value;
/// <inheritdoc />
public string ProviderName => _options.ProviderName;
/// <inheritdoc />
public Task<AuthStatus> GetAuthStatusAsync(CancellationToken cancellationToken)
=> Task.FromResult(Internal.ProviderAuthStatusFactory.FromConfiguration(
ProviderName,
_options.ApiKey,
ProviderAuthMode.ApiKey,
hasAuthOptionalRuntime: false));
/// <inheritdoc />
public Task<ProviderStreamHandle> StartStreamAsync(ProviderRequest request, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var client = CreateClient();
var modelId = Internal.ProviderHttpHelpers.ResolveModelOrDefault(request.Model, _options.DefaultModel);
var systemPrompt = string.IsNullOrWhiteSpace(request.SystemPrompt) ? null : request.SystemPrompt;
float? temperature = request.Temperature.HasValue ? (float)request.Temperature.Value : null;
MessageCreateParams parameters;
if (request.Messages is not null)
{
var messages = Internal.AnthropicMessageBuilder.BuildMessages(request.Messages);
parameters = new MessageCreateParams
{
MaxTokens = request.MaxTokens ?? 1024,
Model = modelId,
Messages = messages,
Temperature = temperature,
};
if (request.Tools is { Count: > 0 } tools)
{
var anthropicTools = Internal.AnthropicMessageBuilder.BuildTools(tools);
parameters = parameters with { Tools = anthropicTools };
}
}
else
{
parameters = new MessageCreateParams
{
MaxTokens = request.MaxTokens ?? 1024,
Model = modelId,
Messages =
[
new MessageParam
{
Role = Role.User,
Content = request.Prompt,
},
],
Temperature = temperature,
};
}
if (systemPrompt is not null)
{
parameters = parameters with { System = systemPrompt };
}
logger.LogInformation("Started Anthropic SDK stream for request {RequestId}.", request.Id);
IAsyncEnumerable<RawMessageStreamEvent> stream;
try
{
stream = client.Messages.CreateStreaming(parameters, cancellationToken);
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception exception) when (ProviderStreamFailureClassifier.IsAuthenticationFailure(exception))
{
throw new ProviderExecutionException(
ProviderName,
modelId,
ProviderFailureKind.AuthenticationUnavailable,
$"Provider '{ProviderName}' authentication failed while starting the stream.",
exception);
}
return Task.FromResult(new ProviderStreamHandle(request, AnthropicSdkStreamAdapter.AdaptAsync(stream, request.Id, systemClock, cancellationToken)));
}
private AnthropicClient CreateClient()
{
var apiKey = _options.ApiKey ?? string.Empty;
var clientOptions = new ClientOptions
{
ApiKey = apiKey,
};
var normalized = Internal.ProviderHttpHelpers.NormalizeBaseUrl(_options.BaseUrl);
if (normalized is not null)
{
clientOptions.BaseUrl = normalized;
}
return new AnthropicClient(clientOptions);
}
}