google-cloud-agentplatform 2.4.0__tar.gz → 2.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. {google_cloud_agentplatform-2.4.0/google_cloud_agentplatform.egg-info → google_cloud_agentplatform-2.5.0}/PKG-INFO +2 -2
  2. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_common.py +77 -74
  3. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_constant.py +14 -0
  4. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_metric_handlers.py +8 -15
  5. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_visualization.py +15 -1
  6. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_runtimes_utils.py +18 -0
  7. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/evals.py +6 -6
  8. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/runtimes.py +26 -8
  9. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/types/common.py +22 -3
  10. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/version.py +1 -1
  11. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0/google_cloud_agentplatform.egg-info}/PKG-INFO +2 -2
  12. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/google_cloud_agentplatform.egg-info/requires.txt +1 -1
  13. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/pyproject.toml +1 -1
  14. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/LICENSE +0 -0
  15. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/README.md +0 -0
  16. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/__init__.py +0 -0
  17. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/__init__.py +0 -0
  18. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_bigquery_utils.py +0 -0
  19. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_datasets_utils.py +0 -0
  20. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_builtin_tools.py +0 -0
  21. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_data_converters.py +0 -0
  22. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_metric_loaders.py +0 -0
  23. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_utils.py +0 -0
  24. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_gcs_utils.py +0 -0
  25. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_logging_utils.py +0 -0
  26. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_memory_bank_utils.py +0 -0
  27. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_observability_data_converter.py +0 -0
  28. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_operations_utils.py +0 -0
  29. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_prompt_management_utils.py +0 -0
  30. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_prompt_optimizer_utils.py +0 -0
  31. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_skills_utils.py +0 -0
  32. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_transformers.py +0 -0
  33. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/a2a_task_events.py +0 -0
  34. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/a2a_tasks.py +0 -0
  35. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/client.py +0 -0
  36. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/datasets.py +0 -0
  37. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/endpoints.py +0 -0
  38. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/example_stores.py +0 -0
  39. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/feedback_contexts.py +0 -0
  40. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/feedback_entries.py +0 -0
  41. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/live.py +0 -0
  42. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/live_runtimes.py +0 -0
  43. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/memories.py +0 -0
  44. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/memory_banks.py +0 -0
  45. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/memory_revisions.py +0 -0
  46. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/model_garden.py +0 -0
  47. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/prompt_optimizer.py +0 -0
  48. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/prompts.py +0 -0
  49. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/rag.py +0 -0
  50. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/runtime_revisions.py +0 -0
  51. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/sandbox_snapshots.py +0 -0
  52. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/sandbox_templates.py +0 -0
  53. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/sandboxes.py +0 -0
  54. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/serving_profiles.py +0 -0
  55. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/session_events.py +0 -0
  56. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/sessions.py +0 -0
  57. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/skill_revisions.py +0 -0
  58. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/skills.py +0 -0
  59. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/types/__init__.py +0 -0
  60. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/types/agent_engines.py +0 -0
  61. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/types/evals.py +0 -0
  62. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/types/prompt_optimizer.py +0 -0
  63. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/types/prompts.py +0 -0
  64. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/__init__.py +0 -0
  65. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/a2a.py +0 -0
  66. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/adk.py +0 -0
  67. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/ag2.py +0 -0
  68. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/langchain.py +0 -0
  69. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/langgraph.py +0 -0
  70. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/llama_index.py +0 -0
  71. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/google_cloud_agentplatform.egg-info/SOURCES.txt +0 -0
  72. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/google_cloud_agentplatform.egg-info/dependency_links.txt +0 -0
  73. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/google_cloud_agentplatform.egg-info/top_level.txt +0 -0
  74. {google_cloud_agentplatform-2.4.0 → google_cloud_agentplatform-2.5.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-cloud-agentplatform
3
- Version: 2.4.0
3
+ Version: 2.5.0
4
4
  Summary: Google Cloud Agent Platform Client Library
5
5
  Author-email: Google LLC <googleapis-packages@google.com>
6
6
  License: Apache-2.0
@@ -31,7 +31,7 @@ Requires-Dist: google-cloud-storage<4.0.0,>=1.32.0; extra == "agent-engines"
31
31
  Requires-Dist: opentelemetry-sdk<2; extra == "agent-engines"
32
32
  Requires-Dist: opentelemetry-exporter-gcp-logging<2.0.0,>=1.11.0a0; extra == "agent-engines"
33
33
  Requires-Dist: opentelemetry-exporter-otlp-proto-http<2; extra == "agent-engines"
34
- Requires-Dist: opentelemetry-instrumentation-google-genai<1.0.0,>=0.3b0; extra == "agent-engines"
34
+ Requires-Dist: opentelemetry-instrumentation-google-genai<2.0.0,>=0.3b0; extra == "agent-engines"
35
35
  Requires-Dist: pydantic<3,>=2.11.1; extra == "agent-engines"
36
36
  Provides-Extra: a2a
37
37
  Requires-Dist: google-cloud-agentplatform[agent_engines]; extra == "a2a"
@@ -22,6 +22,7 @@ import datetime
22
22
  import json
23
23
  import logging
24
24
  import os
25
+ import random
25
26
  import threading
26
27
  import time
27
28
  from typing import Any, Callable, Literal, Optional, Union, cast
@@ -122,6 +123,11 @@ def _temp_logger_level(logger_name: str, level: int) -> None: # type: ignore[mi
122
123
  logger_instance.setLevel(original_level)
123
124
 
124
125
 
126
+ def _is_missing(value: Any) -> bool:
127
+ """Returns True for None or a scalar NaN, such as a missing DataFrame cell."""
128
+ return bool(pd.api.types.is_scalar(value) and pd.isna(value))
129
+
130
+
125
131
  def _get_api_client_with_location(
126
132
  api_client: BaseApiClient, location: Optional[str]
127
133
  ) -> BaseApiClient:
@@ -153,6 +159,8 @@ def _get_runtime_instance(
153
159
  client = agentplatform.Client(
154
160
  project=api_client.project,
155
161
  location=api_client.location,
162
+ credentials=api_client._credentials,
163
+ http_options=api_client._http_options,
156
164
  )
157
165
  _thread_local_data.runtime_instances[agent_name] = client.runtimes.get(
158
166
  name=agent_name
@@ -160,6 +168,45 @@ def _get_runtime_instance(
160
168
  return _thread_local_data.runtime_instances[agent_name]
161
169
 
162
170
 
171
+ def _is_retryable_error(e: Exception) -> bool:
172
+ """Returns True for transient errors that are safe to retry with backoff."""
173
+ if isinstance(e, genai_errors.APIError):
174
+ return e.code in _evals_constant.RETRYABLE_STATUS_CODES
175
+ return isinstance(
176
+ e, (api_exceptions.ResourceExhausted, api_exceptions.ServiceUnavailable)
177
+ )
178
+
179
+
180
+ def _get_retry_backoff(
181
+ e: Exception, attempt: int, max_retries: int, operation: str
182
+ ) -> Optional[float]:
183
+ """Returns the seconds to wait before the next attempt, or None to stop.
184
+
185
+ Retryable errors back off exponentially with jitter. Other errors fail
186
+ fast, and the last attempt never waits.
187
+ """
188
+ if not _is_retryable_error(e) or attempt == max_retries - 1:
189
+ logger.error(
190
+ "Error during %s on attempt %d/%d: %s",
191
+ operation,
192
+ attempt + 1,
193
+ max_retries,
194
+ e,
195
+ )
196
+ return None
197
+ backoff = 2**attempt + random.uniform(0, 1)
198
+ logger.warning(
199
+ "Retryable error during %s on attempt %d/%d: %s. Retrying in %.1f"
200
+ " seconds...",
201
+ operation,
202
+ attempt + 1,
203
+ max_retries,
204
+ e,
205
+ backoff,
206
+ )
207
+ return backoff
208
+
209
+
163
210
  def _generate_content_with_retry(
164
211
  api_client: BaseApiClient,
165
212
  model: str,
@@ -222,29 +269,11 @@ def _generate_content_with_retry(
222
269
  }
223
270
  else:
224
271
  return response
225
- except api_exceptions.ResourceExhausted as e:
226
- logger.warning(
227
- "Resource Exhausted error on attempt %d/%d: %s. Retrying in %s"
228
- " seconds...",
229
- attempt + 1,
230
- max_retries,
231
- e,
232
- 2**attempt,
233
- )
234
- if attempt == max_retries - 1:
235
- return {"error": f"Resource exhausted after retries: {e}"}
236
- time.sleep(2**attempt)
237
272
  except Exception as e: # pylint: disable=broad-exception-caught
238
- logger.error(
239
- "Unexpected error during generate_content on attempt %d/%d: %s",
240
- attempt + 1,
241
- max_retries,
242
- e,
243
- )
244
-
245
- if attempt == max_retries - 1:
246
- return {"error": f"Failed after retries: {e}"}
247
- time.sleep(1)
273
+ backoff = _get_retry_backoff(e, attempt, max_retries, "generate_content")
274
+ if backoff is None:
275
+ return {"error": f"Failed on attempt {attempt + 1}/{max_retries}: {e}"}
276
+ time.sleep(backoff)
248
277
  return {"error": f"Failed to generate content after {max_retries} retries"}
249
278
 
250
279
 
@@ -1561,7 +1590,7 @@ def _build_interaction_id_dataset(
1561
1590
  eval_cases = []
1562
1591
  for i, row in enumerate(loaded_data):
1563
1592
  interaction = row.get(_evals_constant.INTERACTION_ID)
1564
- if not interaction:
1593
+ if _is_missing(interaction) or not interaction:
1565
1594
  raise ValueError(f"Missing `interaction_id` value for row {i}.")
1566
1595
  eval_cases.append(
1567
1596
  types.EvalCase(
@@ -1871,12 +1900,12 @@ def _execute_inference_concurrently(
1871
1900
  max_workers = AGENT_MAX_WORKERS if runtime or agent else MAX_WORKERS
1872
1901
  with tqdm(total=len(prompt_dataset), desc=progress_desc) as pbar:
1873
1902
  with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
1874
- for index, row in prompt_dataset.iterrows():
1903
+ for index, (_, row) in enumerate(prompt_dataset.iterrows()):
1875
1904
  try:
1876
1905
  if (
1877
1906
  has_agent_data
1878
1907
  and AGENT_DATA in row.index
1879
- and row.get(AGENT_DATA) is not None
1908
+ and not _is_missing(row.get(AGENT_DATA))
1880
1909
  ):
1881
1910
  agent_data_obj = row[AGENT_DATA]
1882
1911
  if isinstance(agent_data_obj, dict):
@@ -2101,7 +2130,7 @@ def _run_litellm_inference(
2101
2130
 
2102
2131
  with tqdm(total=len(prompt_dataset), desc=f"LiteLLM Inference ({model})") as pbar:
2103
2132
  with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
2104
- for index, row in prompt_dataset.iterrows():
2133
+ for index, (_, row) in enumerate(prompt_dataset.iterrows()):
2105
2134
  messages = _convert_prompt_row_to_litellm_messages(row)
2106
2135
  future = executor.submit(
2107
2136
  _call_litellm_completion, model=model, messages=messages
@@ -3481,7 +3510,9 @@ def _create_runtime_session(
3481
3510
  raise RuntimeError(
3482
3511
  "Failed to create session: runtime.api_client is None."
3483
3512
  ) from exc
3484
- operation = runtime.api_client.sessions.create(
3513
+ from . import sessions
3514
+
3515
+ operation = sessions.Sessions(runtime.api_client._api_client).create(
3485
3516
  name=runtime.api_resource.name,
3486
3517
  user_id=user_id,
3487
3518
  config=types.CreateRuntimeSessionConfig(
@@ -3545,10 +3576,12 @@ def _execute_agent_run_with_retry(
3545
3576
  return {"error": "runtime.api_resource is None."}
3546
3577
  if runtime.api_client is None:
3547
3578
  return {"error": "runtime.api_client is None."}
3579
+ from . import sessions
3580
+
3548
3581
  session_name = f"{runtime.api_resource.name}/sessions/{session_id}"
3549
3582
  base_ts = datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc)
3550
3583
  for i, ag_event in enumerate(history_events):
3551
- runtime.api_client.sessions.events.append(
3584
+ sessions.Sessions(runtime.api_client._api_client).events.append(
3552
3585
  name=session_name,
3553
3586
  author=ag_event.author or "user",
3554
3587
  invocation_id="history",
@@ -3570,28 +3603,11 @@ def _execute_agent_run_with_retry(
3570
3603
  if event and CONTENT in event and PARTS in event[CONTENT]:
3571
3604
  responses.append(event)
3572
3605
  return responses
3573
- except api_exceptions.ResourceExhausted as e:
3574
- logger.warning(
3575
- "Resource Exhausted error on attempt %d/%d: %s. Retrying in %s"
3576
- " seconds...",
3577
- attempt + 1,
3578
- max_retries,
3579
- e,
3580
- 2**attempt,
3581
- )
3582
- if attempt == max_retries - 1:
3583
- return {"error": f"Resource exhausted after retries: {e}"}
3584
- time.sleep(2**attempt)
3585
3606
  except Exception as e: # pylint: disable=broad-exception-caught
3586
- logger.error(
3587
- "Unexpected error during agent engine run on attempt %d/%d: %s",
3588
- attempt + 1,
3589
- max_retries,
3590
- e,
3591
- )
3592
- if attempt == max_retries - 1:
3593
- return {"error": f"Failed after retries: {e}"}
3594
- time.sleep(1)
3607
+ backoff = _get_retry_backoff(e, attempt, max_retries, "agent engine run")
3608
+ if backoff is None:
3609
+ return {"error": f"Failed on attempt {attempt + 1}/{max_retries}: {e}"}
3610
+ time.sleep(backoff)
3595
3611
  return {"error": f"Failed to get agent run results after {max_retries} retries"}
3596
3612
 
3597
3613
 
@@ -3623,7 +3639,7 @@ async def _execute_local_agent_run_with_retry_async(
3623
3639
  # Lazy-import ADK dependencies to avoid top-level import failures when
3624
3640
  # google-adk is not installed.
3625
3641
  from google.adk.runners import Runner
3626
- from google.adk.sessions import InMemorySessionService
3642
+ from google.adk.sessions.in_memory_session_service import InMemorySessionService
3627
3643
 
3628
3644
  # Multi-turn agent scraping with user simulation.
3629
3645
  if user_simulator_config or "conversation_plan" in row:
@@ -3697,28 +3713,13 @@ async def _execute_local_agent_run_with_retry_async(
3697
3713
  if event and CONTENT in event and PARTS in event[CONTENT]:
3698
3714
  events.append(event)
3699
3715
  return events
3700
- except api_exceptions.ResourceExhausted as e:
3701
- logger.warning(
3702
- "Resource Exhausted error on attempt %d/%d: %s. Retrying"
3703
- " in %s seconds...",
3704
- attempt + 1,
3705
- max_retries,
3706
- e,
3707
- 2**attempt,
3708
- )
3709
- if attempt == max_retries - 1:
3710
- return {"error": f"Resource exhausted after retries: {e}"}
3711
- await asyncio.sleep(2**attempt)
3712
3716
  except Exception as e: # pylint: disable=broad-exception-caught
3713
- logger.error(
3714
- "Unexpected error during agent run on attempt %d/%d: %s",
3715
- attempt + 1,
3716
- max_retries,
3717
- e,
3718
- )
3719
- if attempt == max_retries - 1:
3720
- return {"error": f"Failed after retries: {e}"}
3721
- await asyncio.sleep(1)
3717
+ backoff = _get_retry_backoff(e, attempt, max_retries, "agent run")
3718
+ if backoff is None:
3719
+ return {
3720
+ "error": f"Failed on attempt {attempt + 1}/{max_retries}: {e}"
3721
+ }
3722
+ await asyncio.sleep(backoff)
3722
3723
  return {"error": f"Failed to get agent run results after {max_retries} retries"}
3723
3724
 
3724
3725
 
@@ -3874,7 +3875,7 @@ def _drop_empty_columns(df: "pd.DataFrame") -> "pd.DataFrame":
3874
3875
  def is_empty(x: Any) -> bool:
3875
3876
  if isinstance(x, (list, dict)):
3876
3877
  return not x
3877
- return pd.isna(x) # type: ignore[no-any-return]
3878
+ return _is_missing(x)
3878
3879
 
3879
3880
  cols_to_drop = [col for col in df.columns if df[col].apply(is_empty).all()]
3880
3881
  return df.drop(columns=cols_to_drop)
@@ -4173,6 +4174,8 @@ def _create_evaluation_set_from_dataframe(
4173
4174
  if _evals_constant.RESPONSE in row or agent_data_obj or intermediate_events:
4174
4175
  # Resolve the oneof conflict: prioritize agent_data over flat text
4175
4176
  response_text = row.get(_evals_constant.RESPONSE) or None
4177
+ if _is_missing(response_text):
4178
+ response_text = None
4176
4179
 
4177
4180
  if agent_data_obj and response_text:
4178
4181
  logger.info(
@@ -4227,7 +4230,7 @@ def _create_evaluation_set_from_dataframe(
4227
4230
  prompt = types.EvaluationPrompt(
4228
4231
  prompt_template_data=types.PromptTemplateData(values=values)
4229
4232
  )
4230
- elif _evals_constant.PROMPT in row:
4233
+ elif not _is_missing(row.get(_evals_constant.PROMPT)):
4231
4234
  prompt = types.EvaluationPrompt(text=row[_evals_constant.PROMPT])
4232
4235
 
4233
4236
  eval_item_requests.append(
@@ -4235,7 +4238,7 @@ def _create_evaluation_set_from_dataframe(
4235
4238
  prompt=prompt or None,
4236
4239
  golden_response=(
4237
4240
  types.CandidateResponse(text=row[_evals_constant.REFERENCE])
4238
- if _evals_constant.REFERENCE in row
4241
+ if not _is_missing(row.get(_evals_constant.REFERENCE))
4239
4242
  else None
4240
4243
  ),
4241
4244
  candidate_responses=(
@@ -120,3 +120,17 @@ COMMON_DATASET_COLUMNS = frozenset(
120
120
  INTERACTION_ID,
121
121
  }
122
122
  )
123
+
124
+ # HTTP status codes that are safe to retry with backoff.
125
+ RETRYABLE_STATUS_CODES = frozenset(
126
+ {
127
+ 408, # RequestTimeout (DEADLINE_EXCEEDED)
128
+ 409, # Conflict / Aborted (ABORTED)
129
+ 429, # TooManyRequests / ResourceExhausted (RESOURCE_EXHAUSTED)
130
+ 499, # Client Closed Request (CANCELLED)
131
+ 500, # InternalServerError (INTERNAL)
132
+ 502, # BadGateway
133
+ 503, # ServiceUnavailable (UNAVAILABLE)
134
+ 504, # GatewayTimeout (DEADLINE_EXCEEDED)
135
+ }
136
+ )
@@ -39,19 +39,6 @@ from . import types
39
39
 
40
40
  logger = logging.getLogger(__name__)
41
41
  _MAX_RETRIES = 5
42
- # HTTP status codes that are safe to retry with backoff.
43
- _RETRYABLE_STATUS_CODES = frozenset(
44
- {
45
- 408, # RequestTimeout (DEADLINE_EXCEEDED)
46
- 409, # Conflict / Aborted (ABORTED)
47
- 429, # TooManyRequests / ResourceExhausted (RESOURCE_EXHAUSTED)
48
- 499, # Client Closed Request (CANCELLED)
49
- 500, # InternalServerError (INTERNAL)
50
- 502, # BadGateway
51
- 503, # ServiceUnavailable (UNAVAILABLE)
52
- 504, # GatewayTimeout (DEADLINE_EXCEEDED)
53
- }
54
- )
55
42
 
56
43
  R = TypeVar("R")
57
44
  T = TypeVar("T", types.Metric, types.MetricSource, types.LLMMetric)
@@ -64,7 +51,7 @@ def _call_with_retry(
64
51
  """Calls ``fn()`` with exponential backoff + jitter on retryable errors.
65
52
 
66
53
  Retries up to ``_MAX_RETRIES`` times on errors whose HTTP status code is
67
- in ``_RETRYABLE_STATUS_CODES`` (Aborted, DeadlineExceeded,
54
+ in ``_evals_constant.RETRYABLE_STATUS_CODES`` (Aborted, DeadlineExceeded,
68
55
  ResourceExhausted, ServiceUnavailable, Cancelled). Non-retryable errors
69
56
  are re-raised immediately. If all retries are exhausted the last
70
57
  exception is re-raised so the caller can decide how to handle it.
@@ -84,7 +71,7 @@ def _call_with_retry(
84
71
  try:
85
72
  return fn()
86
73
  except genai_errors.APIError as e:
87
- if e.code in _RETRYABLE_STATUS_CODES:
74
+ if e.code in _evals_constant.RETRYABLE_STATUS_CODES:
88
75
  backoff = 2**attempt + random.uniform(0, 1)
89
76
  logger.warning(
90
77
  "Retryable error (code=%s) on attempt %d/%d for metric"
@@ -686,6 +673,12 @@ def _build_evaluation_instance(
686
673
  other_data_map["context"] = types.evals.InstanceData(text=eval_case.context)
687
674
  elif isinstance(eval_case.context, genai_types.Content):
688
675
  other_data_map["context"] = _content_to_instance_data(eval_case.context)
676
+ else:
677
+ other_data_map["context"] = types.evals.InstanceData(
678
+ contents=types.evals.InstanceDataContents(
679
+ contents=_value_to_content_list(eval_case.context)
680
+ )
681
+ )
689
682
 
690
683
  # 3. Extract custom variables from LLMMetric templates
691
684
  if prompt_template:
@@ -229,6 +229,20 @@ def _extract_dataset_rows(dataset: types.EvaluationDataset) -> list[dict[str, An
229
229
  }
230
230
  processed_rows.append(processed_row)
231
231
 
232
+ for row in processed_rows:
233
+ if isinstance(row["agent_data"], str) or not isinstance(
234
+ row["intermediate_events"], str
235
+ ):
236
+ continue
237
+ try:
238
+ events = json.loads(row["intermediate_events"])
239
+ except json.JSONDecodeError:
240
+ continue
241
+ if isinstance(events, list) and events:
242
+ row["agent_data"] = json.dumps(
243
+ {"turns": [{"turn_index": 0, "events": events}]}, ensure_ascii=False
244
+ )
245
+
232
246
  return processed_rows
233
247
 
234
248
 
@@ -1155,7 +1169,7 @@ def get_comparison_html(eval_result_json: str) -> str:
1155
1169
  winRateText = m.win_rates.map((rate, i) => `<b>${{candidateNames ? candidateNames[i] : `Candidate #${{i+1}}`}}</b> wins: <b>${{(rate * 100).toFixed(1)}}%</b>`).join('<br>');
1156
1170
  if (m.tie_rate !== undefined) {{ winRateText += `<br>Ties: <b>${{(m.tie_rate * 100).toFixed(1)}}%</b>`; }}
1157
1171
  }}
1158
- table += `<tr><td>${{m.metric_name}}</td><td>${{m.mean_score.toFixed(4)}}</td><td>${{m.stdev_score.toFixed(4)}}</td><td>${{winRateText}}</td></tr>`;
1172
+ table += `<tr><td>${{m.metric_name}}</td><td>${{m.mean_score != null ? m.mean_score.toFixed(4) : 'N/A'}}</td><td>${{m.stdev_score != null ? m.stdev_score.toFixed(4) : 'N/A'}}</td><td>${{winRateText}}</td></tr>`;
1159
1173
  }});
1160
1174
  container.innerHTML = table + '</tbody></table>';
1161
1175
  }}
@@ -52,6 +52,7 @@ import httpx
52
52
  import proto
53
53
 
54
54
  from google.api_core import exceptions
55
+ from google.genai import errors as google_genai_errors
55
56
  from google.genai import types as google_genai_types
56
57
  from google.api import httpbody_pb2
57
58
  from google.protobuf import struct_pb2
@@ -564,6 +565,23 @@ def _await_operation(
564
565
  return operation
565
566
 
566
567
 
568
+ def _raise_for_operation_error(
569
+ *,
570
+ operation: Any,
571
+ message: str,
572
+ ) -> None:
573
+ """Raises an APIError for a failed operation with a context note."""
574
+ try:
575
+ google_genai_errors.APIError.raise_error(
576
+ operation.error.get("code"), operation.error, None
577
+ )
578
+ except google_genai_errors.APIError as e:
579
+ # TODO(yifany): Drop guard once Python 3.10 support is removed.
580
+ if sys.version_info >= (3, 11):
581
+ e.add_note(message)
582
+ raise
583
+
584
+
567
585
  def _compare_requirements(
568
586
  *,
569
587
  requirements: Mapping[str, str],
@@ -3070,7 +3070,7 @@ class Evals(_api_module.BaseModule):
3070
3070
  events are replayed as session history for local ADK agents.
3071
3071
  model: Optional type is experimental and may change in future versions.
3072
3072
  The model to use for inference, optional for agent evaluations.
3073
- - For Google Gemini models, provide the model name string (e.g., "gemini-2.5-flash").
3073
+ - For Google Gemini models, provide the model name string (e.g., "gemini-flash-latest").
3074
3074
  - For third-party models via LiteLLM, use the format "provider/model_name"
3075
3075
  (e.g., "openai/gpt-4o"). Ensure the necessary API key (e.g., OPENAI_API_KEY)
3076
3076
  is set as an environment variable.
@@ -3342,7 +3342,7 @@ class Evals(_api_module.BaseModule):
3342
3342
  with the content.
3343
3343
  generator_model_config: Optional. Configuration for the model used
3344
3344
  in custom rubric generation. Only used if `prompt_template` is provided.
3345
- e.g., {"autorater_model": "gemini-2.5-flash"}.
3345
+ e.g., {"autorater_model": "gemini-flash-latest"}.
3346
3346
  rubric_content_type: Optional. The type of rubric content to be
3347
3347
  generated. Only used if `prompt_template` is provided.
3348
3348
  rubric_type_ontology: Optional. A pre-defined list of allowed types
@@ -3619,12 +3619,12 @@ class Evals(_api_module.BaseModule):
3619
3619
  If provided, `agent_info` must be None. If omitted and `agent_info` is provided,
3620
3620
  this will be automatically constructed using `agent_info` and `user_simulator_config`.
3621
3621
  The `model` field of an inference config accepts a short Gemini model
3622
- name (e.g. `gemini-2.5-flash`), which is automatically expanded to a
3622
+ name (e.g. `gemini-flash-latest`), which is automatically expanded to a
3623
3623
  fully-qualified resource name using the client's project and location,
3624
3624
  or an already fully-qualified publisher-model or endpoint resource
3625
3625
  name.
3626
3626
  Example:
3627
- {"candidate-1": types.EvaluationRunInferenceConfig(model="gemini-2.5-flash")}
3627
+ {"candidate-1": types.EvaluationRunInferenceConfig(model="gemini-flash-latest")}
3628
3628
  labels: The labels to apply to the evaluation run.
3629
3629
  loss_analysis_metrics: This field is experimental and may change in future
3630
3630
  versions. Optional list of metrics to run loss analysis on. The
@@ -6261,12 +6261,12 @@ class AsyncEvals(_api_module.BaseModule):
6261
6261
  If provided, `agent_info` must be None. If omitted and `agent_info` is provided,
6262
6262
  this will be automatically constructed using `agent_info` and `user_simulator_config`.
6263
6263
  The `model` field of an inference config accepts a short Gemini model
6264
- name (e.g. `gemini-2.5-flash`), which is automatically expanded to a
6264
+ name (e.g. `gemini-flash-latest`), which is automatically expanded to a
6265
6265
  fully-qualified resource name using the client's project and location,
6266
6266
  or an already fully-qualified publisher-model or endpoint resource
6267
6267
  name.
6268
6268
  Example:
6269
- {"candidate-1": types.EvaluationRunInferenceConfig(model="gemini-2.5-flash")}
6269
+ {"candidate-1": types.EvaluationRunInferenceConfig(model="gemini-flash-latest")}
6270
6270
  red_teaming_config: This field is experimental and may change in future
6271
6271
  versions. Optional configuration for automated Agent Red Teaming
6272
6272
  analysis. Specifies attack categories and vulnerable tools to
@@ -1911,6 +1911,7 @@ class Runtimes(_api_module.BaseModule):
1911
1911
  container_spec=config.container_spec,
1912
1912
  keep_alive_probe=keep_alive_probe,
1913
1913
  build_config=config.build_config,
1914
+ agent_card=config.agent_card,
1914
1915
  )
1915
1916
  operation = self._create(config=api_config)
1916
1917
  logger.info(f"Create Agent Runtime backing LRO: {operation.name}")
@@ -1944,8 +1945,12 @@ class Runtimes(_api_module.BaseModule):
1944
1945
  f"runtime=client.runtimes.get(name='{runtime.api_resource.name}')"
1945
1946
  )
1946
1947
  elif operation.error:
1947
- raise RuntimeError(
1948
- f"Operation {operation.name} failed to create Agent Runtime: {operation.error}"
1948
+ _runtimes_utils._raise_for_operation_error(
1949
+ operation=operation,
1950
+ message=(
1951
+ f"Operation {operation.name} failed to create Agent Runtime:"
1952
+ f" {operation.error}"
1953
+ ),
1949
1954
  )
1950
1955
  else:
1951
1956
  logger.warning("The operation returned an empty response.")
@@ -2226,6 +2231,7 @@ class Runtimes(_api_module.BaseModule):
2226
2231
  keep_alive_probe: Optional[dict[str, Any]] = None,
2227
2232
  traffic_config: Optional[types.ReasoningEngineTrafficConfigDict] = None,
2228
2233
  build_config: Optional[types.ReasoningEngineSpecBuildSpecDict] = None,
2234
+ agent_card: Optional[dict[str, Any]] = None,
2229
2235
  ) -> types.UpdateRuntimeConfigDict:
2230
2236
  import sys
2231
2237
 
@@ -2400,14 +2406,15 @@ class Runtimes(_api_module.BaseModule):
2400
2406
  agent=agent,
2401
2407
  )
2402
2408
 
2403
- if hasattr(agent, "agent_card"):
2404
- agent_card = getattr(agent, "agent_card")
2405
- if agent_card:
2409
+ # An agent_card given in the config takes precedence; it is added below.
2410
+ if agent_card is None and hasattr(agent, "agent_card"):
2411
+ agent_object_card = getattr(agent, "agent_card")
2412
+ if agent_object_card:
2406
2413
  try:
2407
2414
  from google.protobuf import json_format
2408
2415
 
2409
2416
  runtime_spec["agent_card"] = json_format.MessageToDict(
2410
- agent_card
2417
+ agent_object_card
2411
2418
  )
2412
2419
  except Exception as e:
2413
2420
  raise ValueError(
@@ -2448,6 +2455,12 @@ class Runtimes(_api_module.BaseModule):
2448
2455
  if build_spec:
2449
2456
  runtime_spec["build_spec"] = build_spec
2450
2457
 
2458
+ if agent_card is not None:
2459
+ if runtime_spec is None:
2460
+ runtime_spec = {}
2461
+ runtime_spec["agent_card"] = agent_card
2462
+ update_masks.append("spec.agent_card")
2463
+
2451
2464
  if runtime_spec is not None:
2452
2465
  config["spec"] = runtime_spec
2453
2466
 
@@ -2720,6 +2733,7 @@ class Runtimes(_api_module.BaseModule):
2720
2733
  keep_alive_probe=keep_alive_probe,
2721
2734
  traffic_config=traffic_config,
2722
2735
  build_config=config.build_config,
2736
+ agent_card=config.agent_card,
2723
2737
  )
2724
2738
  operation = self._update(name=name, config=api_config)
2725
2739
  logger.info(f"Update Agent Runtime backing LRO: {operation.name}")
@@ -2747,8 +2761,12 @@ class Runtimes(_api_module.BaseModule):
2747
2761
  f"runtime=client.runtimes.get(name='{runtime.api_resource.name}')"
2748
2762
  )
2749
2763
  elif operation.error:
2750
- raise RuntimeError(
2751
- f"Operation {operation.name} failed to update Agent Runtime: {operation.error}"
2764
+ _runtimes_utils._raise_for_operation_error(
2765
+ operation=operation,
2766
+ message=(
2767
+ f"Operation {operation.name} failed to update Agent Runtime:"
2768
+ f" {operation.error}"
2769
+ ),
2752
2770
  )
2753
2771
  if runtime.api_resource.spec:
2754
2772
  self._register_api_methods(runtime=runtime)
@@ -2238,7 +2238,7 @@ class EvaluationRunInferenceConfig(_common.BaseModel):
2238
2238
  )
2239
2239
  model: Optional[str] = Field(
2240
2240
  default=None,
2241
- description="""The model to use for inference. Accepts a short Gemini model name (e.g. `gemini-2.5-flash`), which is automatically expanded to a fully-qualified resource name using the client's project and location, or an already fully-qualified publisher-model or endpoint resource name (e.g. `projects/{project}/locations/{location}/publishers/google/models/gemini-2.5-flash`).""",
2241
+ description="""The model to use for inference. Accepts a short Gemini model name (e.g. `gemini-flash-latest`), which is automatically expanded to a fully-qualified resource name using the client's project and location, or an already fully-qualified publisher-model or endpoint resource name (e.g. `projects/{project}/locations/{location}/publishers/google/models/gemini-flash-latest`).""",
2242
2242
  )
2243
2243
  prompt_template: Optional[EvaluationRunPromptTemplate] = Field(
2244
2244
  default=None, description="""The prompt template used for inference."""
@@ -2260,7 +2260,7 @@ class EvaluationRunInferenceConfigDict(TypedDict, total=False):
2260
2260
  """The agent config."""
2261
2261
 
2262
2262
  model: Optional[str]
2263
- """The model to use for inference. Accepts a short Gemini model name (e.g. `gemini-2.5-flash`), which is automatically expanded to a fully-qualified resource name using the client's project and location, or an already fully-qualified publisher-model or endpoint resource name (e.g. `projects/{project}/locations/{location}/publishers/google/models/gemini-2.5-flash`)."""
2263
+ """The model to use for inference. Accepts a short Gemini model name (e.g. `gemini-flash-latest`), which is automatically expanded to a fully-qualified resource name using the client's project and location, or an already fully-qualified publisher-model or endpoint resource name (e.g. `projects/{project}/locations/{location}/publishers/google/models/gemini-flash-latest`)."""
2264
2264
 
2265
2265
  prompt_template: Optional[EvaluationRunPromptTemplateDict]
2266
2266
  """The prompt template used for inference."""
@@ -31000,7 +31000,7 @@ class Prompt(_common.BaseModel):
31000
31000
 
31001
31001
  my_prompt = types.Prompt(
31002
31002
  prompt_data=types.PromptData(
31003
- model="gemini-2.0-flash-001",
31003
+ model="gemini-flash-latest",
31004
31004
  contents=[
31005
31005
  genai_types.Content(
31006
31006
  parts=[
@@ -32104,6 +32104,16 @@ class AgentRuntimeConfig(_common.BaseModel):
32104
32104
  default=None,
32105
32105
  description="""The build config for the Agent Runtime. Allows bringing your own Cloud Build private worker pool (BYOBP) and, optionally, a build-time service account for the container build. Supported keys: `worker_pool` (the resource name of the Cloud Build WorkerPool to use for the build) and `service_account` (the service account that Cloud Build uses to run the build; only applicable when `worker_pool` is specified).""",
32106
32106
  )
32107
+ agent_card: Optional[dict[str, Any]] = Field(
32108
+ default=None,
32109
+ description="""The A2A agent card for the Agent Runtime, as a dictionary with
32110
+ the A2A JSON field names (camelCase, such as `preferredTransport`).
32111
+ Use it when there is no `agent` object to read the card from, for
32112
+ example with `container_spec` or `source_packages`. If the `agent` also
32113
+ has an `agent_card`, this value is used instead. On `update()`, it
32114
+ replaces the deployed card; pass `{}` to remove it. The service sets
32115
+ the card's `url` to the runtime's A2A endpoint.""",
32116
+ )
32107
32117
 
32108
32118
 
32109
32119
  class AgentRuntimeConfigDict(TypedDict, total=False):
@@ -32294,6 +32304,15 @@ class AgentRuntimeConfigDict(TypedDict, total=False):
32294
32304
  build_config: Optional[ReasoningEngineSpecBuildSpecDict]
32295
32305
  """The build config for the Agent Runtime. Allows bringing your own Cloud Build private worker pool (BYOBP) and, optionally, a build-time service account for the container build. Supported keys: `worker_pool` (the resource name of the Cloud Build WorkerPool to use for the build) and `service_account` (the service account that Cloud Build uses to run the build; only applicable when `worker_pool` is specified)."""
32296
32306
 
32307
+ agent_card: Optional[dict[str, Any]]
32308
+ """The A2A agent card for the Agent Runtime, as a dictionary with
32309
+ the A2A JSON field names (camelCase, such as `preferredTransport`).
32310
+ Use it when there is no `agent` object to read the card from, for
32311
+ example with `container_spec` or `source_packages`. If the `agent` also
32312
+ has an `agent_card`, this value is used instead. On `update()`, it
32313
+ replaces the deployed card; pass `{}` to remove it. The service sets
32314
+ the card's `url` to the runtime's A2A endpoint."""
32315
+
32297
32316
 
32298
32317
  AgentRuntimeConfigOrDict = Union[AgentRuntimeConfig, AgentRuntimeConfigDict]
32299
32318
 
@@ -14,4 +14,4 @@
14
14
  #
15
15
  """Version file for agentplatform."""
16
16
 
17
- __version__ = "2.4.0"
17
+ __version__ = "2.5.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-cloud-agentplatform
3
- Version: 2.4.0
3
+ Version: 2.5.0
4
4
  Summary: Google Cloud Agent Platform Client Library
5
5
  Author-email: Google LLC <googleapis-packages@google.com>
6
6
  License: Apache-2.0
@@ -31,7 +31,7 @@ Requires-Dist: google-cloud-storage<4.0.0,>=1.32.0; extra == "agent-engines"
31
31
  Requires-Dist: opentelemetry-sdk<2; extra == "agent-engines"
32
32
  Requires-Dist: opentelemetry-exporter-gcp-logging<2.0.0,>=1.11.0a0; extra == "agent-engines"
33
33
  Requires-Dist: opentelemetry-exporter-otlp-proto-http<2; extra == "agent-engines"
34
- Requires-Dist: opentelemetry-instrumentation-google-genai<1.0.0,>=0.3b0; extra == "agent-engines"
34
+ Requires-Dist: opentelemetry-instrumentation-google-genai<2.0.0,>=0.3b0; extra == "agent-engines"
35
35
  Requires-Dist: pydantic<3,>=2.11.1; extra == "agent-engines"
36
36
  Provides-Extra: a2a
37
37
  Requires-Dist: google-cloud-agentplatform[agent_engines]; extra == "a2a"
@@ -28,7 +28,7 @@ google-cloud-storage<4.0.0,>=1.32.0
28
28
  opentelemetry-sdk<2
29
29
  opentelemetry-exporter-gcp-logging<2.0.0,>=1.11.0a0
30
30
  opentelemetry-exporter-otlp-proto-http<2
31
- opentelemetry-instrumentation-google-genai<1.0.0,>=0.3b0
31
+ opentelemetry-instrumentation-google-genai<2.0.0,>=0.3b0
32
32
  pydantic<3,>=2.11.1
33
33
 
34
34
  [all]
@@ -42,7 +42,7 @@ agent_engines = [
42
42
  "opentelemetry-sdk < 2",
43
43
  "opentelemetry-exporter-gcp-logging >= 1.11.0a0, < 2.0.0",
44
44
  "opentelemetry-exporter-otlp-proto-http < 2",
45
- "opentelemetry-instrumentation-google-genai >= 0.3b0, < 1.0.0",
45
+ "opentelemetry-instrumentation-google-genai >= 0.3b0, < 2.0.0",
46
46
  "pydantic >= 2.11.1, < 3",
47
47
  ]
48
48
  a2a = [