agent-starter-pack 0.11.2__py3-none-any.whl → 0.12.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-0.11.2.dist-info → agent_starter_pack-0.12.0.dist-info}/METADATA +1 -1
- {agent_starter_pack-0.11.2.dist-info → agent_starter_pack-0.12.0.dist-info}/RECORD +41 -66
- agents/adk_base/app/__init__.py +17 -0
- agents/adk_base/notebooks/adk_app_testing.ipynb +4 -1
- agents/adk_base/tests/integration/test_agent.py +1 -1
- agents/agentic_rag/app/__init__.py +17 -0
- agents/agentic_rag/app/agent.py +2 -2
- agents/agentic_rag/notebooks/adk_app_testing.ipynb +4 -1
- agents/agentic_rag/tests/integration/test_agent.py +2 -2
- agents/crewai_coding_crew/tests/integration/test_agent.py +1 -1
- agents/langgraph_base_react/tests/integration/test_agent.py +1 -1
- agents/live_api/tests/unit/test_server.py +6 -6
- llm.txt +15 -4
- src/base_template/Makefile +5 -5
- src/base_template/README.md +4 -4
- src/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} +2 -2
- src/base_template/pyproject.toml +2 -2
- src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +1 -1
- src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +1 -1
- src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +2 -2
- src/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +1 -1
- src/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +1 -1
- src/cli/commands/create.py +25 -2
- src/cli/commands/enhance.py +94 -15
- src/cli/commands/list.py +1 -1
- src/cli/utils/remote_template.py +1 -1
- src/cli/utils/template.py +120 -41
- src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +3 -3
- src/deployment_targets/agent_engine/{app → {{cookiecutter.agent_directory}}}/agent_engine_app.py +10 -10
- src/deployment_targets/cloud_run/Dockerfile +2 -2
- src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +3 -3
- src/deployment_targets/cloud_run/tests/load_test/README.md +1 -1
- src/deployment_targets/cloud_run/tests/load_test/load_test.py +2 -2
- {agents/live_api/app → src/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}}/server.py +186 -7
- src/resources/docs/adk-cheatsheet.md +3 -3
- src/base_template/app/__init__.py +0 -3
- src/deployment_targets/cloud_run/app/server.py +0 -206
- src/frontends/adk_gemini_fullstack/frontend/components.json +0 -21
- src/frontends/adk_gemini_fullstack/frontend/eslint.config.js +0 -28
- src/frontends/adk_gemini_fullstack/frontend/index.html +0 -12
- src/frontends/adk_gemini_fullstack/frontend/package-lock.json +0 -6105
- src/frontends/adk_gemini_fullstack/frontend/package.json +0 -47
- src/frontends/adk_gemini_fullstack/frontend/src/App.tsx +0 -564
- src/frontends/adk_gemini_fullstack/frontend/src/components/ActivityTimeline.tsx +0 -244
- src/frontends/adk_gemini_fullstack/frontend/src/components/ChatMessagesView.tsx +0 -420
- src/frontends/adk_gemini_fullstack/frontend/src/components/InputForm.tsx +0 -60
- src/frontends/adk_gemini_fullstack/frontend/src/components/WelcomeScreen.tsx +0 -56
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/badge.tsx +0 -46
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/button.tsx +0 -59
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/card.tsx +0 -92
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/input.tsx +0 -21
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/scroll-area.tsx +0 -56
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/select.tsx +0 -183
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/tabs.tsx +0 -64
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/textarea.tsx +0 -18
- src/frontends/adk_gemini_fullstack/frontend/src/global.css +0 -154
- src/frontends/adk_gemini_fullstack/frontend/src/main.tsx +0 -13
- src/frontends/adk_gemini_fullstack/frontend/src/utils.ts +0 -7
- src/frontends/adk_gemini_fullstack/frontend/src/vite-env.d.ts +0 -1
- src/frontends/adk_gemini_fullstack/frontend/tsconfig.json +0 -28
- src/frontends/adk_gemini_fullstack/frontend/tsconfig.node.json +0 -24
- src/frontends/adk_gemini_fullstack/frontend/vite.config.ts +0 -41
- {agent_starter_pack-0.11.2.dist-info → agent_starter_pack-0.12.0.dist-info}/WHEEL +0 -0
- {agent_starter_pack-0.11.2.dist-info → agent_starter_pack-0.12.0.dist-info}/entry_points.txt +0 -0
- {agent_starter_pack-0.11.2.dist-info → agent_starter_pack-0.12.0.dist-info}/licenses/LICENSE +0 -0
- /src/base_template/{app → {{cookiecutter.agent_directory}}}/utils/gcs.py +0 -0
- /src/base_template/{app → {{cookiecutter.agent_directory}}}/utils/tracing.py +0 -0
- /src/base_template/{app → {{cookiecutter.agent_directory}}}/utils/typing.py +0 -0
@@ -20,7 +20,7 @@ WORKDIR /code
|
|
20
20
|
|
21
21
|
COPY ./pyproject.toml ./README.md ./uv.lock* ./
|
22
22
|
|
23
|
-
COPY ./
|
23
|
+
COPY ./{{cookiecutter.agent_directory}} ./{{cookiecutter.agent_directory}}
|
24
24
|
|
25
25
|
RUN uv sync --frozen
|
26
26
|
|
@@ -29,4 +29,4 @@ ENV COMMIT_SHA=${COMMIT_SHA}
|
|
29
29
|
|
30
30
|
EXPOSE 8080
|
31
31
|
|
32
|
-
CMD ["uv", "run", "uvicorn", "
|
32
|
+
CMD ["uv", "run", "uvicorn", "{{cookiecutter.agent_directory}}.server:app", "--host", "0.0.0.0", "--port", "8080"]
|
@@ -54,7 +54,7 @@ def start_server() -> subprocess.Popen[str]:
|
|
54
54
|
sys.executable,
|
55
55
|
"-m",
|
56
56
|
"uvicorn",
|
57
|
-
"
|
57
|
+
"{{cookiecutter.agent_directory}}.server:app",
|
58
58
|
"--host",
|
59
59
|
"0.0.0.0",
|
60
60
|
"--port",
|
@@ -129,7 +129,7 @@ def test_chat_stream(server_fixture: subprocess.Popen[str]) -> None:
|
|
129
129
|
user_id = "test_user_123"
|
130
130
|
session_data = {"state": {"preferred_language": "English", "visit_count": 1}}
|
131
131
|
|
132
|
-
session_url = f"{BASE_URL}/apps/
|
132
|
+
session_url = f"{BASE_URL}/apps/{{cookiecutter.agent_directory}}/users/{user_id}/sessions"
|
133
133
|
session_response = requests.post(
|
134
134
|
session_url,
|
135
135
|
headers=HEADERS,
|
@@ -142,7 +142,7 @@ def test_chat_stream(server_fixture: subprocess.Popen[str]) -> None:
|
|
142
142
|
|
143
143
|
# Then send chat message
|
144
144
|
data = {
|
145
|
-
"app_name": "
|
145
|
+
"app_name": "{{cookiecutter.agent_directory}}",
|
146
146
|
"user_id": user_id,
|
147
147
|
"session_id": session_id,
|
148
148
|
"new_message": {
|
@@ -11,7 +11,7 @@ Follow these steps to execute load tests on your local machine:
|
|
11
11
|
Launch the FastAPI server in a separate terminal:
|
12
12
|
|
13
13
|
```bash
|
14
|
-
uv run uvicorn
|
14
|
+
uv run uvicorn {{cookiecutter.agent_directory}}.server:app --host 0.0.0.0 --port 8000 --reload
|
15
15
|
```
|
16
16
|
|
17
17
|
**2. (In another tab) Create virtual environment with Locust**
|
@@ -45,7 +45,7 @@ class ChatStreamUser(HttpUser):
|
|
45
45
|
user_id = f"user_{uuid.uuid4()}"
|
46
46
|
session_data = {"state": {"preferred_language": "English", "visit_count": 1}}
|
47
47
|
|
48
|
-
session_url = f"{self.client.base_url}/apps/
|
48
|
+
session_url = f"{self.client.base_url}/apps/{{cookiecutter.agent_directory}}/users/{user_id}/sessions"
|
49
49
|
session_response = requests.post(
|
50
50
|
session_url,
|
51
51
|
headers=headers,
|
@@ -58,7 +58,7 @@ class ChatStreamUser(HttpUser):
|
|
58
58
|
|
59
59
|
# Send chat message
|
60
60
|
data = {
|
61
|
-
"app_name": "
|
61
|
+
"app_name": "{{cookiecutter.agent_directory}}",
|
62
62
|
"user_id": user_id,
|
63
63
|
"session_id": session_id,
|
64
64
|
"new_message": {
|
{agents/live_api/app → src/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}}/server.py
RENAMED
@@ -11,7 +11,7 @@
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
|
-
|
14
|
+
{% if cookiecutter.agent_name == "live_api" %}
|
15
15
|
import asyncio
|
16
16
|
import json
|
17
17
|
import logging
|
@@ -27,7 +27,7 @@ from google.genai.types import LiveServerToolCall
|
|
27
27
|
from pydantic import BaseModel
|
28
28
|
from websockets.exceptions import ConnectionClosedError
|
29
29
|
|
30
|
-
from
|
30
|
+
from .agent import MODEL_ID, genai_client, live_connect_config, tool_functions
|
31
31
|
|
32
32
|
app = FastAPI()
|
33
33
|
app.add_middleware(
|
@@ -197,16 +197,195 @@ class Feedback(BaseModel):
|
|
197
197
|
run_id: str
|
198
198
|
user_id: str | None
|
199
199
|
log_type: Literal["feedback"] = "feedback"
|
200
|
+
{% elif "adk" in cookiecutter.tags %}
|
201
|
+
import os
|
202
|
+
|
203
|
+
import google.auth
|
204
|
+
from fastapi import FastAPI
|
205
|
+
from google.adk.cli.fast_api import get_fast_api_app
|
206
|
+
from google.cloud import logging as google_cloud_logging
|
207
|
+
from opentelemetry import trace
|
208
|
+
from opentelemetry.sdk.trace import TracerProvider, export
|
209
|
+
{%- if cookiecutter.session_type == "agent_engine" %}
|
210
|
+
from vertexai import agent_engines
|
211
|
+
{%- endif %}
|
212
|
+
|
213
|
+
from {{cookiecutter.agent_directory}}.utils.gcs import create_bucket_if_not_exists
|
214
|
+
from {{cookiecutter.agent_directory}}.utils.tracing import CloudTraceLoggingSpanExporter
|
215
|
+
from {{cookiecutter.agent_directory}}.utils.typing import Feedback
|
200
216
|
|
217
|
+
_, project_id = google.auth.default()
|
218
|
+
logging_client = google_cloud_logging.Client()
|
219
|
+
logger = logging_client.logger(__name__)
|
220
|
+
allow_origins = (
|
221
|
+
os.getenv("ALLOW_ORIGINS", "").split(",") if os.getenv("ALLOW_ORIGINS") else None
|
222
|
+
)
|
223
|
+
|
224
|
+
bucket_name = f"gs://{project_id}-{{cookiecutter.project_name}}-logs-data"
|
225
|
+
create_bucket_if_not_exists(
|
226
|
+
bucket_name=bucket_name, project=project_id, location="us-central1"
|
227
|
+
)
|
228
|
+
|
229
|
+
provider = TracerProvider()
|
230
|
+
processor = export.BatchSpanProcessor(CloudTraceLoggingSpanExporter())
|
231
|
+
provider.add_span_processor(processor)
|
232
|
+
trace.set_tracer_provider(provider)
|
233
|
+
|
234
|
+
AGENT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
235
|
+
|
236
|
+
{%- if cookiecutter.session_type == "alloydb" %}
|
237
|
+
# AlloyDB session configuration
|
238
|
+
db_user = os.environ.get("DB_USER", "postgres")
|
239
|
+
db_name = os.environ.get("DB_NAME", "postgres")
|
240
|
+
db_pass = os.environ.get("DB_PASS")
|
241
|
+
db_host = os.environ.get("DB_HOST")
|
242
|
+
|
243
|
+
# Set session_service_uri if database credentials are available
|
244
|
+
session_service_uri = None
|
245
|
+
if db_host and db_pass:
|
246
|
+
session_service_uri = f"postgresql://{db_user}:{db_pass}@{db_host}:5432/{db_name}"
|
247
|
+
{%- elif cookiecutter.session_type == "agent_engine" %}
|
248
|
+
# Agent Engine session configuration
|
249
|
+
# Use environment variable for agent name, default to project name
|
250
|
+
agent_name = os.environ.get("AGENT_ENGINE_SESSION_NAME", "{{cookiecutter.project_name}}")
|
251
|
+
|
252
|
+
# Check if an agent with this name already exists
|
253
|
+
existing_agents = list(agent_engines.list(filter=f"display_name={agent_name}"))
|
254
|
+
|
255
|
+
if existing_agents:
|
256
|
+
# Use the existing agent
|
257
|
+
agent_engine = existing_agents[0]
|
258
|
+
else:
|
259
|
+
# Create a new agent if none exists
|
260
|
+
agent_engine = agent_engines.create(display_name=agent_name)
|
261
|
+
|
262
|
+
session_service_uri = f"agentengine://{agent_engine.resource_name}"
|
263
|
+
{%- else %}
|
264
|
+
# In-memory session configuration - no persistent storage
|
265
|
+
session_service_uri = None
|
266
|
+
{%- endif %}
|
267
|
+
|
268
|
+
app: FastAPI = get_fast_api_app(
|
269
|
+
agents_dir=AGENT_DIR,
|
270
|
+
web=True,
|
271
|
+
artifact_service_uri=bucket_name,
|
272
|
+
allow_origins=allow_origins,
|
273
|
+
session_service_uri=session_service_uri,
|
274
|
+
)
|
275
|
+
app.title = "{{cookiecutter.project_name}}"
|
276
|
+
app.description = "API for interacting with the Agent {{cookiecutter.project_name}}"
|
277
|
+
{% else %}
|
278
|
+
import logging
|
279
|
+
import os
|
280
|
+
from collections.abc import Generator
|
281
|
+
|
282
|
+
from fastapi import FastAPI
|
283
|
+
from fastapi.responses import RedirectResponse, StreamingResponse
|
284
|
+
from google.cloud import logging as google_cloud_logging
|
285
|
+
from langchain_core.runnables import RunnableConfig
|
286
|
+
from traceloop.sdk import Instruments, Traceloop
|
287
|
+
|
288
|
+
from {{cookiecutter.agent_directory}}.agent import agent
|
289
|
+
from {{cookiecutter.agent_directory}}.utils.tracing import CloudTraceLoggingSpanExporter
|
290
|
+
from {{cookiecutter.agent_directory}}.utils.typing import Feedback, InputChat, Request, dumps, ensure_valid_config
|
291
|
+
|
292
|
+
# Initialize FastAPI app and logging
|
293
|
+
app = FastAPI(
|
294
|
+
title="{{cookiecutter.project_name}}",
|
295
|
+
description="API for interacting with the Agent {{cookiecutter.project_name}}",
|
296
|
+
)
|
297
|
+
logging_client = google_cloud_logging.Client()
|
298
|
+
logger = logging_client.logger(__name__)
|
299
|
+
|
300
|
+
# Initialize Telemetry
|
301
|
+
try:
|
302
|
+
Traceloop.init(
|
303
|
+
app_name=app.title,
|
304
|
+
disable_batch=False,
|
305
|
+
exporter=CloudTraceLoggingSpanExporter(),
|
306
|
+
instruments={Instruments.LANGCHAIN, Instruments.CREW},
|
307
|
+
)
|
308
|
+
except Exception as e:
|
309
|
+
logging.error("Failed to initialize Telemetry: %s", str(e))
|
310
|
+
|
311
|
+
|
312
|
+
def set_tracing_properties(config: RunnableConfig) -> None:
|
313
|
+
"""Sets tracing association properties for the current request.
|
314
|
+
|
315
|
+
Args:
|
316
|
+
config: Optional RunnableConfig containing request metadata
|
317
|
+
"""
|
318
|
+
Traceloop.set_association_properties(
|
319
|
+
{
|
320
|
+
"log_type": "tracing",
|
321
|
+
"run_id": str(config.get("run_id", "None")),
|
322
|
+
"user_id": config["metadata"].pop("user_id", "None"),
|
323
|
+
"session_id": config["metadata"].pop("session_id", "None"),
|
324
|
+
"commit_sha": os.environ.get("COMMIT_SHA", "None"),
|
325
|
+
}
|
326
|
+
)
|
327
|
+
|
328
|
+
|
329
|
+
def stream_messages(
|
330
|
+
input: InputChat,
|
331
|
+
config: RunnableConfig | None = None,
|
332
|
+
) -> Generator[str, None, None]:
|
333
|
+
"""Stream events in response to an input chat.
|
334
|
+
|
335
|
+
Args:
|
336
|
+
input: The input chat messages
|
337
|
+
config: Optional configuration for the runnable
|
338
|
+
|
339
|
+
Yields:
|
340
|
+
JSON serialized event data
|
341
|
+
"""
|
342
|
+
config = ensure_valid_config(config=config)
|
343
|
+
set_tracing_properties(config)
|
344
|
+
input_dict = input.model_dump()
|
345
|
+
|
346
|
+
for data in agent.stream(input_dict, config=config, stream_mode="messages"): # type: ignore[arg-type]
|
347
|
+
yield dumps(data) + "\n"
|
348
|
+
|
349
|
+
|
350
|
+
# Routes
|
351
|
+
@app.get("/", response_class=RedirectResponse)
|
352
|
+
def redirect_root_to_docs() -> RedirectResponse:
|
353
|
+
"""Redirect the root URL to the API documentation."""
|
354
|
+
return RedirectResponse(url="/docs")
|
355
|
+
|
356
|
+
|
357
|
+
@app.post("/stream_messages")
|
358
|
+
def stream_chat_events(request: Request) -> StreamingResponse:
|
359
|
+
"""Stream chat events in response to an input request.
|
360
|
+
|
361
|
+
Args:
|
362
|
+
request: The chat request containing input and config
|
363
|
+
|
364
|
+
Returns:
|
365
|
+
Streaming response of chat events
|
366
|
+
"""
|
367
|
+
return StreamingResponse(
|
368
|
+
stream_messages(input=request.input, config=request.config),
|
369
|
+
media_type="text/event-stream",
|
370
|
+
)
|
371
|
+
{% endif %}
|
201
372
|
|
202
373
|
@app.post("/feedback")
|
203
|
-
|
204
|
-
"""Collect and log feedback.
|
205
|
-
|
206
|
-
|
374
|
+
def collect_feedback(feedback: Feedback) -> dict[str, str]:
|
375
|
+
"""Collect and log feedback.
|
376
|
+
|
377
|
+
Args:
|
378
|
+
feedback: The feedback data to log
|
379
|
+
|
380
|
+
Returns:
|
381
|
+
Success message
|
382
|
+
"""
|
383
|
+
logger.log_struct(feedback.model_dump(), severity="INFO")
|
384
|
+
return {"status": "success"}
|
207
385
|
|
208
386
|
|
387
|
+
# Main execution
|
209
388
|
if __name__ == "__main__":
|
210
389
|
import uvicorn
|
211
390
|
|
212
|
-
uvicorn.run(app, host="0.0.0.0", port=8000
|
391
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
@@ -1193,7 +1193,7 @@ import asyncio
|
|
1193
1193
|
|
1194
1194
|
from google.adk.runners import Runner
|
1195
1195
|
from google.adk.sessions import InMemorySessionService
|
1196
|
-
from
|
1196
|
+
from {{cookiecutter.agent_directory}}.agent import root_agent
|
1197
1197
|
from google.genai import types as genai_types
|
1198
1198
|
|
1199
1199
|
|
@@ -1201,10 +1201,10 @@ async def main():
|
|
1201
1201
|
"""Runs the agent with a sample query."""
|
1202
1202
|
session_service = InMemorySessionService()
|
1203
1203
|
await session_service.create_session(
|
1204
|
-
app_name="
|
1204
|
+
app_name="{{cookiecutter.agent_directory}}", user_id="test_user", session_id="test_session"
|
1205
1205
|
)
|
1206
1206
|
runner = Runner(
|
1207
|
-
agent=root_agent, app_name="
|
1207
|
+
agent=root_agent, app_name="{{cookiecutter.agent_directory}}", session_service=session_service
|
1208
1208
|
)
|
1209
1209
|
query = "I want a recipe for pancakes"
|
1210
1210
|
async for event in runner.run_async(
|
@@ -1,206 +0,0 @@
|
|
1
|
-
# Copyright 2025 Google LLC
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
{% if "adk" in cookiecutter.tags %}
|
15
|
-
import os
|
16
|
-
|
17
|
-
import google.auth
|
18
|
-
from fastapi import FastAPI
|
19
|
-
from google.adk.cli.fast_api import get_fast_api_app
|
20
|
-
from google.cloud import logging as google_cloud_logging
|
21
|
-
from opentelemetry import trace
|
22
|
-
from opentelemetry.sdk.trace import TracerProvider, export
|
23
|
-
{%- if cookiecutter.session_type == "agent_engine" %}
|
24
|
-
from vertexai import agent_engines
|
25
|
-
{%- endif %}
|
26
|
-
|
27
|
-
from app.utils.gcs import create_bucket_if_not_exists
|
28
|
-
from app.utils.tracing import CloudTraceLoggingSpanExporter
|
29
|
-
from app.utils.typing import Feedback
|
30
|
-
|
31
|
-
_, project_id = google.auth.default()
|
32
|
-
logging_client = google_cloud_logging.Client()
|
33
|
-
logger = logging_client.logger(__name__)
|
34
|
-
allow_origins = (
|
35
|
-
os.getenv("ALLOW_ORIGINS", "").split(",") if os.getenv("ALLOW_ORIGINS") else None
|
36
|
-
)
|
37
|
-
|
38
|
-
bucket_name = f"gs://{project_id}-{{cookiecutter.project_name}}-logs-data"
|
39
|
-
create_bucket_if_not_exists(
|
40
|
-
bucket_name=bucket_name, project=project_id, location="us-central1"
|
41
|
-
)
|
42
|
-
|
43
|
-
provider = TracerProvider()
|
44
|
-
processor = export.BatchSpanProcessor(CloudTraceLoggingSpanExporter())
|
45
|
-
provider.add_span_processor(processor)
|
46
|
-
trace.set_tracer_provider(provider)
|
47
|
-
|
48
|
-
AGENT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
49
|
-
|
50
|
-
{%- if cookiecutter.session_type == "alloydb" %}
|
51
|
-
# AlloyDB session configuration
|
52
|
-
db_user = os.environ.get("DB_USER", "postgres")
|
53
|
-
db_name = os.environ.get("DB_NAME", "postgres")
|
54
|
-
db_pass = os.environ.get("DB_PASS")
|
55
|
-
db_host = os.environ.get("DB_HOST")
|
56
|
-
|
57
|
-
# Set session_service_uri if database credentials are available
|
58
|
-
session_service_uri = None
|
59
|
-
if db_host and db_pass:
|
60
|
-
session_service_uri = f"postgresql://{db_user}:{db_pass}@{db_host}:5432/{db_name}"
|
61
|
-
{%- elif cookiecutter.session_type == "agent_engine" %}
|
62
|
-
# Agent Engine session configuration
|
63
|
-
# Use environment variable for agent name, default to project name
|
64
|
-
agent_name = os.environ.get("AGENT_ENGINE_SESSION_NAME", "{{cookiecutter.project_name}}")
|
65
|
-
|
66
|
-
# Check if an agent with this name already exists
|
67
|
-
existing_agents = list(agent_engines.list(filter=f"display_name={agent_name}"))
|
68
|
-
|
69
|
-
if existing_agents:
|
70
|
-
# Use the existing agent
|
71
|
-
agent_engine = existing_agents[0]
|
72
|
-
else:
|
73
|
-
# Create a new agent if none exists
|
74
|
-
agent_engine = agent_engines.create(display_name=agent_name)
|
75
|
-
|
76
|
-
session_service_uri = f"agentengine://{agent_engine.resource_name}"
|
77
|
-
{%- else %}
|
78
|
-
# In-memory session configuration - no persistent storage
|
79
|
-
session_service_uri = None
|
80
|
-
{%- endif %}
|
81
|
-
|
82
|
-
app: FastAPI = get_fast_api_app(
|
83
|
-
agents_dir=AGENT_DIR,
|
84
|
-
web=True,
|
85
|
-
artifact_service_uri=bucket_name,
|
86
|
-
allow_origins=allow_origins,
|
87
|
-
session_service_uri=session_service_uri,
|
88
|
-
)
|
89
|
-
app.title = "{{cookiecutter.project_name}}"
|
90
|
-
app.description = "API for interacting with the Agent {{cookiecutter.project_name}}"
|
91
|
-
{%- else %}
|
92
|
-
import logging
|
93
|
-
import os
|
94
|
-
from collections.abc import Generator
|
95
|
-
|
96
|
-
from fastapi import FastAPI
|
97
|
-
from fastapi.responses import RedirectResponse, StreamingResponse
|
98
|
-
from google.cloud import logging as google_cloud_logging
|
99
|
-
from langchain_core.runnables import RunnableConfig
|
100
|
-
from traceloop.sdk import Instruments, Traceloop
|
101
|
-
|
102
|
-
from app.agent import agent
|
103
|
-
from app.utils.tracing import CloudTraceLoggingSpanExporter
|
104
|
-
from app.utils.typing import Feedback, InputChat, Request, dumps, ensure_valid_config
|
105
|
-
|
106
|
-
# Initialize FastAPI app and logging
|
107
|
-
app = FastAPI(
|
108
|
-
title="{{cookiecutter.project_name}}",
|
109
|
-
description="API for interacting with the Agent {{cookiecutter.project_name}}",
|
110
|
-
)
|
111
|
-
logging_client = google_cloud_logging.Client()
|
112
|
-
logger = logging_client.logger(__name__)
|
113
|
-
|
114
|
-
# Initialize Telemetry
|
115
|
-
try:
|
116
|
-
Traceloop.init(
|
117
|
-
app_name=app.title,
|
118
|
-
disable_batch=False,
|
119
|
-
exporter=CloudTraceLoggingSpanExporter(),
|
120
|
-
instruments={Instruments.LANGCHAIN, Instruments.CREW},
|
121
|
-
)
|
122
|
-
except Exception as e:
|
123
|
-
logging.error("Failed to initialize Telemetry: %s", str(e))
|
124
|
-
|
125
|
-
|
126
|
-
def set_tracing_properties(config: RunnableConfig) -> None:
|
127
|
-
"""Sets tracing association properties for the current request.
|
128
|
-
|
129
|
-
Args:
|
130
|
-
config: Optional RunnableConfig containing request metadata
|
131
|
-
"""
|
132
|
-
Traceloop.set_association_properties(
|
133
|
-
{
|
134
|
-
"log_type": "tracing",
|
135
|
-
"run_id": str(config.get("run_id", "None")),
|
136
|
-
"user_id": config["metadata"].pop("user_id", "None"),
|
137
|
-
"session_id": config["metadata"].pop("session_id", "None"),
|
138
|
-
"commit_sha": os.environ.get("COMMIT_SHA", "None"),
|
139
|
-
}
|
140
|
-
)
|
141
|
-
|
142
|
-
|
143
|
-
def stream_messages(
|
144
|
-
input: InputChat,
|
145
|
-
config: RunnableConfig | None = None,
|
146
|
-
) -> Generator[str, None, None]:
|
147
|
-
"""Stream events in response to an input chat.
|
148
|
-
|
149
|
-
Args:
|
150
|
-
input: The input chat messages
|
151
|
-
config: Optional configuration for the runnable
|
152
|
-
|
153
|
-
Yields:
|
154
|
-
JSON serialized event data
|
155
|
-
"""
|
156
|
-
config = ensure_valid_config(config=config)
|
157
|
-
set_tracing_properties(config)
|
158
|
-
input_dict = input.model_dump()
|
159
|
-
|
160
|
-
for data in agent.stream(input_dict, config=config, stream_mode="messages"): # type: ignore[arg-type]
|
161
|
-
yield dumps(data) + "\n"
|
162
|
-
|
163
|
-
|
164
|
-
# Routes
|
165
|
-
@app.get("/", response_class=RedirectResponse)
|
166
|
-
def redirect_root_to_docs() -> RedirectResponse:
|
167
|
-
"""Redirect the root URL to the API documentation."""
|
168
|
-
return RedirectResponse(url="/docs")
|
169
|
-
|
170
|
-
|
171
|
-
@app.post("/stream_messages")
|
172
|
-
def stream_chat_events(request: Request) -> StreamingResponse:
|
173
|
-
"""Stream chat events in response to an input request.
|
174
|
-
|
175
|
-
Args:
|
176
|
-
request: The chat request containing input and config
|
177
|
-
|
178
|
-
Returns:
|
179
|
-
Streaming response of chat events
|
180
|
-
"""
|
181
|
-
return StreamingResponse(
|
182
|
-
stream_messages(input=request.input, config=request.config),
|
183
|
-
media_type="text/event-stream",
|
184
|
-
)
|
185
|
-
{%- endif %}
|
186
|
-
|
187
|
-
|
188
|
-
@app.post("/feedback")
|
189
|
-
def collect_feedback(feedback: Feedback) -> dict[str, str]:
|
190
|
-
"""Collect and log feedback.
|
191
|
-
|
192
|
-
Args:
|
193
|
-
feedback: The feedback data to log
|
194
|
-
|
195
|
-
Returns:
|
196
|
-
Success message
|
197
|
-
"""
|
198
|
-
logger.log_struct(feedback.model_dump(), severity="INFO")
|
199
|
-
return {"status": "success"}
|
200
|
-
|
201
|
-
|
202
|
-
# Main execution
|
203
|
-
if __name__ == "__main__":
|
204
|
-
import uvicorn
|
205
|
-
|
206
|
-
uvicorn.run(app, host="0.0.0.0", port=8000)
|
@@ -1,21 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"$schema": "https://ui.shadcn.com/schema.json",
|
3
|
-
"style": "new-york",
|
4
|
-
"rsc": false,
|
5
|
-
"tsx": true,
|
6
|
-
"tailwind": {
|
7
|
-
"config": "",
|
8
|
-
"css": "src/app.css",
|
9
|
-
"baseColor": "neutral",
|
10
|
-
"cssVariables": true,
|
11
|
-
"prefix": ""
|
12
|
-
},
|
13
|
-
"aliases": {
|
14
|
-
"components": "@/components",
|
15
|
-
"utils": "@/lib/utils",
|
16
|
-
"ui": "@/components/ui",
|
17
|
-
"lib": "@/lib",
|
18
|
-
"hooks": "@/hooks"
|
19
|
-
},
|
20
|
-
"iconLibrary": "lucide"
|
21
|
-
}
|
@@ -1,28 +0,0 @@
|
|
1
|
-
import js from '@eslint/js'
|
2
|
-
import globals from 'globals'
|
3
|
-
import reactHooks from 'eslint-plugin-react-hooks'
|
4
|
-
import reactRefresh from 'eslint-plugin-react-refresh'
|
5
|
-
import tseslint from 'typescript-eslint'
|
6
|
-
|
7
|
-
export default tseslint.config(
|
8
|
-
{ ignores: ['dist'] },
|
9
|
-
{
|
10
|
-
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
11
|
-
files: ['**/*.{ts,tsx}'],
|
12
|
-
languageOptions: {
|
13
|
-
ecmaVersion: 2020,
|
14
|
-
globals: globals.browser,
|
15
|
-
},
|
16
|
-
plugins: {
|
17
|
-
'react-hooks': reactHooks,
|
18
|
-
'react-refresh': reactRefresh,
|
19
|
-
},
|
20
|
-
rules: {
|
21
|
-
...reactHooks.configs.recommended.rules,
|
22
|
-
'react-refresh/only-export-components': [
|
23
|
-
'warn',
|
24
|
-
{ allowConstantExport: true },
|
25
|
-
],
|
26
|
-
},
|
27
|
-
},
|
28
|
-
)
|
@@ -1,12 +0,0 @@
|
|
1
|
-
<!doctype html>
|
2
|
-
<html lang="en">
|
3
|
-
<head>
|
4
|
-
<meta charset="UTF-8" />
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
6
|
-
<title>ADK Fullstack Agent</title>
|
7
|
-
</head>
|
8
|
-
<body>
|
9
|
-
<div id="root"></div>
|
10
|
-
<script type="module" src="/src/main.tsx"></script>
|
11
|
-
</body>
|
12
|
-
</html>
|