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
@@ -14,52 +14,115 @@
14
14
 
15
15
  # mypy: disable-error-code="attr-defined,arg-type"
16
16
  {%- if cookiecutter.is_adk %}
17
+ {%- if cookiecutter.is_a2a %}
18
+ import asyncio
19
+ {%- endif %}
17
20
  import logging
18
21
  import os
19
22
  from typing import Any
20
23
 
21
- import click
22
- import google.auth
24
+ {% if cookiecutter.is_a2a -%}
25
+ import nest_asyncio
26
+ {% endif -%}
23
27
  import vertexai
24
- from google.adk.artifacts import GcsArtifactService
28
+ {%- if cookiecutter.is_a2a %}
29
+ from a2a.types import AgentCapabilities, AgentCard, TransportProtocol
30
+ from google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor
31
+ from google.adk.a2a.utils.agent_card_builder import AgentCardBuilder
32
+ from google.adk.apps.app import App
33
+ {%- endif %}
34
+ from google.adk.artifacts import GcsArtifactService, InMemoryArtifactService
35
+ {%- if cookiecutter.is_a2a %}
36
+ from google.adk.runners import Runner
37
+ from google.adk.sessions import InMemorySessionService
38
+ {%- endif %}
25
39
  from google.cloud import logging as google_cloud_logging
26
- from opentelemetry import trace
27
- from opentelemetry.sdk.trace import TracerProvider, export
28
- from vertexai._genai.types import AgentEngine, AgentEngineConfig{%- if cookiecutter.is_adk_live %}, AgentServerMode{%- endif %}
29
40
  {%- if cookiecutter.is_adk_live %}
30
- from vertexai.preview.reasoning_engines import AdkApp
41
+ from vertexai.agent_engines.templates.adk import AdkApp
42
+ from vertexai.preview.reasoning_engines import AdkApp as PreviewAdkApp
43
+ {%- elif cookiecutter.is_a2a %}
44
+ from vertexai.preview.reasoning_engines import A2aAgent
31
45
  {%- else %}
32
46
  from vertexai.agent_engines.templates.adk import AdkApp
33
47
  {%- endif %}
48
+ {%- if cookiecutter.is_adk or cookiecutter.is_adk_live %}
34
49
 
35
- from {{cookiecutter.agent_directory}}.agent import root_agent
36
- from {{cookiecutter.agent_directory}}.utils.deployment import (
37
- parse_env_vars,
38
- print_deployment_success,
39
- write_deployment_metadata,
40
- )
41
- from {{cookiecutter.agent_directory}}.utils.gcs import create_bucket_if_not_exists
42
- from {{cookiecutter.agent_directory}}.utils.tracing import CloudTraceLoggingSpanExporter
43
- from {{cookiecutter.agent_directory}}.utils.typing import Feedback
50
+ from {{cookiecutter.agent_directory}}.agent import app as adk_app
51
+ {%- else %}
52
+
53
+ {%- endif %}
54
+ from {{cookiecutter.agent_directory}}.app_utils.telemetry import setup_telemetry
55
+ from {{cookiecutter.agent_directory}}.app_utils.typing import Feedback
56
+ {%- if cookiecutter.is_a2a %}
57
+
58
+
59
+ class AgentEngineApp(A2aAgent):
60
+ @staticmethod
61
+ def create(
62
+ app: App | None = None,
63
+ artifact_service: Any = None,
64
+ session_service: Any = None,
65
+ ) -> Any:
66
+ """Create an AgentEngineApp instance.
67
+
68
+ This method detects whether it's being called in an async context (like notebooks
69
+ or Agent Engine) and handles agent card creation appropriately.
70
+ """
71
+ if app is None:
72
+ app = adk_app
73
+
74
+ def create_runner() -> Runner:
75
+ """Create a Runner for the AgentEngineApp."""
76
+ return Runner(
77
+ app=app,
78
+ session_service=session_service,
79
+ artifact_service=artifact_service,
80
+ )
81
+
82
+ # Build agent card in an async context if needed
83
+ try:
84
+ asyncio.get_running_loop()
85
+ # Running event loop detected - enable nested asyncio.run()
86
+ nest_asyncio.apply()
87
+ except RuntimeError:
88
+ pass
89
+
90
+ agent_card = asyncio.run(AgentEngineApp.build_agent_card(app=app))
91
+
92
+ return AgentEngineApp(
93
+ agent_executor_builder=lambda: A2aAgentExecutor(runner=create_runner()),
94
+ agent_card=agent_card,
95
+ )
96
+
97
+ @staticmethod
98
+ async def build_agent_card(app: App) -> AgentCard:
99
+ """Builds the Agent Card dynamically from the app."""
100
+ agent_card_builder = AgentCardBuilder(
101
+ agent=app.root_agent,
102
+ # Agent Engine does not support streaming yet
103
+ capabilities=AgentCapabilities(streaming=False),
104
+ rpc_url="http://localhost:9999/",
105
+ agent_version=os.getenv("AGENT_VERSION", "0.1.0"),
106
+ )
107
+ agent_card = await agent_card_builder.build()
108
+ agent_card.preferred_transport = TransportProtocol.http_json # Http Only.
109
+ agent_card.supports_authenticated_extended_card = True
110
+ return agent_card
111
+ {% else %}
44
112
 
