Links: Architecture: docs/Architecture/Overview.md Modules: GeminiChatClient.cs ADRs: 003-microsoft-extensions-ai-integration.md
Enable GeminiSharpSDK to participate as a first-class provider in the Microsoft.Extensions.AI ecosystem by implementing IChatClient, unlocking DI registration, middleware pipelines, and provider-agnostic consumer code.
IChatClientimplementation (GeminiChatClient) adaptingGeminiClient/GeminiThread- Input mapping:
ChatMessage[]→ Gemini prompt + images - Output mapping:
RunResult→ChatResponsewithUsageDetailsand rich content - Streaming:
ThreadEvent→ChatResponseUpdatemapping - Custom
AIContenttypes for Gemini-specific items (commands, file changes, MCP, web search, collab) - DI registration via
AddGeminiChatClient()/AddKeyedGeminiChatClient() - Gemini-specific options via
ChatOptions.AdditionalPropertieswithgemini:*prefix
IEmbeddingGenerator(Gemini CLI is not an embedding service)IImageGenerator(Gemini CLI is not an image generator)- Consumer-side
AIToolregistration (Gemini manages tools internally) - Generic tuning mappings not exposed by the current headless Gemini CLI contract (for example
Temperature,TopP,TopK, and unsupported reasoning flags)
ChatOptions.ModelIdmaps toThreadOptions.Model.ChatOptions.ConversationIdtriggers thread resume viaResumeThread(id).- Multiple
ChatMessageentries are concatenated into a single prompt while preserving original message chronology (Gemini CLI is single-prompt-per-turn). ChatOptions.Toolsis silently ignored; tool results surface as customAIContenttypes.GetService<ChatClientMetadata>()returns provider name"GeminiCLI"with default model from options.- Streaming maps the real CLI event sequence (
init,message,tool_use,tool_result,result,error), not token-level deltas. - Unsupported CLI options fail fast when
GeminiThreadcannot express them through the current headless contract. - Turn failures propagate from CLI
errorevents or process/runtime failures asInvalidOperationException.
-
Basic chat completion
- Actor: Consumer code using
IChatClient - Trigger:
client.GetResponseAsync([new ChatMessage(ChatRole.User, "prompt")]) - Steps: map messages → create thread → RunAsync → map result
- Result:
ChatResponsewith text, usage, thread ID as ConversationId
- Actor: Consumer code using
-
Streaming
- Trigger:
client.GetStreamingResponseAsync(messages) - Steps: map messages → create thread → RunStreamedAsync → stream events as ChatResponseUpdate
- Result:
IAsyncEnumerable<ChatResponseUpdate>with incremental content
- Trigger:
-
Multi-turn resume
- Trigger:
client.GetResponseAsync(messages, new ChatOptions { ConversationId = "thread-123" }) - Steps: resume thread with ID → RunAsync → map result
- Result: Continuation in existing Gemini conversation
- Trigger:
GeminiSharpSDK.Extensions.AI/GeminiSharpSDK.Extensions.AI.csprojManagedCode.GeminiSharpSDK.Extensions.AIpackageIChatClientadapter (GeminiChatClient) and DI extensions
GeminiSharpSDK.Extensions.AI.Tests/GeminiSharpSDK.Extensions.AI.Tests.csproj- mapper/DI test coverage for M.E.AI integration
- Adapter entry points:
GeminiChatClient,GeminiChatClientOptions,GeminiServiceCollectionExtensions - Mapping layer:
ChatMessageMapper,ChatOptionsMapper,ChatResponseMapper,StreamingEventMapper - Rich content models:
CommandExecutionContent,FileChangeContent,McpToolCallContent,WebSearchContent,CollabToolCallContent - Docs: ADR
003and this feature specification
using Microsoft.Extensions.AI;
using ManagedCode.GeminiSharpSDK.Extensions.AI;
using ManagedCode.GeminiSharpSDK.Models;
IChatClient client = new GeminiChatClient(new GeminiChatClientOptions
{
DefaultModel = GeminiModels.Gemini25Pro,
});using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using ManagedCode.GeminiSharpSDK.Extensions.AI.Extensions;
using ManagedCode.GeminiSharpSDK.Models;
var services = new ServiceCollection();
services.AddGeminiChatClient(options =>
{
options.DefaultModel = GeminiModels.Gemini25Pro;
});
using var provider = services.BuildServiceProvider();
var chatClient = provider.GetRequiredService<IChatClient>();using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using ManagedCode.GeminiSharpSDK.Extensions.AI.Extensions;
using ManagedCode.GeminiSharpSDK.Models;
var services = new ServiceCollection();
services.AddKeyedGeminiChatClient("gemini-main", options =>
{
options.DefaultModel = GeminiModels.Gemini25Pro;
});
using var provider = services.BuildServiceProvider();
var keyedClient = provider.GetRequiredKeyedService<IChatClient>("gemini-main");flowchart LR
Input["IEnumerable<ChatMessage>"]
MsgMapper["ChatMessageMapper"]
OptMapper["ChatOptionsMapper"]
Thread["GeminiThread.RunAsync"]
Cli["gemini --prompt ... --output-format stream-json"]
RespMapper["ChatResponseMapper"]
Output["ChatResponse"]
Input --> MsgMapper
MsgMapper --> Thread
OptMapper --> Thread
Thread --> Cli
Thread --> RespMapper
RespMapper --> Output
- build:
dotnet build ManagedCode.GeminiSharpSDK.slnx -c Release -warnaserror - test:
dotnet test --solution ManagedCode.GeminiSharpSDK.slnx -c Release - format:
dotnet format ManagedCode.GeminiSharpSDK.slnx
- Mapper tests:
GeminiSharpSDK.Extensions.AI.Tests/ChatMessageMapperTests.cs,ChatOptionsMapperTests.cs,ChatResponseMapperTests.cs,StreamingEventMapperTests.cs - DI tests:
GeminiSharpSDK.Extensions.AI.Tests/GeminiServiceCollectionExtensionsTests.cs - Adapter regression tests must stay aligned with the current real CLI event contract and the Agent Framework composition layer.
GeminiChatClientimplementsIChatClientwith full mapper coverage.- DI extensions register client correctly.
- All mapper and DI tests pass.
- ADR and feature docs created.
- Architecture overview updated.