From 0daffbdadb5148929f809e803393b8626ccc8770 Mon Sep 17 00:00:00 2001 From: Ye Yuan Date: Fri, 3 Oct 2025 10:50:50 -0700 Subject: [PATCH 1/8] feat: add code samples for continuous tuning --- genai/tuning/continuous_tuning_create.py | 78 ++++++++++++++++++++++++ genai/tuning/requirements.txt | 2 +- genai/tuning/test_tuning_examples.py | 21 +++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 genai/tuning/continuous_tuning_create.py diff --git a/genai/tuning/continuous_tuning_create.py b/genai/tuning/continuous_tuning_create.py new file mode 100644 index 00000000000..bf262a220f7 --- /dev/null +++ b/genai/tuning/continuous_tuning_create.py @@ -0,0 +1,78 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def create_continuous_tuning_job(tuned_model_name: str, checkpoint_id: str) -> str: + # [START googlegenaisdk_continuous_tuning_create] + import time + + from google import genai + from google.genai.types import HttpOptions, TuningDataset, CreateTuningJobConfig + + # TODO(developer): Update and un-comment below line + # tuned_model_name = "projects/123456789012/locations/us-central1/models/1234567890@1" + # checkpoint_id = "1" + + client = genai.Client(http_options=HttpOptions(api_version="v1beta1")) + + training_dataset = TuningDataset( + gcs_uri="gs://cloud-samples-data/ai-platform/generative_ai/gemini/text/sft_train_data.jsonl", + ) + validation_dataset = TuningDataset( + gcs_uri="gs://cloud-samples-data/ai-platform/generative_ai/gemini/text/sft_validation_data.jsonl", + ) + + tuning_job = client.tunings.tune( + base_model=tuned_model_name, + training_dataset=training_dataset, + config=CreateTuningJobConfig( + tuned_model_display_name="Example tuning job", + validation_dataset=validation_dataset, + pre_tuned_model_checkpoint_id=checkpoint_id, + ), + ) + + running_states = set([ + "JOB_STATE_PENDING", + "JOB_STATE_RUNNING", + ]) + + while tuning_job.state in running_states: + print(tuning_job.state) + tuning_job = client.tunings.get(name=tuning_job.name) + time.sleep(60) + + print(tuning_job.tuned_model.model) + print(tuning_job.tuned_model.endpoint) + print(tuning_job.experiment) + # Example response: + # projects/123456789012/locations/us-central1/models/1234567890@2 + # projects/123456789012/locations/us-central1/endpoints/123456789012345 + # projects/123456789012/locations/us-central1/metadataStores/default/contexts/tuning-experiment-2025010112345678 + + if tuning_job.tuned_model.checkpoints: + for i, checkpoint in enumerate(tuning_job.tuned_model.checkpoints): + print(f"Checkpoint {i + 1}: ", checkpoint) + # Example response: + # Checkpoint 1: checkpoint_id='1' epoch=1 step=10 endpoint='projects/123456789012/locations/us-central1/endpoints/123456789000000' + # Checkpoint 2: checkpoint_id='2' epoch=2 step=20 endpoint='projects/123456789012/locations/us-central1/endpoints/123456789012345' + + # [END googlegenaisdk_continuous_tuning_create] + return tuning_job.name + + +if __name__ == "__main__": + pre_tuned_model_name = input("Pre-tuned model name: ") + pre_tuned_model_checkpoint_id = input("Pre-tuned model checkpoint id: ") + create_continuous_tuning_job(pre_tuned_model_name, pre_tuned_model_checkpoint_id) \ No newline at end of file diff --git a/genai/tuning/requirements.txt b/genai/tuning/requirements.txt index d64aa5a57b1..391bcb9cc1f 100644 --- a/genai/tuning/requirements.txt +++ b/genai/tuning/requirements.txt @@ -1 +1 @@ -google-genai==1.30.0 +google-genai==1.39.1 diff --git a/genai/tuning/test_tuning_examples.py b/genai/tuning/test_tuning_examples.py index 0974c769483..17cd5cc766f 100644 --- a/genai/tuning/test_tuning_examples.py +++ b/genai/tuning/test_tuning_examples.py @@ -20,6 +20,7 @@ from google.genai import types import pytest +import continuous_tuning_create import tuning_job_create import tuning_job_get import tuning_job_list @@ -306,3 +307,23 @@ def test_tuning_with_checkpoints_textgen_with_txt(mock_genai_client: MagicMock) call(model="test-endpoint-1", contents="Why is the sky blue?"), call(model="test-endpoint-2", contents="Why is the sky blue?"), ] + + +@patch("google.genai.Client") +def test_continuous_tuning_create(mock_genai_client: MagicMock) -> None: + # Mock the API response + mock_tuning_job = types.TuningJob( + name="test-tuning-job", + experiment="test-experiment", + tuned_model=types.TunedModel( + model="test-model-2", + endpoint="test-endpoint" + ) + ) + mock_genai_client.return_value.tunings.tune.return_value = mock_tuning_job + + response = continuous_tuning_create.create_continuous_tuning_job(tuned_model_name="test-model", checkpoint_id="1") + + mock_genai_client.assert_called_once_with(http_options=types.HttpOptions(api_version="v1beta1")) + mock_genai_client.return_value.tunings.tune.assert_called_once() + assert response == "test-tuning-job" \ No newline at end of file From 640a23a93af486448eb3e357911af260456a21e0 Mon Sep 17 00:00:00 2001 From: Ye Yuan Date: Fri, 3 Oct 2025 10:50:50 -0700 Subject: [PATCH 2/8] feat: add code samples for continuous tuning --- genai/tuning/continuous_tuning_create.py | 78 ++++++++++++++++++++++++ genai/tuning/requirements.txt | 2 +- genai/tuning/test_tuning_examples.py | 21 +++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 genai/tuning/continuous_tuning_create.py diff --git a/genai/tuning/continuous_tuning_create.py b/genai/tuning/continuous_tuning_create.py new file mode 100644 index 00000000000..bf262a220f7 --- /dev/null +++ b/genai/tuning/continuous_tuning_create.py @@ -0,0 +1,78 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def create_continuous_tuning_job(tuned_model_name: str, checkpoint_id: str) -> str: + # [START googlegenaisdk_continuous_tuning_create] + import time + + from google import genai + from google.genai.types import HttpOptions, TuningDataset, CreateTuningJobConfig + + # TODO(developer): Update and un-comment below line + # tuned_model_name = "projects/123456789012/locations/us-central1/models/1234567890@1" + # checkpoint_id = "1" + + client = genai.Client(http_options=HttpOptions(api_version="v1beta1")) + + training_dataset = TuningDataset( + gcs_uri="gs://cloud-samples-data/ai-platform/generative_ai/gemini/text/sft_train_data.jsonl", + ) + validation_dataset = TuningDataset( + gcs_uri="gs://cloud-samples-data/ai-platform/generative_ai/gemini/text/sft_validation_data.jsonl", + ) + + tuning_job = client.tunings.tune( + base_model=tuned_model_name, + training_dataset=training_dataset, + config=CreateTuningJobConfig( + tuned_model_display_name="Example tuning job", + validation_dataset=validation_dataset, + pre_tuned_model_checkpoint_id=checkpoint_id, + ), + ) + + running_states = set([ + "JOB_STATE_PENDING", + "JOB_STATE_RUNNING", + ]) + + while tuning_job.state in running_states: + print(tuning_job.state) + tuning_job = client.tunings.get(name=tuning_job.name) + time.sleep(60) + + print(tuning_job.tuned_model.model) + print(tuning_job.tuned_model.endpoint) + print(tuning_job.experiment) + # Example response: + # projects/123456789012/locations/us-central1/models/1234567890@2 + # projects/123456789012/locations/us-central1/endpoints/123456789012345 + # projects/123456789012/locations/us-central1/metadataStores/default/contexts/tuning-experiment-2025010112345678 + + if tuning_job.tuned_model.checkpoints: + for i, checkpoint in enumerate(tuning_job.tuned_model.checkpoints): + print(f"Checkpoint {i + 1}: ", checkpoint) + # Example response: + # Checkpoint 1: checkpoint_id='1' epoch=1 step=10 endpoint='projects/123456789012/locations/us-central1/endpoints/123456789000000' + # Checkpoint 2: checkpoint_id='2' epoch=2 step=20 endpoint='projects/123456789012/locations/us-central1/endpoints/123456789012345' + + # [END googlegenaisdk_continuous_tuning_create] + return tuning_job.name + + +if __name__ == "__main__": + pre_tuned_model_name = input("Pre-tuned model name: ") + pre_tuned_model_checkpoint_id = input("Pre-tuned model checkpoint id: ") + create_continuous_tuning_job(pre_tuned_model_name, pre_tuned_model_checkpoint_id) \ No newline at end of file diff --git a/genai/tuning/requirements.txt b/genai/tuning/requirements.txt index d64aa5a57b1..391bcb9cc1f 100644 --- a/genai/tuning/requirements.txt +++ b/genai/tuning/requirements.txt @@ -1 +1 @@ -google-genai==1.30.0 +google-genai==1.39.1 diff --git a/genai/tuning/test_tuning_examples.py b/genai/tuning/test_tuning_examples.py index 0974c769483..17cd5cc766f 100644 --- a/genai/tuning/test_tuning_examples.py +++ b/genai/tuning/test_tuning_examples.py @@ -20,6 +20,7 @@ from google.genai import types import pytest +import continuous_tuning_create import tuning_job_create import tuning_job_get import tuning_job_list @@ -306,3 +307,23 @@ def test_tuning_with_checkpoints_textgen_with_txt(mock_genai_client: MagicMock) call(model="test-endpoint-1", contents="Why is the sky blue?"), call(model="test-endpoint-2", contents="Why is the sky blue?"), ] + + +@patch("google.genai.Client") +def test_continuous_tuning_create(mock_genai_client: MagicMock) -> None: + # Mock the API response + mock_tuning_job = types.TuningJob( + name="test-tuning-job", + experiment="test-experiment", + tuned_model=types.TunedModel( + model="test-model-2", + endpoint="test-endpoint" + ) + ) + mock_genai_client.return_value.tunings.tune.return_value = mock_tuning_job + + response = continuous_tuning_create.create_continuous_tuning_job(tuned_model_name="test-model", checkpoint_id="1") + + mock_genai_client.assert_called_once_with(http_options=types.HttpOptions(api_version="v1beta1")) + mock_genai_client.return_value.tunings.tune.assert_called_once() + assert response == "test-tuning-job" \ No newline at end of file From 096f6a78465dd5b14a1be2eb10a427aed1246010 Mon Sep 17 00:00:00 2001 From: yeyuanyyg Date: Fri, 3 Oct 2025 11:22:40 -0700 Subject: [PATCH 3/8] Update continuous_tuning_create.py --- genai/tuning/continuous_tuning_create.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genai/tuning/continuous_tuning_create.py b/genai/tuning/continuous_tuning_create.py index bf262a220f7..3e1ad9f52e3 100644 --- a/genai/tuning/continuous_tuning_create.py +++ b/genai/tuning/continuous_tuning_create.py @@ -75,4 +75,4 @@ def create_continuous_tuning_job(tuned_model_name: str, checkpoint_id: str) -> s if __name__ == "__main__": pre_tuned_model_name = input("Pre-tuned model name: ") pre_tuned_model_checkpoint_id = input("Pre-tuned model checkpoint id: ") - create_continuous_tuning_job(pre_tuned_model_name, pre_tuned_model_checkpoint_id) \ No newline at end of file + create_continuous_tuning_job(pre_tuned_model_name, pre_tuned_model_checkpoint_id) From 2418bec6ce6f5ad9fc14d477708ef87068a1c8eb Mon Sep 17 00:00:00 2001 From: yeyuanyyg Date: Fri, 3 Oct 2025 11:23:19 -0700 Subject: [PATCH 4/8] Update test_tuning_examples.py --- genai/tuning/test_tuning_examples.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genai/tuning/test_tuning_examples.py b/genai/tuning/test_tuning_examples.py index 17cd5cc766f..2f54d55a0b0 100644 --- a/genai/tuning/test_tuning_examples.py +++ b/genai/tuning/test_tuning_examples.py @@ -326,4 +326,4 @@ def test_continuous_tuning_create(mock_genai_client: MagicMock) -> None: mock_genai_client.assert_called_once_with(http_options=types.HttpOptions(api_version="v1beta1")) mock_genai_client.return_value.tunings.tune.assert_called_once() - assert response == "test-tuning-job" \ No newline at end of file + assert response == "test-tuning-job" From 5b4b506e1ca644e66fde62034d1e2d1516077ffc Mon Sep 17 00:00:00 2001 From: yeyuanyyg Date: Tue, 7 Oct 2025 12:18:32 -0700 Subject: [PATCH 5/8] Update requirements.txt --- genai/tuning/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genai/tuning/requirements.txt b/genai/tuning/requirements.txt index 391bcb9cc1f..5f6e4dde04f 100644 --- a/genai/tuning/requirements.txt +++ b/genai/tuning/requirements.txt @@ -1 +1 @@ -google-genai==1.39.1 +google-genai==1.41.0 From 2052afbf4102d192f8b0704e56883f9201534137 Mon Sep 17 00:00:00 2001 From: yeyuanyyg Date: Tue, 7 Oct 2025 12:22:26 -0700 Subject: [PATCH 6/8] Rename continuous_tuning_create.py to tuning_with_pretuned_model.py --- ...{continuous_tuning_create.py => tuning_with_pretuned_model.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename genai/tuning/{continuous_tuning_create.py => tuning_with_pretuned_model.py} (100%) diff --git a/genai/tuning/continuous_tuning_create.py b/genai/tuning/tuning_with_pretuned_model.py similarity index 100% rename from genai/tuning/continuous_tuning_create.py rename to genai/tuning/tuning_with_pretuned_model.py From 008eaf1c064414780ab218bb1e9095b9c13d36d3 Mon Sep 17 00:00:00 2001 From: yeyuanyyg Date: Tue, 7 Oct 2025 12:24:10 -0700 Subject: [PATCH 7/8] Update test_tuning_examples.py --- genai/tuning/test_tuning_examples.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/genai/tuning/test_tuning_examples.py b/genai/tuning/test_tuning_examples.py index 2f54d55a0b0..c0e6ec2864d 100644 --- a/genai/tuning/test_tuning_examples.py +++ b/genai/tuning/test_tuning_examples.py @@ -20,7 +20,6 @@ from google.genai import types import pytest -import continuous_tuning_create import tuning_job_create import tuning_job_get import tuning_job_list @@ -30,6 +29,7 @@ import tuning_with_checkpoints_list_checkpoints import tuning_with_checkpoints_set_default_checkpoint import tuning_with_checkpoints_textgen_with_txt +import tuning_with_pretuned_model GCS_OUTPUT_BUCKET = "python-docs-samples-tests" @@ -310,7 +310,7 @@ def test_tuning_with_checkpoints_textgen_with_txt(mock_genai_client: MagicMock) @patch("google.genai.Client") -def test_continuous_tuning_create(mock_genai_client: MagicMock) -> None: +def test_tuning_with_pretuned_model(mock_genai_client: MagicMock) -> None: # Mock the API response mock_tuning_job = types.TuningJob( name="test-tuning-job", @@ -322,7 +322,7 @@ def test_continuous_tuning_create(mock_genai_client: MagicMock) -> None: ) mock_genai_client.return_value.tunings.tune.return_value = mock_tuning_job - response = continuous_tuning_create.create_continuous_tuning_job(tuned_model_name="test-model", checkpoint_id="1") + response = tuning_with_pretuned_model.create_continuous_tuning_job(tuned_model_name="test-model", checkpoint_id="1") mock_genai_client.assert_called_once_with(http_options=types.HttpOptions(api_version="v1beta1")) mock_genai_client.return_value.tunings.tune.assert_called_once() From ec23d790f4d29558e21f8bd1832bef6a4b065df9 Mon Sep 17 00:00:00 2001 From: yeyuanyyg Date: Tue, 7 Oct 2025 12:28:57 -0700 Subject: [PATCH 8/8] Update tuning_with_pretuned_model.py --- genai/tuning/tuning_with_pretuned_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/genai/tuning/tuning_with_pretuned_model.py b/genai/tuning/tuning_with_pretuned_model.py index 3e1ad9f52e3..e6c096a7b5a 100644 --- a/genai/tuning/tuning_with_pretuned_model.py +++ b/genai/tuning/tuning_with_pretuned_model.py @@ -14,7 +14,7 @@ def create_continuous_tuning_job(tuned_model_name: str, checkpoint_id: str) -> str: - # [START googlegenaisdk_continuous_tuning_create] + # [START googlegenaisdk_tuning_with_pretuned_model] import time from google import genai @@ -68,7 +68,7 @@ def create_continuous_tuning_job(tuned_model_name: str, checkpoint_id: str) -> s # Checkpoint 1: checkpoint_id='1' epoch=1 step=10 endpoint='projects/123456789012/locations/us-central1/endpoints/123456789000000' # Checkpoint 2: checkpoint_id='2' epoch=2 step=20 endpoint='projects/123456789012/locations/us-central1/endpoints/123456789012345' - # [END googlegenaisdk_continuous_tuning_create] + # [END googlegenaisdk_tuning_with_pretuned_model] return tuning_job.name