Repository navigation
Model Tuning
Tuning adapts a base model to your data. TuningClient (namespace GenerativeAI.Clients) mirrors client.tunings
of the google-genai SDK and works on both platforms, but they use different resources:
| Gemini API | Vertex AI | |
|---|---|---|
| Resource | tunedModels |
projects/{project}/locations/{location}/tuningJobs |
| Training data | Inline TuningExamples (TextInput / Output) |
JSONL in Cloud Storage (GcsUri) or a Vertex AI dataset (VertexDatasetResource) |
| Methods | Supervised tuning | Supervised fine tuning, preference tuning, distillation, reinforcement tuning (TuningMethod) |
TuneAsync, GetAsync, CancelAsync, WaitForCompletionAsync
|
Yes | Yes |
ListAsync / ListAllAsync
|
No (NotSupportedException) |
Yes |
ValidateRewardAsync |
No (NotSupportedException) |
Yes |
| Hyperparameters |
EpochCount, LearningRateMultiplier, BatchSize, LearningRate
|
All CreateTuningJobConfig options |
On the Gemini API, tuned models are reported as TuningJob objects so the same code works on both platforms.
- NuGet package:
Google_GenerativeAI - Gemini API: an API key. Vertex AI: a project, credentials, and a regional location (for example
us-central1); tuning is a regional service. See Authentication.
using GenerativeAI;
using GenerativeAI.Clients;
using GenerativeAI.Types;
// Vertex AI
var vertexAi = new VertexAI(projectId, "us-central1");
TuningClient tuning = vertexAi.CreateTuningClient();
// Gemini API
var googleAi = new GoogleAi(apiKey);
TuningClient geminiTuning = googleAi.CreateTuningClient();TuningJob job = await tuning.TuneAsync(
"gemini-2.5-flash",
new TuningDataset { GcsUri = "gs://my-bucket/tuning/train.jsonl" },
new CreateTuningJobConfig
{
TunedModelDisplayName = "support-bot",
ValidationDataset = new TuningValidationDataset { GcsUri = "gs://my-bucket/tuning/validation.jsonl" },
EpochCount = 3,
LearningRateMultiplier = 1.0,
AdapterSize = AdapterSize.ADAPTER_SIZE_FOUR,
Labels = new Dictionary<string, string> { ["team"] = "support" }
});
Console.WriteLine($"{job.Name} {job.State}");Pick a base model that supports tuning in your region (see Google's list of tunable models).
Other CreateTuningJobConfig options include Method (TuningMethod.SUPERVISED_FINE_TUNING,
PREFERENCE_TUNING, DISTILLATION, REINFORCEMENT_TUNING), Description, ExportLastCheckpointOnly,
TuningMode, BatchSize, LearningRate, EvaluationConfig, OutputUri, EncryptionSpec, Beta (preference
tuning), BaseTeacherModel / TunedTeacherModelSource (distillation), and RewardConfig /
CompositeRewardConfig / SamplesPerPrompt (reinforcement tuning).
To continue tuning from an existing tuned model, pass its resource name (projects/.../models/...) as the base model
and set PreTunedModelCheckpointId.
The Gemini API takes inline examples:
TuningJob tuned = await geminiTuning.TuneAsync(
"models/your-tunable-base-model",
new TuningDataset
{
Examples = new List<TuningExample>
{
new() { TextInput = "1", Output = "2" },
new() { TextInput = "3", Output = "4" },
new() { TextInput = "-3", Output = "-2" }
}
},
new CreateTuningJobConfig
{
TunedModelDisplayName = "number generator",
EpochCount = 5,
BatchSize = 4,
LearningRate = 0.001
});
Console.WriteLine(tuned.Name); // "tunedModels/..."; State is JOB_STATE_QUEUEDOnly Name and State are set in the create response on the Gemini API. Vertex-only options (GcsUri,
VertexDatasetResource, ValidationDataset, Description, AdapterSize, Labels, reward settings and so on)
throw NotSupportedException.
var current = await tuning.GetAsync(job.Name!);
Console.WriteLine(current.State);
// Polls until succeeded, partially succeeded, failed, cancelled or expired.
var finished = await tuning.WaitForCompletionAsync(job.Name!,
pollInterval: TimeSpan.FromMinutes(1), // default 30 seconds
timeout: TimeSpan.FromHours(4)); // default: no limit
if (finished.State == JobState.JOB_STATE_SUCCEEDED)
{
Console.WriteLine(finished.TunedModel?.Model); // Vertex AI: the tuned model resource
Console.WriteLine(finished.TunedModel?.Endpoint); // Vertex AI: the endpoint to call
}
else
{
Console.WriteLine(finished.Error?.Message);
}
await tuning.CancelAsync(job.Name!);On Vertex AI the name can be the full resource name, tuningJobs/{id} or just the ID. On the Gemini API it is the
tuned model name (tunedModels/...) or its ID.
var page = await tuning.ListAsync(pageSize: 10);
foreach (var j in page.TuningJobs ?? new List<TuningJob>())
Console.WriteLine($"{j.Name} {j.State} {j.BaseModel}");
await foreach (var j in tuning.ListAllAsync(filter: null))
Console.WriteLine(j.Name);ValidateRewardAsync(sampleResponse, example, singleRewardConfig, compositeRewardConfig, ...) checks a reward
configuration against a sample response before you start a reinforcement tuning job.
- API errors are thrown as
GenerativeAI.Exceptions.ApiException(ErrorCode,ErrorStatus,ErrorMessage). On Vertex AI, aFAILED_PRECONDITIONor a message mentioning the region/location usually means the location doesn't support tuning (for exampleglobal); use a regional location. -
NotSupportedException: an option or method that the current platform doesn't support. -
ArgumentException: empty base model or job name, or an invalid configuration.
try
{
var job = await tuning.TuneAsync(baseModel, dataset, config);
}
catch (NotSupportedException ex)
{
Console.WriteLine($"Not available on this platform: {ex.Message}");
}
catch (ApiException ex)
{
Console.WriteLine($"{ex.ErrorCode} {ex.ErrorStatus}: {ex.ErrorMessage}");
}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