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.
- agent_starter_pack/agents/{langgraph_base_react → adk_a2a_base}/.template/templateconfig.yaml +5 -12
- agent_starter_pack/agents/adk_a2a_base/README.md +37 -0
- agent_starter_pack/{frontends/streamlit/frontend/style/app_markdown.py → agents/adk_a2a_base/app/__init__.py} +3 -23
- agent_starter_pack/agents/adk_a2a_base/app/agent.py +70 -0
- agent_starter_pack/agents/adk_a2a_base/notebooks/adk_a2a_app_testing.ipynb +583 -0
- agent_starter_pack/agents/{crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb → adk_a2a_base/notebooks/evaluating_adk_agent.ipynb} +163 -199
- agent_starter_pack/agents/adk_a2a_base/tests/integration/test_agent.py +58 -0
- agent_starter_pack/agents/adk_base/app/__init__.py +2 -2
- agent_starter_pack/agents/adk_base/app/agent.py +3 -0
- agent_starter_pack/agents/adk_base/notebooks/adk_app_testing.ipynb +13 -28
- agent_starter_pack/agents/adk_live/app/__init__.py +17 -0
- agent_starter_pack/agents/adk_live/app/agent.py +3 -0
- agent_starter_pack/agents/agentic_rag/app/__init__.py +2 -2
- agent_starter_pack/agents/agentic_rag/app/agent.py +3 -0
- agent_starter_pack/agents/agentic_rag/notebooks/adk_app_testing.ipynb +13 -28
- agent_starter_pack/agents/{crewai_coding_crew → langgraph_base}/.template/templateconfig.yaml +12 -9
- agent_starter_pack/agents/langgraph_base/README.md +30 -0
- agent_starter_pack/agents/langgraph_base/app/__init__.py +17 -0
- agent_starter_pack/agents/{langgraph_base_react → langgraph_base}/app/agent.py +4 -4
- agent_starter_pack/agents/{langgraph_base_react → langgraph_base}/tests/integration/test_agent.py +1 -1
- agent_starter_pack/base_template/.gitignore +4 -2
- agent_starter_pack/base_template/Makefile +110 -16
- agent_starter_pack/base_template/README.md +97 -12
- agent_starter_pack/base_template/deployment/terraform/dev/apis.tf +4 -6
- agent_starter_pack/base_template/deployment/terraform/dev/providers.tf +5 -1
- agent_starter_pack/base_template/deployment/terraform/dev/variables.tf +5 -3
- agent_starter_pack/base_template/deployment/terraform/dev/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +193 -0
- agent_starter_pack/base_template/deployment/terraform/github.tf +16 -9
- agent_starter_pack/base_template/deployment/terraform/locals.tf +7 -7
- agent_starter_pack/base_template/deployment/terraform/providers.tf +5 -1
- agent_starter_pack/base_template/deployment/terraform/sql/completions.sql +138 -0
- agent_starter_pack/base_template/deployment/terraform/storage.tf +0 -9
- agent_starter_pack/base_template/deployment/terraform/variables.tf +15 -19
- 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
- agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +206 -0
- agent_starter_pack/base_template/pyproject.toml +5 -17
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +19 -4
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +36 -11
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +24 -5
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +44 -9
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/telemetry.py +96 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/{utils → app_utils}/typing.py +4 -6
- 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
- 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
- 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
- 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
- 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
- agent_starter_pack/cli/commands/create.py +40 -4
- agent_starter_pack/cli/commands/enhance.py +1 -1
- agent_starter_pack/cli/commands/register_gemini_enterprise.py +1070 -0
- agent_starter_pack/cli/main.py +2 -0
- agent_starter_pack/cli/utils/cicd.py +20 -4
- agent_starter_pack/cli/utils/template.py +257 -25
- agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +113 -16
- agent_starter_pack/deployment_targets/agent_engine/tests/load_test/README.md +2 -2
- agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py +178 -9
- agent_starter_pack/deployment_targets/agent_engine/tests/{% if cookiecutter.is_a2a %}helpers.py{% else %}unused_helpers.py{% endif %} +138 -0
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +193 -307
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/app_utils/deploy.py +414 -0
- 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
- agent_starter_pack/deployment_targets/cloud_run/Dockerfile +4 -1
- agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +85 -86
- agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/service.tf +139 -107
- agent_starter_pack/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +228 -12
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/README.md +4 -4
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +92 -12
- agent_starter_pack/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/{server.py → fast_api_app.py} +194 -121
- agent_starter_pack/frontends/adk_live_react/frontend/package-lock.json +18 -18
- agent_starter_pack/frontends/adk_live_react/frontend/src/multimodal-live-types.ts +5 -3
- agent_starter_pack/resources/docs/adk-cheatsheet.md +198 -41
- agent_starter_pack/resources/locks/uv-adk_a2a_base-agent_engine.lock +4966 -0
- agent_starter_pack/resources/locks/uv-adk_a2a_base-cloud_run.lock +5011 -0
- agent_starter_pack/resources/locks/uv-adk_base-agent_engine.lock +1443 -709
- agent_starter_pack/resources/locks/uv-adk_base-cloud_run.lock +1058 -874
- agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +1443 -709
- agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +1058 -874
- agent_starter_pack/resources/locks/uv-agentic_rag-agent_engine.lock +1568 -749
- agent_starter_pack/resources/locks/uv-agentic_rag-cloud_run.lock +1123 -929
- agent_starter_pack/resources/locks/{uv-langgraph_base_react-agent_engine.lock → uv-langgraph_base-agent_engine.lock} +1714 -1689
- agent_starter_pack/resources/locks/{uv-langgraph_base_react-cloud_run.lock → uv-langgraph_base-cloud_run.lock} +1285 -2374
- agent_starter_pack/utils/watch_and_rebuild.py +1 -1
- {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/METADATA +3 -6
- {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/RECORD +89 -93
- agent_starter_pack-0.21.0.dist-info/entry_points.txt +2 -0
- llm.txt +4 -5
- agent_starter_pack/agents/crewai_coding_crew/README.md +0 -34
- agent_starter_pack/agents/crewai_coding_crew/app/agent.py +0 -47
- agent_starter_pack/agents/crewai_coding_crew/app/crew/config/tasks.yaml +0 -37
- agent_starter_pack/agents/crewai_coding_crew/app/crew/crew.py +0 -71
- agent_starter_pack/agents/crewai_coding_crew/tests/integration/test_agent.py +0 -47
- agent_starter_pack/agents/langgraph_base_react/README.md +0 -9
- agent_starter_pack/agents/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb +0 -1574
- agent_starter_pack/base_template/deployment/terraform/dev/log_sinks.tf +0 -69
- agent_starter_pack/base_template/deployment/terraform/log_sinks.tf +0 -79
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/utils/tracing.py +0 -155
- agent_starter_pack/cli/utils/register_gemini_enterprise.py +0 -406
- agent_starter_pack/deployment_targets/agent_engine/deployment/terraform/{% if not cookiecutter.is_adk_live %}service.tf{% else %}unused_service.tf{% endif %} +0 -82
- agent_starter_pack/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -1025
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/deployment.py +0 -99
- agent_starter_pack/frontends/streamlit/frontend/side_bar.py +0 -214
- agent_starter_pack/frontends/streamlit/frontend/streamlit_app.py +0 -265
- agent_starter_pack/frontends/streamlit/frontend/utils/chat_utils.py +0 -67
- agent_starter_pack/frontends/streamlit/frontend/utils/local_chat_history.py +0 -127
- agent_starter_pack/frontends/streamlit/frontend/utils/message_editing.py +0 -59
- agent_starter_pack/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -217
- agent_starter_pack/frontends/streamlit/frontend/utils/stream_handler.py +0 -310
- agent_starter_pack/frontends/streamlit/frontend/utils/title_summary.py +0 -94
- agent_starter_pack/resources/locks/uv-crewai_coding_crew-agent_engine.lock +0 -6650
- agent_starter_pack/resources/locks/uv-crewai_coding_crew-cloud_run.lock +0 -7825
- agent_starter_pack-0.18.2.dist-info/entry_points.txt +0 -3
- /agent_starter_pack/agents/{crewai_coding_crew → langgraph_base}/notebooks/evaluating_langgraph_agent.ipynb +0 -0
- /agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/{utils → app_utils}/gcs.py +0 -0
- {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/WHEEL +0 -0
- {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
|
-
|
|
22
|
-
import
|
|
24
|
+
{% if cookiecutter.is_a2a -%}
|
|
25
|
+
import nest_asyncio
|
|
26
|
+
{% endif -%}
|
|
23
27
|
import vertexai
|
|
24
|
-
|
|
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.
|
|
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
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
"""
|
|
49
|
-
|
|
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
|
-
|
|
56
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
87
|
-
import
|
|
88
|
-
import
|
|
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
|
-
|
|
105
|
-
|
|
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
|
-
|
|
112
|
-
|
|
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
|
-
|
|
117
|
-
|
|
196
|
+
@staticmethod
|
|
197
|
+
def create() -> "AgentEngineApp":
|
|
198
|
+
"""Create an AgentEngineApp instance with A2A support.
|
|
118
199
|
|
|
119
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
|
|
136
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
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
|
-
|
|
190
|
-
|
|
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
|
-
|
|
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()
|