Skip to content

Commit 469b13a

Browse files
IEvangelistgewarrenalexwolfmsft
authored
Add the runtime libraries bits for MEAI (#43985)
* Add the runtime libraries bits for MEAI * Apply suggestions from code review Co-authored-by: Genevieve Warren <24882762+gewarren@users.noreply.github.com> * Added a mock TOC (or in article nav) * Add all the snippets * Apply suggestions from code review Co-authored-by: alexwolfmsft <93200798+alexwolfmsft@users.noreply.github.com> * Fix code includes --------- Co-authored-by: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Co-authored-by: alexwolfmsft <93200798+alexwolfmsft@users.noreply.github.com>
1 parent ab89a0e commit 469b13a

File tree

43 files changed

+1103
-8
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1103
-8
lines changed

docs/ai/ai-extensions.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Unified AI building blocks for .NET
33
description: Learn how to develop with unified AI building blocks for .NET using Microsoft.Extensions.AI and Microsoft.Extensions.AI.Abstractions libraries
4-
ms.date: 11/04/2024
4+
ms.date: 12/16/2024
55
ms.topic: quickstart
66
ms.custom: devx-track-dotnet, devx-track-dotnet-ai
77
author: alexwolfmsft
@@ -16,11 +16,13 @@ The .NET ecosystem provides abstractions for integrating AI services into .NET a
1616
- How to work with AI abstractions in your apps and the benefits they offer.
1717
- Essential AI middleware concepts.
1818

19+
For more information, see [Introduction to Microsoft.Extensions.AI](../core/extensions/artificial-intelligence.md).
20+
1921
## What is the Microsoft.Extensions.AI library?
2022

2123
`Microsoft.Extensions.AI` is a set of core .NET libraries created in collaboration with developers across the .NET ecosystem, including Semantic Kernel. These libraries provide a unified layer of C# abstractions for interacting with AI services, such as small and large language models (SLMs and LLMs), embeddings, and middleware.
2224

23-
:::image type="content" source="media/ai-extensions/meai-architecture-diagram.png" alt-text="An architectural diagram of the AI extensions libraries.":::
25+
:::image type="content" source="media/ai-extensions/meai-architecture-diagram.png" lightbox="media/ai-extensions/meai-architecture-diagram.png" alt-text="An architectural diagram of the AI extensions libraries.":::
2426

2527
`Microsoft.Extensions.AI` provides abstractions that can be implemented by various services, all adhering to the same core concepts. This library is not intended to provide APIs tailored to any specific provider's services. The goal of `Microsoft.Extensions.AI` is to act as a unifying layer within the .NET ecosystem, enabling developers to choose their preferred frameworks and libraries while ensuring seamless integration and collaboration across the ecosystem.
2628

docs/core/extensions/artificial-intelligence.md

Lines changed: 282 additions & 0 deletions
Large diffs are not rendered by default.

docs/core/extensions/http-ratelimiter.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Rate limiting an HTTP handler in .NET
33
description: Learn how to create a client-side HTTP handler that limits the number of requests, with the inbuilt rate limiter API from .NET.
44
author: IEvangelist
55
ms.author: dapine
6-
ms.date: 03/13/2023
6+
ms.date: 12/16/2024
77
---
88

99
# Rate limit an HTTP handler in .NET
@@ -161,12 +161,12 @@ You'll notice that the first logged entries are always the immediately returned
161161

162162
Note also that each URL's query string is unique: examine the `iteration` parameter to see that it's incremented by one for each request. This parameter helps to illustrate that the 429 responses aren't from the first requests, but rather from the requests that are made after the rate limit is reached. The 200 responses arrive later but these requests were made earlier&mdash;before the limit was reached.
163163

164-
To have a better understanding of the various rate-limiting algorithms, try rewriting this code to accept a different `RateLimiter` implementation. In addition to the `TokenBucketRateLimiter` you could try:
164+
To have a better understanding of the various rate-limiting algorithms, try rewriting this code to accept a different <xref:System.Threading.RateLimiting.RateLimiter> implementation. In addition to the <xref:System.Threading.RateLimiting.TokenBucketRateLimiter> you could try:
165165

166-
- `ConcurrencyLimiter`
167-
- `FixedWindowRateLimiter`
168-
- `PartitionedRateLimiter`
169-
- `SlidingWindowRateLimiter`
166+
- <xref:System.Threading.RateLimiting.ConcurrencyLimiter>
167+
- <xref:System.Threading.RateLimiting.FixedWindowRateLimiter>
168+
- <xref:System.Threading.RateLimiting.PartitionedRateLimiter>
169+
- <xref:System.Threading.RateLimiting.SlidingWindowRateLimiter>
170170

171171
## Summary
172172

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.Extensions.AI" Version="9.0.1-preview.1.24570.5" />
11+
<PackageReference Include="System.Threading.RateLimiting" Version="9.0.0" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using Microsoft.Extensions.AI;
2+
using System.Runtime.CompilerServices;
3+
using System.Threading.RateLimiting;
4+
5+
public sealed class RateLimitingChatClient(
6+
IChatClient innerClient, RateLimiter rateLimiter)
7+
: DelegatingChatClient(innerClient)
8+
{
9+
public override async Task<ChatCompletion> CompleteAsync(
10+
IList<ChatMessage> chatMessages,
11+
ChatOptions? options = null,
12+
CancellationToken cancellationToken = default)
13+
{
14+
using var lease = await rateLimiter.AcquireAsync(permitCount: 1, cancellationToken)
15+
.ConfigureAwait(false);
16+
17+
if (!lease.IsAcquired)
18+
{
19+
throw new InvalidOperationException("Unable to acquire lease.");
20+
}
21+
22+
return await base.CompleteAsync(chatMessages, options, cancellationToken)
23+
.ConfigureAwait(false);
24+
}
25+
26+
public override async IAsyncEnumerable<StreamingChatCompletionUpdate> CompleteStreamingAsync(
27+
IList<ChatMessage> chatMessages,
28+
ChatOptions? options = null,
29+
[EnumeratorCancellation] CancellationToken cancellationToken = default)
30+
{
31+
using var lease = await rateLimiter.AcquireAsync(permitCount: 1, cancellationToken)
32+
.ConfigureAwait(false);
33+
34+
if (!lease.IsAcquired)
35+
{
36+
throw new InvalidOperationException("Unable to acquire lease.");
37+
}
38+
39+
await foreach (var update in base.CompleteStreamingAsync(chatMessages, options, cancellationToken)
40+
.ConfigureAwait(false))
41+
{
42+
yield return update;
43+
}
44+
}
45+
46+
protected override void Dispose(bool disposing)
47+
{
48+
if (disposing)
49+
{
50+
rateLimiter.Dispose();
51+
}
52+
53+
base.Dispose(disposing);
54+
}
55+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Example.Two;
2+
3+
// <two>
4+
using Microsoft.Extensions.AI;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using System.Threading.RateLimiting;
7+
8+
public static class RateLimitingChatClientExtensions
9+
{
10+
public static ChatClientBuilder UseRateLimiting(
11+
this ChatClientBuilder builder, RateLimiter? rateLimiter = null) =>
12+
builder.Use((innerClient, services) =>
13+
new RateLimitingChatClient(
14+
innerClient,
15+
rateLimiter ?? services.GetRequiredService<RateLimiter>()));
16+
}
17+
// </two>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace Example.One;
2+
3+
// <one>
4+
using Microsoft.Extensions.AI;
5+
using System.Threading.RateLimiting;
6+
7+
public static class RateLimitingChatClientExtensions
8+
{
9+
public static ChatClientBuilder UseRateLimiting(
10+
this ChatClientBuilder builder, RateLimiter rateLimiter) =>
11+
builder.Use(innerClient => new RateLimitingChatClient(innerClient, rateLimiter));
12+
}
13+
// </one>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using Microsoft.Extensions.AI;
2+
using System.Threading.RateLimiting;
3+
4+
public class RateLimitingEmbeddingGenerator(
5+
IEmbeddingGenerator<string, Embedding<float>> innerGenerator, RateLimiter rateLimiter)
6+
: DelegatingEmbeddingGenerator<string, Embedding<float>>(innerGenerator)
7+
{
8+
public override async Task<GeneratedEmbeddings<Embedding<float>>> GenerateAsync(
9+
IEnumerable<string> values,
10+
EmbeddingGenerationOptions? options = null,
11+
CancellationToken cancellationToken = default)
12+
{
13+
using var lease = await rateLimiter.AcquireAsync(permitCount: 1, cancellationToken)
14+
.ConfigureAwait(false);
15+
16+
if (!lease.IsAcquired)
17+
{
18+
throw new InvalidOperationException("Unable to acquire lease.");
19+
}
20+
21+
return await base.GenerateAsync(values, options, cancellationToken);
22+
}
23+
24+
protected override void Dispose(bool disposing)
25+
{
26+
if (disposing)
27+
{
28+
rateLimiter.Dispose();
29+
}
30+
31+
base.Dispose(disposing);
32+
}
33+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System.Runtime.CompilerServices;
2+
using Microsoft.Extensions.AI;
3+
4+
public sealed class SampleChatClient(Uri endpoint, string modelId) : IChatClient
5+
{
6+
public ChatClientMetadata Metadata { get; } = new(nameof(SampleChatClient), endpoint, modelId);
7+
8+
public async Task<ChatCompletion> CompleteAsync(
9+
IList<ChatMessage> chatMessages,
10+
ChatOptions? options = null,
11+
CancellationToken cancellationToken = default)
12+
{
13+
// Simulate some operation.
14+
await Task.Delay(300, cancellationToken);
15+
16+
// Return a sample chat completion response randomly.
17+
string[] responses =
18+
[
19+
"This is the first sample response.",
20+
"Here is another example of a response message.",
21+
"This is yet another response message."
22+
];
23+
24+
return new([new ChatMessage()
25+
{
26+
Role = ChatRole.Assistant,
27+
Text = responses[Random.Shared.Next(responses.Length)],
28+
}]);
29+
}
30+
31+
public async IAsyncEnumerable<StreamingChatCompletionUpdate> CompleteStreamingAsync(
32+
IList<ChatMessage> chatMessages,
33+
ChatOptions? options = null,
34+
[EnumeratorCancellation] CancellationToken cancellationToken = default)
35+
{
36+
// Simulate streaming by yielding messages one by one.
37+
string[] words = ["This ", "is ", "the ", "response ", "for ", "the ", "request."];
38+
foreach (string word in words)
39+
{
40+
// Simulate some operation.
41+
await Task.Delay(100, cancellationToken);
42+
43+
// Yield the next message in the response.
44+
yield return new StreamingChatCompletionUpdate
45+
{
46+
Role = ChatRole.Assistant,
47+
Text = word,
48+
};
49+
}
50+
}
51+
52+
public object? GetService(Type serviceType, object? serviceKey) => this;
53+
54+
public TService? GetService<TService>(object? key = null)
55+
where TService : class => this as TService;
56+
57+
void IDisposable.Dispose() { }
58+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Microsoft.Extensions.AI;
2+
3+
public sealed class SampleEmbeddingGenerator(
4+
Uri endpoint, string modelId)
5+
: IEmbeddingGenerator<string, Embedding<float>>
6+
{
7+
public EmbeddingGeneratorMetadata Metadata { get; } =
8+
new(nameof(SampleEmbeddingGenerator), endpoint, modelId);
9+
10+
public async Task<GeneratedEmbeddings<Embedding<float>>> GenerateAsync(
11+
IEnumerable<string> values,
12+
EmbeddingGenerationOptions? options = null,
13+
CancellationToken cancellationToken = default)
14+
{
15+
// Simulate some async operation
16+
await Task.Delay(100, cancellationToken);
17+
18+
// Create random embeddings
19+
return
20+
[
21+
.. from value in values
22+
select new Embedding<float>(
23+
Enumerable.Range(0, 384)
24+
.Select(_ => Random.Shared.NextSingle())
25+
.ToArray())
26+
];
27+
}
28+
29+
public object? GetService(Type serviceType, object? serviceKey) => this;
30+
31+
public TService? GetService<TService>(object? key = null)
32+
where TService : class => this as TService;
33+
34+
void IDisposable.Dispose() { }
35+
}

0 commit comments

Comments
 (0)