Microsoft Agent Framework Microsoft.Extensions.AI Created: 27 Feb 2026 Updated: 27 Feb 2026

IEmbeddingGenerator: Custom Middleware

DelegatingEmbeddingGenerator<TInput, TEmbedding> is the embedding counterpart of DelegatingChatClient. Derive from it to add custom behaviour — logging, metrics, rate limiting, retries — without changing the rest of the pipeline. The base class delegates all calls to the inner generator by default; you only override what you need.

Key Concepts

1. Deriving from DelegatingEmbeddingGenerator

Override GenerateAsync to intercept embedding requests. Call base.GenerateAsync to pass through to the inner generator:

public sealed class LoggingEmbeddingGenerator(
IEmbeddingGenerator<string, Embedding<float>> inner)
: DelegatingEmbeddingGenerator<string, Embedding<float>>(inner)
{
public override async Task<GeneratedEmbeddings<Embedding<float>>> GenerateAsync(
IEnumerable<string> values,
EmbeddingGenerationOptions? options = null,
CancellationToken cancellationToken = default)
{
Console.WriteLine($"Embedding {values.Count()} input(s)...");

var result = await base.GenerateAsync(values, options, cancellationToken);

Console.WriteLine($"Got {result.Count} vector(s), dims={result[0].Vector.Length}");
return result;
}
}

2. Wrapping the Generator

Pass the inner generator to the constructor — no builder required:

IEmbeddingGenerator<string, Embedding<float>> raw =
new OpenAIClient(apiKey)
.GetEmbeddingClient("text-embedding-3-small")
.AsIEmbeddingGenerator();

IEmbeddingGenerator<string, Embedding<float>> generator =
new LoggingEmbeddingGenerator(raw);

3. GenerateVectorAsync Goes Through Middleware

The convenience method GenerateVectorAsync internally calls GenerateAsync, so your middleware intercepts it automatically — no extra work needed.

Full Example

using Microsoft.Extensions.AI;
using OpenAI;

namespace MicrosoftAgentFrameworkLesson.ConsoleApp.Embeddings;

/// <summary>
/// Custom middleware built on DelegatingEmbeddingGenerator.
/// Logs every generation request: input count, vector dimensions, and elapsed time.
/// </summary>
public sealed class LoggingEmbeddingGenerator(
IEmbeddingGenerator<string, Embedding<float>> inner)
: DelegatingEmbeddingGenerator<string, Embedding<float>>(inner)
{
private int _batchNumber;

public override async Task<GeneratedEmbeddings<Embedding<float>>> GenerateAsync(
IEnumerable<string> values,
EmbeddingGenerationOptions? options = null,
CancellationToken cancellationToken = default)
{
var list = values.ToList();
int id = Interlocked.Increment(ref _batchNumber);

Console.WriteLine($" [EMB #{id}] Generating {list.Count} embedding(s)...");
foreach (var v in list)
Console.WriteLine($" [EMB #{id}] \"{v}\"");

var started = DateTime.UtcNow;
GeneratedEmbeddings<Embedding<float>> result =
await base.GenerateAsync(list, options, cancellationToken);
var ms = (DateTime.UtcNow - started).TotalMilliseconds;

Console.WriteLine($" [EMB #{id}] Done — {result.Count} vector(s), " +
$"dims={result[0].Vector.Length}, elapsed={ms:F0} ms");
return result;
}
}

/// <summary>
/// Demonstrates DelegatingEmbeddingGenerator custom middleware.
/// Scenario: E-commerce product tagging with audit logging.
/// </summary>
public static class EmbeddingMiddlewareDemo
{
public static async Task RunAsync()
{
var apiKey = Environment.GetEnvironmentVariable("OPEN_AI_KEY")
?? throw new InvalidOperationException("Set OPEN_AI_KEY environment variable.");

IEmbeddingGenerator<string, Embedding<float>> rawGenerator =
new OpenAIClient(apiKey)
.GetEmbeddingClient("text-embedding-3-small")
.AsIEmbeddingGenerator();

IEmbeddingGenerator<string, Embedding<float>> generator =
new LoggingEmbeddingGenerator(rawGenerator);

Console.WriteLine("====== IEmbeddingGenerator — Custom Middleware (DelegatingEmbeddingGenerator) ======\n");

Console.WriteLine("--- Batch 1: Product Names ---");
await generator.GenerateAsync(["Wireless Headphones", "Mechanical Keyboard"]);
Console.WriteLine();

Console.WriteLine("--- Batch 2: Single Item ---");
await generator.GenerateVectorAsync("USB-C Docking Station");
Console.WriteLine();

Console.WriteLine("--- Batch 3: Categories ---");
await generator.GenerateAsync(["Audio & Video", "Computing", "Accessories"]);
Console.WriteLine();
}
}
Share this lesson: