commit 57cbf155eaa4e43d01396bb9ab14d892a206caf1 Author: abhishekbhakat Date: Sat Aug 9 16:09:37 2025 +0530 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ea6bd2 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e978e7 --- /dev/null +++ b/README.md @@ -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": "..." }`. + + diff --git a/agents/__init__.py b/agents/__init__.py new file mode 100644 index 0000000..f667acc --- /dev/null +++ b/agents/__init__.py @@ -0,0 +1 @@ +from agents.pipeline import agent as agent diff --git a/agents/pipeline/__init__.py b/agents/pipeline/__init__.py new file mode 100644 index 0000000..f667acc --- /dev/null +++ b/agents/pipeline/__init__.py @@ -0,0 +1 @@ +from agents.pipeline import agent as agent diff --git a/agents/pipeline/agent.py b/agents/pipeline/agent.py new file mode 100644 index 0000000..f1955bd --- /dev/null +++ b/agents/pipeline/agent.py @@ -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], +) diff --git a/main.py b/main.py new file mode 100644 index 0000000..e9a79e0 --- /dev/null +++ b/main.py @@ -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))) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0ee45a4 --- /dev/null +++ b/pyproject.toml @@ -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"