Repository navigation
Microsoft.Extensions.AI IChatClient Implementation
The Google_GenerativeAI.Microsoft package implements the Microsoft.Extensions.AI (MEAI) abstractions on top of the SDK, so Gemini can be used anywhere an IChatClient, IEmbeddingGenerator or IImageGenerator is expected, including MEAI middleware (function invocation, logging, caching, telemetry) and dependency injection.
| MEAI abstraction | Implementation | Wraps |
|---|---|---|
IChatClient |
GenerativeAIChatClient |
GenerativeModel |
IEmbeddingGenerator<string, Embedding<float>> |
GenerativeAIEmbeddingGenerator |
EmbeddingModel (see Embeddings) |
IImageGenerator |
GenerativeAIImageGenerator (Gemini image models), GenerativeAIImagenGenerator (obsolete: Imagen is retired) |
see Image Generation |
dotnet add package Google_GenerativeAI.MicrosoftThe package references Microsoft.Extensions.AI.Abstractions 10.x on net8.0 and net9.0, and 11.x on net10.0, net11.0, netstandard2.0 and net462. Middleware such as UseFunctionInvocation() and the AddChatClient(...) DI helper come from the separate Microsoft.Extensions.AI package; add it if you use them, with the same major version.
using GenerativeAI;
using GenerativeAI.Microsoft;
using Microsoft.Extensions.AI;
// Gemini API with an API key (model defaults to GoogleAIModels.DefaultGeminiModel)
IChatClient client = new GenerativeAIChatClient(apiKey, GoogleAIModels.Gemini38Flash);
// Any platform adapter, e.g. Vertex AI
IChatClient vertexClient = new GenerativeAIChatClient(
new VertextPlatformAdapter(projectId: "my-project", region: "us-central1"),
VertexAIModels.Gemini.Gemini38Flash);Constructors:
GenerativeAIChatClient(string apiKey, string modelName = GoogleAIModels.DefaultGeminiModel, bool autoCallFunction = true)GenerativeAIChatClient(IPlatformAdapter adapter, string modelName = GoogleAIModels.DefaultGeminiModel, bool autoCallFunction = true)
The underlying GenerativeModel is available through the model property.
ChatResponse response = await client.GetResponseAsync("What is the capital of France?");
Console.WriteLine(response.Text);IChatClient is stateless: pass the whole conversation each time and append the replies.
var history = new List<ChatMessage>
{
new(ChatRole.System, "You are a helpful assistant."),
new(ChatRole.User, "Give me three names for a cat.")
};
ChatResponse reply = await client.GetResponseAsync(history);
history.AddMessages(reply);
history.Add(new ChatMessage(ChatRole.User, "Which one is the shortest?"));
reply = await client.GetResponseAsync(history);
Console.WriteLine(reply.Text);System messages, together with ChatOptions.Instructions, become the Gemini system instruction.
await foreach (ChatResponseUpdate update in client.GetStreamingResponseAsync("Write a haiku about the sea."))
{
Console.Write(update.Text);
}var options = new ChatOptions
{
Temperature = 0.7f,
MaxOutputTokens = 1024,
TopP = 0.9f,
TopK = 40,
StopSequences = new List<string> { "END" },
Seed = 42,
ResponseFormat = ChatResponseFormat.Json // or ChatResponseFormat.ForJsonSchema(...)
};
ChatResponse response = await client.GetResponseAsync(history, options);Temperature, TopP, TopK, MaxOutputTokens, StopSequences, Seed, PresencePenalty, FrequencyPenalty and ResponseFormat map to Gemini's GenerationConfig. A JSON schema in ResponseFormat is converted to the schema subset Gemini accepts. Structured output helpers such as client.GetResponseAsync<T>(...) from Microsoft.Extensions.AI work as well.
Use ChatOptions.AdditionalProperties with the keys in AdditionalPropertiesKeys:
| Key | Value type | Gemini setting |
|---|---|---|
AdditionalPropertiesKeys.ThinkingBudget |
int? |
ThinkingConfig.ThinkingBudget |
AdditionalPropertiesKeys.ThinkingConfigIncludeThoughts |
bool? |
ThinkingConfig.IncludeThoughts |
AdditionalPropertiesKeys.ResponseModalities |
List<Modality> |
ResponseModalities (for example text + image) |
AdditionalPropertiesKeys.ImageConfigAspectRatio |
string |
ImageConfig.AspectRatio |
AdditionalPropertiesKeys.ImageConfigImageSize |
string |
ImageConfig.ImageSize |
AdditionalPropertiesKeys.ImageOutputOptionsMimeType |
string |
ImageConfig.ImageOutputOptions.MimeType |
AdditionalPropertiesKeys.ImageOutputOptionsCompressionQuality |
int |
ImageConfig.ImageOutputOptions.CompressionQuality |
"ResponseLogprobs" / "Logprobs"
|
bool? / int?
|
log probabilities |
var options = new ChatOptions
{
AdditionalProperties = new AdditionalPropertiesDictionary
{
[AdditionalPropertiesKeys.ThinkingBudget] = 1024,
[AdditionalPropertiesKeys.ThinkingConfigIncludeThoughts] = true
}
};Thought signatures returned by Gemini 3.x are kept in the AdditionalProperties of the returned contents (key AdditionalPropertiesKeys.ThoughtSignature) and sent back when you pass the messages in the next request, which multi-turn function calling with thinking models needs.
Add DataContent (bytes plus media type) to a message; it is sent as inline data. DataContent larger than 20 MB is uploaded through the Files API automatically instead. To reference a file you already uploaded (see Files Client), use the SDK's RemoteFileContent(RemoteFile remoteFile, string? mimeType = null) from GenerativeAI.Microsoft.Extensions. Other content types such as UriContent are not converted and are skipped.
var message = new ChatMessage(ChatRole.User, new List<AIContent>
{
new TextContent("Describe this picture."),
new DataContent(await File.ReadAllBytesAsync("photo.jpg"), "image/jpeg")
});
ChatResponse response = await client.GetResponseAsync(message);With autoCallFunction: true (the default) the client runs the AIFunctions in ChatOptions.Tools itself and sends the results back to the model:
var options = new ChatOptions
{
Tools = new List<AITool> { AIFunctionFactory.Create(GetWeather) }
};
ChatResponse response = await client.GetResponseAsync("What's the weather in Paris?", options);
Console.WriteLine(response.Text);
[Description("Gets the current weather for a city")]
static string GetWeather(string city) => $"It's sunny in {city}.";HostedWebSearchTool in ChatOptions.Tools turns on Gemini's Google Search tool, and HostedCodeInterpreterTool turns on code execution.
To use MEAI's function-invocation middleware instead, turn the built-in loop off with autoCallFunction: false:
IChatClient client = new GenerativeAIChatClient(apiKey, GoogleAIModels.Gemini38Flash, autoCallFunction: false)
.AsBuilder()
.UseFunctionInvocation()
.Build();You can also change it later with the AutoCallFunction property.
The source-generated and reflection-based tools from Google_GenerativeAI.Tools (see Function Calling) convert to MEAI tools:
// Method-based: [FunctionTool(MeaiFunctionTool = true)]
options.Tools = new Tools([GetCurrentDateTime]).AsMeaiTools();
// Interface-based: [GenerateJsonSchema(MeaiFunctionTool = true)]
options.Tools = new WeatherService().AsMeaiTools();
// Reflection-based
options.Tools = new List<AITool> { new QuickTool(GetCurrentDateTime).AsMeaiTool() };- Parallel function calls: when the model asks for several functions in one turn, the model's function-call turn is added to the history once, followed by all function results in one turn. Earlier versions repeated the function-call turn for every call.
-
Streaming function-call detection:
GetStreamingResponseAsynccollects parts across the whole stream, so function calls are found even when the last chunk only carries the finish reason (common with newer models). -
userrole for function results: results are sent with theuserrole because Gemini 3.x rejects thefunctionrole.
using GenerativeAI.Microsoft;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
string apiKey = Environment.GetEnvironmentVariable("GOOGLE_API_KEY")!;
// Plain registration (Google_GenerativeAI.Microsoft only)
services.AddSingleton<IChatClient>(_ => new GenerativeAIChatClient(apiKey));
// Or with the MEAI builder and middleware (needs the Microsoft.Extensions.AI package)
services.AddChatClient(_ => new GenerativeAIChatClient(apiKey, GoogleAIModels.Gemini38Flash, autoCallFunction: false))
.UseFunctionInvocation();Inject IChatClient into your services:
public class MyChatService(IChatClient chatClient)
{
public async Task<string> AskAsync(string userMessage)
{
ChatResponse response = await chatClient.GetResponseAsync(userMessage);
return response.Text;
}
}For the SDK's own DI integration (IGenerativeAiService), see Web Application Integration.
- API keys: keep keys out of source code; use environment variables, user secrets or a secret store.
-
Errors: handle exceptions from API calls (for example
GenerativeAIExceptionand HTTP errors). - Rate limits: respect the API's rate limits and add retries or throttling where needed.
- Context window: long conversations can exceed the model's context window; trim or summarize older messages.
- Model choice: pick the model for your task and budget; see Supported Models.
Getting Started
- Initialization
- Authentication
- Configuration & Error Handling
- Supported Models
- Migration Guide (v4)
- Release Notes (v4)
Core Features
- Generate Content
- Chat Session
- Streaming
- Multimodal Features
- JSON Mode
- Function Calling
- Embeddings
- Context Caching
Gemini APIs
- Interactions API
- Multimodal Live API
- File Search
- Batch API
- Model Tuning
- Webhooks
- Agent APIs
- Image Generation
- Video Generation
- Speech Generation
- Files Client
- Model Client
Vertex AI
Integrations
Guides
Legacy