Skip to content

Commit 5a10894

Browse files
GuinersGuinersgericdongmsampathkumar
authored
feat(genai): Sample/tuning (#4179)
* adding samples, test, lints * adding samples, test, lints * adding samples, test, lints * adding samples, test, lints * adding samples, test, lints * adding samples, test, lints * fixing functions names * adding new samples --------- Co-authored-by: Guiners <[email protected]> Co-authored-by: Eric Dong <[email protected]> Co-authored-by: Sampath Kumar <[email protected]>
1 parent 90fd0b4 commit 5a10894

9 files changed

+435
-1
lines changed

genai/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"chai": "^4.5.0",
2424
"mocha": "^10.0.0",
2525
"sinon": "^18.0.0",
26-
"uuid": "^10.0.0"
26+
"uuid": "^10.0.0",
27+
"proxyquire": "^2.1.3"
2728
}
2829
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
const {assert} = require('chai');
18+
const {describe, it} = require('mocha');
19+
const proxyquire = require('proxyquire').noCallThru();
20+
21+
const projectId = process.env.CAIP_PROJECT_ID;
22+
23+
describe('tuning-job-create', () => {
24+
it('should create tuning job and return job name', async function () {
25+
this.timeout(1000000);
26+
const mockTuningJob = {
27+
name: 'test-tuning-job',
28+
experiment: 'test-experiment',
29+
tunedModel: {
30+
model: 'test-model',
31+
endpoint: 'test-endpoint',
32+
},
33+
};
34+
35+
class MockTunings {
36+
async tune() {
37+
return mockTuningJob;
38+
}
39+
async get() {}
40+
}
41+
42+
class MockGoogleGenAI {
43+
constructor() {
44+
this.tunings = new MockTunings();
45+
}
46+
}
47+
48+
const sample = proxyquire('../tuning/tuning-job-create.js', {
49+
'@google/genai': {GoogleGenAI: MockGoogleGenAI},
50+
});
51+
52+
const response = await sample.createTuningJob(projectId);
53+
54+
assert.strictEqual(response, 'test-tuning-job');
55+
});
56+
});

genai/test/tuning-job-get.test.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
const {assert} = require('chai');
18+
const {describe, it} = require('mocha');
19+
const proxyquire = require('proxyquire').noCallThru();
20+
21+
const projectId = process.env.GOOGLE_CLOUD_PROJECT || 'test-project';
22+
23+
describe('tuning-job-get', () => {
24+
it('should get tuning job and return job name', async function () {
25+
this.timeout(1000000);
26+
27+
const mockTuningJob = {
28+
name: 'test-tuning-job',
29+
experiment: 'test-experiment',
30+
tunedModel: {
31+
model: 'test-model',
32+
endpoint: 'test-endpoint',
33+
},
34+
};
35+
36+
class MockTunings {
37+
async get({name}) {
38+
if (name !== 'TestJobName')
39+
throw new Error('Unexpected tuning job name');
40+
return mockTuningJob;
41+
}
42+
}
43+
44+
class MockGoogleGenAI {
45+
constructor() {
46+
this.tunings = new MockTunings();
47+
}
48+
}
49+
50+
const sample = proxyquire('../tuning/tuning-job-get.js', {
51+
'@google/genai': {GoogleGenAI: MockGoogleGenAI},
52+
});
53+
54+
const response = await sample.getTuningJob('TestJobName', projectId);
55+
56+
assert.strictEqual(response, 'test-tuning-job');
57+
});
58+
});

genai/test/tuning-job-list.test.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
const {assert} = require('chai');
18+
const {describe, it} = require('mocha');
19+
20+
const projectId = process.env.CAIP_PROJECT_ID;
21+
const sample = require('../tuning/tuning-job-list.js');
22+
23+
describe('tuning-job-list', () => {
24+
it('should return tuning job list', async () => {
25+
const output = await sample.listTuningJobs(projectId);
26+
assert(output);
27+
});
28+
});
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// You may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
const {assert} = require('chai');
18+
const {describe, it} = require('mocha');
19+
const proxyquire = require('proxyquire').noCallThru();
20+
21+
const projectId = process.env.CAIP_PROJECT_ID;
22+
23+
describe('tuning-textgen-with-txt', () => {
24+
it('should fetch tuning job and generate content', async function () {
25+
this.timeout(1000000);
26+
27+
const mockTuningJob = {
28+
name: 'test-tuning-job',
29+
experiment: 'test-experiment',
30+
tunedModel: {
31+
model: 'test-model',
32+
endpoint: 'test-endpoint',
33+
},
34+
};
35+
36+
const mockGenerateContentResult = {
37+
text: 'Because it is hot and glowing!',
38+
};
39+
40+
class MockTunings {
41+
async get() {
42+
return mockTuningJob;
43+
}
44+
}
45+
46+
class MockModels {
47+
async generateContent() {
48+
return mockGenerateContentResult;
49+
}
50+
}
51+
52+
class MockGoogleGenAI {
53+
constructor() {
54+
this.tunings = new MockTunings();
55+
this.models = new MockModels();
56+
}
57+
}
58+
59+
const sample = proxyquire('../tuning/tuning-textgen-with-txt.js', {
60+
'@google/genai': {GoogleGenAI: MockGoogleGenAI},
61+
});
62+
63+
const response = await sample.generateContent(projectId);
64+
65+
assert.strictEqual(response, 'Because it is hot and glowing!');
66+
});
67+
});

