Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 83 additions & 17 deletions tests/unit/vertexai/genai/test_prompt_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import vertexai
from vertexai._genai import prompt_optimizer
from vertexai._genai import prompts
from vertexai._genai import types
from google.genai import client
import pandas as pd
Expand Down Expand Up @@ -51,7 +52,10 @@ def test_prompt_optimizer_client(self):

@mock.patch.object(client.Client, "_get_api_client")
@mock.patch.object(prompt_optimizer.PromptOptimizer, "_create_custom_job_resource")
def test_prompt_optimizer_optimize(self, mock_custom_job, mock_client):
@mock.patch.object(prompts.Prompts, "_create_custom_job_resource")
def test_prompt_optimizer_optimize(
self, mock_custom_job_prompts, mock_custom_job_prompt_optimizer, mock_client
):
"""Test that prompt_optimizer.optimize method creates a custom job."""
test_client = vertexai.Client(project=_TEST_PROJECT, location=_TEST_LOCATION)
test_client.prompt_optimizer.optimize(
Expand All @@ -63,11 +67,14 @@ def test_prompt_optimizer_optimize(self, mock_custom_job, mock_client):
),
)
mock_client.assert_called_once()
mock_custom_job.assert_called_once()
mock_custom_job_prompts.assert_called_once()

@mock.patch.object(client.Client, "_get_api_client")
@mock.patch.object(prompt_optimizer.PromptOptimizer, "_create_custom_job_resource")
def test_prompt_optimizer_optimize_nano(self, mock_custom_job, mock_client):
@mock.patch.object(prompts.Prompts, "_create_custom_job_resource")
def test_prompt_optimizer_optimize_nano(
self, mock_custom_job_prompts, mock_custom_job_prompt_optimizer, mock_client
):
"""Test that prompt_optimizer.optimize method creates a custom job."""
test_client = vertexai.Client(project=_TEST_PROJECT, location=_TEST_LOCATION)
test_client.prompt_optimizer.optimize(
Expand All @@ -79,10 +86,10 @@ def test_prompt_optimizer_optimize_nano(self, mock_custom_job, mock_client):
),
)
mock_client.assert_called_once()
mock_custom_job.assert_called_once()
mock_custom_job_prompts.assert_called_once()

