Skip to content

Commit 29dd96e

Browse files
alexkuzmikclaude
andauthored
[NA] [SDK] [GHA] test: stabilize Gemini + agentic-judge integration tests (#7405)
Three related changes to stop provider-driven flakiness in the real-model integration suites: - Agentic LLM-judge tests: run the Anthropic lanes against claude-sonnet-4-6 instead of claude-haiku-4-5. On the agentic path (tools in the request, so response_format is best-effort) Haiku wrapped verdicts in prose/```json fences; Sonnet follows the structured-output contract reliably (the one-shot suite already used Sonnet). - GenAI + ADK test matrices: run only the oldest and newest supported Python versions (3.10, 3.14). These suites make live Vertex AI calls against the shared opik-sdk-tests GCP project; the full 3.10-3.14 matrix re-multiplied those calls into 429 RESOURCE_EXHAUSTED. Endpoints cover version-specific breakage at a fraction of the request volume; max-parallel cap kept. - Remove the broken GenAI rate-limit retry helpers: _is_rate_limit_error checked exception.response.status_code, which raises AttributeError on google-genai's ClientError (no such attribute), so the retry crashed instead of retrying. With the matrix trim as the 429 mitigation, the dead helper and its decorators are removed rather than repaired. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 422ea07 commit 29dd96e

5 files changed

Lines changed: 23 additions & 59 deletions

File tree

.github/workflows/lib-adk-tests.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ jobs:
2929

3030
strategy:
3131
fail-fast: true
32-
# Throttle concurrent matrix jobs so we keep full Python coverage
33-
# without re-multiplying live Vertex AI calls into 429 RESOURCE_EXHAUSTED
34-
# against the shared opik-sdk-tests GCP project.
32+
# This suite makes live Vertex AI calls against the shared
33+
# opik-sdk-tests GCP project (shared with the GenAI suite). Running the
34+
# full 3.10-3.14 matrix re-multiplied those calls into 429
35+
# RESOURCE_EXHAUSTED, so we run only the oldest and newest supported
36+
# Python versions — enough to catch version-specific breakage at a
37+
# fraction of the request volume — and keep the concurrency cap.
3538
max-parallel: 2
3639
matrix:
37-
python_version: ${{ fromJSON(vars.PYTHON_VERSIONS) }}
40+
python_version: ["3.10", "3.14"]
3841

3942
steps:
4043
- name: Check out code

.github/workflows/lib-genai-tests.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ jobs:
3333

3434
strategy:
3535
fail-fast: true
36-
# Throttle concurrent matrix jobs so we keep full Python coverage
37-
# without re-multiplying live Vertex AI calls into 429 RESOURCE_EXHAUSTED
38-
# against the shared opik-sdk-tests GCP project (shared with the ADK
39-
# suite, so any uncapped suite re-multiplies their contention too).
36+
# This suite makes live Vertex AI calls against the shared
37+
# opik-sdk-tests GCP project (shared with the ADK suite). Running the
38+
# full 3.10-3.14 matrix re-multiplied those calls into 429
39+
# RESOURCE_EXHAUSTED, so we run only the oldest and newest supported
40+
# Python versions — enough to catch version-specific breakage at a
41+
# fraction of the request volume — and keep the concurrency cap.
4042
max-parallel: 2
4143
matrix:
42-
python_version: ${{ fromJSON(vars.PYTHON_VERSIONS) }}
44+
python_version: ["3.10", "3.14"]
4345

4446
steps:
4547
- name: Check out code

sdks/python/tests/library_integration/genai/test_genai.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import pytest
66
from google import genai
77
from google.genai.types import HttpOptions, GenerateContentConfig
8-
from google.genai import errors as genai_errors
98
import opik
109
from opik.config import OPIK_PROJECT_DEFAULT_NAME
1110
from opik.integrations.genai import track_genai
@@ -21,7 +20,6 @@
2120
assert_dict_has_keys,
2221
assert_equal,
2322
)
24-
import tenacity
2523

2624
pytestmark = pytest.mark.usefixtures("ensure_vertexai_configured")
2725

@@ -38,19 +36,6 @@
3836
)
3937

4038

41-
def _is_rate_limit_error(exception: Exception) -> bool:
42-
if isinstance(exception, genai_errors.ClientError):
43-
return exception.response.status_code == 429
44-
return False
45-
46-
47-
retry_with_waiting_on_rate_limit_errors = tenacity.retry(
48-
stop=tenacity.stop_after_attempt(3),
49-
wait=tenacity.wait_incrementing(start=5, increment=5),
50-
retry=tenacity.retry_if_exception(_is_rate_limit_error),
51-
)
52-
53-
5439
def _assert_metadata_contains_required_keys(metadata: Dict[str, Any]):
5540
REQUIRED_METADATA_KEYS = [
5641
"model",
@@ -61,7 +46,6 @@ def _assert_metadata_contains_required_keys(metadata: Dict[str, Any]):
6146
assert_dict_has_keys(metadata, REQUIRED_METADATA_KEYS)
6247

6348

64-
@retry_with_waiting_on_rate_limit_errors
6549
@pytest.mark.parametrize(
6650
"project_name, expected_project_name",
6751
[
@@ -131,7 +115,6 @@ def test_genai_client__generate_content__happyflow(
131115
_assert_metadata_contains_required_keys(llm_span_metadata)
132116

133117

134-
@retry_with_waiting_on_rate_limit_errors
135118
def test_genai_client__async_generate_content__happyflow(fake_backend):
136119
client = genai.Client(
137120
vertexai=True,
@@ -187,7 +170,6 @@ def test_genai_client__async_generate_content__happyflow(fake_backend):
187170
_assert_metadata_contains_required_keys(llm_span_metadata)
188171

189172

190-
@retry_with_waiting_on_rate_limit_errors
191173
@pytest.mark.asyncio
192174
async def test_genai_client__async_generate_content__opik_args__happyflow(fake_backend):
193175
client = genai.Client(
@@ -254,7 +236,6 @@ async def test_genai_client__async_generate_content__opik_args__happyflow(fake_b
254236
_assert_metadata_contains_required_keys(llm_span_metadata)
255237

256238

257-
@retry_with_waiting_on_rate_limit_errors
258239
@pytest.mark.parametrize(
259240
"project_name, expected_project_name",
260241
[
@@ -331,7 +312,6 @@ def f():
331312
_assert_metadata_contains_required_keys(llm_span_metadata)
332313

333314

334-
@retry_with_waiting_on_rate_limit_errors
335315
def test_genai_client__async_generate_content_called_inside_another_tracked_function__happyflow(
336316
fake_backend,
337317
):
@@ -400,7 +380,6 @@ async def f():
400380
_assert_metadata_contains_required_keys(llm_span_metadata)
401381

402382

403-
@retry_with_waiting_on_rate_limit_errors
404383
def test_genai_client__generate_content_stream__happyflow(fake_backend):
405384
client = genai.Client(
406385
vertexai=True,
@@ -459,7 +438,6 @@ def test_genai_client__generate_content_stream__happyflow(fake_backend):
459438
_assert_metadata_contains_required_keys(llm_span_metadata)
460439

461440

462-
@retry_with_waiting_on_rate_limit_errors
463441
def test_genai_client__async_generate_content_stream__happyflow(fake_backend):
464442
client = genai.Client(
465443
vertexai=True,
@@ -520,7 +498,6 @@ async def stream_example():
520498
_assert_metadata_contains_required_keys(llm_span_metadata)
521499

522500

523-
@retry_with_waiting_on_rate_limit_errors
524501
def test_genai_client__generate_content_stream_called_inside_another_tracked_function__generations_started_after_the_parent_span_closed__llm_span_attached_to_a_parent_function_span(
525502
fake_backend,
526503
):
@@ -595,7 +572,6 @@ def f():
595572
_assert_metadata_contains_required_keys(llm_span_metadata)
596573

597574

598-
@retry_with_waiting_on_rate_limit_errors
599575
def test_genai_client__async_generate_content_stream_called_inside_another_tracked_function__generations_started_after_the_parent_span_closed__llm_span_has_a_separate_trace(
600576
fake_backend,
601577
):
@@ -673,7 +649,6 @@ async def stream_outside_of_parent_function_example():
673649
_assert_metadata_contains_required_keys(llm_span_metadata)
674650

675651

676-
@retry_with_waiting_on_rate_limit_errors
677652
@pytest.mark.parametrize(
678653
"project_name, expected_project_name",
679654
[
@@ -755,7 +730,6 @@ def test_genai_client__generate_content__opik_args__happyflow(
755730
_assert_metadata_contains_required_keys(llm_span_metadata)
756731

757732

758-
@retry_with_waiting_on_rate_limit_errors
759733
def test_genai_client__generate_content__cost_callback__sets_span_total_cost(
760734
fake_backend,
761735
):

sdks/python/tests/library_integration/genai/test_genai_videos.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
assert_equal,
2828
patch_environ,
2929
)
30-
import tenacity
3130

3231
pytestmark = [
3332
pytest.mark.usefixtures("ensure_vertexai_configured"),
@@ -57,24 +56,10 @@ def use_us_central1_for_veo():
5756
yield
5857

5958

60-
def _is_rate_limit_error(exception: Exception) -> bool:
61-
if isinstance(exception, genai_errors.ClientError):
62-
return exception.response.status_code == 429
63-
return False
64-
65-
66-
retry_on_rate_limit = tenacity.retry(
67-
stop=tenacity.stop_after_attempt(3),
68-
wait=tenacity.wait_incrementing(start=5, increment=5),
69-
retry=tenacity.retry_if_exception(_is_rate_limit_error),
70-
)
71-
72-
7359
@pytest.mark.skipif(
7460
SKIP_EXPENSIVE_TESTS,
7561
reason="Expensive tests disabled. Set OPIK_TEST_EXPENSIVE=1 to enable.",
7662
)
77-
@retry_on_rate_limit
7863
def test_genai_client__generate_videos_and_save__sync__happyflow(fake_backend):
7964
"""
8065
Test sync video generation workflow: create -> wait -> save.
@@ -283,7 +268,6 @@ def test_genai_client__generate_videos_and_save__sync__happyflow(fake_backend):
283268
reason="Expensive tests disabled. Set OPIK_TEST_EXPENSIVE=1 to enable.",
284269
)
285270
@pytest.mark.asyncio
286-
@retry_on_rate_limit
287271
async def test_genai_client__generate_videos_and_save__async__happyflow(fake_backend):
288272
"""
289273
Test async video generation workflow: create -> wait -> save.
@@ -571,7 +555,6 @@ def test_genai_client__generate_videos__error_handling(fake_backend):
571555
SKIP_EXPENSIVE_TESTS,
572556
reason="Expensive tests disabled. Set OPIK_TEST_EXPENSIVE=1 to enable.",
573557
)
574-
@retry_on_rate_limit
575558
def test_genai_client__generate_videos_with_upload_videos_disabled__no_attachment(
576559
fake_backend,
577560
):

sdks/python/tests/library_integration/metrics_with_llm_judge/agentic/conftest.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,13 @@
3232
# canonical "doesn't call tools" failure mode (see
3333
# `SupportedJudgeProvider.java`), so don't swap it in here without
3434
# re-validating tool-use tests by hand.
35-
# - Anthropic `claude-haiku-4-5` (native `AnthropicChatModel`): the
36-
# cheap+fast Claude tier. Sonnet would also work but the per-call
37-
# latency hurts CI throughput given the agentic loop's multi-round
38-
# nature.
39-
# - Anthropic `claude-haiku-4-5` *via LiteLLM* (`litellm_anthropic`):
35+
# - Anthropic `claude-sonnet-4-6` (native `AnthropicChatModel`): the
36+
# Claude tier we run the agentic judge against. Haiku is cheaper but
37+
# on the agentic path (tools in the request, so `response_format` is
38+
# best-effort) it narrates in prose and wraps the verdict in a
39+
# ```json fence rather than emitting the bare object the parser
40+
# expects; Sonnet follows the structured-output contract reliably.
41+
# - Anthropic `claude-sonnet-4-6` *via LiteLLM* (`litellm_anthropic`):
4042
# identical model string as the native row, but routed through the
4143
# LiteLLM adapter by forcing `_should_use_anthropic_native=False`.
4244
# This is the only parametrize entry that exercises Anthropic-by-
@@ -51,11 +53,11 @@
5153
_JUDGE_MODEL_PARAMS: List[Tuple[str, List[str]]] = [
5254
(llm_constants.OPENAI_GPT_4O_MINI, ["_skip_unless_openai_configured"]),
5355
(
54-
f"{llm_constants.ANTHROPIC_CLAUDE_HAIKU}",
56+
f"{llm_constants.ANTHROPIC_CLAUDE_SONNET}",
5557
["_skip_unless_anthropic_configured"],
5658
),
5759
(
58-
f"{llm_constants.LITELLM_ANTHROPIC_CLAUDE_HAIKU}",
60+
f"{llm_constants.LITELLM_ANTHROPIC_CLAUDE_SONNET}",
5961
["_skip_unless_anthropic_configured", "_force_litellm_path"],
6062
),
6163
]

0 commit comments

Comments
 (0)