genai/tuning/tuning-job-create.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
// [START googlegenaisdk_tuning_job_create]
18+
const {GoogleGenAI} = require('@google/genai');
19+
20+
const GOOGLE_CLOUD_PROJECT = process.env.GOOGLE_CLOUD_PROJECT;
21+
const GOOGLE_CLOUD_LOCATION = process.env.GOOGLE_CLOUD_LOCATION || 'global';
22+
23+
async function createTuningJob(
24+
projectId = GOOGLE_CLOUD_PROJECT,
25+
location = GOOGLE_CLOUD_LOCATION
26+
) {
27+
const client = new GoogleGenAI({
28+
vertexai: true,
29+
project: projectId,
30+
location: location,
31+
});
32+
33+
function sleep(ms) {
34+
return new Promise(resolve => setTimeout(resolve, ms));
35+
}
36+
37+
let tuningJob = await client.tunings.tune({
38+
baseModel: 'gemini-2.5-flash',
39+
trainingDataset: {
40+
gcsUri:
41+
'gs://cloud-samples-data/ai-platform/generative_ai/gemini/text/sft_train_data.jsonl',
42+
},
43+
config: {
44+
tunedModelDisplayName: 'Example tuning job',
45+
},
46+
});
47+
console.log('Created tuning job:', tuningJob);
48+
49+
const runningStates = new Set(['JOB_STATE_PENDING', 'JOB_STATE_RUNNING']);
50+
51+
while (runningStates.has(tuningJob.state)) {
52+
console.log(`Job state: ${tuningJob.state}`);
53+
tuningJob = await client.tunings.get({name: tuningJob.name});
54+
await sleep(60000);
55+
}
56+
57+
console.log(tuningJob.tunedModel.model);
58+
console.log(tuningJob.tunedModel.endpoint);
59+
console.log(tuningJob.experiment);
60+
61+
// Example response:
62+
// projects/123456789012/locations/us-central1/models/1234567890@1
63+
// projects/123456789012/locations/us-central1/endpoints/123456789012345
64+
// projects/123456789012/locations/us-central1/metadataStores/default/contexts/tuning-experiment-2025010112345678
65+
66+
return tuningJob.name;
67+
}
68+
// [END googlegenaisdk_tuning_job_create]
69+
70+
module.exports = {
71+
createTuningJob,
72+
};

genai/tuning/tuning-job-get.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
// [START googlegenaisdk_tuning_job_get]
18+
const {GoogleGenAI} = require('@google/genai');
19+
const GOOGLE_CLOUD_PROJECT = process.env.GOOGLE_CLOUD_PROJECT;
20+
const GOOGLE_CLOUD_LOCATION = process.env.GOOGLE_CLOUD_LOCATION || 'global';
21+
const TUNING_JOB_NAME = 'TestJobName';
22+
23+
async function getTuningJob(
24+
tuningJobName = TUNING_JOB_NAME,
25+
projectId = GOOGLE_CLOUD_PROJECT,
26+
location = GOOGLE_CLOUD_LOCATION
27+
) {
28+
const client = new GoogleGenAI({
29+
vertexai: true,
30+
project: projectId,
31+
location: location,
32+
});
33+
34+
// Get the tuning job and the tuned model.
35+
const tuningJob = await client.tunings.get({name: tuningJobName});
36+
37+
console.log(tuningJob.tunedModel.model);
38+
console.log(tuningJob.tunedModel.endpoint);
39+
console.log(tuningJob.experiment);
40+
41+
// Example response:
42+
// projects/123456789012/locations/us-central1/models/1234567890@1
43+
// projects/123456789012/locations/us-central1/endpoints/123456789012345
44+
// projects/123456789012/locations/us-central1/metadataStores/default/contexts/tuning-experiment-2025010112345678
45+
46+
return tuningJob.name;
47+
}
48+
// [END googlegenaisdk_tuning_job_get]
49+
50+
module.exports = {
51+
getTuningJob,
52+
};

genai/tuning/tuning-job-list.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
// [START googlegenaisdk_tuning_job_list]
18+
const {GoogleGenAI} = require('@google/genai');
19+
const GOOGLE_CLOUD_PROJECT = process.env.GOOGLE_CLOUD_PROJECT;
20+
const GOOGLE_CLOUD_LOCATION = process.env.GOOGLE_CLOUD_LOCATION || 'global';
21+
22+
async function listTuningJobs(
23+
projectId = GOOGLE_CLOUD_PROJECT,
24+
location = GOOGLE_CLOUD_LOCATION
25+
) {
26+
const client = new GoogleGenAI({
27+
vertexai: true,
28+
project: projectId,
29+
location: location,
30+
});
31+
32+
const responses = await client.tunings.list();
33+
34+
for await (const item of responses) {
35+
if (item.name && item.name.includes('/tuningJobs/')) {
36+
console.log(item.name);
37+
// Example response:
38+
// projects/123456789012/locations/us-central1/tuningJobs/123456789012345
39+
}
40+
}
41+
42+
return responses;
43+
}
44+
// [END googlegenaisdk_tuning_job_list]
45+
46+
module.exports = {
47+
listTuningJobs,
48+
};

0 commit comments

Comments
 (0)