Skip to content

Commit e66d1db

Browse files
authored
Update to stable Microsoft.Extensions.AI (#146)
1 parent 0670dda commit e66d1db

File tree

2 files changed

+153
-94
lines changed

2 files changed

+153
-94
lines changed

src/libs/Anthropic/Anthropic.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<ItemGroup>
1919
<PackageReference Include="CSharpToJsonSchema" Version="3.10.1" />
2020
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.2" />
21-
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="9.5.0-preview.1.25262.9" />
21+
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="9.5.0" />
2222
<PackageReference Include="PolySharp" Version="1.15.0">
2323
<PrivateAssets>all</PrivateAssets>
2424
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

src/libs/Anthropic/Extensions/AnthropicClient.ChatClient.cs

Lines changed: 152 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ async Task<ChatResponse> IChatClient.GetResponseAsync(
2525
{
2626
CreateMessageParams request = CreateRequest(messages, options);
2727

28-
var response = await this.Messages.MessagesPostAsync(request, anthropicVersion: "2023-06-01", cancellationToken).ConfigureAwait(false);
28+
var response = await this.Messages.MessagesPostAsync(request, cancellationToken: cancellationToken).ConfigureAwait(false);
2929

3030
ChatMessage responseMessage = new()
3131
{
32-
Role = response.Role == MessageRole.Assistant ? ChatRole.Assistant : ChatRole.User,
32+
MessageId = response.Id,
33+
Role = ChatRole.Assistant,
3334
RawRepresentation = response,
3435
};
3536

@@ -38,47 +39,37 @@ async Task<ChatResponse> IChatClient.GetResponseAsync(
3839
(responseMessage.AdditionalProperties ??= [])[nameof(response.StopSequence)] = response.StopSequence;
3940
}
4041

41-
// if (response.Content.Value1 is string stringContents)
42-
// {
43-
// responseMessage.Contents.Add(new TextContent(stringContents));
44-
// }
45-
//else if (response.Content.Value2 is IList<Block> blocks)
42+
foreach (var block in response.Content)
4643
{
47-
foreach (var block in response.Content)
44+
if (block.IsText)
4845
{
49-
if (block.IsText)
46+
responseMessage.Contents.Add(new TextContent(block.Text!.Text) { RawRepresentation = block });
47+
}
48+
else if (block.IsThinking)
49+
{
50+
responseMessage.Contents.Add(new TextReasoningContent(block.Thinking!.Thinking)
5051
{
51-
responseMessage.Contents.Add(new TextContent(block.Text!.Text) { RawRepresentation = block.Text });
52-
}
53-
// else if (block.IsImage)
54-
// {
55-
// responseMessage.Contents.Add(new ImageContent(
56-
// block.Image!.Source.Data,
57-
// block.Image!.Source.MediaType switch
58-
// {
59-
// ImageBlockSourceMediaType.ImagePng => "image/png",
60-
// ImageBlockSourceMediaType.ImageGif => "image/gif",
61-
// ImageBlockSourceMediaType.ImageWebp => "image/webp",
62-
// _ => "image/jpeg",
63-
// })
64-
// {
65-
// RawRepresentation = block.Image
66-
// });
67-
// }
68-
else if (block.IsToolUse)
52+
AdditionalProperties = new() { [nameof(RequestThinkingBlock.Signature)] = block.Thinking.Signature },
53+
RawRepresentation = block,
54+
});
55+
}
56+
else if (block.IsToolUse)
57+
{
58+
responseMessage.Contents.Add(new FunctionCallContent(
59+
block.ToolUse!.Id,
60+
block.ToolUse!.Name,
61+
block.ToolUse!.Input is JsonElement e ? (IDictionary<string, object?>?)e.Deserialize(
62+
JsonSerializerContext.Options.GetTypeInfo(typeof(Dictionary<string, object?>))) :
63+
null)
6964
{
70-
responseMessage.Contents.Add(new FunctionCallContent(
71-
block.ToolUse!.Id,
72-
block.ToolUse!.Name,
73-
block.ToolUse!.Input is JsonElement e ? (IDictionary<string, object?>?)e.Deserialize(
74-
JsonSerializerContext.Options.GetTypeInfo(typeof(Dictionary<string, object?>))) :
75-
null));
76-
}
65+
RawRepresentation = block
66+
});
7767
}
7868
}
7969

8070
ChatResponse completion = new(responseMessage)
8171
{
72+
RawRepresentation = response,
8273
ResponseId = response.Id,
8374
ModelId = response.Model,
8475
FinishReason = response.StopReason switch
@@ -99,15 +90,15 @@ async Task<ChatResponse> IChatClient.GetResponseAsync(
9990
TotalTokenCount = u.InputTokens + u.OutputTokens,
10091
};
10192

102-
// if (u.CacheCreationInputTokens is not null)
103-
// {
104-
// (completion.Usage.AdditionalProperties ??= [])[nameof(u.CacheCreationInputTokens)] = u.CacheCreationInputTokens;
105-
// }
106-
//
107-
// if (u.CacheReadInputTokens is not null)
108-
// {
109-
// (completion.Usage.AdditionalProperties ??= [])[nameof(u.CacheReadInputTokens)] = u.CacheReadInputTokens;
110-
// }
93+
if (u.CacheCreationInputTokens is int cacheCreationTokens)
94+
{
95+
(completion.Usage.AdditionalCounts ??= [])[nameof(u.CacheCreationInputTokens)] = cacheCreationTokens;
96+
}
97+
98+
if (u.CacheReadInputTokens is int cacheReadTokens)
99+
{
100+
(completion.Usage.AdditionalCounts ??= [])[nameof(u.CacheReadInputTokens)] = cacheReadTokens;
101+
}
111102
}
112103

113104
return completion;
@@ -119,39 +110,26 @@ async IAsyncEnumerable<ChatResponseUpdate> IChatClient.GetStreamingResponseAsync
119110
CreateMessageParams request = CreateRequest(messages, options);
120111

121112
IAsyncEnumerable<MessageStreamEvent> enumerable =
122-
CreateMessageAsStreamAsync(request, anthropicVersion: "2023-06-01", cancellationToken);
113+
CreateMessageAsStreamAsync(request, cancellationToken: cancellationToken);
123114

124115
await foreach (var response in enumerable.ConfigureAwait(false))
125116
{
126-
var chatResponseUpdate = new ChatResponseUpdate();
117+
ChatResponseUpdate chatResponseUpdate = new();
118+
127119
if (response.IsContentBlockDelta)
128120
{
129121
var delta = response.ContentBlockDelta!.Delta;
130122
if (delta.IsTextDelta)
131123
{
132-
chatResponseUpdate.Contents.Add(new TextContent(delta.TextDelta!.Text) { RawRepresentation = delta.TextDelta!.Text });
124+
chatResponseUpdate.Contents.Add(new TextContent(delta.TextDelta!.Text) { RawRepresentation = response });
133125
}
134126
}
135127

136128
yield return chatResponseUpdate;
137-
// yield return new ChatResponseUpdate
138-
// {
139-
// //AdditionalProperties = response.AdditionalProperties,
140-
// //AuthorName = choice.AuthorName,
141-
// ChatThreadId = response.ChatThreadId,
142-
// ChoiceIndex = i,
143-
// ResponseId = response.ResponseId,
144-
// Contents = choice.Contents,
145-
// CreatedAt = response.CreatedAt,
146-
// FinishReason = response.FinishReason,
147-
// ModelId = response.ModelId,
148-
// RawRepresentation = choice.RawRepresentation,
149-
// Role = choice.Role,
150-
// };
151129
}
152130
}
153131

