google-cloud-agentplatform 2.3.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.
- {google_cloud_agentplatform-2.3.0/google_cloud_agentplatform.egg-info → google_cloud_agentplatform-2.5.0}/PKG-INFO +2 -2
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_common.py +105 -92
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_constant.py +14 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_data_converters.py +306 -1
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_metric_handlers.py +29 -20
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_metric_loaders.py +9 -4
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_visualization.py +18 -1
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_runtimes_utils.py +18 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_transformers.py +5 -1
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/evals.py +20 -6
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/runtimes.py +30 -6
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/types/__init__.py +6 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/types/common.py +162 -9
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/adk.py +35 -18
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/version.py +1 -1
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0/google_cloud_agentplatform.egg-info}/PKG-INFO +2 -2
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/google_cloud_agentplatform.egg-info/requires.txt +1 -1
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/pyproject.toml +1 -1
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/LICENSE +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/README.md +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/__init__.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/__init__.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_bigquery_utils.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_datasets_utils.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_builtin_tools.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_evals_utils.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_gcs_utils.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_logging_utils.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_memory_bank_utils.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_observability_data_converter.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_operations_utils.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_prompt_management_utils.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_prompt_optimizer_utils.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/_skills_utils.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/a2a_task_events.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/a2a_tasks.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/client.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/datasets.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/endpoints.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/example_stores.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/feedback_contexts.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/feedback_entries.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/live.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/live_runtimes.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/memories.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/memory_banks.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/memory_revisions.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/model_garden.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/prompt_optimizer.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/prompts.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/rag.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/runtime_revisions.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/sandbox_snapshots.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/sandbox_templates.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/sandboxes.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/serving_profiles.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/session_events.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/sessions.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/skill_revisions.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/skills.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/types/agent_engines.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/types/evals.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/types/prompt_optimizer.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/_genai/types/prompts.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/__init__.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/a2a.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/ag2.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/langchain.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/langgraph.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/agentplatform/frameworks/llama_index.py +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/google_cloud_agentplatform.egg-info/SOURCES.txt +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/google_cloud_agentplatform.egg-info/dependency_links.txt +0 -0
- {google_cloud_agentplatform-2.3.0 → google_cloud_agentplatform-2.5.0}/google_cloud_agentplatform.egg-info/top_level.txt +0 -0
- {google_cloud_agentplatform-2.3.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.
|
|
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<
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
attempt + 1
|
|
241
|
-
|
|
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)
|
|
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
|
|
@@ -2654,7 +2683,7 @@ def _execute_inference(
|
|
|
2654
2683
|
"Gemini Agent inference completed in %.2f seconds.",
|
|
2655
2684
|
end_time - start_time,
|
|
2656
2685
|
)
|
|
2657
|
-
|
|
2686
|
+
evaluation_dataset = types.EvaluationDataset(
|
|
2658
2687
|
eval_dataset_df=results_df,
|
|
2659
2688
|
candidate_name=gemini_agent.split("/")[-1],
|
|
2660
2689
|
)
|
|
@@ -2759,7 +2788,12 @@ def _execute_inference(
|
|
|
2759
2788
|
results_df.to_json(full_dest_path, orient="records", lines=True)
|
|
2760
2789
|
logger.info("Results saved locally to: %s", full_dest_path)
|
|
2761
2790
|
except Exception as e: # pylint: disable=broad-exception-caught
|
|
2762
|
-
logger.error(
|
|
2791
|
+
logger.error(
|
|
2792
|
+
"Failed to save results to %s. Error: %s",
|
|
2793
|
+
full_dest_path,
|
|
2794
|
+
e,
|
|
2795
|
+
exc_info=True,
|
|
2796
|
+
)
|
|
2763
2797
|
|
|
2764
2798
|
return evaluation_dataset
|
|
2765
2799
|
|
|
@@ -2886,6 +2920,21 @@ def _resolve_dataset_inputs(
|
|
|
2886
2920
|
return processed_eval_dataset, num_response_candidates
|
|
2887
2921
|
|
|
2888
2922
|
|
|
2923
|
+
def _prebuilt_evaluation_run_metric(
|
|
2924
|
+
resolved_metric: types.Metric,
|
|
2925
|
+
) -> types.EvaluationRunMetric:
|
|
2926
|
+
"""Builds the evaluation run metric for a resolved RubricMetric."""
|
|
2927
|
+
if resolved_metric.name in _evals_constant.SUPPORTED_PREDEFINED_METRICS:
|
|
2928
|
+
metric_config = t.t_metrics([resolved_metric])[0]
|
|
2929
|
+
else:
|
|
2930
|
+
metric_config = {
|
|
2931
|
+
"predefined_metric_spec": {"metric_spec_name": resolved_metric.name}
|
|
2932
|
+
}
|
|
2933
|
+
return types.EvaluationRunMetric(
|
|
2934
|
+
metric=resolved_metric.name, metric_config=metric_config
|
|
2935
|
+
)
|
|
2936
|
+
|
|
2937
|
+
|
|
2889
2938
|
def _resolve_evaluation_run_metrics(
|
|
2890
2939
|
metrics: Union[list[types.EvaluationRunMetric], list[types.Metric]], api_client: Any
|
|
2891
2940
|
) -> list[types.EvaluationRunMetric]:
|
|
@@ -2903,14 +2952,7 @@ def _resolve_evaluation_run_metrics(
|
|
|
2903
2952
|
resolved_metric = metric_instance.resolve(api_client=api_client)
|
|
2904
2953
|
if resolved_metric.name:
|
|
2905
2954
|
resolved_metrics_list.append(
|
|
2906
|
-
|
|
2907
|
-
metric=resolved_metric.name,
|
|
2908
|
-
metric_config=types.UnifiedMetric(
|
|
2909
|
-
predefined_metric_spec=genai_types.PredefinedMetricSpec(
|
|
2910
|
-
metric_spec_name=resolved_metric.name,
|
|
2911
|
-
)
|
|
2912
|
-
),
|
|
2913
|
-
)
|
|
2955
|
+
_prebuilt_evaluation_run_metric(resolved_metric)
|
|
2914
2956
|
)
|
|
2915
2957
|
except Exception as e:
|
|
2916
2958
|
logger.error(
|
|
@@ -2944,14 +2986,7 @@ def _resolve_evaluation_run_metrics(
|
|
|
2944
2986
|
)
|
|
2945
2987
|
if resolved_metric.name:
|
|
2946
2988
|
resolved_metrics_list.append(
|
|
2947
|
-
|
|
2948
|
-
metric=resolved_metric.name,
|
|
2949
|
-
metric_config=types.UnifiedMetric(
|
|
2950
|
-
predefined_metric_spec=genai_types.PredefinedMetricSpec(
|
|
2951
|
-
metric_spec_name=resolved_metric.name,
|
|
2952
|
-
)
|
|
2953
|
-
),
|
|
2954
|
-
)
|
|
2989
|
+
_prebuilt_evaluation_run_metric(resolved_metric)
|
|
2955
2990
|
)
|
|
2956
2991
|
else:
|
|
2957
2992
|
raise TypeError(
|
|
@@ -3020,6 +3055,7 @@ def _execute_evaluation( # type: ignore[no-untyped-def]
|
|
|
3020
3055
|
dest: Optional[str] = None,
|
|
3021
3056
|
location: Optional[str] = None,
|
|
3022
3057
|
evaluation_service_qps: Optional[float] = None,
|
|
3058
|
+
allow_cross_region_model: Optional[bool] = None,
|
|
3023
3059
|
**kwargs,
|
|
3024
3060
|
) -> types.EvaluationResult:
|
|
3025
3061
|
"""Evaluates a dataset using the provided metrics.
|
|
@@ -3035,6 +3071,8 @@ def _execute_evaluation( # type: ignore[no-untyped-def]
|
|
|
3035
3071
|
evaluation_service_qps: The rate limit (queries per second) for calls
|
|
3036
3072
|
to the evaluation service. Defaults to 10. Increase this value if
|
|
3037
3073
|
your project has a higher EvaluateInstances API quota.
|
|
3074
|
+
allow_cross_region_model: Opt-in flag to authorize cross-region
|
|
3075
|
+
routing for judge models.
|
|
3038
3076
|
**kwargs: Extra arguments to pass to evaluation, such as `agent_info`.
|
|
3039
3077
|
|
|
3040
3078
|
Returns:
|
|
@@ -3117,6 +3155,7 @@ def _execute_evaluation( # type: ignore[no-untyped-def]
|
|
|
3117
3155
|
evaluation_result = _evals_metric_handlers.compute_metrics_and_aggregate(
|
|
3118
3156
|
evaluation_run_config,
|
|
3119
3157
|
evaluation_service_qps=evaluation_service_qps,
|
|
3158
|
+
allow_cross_region_model=allow_cross_region_model,
|
|
3120
3159
|
)
|
|
3121
3160
|
t2 = time.perf_counter()
|
|
3122
3161
|
logger.info("Evaluation took: %f seconds", t2 - t1)
|
|
@@ -3471,7 +3510,9 @@ def _create_runtime_session(
|
|
|
3471
3510
|
raise RuntimeError(
|
|
3472
3511
|
"Failed to create session: runtime.api_client is None."
|
|
3473
3512
|
) from exc
|
|
3474
|
-
|
|
3513
|
+
from . import sessions
|
|
3514
|
+
|
|
3515
|
+
operation = sessions.Sessions(runtime.api_client._api_client).create(
|
|
3475
3516
|
name=runtime.api_resource.name,
|
|
3476
3517
|
user_id=user_id,
|
|
3477
3518
|
config=types.CreateRuntimeSessionConfig(
|
|
@@ -3535,10 +3576,12 @@ def _execute_agent_run_with_retry(
|
|
|
3535
3576
|
return {"error": "runtime.api_resource is None."}
|
|
3536
3577
|
if runtime.api_client is None:
|
|
3537
3578
|
return {"error": "runtime.api_client is None."}
|
|
3579
|
+
from . import sessions
|
|
3580
|
+
|
|
3538
3581
|
session_name = f"{runtime.api_resource.name}/sessions/{session_id}"
|
|
3539
3582
|
base_ts = datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc)
|
|
3540
3583
|
for i, ag_event in enumerate(history_events):
|
|
3541
|
-
runtime.api_client.
|
|
3584
|
+
sessions.Sessions(runtime.api_client._api_client).events.append(
|
|
3542
3585
|
name=session_name,
|
|
3543
3586
|
author=ag_event.author or "user",
|
|
3544
3587
|
invocation_id="history",
|
|
@@ -3560,28 +3603,11 @@ def _execute_agent_run_with_retry(
|
|
|
3560
3603
|
if event and CONTENT in event and PARTS in event[CONTENT]:
|
|
3561
3604
|
responses.append(event)
|
|
3562
3605
|
return responses
|
|
3563
|
-
except api_exceptions.ResourceExhausted as e:
|
|
3564
|
-
logger.warning(
|
|
3565
|
-
"Resource Exhausted error on attempt %d/%d: %s. Retrying in %s"
|
|
3566
|
-
" seconds...",
|
|
3567
|
-
attempt + 1,
|
|
3568
|
-
max_retries,
|
|
3569
|
-
e,
|
|
3570
|
-
2**attempt,
|
|
3571
|
-
)
|
|
3572
|
-
if attempt == max_retries - 1:
|
|
3573
|
-
return {"error": f"Resource exhausted after retries: {e}"}
|
|
3574
|
-
time.sleep(2**attempt)
|
|
3575
3606
|
except Exception as e: # pylint: disable=broad-exception-caught
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
attempt + 1
|
|
3579
|
-
|
|
3580
|
-
e,
|
|
3581
|
-
)
|
|
3582
|
-
if attempt == max_retries - 1:
|
|
3583
|
-
return {"error": f"Failed after retries: {e}"}
|
|
3584
|
-
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)
|
|
3585
3611
|
return {"error": f"Failed to get agent run results after {max_retries} retries"}
|
|
3586
3612
|
|
|
3587
3613
|
|
|
@@ -3613,7 +3639,7 @@ async def _execute_local_agent_run_with_retry_async(
|
|
|
3613
3639
|
# Lazy-import ADK dependencies to avoid top-level import failures when
|
|
3614
3640
|
# google-adk is not installed.
|
|
3615
3641
|
from google.adk.runners import Runner
|
|
3616
|
-
from google.adk.sessions import InMemorySessionService
|
|
3642
|
+
from google.adk.sessions.in_memory_session_service import InMemorySessionService
|
|
3617
3643
|
|
|
3618
3644
|
# Multi-turn agent scraping with user simulation.
|
|
3619
3645
|
if user_simulator_config or "conversation_plan" in row:
|
|
@@ -3687,28 +3713,13 @@ async def _execute_local_agent_run_with_retry_async(
|
|
|
3687
3713
|
if event and CONTENT in event and PARTS in event[CONTENT]:
|
|
3688
3714
|
events.append(event)
|
|
3689
3715
|
return events
|
|
3690
|
-
except api_exceptions.ResourceExhausted as e:
|
|
3691
|
-
logger.warning(
|
|
3692
|
-
"Resource Exhausted error on attempt %d/%d: %s. Retrying"
|
|
3693
|
-
" in %s seconds...",
|
|
3694
|
-
attempt + 1,
|
|
3695
|
-
max_retries,
|
|
3696
|
-
e,
|
|
3697
|
-
2**attempt,
|
|
3698
|
-
)
|
|
3699
|
-
if attempt == max_retries - 1:
|
|
3700
|
-
return {"error": f"Resource exhausted after retries: {e}"}
|
|
3701
|
-
await asyncio.sleep(2**attempt)
|
|
3702
3716
|
except Exception as e: # pylint: disable=broad-exception-caught
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
)
|
|
3709
|
-
if attempt == max_retries - 1:
|
|
3710
|
-
return {"error": f"Failed after retries: {e}"}
|
|
3711
|
-
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)
|
|
3712
3723
|
return {"error": f"Failed to get agent run results after {max_retries} retries"}
|
|
3713
3724
|
|
|
3714
3725
|
|
|
@@ -3864,7 +3875,7 @@ def _drop_empty_columns(df: "pd.DataFrame") -> "pd.DataFrame":
|
|
|
3864
3875
|
def is_empty(x: Any) -> bool:
|
|
3865
3876
|
if isinstance(x, (list, dict)):
|
|
3866
3877
|
return not x
|
|
3867
|
-
return
|
|
3878
|
+
return _is_missing(x)
|
|
3868
3879
|
|
|
3869
3880
|
cols_to_drop = [col for col in df.columns if df[col].apply(is_empty).all()]
|
|
3870
3881
|
return df.drop(columns=cols_to_drop)
|
|
@@ -4163,6 +4174,8 @@ def _create_evaluation_set_from_dataframe(
|
|
|
4163
4174
|
if _evals_constant.RESPONSE in row or agent_data_obj or intermediate_events:
|
|
4164
4175
|
# Resolve the oneof conflict: prioritize agent_data over flat text
|
|
4165
4176
|
response_text = row.get(_evals_constant.RESPONSE) or None
|
|
4177
|
+
if _is_missing(response_text):
|
|
4178
|
+
response_text = None
|
|
4166
4179
|
|
|
4167
4180
|
if agent_data_obj and response_text:
|
|
4168
4181
|
logger.info(
|
|
@@ -4217,7 +4230,7 @@ def _create_evaluation_set_from_dataframe(
|
|
|
4217
4230
|
prompt = types.EvaluationPrompt(
|
|
4218
4231
|
prompt_template_data=types.PromptTemplateData(values=values)
|
|
4219
4232
|
)
|
|
4220
|
-
elif _evals_constant.PROMPT
|
|
4233
|
+
elif not _is_missing(row.get(_evals_constant.PROMPT)):
|
|
4221
4234
|
prompt = types.EvaluationPrompt(text=row[_evals_constant.PROMPT])
|
|
4222
4235
|
|
|
4223
4236
|
eval_item_requests.append(
|
|
@@ -4225,7 +4238,7 @@ def _create_evaluation_set_from_dataframe(
|
|
|
4225
4238
|
prompt=prompt or None,
|
|
4226
4239
|
golden_response=(
|
|
4227
4240
|
types.CandidateResponse(text=row[_evals_constant.REFERENCE])
|
|
4228
|
-
if _evals_constant.REFERENCE
|
|
4241
|
+
if not _is_missing(row.get(_evals_constant.REFERENCE))
|
|
4229
4242
|
else None
|
|
4230
4243
|
),
|
|
4231
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
|
+
)
|