create-google-adk-agent 1.0.0__tar.gz
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.
- create_google_adk_agent-1.0.0/PKG-INFO +35 -0
- create_google_adk_agent-1.0.0/README.md +15 -0
- create_google_adk_agent-1.0.0/create_adk_agent/__init__.py +0 -0
- create_google_adk_agent-1.0.0/create_adk_agent/cli.py +179 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/agentic_rag/tests/test_agent.py +27 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/agentic_rag/{{PROJECT_NAME}}/__init__.py +1 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/agentic_rag/{{PROJECT_NAME}}/agent.py +76 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/base/Makefile +15 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/base/README.md +63 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/base/_dot_env.example +5 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/base/_dot_github/copilot-instructions.md +59 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/base/_dot_gitignore +7 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/base/pyproject.toml +19 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/multi/tests/test_agent.py +14 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/multi/{{PROJECT_NAME}}/__init__.py +1 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/multi/{{PROJECT_NAME}}/agent.py +49 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/single/tests/test_agent.py +28 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/single/{{PROJECT_NAME}}/__init__.py +1 -0
- create_google_adk_agent-1.0.0/create_adk_agent/template/single/{{PROJECT_NAME}}/agent.py +43 -0
- create_google_adk_agent-1.0.0/create_google_adk_agent.egg-info/PKG-INFO +35 -0
- create_google_adk_agent-1.0.0/create_google_adk_agent.egg-info/SOURCES.txt +25 -0
- create_google_adk_agent-1.0.0/create_google_adk_agent.egg-info/dependency_links.txt +1 -0
- create_google_adk_agent-1.0.0/create_google_adk_agent.egg-info/entry_points.txt +2 -0
- create_google_adk_agent-1.0.0/create_google_adk_agent.egg-info/requires.txt +1 -0
- create_google_adk_agent-1.0.0/create_google_adk_agent.egg-info/top_level.txt +1 -0
- create_google_adk_agent-1.0.0/setup.cfg +4 -0
- create_google_adk_agent-1.0.0/setup.py +32 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: create-google-adk-agent
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Interactive one-command scaffold for Google ADK (Agent Development Kit) projects
|
|
5
|
+
Home-page: https://github.com/unrealandychan/create-adk-agent
|
|
6
|
+
Author: unrealandychan
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: inquirerpy>=0.3.4
|
|
12
|
+
Dynamic: author
|
|
13
|
+
Dynamic: classifier
|
|
14
|
+
Dynamic: description
|
|
15
|
+
Dynamic: description-content-type
|
|
16
|
+
Dynamic: home-page
|
|
17
|
+
Dynamic: requires-dist
|
|
18
|
+
Dynamic: requires-python
|
|
19
|
+
Dynamic: summary
|
|
20
|
+
|
|
21
|
+
# create-google-adk-agent
|
|
22
|
+
|
|
23
|
+
Interactive one-command scaffold for [Google ADK](https://google.github.io/adk-docs/) projects.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uvx create-google-adk-agent
|
|
27
|
+
# or
|
|
28
|
+
npx create-google-adk-agent
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Supports:
|
|
32
|
+
- **3 agent types**: single, multi-agent, agentic RAG
|
|
33
|
+
- **5 LLM providers**: Google AI Studio, Vertex AI, OpenAI, Anthropic, Ollama
|
|
34
|
+
- Copilot instructions pre-wired
|
|
35
|
+
- `uv` + `adk web` ready out of the box
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# create-google-adk-agent
|
|
2
|
+
|
|
3
|
+
Interactive one-command scaffold for [Google ADK](https://google.github.io/adk-docs/) projects.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
uvx create-google-adk-agent
|
|
7
|
+
# or
|
|
8
|
+
npx create-google-adk-agent
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Supports:
|
|
12
|
+
- **3 agent types**: single, multi-agent, agentic RAG
|
|
13
|
+
- **5 LLM providers**: Google AI Studio, Vertex AI, OpenAI, Anthropic, Ollama
|
|
14
|
+
- Copilot instructions pre-wired
|
|
15
|
+
- `uv` + `adk web` ready out of the box
|
|
File without changes
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""create-google-adk-agent CLI — interactive ADK project scaffold."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import shutil
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from InquirerPy import inquirer
|
|
10
|
+
from InquirerPy.base.control import Choice
|
|
11
|
+
except ImportError:
|
|
12
|
+
print("Installing InquirerPy...")
|
|
13
|
+
os.system(f"{sys.executable} -m pip install inquirerpy -q")
|
|
14
|
+
from InquirerPy import inquirer
|
|
15
|
+
from InquirerPy.base.control import Choice
|
|
16
|
+
|
|
17
|
+
TEMPLATE_DIR = Path(__file__).parent / "template"
|
|
18
|
+
|
|
19
|
+
PROVIDERS = {
|
|
20
|
+
"google_ai_studio": {
|
|
21
|
+
"label": "Google AI Studio (free tier, just GOOGLE_API_KEY)",
|
|
22
|
+
"envVars": ["GOOGLE_API_KEY"],
|
|
23
|
+
"envComment": "# Get yours: https://aistudio.google.com/app/apikey",
|
|
24
|
+
"defaultModel": "gemini-2.0-flash",
|
|
25
|
+
"modelExpr": lambda m: f'"{m}"',
|
|
26
|
+
"litellmImport": "",
|
|
27
|
+
"extraDeps": "",
|
|
28
|
+
},
|
|
29
|
+
"vertex_ai": {
|
|
30
|
+
"label": "Vertex AI (GCP project required)",
|
|
31
|
+
"envVars": ["GOOGLE_CLOUD_PROJECT", "GOOGLE_CLOUD_LOCATION"],
|
|
32
|
+
"envComment": "# Set up: gcloud auth application-default login",
|
|
33
|
+
"defaultModel": "gemini-2.0-flash",
|
|
34
|
+
"modelExpr": lambda m: f'"{m}"',
|
|
35
|
+
"litellmImport": "",
|
|
36
|
+
"extraDeps": ' "google-cloud-aiplatform>=1.38",',
|
|
37
|
+
},
|
|
38
|
+
"openai": {
|
|
39
|
+
"label": "OpenAI (gpt-4o via ADK LiteLLM)",
|
|
40
|
+
"envVars": ["OPENAI_API_KEY"],
|
|
41
|
+
"envComment": "# Get yours: https://platform.openai.com/api-keys",
|
|
42
|
+
"defaultModel": "gpt-4o",
|
|
43
|
+
"modelExpr": lambda m: f'LiteLlm(model="openai/{m}")',
|
|
44
|
+
"litellmImport": "from google.adk.models.lite_llm import LiteLlm\n",
|
|
45
|
+
"extraDeps": ' "litellm>=1.40",',
|
|
46
|
+
},
|
|
47
|
+
"anthropic": {
|
|
48
|
+
"label": "Anthropic (claude via ADK LiteLLM)",
|
|
49
|
+
"envVars": ["ANTHROPIC_API_KEY"],
|
|
50
|
+
"envComment": "# Get yours: https://console.anthropic.com/",
|
|
51
|
+
"defaultModel": "claude-3-5-sonnet-20241022",
|
|
52
|
+
"modelExpr": lambda m: f'LiteLlm(model="anthropic/{m}")',
|
|
53
|
+
"litellmImport": "from google.adk.models.lite_llm import LiteLlm\n",
|
|
54
|
+
"extraDeps": ' "litellm>=1.40",',
|
|
55
|
+
},
|
|
56
|
+
"ollama": {
|
|
57
|
+
"label": "Ollama (local, no API key needed)",
|
|
58
|
+
"envVars": [],
|
|
59
|
+
"envComment": "# Make sure ollama is running: ollama serve",
|
|
60
|
+
"defaultModel": "llama3.2",
|
|
61
|
+
"modelExpr": lambda m: f'LiteLlm(model="ollama/{m}")',
|
|
62
|
+
"litellmImport": "from google.adk.models.lite_llm import LiteLlm\n",
|
|
63
|
+
"extraDeps": ' "litellm>=1.40",',
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
AGENT_TYPES = {
|
|
68
|
+
"single": "One agent with tools — best starting point",
|
|
69
|
+
"multi": "Orchestrator + specialist sub-agents",
|
|
70
|
+
"agentic_rag": "RAG pipeline — load & query documents",
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def render(text: str, vars: dict) -> str:
|
|
75
|
+
for k, v in vars.items():
|
|
76
|
+
text = text.replace("{{" + k + "}}", v)
|
|
77
|
+
return text
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def copy_tree(src: Path, dest: Path, vars: dict):
|
|
81
|
+
dest.mkdir(parents=True, exist_ok=True)
|
|
82
|
+
for entry in src.iterdir():
|
|
83
|
+
real_name = entry.name.replace("_dot_", ".", 1)
|
|
84
|
+
dest_name = render(real_name, vars)
|
|
85
|
+
dest_path = dest / dest_name
|
|
86
|
+
if entry.is_dir():
|
|
87
|
+
copy_tree(entry, dest_path, vars)
|
|
88
|
+
else:
|
|
89
|
+
content = render(entry.read_text(errors="replace"), vars)
|
|
90
|
+
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
dest_path.write_text(content)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def main():
|
|
95
|
+
print("\n \033[1;36m╔═══════════════════════════════════╗")
|
|
96
|
+
print(" ║ create-google-adk-agent 🤖 ║")
|
|
97
|
+
print(" ╚═══════════════════════════════════╝\033[0m")
|
|
98
|
+
print(" \033[2mBootstrap a Google ADK project in seconds\033[0m\n")
|
|
99
|
+
|
|
100
|
+
# Project name
|
|
101
|
+
project_name = inquirer.text(
|
|
102
|
+
message="Project name:",
|
|
103
|
+
default="my-adk-agent",
|
|
104
|
+
).execute()
|
|
105
|
+
safe_name = "".join(c if c.isalnum() or c == "_" else "_" for c in project_name)
|
|
106
|
+
if safe_name[0].isdigit():
|
|
107
|
+
safe_name = "_" + safe_name
|
|
108
|
+
|
|
109
|
+
# Agent type
|
|
110
|
+
agent_type = inquirer.select(
|
|
111
|
+
message="Agent type:",
|
|
112
|
+
choices=[Choice(k, name=f"{k:<14} — {v}") for k, v in AGENT_TYPES.items()],
|
|
113
|
+
).execute()
|
|
114
|
+
|
|
115
|
+
# Provider
|
|
116
|
+
provider_key = inquirer.select(
|
|
117
|
+
message="LLM Provider:",
|
|
118
|
+
choices=[Choice(k, name=v["label"]) for k, v in PROVIDERS.items()],
|
|
119
|
+
).execute()
|
|
120
|
+
provider = PROVIDERS[provider_key]
|
|
121
|
+
|
|
122
|
+
# Model
|
|
123
|
+
model = inquirer.text(
|
|
124
|
+
message="Model:",
|
|
125
|
+
default=provider["defaultModel"],
|
|
126
|
+
).execute()
|
|
127
|
+
|
|
128
|
+
# Build vars
|
|
129
|
+
env_content = (
|
|
130
|
+
"\n".join(f"{v}=your_key_here" for v in provider["envVars"])
|
|
131
|
+
if provider["envVars"]
|
|
132
|
+
else "# No API key required for this provider"
|
|
133
|
+
)
|
|
134
|
+
VARS = {
|
|
135
|
+
"PROJECT_NAME": safe_name,
|
|
136
|
+
"AGENT_TYPE": agent_type,
|
|
137
|
+
"PROVIDER_KEY": provider_key,
|
|
138
|
+
"MODEL_NAME": model,
|
|
139
|
+
"MODEL_EXPR": provider["modelExpr"](model),
|
|
140
|
+
"LITELM_IMPORT": provider["litellmImport"],
|
|
141
|
+
"EXTRA_DEPS": provider["extraDeps"],
|
|
142
|
+
"ENV_VARS_COMMENT": provider["envComment"],
|
|
143
|
+
"ENV_CONTENT": env_content,
|
|
144
|
+
"LITELM_IMPORT_COPILOT": provider["litellmImport"],
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
out_dir = Path.cwd() / project_name
|
|
148
|
+
if out_dir.exists():
|
|
149
|
+
print(f"\n\033[31m✗ Directory '{project_name}' already exists.\033[0m\n")
|
|
150
|
+
sys.exit(1)
|
|
151
|
+
|
|
152
|
+
copy_tree(TEMPLATE_DIR / "base", out_dir, VARS)
|
|
153
|
+
copy_tree(TEMPLATE_DIR / agent_type, out_dir, VARS)
|
|
154
|
+
|
|
155
|
+
env_ex = out_dir / "_dot_env.example"
|
|
156
|
+
if env_ex.exists():
|
|
157
|
+
env_ex.rename(out_dir / ".env.example")
|
|
158
|
+
|
|
159
|
+
env_note = "\n".join(f" {v}=<your_key>" for v in provider["envVars"]) or " # No key needed!"
|
|
160
|
+
|
|
161
|
+
print(f"""
|
|
162
|
+
\033[32m✅ Done!\033[0m \033[1m{project_name}\033[0m created.
|
|
163
|
+
|
|
164
|
+
\033[1mNext steps:\033[0m
|
|
165
|
+
|
|
166
|
+
1. \033[36mcd {project_name}\033[0m
|
|
167
|
+
2. Edit \033[36m.env.example\033[0m → \033[36m.env\033[0m:
|
|
168
|
+
\033[2m{env_note}\033[0m
|
|
169
|
+
3. \033[36muv sync\033[0m
|
|
170
|
+
4. \033[36muv run adk web\033[0m \033[2m→ open http://localhost:8000\033[0m
|
|
171
|
+
|
|
172
|
+
\033[2mAgent type: \033[0m\033[1m{agent_type}\033[0m
|
|
173
|
+
\033[2mProvider: \033[0m\033[1m{provider_key}\033[0m
|
|
174
|
+
\033[2mModel: \033[0m\033[1m{model}\033[0m
|
|
175
|
+
""")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
if __name__ == "__main__":
|
|
179
|
+
main()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Tests for {{PROJECT_NAME}} RAG agent."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import tempfile, os
|
|
5
|
+
|
|
6
|
+
def test_root_agent_exists():
|
|
7
|
+
from {{PROJECT_NAME}}.agent import root_agent
|
|
8
|
+
assert root_agent.name == "{{PROJECT_NAME}}"
|
|
9
|
+
|
|
10
|
+
def test_load_documents_missing_folder():
|
|
11
|
+
from {{PROJECT_NAME}}.agent import load_documents
|
|
12
|
+
result = load_documents("/nonexistent/folder")
|
|
13
|
+
assert result["status"] == "error"
|
|
14
|
+
|
|
15
|
+
def test_load_documents_success():
|
|
16
|
+
from {{PROJECT_NAME}}.agent import load_documents, search_documents
|
|
17
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
18
|
+
(open(f"{tmpdir}/test.txt", "w")).write("ADK is a framework for building AI agents by Google.")
|
|
19
|
+
result = load_documents(tmpdir)
|
|
20
|
+
assert result["status"] == "success"
|
|
21
|
+
assert result["loaded"] == 1
|
|
22
|
+
|
|
23
|
+
def test_search_no_docs():
|
|
24
|
+
from {{PROJECT_NAME}}.agent import _DOCS, search_documents
|
|
25
|
+
_DOCS.clear()
|
|
26
|
+
result = search_documents("anything")
|
|
27
|
+
assert result["status"] == "error"
|
create_google_adk_agent-1.0.0/create_adk_agent/template/agentic_rag/{{PROJECT_NAME}}/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import agent
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""{{PROJECT_NAME}} — ADK agentic RAG pipeline."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
{{LITELM_IMPORT}}from google.adk.agents import Agent
|
|
7
|
+
|
|
8
|
+
load_dotenv()
|
|
9
|
+
|
|
10
|
+
_MODEL = {{MODEL_EXPR}}
|
|
11
|
+
|
|
12
|
+
# ── Simple in-memory document store (replace with your vector DB) ─────────────
|
|
13
|
+
|
|
14
|
+
_DOCS: list[dict] = [] # {"id": str, "content": str, "source": str}
|
|
15
|
+
|
|
16
|
+
def load_documents(folder: str = "./docs") -> dict:
|
|
17
|
+
"""
|
|
18
|
+
Load all .txt and .md files from a folder into the document store.
|
|
19
|
+
Call this before asking questions.
|
|
20
|
+
"""
|
|
21
|
+
global _DOCS
|
|
22
|
+
_DOCS = []
|
|
23
|
+
p = Path(folder)
|
|
24
|
+
if not p.exists():
|
|
25
|
+
return {"status": "error", "message": f"Folder '{folder}' not found."}
|
|
26
|
+
for f in p.glob("**/*.txt"):
|
|
27
|
+
_DOCS.append({"id": str(f), "content": f.read_text(errors="replace"), "source": str(f)})
|
|
28
|
+
for f in p.glob("**/*.md"):
|
|
29
|
+
_DOCS.append({"id": str(f), "content": f.read_text(errors="replace"), "source": str(f)})
|
|
30
|
+
return {"status": "success", "loaded": len(_DOCS), "sources": [d["source"] for d in _DOCS]}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def search_documents(query: str, top_k: int = 3) -> dict:
|
|
34
|
+
"""
|
|
35
|
+
Search loaded documents for content relevant to the query.
|
|
36
|
+
Returns top matching passages. Load documents first with load_documents().
|
|
37
|
+
"""
|
|
38
|
+
if not _DOCS:
|
|
39
|
+
return {
|
|
40
|
+
"status": "error",
|
|
41
|
+
"message": "No documents loaded. Call load_documents() first.",
|
|
42
|
+
}
|
|
43
|
+
# Simple keyword search — swap for vector similarity in production
|
|
44
|
+
query_words = set(query.lower().split())
|
|
45
|
+
scored = []
|
|
46
|
+
for doc in _DOCS:
|
|
47
|
+
content_lower = doc["content"].lower()
|
|
48
|
+
score = sum(1 for w in query_words if w in content_lower)
|
|
49
|
+
if score > 0:
|
|
50
|
+
# Return a relevant chunk (first 500 chars for now)
|
|
51
|
+
scored.append((score, doc))
|
|
52
|
+
scored.sort(key=lambda x: x[0], reverse=True)
|
|
53
|
+
results = [
|
|
54
|
+
{"source": d["source"], "excerpt": d["content"][:500]}
|
|
55
|
+
for _, d in scored[:top_k]
|
|
56
|
+
]
|
|
57
|
+
if not results:
|
|
58
|
+
return {"status": "not_found", "message": "No relevant documents found.", "results": []}
|
|
59
|
+
return {"status": "success", "results": results}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
root_agent = Agent(
|
|
63
|
+
name="{{PROJECT_NAME}}",
|
|
64
|
+
model=_MODEL,
|
|
65
|
+
description="RAG agent that answers questions from loaded documents.",
|
|
66
|
+
instruction="""
|
|
67
|
+
You are a helpful RAG assistant. To answer questions:
|
|
68
|
+
1. First call search_documents() to retrieve relevant passages
|
|
69
|
+
2. Base your answer strictly on the retrieved content
|
|
70
|
+
3. Always cite which document your answer comes from
|
|
71
|
+
4. If no relevant documents are found, say so clearly
|
|
72
|
+
|
|
73
|
+
If the user wants to load documents, call load_documents() with the folder path.
|
|
74
|
+
""",
|
|
75
|
+
tools=[load_documents, search_documents],
|
|
76
|
+
)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
> 🤖 ADK agent — powered by [Google Agent Development Kit](https://google.github.io/adk-docs/)
|
|
4
|
+
> **Type:** {{AGENT_TYPE}} | **Provider:** {{PROVIDER_KEY}} | **Model:** {{MODEL_NAME}}
|
|
5
|
+
|
|
6
|
+
## Quick Start
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
cp .env.example .env # add your API key
|
|
10
|
+
uv sync # install deps
|
|
11
|
+
uv run adk web # open Dev UI at http://localhost:8000
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Project Structure
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
{{PROJECT_NAME}}/ ← ADK requires this folder = module name
|
|
18
|
+
__init__.py
|
|
19
|
+
agent.py ← root_agent defined here
|
|
20
|
+
tools/
|
|
21
|
+
example.py
|
|
22
|
+
tests/
|
|
23
|
+
test_agent.py
|
|
24
|
+
pyproject.toml
|
|
25
|
+
.env.example
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> ⚠️ ADK convention: `adk web` is run from the **parent** folder, and it loads `{{PROJECT_NAME}}/agent.py`
|
|
29
|
+
|
|
30
|
+
## Running
|
|
31
|
+
|
|
32
|
+
| Command | What it does |
|
|
33
|
+
|---------|-------------|
|
|
34
|
+
| `uv run adk web` | Dev UI at localhost:8000 (chat + trace + events) |
|
|
35
|
+
| `uv run adk run {{PROJECT_NAME}}` | CLI chat mode |
|
|
36
|
+
| `uv run adk api_server` | FastAPI server |
|
|
37
|
+
| `make dev` | Same as `adk web` |
|
|
38
|
+
| `make test` | Run pytest |
|
|
39
|
+
|
|
40
|
+
## Adding Tools
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
# {{PROJECT_NAME}}/tools/my_tool.py
|
|
44
|
+
def my_tool(input: str) -> dict:
|
|
45
|
+
"""Describe what this tool does — ADK uses this as the tool description."""
|
|
46
|
+
return {"result": input}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Register in `agent.py`:
|
|
50
|
+
```python
|
|
51
|
+
from .tools.my_tool import my_tool
|
|
52
|
+
root_agent = Agent(..., tools=[my_tool])
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Provider: {{PROVIDER_KEY}}
|
|
56
|
+
|
|
57
|
+
{{ENV_CONTENT}}
|
|
58
|
+
|
|
59
|
+
## Docs
|
|
60
|
+
|
|
61
|
+
- [ADK Docs](https://google.github.io/adk-docs/)
|
|
62
|
+
- [ADK Samples](https://github.com/google/adk-samples)
|
|
63
|
+
- [ADK Python](https://github.com/google/adk-python)
|
create_google_adk_agent-1.0.0/create_adk_agent/template/base/_dot_github/copilot-instructions.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Copilot Instructions — {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
## What this project is
|
|
4
|
+
|
|
5
|
+
A **Google ADK** (Agent Development Kit) agent.
|
|
6
|
+
- **Type:** {{AGENT_TYPE}}
|
|
7
|
+
- **Provider:** {{PROVIDER_KEY}}
|
|
8
|
+
- **Model:** {{MODEL_NAME}}
|
|
9
|
+
|
|
10
|
+
## ADK Key Concepts
|
|
11
|
+
|
|
12
|
+
- `Agent` — core agent class with `name`, `model`, `instruction`, `tools`, `sub_agents`
|
|
13
|
+
- `root_agent` — **must** be named exactly `root_agent` in `agent.py` (ADK convention)
|
|
14
|
+
- Tools are plain Python functions with a docstring — ADK auto-generates the schema
|
|
15
|
+
- `SequentialAgent` — runs sub-agents one after another
|
|
16
|
+
- `ParallelAgent` — runs sub-agents concurrently
|
|
17
|
+
- `LoopAgent` — repeats a sub-agent until condition met
|
|
18
|
+
|
|
19
|
+
## Project Layout
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
{{PROJECT_NAME}}/ ← Python module (ADK loads this)
|
|
23
|
+
__init__.py
|
|
24
|
+
agent.py ← root_agent lives here
|
|
25
|
+
tools/ ← plain Python functions used as tools
|
|
26
|
+
tests/
|
|
27
|
+
pyproject.toml
|
|
28
|
+
.env / .env.example
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Running
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv run adk web # Dev UI at http://localhost:8000
|
|
35
|
+
uv run adk run {{PROJECT_NAME}} # CLI mode
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
> ⚠️ Run from the **parent** folder, not inside `{{PROJECT_NAME}}/`
|
|
39
|
+
|
|
40
|
+
## Model config ({{PROVIDER_KEY}})
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
{{LITELM_IMPORT}}model = {{MODEL_EXPR}}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Common Copilot tasks
|
|
47
|
+
|
|
48
|
+
- "Add a tool that fetches live stock prices"
|
|
49
|
+
- "Add a sub-agent for data analysis and wire it with SequentialAgent"
|
|
50
|
+
- "Add input/output guardrails"
|
|
51
|
+
- "Write a pytest test for the root agent"
|
|
52
|
+
- "Add session memory so the agent remembers previous turns"
|
|
53
|
+
- "Switch the model to gemini-1.5-pro"
|
|
54
|
+
|
|
55
|
+
## Code style
|
|
56
|
+
|
|
57
|
+
- Python 3.11+, type hints everywhere
|
|
58
|
+
- Tool functions: return `dict` with `{"status": "success", ...}` or `{"status": "error", "message": ...}`
|
|
59
|
+
- `root_agent` must always be exported from `agent.py`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "{{PROJECT_NAME}}"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
requires-python = ">=3.11"
|
|
5
|
+
dependencies = [
|
|
6
|
+
"google-adk>=1.0",
|
|
7
|
+
"python-dotenv>=1.0",
|
|
8
|
+
{{EXTRA_DEPS}}
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
[project.optional-dependencies]
|
|
12
|
+
dev = ["pytest>=8.0", "pytest-asyncio>=0.23"]
|
|
13
|
+
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["hatchling"]
|
|
16
|
+
build-backend = "hatchling.build"
|
|
17
|
+
|
|
18
|
+
[tool.pytest.ini_options]
|
|
19
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Tests for {{PROJECT_NAME}} multi-agent."""
|
|
2
|
+
|
|
3
|
+
def test_root_agent_exists():
|
|
4
|
+
from {{PROJECT_NAME}}.agent import root_agent
|
|
5
|
+
assert root_agent.name == "{{PROJECT_NAME}}"
|
|
6
|
+
|
|
7
|
+
def test_root_agent_has_sub_agents():
|
|
8
|
+
from {{PROJECT_NAME}}.agent import root_agent
|
|
9
|
+
assert len(root_agent.sub_agents) == 2
|
|
10
|
+
|
|
11
|
+
def test_specialist_agents():
|
|
12
|
+
from {{PROJECT_NAME}}.agent import research_agent, writer_agent
|
|
13
|
+
assert research_agent.name == "research_agent"
|
|
14
|
+
assert writer_agent.name == "writer_agent"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import agent
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""{{PROJECT_NAME}} — ADK multi-agent (orchestrator + specialists)."""
|
|
2
|
+
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
{{LITELM_IMPORT}}from google.adk.agents import Agent
|
|
5
|
+
|
|
6
|
+
load_dotenv()
|
|
7
|
+
|
|
8
|
+
_MODEL = {{MODEL_EXPR}}
|
|
9
|
+
|
|
10
|
+
# ── Specialist agents ─────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
research_agent = Agent(
|
|
13
|
+
name="research_agent",
|
|
14
|
+
model=_MODEL,
|
|
15
|
+
description="Specialist for research, facts, and information retrieval.",
|
|
16
|
+
instruction="""
|
|
17
|
+
You are a research specialist. Answer factual questions thoroughly.
|
|
18
|
+
Be precise and cite reasoning. Return structured, detailed answers.
|
|
19
|
+
""",
|
|
20
|
+
tools=[],
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
writer_agent = Agent(
|
|
24
|
+
name="writer_agent",
|
|
25
|
+
model=_MODEL,
|
|
26
|
+
description="Specialist for writing, editing, summarising, and drafting content.",
|
|
27
|
+
instruction="""
|
|
28
|
+
You are a writing specialist. Help draft, edit, summarise, and improve text.
|
|
29
|
+
Be clear, concise, and match the requested tone.
|
|
30
|
+
""",
|
|
31
|
+
tools=[],
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# ── Orchestrator ──────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
root_agent = Agent(
|
|
37
|
+
name="{{PROJECT_NAME}}",
|
|
38
|
+
model=_MODEL,
|
|
39
|
+
description="Orchestrator that routes requests to the right specialist.",
|
|
40
|
+
instruction="""
|
|
41
|
+
You are an orchestrator. Delegate tasks to the most suitable specialist:
|
|
42
|
+
- research_agent: factual questions, research, data, analysis
|
|
43
|
+
- writer_agent: writing, editing, drafting, summarising
|
|
44
|
+
|
|
45
|
+
If neither specialist is needed, handle it yourself briefly.
|
|
46
|
+
Always pass the full user request to the chosen specialist.
|
|
47
|
+
""",
|
|
48
|
+
sub_agents=[research_agent, writer_agent],
|
|
49
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Tests for {{PROJECT_NAME}}."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from unittest.mock import patch, MagicMock
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_root_agent_exists():
|
|
8
|
+
from {{PROJECT_NAME}}.agent import root_agent
|
|
9
|
+
assert root_agent is not None
|
|
10
|
+
assert root_agent.name == "{{PROJECT_NAME}}"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_root_agent_has_tools():
|
|
14
|
+
from {{PROJECT_NAME}}.agent import root_agent
|
|
15
|
+
assert len(root_agent.tools) > 0
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_get_current_time_known_city():
|
|
19
|
+
from {{PROJECT_NAME}}.agent import get_current_time
|
|
20
|
+
result = get_current_time("hong kong")
|
|
21
|
+
assert result["status"] == "success"
|
|
22
|
+
assert "time" in result
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_get_current_time_unknown_city():
|
|
26
|
+
from {{PROJECT_NAME}}.agent import get_current_time
|
|
27
|
+
result = get_current_time("atlantis")
|
|
28
|
+
assert result["status"] == "error"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import agent
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""{{PROJECT_NAME}} — ADK single agent."""
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
from zoneinfo import ZoneInfo
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
|
|
7
|
+
{{LITELM_IMPORT}}from google.adk.agents import Agent
|
|
8
|
+
|
|
9
|
+
load_dotenv()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_current_time(city: str) -> dict:
|
|
13
|
+
"""Get the current time in a given city. Returns time as a formatted string."""
|
|
14
|
+
TIMEZONES = {
|
|
15
|
+
"hong kong": "Asia/Hong_Kong",
|
|
16
|
+
"london": "Europe/London",
|
|
17
|
+
"new york": "America/New_York",
|
|
18
|
+
"tokyo": "Asia/Tokyo",
|
|
19
|
+
"sydney": "Australia/Sydney",
|
|
20
|
+
"paris": "Europe/Paris",
|
|
21
|
+
}
|
|
22
|
+
tz_name = TIMEZONES.get(city.lower())
|
|
23
|
+
if not tz_name:
|
|
24
|
+
return {"status": "error", "message": f"Timezone for '{city}' not found."}
|
|
25
|
+
now = datetime.datetime.now(ZoneInfo(tz_name))
|
|
26
|
+
return {
|
|
27
|
+
"status": "success",
|
|
28
|
+
"city": city,
|
|
29
|
+
"time": now.strftime("%Y-%m-%d %H:%M:%S %Z"),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
root_agent = Agent(
|
|
34
|
+
name="{{PROJECT_NAME}}",
|
|
35
|
+
model={{MODEL_EXPR}},
|
|
36
|
+
description="A helpful AI assistant.",
|
|
37
|
+
instruction="""
|
|
38
|
+
You are a helpful and concise AI assistant.
|
|
39
|
+
Use available tools when relevant to answer the user's question.
|
|
40
|
+
Always be accurate, friendly, and brief.
|
|
41
|
+
""",
|
|
42
|
+
tools=[get_current_time],
|
|
43
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: create-google-adk-agent
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Interactive one-command scaffold for Google ADK (Agent Development Kit) projects
|
|
5
|
+
Home-page: https://github.com/unrealandychan/create-adk-agent
|
|
6
|
+
Author: unrealandychan
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: inquirerpy>=0.3.4
|
|
12
|
+
Dynamic: author
|
|
13
|
+
Dynamic: classifier
|
|
14
|
+
Dynamic: description
|
|
15
|
+
Dynamic: description-content-type
|
|
16
|
+
Dynamic: home-page
|
|
17
|
+
Dynamic: requires-dist
|
|
18
|
+
Dynamic: requires-python
|
|
19
|
+
Dynamic: summary
|
|
20
|
+
|
|
21
|
+
# create-google-adk-agent
|
|
22
|
+
|
|
23
|
+
Interactive one-command scaffold for [Google ADK](https://google.github.io/adk-docs/) projects.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uvx create-google-adk-agent
|
|
27
|
+
# or
|
|
28
|
+
npx create-google-adk-agent
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Supports:
|
|
32
|
+
- **3 agent types**: single, multi-agent, agentic RAG
|
|
33
|
+
- **5 LLM providers**: Google AI Studio, Vertex AI, OpenAI, Anthropic, Ollama
|
|
34
|
+
- Copilot instructions pre-wired
|
|
35
|
+
- `uv` + `adk web` ready out of the box
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
setup.py
|
|
3
|
+
create_adk_agent/__init__.py
|
|
4
|
+
create_adk_agent/cli.py
|
|
5
|
+
create_adk_agent/template/agentic_rag/tests/test_agent.py
|
|
6
|
+
create_adk_agent/template/agentic_rag/{{PROJECT_NAME}}/__init__.py
|
|
7
|
+
create_adk_agent/template/agentic_rag/{{PROJECT_NAME}}/agent.py
|
|
8
|
+
create_adk_agent/template/base/Makefile
|
|
9
|
+
create_adk_agent/template/base/README.md
|
|
10
|
+
create_adk_agent/template/base/_dot_env.example
|
|
11
|
+
create_adk_agent/template/base/_dot_gitignore
|
|
12
|
+
create_adk_agent/template/base/pyproject.toml
|
|
13
|
+
create_adk_agent/template/base/_dot_github/copilot-instructions.md
|
|
14
|
+
create_adk_agent/template/multi/tests/test_agent.py
|
|
15
|
+
create_adk_agent/template/multi/{{PROJECT_NAME}}/__init__.py
|
|
16
|
+
create_adk_agent/template/multi/{{PROJECT_NAME}}/agent.py
|
|
17
|
+
create_adk_agent/template/single/tests/test_agent.py
|
|
18
|
+
create_adk_agent/template/single/{{PROJECT_NAME}}/__init__.py
|
|
19
|
+
create_adk_agent/template/single/{{PROJECT_NAME}}/agent.py
|
|
20
|
+
create_google_adk_agent.egg-info/PKG-INFO
|
|
21
|
+
create_google_adk_agent.egg-info/SOURCES.txt
|
|
22
|
+
create_google_adk_agent.egg-info/dependency_links.txt
|
|
23
|
+
create_google_adk_agent.egg-info/entry_points.txt
|
|
24
|
+
create_google_adk_agent.egg-info/requires.txt
|
|
25
|
+
create_google_adk_agent.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
inquirerpy>=0.3.4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
create_adk_agent
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
from setuptools import setup, find_packages
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
# Collect all template files
|
|
6
|
+
def get_package_data():
|
|
7
|
+
data = []
|
|
8
|
+
for root, dirs, files in os.walk("create_adk_agent/template"):
|
|
9
|
+
for f in files:
|
|
10
|
+
rel = os.path.relpath(os.path.join(root, f), "create_adk_agent")
|
|
11
|
+
data.append(rel)
|
|
12
|
+
return data
|
|
13
|
+
|
|
14
|
+
setup(
|
|
15
|
+
name="create-google-adk-agent",
|
|
16
|
+
version="1.0.0",
|
|
17
|
+
description="Interactive one-command scaffold for Google ADK (Agent Development Kit) projects",
|
|
18
|
+
long_description=open("README.md").read(),
|
|
19
|
+
long_description_content_type="text/markdown",
|
|
20
|
+
author="unrealandychan",
|
|
21
|
+
url="https://github.com/unrealandychan/create-adk-agent",
|
|
22
|
+
packages=find_packages(),
|
|
23
|
+
package_data={"create_adk_agent": get_package_data()},
|
|
24
|
+
include_package_data=True,
|
|
25
|
+
entry_points={"console_scripts": ["create-google-adk-agent=create_adk_agent.cli:main"]},
|
|
26
|
+
python_requires=">=3.11",
|
|
27
|
+
install_requires=["inquirerpy>=0.3.4"],
|
|
28
|
+
classifiers=[
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"License :: OSI Approved :: MIT License",
|
|
31
|
+
],
|
|
32
|
+
)
|