154-
private static CreateMessageParams CreateRequest(IEnumerable<ChatMessage> chatMessages, ChatOptions? options)
132+
private CreateMessageParams CreateRequest(IEnumerable<ChatMessage> chatMessages, ChatOptions? options)
155133
{
156134
string? systemMessage = null;
157135

@@ -177,21 +155,60 @@ private static CreateMessageParams CreateRequest(IEnumerable<ChatMessage> chatMe
177155
blocks.Add(new InputContentBlock(new RequestTextBlock { Text = tc.Text }));
178156
break;
179157

180-
case DataContent ic when ic.HasTopLevelMediaType("image"):
158+
case TextReasoningContent trc:
159+
blocks.Add(new InputContentBlock(new RequestThinkingBlock
160+
{
161+
Thinking = trc.Text,
162+
Signature = trc.AdditionalProperties?.TryGetValue(nameof(RequestThinkingBlock.Signature), out string? sig) is true ? sig : "",
163+
}));
164+
break;
165+
166+
case DataContent dc when dc.HasTopLevelMediaType("image"):
181167
blocks.Add(new InputContentBlock(new RequestImageBlock
182168
{
183169
Source = new Base64ImageSource
184170
{
185-
MediaType = ic.MediaType switch
171+
MediaType = dc.MediaType switch
186172
{
187173
"image/png" => Base64ImageSourceMediaType.ImagePng,
188174
"image/gif" => Base64ImageSourceMediaType.ImageGif,
189175
"image/webp" => Base64ImageSourceMediaType.ImageWebp,
190176
_ => Base64ImageSourceMediaType.ImageJpeg,
191177
},
192-
Data = ic.Data.ToArray() ?? [], //Convert.ToBase64String(ic.Data?.ToArray() ?? []),
178+
Data = dc.Data.ToArray() ?? [],
193179
Type = Base64ImageSourceType.Base64,
194-
}
180+
},
181+
}));
182+
break;
183+
184+
case DataContent dc when dc.MediaType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase):
185+
blocks.Add(new InputContentBlock(new RequestDocumentBlock
186+
{
187+
Source = new Base64PDFSource
188+
{
189+
MediaType = Base64PDFSourceMediaType.ApplicationPdf,
190+
Data = dc.Data.ToArray() ?? [],
191+
},
192+
}));
193+
break;
194+
195+
case UriContent uc when uc.HasTopLevelMediaType("image"):
196+
blocks.Add(new InputContentBlock(new RequestImageBlock
197+
{
198+
Source = new URLImageSource
199+
{
200+
Url = uc.Uri.ToString(),
201+
},
202+
}));
203+
break;
204+
205+
case UriContent uc when uc.MediaType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase):
206+
blocks.Add(new InputContentBlock(new RequestDocumentBlock
207+
{
208+
Source = new URLPDFSource
209+
{
210+
Url = uc.Uri.ToString(),
211+
},
195212
}));
196213
break;
197214

