Junaid.GoogleGemini.Net.Extensions.AI 6.2.0

dotnet add package Junaid.GoogleGemini.Net.Extensions.AI --version 6.2.0
                    
NuGet\Install-Package Junaid.GoogleGemini.Net.Extensions.AI -Version 6.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Junaid.GoogleGemini.Net.Extensions.AI" Version="6.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Junaid.GoogleGemini.Net.Extensions.AI" Version="6.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Junaid.GoogleGemini.Net.Extensions.AI" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Junaid.GoogleGemini.Net.Extensions.AI --version 6.2.0
                    
#r "nuget: Junaid.GoogleGemini.Net.Extensions.AI, 6.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Junaid.GoogleGemini.Net.Extensions.AI@6.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Junaid.GoogleGemini.Net.Extensions.AI&version=6.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Junaid.GoogleGemini.Net.Extensions.AI&version=6.2.0
                    
Install as a Cake Tool

Junaid.GoogleGemini.Net

.NET NuGet License

A production-ready .NET client for the Google Gemini API — resilient, observable, DI-native, and built to feel right in ASP.NET Core.

🤖 Built with AI. The v5 → v6 modernization was implemented end-to-end by Claude (Anthropic). See AI-assisted development.

It covers the modern Gemini surface (structured output, system instructions, thinking, grounding, the Files API, context caching), but what makes it worth choosing is everything around the API call:

This library Typical thin wrappers
Resilience built in ✅ retries + backoff on the HttpClient pipeline ❌ roll your own
Client-side rate limiting ✅ token-bucket, configurable ❌ none
OpenTelemetry-native ✅ traces + token/latency metrics (GenAI semconv), zero extra deps ❌ none
IChatClient / IEmbeddingGenerator ✅ via companion package sometimes
Typed structured output GenerateAsync<T>() rare
DI-first + Options pattern varies
Cost governance ✅ daily USD budget + per-request estimate + cost metric ❌ not found in any .NET Gemini client we surveyed[^cost-governance-survey]

[^cost-governance-survey]: Checked as of August 2026: Google's own Google.GenAI, Mscc.GenerativeAI, Google_GenerativeAI (784K+ downloads, self-described "most complete" .NET Gemini SDK), GeminiDotnet, Gemini.NET, dotnet-gemini-sdk, Google_Generative_AI, and GemiNet — none offer budget caps, cost tracking, or spend-limit enforcement. The closest any come is passing through the server's own 429 rate-limit errors as a typed exception, which is not the same thing.

v6 is a modernization release with breaking changes (idiomatic PascalCase models, IAsyncEnumerable streaming, typed exceptions). See ROADMAP.md.

Installation

dotnet add package Junaid.GoogleGemini.Net
# optional: Microsoft.Extensions.AI adapters (IChatClient, IEmbeddingGenerator)
dotnet add package Junaid.GoogleGemini.Net.Extensions.AI

Authentication

Get an API key from Google AI Studio, then provide it via environment variable (GeminiApiKey) or configuration:

{
  "Gemini": {
    "ApiKey": "your-api-key-here",
    "DefaultModel": "gemini-3.6-flash",
    "TimeoutSeconds": 100,
    "MaxRetries": 3,
    "RateLimit": { "Enabled": true, "RequestsPerMinute": 60 }
  }
}

Quick start

builder.Services.AddGemini(builder.Configuration.GetSection("Gemini"));

app.MapGet("/", async (IGeminiService gemini) =>
{
    var response = await gemini.GenerateAsync("Say hello!");
    return response.Text();
});

Core features

Generation, vision, chat

// Text
var response = await gemini.GenerateAsync("Write a haiku about C#");

// Vision (text + image)
var image = new FileObject(File.ReadAllBytes("photo.jpg"), "photo.jpg");
var vision = await gemini.GenerateWithImageAsync("What's in this image?", image);

// Chat
var messages = new[]
{
    new MessageObject("user", "Hello, who are you?"),
    new MessageObject("model", "I'm Gemini."),
    new MessageObject("user", "Tell me a joke."),
};
var chat = await gemini.ChatAsync(messages);

Streaming (IAsyncEnumerable)

await foreach (var chunk in gemini.StreamAsync("Tell me a long story"))
{
    Console.Write(chunk.Text());
}

// Or a simple callback overload:
await gemini.StreamAsync("Tell me a long story", text => Console.Write(text));

⭐ Typed structured output

Get a strongly-typed result back — the JSON schema is derived from your type automatically:

record Recipe(string Title, string[] Ingredients, int Minutes);

Recipe recipe = await gemini.GenerateAsync<Recipe>("A quick pasta recipe");
Console.WriteLine(recipe.Title);

Reading responses safely

var response = await gemini.GenerateAsync("...");

string text = response.Text();                 // "" if there was no text (never a placeholder)
if (response.TryGetText(out var t)) { /* ... */ }
string guaranteed = response.GetTextOrThrow();  // throws GeminiContentException if blocked/empty