45
113
 
46
114
  class AgentEngineApp(AdkApp):
115
+ {%- endif %}
47
116
  def set_up(self) -> None:
48
- """Set up logging and tracing for the agent engine app."""
49
- import logging
50
-
117
+ """Initialize the agent engine app with logging and telemetry."""
118
+ vertexai.init()
119
+ setup_telemetry()
51
120
  super().set_up()
52
121
  logging.basicConfig(level=logging.INFO)
53
122
  logging_client = google_cloud_logging.Client()
54
123
  self.logger = logging_client.logger(__name__)
55
- provider = TracerProvider()
56
- processor = export.BatchSpanProcessor(
57
- CloudTraceLoggingSpanExporter(
58
- project_id=os.environ.get("GOOGLE_CLOUD_PROJECT")
59
- )
60
- )
61
- provider.add_span_processor(processor)
62
- trace.set_tracer_provider(provider)
124
+ if gemini_location:
125
+ os.environ["GOOGLE_CLOUD_LOCATION"] = gemini_location
63
126
 
64
127
  def register_feedback(self, feedback: dict[str, Any]) -> None:
65
128
  """Collect and log feedback."""
@@ -67,311 +130,134 @@ class AgentEngineApp(AdkApp):
67
130
  self.logger.log_struct(feedback_obj.model_dump(), severity="INFO")
68
131
 
69
132
  def register_operations(self) -> dict[str, list[str]]:
70
- """Registers the operations of the Agent.
71
-
72
- Extends the base operations to include feedback registration functionality.
73
- """
133
+ """Registers the operations of the Agent."""
74
134
  operations = super().register_operations()
75
135
  operations[""] = operations.get("", []) + ["register_feedback"]
136
+ {%- if cookiecutter.is_adk_live %}
137
+ # Add bidi_stream_query for adk_live
138
+ operations["bidi_stream"] = ["bidi_stream_query"]
139
+ {%- endif %}
76
140
  return operations
141
+ {%- if cookiecutter.is_a2a %}
142
+
143
+ def clone(self) -> "AgentEngineApp":
144
+ """Returns a clone of the Agent Engine application."""
145
+ return self
146
+ {%- endif %}
147
+ {%- if cookiecutter.is_adk_live %}
148
+
149
+
150
+ # Add bidi_stream_query support from preview AdkApp for adk_live
151
+ AgentEngineApp.bidi_stream_query = PreviewAdkApp.bidi_stream_query
152
+ {%- endif %}
153
+
77
154
 
