Initial commit
This commit is contained in:
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Python-generated files
|
||||||
|
__pycache__/
|
||||||
|
*.py[oc]
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
wheels/
|
||||||
|
*.egg-info
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv
|
||||||
|
|
||||||
|
# uv lock files
|
||||||
|
uv.lock
|
||||||
|
|
||||||
|
# config
|
||||||
|
.env
|
||||||
|
|
||||||
|
# database
|
||||||
|
sessions.db
|
||||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.13
|
||||||
34
README.md
Normal file
34
README.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
### ADK Sample Project
|
||||||
|
|
||||||
|
Public example using Google ADK with a simple email pipeline.
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
- Python 3.13, `uv` (`pip install uv` or see `https://astral.sh/uv`)
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
1) Install deps:
|
||||||
|
```
|
||||||
|
uv sync
|
||||||
|
```
|
||||||
|
2) Create `.env` in project root:
|
||||||
|
```
|
||||||
|
GEMINI_API_KEY=your_key
|
||||||
|
GENAI_MODEL=gemini-2.5-flash
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run
|
||||||
|
```
|
||||||
|
uv run uvicorn main:app --reload
|
||||||
|
```
|
||||||
|
Web UI at `http://localhost:8000`.
|
||||||
|
|
||||||
|
### API
|
||||||
|
POST `http://localhost:8000/process_data`
|
||||||
|
```
|
||||||
|
curl -s -X POST http://localhost:8000/process_data \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"message":"Draft an email about Friday meeting at 2pm"}'
|
||||||
|
```
|
||||||
|
Returns `{ "emails": "..." }`.
|
||||||
|
|
||||||
|
|
||||||
1
agents/__init__.py
Normal file
1
agents/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from agents.pipeline import agent as agent
|
||||||
1
agents/pipeline/__init__.py
Normal file
1
agents/pipeline/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from agents.pipeline import agent as agent
|
||||||
43
agents/pipeline/agent.py
Normal file
43
agents/pipeline/agent.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from google.adk.agents.llm_agent import LlmAgent
|
||||||
|
from google.adk.agents.sequential_agent import SequentialAgent
|
||||||
|
|
||||||
|
MODEL_ID = os.getenv("GENAI_MODEL", "gemini-2.5-flash")
|
||||||
|
|
||||||
|
intake_agent = LlmAgent(
|
||||||
|
name="intake",
|
||||||
|
description="Receives the user's message and produces a cleaned version.",
|
||||||
|
model=MODEL_ID,
|
||||||
|
instruction=(
|
||||||
|
"You are the first step in a pipeline. Read the user's message and "
|
||||||
|
"briefly restate it clearly without changing meaning. Return only the restated text."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
transform_agent = LlmAgent(
|
||||||
|
name="transform",
|
||||||
|
description="Transforms the intake text into a concise bullet list summary.",
|
||||||
|
model=MODEL_ID,
|
||||||
|
instruction=(
|
||||||
|
"Convert the previous step's text into 3-6 concise bullet points. "
|
||||||
|
"No preamble, output only bullet points prefixed with '- '."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
email_agent = LlmAgent(
|
||||||
|
name="email_generator",
|
||||||
|
description="Generates a professional email draft from the bullet points.",
|
||||||
|
model=MODEL_ID,
|
||||||
|
instruction=(
|
||||||
|
"You are an assistant that writes concise, friendly emails. Given bullet points, "
|
||||||
|
"compose a short email with: subject line, greeting, 1-2 paragraphs, and sign-off. "
|
||||||
|
"Return in plain text with 'Subject:' on the first line."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
root_agent = SequentialAgent(
|
||||||
|
name="root_pipeline",
|
||||||
|
description="Runs intake -> transform -> email_generator sequentially.",
|
||||||
|
sub_agents=[intake_agent, transform_agent, email_agent],
|
||||||
|
)
|
||||||
77
main.py
Normal file
77
main.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import uvicorn
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from fastapi import Body, FastAPI
|
||||||
|
from google.adk.cli.fast_api import get_fast_api_app
|
||||||
|
from google.adk.runners import InMemoryRunner
|
||||||
|
from google.genai import types as genai_types
|
||||||
|
|
||||||
|
from agents.pipeline.agent import root_agent as pipeline_root_agent
|
||||||
|
|
||||||
|
|
||||||
|
def build_pipeline_root(_: str):
|
||||||
|
# Agents now live under ./agents and are auto-loaded by ADK FastAPI app.
|
||||||
|
# Kept for compatibility with run_sample(); unused in API path.
|
||||||
|
from agents.pipeline.agent import root_agent as ra
|
||||||
|
return ra
|
||||||
|
|
||||||
|
|
||||||
|
def run_sample():
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# Allow GEMINI_API_KEY provided in .env to be used by google-genai client
|
||||||
|
api_key = os.getenv("GEMINI_API_KEY", "")
|
||||||
|
if api_key and not os.getenv("GOOGLE_GENAI_API_KEY"):
|
||||||
|
os.environ["GOOGLE_GENAI_API_KEY"] = api_key
|
||||||
|
|
||||||
|
# Sample disabled: use the FastAPI server instead.
|
||||||
|
print("Sample disabled: start the FastAPI app.")
|
||||||
|
|
||||||
|
|
||||||
|
"""FastAPI application configured via ADK's get_fast_api_app, auto-loading agents from ./agents."""
|
||||||
|
DEFAULT_ENV = os.path.abspath(os.path.join(os.path.dirname(__file__), ".env"))
|
||||||
|
ENV_FILE = os.getenv("ENV_FILE", DEFAULT_ENV)
|
||||||
|
load_dotenv(dotenv_path=ENV_FILE)
|
||||||
|
if os.getenv("GEMINI_API_KEY") and not os.getenv("GOOGLE_GENAI_API_KEY"):
|
||||||
|
os.environ["GOOGLE_GENAI_API_KEY"] = os.getenv("GEMINI_API_KEY")
|
||||||
|
|
||||||
|
AGENTS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "agents")
|
||||||
|
SESSION_SERVICE_URI = "sqlite:///./sessions.db"
|
||||||
|
ALLOWED_ORIGINS = ["*"]
|
||||||
|
SERVE_WEB_INTERFACE = True
|
||||||
|
|
||||||
|
app: FastAPI = get_fast_api_app(
|
||||||
|
agents_dir=AGENTS_DIR,
|
||||||
|
session_service_uri=SESSION_SERVICE_URI,
|
||||||
|
allow_origins=ALLOWED_ORIGINS,
|
||||||
|
web=SERVE_WEB_INTERFACE,
|
||||||
|
)
|
||||||
|
|
||||||
|
_api_runner = InMemoryRunner(agent=pipeline_root_agent)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/process_data")
|
||||||
|
def process_data(data: dict = Body(...)):
|
||||||
|
message = data.get("message") or data.get("text") or data.get("input") or ""
|
||||||
|
content = genai_types.Content(parts=[genai_types.Part.from_text(text=message)], role="user")
|
||||||
|
|
||||||
|
session = _api_runner.session_service.create_session_sync(
|
||||||
|
app_name=_api_runner.app_name,
|
||||||
|
user_id="api-user",
|
||||||
|
)
|
||||||
|
|
||||||
|
final_text = None
|
||||||
|
for event in _api_runner.run(user_id="api-user", session_id=session.id, new_message=content):
|
||||||
|
if getattr(event, "content", None):
|
||||||
|
for part in event.content.parts or []:
|
||||||
|
text = getattr(part, "text", None)
|
||||||
|
if text:
|
||||||
|
final_text = text
|
||||||
|
|
||||||
|
return {"emails": final_text or ""}
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Run API from this directory: `uv run uvicorn main:app --reload`
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 8000)))
|
||||||
20
pyproject.toml
Normal file
20
pyproject.toml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
[project]
|
||||||
|
name = "test-project"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Add your description here"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
dependencies = [
|
||||||
|
"google-adk>=1.10.0",
|
||||||
|
"ruff>=0.12.8",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
line-length = 100
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = ["E", "F", "I", "TID"]
|
||||||
|
[tool.ruff.lint.isort]
|
||||||
|
combine-as-imports = true
|
||||||
|
known-first-party = ["agents"]
|
||||||
|
[tool.ruff.lint.flake8-tidy-imports]
|
||||||
|
ban-relative-imports = "all"
|
||||||
Reference in New Issue
Block a user