0
# Model Tuning (Experimental)
1
2
The Tunings module provides model fine-tuning capabilities, allowing you to create custom models adapted to your specific use cases through supervised fine-tuning.
3
4
## Capabilities
5
6
### tune
7
8
Create a supervised fine-tuning job.
9
10
```typescript { .api }
11
/**
12
* Create supervised fine-tuning job
13
* @param params - Tuning job creation parameters
14
* @returns Promise resolving to tuning job
15
*/
16
function tune(
17
params: CreateTuningJobParameters
18
): Promise<TuningJob>;
19
20
interface CreateTuningJobParameters {
21
/** Base model to tune (e.g., 'gemini-1.5-flash-001') */
22
baseModel: string;
23
/** Training dataset */
24
trainingDataset: TuningDataset;
25
/** Validation dataset (optional) */
26
validationDataset?: TuningDataset;
27
/** Tuning configuration */
28
config?: TuningConfig;
29
}
30
31
interface TuningJob {
32
/** Job name (unique identifier) */
33
name?: string;
34
/** Display name for tuned model */
35
tunedModelDisplayName?: string;
36
/** Base model used */
37
baseModel?: string;
38
/** Job state */
39
state?: JobState;
40
/** Creation timestamp */
41
createTime?: string;
42
/** Start timestamp */
43
startTime?: string;
44
/** End timestamp */
45
endTime?: string;
46
/** Training dataset */
47
trainingDataset?: TuningDataset;
48
/** Validation dataset */
49
validationDataset?: TuningDataset;
50
/** Hyperparameters */
51
hyperparameters?: Hyperparameters;
52
/** Resulting tuned model name */
53
tunedModel?: string;
54
/** Error if job failed */
55
error?: Status;
56
}
57
```
58
59
**Usage Examples:**
60
61
```typescript
62
import { GoogleGenAI } from '@google/genai';
63
64
const client = new GoogleGenAI({ apiKey: 'YOUR_API_KEY' });
65
66
// Prepare training data
67
const trainingExamples: TuningExample[] = [
68
{
69
contents: [
70
{ role: 'user', parts: [{ text: 'Input example 1' }] },
71
{ role: 'model', parts: [{ text: 'Output example 1' }] }
72
]
73
},
74
{
75
contents: [
76
{ role: 'user', parts: [{ text: 'Input example 2' }] },
77
{ role: 'model', parts: [{ text: 'Output example 2' }] }
78
]
79
}
80
// ... more examples
81
];
82
83
// Create tuning job
84
const tuningJob = await client.tunings.tune({
85
baseModel: 'gemini-1.5-flash-001',
86
trainingDataset: {
87
examples: trainingExamples
88
},
89
config: {
90
tunedModelDisplayName: 'My Custom Model',
91
epochCount: 3,
92
learningRate: 0.001
93
}
94
});
95
96
console.log('Tuning job created:', tuningJob.name);
97
console.log('State:', tuningJob.state);
98
```
99
100
### list
101
102
List tuning jobs with pagination.
103
104
```typescript { .api }
105
/**
106
* List tuning jobs
107
* @param params - List parameters
108
* @returns Promise resolving to pager of tuning jobs
109
*/
110
function list(
111
params?: ListTuningJobsParameters
112
): Promise<Pager<TuningJob>>;
113
114
interface ListTuningJobsParameters {
115
/** Page size */
116
pageSize?: number;
117
/** Page token for pagination */
118
pageToken?: string;
119
}
120
```
121
122
**Usage Examples:**
123
124
```typescript
125
// List all tuning jobs
126
const pager = await client.tunings.list({
127
pageSize: 10
128
});
129
130
for await (const job of pager) {
131
console.log(`Job: ${job.name}`);
132
console.log(` Display Name: ${job.tunedModelDisplayName}`);
133
console.log(` Base Model: ${job.baseModel}`);
134
console.log(` State: ${job.state}`);
135
console.log(` Created: ${job.createTime}`);
136
if (job.tunedModel) {
137
console.log(` Tuned Model: ${job.tunedModel}`);
138
}
139
console.log('');
140
}
141
```
142
143
### get
144
145
Get tuning job status and details.
146
147
```typescript { .api }
148
/**
149
* Get tuning job status
150
* @param params - Get parameters
151
* @returns Promise resolving to tuning job
152
*/
153
function get(
154
params: GetTuningJobParameters
155
): Promise<TuningJob>;
156
157
interface GetTuningJobParameters {
158
/** Tuning job name */
159
tuningJob: string;
160
}
161
```
162
163
**Usage Examples:**
164
165
```typescript
166
// Get job details
167
const job = await client.tunings.get({
168
tuningJob: 'tunedModels/my-model-abc123/operations/xyz'
169
});
170
171
console.log('Job state:', job.state);
172
console.log('Tuned model:', job.tunedModel);
173
174
// Poll until complete
175
while (
176
job.state === JobState.JOB_STATE_RUNNING ||
177
job.state === JobState.JOB_STATE_PENDING
178
) {
179
console.log('Tuning in progress...');
180
await new Promise(resolve => setTimeout(resolve, 30000)); // Wait 30s
181
182
const updated = await client.tunings.get({
183
tuningJob: job.name!
184
});
185
186
if (updated.state === JobState.JOB_STATE_SUCCEEDED) {
187
console.log('Tuning completed!');
188
console.log('Tuned model:', updated.tunedModel);
189
break;
190
} else if (updated.state === JobState.JOB_STATE_FAILED) {
191
console.error('Tuning failed:', updated.error);
192
break;
193
}
194
}
195
```
196
197
## Types
198
199
### TuningDataset
200
201
Training or validation dataset.
202
203
```typescript { .api }
204
interface TuningDataset {
205
/** Training examples */
206
examples?: TuningExample[];
207
}
208
209
interface TuningExample {
210
/** Example contents (user input + model output) */
211
contents?: Content[];
212
}
213
```
214
215
### TuningConfig
216
217
Configuration for tuning job.
218
219
```typescript { .api }
220
interface TuningConfig {
221
/** Display name for tuned model */
222
tunedModelDisplayName?: string;
223
/** Tuning mode */
224
tuningMode?: TuningMode;
225
/** Tuning method */
226
tuningMethod?: TuningMethod;
227
/** Tuning task */
228
tuningTask?: TuningTask;
229
/** Number of training epochs */
230
epochCount?: number;
231
/** Learning rate */
232
learningRate?: number;
233
/** Batch size */
234
batchSize?: number;
235
/** Learning rate multiplier */
236
learningRateMultiplier?: number;
237
}
238
```
239
240
### Hyperparameters
241
242
Hyperparameters used in tuning.
243
244
```typescript { .api }
245
interface Hyperparameters {
246
/** Number of epochs */
247
epochCount?: number;
248
/** Learning rate */
249
learningRate?: number;
250
/** Batch size */
251
batchSize?: number;
252
/** Learning rate multiplier */
253
learningRateMultiplier?: number;
254
}
255
```
256
257
### Enumerations
258
259
```typescript { .api }
260
enum TuningMode {
261
TUNING_MODE_UNSPECIFIED = 'TUNING_MODE_UNSPECIFIED',
262
/** Full model tuning */
263
FULL = 'FULL',
264
/** Parameter-efficient fine-tuning */
265
PEFT_ADAPTER = 'PEFT_ADAPTER'
266
}
267
268
enum TuningMethod {
269
TUNING_METHOD_UNSPECIFIED = 'TUNING_METHOD_UNSPECIFIED',
270
/** Supervised fine-tuning */
271
SUPERVISED_FINE_TUNING = 'SUPERVISED_FINE_TUNING',
272
/** Preference tuning */
273
PREFERENCE_TUNING = 'PREFERENCE_TUNING'
274
}
275
276
enum TuningTask {
277
TUNING_TASK_UNSPECIFIED = 'TUNING_TASK_UNSPECIFIED',
278
/** Image to video */
279
I2V = 'I2V',
280
/** Text to video */
281
T2V = 'T2V',
282
/** Reference to video */
283
R2V = 'R2V'
284
}
285
286
enum JobState {
287
JOB_STATE_UNSPECIFIED = 'JOB_STATE_UNSPECIFIED',
288
JOB_STATE_QUEUED = 'JOB_STATE_QUEUED',
289
JOB_STATE_PENDING = 'JOB_STATE_PENDING',
290
JOB_STATE_RUNNING = 'JOB_STATE_RUNNING',
291
JOB_STATE_SUCCEEDED = 'JOB_STATE_SUCCEEDED',
292
JOB_STATE_FAILED = 'JOB_STATE_FAILED',
293
JOB_STATE_CANCELLING = 'JOB_STATE_CANCELLING',
294
JOB_STATE_CANCELLED = 'JOB_STATE_CANCELLED'
295
}
296
```
297
298
## Complete Examples
299
300
### Basic Supervised Fine-Tuning
301
302
```typescript
303
import { GoogleGenAI, TuningExample } from '@google/genai';
304
import * as fs from 'fs';
305
306
const client = new GoogleGenAI({ apiKey: 'YOUR_API_KEY' });
307
308
// Load training data from JSONL file
309
const trainingData: TuningExample[] = fs
310
.readFileSync('./training-data.jsonl', 'utf-8')
311
.split('\n')
312
.filter(line => line.trim())
313
.map(line => JSON.parse(line));
314
315
console.log(`Loaded ${trainingData.length} training examples`);
316
317
// Create tuning job
318
const tuningJob = await client.tunings.tune({
319
baseModel: 'gemini-1.5-flash-001',
320
trainingDataset: {
321
examples: trainingData
322
},
323
config: {
324
tunedModelDisplayName: 'Customer Support Bot',
325
epochCount: 5,
326
learningRate: 0.001,
327
batchSize: 4
328
}
329
});
330
331
console.log('Tuning job started:', tuningJob.name);
332
333
// Poll for completion
334
async function pollTuningJob(jobName: string): Promise<TuningJob> {
335
let job = await client.tunings.get({ tuningJob: jobName });
336
337
while (
338
job.state === JobState.JOB_STATE_RUNNING ||
339
job.state === JobState.JOB_STATE_PENDING ||
340
job.state === JobState.JOB_STATE_QUEUED
341
) {
342
console.log(`Status: ${job.state}`);
343
await new Promise(resolve => setTimeout(resolve, 60000)); // Wait 1 minute
344
345
job = await client.tunings.get({ tuningJob: jobName });
346
}
347
348
return job;
349
}
350
351
const completed = await pollTuningJob(tuningJob.name!);
352
353
if (completed.state === JobState.JOB_STATE_SUCCEEDED) {
354
console.log('Tuning completed successfully!');
355
console.log('Tuned model:', completed.tunedModel);
356
357
// Use the tuned model
358
const response = await client.models.generateContent({
359
model: completed.tunedModel!,
360
contents: 'Test input for tuned model'
361
});
362
363
console.log('Response:', response.text);
364
} else {
365
console.error('Tuning failed:', completed.error);
366
}
367
```
368
369
### Tuning with Validation Dataset
370
371
```typescript
372
// Prepare training and validation data
373
const trainingExamples: TuningExample[] = [
374
// ... 80% of your data
375
];
376
377
const validationExamples: TuningExample[] = [
378
// ... 20% of your data
379
];
380
381
// Create tuning job with validation
382
const tuningJob = await client.tunings.tune({
383
baseModel: 'gemini-1.5-flash-001',
384
trainingDataset: {
385
examples: trainingExamples
386
},
387
validationDataset: {
388
examples: validationExamples
389
},
390
config: {
391
tunedModelDisplayName: 'Validated Custom Model',
392
epochCount: 10,
393
learningRate: 0.0005
394
}
395
});
396
397
console.log('Tuning with validation started:', tuningJob.name);
398
```
399
400
### Prepare Training Data
401
402
```typescript
403
// Helper to create training examples
404
function createTrainingExample(
405
userInput: string,
406
modelOutput: string
407
): TuningExample {
408
return {
409
contents: [
410
{
411
role: 'user',
412
parts: [{ text: userInput }]
413
},
414
{
415
role: 'model',
416
parts: [{ text: modelOutput }]
417
}
418
]
419
};
420
}
421
422
// Create training dataset
423
const examples: TuningExample[] = [
424
createTrainingExample(
425
'What are your business hours?',
426
'We are open Monday to Friday, 9 AM to 5 PM EST.'
427
),
428
createTrainingExample(
429
'How do I track my order?',
430
'You can track your order using the tracking link sent to your email.'
431
),
432
createTrainingExample(
433
'What is your return policy?',
434
'Items can be returned within 30 days of purchase with original receipt.'
435
),
436
// ... more examples
437
];
438
439
// Save to JSONL format
440
const jsonl = examples.map(ex => JSON.stringify(ex)).join('\n');
441
fs.writeFileSync('./training-data.jsonl', jsonl);
442
443
console.log(`Prepared ${examples.length} training examples`);
444
```
445
446
### Manage Tuning Jobs
447
448
```typescript
449
// List all tuning jobs
450
const jobs = await client.tunings.list();
451
452
const activeJobs: TuningJob[] = [];
453
const completedJobs: TuningJob[] = [];
454
const failedJobs: TuningJob[] = [];
455
456
for await (const job of jobs) {
457
if (job.state === JobState.JOB_STATE_RUNNING ||
458
job.state === JobState.JOB_STATE_PENDING) {
459
activeJobs.push(job);
460
} else if (job.state === JobState.JOB_STATE_SUCCEEDED) {
461
completedJobs.push(job);
462
} else if (job.state === JobState.JOB_STATE_FAILED) {
463
failedJobs.push(job);
464
}
465
}
466
467
console.log(`Active jobs: ${activeJobs.length}`);
468
console.log(`Completed jobs: ${completedJobs.length}`);
469
console.log(`Failed jobs: ${failedJobs.length}\n`);
470
471
// Display active jobs
472
if (activeJobs.length > 0) {
473
console.log('Active Tuning Jobs:');
474
activeJobs.forEach(job => {
475
console.log(` - ${job.tunedModelDisplayName}`);
476
console.log(` State: ${job.state}`);
477
console.log(` Started: ${job.startTime}`);
478
});
479
}
480
481
// Display completed models
482
if (completedJobs.length > 0) {
483
console.log('\nCompleted Tuned Models:');
484
completedJobs.forEach(job => {
485
console.log(` - ${job.tunedModelDisplayName}`);
486
console.log(` Model: ${job.tunedModel}`);
487
console.log(` Completed: ${job.endTime}`);
488
});
489
}
490
```
491
492
### Use Tuned Model
493
494
```typescript
495
// Get completed tuning job
496
const job = await client.tunings.get({
497
tuningJob: 'tunedModels/my-model-abc123/operations/xyz'
498
});
499
500
if (job.state === JobState.JOB_STATE_SUCCEEDED && job.tunedModel) {
501
console.log('Using tuned model:', job.tunedModel);
502
503
// Generate with tuned model
504
const response = await client.models.generateContent({
505
model: job.tunedModel,
506
contents: 'Input for tuned model'
507
});
508
509
console.log('Response:', response.text);
510
511
// Create chat with tuned model
512
const chat = client.chats.create({
513
model: job.tunedModel,
514
config: {
515
temperature: 0.7
516
}
517
});
518
519
const chatResponse = await chat.sendMessage({
520
message: 'Hello!'
521
});
522
523
console.log('Chat response:', chatResponse.text);
524
}
525
```
526
527
### Hyperparameter Tuning
528
529
```typescript
530
// Try different hyperparameter configurations
531
const configs = [
532
{ epochCount: 3, learningRate: 0.001, batchSize: 4 },
533
{ epochCount: 5, learningRate: 0.0005, batchSize: 8 },
534
{ epochCount: 10, learningRate: 0.0001, batchSize: 4 }
535
];
536
537
const tuningJobs = await Promise.all(
538
configs.map((config, index) =>
539
client.tunings.tune({
540
baseModel: 'gemini-1.5-flash-001',
541
trainingDataset: { examples: trainingData },
542
validationDataset: { examples: validationData },
543
config: {
544
tunedModelDisplayName: `Model Config ${index + 1}`,
545
...config
546
}
547
})
548
)
549
);
550
551
console.log(`Started ${tuningJobs.length} tuning experiments`);
552
553
// Monitor all jobs
554
const results = await Promise.all(
555
tuningJobs.map(job => pollTuningJob(job.name!))
556
);
557
558
// Compare results
559
console.log('\nResults:');
560
results.forEach((result, index) => {
561
console.log(`Config ${index + 1}:`, configs[index]);
562
console.log(` State: ${result.state}`);
563
if (result.tunedModel) {
564
console.log(` Model: ${result.tunedModel}`);
565
}
566
});
567
```
568
569
### Error Handling and Retry
570
571
```typescript
572
async function tuneWithRetry(
573
params: CreateTuningJobParameters,
574
maxRetries: number = 3
575
): Promise<TuningJob> {
576
for (let attempt = 1; attempt <= maxRetries; attempt++) {
577
try {
578
console.log(`Attempt ${attempt}/${maxRetries}`);
579
580
const job = await client.tunings.tune(params);
581
const completed = await pollTuningJob(job.name!);
582
583
if (completed.state === JobState.JOB_STATE_SUCCEEDED) {
584
console.log('Tuning succeeded');
585
return completed;
586
} else if (completed.state === JobState.JOB_STATE_FAILED) {
587
throw new Error(`Tuning failed: ${completed.error?.message}`);
588
}
589
} catch (error) {
590
console.error(`Attempt ${attempt} failed:`, error);
591
592
if (attempt === maxRetries) {
593
throw error;
594
}
595
596
// Exponential backoff
597
const backoffMs = Math.pow(2, attempt) * 60000; // Minutes
598
console.log(`Retrying in ${backoffMs / 60000} minutes...`);
599
await new Promise(resolve => setTimeout(resolve, backoffMs));
600
}
601
}
602
603
throw new Error('Max retries exceeded');
604
}
605
606
// Use with retry
607
try {
608
const result = await tuneWithRetry({
609
baseModel: 'gemini-1.5-flash-001',
610
trainingDataset: { examples: trainingData },
611
config: {
612
tunedModelDisplayName: 'Robust Model',
613
epochCount: 5
614
}
615
});
616
617
console.log('Final tuned model:', result.tunedModel);
618
} catch (error) {
619
console.error('Tuning failed after retries:', error);
620
}
621
```
622
623
### Data Validation
624
625
```typescript
626
function validateTrainingData(examples: TuningExample[]): boolean {
627
if (examples.length < 10) {
628
console.error('Need at least 10 training examples');
629
return false;
630
}
631
632
for (let i = 0; i < examples.length; i++) {
633
const example = examples[i];
634
635
if (!example.contents || example.contents.length !== 2) {
636
console.error(`Example ${i}: Must have exactly 2 content items (user and model)`);
637
return false;
638
}
639
640
const [userContent, modelContent] = example.contents;
641
642
if (userContent.role !== 'user') {
643
console.error(`Example ${i}: First content must be 'user' role`);
644
return false;
645
}
646
647
if (modelContent.role !== 'model') {
648
console.error(`Example ${i}: Second content must be 'model' role`);
649
return false;
650
}
651
652
if (!userContent.parts?.[0]?.text) {
653
console.error(`Example ${i}: User content must have text`);
654
return false;
655
}
656
657
if (!modelContent.parts?.[0]?.text) {
658
console.error(`Example ${i}: Model content must have text`);
659
return false;
660
}
661
}
662
663
console.log(`Validated ${examples.length} examples successfully`);
664
return true;
665
}
666
667
// Validate before tuning
668
if (validateTrainingData(trainingExamples)) {
669
const job = await client.tunings.tune({
670
baseModel: 'gemini-1.5-flash-001',
671
trainingDataset: { examples: trainingExamples },
672
config: {
673
tunedModelDisplayName: 'Validated Model'
674
}
675
});
676
}
677
```
678
679
### Track Multiple Tuning Jobs
680
681
```typescript
682
class TuningJobTracker {
683
private jobs: Map<string, TuningJob> = new Map();
684
685
async startJob(params: CreateTuningJobParameters): Promise<string> {
686
const job = await client.tunings.tune(params);
687
this.jobs.set(job.name!, job);
688
console.log(`Started job: ${job.name}`);
689
return job.name!;
690
}
691
692
async checkAll(): Promise<void> {
693
console.log(`\nChecking ${this.jobs.size} jobs...`);
694
695
for (const [name, _] of this.jobs) {
696
const updated = await client.tunings.get({ tuningJob: name });
697
this.jobs.set(name, updated);
698
699
console.log(`${updated.tunedModelDisplayName}: ${updated.state}`);
700
701
if (updated.state === JobState.JOB_STATE_SUCCEEDED) {
702
console.log(` ✓ Model ready: ${updated.tunedModel}`);
703
} else if (updated.state === JobState.JOB_STATE_FAILED) {
704
console.log(` ✗ Failed: ${updated.error?.message}`);
705
}
706
}
707
}
708
709
getCompleted(): TuningJob[] {
710
return Array.from(this.jobs.values()).filter(
711
job => job.state === JobState.JOB_STATE_SUCCEEDED
712
);
713
}
714
715
getActive(): TuningJob[] {
716
return Array.from(this.jobs.values()).filter(
717
job => job.state === JobState.JOB_STATE_RUNNING ||
718
job.state === JobState.JOB_STATE_PENDING
719
);
720
}
721
}
722
723
// Use tracker
724
const tracker = new TuningJobTracker();
725
726
await tracker.startJob({
727
baseModel: 'gemini-1.5-flash-001',
728
trainingDataset: { examples: dataset1 },
729
config: { tunedModelDisplayName: 'Model 1' }
730
});
731
732
await tracker.startJob({
733
baseModel: 'gemini-1.5-flash-001',
734
trainingDataset: { examples: dataset2 },
735
config: { tunedModelDisplayName: 'Model 2' }
736
});
737
738
// Monitor progress
739
const checkInterval = setInterval(async () => {
740
await tracker.checkAll();
741
742
if (tracker.getActive().length === 0) {
743
clearInterval(checkInterval);
744
console.log('\nAll jobs completed');
745
746
const completed = tracker.getCompleted();
747
console.log(`${completed.length} models ready to use`);
748
}
749
}, 60000); // Check every minute
750
```
751