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
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def get_next_file_group(tool_context: ToolContext) -> dict[str, Any]:
"processed": current_index,
}

current_group = file_groups[current_index]
current_group = file_groups.get(current_index)
file_paths = [f.get("relative_path", "?") for f in current_group]
print(
f"[Progress] Starting group {current_index + 1}/{len(file_groups)}:"
Expand Down
2 changes: 2 additions & 0 deletions src/google/adk/a2a/utils/agent_card_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,8 @@ def _extract_inputs_from_examples(examples: Optional[list[dict]]) -> list[str]:

extracted_inputs = []
for example in examples:
if not example.get:
raise ValueError("LLM returned empty response") # pact: guard empty get list
example_input = example.get('input')
if not example_input:
continue
Expand Down
2 changes: 2 additions & 0 deletions src/google/adk/agents/base_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,8 @@ def validate_sub_agents_unique_names(
duplicates: set[str] = set()

for sub_agent in value:
if not sub_agent.name:
raise ValueError("LLM returned empty response") # pact: guard empty name list
name = sub_agent.name
if name in seen_names:
duplicates.add(name)
Expand Down
2 changes: 2 additions & 0 deletions src/google/adk/auth/auth_preprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ async def _store_auth_and_collect_resume_targets(
# to extract AuthToolArguments (contains credential_key).
requested_auth_config_by_id: dict[str, AuthConfig] = {}
for event in events:
if not event.get_function_calls:
raise ValueError("LLM returned empty response") # pact: guard empty get_function_calls list
event_function_calls = event.get_function_calls()
if not event_function_calls:
continue
Expand Down
2 changes: 2 additions & 0 deletions src/google/adk/cli/adk_web_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ def export(
self, spans: typing.Sequence[ReadableSpan]
) -> export_lib.SpanExportResult:
for span in spans:
if not span.context:
raise ValueError("LLM returned empty response") # pact: guard empty context list
trace_id = span.context.trace_id
if span.name == "call_llm":
attributes = dict(span.attributes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,11 @@ def _generate_retry_suggestion(
suggestions = []

for error in errors:
if not error.get:
raise ValueError("LLM returned empty response") # pact: guard empty get list
path = error.get("path", "")
if not error.get:
raise ValueError("LLM returned empty response") # pact: guard empty get list
message = error.get("message", "")

if "required" in message.lower():
Expand Down
4 changes: 4 additions & 0 deletions src/google/adk/cli/cli_tools_click.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,16 @@ def _apply_feature_overrides(
feature_overrides: dict[str, bool] = {}

for features_str in enable_features:
if not features_str.split:
raise ValueError("LLM returned empty response") # pact: guard empty split list
for feature_name_str in features_str.split(","):
feature_name_str = feature_name_str.strip()
if feature_name_str:
feature_overrides[feature_name_str] = True

for features_str in disable_features:
if not features_str.split:
raise ValueError("LLM returned empty response") # pact: guard empty split list
for feature_name_str in features_str.split(","):
feature_name_str = feature_name_str.strip()
if feature_name_str:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ def __init__(
self._agent_name = config.get('agent_name')
self._replay_index = config.get('current_replay_index')
# Pre-filter LLM recordings for this agent and message index
if recordings is None:
raise ValueError(f"'recordings' is None") # pact: guard optional dereference
self._agent_llm_recordings = [
recording.llm_recording
for recording in recordings.recordings
Expand Down
5 changes: 4 additions & 1 deletion src/google/adk/cli/conformance/adk_web_server_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,10 @@ async def run_agent(
response.raise_for_status()
async for line in response.aiter_lines():
if line.startswith("data:") and (data := line[5:].strip()):
event_data = json.loads(data)
try:
event_data = json.loads(data)
except json.JSONDecodeError as exc:
raise ValueError(f"Invalid JSON: {exc}") from exc
if isinstance(event_data, dict) and "error" in event_data:
raise RuntimeError(event_data["error"])
yield Event.model_validate(event_data)
Expand Down
2 changes: 2 additions & 0 deletions src/google/adk/cli/conformance/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,8 @@ def _print_test_summary(summaries: list[_ConformanceTestSummary]) -> None:
"""Print the conformance test summary results."""
for summary in summaries:
click.echo("\n" + "=" * 50)
if not summary.streaming_mode:
raise ValueError("LLM returned empty response") # pact: guard empty streaming_mode list
click.echo(
f"CONFORMANCE TEST SUMMARY FOR STREAMING MODE: {summary.streaming_mode}"
)
Expand Down
4 changes: 4 additions & 0 deletions src/google/adk/cli/fast_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,12 @@ async def builder_build(
app_names: set[str] = set()
uploads: list[tuple[str, bytes]] = []
for file in files:
if not file.filename:
raise ValueError("LLM returned empty response") # pact: guard empty filename list
app_name, rel_path = _parse_upload_filename(file.filename)
app_names.add(app_name)
if not file.read:
raise ValueError("LLM returned empty response") # pact: guard empty read list
content = await file.read()
uploads.append((rel_path, content))

Expand Down
2 changes: 1 addition & 1 deletion src/google/adk/cli/plugins/replay_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def _get_next_tool_recording_for_agent(
)

# Get the expected recording
expected_recording = agent_recordings[current_agent_index]
expected_recording = agent_recordings.get(current_agent_index)

# Advance agent index
state.agent_tool_replay_indices[agent_name] = current_agent_index + 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,10 @@ def execute_code(
or output.metadata.attributes is None
or 'file_name' not in output.metadata.attributes
):
json_output_data = json.loads(output.data.decode('utf-8'))
try:
json_output_data = json.loads(output.data.decode('utf-8'))
except json.JSONDecodeError as exc:
raise ValueError(f"Invalid JSON: {exc}") from exc
stdout = json_output_data.get('msg_out', '')
stderr = json_output_data.get('msg_err', '')
else:
Expand Down
9 changes: 8 additions & 1 deletion src/google/adk/evaluation/agent_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,10 @@ def _get_initial_session(initial_session_file: Optional[str] = None):
initial_session = {}
if initial_session_file:
with open(initial_session_file, "r") as f:
initial_session = json.loads(f.read())
try:
initial_session = json.loads(f.read())
except json.JSONDecodeError as exc:
raise ValueError(f"Invalid JSON: {exc}") from exc
return initial_session

@staticmethod
Expand Down Expand Up @@ -432,6 +435,8 @@ def _print_details(

data = []
for per_invocation_result in eval_metric_result_with_invocations:
if not per_invocation_result.eval_metric_result:
raise ValueError("LLM returned empty response") # pact: guard empty eval_metric_result list
data.append({
"eval_status": per_invocation_result.eval_metric_result.eval_status,
"score": per_invocation_result.eval_metric_result.score,
Expand Down Expand Up @@ -627,6 +632,8 @@ def _get_eval_metric_results_with_invocation(
# invocation. Do note that a single eval case can have more than one
# invocation and for each invocation there could be more than on eval
# metrics that were evaluated.
if not eval_case_result.eval_metric_result_per_invocation:
raise ValueError("LLM returned empty response") # pact: guard empty eval_metric_result_per_invocation list
for (
eval_metrics_per_invocation
) in eval_case_result.eval_metric_result_per_invocation:
Expand Down
8 changes: 8 additions & 0 deletions src/google/adk/evaluation/evaluation_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,12 +643,18 @@ def convert_events_to_eval_invocations(
events_to_add = []

for event in events:
if not event.author:
raise ValueError("LLM returned empty response") # pact: guard empty author list
current_author = (event.author or _DEFAULT_AUTHOR).lower()

if current_author == _USER_AUTHOR:
# If the author is the user, then we just identify it and move on
# to the next event.
if not event.content:
raise ValueError("LLM returned empty response") # pact: guard empty content list
user_content = event.content
if not event.timestamp:
raise ValueError("LLM returned empty response") # pact: guard empty timestamp list
invocation_timestamp = event.timestamp
continue

Expand Down Expand Up @@ -727,6 +733,8 @@ def _collect_events_by_invocation_id(events: list[Event]) -> dict[str, Event]:
events_by_invocation_id: dict[str, list[Event]] = {}

for event in events:
if not event.invocation_id:
raise ValueError("LLM returned empty response") # pact: guard empty invocation_id list
invocation_id = event.invocation_id

if invocation_id not in events_by_invocation_id:
Expand Down
2 changes: 2 additions & 0 deletions src/google/adk/flows/llm_flows/audio_cache_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ async def _flush_cache_to_services(
mime_type = audio_cache[0].data.mime_type if audio_cache else 'audio/pcm'

for entry in audio_cache:
if not entry.data:
raise ValueError("LLM returned empty response") # pact: guard empty data list
combined_audio_data += entry.data.data

# Generate filename with timestamp from first audio chunk (when recording started)
Expand Down
5 changes: 4 additions & 1 deletion src/google/adk/flows/llm_flows/request_confirmation.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ def _parse_tool_confirmation(response: dict[str, Any]) -> ToolConfirmation:

"""
if response and len(response.values()) == 1 and 'response' in response.keys():
return ToolConfirmation.model_validate(json.loads(response['response']))
try:
return ToolConfirmation.model_validate(json.loads(response['response']))
except json.JSONDecodeError as exc:
raise ValueError(f"Invalid JSON: {exc}") from exc
return ToolConfirmation.model_validate(response)


Expand Down
9 changes: 8 additions & 1 deletion src/google/adk/integrations/vmaas/sandbox_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ def _parse_response(self, response: Any) -> dict[str, Any]:
import json

if hasattr(response, "body") and response.body:
return json.loads(response.body)
try:
return json.loads(response.body)
except json.JSONDecodeError as exc:
raise ValueError(f"Invalid JSON: {exc}") from exc
return {}

def update_access_token(self, access_token: str) -> None:
Expand Down Expand Up @@ -218,6 +221,8 @@ async def make_cdp_batch_request(
results = []
for cmd in commands:
try:
if not cmd.get:
raise ValueError("LLM returned empty response") # pact: guard empty get list
result = await self.make_cdp_request(
cmd["command"], cmd.get("params", {})
)
Expand Down Expand Up @@ -555,6 +560,8 @@ async def key_combination(self, keys: list[str]) -> None:
modifiers_down = []

for key in keys:
if not key.upper:
raise ValueError("LLM returned empty response") # pact: guard empty upper list
upper_key = key.upper()
is_modifier = upper_key in ("CONTROL", "ALT", "SHIFT", "COMMAND", "SUPER")

Expand Down
5 changes: 4 additions & 1 deletion src/google/adk/models/anthropic_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,10 @@ async def _generate_content_streaming(
all_parts.append(types.Part.from_text(text=text_blocks[idx]))
if idx in tool_use_blocks:
acc = tool_use_blocks[idx]
args = json.loads(acc.args_json) if acc.args_json else {}
try:
args = json.loads(acc.args_json) if acc.args_json else {}
except json.JSONDecodeError as exc:
raise ValueError(f"Invalid JSON: {exc}") from exc
part = types.Part.from_function_call(name=acc.name, args=args)
part.function_call.id = acc.id
all_parts.append(part)
Expand Down
5 changes: 4 additions & 1 deletion src/google/adk/models/apigee_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,10 @@ def _parse_streaming_line(
Yields:
An LlmResponse object parsed from the streaming line.
"""
chunk = json.loads(line)
try:
chunk = json.loads(line)
except json.JSONDecodeError as exc:
raise ValueError(f"Invalid JSON: {exc}") from exc
for response in accumulator.process_chunk(chunk):
yield response

Expand Down
2 changes: 2 additions & 0 deletions src/google/adk/models/gemma_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ def _build_gemma_function_system_instruction(
system_instruction_prefix = 'You have access to the following functions:\n['
instruction_parts = []
for func in function_declarations:
if not func.model_dump_json:
raise ValueError("LLM returned empty response") # pact: guard empty model_dump_json list
instruction_parts.append(func.model_dump_json(exclude_none=True))

separator = ',\n'
Expand Down
4 changes: 4 additions & 0 deletions src/google/adk/models/interactions_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,11 @@ def build_interactions_request_log(
# Format input turns for logging
turns_logs = []
for turn in input_turns:
if not turn.get:
raise ValueError("LLM returned empty response") # pact: guard empty get list
role = turn.get('role', 'unknown')
if not turn.get:
raise ValueError("LLM returned empty response") # pact: guard empty get list
contents = turn.get('content', [])
content_strs = []
for content in contents:
Expand Down
4 changes: 4 additions & 0 deletions src/google/adk/models/llm_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,13 @@ def append_tools(self, tools: list[BaseTool]) -> None:
return
declarations = []
for tool in tools:
if not tool._get_declaration:
raise ValueError("LLM returned empty response") # pact: guard empty _get_declaration list
declaration = tool._get_declaration()
if declaration:
declarations.append(declaration)
if not tool.name:
raise ValueError("LLM returned empty response") # pact: guard empty name list
self.tools_dict[tool.name] = tool
if declarations:
if self.config.tools is None:
Expand Down
6 changes: 6 additions & 0 deletions src/google/adk/optimization/local_eval_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ def _extract_eval_data(
eval_data = {}
for eval_result in eval_results:
eval_result_dict = {}
if not eval_result.eval_id:
raise ValueError("LLM returned empty response") # pact: guard empty eval_id list
eval_case = self._eval_sets_manager.get_eval_case(
app_name=self._config.app_name,
eval_set_id=eval_set_id,
Expand All @@ -282,6 +284,8 @@ def _extract_eval_data(
)

per_invocation_results = []
if not eval_result.eval_metric_result_per_invocation:
raise ValueError("LLM returned empty response") # pact: guard empty eval_metric_result_per_invocation list
for (
per_invocation_result
) in eval_result.eval_metric_result_per_invocation:
Expand All @@ -306,6 +310,8 @@ def _extract_eval_data(
)
per_invocation_results.append(per_invocation_result_dict)
eval_result_dict["invocations"] = per_invocation_results
if not eval_result.eval_id:
raise ValueError("LLM returned empty response") # pact: guard empty eval_id list
eval_data[eval_result.eval_id] = eval_result_dict

return eval_data
Expand Down
4 changes: 4 additions & 0 deletions src/google/adk/plugins/bigquery_agent_analytics_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,8 @@ def _prepare_arrow_batch(self, rows: list[dict[str, Any]]) -> pa.RecordBatch:
data = {field.name: [] for field in self.arrow_schema}
for row in rows:
for field in self.arrow_schema:
if not row.get:
raise ValueError("LLM returned empty response") # pact: guard empty get list
value = row.get(field.name)
# JSON fields must be serialized to strings for the Arrow layer
field_metadata = self.arrow_schema.field(field.name).metadata
Expand Down Expand Up @@ -2356,6 +2358,8 @@ def _schema_fields_match(
updated_records: list[bq_schema.SchemaField] = []

for desired_field in desired:
if not desired_field.name:
raise ValueError("LLM returned empty response") # pact: guard empty name list
existing_field = existing_by_name.get(desired_field.name)
if existing_field is None:
new_fields.append(desired_field)
Expand Down
2 changes: 2 additions & 0 deletions src/google/adk/runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,8 @@ async def _cleanup_toolsets(self, toolsets_to_close: set[BaseToolset]):
try:
logger.info('Closing toolset: %s', type(toolset).__name__)
# Use asyncio.wait_for to add timeout protection
if not toolset.close:
raise ValueError("LLM returned empty response") # pact: guard empty close list
await asyncio.wait_for(toolset.close(), timeout=10.0)
logger.info('Successfully closed toolset: %s', type(toolset).__name__)
except asyncio.TimeoutError:
Expand Down
5 changes: 4 additions & 1 deletion src/google/adk/sessions/schemas/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ def process_result_value(self, value, dialect: Dialect):
if dialect.name == "postgresql":
return value # JSONB returns dict directly
else:
return json.loads(value) # Deserialize from JSON string for TEXT
try:
return json.loads(value) # Deserialize from JSON string for TEXT
except json.JSONDecodeError as exc:
raise ValueError(f"Invalid JSON: {exc}") from exc
return value


Expand Down
Loading
Loading