@mock.patch.object(client.Client, "_get_api_client")
@mock.patch.object(prompt_optimizer.PromptOptimizer, "_custom_optimize_prompt")
@mock.patch.object(prompts.Prompts, "_custom_optimize")
def test_prompt_optimizer_optimize_prompt(
self, mock_custom_optimize_prompt, mock_client
):
Expand All @@ -92,7 +99,66 @@ def test_prompt_optimizer_optimize_prompt(
mock_client.assert_called_once()
mock_custom_optimize_prompt.assert_called_once()

@mock.patch.object(prompt_optimizer.PromptOptimizer, "_custom_optimize_prompt")

class TestPrompts:
"""Unit tests for the Prompts client."""

def setup_method(self):
importlib.reload(vertexai)
vertexai.init(
project=_TEST_PROJECT,
location=_TEST_LOCATION,
)

@pytest.mark.usefixtures("google_auth_mock")
def test_prompt_optimizer_client(self):
test_client = vertexai.Client(project=_TEST_PROJECT, location=_TEST_LOCATION)
assert test_client.prompts is not None

@mock.patch.object(client.Client, "_get_api_client")
@mock.patch.object(prompts.Prompts, "_create_custom_job_resource")
def test_prompt_optimizer_optimize(self, mock_custom_job, mock_client):
"""Test that prompt_optimizer.optimize method creates a custom job."""
test_client = vertexai.Client(project=_TEST_PROJECT, location=_TEST_LOCATION)
test_client.prompts.launch_optimization_job(
method=types.PromptOptimizerMethod.VAPO,
config=types.PromptOptimizerConfig(
config_path="gs://ssusie-vapo-sdk-test/config.json",
wait_for_completion=False,
service_account="test-service-account",
),
)
mock_client.assert_called_once()
mock_custom_job.assert_called_once()

@mock.patch.object(client.Client, "_get_api_client")
@mock.patch.object(prompts.Prompts, "_create_custom_job_resource")
def test_prompt_optimizer_optimize_nano(self, mock_custom_job, mock_client):
"""Test that prompt_optimizer.optimize method creates a custom job."""
test_client = vertexai.Client(project=_TEST_PROJECT, location=_TEST_LOCATION)
test_client.prompts.launch_optimization_job(
method=types.PromptOptimizerMethod.OPTIMIZATION_TARGET_GEMINI_NANO,
config=types.PromptOptimizerConfig(
config_path="gs://ssusie-vapo-sdk-test/config.json",
wait_for_completion=False,
service_account="test-service-account",
),
)
mock_client.assert_called_once()
mock_custom_job.assert_called_once()

@mock.patch.object(client.Client, "_get_api_client")
@mock.patch.object(prompts.Prompts, "_custom_optimize")
def test_prompt_optimizer_optimize_prompt(
self, mock_custom_optimize_prompt, mock_client
):
"""Test that prompt_optimizer.optimize_prompt method calls optimize_prompt API."""
test_client = vertexai.Client(project=_TEST_PROJECT, location=_TEST_LOCATION)
test_client.prompts.optimize(prompt="test_prompt")
mock_client.assert_called_once()
mock_custom_optimize_prompt.assert_called_once()

@mock.patch.object(prompts.Prompts, "_custom_optimize")
def test_prompt_optimizer_optimize_few_shot(self, mock_custom_optimize_prompt):
"""Test that prompt_optimizer.optimize method for few shot optimizer."""
df = pd.DataFrame(
Expand All @@ -107,7 +173,7 @@ def test_prompt_optimizer_optimize_few_shot(self, mock_custom_optimize_prompt):
optimization_target=types.OptimizeTarget.OPTIMIZATION_TARGET_FEW_SHOT_TARGET_RESPONSE,
examples_dataframe=df,
)
test_client.prompt_optimizer.optimize_prompt(
test_client.prompts.optimize(
prompt="test_prompt",
config=test_config,
)
Expand All @@ -120,7 +186,7 @@ def test_prompt_optimizer_optimize_few_shot(self, mock_custom_optimize_prompt):
mock_kwargs["config"].examples_dataframe, test_config.examples_dataframe
)

@mock.patch.object(prompt_optimizer.PromptOptimizer, "_custom_optimize_prompt")
@mock.patch.object(prompts.Prompts, "_custom_optimize")
def test_prompt_optimizer_optimize_prompt_with_optimization_target(
self, mock_custom_optimize_prompt
):
Expand All @@ -129,7 +195,7 @@ def test_prompt_optimizer_optimize_prompt_with_optimization_target(
config = types.OptimizeConfig(
optimization_target=types.OptimizeTarget.OPTIMIZATION_TARGET_GEMINI_NANO,
)
test_client.prompt_optimizer.optimize_prompt(
test_client.prompts.optimize(
prompt="test_prompt",
config=config,
)
Expand All @@ -139,17 +205,17 @@ def test_prompt_optimizer_optimize_prompt_with_optimization_target(
)

@pytest.mark.asyncio
@mock.patch.object(prompt_optimizer.AsyncPromptOptimizer, "_custom_optimize_prompt")
@mock.patch.object(prompts.AsyncPrompts, "_custom_optimize")
async def test_async_prompt_optimizer_optimize_prompt(
self, mock_custom_optimize_prompt
):
"""Test that async prompt_optimizer.optimize_prompt method calls optimize_prompt API."""
test_client = vertexai.Client(project=_TEST_PROJECT, location=_TEST_LOCATION)
await test_client.aio.prompt_optimizer.optimize_prompt(prompt="test_prompt")
await test_client.aio.prompts.optimize(prompt="test_prompt")
mock_custom_optimize_prompt.assert_called_once()

@pytest.mark.asyncio
@mock.patch.object(prompt_optimizer.AsyncPromptOptimizer, "_custom_optimize_prompt")
@mock.patch.object(prompts.AsyncPrompts, "_custom_optimize")
async def test_async_prompt_optimizer_optimize_prompt_with_optimization_target(
self, mock_custom_optimize_prompt
):
Expand All @@ -158,7 +224,7 @@ async def test_async_prompt_optimizer_optimize_prompt_with_optimization_target(
config = types.OptimizeConfig(
optimization_target=types.OptimizeTarget.OPTIMIZATION_TARGET_GEMINI_NANO,
)
await test_client.aio.prompt_optimizer.optimize_prompt(
await test_client.aio.prompts.optimize(
prompt="test_prompt",
config=config,
)
Expand All @@ -168,7 +234,7 @@ async def test_async_prompt_optimizer_optimize_prompt_with_optimization_target(
)

@pytest.mark.asyncio
@mock.patch.object(prompt_optimizer.AsyncPromptOptimizer, "_custom_optimize_prompt")
@mock.patch.object(prompts.AsyncPrompts, "_custom_optimize")
async def test_async_prompt_optimizer_optimize_prompt_few_shot_target_response(
self, mock_custom_optimize_prompt
):
Expand All @@ -185,7 +251,7 @@ async def test_async_prompt_optimizer_optimize_prompt_few_shot_target_response(
optimization_target=types.OptimizeTarget.OPTIMIZATION_TARGET_FEW_SHOT_TARGET_RESPONSE,
examples_dataframe=df,
)
await test_client.aio.prompt_optimizer.optimize_prompt(
await test_client.aio.prompts.optimize(
prompt="test_prompt",
config=config,
)
Expand All @@ -195,7 +261,7 @@ async def test_async_prompt_optimizer_optimize_prompt_few_shot_target_response(
)

@pytest.mark.asyncio
@mock.patch.object(prompt_optimizer.AsyncPromptOptimizer, "_custom_optimize_prompt")
@mock.patch.object(prompts.AsyncPrompts, "_custom_optimize")
async def test_async_prompt_optimizer_optimize_prompt_few_shot_rubrics(
self, mock_custom_optimize_prompt
):
Expand All @@ -213,7 +279,7 @@ async def test_async_prompt_optimizer_optimize_prompt_few_shot_rubrics(
optimization_target=types.OptimizeTarget.OPTIMIZATION_TARGET_FEW_SHOT_RUBRICS,
examples_dataframe=df,
)
await test_client.aio.prompt_optimizer.optimize_prompt(
await test_client.aio.prompts.optimize(
prompt="test_prompt",
config=config,
)
Expand Down
47 changes: 47 additions & 0 deletions vertexai/_genai/_logging_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 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
#
# http://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.
#

import functools
from typing import Any, Callable
from google.genai import _common
import warnings


def show_deprecation_warning_once(
message: str,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
"""Decorator to show a deprecation warning once for a function."""

def decorator(func: Any) -> Any:
warning_done = False

@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
nonlocal warning_done
if not warning_done:
warning_done = True
warnings.warn(message, DeprecationWarning, stacklevel=2)

# Suppress ExperimentalWarning while executing the deprecated wrapper
with warnings.catch_warnings():
# We ignore ExperimentalWarning because the user will see it
# when they migrate to the new prompts module
warnings.simplefilter("ignore", category=_common.ExperimentalWarning)
return func(*args, **kwargs)
return func(*args, **kwargs)

return wrapper

return decorator
8 changes: 0 additions & 8 deletions vertexai/_genai/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,6 @@ def evals(self) -> "evals_module.AsyncEvals":
return self._evals.AsyncEvals(self._api_client) # type: ignore[no-any-return]

@property
@_common.experimental_warning(
"The Vertex SDK GenAI prompt optimizer module is experimental, "
"and may change in future versions."
)
def prompt_optimizer(self) -> "prompt_optimizer_module.AsyncPromptOptimizer":
if self._prompt_optimizer is None:
self._prompt_optimizer = importlib.import_module(
Expand Down Expand Up @@ -258,10 +254,6 @@ def evals(self) -> "evals_module.Evals":
return self._evals.Evals(self._api_client) # type: ignore[no-any-return]

@property
@_common.experimental_warning(
"The Vertex SDK GenAI prompt optimizer module is experimental, and may change in future "
"versions."
)
def prompt_optimizer(self) -> "prompt_optimizer_module.PromptOptimizer":
if self._prompt_optimizer is None:
self._prompt_optimizer = importlib.import_module(
Expand Down
Loading
Loading