155
+ gemini_location = os.environ.get("GOOGLE_CLOUD_LOCATION")
156
+ logs_bucket_name = os.environ.get("LOGS_BUCKET_NAME")
157
+ {%- if cookiecutter.is_a2a %}
158
+ agent_engine = AgentEngineApp.create(
159
+ app=adk_app,
160
+ artifact_service=(
161
+ GcsArtifactService(bucket_name=logs_bucket_name)
162
+ if logs_bucket_name
163
+ else InMemoryArtifactService()
164
+ ),
165
+ session_service=InMemorySessionService(),
166
+ )
78
167
  {%- else %}
79
- import logging
80
- import os
81
- from collections.abc import Iterable, Mapping
82
- from typing import (
83
- Any,
168
+ agent_engine = AgentEngineApp(
169
+ app=adk_app,
170
+ artifact_service_builder=lambda: GcsArtifactService(bucket_name=logs_bucket_name)
171
+ if logs_bucket_name
172
+ else InMemoryArtifactService(),
84
173
  )
174
+ {%- endif -%}
175
+ {% else %}
85
176
 
86
- import click
87
- import google.auth
88
- import vertexai
89
- from google.cloud import logging as google_cloud_logging
90
- from langchain_core.runnables import RunnableConfig
91
- from traceloop.sdk import Instruments, Traceloop
92
- from vertexai._genai.types import AgentEngine, AgentEngineConfig
93
-
94
- from {{cookiecutter.agent_directory}}.utils.deployment import (
95
- parse_env_vars,
96
- print_deployment_success,
97
- write_deployment_metadata,
98
- )
99
- from {{cookiecutter.agent_directory}}.utils.gcs import create_bucket_if_not_exists
100
- from {{cookiecutter.agent_directory}}.utils.tracing import CloudTraceLoggingSpanExporter
101
- from {{cookiecutter.agent_directory}}.utils.typing import Feedback, InputChat, dumpd, ensure_valid_config
177
+ import asyncio
178
+ import os
179
+ from typing import Any
102
180
 
181
+ import nest_asyncio
182
+ from a2a.types import AgentCapabilities, AgentCard, AgentSkill, TransportProtocol
183
+ from google.cloud import logging as google_cloud_logging
184
+ from vertexai.preview.reasoning_engines import A2aAgent
103
185
 
104
- class AgentEngineApp:
105
- """Class for managing agent engine functionality."""
186
+ from {{cookiecutter.agent_directory}}.agent import root_agent
187
+ from {{cookiecutter.agent_directory}}.app_utils.executor.a2a_agent_executor import (
188
+ LangGraphAgentExecutor,
189
+ )
190
+ from {{cookiecutter.agent_directory}}.app_utils.typing import Feedback
106
191
 
107
- def __init__(self, project_id: str | None = None) -> None:
108
- """Initialize the AgentEngineApp variables"""
109
- self.project_id = project_id
110
192
 
111
- def set_up(self) -> None:
112
- """The set_up method is used to define application initialization logic"""
113
- # Lazy import agent at setup time to avoid deployment dependencies
114
- from {{cookiecutter.agent_directory}}.agent import agent
193
+ class AgentEngineApp(A2aAgent):
194
+ """Agent Engine App with A2A Protocol support for LangGraph agents."""
115
195
 
116
- logging_client = google_cloud_logging.Client(project=self.project_id)
117
- self.logger = logging_client.logger(__name__)
196
+ @staticmethod
197
+ def create() -> "AgentEngineApp":
198
+ """Create an AgentEngineApp instance with A2A support.
118
199
 
119
- # Initialize Telemetry
200
+ This method handles agent card creation in async context.
201
+ """
202
+ # Handle nested asyncio contexts (like notebooks or Agent Engine)
120
203
  try:
121
- Traceloop.init(
122
- app_name="{{cookiecutter.project_name}}",
123
- disable_batch=False,
124
- exporter=CloudTraceLoggingSpanExporter(project_id=self.project_id),
125
- instruments={Instruments.LANGCHAIN, Instruments.CREW},
126
- )
127
- except Exception as e:
128
- logging.error("Failed to initialize Telemetry: %s", str(e))
129
- self.runnable = agent
204
+ asyncio.get_running_loop()
205
+ nest_asyncio.apply()
206
+ except RuntimeError:
207
+ pass
130
208
 
131
- # Add any additional variables here that should be included in the tracing logs
132
- def set_tracing_properties(self, config: RunnableConfig | None) -> None:
133
- """Sets tracing association properties for the current request.
209
+ agent_card = asyncio.run(AgentEngineApp.build_agent_card())
134
210
 
135
- Args:
136
- config: Optional RunnableConfig containing request metadata
137
- """
138
- config = ensure_valid_config(config)
139
- Traceloop.set_association_properties(
140
- {
141
- "log_type": "tracing",
142
- "run_id": str(config["run_id"]),
143
- "user_id": config["metadata"].pop("user_id", "None"),
144
- "session_id": config["metadata"].pop("session_id", "None"),
145
- "commit_sha": os.environ.get("COMMIT_SHA", "None"),
146
- }
211
+ return AgentEngineApp(
212
+ agent_executor_builder=lambda: LangGraphAgentExecutor(graph=root_agent),
213
+ agent_card=agent_card,
147
214
  )
148
215
 
149
- def stream_query(
150
- self,
151
- *,
152
- input: str | Mapping,
153
- config: RunnableConfig | None = None,
154
- **kwargs: Any,
155
- ) -> Iterable[Any]:
156
- """Stream responses from the agent for a given input."""
157
-
158
- config = ensure_valid_config(config)
159
- self.set_tracing_properties(config=config)
160
- # Validate input. We assert the input is a list of messages
161
- input_chat = InputChat.model_validate(input)
162
-
163
- for chunk in self.runnable.stream(
164
- input=input_chat, config=config, **kwargs, stream_mode="messages"
165
- ):
166
- dumped_chunk = dumpd(chunk)
167
- yield dumped_chunk
168
-
169
- def query(
170
- self,
171
- *,
172
- input: str | Mapping,
173
- config: RunnableConfig | None = None,
174
- **kwargs: Any,
175
- ) -> Any:
176
- """Process a single input and return the agent's response."""
177
- config = ensure_valid_config(config)
178
- self.set_tracing_properties(config=config)
179
- return dumpd(self.runnable.invoke(input=input, config=config, **kwargs))
216
+ @staticmethod
217
+ async def build_agent_card() -> AgentCard:
218
+ """Build the Agent Card for the LangGraph agent."""
219
+ skill = AgentSkill(
220
+ id="root_agent-get_weather",
221
+ name="get_weather",
222
+ description="Simulates a web search. Use it get information on weather.",
223
+ tags=["llm", "tools"],
224
+ examples=["What's the weather in San Francisco?"],
225
+ )
226
+ agent_card = AgentCard(
227
+ name="root_agent",
228
+ description="A base ReAct agent using LangGraph with Agent2Agent (A2A) Protocol support",
229
+ url="http://localhost:9999/", # RPC URL for Agent Engine
230
+ version=os.getenv("AGENT_VERSION", "0.1.0"),
231
+ default_input_modes=["text/plain"],
232
+ default_output_modes=["text/plain"],
233
+ capabilities=AgentCapabilities(
234
+ streaming=False
235
+ ), # Agent Engine does not support streaming yet
236
+ skills=[skill],
237
+ )
238
+
239
+ agent_card.preferred_transport = TransportProtocol.http_json # Http Only.
240
+ agent_card.supports_authenticated_extended_card = True
241
+
242
+ return agent_card
180
243
 
181
244
  def register_feedback(self, feedback: dict[str, Any]) -> None:
182
245
  """Collect and log feedback."""
183
246
  feedback_obj = Feedback.model_validate(feedback)
184
- self.logger.log_struct(feedback_obj.model_dump(), severity="INFO")
247
+ logging_client = google_cloud_logging.Client()
248
+ logger = logging_client.logger(__name__)
249
+ logger.log_struct(feedback_obj.model_dump(), severity="INFO")
185
250
 
186
251
  def register_operations(self) -> dict[str, list[str]]:
187
- """Registers the operations of the Agent.
188
-
189
- This mapping defines how different operation modes (e.g., "", "stream")
190
- are implemented by specific methods of the Agent. The "default" mode,
191
- represented by the empty string ``, is associated with the `query` API,
192
- while the "stream" mode is associated with the `stream_query` API.
193
-
194
- Returns:
195
- Mapping[str, Sequence[str]]: A mapping of operation modes to a list
196
- of method names that implement those operation modes.
197
- """
198
- return {
199
- "": ["query", "register_feedback"],
200
- "stream": ["stream_query"],
201
- }
202
- {%- endif %}
252
+ """Registers the operations of the Agent."""
253
+ operations = super().register_operations()
254
+ operations[""] = operations.get("", []) + ["register_feedback"]
255
+ return operations
203
256
 
257
+ def clone(self) -> "AgentEngineApp":
258
+ """Returns a clone of the Agent Engine application."""
259
+ return self
204
260
 
205
- @click.command()
206
- @click.option(
207
- "--project",
208
- default=None,
209
- help="GCP project ID (defaults to application default credentials)",
210
- )
211
- @click.option(
212
- "--location",
213
- default="us-central1",
214
- help="GCP region (defaults to us-central1)",
215
- )
216
- @click.option(
217
- "--agent-name",
218
- default="{{cookiecutter.project_name}}",
219
- help="Name for the agent engine",
220
- )
221
- @click.option(
222
- "--requirements-file",
223
- default=".requirements.txt",
224
- help="Path to requirements.txt file",
225
- )
226
- @click.option(
227
- "--extra-packages",
228
- multiple=True,
229
- default=["./{{cookiecutter.agent_directory}}"],
230
- help="Additional packages to include",
231
- )
232
- @click.option(
233
- "--set-env-vars",
234
- default=None,
235
- help="Comma-separated list of environment variables in KEY=VALUE format",
236
- )
237
- @click.option(
238
- "--service-account",
239
- default=None,
240
- help="Service account email to use for the agent engine",
241
- )
242
- @click.option(
243
- "--staging-bucket-uri",
244
- default=None,
245
- help="GCS bucket URI for staging files (defaults to gs://{project}-agent-engine)",
246
- )
247
- @click.option(
248
- "--artifacts-bucket-name",
249
- default=None,
250
- help="GCS bucket name for artifacts (defaults to gs://{project}-agent-engine)",
251
- )
252
- def deploy_agent_engine_app(
253
- project: str | None,
254
- location: str,
255
- agent_name: str,
256
- requirements_file: str,
257
- extra_packages: tuple[str, ...],
258
- set_env_vars: str | None,
259
- service_account: str | None,
260
- staging_bucket_uri: str | None,
261
- artifacts_bucket_name: str | None,
262
- ) -> AgentEngine:
263
- """Deploy the agent engine app to Vertex AI."""
264
-
265
- logging.basicConfig(level=logging.INFO)
266
-
267
- # Parse environment variables if provided
268
- env_vars = parse_env_vars(set_env_vars)
269
-
270
- if not project:
271
- _, project = google.auth.default()
272
- if not staging_bucket_uri:
273
- staging_bucket_uri = f"gs://{project}-agent-engine"
274
- if not artifacts_bucket_name:
275
- artifacts_bucket_name = f"gs://{project}-agent-engine"
276
-
277
- {%- if "adk" in cookiecutter.tags %}
278
- create_bucket_if_not_exists(
279
- bucket_name=artifacts_bucket_name, project=project, location=location
280
- )
281
- {%- endif %}
282
- create_bucket_if_not_exists(
283
- bucket_name=staging_bucket_uri, project=project, location=location
284
- )
285
-
286
- print("""
287
- ╔═══════════════════════════════════════════════════════════╗
288
- ║ ║
289
- ║ 🤖 DEPLOYING AGENT TO VERTEX AI AGENT ENGINE 🤖 ║
290
- ║ ║
291
- ╚═══════════════════════════════════════════════════════════╝
292
- """)
293
-
294
- extra_packages_list = list(extra_packages)
295
-
296
- # Initialize vertexai client
297
- client = vertexai.Client(
298
- project=project,
299
- location=location,
300
- )
301
- vertexai.init(project=project, location=location)
302
-
303
- # Read requirements
304
- with open(requirements_file) as f:
305
- requirements = f.read().strip().split("\n")
306
- {% if cookiecutter.is_adk %}
307
- agent_engine = AgentEngineApp(
308
- agent=root_agent,
309
- artifact_service_builder=lambda: GcsArtifactService(
310
- bucket_name=artifacts_bucket_name
311
- ),
312
- )
313
- {% else %}
314
- agent_engine = AgentEngineApp(project_id=project)
315
- {% endif %}
316
- # Set worker parallelism to 1
317
- env_vars["NUM_WORKERS"] = "1"
318
-
319
- # Common configuration for both create and update operations
320
- labels: dict[str, str] = {}
321
- {%- if cookiecutter.agent_garden %}
322
- {%- if cookiecutter.agent_sample_id and cookiecutter.agent_sample_publisher %}
323
- labels["vertex-agent-sample-id"] = "{{cookiecutter.agent_sample_id}}"
324
- labels["vertex-agent-sample-publisher"] = "{{cookiecutter.agent_sample_publisher}}"
325
- labels["deployed-with"] = "agent-garden"
326
- {%- endif %}
327
- {%- endif %}
328
261
 
329
- config = AgentEngineConfig(
330
- display_name=agent_name,
331
- description="{{cookiecutter.agent_description}}",
332
- extra_packages=extra_packages_list,
333
- env_vars=env_vars,
334
- service_account=service_account,
335
- requirements=requirements,
336
- staging_bucket=staging_bucket_uri,
337
- labels=labels,
338
- gcs_dir_name=agent_name,
339
- {%- if cookiecutter.is_adk_live %}
340
- agent_server_mode=AgentServerMode.EXPERIMENTAL, # Enable bidi streaming
341
- resource_limits={"cpu": "4", "memory": "8Gi"},
262
+ agent_engine = AgentEngineApp.create()
342
263
  {%- endif %}
343
- )
344
-
345
- agent_config = {
346
- "agent": agent_engine,
347
- "config": config,
348
- }
349
- logging.info(f"Agent config: {agent_config}")
350
-
351
- # Check if an agent with this name already exists
352
- existing_agents = list(client.agent_engines.list())
353
- matching_agents = [
354
- agent
355
- for agent in existing_agents
356
- if agent.api_resource.display_name == agent_name
357
- ]
358
-
359
- if matching_agents:
360
- # Update the existing agent with new configuration
361
- logging.info(f"\n📝 Updating existing agent: {agent_name}")
362
- remote_agent = client.agent_engines.update(
363
- name=matching_agents[0].api_resource.name, **agent_config
364
- )
365
- else:
366
- # Create a new agent if none exists
367
- logging.info(f"\n🚀 Creating new agent: {agent_name}")
368
- remote_agent = client.agent_engines.create(**agent_config)
369
-
370
- write_deployment_metadata(remote_agent)
371
- print_deployment_success(remote_agent, location, project)
372
-
373
- return remote_agent
374
-
375
-
376
- if __name__ == "__main__":
377
- deploy_agent_engine_app()