diff --git a/examples/code_review_agent/README.md b/examples/code_review_agent/README.md new file mode 100644 index 00000000..fc8086ab --- /dev/null +++ b/examples/code_review_agent/README.md @@ -0,0 +1,30 @@ +# Code Review Agent + +An automated AI code reviewer built with tRPC-Agent SDK, backed by Hy3 LLM. + +## Features + +- **Structured review**: Bugs, style, security, improvement suggestions +- **SQLite persistence**: All reviews saved to `code_reviews.db` +- **Skills-ready**: Can be extended with CubeSandbox skills for sandboxed execution + +## Quick Start + +```bash +pip install -e '.[cube]' # from trpc-agent-python root + +export TRPC_AGENT_API_KEY=your-hy3-key +export TRPC_AGENT_BASE_URL=http://127.0.0.1:8000/v1 +export TRPC_AGENT_MODEL_NAME=tencent/Hy3 + +python run_agent.py path/to/your/code.py +``` + +## Architecture + +``` +User → Code Review Agent (Hy3 LLM) + ├── review_code() → Analyze code + ├── save_review() → SQLite persistence + └── skills (optional) → CubeSandbox sandbox +``` diff --git a/examples/code_review_agent/agent/__init__.py b/examples/code_review_agent/agent/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/code_review_agent/agent/agent.py b/examples/code_review_agent/agent/agent.py new file mode 100644 index 00000000..e24c54c0 --- /dev/null +++ b/examples/code_review_agent/agent/agent.py @@ -0,0 +1,25 @@ +from trpc_agent_sdk.agents import LlmAgent +from trpc_agent_sdk.models import LLMModel, OpenAIModel +from trpc_agent_sdk.tools import FunctionTool + +from .config import get_model_config +from .prompts import INSTRUCTION +from .tools import review_code, save_review + + +def _create_model() -> LLMModel: + api_key, url, model_name = get_model_config() + return OpenAIModel(model_name=model_name, api_key=api_key, base_url=url) + + +def create_agent() -> LlmAgent: + return LlmAgent( + name="code_reviewer", + description="An AI-powered code review agent backed by Hy3.", + model=_create_model(), + instruction=INSTRUCTION, + tools=[ + FunctionTool(review_code), + FunctionTool(save_review), + ], + ) diff --git a/examples/code_review_agent/agent/config.py b/examples/code_review_agent/agent/config.py new file mode 100644 index 00000000..90723125 --- /dev/null +++ b/examples/code_review_agent/agent/config.py @@ -0,0 +1,8 @@ +import os + + +def get_model_config(): + api_key = os.environ.get("TRPC_AGENT_API_KEY", "EMPTY") + url = os.environ.get("TRPC_AGENT_BASE_URL", "http://127.0.0.1:8000/v1") + model_name = os.environ.get("TRPC_AGENT_MODEL_NAME", "hy3") + return api_key, url, model_name diff --git a/examples/code_review_agent/agent/prompts.py b/examples/code_review_agent/agent/prompts.py new file mode 100644 index 00000000..603b4583 --- /dev/null +++ b/examples/code_review_agent/agent/prompts.py @@ -0,0 +1,7 @@ +INSTRUCTION = """You are an expert code reviewer. When a user provides code: + +1. Use `review_code` to analyze the code and produce a structured review. +2. Use `save_review` to persist the review results to the database. +3. Summarize the most important findings for the user. + +Always reference specific line numbers in your findings. Be constructive, not critical.""" diff --git a/examples/code_review_agent/agent/tools.py b/examples/code_review_agent/agent/tools.py new file mode 100644 index 00000000..96ef0926 --- /dev/null +++ b/examples/code_review_agent/agent/tools.py @@ -0,0 +1,59 @@ +"""Code review tools: review code, save results to database.""" +import sqlite3 +import json +from datetime import datetime + +DB_PATH = "code_reviews.db" + + +def _get_db(): + conn = sqlite3.connect(DB_PATH) + conn.execute(""" + CREATE TABLE IF NOT EXISTS reviews ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + file_path TEXT, + summary TEXT, + bugs TEXT, + improvements TEXT, + score INTEGER, + created_at TEXT + ) + """) + return conn + + +def review_code(code: str, file_path: str = "") -> dict: + """Analyze code and return structured review. + + This is a tool the agent calls — the actual LLM analysis happens + through the agent's model. The returned dict is the structured + output template. + """ + return { + "code_snippet": code[:500], + "file_path": file_path, + "needs_review": True, + } + + +def save_review(file_path: str, summary: str, bugs: str, + improvements: str, score: int = 0) -> str: + """Save a code review to the SQLite database. + + Args: + file_path: Path to the reviewed file + summary: One-sentence summary + bugs: Bug findings + improvements: Suggested improvements + score: Quality score (0-10) + """ + conn = _get_db() + conn.execute( + "INSERT INTO reviews (file_path, summary, bugs, improvements, score, created_at) " + "VALUES (?, ?, ?, ?, ?, ?)", + (file_path, summary, bugs, improvements, score, datetime.now().isoformat()) + ) + conn.commit() + count = conn.execute("SELECT COUNT(*) FROM reviews").fetchone()[0] + conn.close() + return f"Review saved. Total reviews in database: {count}" diff --git a/examples/code_review_agent/run_agent.py b/examples/code_review_agent/run_agent.py new file mode 100644 index 00000000..9e5aa913 --- /dev/null +++ b/examples/code_review_agent/run_agent.py @@ -0,0 +1,45 @@ +"""Run the code review agent. + +Usage:: + + export TRPC_AGENT_API_KEY=your-hy3-key + export TRPC_AGENT_BASE_URL=http://127.0.0.1:8000/v1 + python run_agent.py path/to/file.py +""" + +import asyncio +import sys + +from trpc_agent_sdk.runners import Runner +from trpc_agent_sdk.sessions import InMemorySessionService + +from agent.agent import create_agent + + +async def main(): + if len(sys.argv) < 2: + print("Usage: python run_agent.py ") + sys.exit(1) + + filepath = sys.argv[1] + with open(filepath, "r", encoding="utf-8") as f: + code = f.read() + + agent = create_agent() + session_service = InMemorySessionService() + + prompt = ( + f"Please review the following code file ({filepath}).\n" + f"Call review_code first, then save_review with a score from 0-10, " + f"and summarize your findings:\n\n```\n{code[:20000]}\n```" + ) + + runner = Runner(agent=agent, session_service=session_service) + async for event in runner.run(prompt): + if event.content: + print(event.content, end="", flush=True) + print() + + +if __name__ == "__main__": + asyncio.run(main())