@@ -225,36 +242,78 @@ private static CreateMessageParams CreateRequest(IEnumerable<ChatMessage> chatMe
225242
}
226243
}
227244

228-
var request = new CreateMessageParams
245+
CreateMessageParams? request = options?.RawRepresentationFactory?.Invoke(this) as CreateMessageParams;
246+
if (request is not null)
229247
{
230-
MaxTokens = options?.MaxOutputTokens ?? 250,
231-
Messages = messages,
232-
Model = options?.ModelId ?? string.Empty,
233-
StopSequences = options?.StopSequences,
234-
System = systemMessage is not null ? new(systemMessage) : null,
235-
Temperature = options?.Temperature,
236-
TopP = options?.TopP,
237-
TopK = options?.TopK,
238-
ToolChoice =
239-
options?.Tools is not { Count: > 0 } ? null:
240-
options?.ToolMode is AutoChatToolMode ? new ToolChoice(new ToolChoiceAuto()) :
241-
options?.ToolMode is RequiredChatToolMode r
242-
? r.RequiredFunctionName is not null
243-
? new ToolChoice(new ToolChoiceTool
244-
{
245-
Name = r.RequiredFunctionName,
246-
})
247-
: new ToolChoice(new ToolChoiceAny())
248-
: (ToolChoice?)null,
249-
Tools = options?.Tools is IList<AITool> tools ?
250-
tools.OfType<AIFunction>().Select(f => new global::Anthropic.OneOf<global::Anthropic.Tool, global::Anthropic.BashTool20250124, global::Anthropic.TextEditor20250124, global::Anthropic.WebSearchTool20250305>(new Tool
248+
request.Model = options?.ModelId ?? string.Empty;
249+
if (request.Messages is null)
250+
{
251+
request.Messages = messages;
252+
}
253+
else
254+
{
255+
foreach (var message in messages)
251256
{
252-
Name = f.Name,
253-
Description = f.Description,
254-
InputSchema = CreateSchema(f),
255-
})).ToList() :
256-
null,
257-
};
257+
request.Messages.Add(message);
258+
}
259+
}
260+
}
261+
else
262+
{
263+
request = new()
264+
{
265+
Model = options?.ModelId ?? string.Empty,
266+
MaxTokens = options?.MaxOutputTokens ?? 250,
267+
Messages = messages,
268+
};
269+
}
270+
271+
request.StopSequences ??= options?.StopSequences;
272+
if (systemMessage is not null)
273+
{
274+
request.System ??= new(systemMessage);
275+
}
276+
request.Temperature ??= options?.Temperature;
277+
request.TopP ??= options?.TopP;
278+
request.TopK ??= options?.TopK;
279+
280+
request.ToolChoice ??=
281+
options?.Tools is not { Count: > 0 } ? null :
282+
options?.ToolMode is AutoChatToolMode ? new ToolChoice(new ToolChoiceAuto()) :
283+
options?.ToolMode is RequiredChatToolMode r
284+
? r.RequiredFunctionName is not null
285+
? new ToolChoice(new ToolChoiceTool
286+
{
287+
Name = r.RequiredFunctionName,
288+
})
289+
: new ToolChoice(new ToolChoiceAny())
290+
: (ToolChoice?)null;
291+
292+
if (options?.Tools is { Count: > 0 } aitools)
293+
{
294+
var tools = request.Tools ?? [];
295+
request.Tools = tools;
296+
297+
foreach (var tool in aitools)
298+
{
299+
switch (tool)
300+
{
301+
case AIFunction f:
302+
tools.Add(new Tool
303+
{
304+
Name = f.Name,
305+
Description = f.Description,
306+
InputSchema = CreateSchema(f),
307+
});
308+
break;
309+
310+
case HostedWebSearchTool ws:
311+
tools.Add(new WebSearchTool20250305());
312+
break;
313+
}
314+
}
315+
}
316+
258317
return request;
259318
}
260319

0 commit comments

Comments
 (0)