var reason = response.FinishReason;             // e.g. "STOP", "SAFETY"
var usage  = response.Usage;                    // PromptTokenCount, CandidatesTokenCount, ...

Request options (system instructions, thinking, JSON, grounding)

var options = new GeminiRequestOptions
{
    Model = GeminiConstants.Models.Gemini36Flash,        // Gemini 3 (the default is gemini-3.6-flash)
    SystemInstruction = "You are a terse senior engineer.",
    ThinkingLevel = GeminiConstants.ThinkingLevels.Low,  // Gemini 3 reasoning depth (use ThinkingBudget on 2.5)
    EnableGoogleSearch = true,                           // ground answers with Google Search
};
var grounded = await gemini.GenerateAsync("What shipped in .NET 9?", options);
foreach (var q in grounded.Candidates?[0].GroundingMetadata?.WebSearchQueries ?? [])
    Console.WriteLine($"searched: {q}");

Convenience presets: GeminiRequestOptions.Creative(), .Factual(), .Code(), .Fast().

Note: Google deprecated temperature/topP/topK on gemini-3.6-flash and gemini-3.5-flash-lite (July 2026) — those models ignore the sampling params entirely, so .Factual()/.Code() won't have the effect you'd expect on the default model. Use SystemInstruction with explicit rules instead.

Gemini 3 ready. Model names aren't allow-listed, so any current/future model works without a library update. ThinkingLevel and MediaResolution are supported, and the model's encrypted thoughtSignature is captured and can be replayed for multi-turn function calling via the Content-based ChatAsync/StreamChatAsync overloads. Tip: Gemini 3 thinking models default to deep reasoning — set a lower ThinkingLevel for latency-sensitive calls.

Image generation

var response = await gemini.GenerateImageAsync("A watercolor painting of a lighthouse at sunset.");

foreach (var image in response.Images())
    await File.WriteAllBytesAsync($"lighthouse.{image.MimeType.Split('/')[1]}", image.Data);

Defaults to the efficient flash image model; pass Model = GeminiConstants.Models.Gemini3ProImage for higher quality, or ImageAspectRatio/ImageSize (Gemini 3+ image models) for finer control. See docs/articles/image-generation.md.

Embeddings

var embedding = await embeddings.EmbedContentAsync(
    "gemini-embedding-001", "Your text",
    new EmbeddingOptions { TaskType = GeminiConstants.EmbeddingTaskTypes.RetrievalDocument });

var batch = await embeddings.BatchEmbedContentAsync("gemini-embedding-001", texts);

Files API & context caching

// Upload a file, wait until it's processed, then reference it
var file = await files.UploadFileAsync(bytes, "video/mp4", "clip.mp4");
await files.WaitUntilActiveAsync(file.Name!);

// Cache a large reusable context, then reference it by name to save tokens
var cache = await caching.CreateAsync(new CachedContent
{
    Model = "models/gemini-2.5-flash",
    Contents = [ /* large shared context */ ],
    Ttl = "3600s",
});
var answer = await gemini.GenerateAsync("Summarize.", new GeminiRequestOptions { CachedContent = cache.Name });

Token counting & model info

var tokens = await gemini.CountTokensAsync("Your text");
Console.WriteLine(tokens.TotalTokens);

var models = await modelInfo.ListModelsAsync();

Cost governance

builder.Services.AddGemini(options =>
{
    options.Budget = new BudgetOptions
    {
        MaxCostPerDayUsd = 50.00m,     // reject the next call once today's real spend hits $50
        MaxCostPerRequestUsd = 2.00m,  // optional: reject a single call whose estimated cost is too high
    };
});

// Once today's (UTC) real recorded spend reaches $50, the next call throws before it's sent:
try
{
    var response = await gemini.GenerateAsync(prompt);
}
catch (GeminiBudgetExceededException ex)
{
    // ex.CurrentSpendUsd, ex.BudgetLimitUsd
}
catch (GeminiRequestCostExceededException ex)
{
    // A single request's estimated cost exceeded MaxCostPerRequestUsd.
    // ex.EstimatedCostUsd, ex.MaxCostPerRequestUsd
}

Microsoft.Extensions.AI integration

Use Gemini anywhere the .NET AI abstractions are consumed (Semantic Kernel, agent frameworks, middleware):

builder.Services.AddGemini(builder.Configuration.GetSection("Gemini"));
builder.Services.AddGeminiChatClient("gemini-2.5-flash");          // registers IChatClient
builder.Services.AddGeminiEmbeddingGenerator("gemini-embedding-001"); // registers IEmbeddingGenerator

// elsewhere:
public class MyService(IChatClient chat)
{
    public Task<ChatResponse> Ask(string q) =>
        chat.GetResponseAsync([new ChatMessage(ChatRole.User, q)]);
}

Observability (OpenTelemetry)

Traces and metrics are emitted via System.Diagnostics following the OTel GenAI conventions — no OpenTelemetry dependency is forced on you. Opt in:

