Skip to content

Commit 6c5b392

Browse files
authored
Fix load test error detection and add Vertex AI initialization (#493)
* fix: Improve load test error detection and initialize Vertex AI - Add error detection for HTTP 4xx/5xx responses in SSE streams - Initialize Vertex AI client with project ID for Agent Engine deployments - Add logging configuration to track error responses during load tests ## Problem Load tests were not detecting errors returned within SSE response streams, causing false positives when the server returned error codes in the JSON payload. Additionally, Vertex AI client was not properly initialized, causing potential failures in Agent Engine deployments. ## Solution - Parse JSON payloads in SSE streams to detect error codes >= 400 - Mark requests as failures and log error details when errors are detected - Initialize vertexai with project ID and location before creating agent engine - Add logging configuration to capture error details during load testing * fix: Correct import order for nest_asyncio in a2a agents Ruff requires imports to be alphabetically sorted. Move nest_asyncio import before vertexai import to satisfy linting requirements for adk_a2a agents.
1 parent ac30ab8 commit 6c5b392

File tree

3 files changed

+79
-17
lines changed

3 files changed

+79
-17
lines changed

agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ def chat_stream(self) -> None:
365365
) as response:
366366
if response.status_code == 200:
367367
events = []
368+
has_error = False
368369
for line in response.iter_lines():
369370
if line:
370371
line_str = line.decode("utf-8")
@@ -379,20 +380,44 @@ def chat_stream(self) -> None:
379380
response=response,
380381
context={},
381382
)
383+
384+
# Check for error responses in the JSON payload
385+
try:
386+
event_data = json.loads(line_str)
387+
if isinstance(event_data, dict) and "code" in event_data:
388+
# Flag any non-2xx codes as errors
389+
if event_data["code"] >= 400:
390+
has_error = True
391+
error_msg = event_data.get(
392+
"message", "Unknown error"
393+
)
394+
response.failure(f"Error in response: {error_msg}")
395+
logger.error(
396+
"Received error response: code=%s, message=%s",
397+
event_data["code"],
398+
error_msg,
399+
)
400+
except json.JSONDecodeError:
401+
# If it's not valid JSON, continue processing
402+
pass
403+
382404
end_time = time.time()
383405
total_time = end_time - start_time
384-
self.environment.events.request.fire(
385-
request_type="POST",
406+
407+
# Only fire success event if no errors were found
408+
if not has_error:
409+
self.environment.events.request.fire(
410+
request_type="POST",
386411
{%- if cookiecutter.is_adk %}
387-
name="/streamQuery end",
412+
name="/streamQuery end",
388413
{%- else %}
389-
name="/stream_messages end",
414+
name="/stream_messages end",
390415
{%- endif %}
391-
response_time=total_time * 1000, # Convert to milliseconds
392-
response_length=len(events),
393-
response=response,
394-
context={},
395-
)
416+
response_time=total_time * 1000, # Convert to milliseconds
417+
response_length=len(events),
418+
response=response,
419+
context={},
420+
)
396421
else:
397422
response.failure(f"Unexpected status code: {response.status_code}")
398423
{%- endif %}

agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
{%- if cookiecutter.is_adk_a2a %}
2626
import nest_asyncio
2727
{%- endif %}
28+
import vertexai
2829
{%- if cookiecutter.is_adk_a2a %}
2930
from a2a.types import AgentCapabilities, AgentCard, TransportProtocol
3031
from google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor
@@ -162,6 +163,7 @@ def clone(self) -> "AgentEngineApp":
162163

163164

164165
_, project_id = google.auth.default()
166+
vertexai.init(project=project_id, location="us-central1")
165167
artifacts_bucket_name = os.environ.get("ARTIFACTS_BUCKET_NAME")
166168
{%- if cookiecutter.is_adk_a2a %}
167169
agent_engine = AgentEngineApp.create(
@@ -193,6 +195,7 @@ def clone(self) -> "AgentEngineApp":
193195
)
194196

195197
import google.auth
198+
import vertexai
196199
from google.cloud import logging as google_cloud_logging
197200
from langchain_core.runnables import RunnableConfig
198201
from traceloop.sdk import Instruments, Traceloop
@@ -302,5 +305,6 @@ def register_operations(self) -> dict[str, list[str]]:
302305

303306

304307
_, project_id = google.auth.default()
308+
vertexai.init(project=project_id, location="us-central1")
305309
agent_engine = AgentEngineApp(project_id=project_id)
306310
{%- endif %}

agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ class RemoteAgentUser(WebSocketUser):
138138
host = "http://localhost:8000" # Default for local testing
139139
{%- else %}
140140

141+
import json
142+
import logging
141143
import os
142144
import time
143145
{%- if cookiecutter.is_adk_a2a %}
@@ -172,6 +174,12 @@ class RemoteAgentUser(WebSocketUser):
172174
ENDPOINT = "/stream_messages"
173175
{%- endif %}
174176

177+
# Configure logging
178+
logging.basicConfig(
179+
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
180+
)
181+
logger = logging.getLogger(__name__)
182+
175183

176184
class ChatStreamUser(HttpUser):
177185
"""Simulates a user interacting with the chat stream API."""
@@ -267,6 +275,7 @@ def chat_stream(self) -> None:
267275
{%- endif %}
268276
if response.status_code == 200:
269277
events = []
278+
has_error = False
270279
for line in response.iter_lines():
271280
if line:
272281
line_str = line.decode("utf-8")
@@ -281,16 +290,40 @@ def chat_stream(self) -> None:
281290
response=response,
282291
context={},
283292
)
293+
294+
# Check for error responses in the JSON payload
295+
try:
296+
event_data = json.loads(line_str)
297+
if isinstance(event_data, dict) and "code" in event_data:
298+
# Flag any non-2xx codes as errors
299+
if event_data["code"] >= 400:
300+
has_error = True
301+
error_msg = event_data.get(
302+
"message", "Unknown error"
303+
)
304+
response.failure(f"Error in response: {error_msg}")
305+
logger.error(
306+
"Received error response: code=%s, message=%s",
307+
event_data["code"],
308+
error_msg,
309+
)
310+
except json.JSONDecodeError:
311+
# If it's not valid JSON, continue processing
312+
pass
313+
284314
end_time = time.time()
285315
total_time = end_time - start_time
286-
self.environment.events.request.fire(
287-
request_type="POST",
288-
name=f"{ENDPOINT} end",
289-
response_time=total_time * 1000, # Convert to milliseconds
290-
response_length=len(events),
291-
response=response,
292-
context={},
293-
)
316+
317+
# Only fire success event if no errors were found
318+
if not has_error:
319+
self.environment.events.request.fire(
320+
request_type="POST",
321+
name=f"{ENDPOINT} end",
322+
response_time=total_time * 1000, # Convert to milliseconds
323+
response_length=len(events),
324+
response=response,
325+
context={},
326+
)
294327
else:
295328
response.failure(f"Unexpected status code: {response.status_code}")
296329
{%- endif %}

0 commit comments

Comments
 (0)