Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 71 additions & 51 deletions frontend/src/create/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ function renderEnvExample(env: EnvVar[]): string {
function renderRequirements(extras: Set<string>): string {
const list = [...extras].sort();
const pkg = list.length ? `veadk-python[${list.join(",")}]` : "veadk-python";
return `${pkg}\n`;
return `${pkg}\nagentkit-sdk-python\ngoogle-adk\n`;
}

function renderReadme(name: string, draft: AgentDraft): string {
Expand All @@ -307,11 +307,10 @@ function renderReadme(name: string, draft: AgentDraft): string {
"```bash",
"pip install -r requirements.txt",
"cp .env.example .env # 填入你的密钥",
"# 在本项目的上级目录启动 ADK API 服务:",
`adk api_server --agents_dir .`,
"python app.py",
"```",
"",
"`agent.py` 在模块级别暴露 `root_agent`,可被 ADK / VeADK 直接加载。",
"`app.py` 使用 AgentKit AgentServerApp 包裹 `root_agent`,监听 `0.0.0.0:8000`。",
"",
].join("\n");
}
Expand All @@ -323,16 +322,17 @@ export function generateProject(draft: AgentDraft): AgentProject {

buildAgent(acc, draft, "agent");

// Assemble agent.py with FastAPI deployment support
// Assemble agent.py with AgentKit deployment support
const importBlock = ["from veadk import Agent", ...dedupeImports(acc.imports)].join("\n");

// Add deployment-specific imports
const deploymentImports = [
"import os",
"from pathlib import Path",
"import uvicorn",
"from agentkit.apps import AgentkitAgentServerApp",
"from fastapi.staticfiles import StaticFiles",
"from google.adk.cli.fast_api import get_fast_api_app",
"from veadk.memory.short_term_memory import ShortTermMemory",
`from agents.${pkg}.agent import root_agent`,
].join("\n");

// Build agent definition
Expand All @@ -346,15 +346,22 @@ export function generateProject(draft: AgentDraft): AgentProject {
# Deployment configuration
HOST = os.getenv("HOST", "0.0.0.0")
PORT = int(os.getenv("PORT", "8000"))
AGENTS_DIR = str(Path(__file__).resolve().parent / "agents")

def build_app():
"""Build FastAPI app for deployment."""
"""Build AgentKit AgentServerApp for deployment."""
import veadk
WEBUI_DIR = Path(veadk.__file__).resolve().parent / "webui"

# Create FastAPI app with agents_dir (ADK multi-agent structure)
app = get_fast_api_app(agents_dir=AGENTS_DIR, web=False)
# AgentKit's AgentServerApp exposes the ADK-compatible API surface
# (/list-apps, /run, /run_sse, sessions) expected by AgentKit runtime tests.
short_term_memory = getattr(root_agent, "short_term_memory", None) or ShortTermMemory(
backend="local"
)
agent_server_app = AgentkitAgentServerApp(
agent=root_agent,
short_term_memory=short_term_memory,
)
app = agent_server_app.app

# Add health check endpoint
@app.get("/ping")
Expand All @@ -365,13 +372,6 @@ def build_app():
# UI's "管理 Agent" view to show this runtime's agent name + sub-agent tree.
from fastapi import HTTPException as _HTTPException

try:
from google.adk.cli.utils.agent_loader import AgentLoader as _AgentLoader

_agent_loader = _AgentLoader(AGENTS_DIR)
except Exception:
_agent_loader = None

def _agent_type(a: object) -> str:
try:
from google.adk.agents import LoopAgent, ParallelAgent, SequentialAgent
Expand Down Expand Up @@ -417,56 +417,76 @@ def build_app():

@app.get("/web/agent-info/{app_name}")
def agent_info(app_name: str) -> dict:
if _agent_loader is None:
raise _HTTPException(status_code=500, detail="agent loader unavailable")
try:
a = _agent_loader.load_agent(app_name)
except Exception:
expected_name = getattr(root_agent, "name", "") or ""
if app_name != expected_name:
raise _HTTPException(status_code=404, detail="unknown agent: " + app_name)
return {
"name": getattr(a, "name", app_name),
"description": getattr(a, "description", "") or "",
"type": _agent_type(a),
"model": _model_name(getattr(a, "model", "")),
"tools": [_tool_label(t) for t in getattr(a, "tools", []) or []],
"subAgents": [getattr(s, "name", "") for s in getattr(a, "sub_agents", []) or []],
"graph": _agent_node(a),
"name": expected_name or app_name,
"description": getattr(root_agent, "description", "") or "",
"type": _agent_type(root_agent),
"model": _model_name(getattr(root_agent, "model", "")),
"tools": [_tool_label(t) for t in getattr(root_agent, "tools", []) or []],
"subAgents": [getattr(s, "name", "") for s in getattr(root_agent, "sub_agents", []) or []],
"graph": _agent_node(root_agent),
}

@app.get("/web/agent-graph")
def agent_graph() -> dict:
# Single introspection endpoint on the main agent: returns this runtime's
# root agent + recursive sub-agent tree, with no /list-apps discovery
# needed. Used by the VeADK "管理 Agent" view.
if _agent_loader is None:
raise _HTTPException(status_code=500, detail="agent loader unavailable")
names = sorted(
p.name
for p in Path(AGENTS_DIR).iterdir()
if p.is_dir() and (p / "agent.py").is_file()
)
if not names:
raise _HTTPException(status_code=404, detail="no agent found")
a = _agent_loader.load_agent(names[0])
return {
"name": getattr(a, "name", names[0]),
"description": getattr(a, "description", "") or "",
"type": _agent_type(a),
"model": _model_name(getattr(a, "model", "")),
"tools": [_tool_label(t) for t in getattr(a, "tools", []) or []],
"graph": _agent_node(a),
"name": getattr(root_agent, "name", "") or "",
"description": getattr(root_agent, "description", "") or "",
"type": _agent_type(root_agent),
"model": _model_name(getattr(root_agent, "model", "")),
"tools": [_tool_label(t) for t in getattr(root_agent, "tools", []) or []],
"graph": _agent_node(root_agent),
}

# Mount web UI if available
# Serve the bundled VeADK web UI without taking over "/", which is reserved
# by AgentServerApp for the A2A protocol surface.
if (WEBUI_DIR / "index.html").is_file():
app.mount("/", StaticFiles(directory=str(WEBUI_DIR), html=True), name="webui")
if (WEBUI_DIR / "assets").is_dir():
app.mount(
"/assets",
StaticFiles(directory=str(WEBUI_DIR / "assets")),
name="webui-assets",
)

from fastapi.responses import FileResponse as _FileResponse

@app.get("/")
@app.get("/webui")
@app.get("/webui/{path:path}")
def webui(path: str = ""):
return _FileResponse(WEBUI_DIR / "index.html")

# AgentServerApp mounts A2A at "/", so routes added after construction must
# be moved before that root mount or they will be shadowed.
_priority_paths = {
"/",
"/ping",
"/web/agent-info/{app_name}",
"/web/agent-graph",
"/assets",
"/webui",
"/webui/{path:path}",
}
_priority_routes = [
r for r in app.router.routes if getattr(r, "path", None) in _priority_paths
]
if _priority_routes:
app.router.routes[:] = _priority_routes + [
r for r in app.router.routes if r not in _priority_routes
]

return app
return agent_server_app, app

app = build_app()
agent_server_app, app = build_app()

if __name__ == "__main__":
uvicorn.run(app, host=HOST, port=PORT)
agent_server_app.run(host=HOST, port=PORT)
`;

const files: ProjectFile[] = [
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion veadk/webui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>VeADK Web</title>
<script type="module" crossorigin src="/assets/index-BVoI2cXd.js"></script>
<script type="module" crossorigin src="/assets/index-DpU-SRTV.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-_In09EL4.css">
</head>
<body>
Expand Down
Loading