builder.Services.AddOpenTelemetry()
    .WithTracing(t => t.AddSource(GeminiTelemetry.SourceName))
    .WithMetrics(m => m.AddMeter(GeminiTelemetry.SourceName));

You get per-call spans (gen_ai.system, gen_ai.request.model, token counts, finish reasons) and the gen_ai.client.operation.duration / gen_ai.client.token.usage metrics.

Resilience & rate limiting

Configured once, applied automatically:

builder.Services.AddGemini(options =>
{
    options.MaxRetries = 3;                       // retried on 429/5xx/transient with exponential backoff
    options.RetryBaseDelay = TimeSpan.FromSeconds(2);
    options.RateLimit.Enabled = true;             // client-side token bucket
    options.RateLimit.RequestsPerMinute = 60;
});

Failures surface as typed exceptions: GeminiApiException (status + parsed error), GeminiRateLimitException, GeminiTimeoutException, GeminiSerializationException, GeminiContentException — all deriving from GeminiException.

Cost governance

Cap what a Gemini integration can spend, and observe what it actually spends, without rolling your own token-counting and pricing math. As far as we've been able to find, no other .NET Gemini client offers this — see the footnote on the feature-comparison table at the top. Covers both non-streaming and streaming calls — a budget guardrail that silently didn't apply to StreamAsync/StreamChatAsync would let a runaway streaming loop blow through the budget completely unchecked.

builder.Services.AddGemini(options =>
{
    options.Budget = new BudgetOptions
    {
        MaxCostPerDayUsd = 50.00m,     // the primary, always-exact mechanism
        MaxCostPerRequestUsd = 2.00m,  // optional: reject one outsized call before it's sent
    };
});

Every response's real token usage (including cached-content and "thinking" tokens, priced correctly per Gemini's billing rules) is converted to a USD cost and recorded as the gemini.client.cost.usd OpenTelemetry metric. Once today's (UTC) cumulative actual spend reaches MaxCostPerDayUsd, the next call throws GeminiBudgetExceededException before it's sent — no network round-trip, no cost incurred by the rejected call itself. This is the primary, exact mechanism (built from real billed usage).

MaxCostPerRequestUsd is a secondary, best-effort estimate ceiling checked before a single call: it spends one extra CountTokensAsync round-trip to get an exact input-token count (skipped entirely when MaxCostPerRequestUsd is unset, so it costs nothing when you don't use it), bounds the output side only when you set MaxTokens, and throws GeminiRequestCostExceededException if the estimate exceeds the ceiling. It can't be exact the way the daily budget is — see Cost governance for exactly what it can and can't guarantee, the multi-instance caveat, pricing overrides, and full details.

Documentation & samples

  • Guides + full API reference: the docs/ DocFX site (Getting started, structured output, streaming, resilience, observability, M.E.AI, files & caching, cost governance, and a v5→v6 migration guide). Published to GitHub Pages via the Docs workflow.
  • Runnable sample: samples/Junaid.GoogleGemini.Net.AspNetCoreSample — a minimal ASP.NET Core API showing generation, GenerateAsync<T>, streaming, IChatClient, and OpenTelemetry.

Requirements

  • .NET 8.0, .NET 9.0, or any netstandard2.0 runtime (.NET Framework 4.6.1+, Mono, Unity)
  • A Google AI Studio API key

AI-assisted development

This library is heavily developed with AI, and we want to be transparent about that. The v5 → v6 modernization — architecture, code, tests, documentation, and this README — was carried out end-to-end by Claude (Anthropic's coding agent) under the maintainer's direction, rather than written by hand.

What that means for you:

  • Shipped behind guardrails. Changes go through an automated test suite and CI on every commit; the public API surface is documented and versioned with semantic versioning.
  • 6.0 is stable, validated live against the Gemini API, and used as the default install for existing users upgrading from 5.x. Still pin a version you've tested for your own use case, as with any dependency.
  • Transparency over polish. We'd rather tell you how the code is produced than hide it. If you spot something off, please open an issue.

Contributing & support

Issues and PRs welcome on GitHub. Licensed under MIT.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
6.2.0 102 8/9/2026
6.1.0 99 8/9/2026
6.0.0 129 6/10/2026
6.0.0-rc.1 66 6/10/2026
6.0.0-beta.4 65 6/10/2026
6.0.0-beta.3 74 6/10/2026
6.0.0-beta.2 65 6/10/2026
6.0.0-beta.1 72 6/8/2026
6.0.0-alpha.4 302 6/7/2026

6.2.0: version bump to track the core package (no functional changes here) — the core's
new cost-governance feature and CountTokensAsync model-overload fix don't affect this
adapter.
6.1.0: version bump to track the core package (no functional changes here) — neither the
core's API-key-format fix nor its new image-generation surface affects this adapter.
6.0.1/6.0.0: no functional changes since initial release (IChatClient/IEmbeddingGenerator
adapters over Junaid.GoogleGemini.Net).