agent-starter-pack 0.18.2__py3-none-any.whl → 0.21.0__py3-none-any.whl

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 (114) hide show
  1. agent_starter_pack/agents/{langgraph_base_react → adk_a2a_base}/.template/templateconfig.yaml +5 -12
  2. agent_starter_pack/agents/adk_a2a_base/README.md +37 -0
  3. agent_starter_pack/{frontends/streamlit/frontend/style/app_markdown.py → agents/adk_a2a_base/app/__init__.py} +3 -23
  4. agent_starter_pack/agents/adk_a2a_base/app/agent.py +70 -0
  5. agent_starter_pack/agents/adk_a2a_base/notebooks/adk_a2a_app_testing.ipynb +583 -0
  6. agent_starter_pack/agents/{crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb → adk_a2a_base/notebooks/evaluating_adk_agent.ipynb} +163 -199
  7. agent_starter_pack/agents/adk_a2a_base/tests/integration/test_agent.py +58 -0
  8. agent_starter_pack/agents/adk_base/app/__init__.py +2 -2
  9. agent_starter_pack/agents/adk_base/app/agent.py +3 -0
  10. agent_starter_pack/agents/adk_base/notebooks/adk_app_testing.ipynb +13 -28
  11. agent_starter_pack/agents/adk_live/app/__init__.py +17 -0
  12. agent_starter_pack/agents/adk_live/app/agent.py +3 -0
  13. agent_starter_pack/agents/agentic_rag/app/__init__.py +2 -2
  14. agent_starter_pack/agents/agentic_rag/app/agent.py +3 -0
  15. agent_starter_pack/agents/agentic_rag/notebooks/adk_app_testing.ipynb +13 -28
  16. agent_starter_pack/agents/{crewai_coding_crew → langgraph_base}/.template/templateconfig.yaml +12 -9
  17. agent_starter_pack/agents/langgraph_base/README.md +30 -0
  18. agent_starter_pack/agents/langgraph_base/app/__init__.py +17 -0
  19. agent_starter_pack/agents/{langgraph_base_react → langgraph_base}/app/agent.py +4 -4
  20. agent_starter_pack/agents/{langgraph_base_react → langgraph_base}/tests/integration/test_agent.py +1 -1
  21. agent_starter_pack/base_template/.gitignore +4 -2
  22. agent_starter_pack/base_template/Makefile +110 -16
  23. agent_starter_pack/base_template/README.md +97 -12
  24. agent_starter_pack/base_template/deployment/terraform/dev/apis.tf +4 -6
  25. agent_starter_pack/base_template/deployment/terraform/dev/providers.tf +5 -1
  26. agent_starter_pack/base_template/deployment/terraform/dev/variables.tf +5 -3
  27. agent_starter_pack/base_template/deployment/terraform/dev/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +193 -0
  28. agent_starter_pack/base_template/deployment/terraform/github.tf +16 -9
  29. agent_starter_pack/base_template/deployment/terraform/locals.tf +7 -7
  30. agent_starter_pack/base_template/deployment/terraform/providers.tf +5 -1
  31. agent_starter_pack/base_template/deployment/terraform/sql/completions.sql +138 -0
  32. agent_starter_pack/base_template/deployment/terraform/storage.tf +0 -9
  33. agent_starter_pack/base_template/deployment/terraform/variables.tf +15 -19
  34. agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} +20 -22
  35. agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +206 -0
  36. agent_starter_pack/base_template/pyproject.toml +5 -17
  37. agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +19 -4
  38. agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +36 -11
  39. agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +24 -5
  40. agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +44 -9
  41. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/telemetry.py +96 -0
  42. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/{utils → app_utils}/typing.py +4 -6
  43. agent_starter_pack/{agents/crewai_coding_crew/app/crew/config/agents.yaml → base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}converters{% else %}unused_converters{% endif %}/__init__.py } +9 -23
  44. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}converters{% else %}unused_converters{% endif %}/part_converter.py +138 -0
  45. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}executor{% else %}unused_executor{% endif %}/__init__.py +13 -0
  46. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}executor{% else %}unused_executor{% endif %}/a2a_agent_executor.py +265 -0
  47. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}executor{% else %}unused_executor{% endif %}/task_result_aggregator.py +152 -0
  48. agent_starter_pack/cli/commands/create.py +40 -4
  49. agent_starter_pack/cli/commands/enhance.py +1 -1
  50. agent_starter_pack/cli/commands/register_gemini_enterprise.py +1070 -0
  51. agent_starter_pack/cli/main.py +2 -0
  52. agent_starter_pack/cli/utils/cicd.py +20 -4
  53. agent_starter_pack/cli/utils/template.py +257 -25
  54. agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +113 -16
  55. agent_starter_pack/deployment_targets/agent_engine/tests/load_test/README.md +2 -2
  56. agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py +178 -9
  57. agent_starter_pack/deployment_targets/agent_engine/tests/{% if cookiecutter.is_a2a %}helpers.py{% else %}unused_helpers.py{% endif %} +138 -0
  58. agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +193 -307
  59. agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/app_utils/deploy.py +414 -0
  60. agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/{utils → app_utils}/{% if cookiecutter.is_adk_live %}expose_app.py{% else %}unused_expose_app.py{% endif %} +13 -14
  61. agent_starter_pack/deployment_targets/cloud_run/Dockerfile +4 -1
  62. agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +85 -86
  63. agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/service.tf +139 -107
  64. agent_starter_pack/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +228 -12
  65. agent_starter_pack/deployment_targets/cloud_run/tests/load_test/README.md +4 -4
  66. agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +92 -12
  67. agent_starter_pack/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/{server.py → fast_api_app.py} +194 -121
  68. agent_starter_pack/frontends/adk_live_react/frontend/package-lock.json +18 -18
  69. agent_starter_pack/frontends/adk_live_react/frontend/src/multimodal-live-types.ts +5 -3
  70. agent_starter_pack/resources/docs/adk-cheatsheet.md +198 -41
  71. agent_starter_pack/resources/locks/uv-adk_a2a_base-agent_engine.lock +4966 -0
  72. agent_starter_pack/resources/locks/uv-adk_a2a_base-cloud_run.lock +5011 -0
  73. agent_starter_pack/resources/locks/uv-adk_base-agent_engine.lock +1443 -709
  74. agent_starter_pack/resources/locks/uv-adk_base-cloud_run.lock +1058 -874
  75. agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +1443 -709
  76. agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +1058 -874
  77. agent_starter_pack/resources/locks/uv-agentic_rag-agent_engine.lock +1568 -749
  78. agent_starter_pack/resources/locks/uv-agentic_rag-cloud_run.lock +1123 -929
  79. agent_starter_pack/resources/locks/{uv-langgraph_base_react-agent_engine.lock → uv-langgraph_base-agent_engine.lock} +1714 -1689
  80. agent_starter_pack/resources/locks/{uv-langgraph_base_react-cloud_run.lock → uv-langgraph_base-cloud_run.lock} +1285 -2374
  81. agent_starter_pack/utils/watch_and_rebuild.py +1 -1
  82. {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/METADATA +3 -6
  83. {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/RECORD +89 -93
  84. agent_starter_pack-0.21.0.dist-info/entry_points.txt +2 -0
  85. llm.txt +4 -5
  86. agent_starter_pack/agents/crewai_coding_crew/README.md +0 -34
  87. agent_starter_pack/agents/crewai_coding_crew/app/agent.py +0 -47
  88. agent_starter_pack/agents/crewai_coding_crew/app/crew/config/tasks.yaml +0 -37
  89. agent_starter_pack/agents/crewai_coding_crew/app/crew/crew.py +0 -71
  90. agent_starter_pack/agents/crewai_coding_crew/tests/integration/test_agent.py +0 -47
  91. agent_starter_pack/agents/langgraph_base_react/README.md +0 -9
  92. agent_starter_pack/agents/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb +0 -1574
  93. agent_starter_pack/base_template/deployment/terraform/dev/log_sinks.tf +0 -69
  94. agent_starter_pack/base_template/deployment/terraform/log_sinks.tf +0 -79
  95. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/utils/tracing.py +0 -155
  96. agent_starter_pack/cli/utils/register_gemini_enterprise.py +0 -406
  97. agent_starter_pack/deployment_targets/agent_engine/deployment/terraform/{% if not cookiecutter.is_adk_live %}service.tf{% else %}unused_service.tf{% endif %} +0 -82
  98. agent_starter_pack/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -1025
  99. agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/deployment.py +0 -99
  100. agent_starter_pack/frontends/streamlit/frontend/side_bar.py +0 -214
  101. agent_starter_pack/frontends/streamlit/frontend/streamlit_app.py +0 -265
  102. agent_starter_pack/frontends/streamlit/frontend/utils/chat_utils.py +0 -67
  103. agent_starter_pack/frontends/streamlit/frontend/utils/local_chat_history.py +0 -127
  104. agent_starter_pack/frontends/streamlit/frontend/utils/message_editing.py +0 -59
  105. agent_starter_pack/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -217
  106. agent_starter_pack/frontends/streamlit/frontend/utils/stream_handler.py +0 -310
  107. agent_starter_pack/frontends/streamlit/frontend/utils/title_summary.py +0 -94
  108. agent_starter_pack/resources/locks/uv-crewai_coding_crew-agent_engine.lock +0 -6650
  109. agent_starter_pack/resources/locks/uv-crewai_coding_crew-cloud_run.lock +0 -7825
  110. agent_starter_pack-0.18.2.dist-info/entry_points.txt +0 -3
  111. /agent_starter_pack/agents/{crewai_coding_crew → langgraph_base}/notebooks/evaluating_langgraph_agent.ipynb +0 -0
  112. /agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/{utils → app_utils}/gcs.py +0 -0
  113. {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/WHEEL +0 -0
  114. {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/licenses/LICENSE +0 -0
@@ -30,14 +30,12 @@ from google.adk.memory.in_memory_memory_service import InMemoryMemoryService
30
30
  from google.adk.runners import Runner
31
31
  from google.adk.sessions.in_memory_session_service import InMemorySessionService
32
32
  from google.cloud import logging as google_cloud_logging
33
- from opentelemetry import trace
34
- from opentelemetry.sdk.trace import TracerProvider, export
35
33
  from vertexai.agent_engines import _utils
36
34
  from websockets.exceptions import ConnectionClosedError
37
35
 
38
- from .agent import root_agent
39
- from .utils.tracing import CloudTraceLoggingSpanExporter
40
- from .utils.typing import Feedback
36
+ from .agent import app as adk_app
37
+ from .app_utils.telemetry import setup_telemetry
38
+ from .app_utils.typing import Feedback
41
39
 
42
40
  app = FastAPI()
43
41
  app.add_middleware(
@@ -62,13 +60,8 @@ logging_client = google_cloud_logging.Client()
62
60
  logger = logging_client.logger(__name__)
63
61
  logging.basicConfig(level=logging.INFO)
64
62
 
63
+ setup_telemetry()
65
64
  _, project_id = google.auth.default()
66
- provider = TracerProvider()
67
- processor = export.BatchSpanProcessor(
68
- CloudTraceLoggingSpanExporter(project_id=project_id)
69
- )
70
- provider.add_span_processor(processor)
71
- trace.set_tracer_provider(provider)
72
65
 
73
66
 
74
67
  # Initialize ADK services
@@ -78,11 +71,10 @@ memory_service = InMemoryMemoryService()
78
71
 
79
72
  # Initialize ADK runner
80
73
  runner = Runner(
81
- agent=root_agent,
74
+ app=adk_app,
82
75
  session_service=session_service,
83
76
  artifact_service=artifact_service,
84
77
  memory_service=memory_service,
85
- app_name="live-app",
86
78
  )
87
79
 
88
80
 
@@ -291,67 +283,163 @@ async def serve_frontend_spa(full_path: str) -> FileResponse:
291
283
  )
292
284
  {% elif cookiecutter.is_adk %}
293
285
  import os
286
+ {%- if cookiecutter.is_a2a %}
287
+ from collections.abc import AsyncIterator
288
+ from contextlib import asynccontextmanager
289
+ {%- endif %}
290
+ {%- if cookiecutter.session_type == "cloud_sql" %}
291
+ from urllib.parse import quote
292
+ {%- endif %}
294
293
 
295
294
  import google.auth
295
+ {%- if cookiecutter.is_a2a %}
296
+ from a2a.server.apps import A2AFastAPIApplication
297
+ from a2a.server.request_handlers import DefaultRequestHandler
298
+ from a2a.server.tasks import InMemoryTaskStore
299
+ from a2a.types import AgentCapabilities, AgentCard
300
+ from a2a.utils.constants import (
301
+ AGENT_CARD_WELL_KNOWN_PATH,
302
+ EXTENDED_AGENT_CARD_PATH,
303
+ )
304
+ {%- endif %}
296
305
  from fastapi import FastAPI
306
+ {%- if cookiecutter.is_a2a %}
307
+ from google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor
308
+ from google.adk.a2a.utils.agent_card_builder import AgentCardBuilder
309
+ from google.adk.artifacts.gcs_artifact_service import GcsArtifactService
310
+ from google.adk.runners import Runner
311
+ from google.adk.sessions import InMemorySessionService
312
+ {%- else %}
297
313
  from google.adk.cli.fast_api import get_fast_api_app
314
+ {%- endif %}
298
315
  from google.cloud import logging as google_cloud_logging
299
- from opentelemetry import trace
300
- from opentelemetry.sdk.trace import TracerProvider, export
301
- {%- if cookiecutter.session_type == "agent_engine" %}
316
+ {% if cookiecutter.session_type == "agent_engine" -%}
302
317
  from vertexai import agent_engines
303
- {%- endif %}
318
+ {% endif %}
304
319
 
305
- from {{cookiecutter.agent_directory}}.utils.gcs import create_bucket_if_not_exists
306
- from {{cookiecutter.agent_directory}}.utils.tracing import CloudTraceLoggingSpanExporter
307
- from {{cookiecutter.agent_directory}}.utils.typing import Feedback
320
+ {%- if cookiecutter.is_a2a %}
321
+ from {{cookiecutter.agent_directory}}.agent import app as adk_app
322
+ {%- endif %}
323
+ from {{cookiecutter.agent_directory}}.app_utils.gcs import create_bucket_if_not_exists
324
+ from {{cookiecutter.agent_directory}}.app_utils.telemetry import setup_telemetry
325
+ from {{cookiecutter.agent_directory}}.app_utils.typing import Feedback
308
326
 
327
+ setup_telemetry()
309
328
  _, project_id = google.auth.default()
310
329
  logging_client = google_cloud_logging.Client()
311
330
  logger = logging_client.logger(__name__)
331
+ {%- if not cookiecutter.is_a2a %}
312
332
  allow_origins = (
313
333
  os.getenv("ALLOW_ORIGINS", "").split(",") if os.getenv("ALLOW_ORIGINS") else None
314
334
  )
335
+ {%- endif %}
315
336
 
337
+ # Artifact bucket for ADK
316
338
  bucket_name = f"gs://{project_id}-{{cookiecutter.project_name}}-logs"
317
339
  create_bucket_if_not_exists(
318
340
  bucket_name=bucket_name, project=project_id, location="us-central1"
319
341
  )
320
342
 
321
- provider = TracerProvider()
322
- processor = export.BatchSpanProcessor(CloudTraceLoggingSpanExporter())
323
- provider.add_span_processor(processor)
324
- trace.set_tracer_provider(provider)
343
+ {%- if cookiecutter.is_a2a %}
344
+
345
+ runner = Runner(
346
+ app=adk_app,
347
+ artifact_service=GcsArtifactService(bucket_name=bucket_name),
348
+ session_service=InMemorySessionService(),
349
+ )
350
+
351
+ request_handler = DefaultRequestHandler(
352
+ agent_executor=A2aAgentExecutor(runner=runner), task_store=InMemoryTaskStore()
353
+ )
354
+
355
+ A2A_RPC_PATH = f"/a2a/{adk_app.name}"
356
+
357
+
358
+ async def build_dynamic_agent_card() -> AgentCard:
359
+ """Builds the Agent Card dynamically from the root_agent."""
360
+ agent_card_builder = AgentCardBuilder(
361
+ agent=adk_app.root_agent,
362
+ capabilities=AgentCapabilities(streaming=True),
363
+ rpc_url=f"{os.getenv('APP_URL', 'http://0.0.0.0:8000')}{A2A_RPC_PATH}",
364
+ agent_version=os.getenv("AGENT_VERSION", "0.1.0"),
365
+ )
366
+ agent_card = await agent_card_builder.build()
367
+ return agent_card
368
+
369
+
370
+ @asynccontextmanager
371
+ async def lifespan(app_instance: FastAPI) -> AsyncIterator[None]:
372
+ agent_card = await build_dynamic_agent_card()
373
+ a2a_app = A2AFastAPIApplication(agent_card=agent_card, http_handler=request_handler)
374
+ a2a_app.add_routes_to_app(
375
+ app_instance,
376
+ agent_card_url=f"{A2A_RPC_PATH}{AGENT_CARD_WELL_KNOWN_PATH}",
377
+ rpc_url=A2A_RPC_PATH,
378
+ extended_agent_card_url=f"{A2A_RPC_PATH}{EXTENDED_AGENT_CARD_PATH}",
379
+ )
380
+ yield
381
+
382
+
383
+ app = FastAPI(
384
+ title="{{cookiecutter.project_name}}",
385
+ description="API for interacting with the Agent {{cookiecutter.project_name}}",
386
+ lifespan=lifespan,
387
+ )
388
+ {%- else %}
325
389
 
326
390
  AGENT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
327
391
 
328
- {%- if cookiecutter.session_type == "alloydb" %}
329
- # AlloyDB session configuration
392
+ {%- if cookiecutter.session_type == "cloud_sql" %}
393
+ # Cloud SQL session configuration
330
394
  db_user = os.environ.get("DB_USER", "postgres")
331
395
  db_name = os.environ.get("DB_NAME", "postgres")
332
396
  db_pass = os.environ.get("DB_PASS")
333
- db_host = os.environ.get("DB_HOST")
397
+ instance_connection_name = os.environ.get("INSTANCE_CONNECTION_NAME")
334
398
 
335
- # Set session_service_uri if database credentials are available
336
399
  session_service_uri = None
337
- if db_host and db_pass:
338
- session_service_uri = f"postgresql://{db_user}:{db_pass}@{db_host}:5432/{db_name}"
400
+ if instance_connection_name and db_pass:
401
+ # Use Unix socket for Cloud SQL
402
+ # URL-encode username and password to handle special characters (e.g., '[', '?', '#', '$')
403
+ # These characters can cause URL parsing errors, especially '[' which triggers IPv6 validation
404
+ encoded_user = quote(db_user, safe="")
405
+ encoded_pass = quote(db_pass, safe="")
406
+ # URL-encode the connection name to prevent colons from being misinterpreted
407
+ encoded_instance = instance_connection_name.replace(":", "%3A")
408
+
409
+ session_service_uri = (
410
+ f"postgresql+psycopg2://{encoded_user}:{encoded_pass}@"
411
+ f"/{db_name}"
412
+ f"?host=/cloudsql/{encoded_instance}"
413
+ )
339
414
  {%- elif cookiecutter.session_type == "agent_engine" %}
340
415
  # Agent Engine session configuration
341
- # Use environment variable for agent name, default to project name
342
- agent_name = os.environ.get("AGENT_ENGINE_SESSION_NAME", "{{cookiecutter.project_name}}")
343
-
344
- # Check if an agent with this name already exists
345
- existing_agents = list(agent_engines.list(filter=f"display_name={agent_name}"))
416
+ # Check if we should use in-memory session for testing (set USE_IN_MEMORY_SESSION=true for E2E tests)
417
+ use_in_memory_session = os.environ.get("USE_IN_MEMORY_SESSION", "").lower() in (
418
+ "true",
419
+ "1",
420
+ "yes",
421
+ )
346
422
 
347
- if existing_agents:
348
- # Use the existing agent
349
- agent_engine = existing_agents[0]
423
+ if use_in_memory_session:
424
+ # Use in-memory session for local testing
425
+ session_service_uri = None
350
426
  else:
351
- # Create a new agent if none exists
352
- agent_engine = agent_engines.create(display_name=agent_name)
427
+ # Use environment variable for agent name, default to project name
428
+ agent_name = os.environ.get(
429
+ "AGENT_ENGINE_SESSION_NAME", "{{cookiecutter.project_name}}"
430
+ )
353
431
 
354
- session_service_uri = f"agentengine://{agent_engine.resource_name}"
432
+ # Check if an agent with this name already exists
433
+ existing_agents = list(agent_engines.list(filter=f"display_name={agent_name}"))
434
+
435
+ if existing_agents:
436
+ # Use the existing agent
437
+ agent_engine = existing_agents[0]
438
+ else:
439
+ # Create a new agent if none exists
440
+ agent_engine = agent_engines.create(display_name=agent_name)
441
+
442
+ session_service_uri = f"agentengine://{agent_engine.resource_name}"
355
443
  {%- else %}
356
444
  # In-memory session configuration - no persistent storage
357
445
  session_service_uri = None
@@ -363,103 +451,88 @@ app: FastAPI = get_fast_api_app(
363
451
  artifact_service_uri=bucket_name,
364
452
  allow_origins=allow_origins,
365
453
  session_service_uri=session_service_uri,
454
+ otel_to_cloud=True,
366
455
  )
367
456
  app.title = "{{cookiecutter.project_name}}"
368
457
  app.description = "API for interacting with the Agent {{cookiecutter.project_name}}"
458
+ {%- endif %}
369
459
  {% else %}
370
- import logging
371
460
  import os
372
- from collections.abc import Generator
373
-
461
+ from collections.abc import AsyncIterator
462
+ from contextlib import asynccontextmanager
463
+
464
+ from a2a.server.apps import A2AFastAPIApplication
465
+ from a2a.server.request_handlers import DefaultRequestHandler
466
+ from a2a.server.tasks import InMemoryTaskStore
467
+ from a2a.types import AgentCapabilities, AgentCard, AgentSkill
468
+ from a2a.utils.constants import (
469
+ AGENT_CARD_WELL_KNOWN_PATH,
470
+ EXTENDED_AGENT_CARD_PATH,
471
+ )
374
472
  from fastapi import FastAPI
375
- from fastapi.responses import RedirectResponse, StreamingResponse
376
473
  from google.cloud import logging as google_cloud_logging
377
- from langchain_core.runnables import RunnableConfig
378
- from traceloop.sdk import Instruments, Traceloop
379
-
380
- from {{cookiecutter.agent_directory}}.agent import agent
381
- from {{cookiecutter.agent_directory}}.utils.tracing import CloudTraceLoggingSpanExporter
382
- from {{cookiecutter.agent_directory}}.utils.typing import Feedback, InputChat, Request, dumps, ensure_valid_config
383
474
 
384
- # Initialize FastAPI app and logging
385
- app = FastAPI(
386
- title="{{cookiecutter.project_name}}",
387
- description="API for interacting with the Agent {{cookiecutter.project_name}}",
475
+ from {{cookiecutter.agent_directory}}.agent import root_agent
476
+ from {{cookiecutter.agent_directory}}.app_utils.executor.a2a_agent_executor import (
477
+ LangGraphAgentExecutor,
388
478
  )
389
- logging_client = google_cloud_logging.Client()
390
- logger = logging_client.logger(__name__)
479
+ from {{cookiecutter.agent_directory}}.app_utils.telemetry import setup_telemetry
480
+ from {{cookiecutter.agent_directory}}.app_utils.typing import Feedback
391
481
 
392
- # Initialize Telemetry
393
- try:
394
- Traceloop.init(
395
- app_name=app.title,
396
- disable_batch=False,
397
- exporter=CloudTraceLoggingSpanExporter(),
398
- instruments={Instruments.LANGCHAIN, Instruments.CREW},
399
- )
400
- except Exception as e:
401
- logging.error("Failed to initialize Telemetry: %s", str(e))
482
+ setup_telemetry()
402
483
 
484
+ request_handler = DefaultRequestHandler(
485
+ agent_executor=LangGraphAgentExecutor(graph=root_agent),
486
+ task_store=InMemoryTaskStore(),
487
+ )
403
488
 
404
- def set_tracing_properties(config: RunnableConfig) -> None:
405
- """Sets tracing association properties for the current request.
406
-
407
- Args:
408
- config: Optional RunnableConfig containing request metadata
409
- """
410
- Traceloop.set_association_properties(
411
- {
412
- "log_type": "tracing",
413
- "run_id": str(config.get("run_id", "None")),
414
- "user_id": config["metadata"].pop("user_id", "None"),
415
- "session_id": config["metadata"].pop("session_id", "None"),
416
- "commit_sha": os.environ.get("COMMIT_SHA", "None"),
417
- }
418
- )
419
-
420
-
421
- def stream_messages(
422
- input: InputChat,
423
- config: RunnableConfig | None = None,
424
- ) -> Generator[str, None, None]:
425
- """Stream events in response to an input chat.
426
-
427
- Args:
428
- input: The input chat messages
429
- config: Optional configuration for the runnable
430
-
431
- Yields:
432
- JSON serialized event data
433
- """
434
- config = ensure_valid_config(config=config)
435
- set_tracing_properties(config)
436
- input_dict = input.model_dump()
437
-
438
- for data in agent.stream(input_dict, config=config, stream_mode="messages"):
439
- yield dumps(data) + "\n"
440
-
489
+ A2A_RPC_PATH = "/a2a/{{cookiecutter.agent_directory}}"
441
490
 
442
- # Routes
443
- @app.get("/", response_class=RedirectResponse)
444
- def redirect_root_to_docs() -> RedirectResponse:
445
- """Redirect the root URL to the API documentation."""
446
- return RedirectResponse(url="/docs")
447
491
 
492
+ def build_agent_card() -> AgentCard:
493
+ """Builds the Agent Card for the LangGraph agent."""
494
+ skill = AgentSkill(
495
+ id="root_agent-get_weather",
496
+ name="get_weather",
497
+ description="Simulates a web search. Use it get information on weather.",
498
+ tags=["llm", "tools"],
499
+ examples=["What's the weather in San Francisco?"],
500
+ )
501
+ agent_card = AgentCard(
502
+ name="root_agent",
503
+ description="API for interacting with the Agent {{cookiecutter.project_name}}",
504
+ url=f"{os.getenv('APP_URL', 'http://0.0.0.0:8000')}{A2A_RPC_PATH}",
505
+ version=os.getenv("AGENT_VERSION", "0.1.0"),
506
+ default_input_modes=["text/plain"],
507
+ default_output_modes=["text/plain"],
508
+ capabilities=AgentCapabilities(streaming=True),
509
+ skills=[skill],
510
+ )
511
+ return agent_card
512
+
513
+
514
+ @asynccontextmanager
515
+ async def lifespan(app_instance: FastAPI) -> AsyncIterator[None]:
516
+ agent_card = build_agent_card()
517
+ a2a_app = A2AFastAPIApplication(agent_card=agent_card, http_handler=request_handler)
518
+ a2a_app.add_routes_to_app(
519
+ app_instance,
520
+ agent_card_url=f"{A2A_RPC_PATH}{AGENT_CARD_WELL_KNOWN_PATH}",
521
+ rpc_url=A2A_RPC_PATH,
522
+ extended_agent_card_url=f"{A2A_RPC_PATH}{EXTENDED_AGENT_CARD_PATH}",
523
+ )
524
+ yield
448
525
 
449
- @app.post("/stream_messages")
450
- def stream_chat_events(request: Request) -> StreamingResponse:
451
- """Stream chat events in response to an input request.
452
526
 
453
- Args:
454
- request: The chat request containing input and config
527
+ # Initialize FastAPI app and logging
528
+ app = FastAPI(
529
+ title="{{cookiecutter.project_name}}",
530
+ description="API for interacting with the Agent {{cookiecutter.project_name}}",
531
+ lifespan=lifespan,
532
+ )
455
533
 
456
- Returns:
457
- Streaming response of chat events
458
- """
459
- return StreamingResponse(
460
- stream_messages(input=request.input, config=request.config),
461
- media_type="text/event-stream",
462
- )
534
+ logging_client = google_cloud_logging.Client()
535
+ logger = logging_client.logger(__name__)
463
536
  {% endif %}
464
537
 
465
538
  @app.post("/feedback")
@@ -2594,9 +2594,9 @@
2594
2594
  }
2595
2595
  },
2596
2596
  "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
2597
- "version": "4.1.0",
2598
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
2599
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
2597
+ "version": "4.1.1",
2598
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
2599
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
2600
2600
  "license": "MIT",
2601
2601
  "dependencies": {
2602
2602
  "argparse": "^2.0.1"
@@ -5856,9 +5856,9 @@
5856
5856
  "license": "ISC"
5857
5857
  },
5858
5858
  "node_modules/brace-expansion": {
5859
- "version": "1.1.11",
5860
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
5861
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
5859
+ "version": "1.1.12",
5860
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
5861
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
5862
5862
  "license": "MIT",
5863
5863
  "dependencies": {
5864
5864
  "balanced-match": "^1.0.0",
@@ -8283,9 +8283,9 @@
8283
8283
  }
8284
8284
  },
8285
8285
  "node_modules/eslint/node_modules/js-yaml": {
8286
- "version": "4.1.0",
8287
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
8288
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
8286
+ "version": "4.1.1",
8287
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
8288
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
8289
8289
  "license": "MIT",
8290
8290
  "dependencies": {
8291
8291
  "argparse": "^2.0.1"
@@ -8702,9 +8702,9 @@
8702
8702
  }
8703
8703
  },
8704
8704
  "node_modules/filelist/node_modules/brace-expansion": {
8705
- "version": "2.0.1",
8706
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
8707
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
8705
+ "version": "2.0.2",
8706
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
8707
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
8708
8708
  "license": "MIT",
8709
8709
  "dependencies": {
8710
8710
  "balanced-match": "^1.0.0"
@@ -11520,9 +11520,9 @@
11520
11520
  "license": "MIT"
11521
11521
  },
11522
11522
  "node_modules/js-yaml": {
11523
- "version": "3.14.1",
11524
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
11525
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
11523
+ "version": "3.14.2",
11524
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
11525
+ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
11526
11526
  "license": "MIT",
11527
11527
  "dependencies": {
11528
11528
  "argparse": "^1.0.7",
@@ -16402,9 +16402,9 @@
16402
16402
  }
16403
16403
  },
16404
16404
  "node_modules/sucrase/node_modules/brace-expansion": {
16405
- "version": "2.0.1",
16406
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
16407
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
16405
+ "version": "2.0.2",
16406
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
16407
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
16408
16408
  "license": "MIT",
16409
16409
  "dependencies": {
16410
16410
  "balanced-match": "^1.0.0"
@@ -244,7 +244,8 @@ export const isToolCallCancellation = (
244
244
 
245
245
  // ADK Event types
246
246
  export interface AdkEvent {
247
- invocation_id: string;
247
+ user_id: string;
248
+ session_id: string;
248
249
  author: string;
249
250
  actions: {
250
251
  state_delta: any;
@@ -265,9 +266,10 @@ export interface AdkEvent {
265
266
 
266
267
  // ADK Event type guards
267
268
  export const isAdkEvent = (a: unknown): a is AdkEvent =>
268
- typeof a === "object" &&
269
+ typeof a === "object" &&
269
270
  a !== null &&
270
- typeof (a as any).invocation_id === "string" &&
271
+ typeof (a as any).user_id === "string" &&
272
+ typeof (a as any).session_id === "string" &&
271
273
  typeof (a as any).author === "string" &&
272
274
  typeof (a as any).actions === "object";
273
275