-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathAnthropicProvider.cs
More file actions
130 lines (113 loc) · 4.46 KB
/
Copy pathAnthropicProvider.cs
File metadata and controls
130 lines (113 loc) · 4.46 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
129
130
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("Starting 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);
}
logger.LogInformation("Started Anthropic SDK stream for request {RequestId}.", request.Id);
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);
}
}