Files
rightnow-ai--openfang/agents/langchain-code-reviewer/server.py
T
wehub-resource-sync d93385b373
CI / Check / macos-latest (push) Waiting to run
CI / Check / ubuntu-latest (push) Waiting to run
CI / Check / windows-latest (push) Waiting to run
CI / Test / macos-latest (push) Waiting to run
CI / Test / ubuntu-latest (push) Waiting to run
CI / Test / windows-latest (push) Waiting to run
CI / Clippy (push) Waiting to run
CI / Format (push) Waiting to run
CI / Security Audit (push) Waiting to run
CI / Secrets Scan (push) Waiting to run
CI / Install Script Smoke Test (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 12:43:09 +08:00

227 lines
6.7 KiB
Python

"""
LangChain Code Review Agent — A2A-compatible server.
Exposes a code review agent via Google's A2A protocol so that
OpenFang workflows can call it as an external agent.
Start:
OPENAI_API_KEY=sk-xxx python server.py
# or with Ollama (no key needed):
USE_OLLAMA=1 python server.py
Endpoints:
GET /.well-known/agent.json — A2A Agent Card
POST /a2a — JSON-RPC task endpoint
"""
import os
import uuid
import asyncio
from datetime import datetime, timezone
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
import uvicorn
from agent import CodeReviewAgent
# ---------------------------------------------------------------------------
# Config
# ---------------------------------------------------------------------------
HOST = os.getenv("HOST", "0.0.0.0")
PORT = int(os.getenv("PORT", "9100"))
BASE_URL = os.getenv("BASE_URL", f"http://127.0.0.1:{PORT}")
app = FastAPI(title="LangChain Code Review Agent")
agent = CodeReviewAgent()
# In-memory task store
tasks: dict[str, dict] = {}
# ---------------------------------------------------------------------------
# A2A Agent Card
# ---------------------------------------------------------------------------
AGENT_CARD = {
"name": "langchain-code-reviewer",
"description": (
"LangChain-powered code review agent. "
"Analyzes code for bugs, security issues, performance problems, "
"and style violations. Returns structured review with severity levels."
),
"url": f"{BASE_URL}/a2a",
"version": "0.1.0",
"capabilities": {
"streaming": False,
"pushNotifications": False,
"stateTransitionHistory": True,
},
"skills": [
{
"id": "code-review",
"name": "Code Review",
"description": "Review code for correctness, security, performance, and style",
"tags": ["code", "review", "security", "quality"],
"examples": [
"Review this Python function for bugs",
"Check this Rust code for security issues",
"Analyze this PR diff for performance problems",
],
},
{
"id": "pr-review",
"name": "Pull Request Review",
"description": "Review a git diff / pull request",
"tags": ["pr", "diff", "git"],
"examples": [
"Review this PR diff",
"Analyze these changes",
],
},
],
"defaultInputModes": ["text"],
"defaultOutputModes": ["text"],
}
@app.get("/.well-known/agent.json")
async def agent_card():
return JSONResponse(content=AGENT_CARD)
# ---------------------------------------------------------------------------
# A2A JSON-RPC Endpoint
# ---------------------------------------------------------------------------
@app.post("/a2a")
async def a2a_endpoint(request: Request):
body = await request.json()
jsonrpc = body.get("jsonrpc", "2.0")
req_id = body.get("id", 1)
method = body.get("method", "")
params = body.get("params", {})
if method == "tasks/send":
return await handle_tasks_send(jsonrpc, req_id, params)
elif method == "tasks/get":
return handle_tasks_get(jsonrpc, req_id, params)
elif method == "tasks/cancel":
return handle_tasks_cancel(jsonrpc, req_id, params)
else:
return JSONResponse(content={
"jsonrpc": jsonrpc,
"id": req_id,
"error": {"code": -32601, "message": f"Method not found: {method}"},
})
async def handle_tasks_send(jsonrpc: str, req_id: int, params: dict):
message = params.get("message", {})
session_id = params.get("sessionId")
task_id = str(uuid.uuid4())
text_parts = [
p["text"] for p in message.get("parts", []) if p.get("type") == "text"
]
user_input = "\n".join(text_parts)
task = {
"id": task_id,
"sessionId": session_id,
"status": {"state": "working", "message": None},
"messages": [message],
"artifacts": [],
}
tasks[task_id] = task
try:
review_result = await asyncio.to_thread(agent.review, user_input)
agent_message = {
"role": "agent",
"parts": [{"type": "text", "text": review_result}],
}
task["messages"].append(agent_message)
task["status"] = {"state": "completed", "message": None}
task["artifacts"] = [
{
"name": "code-review-report",
"description": "Structured code review report",
"parts": [{"type": "text", "text": review_result}],
"index": 0,
"lastChunk": True,
}
]
except Exception as e:
task["status"] = {"state": "failed", "message": str(e)}
task["messages"].append({
"role": "agent",
"parts": [{"type": "text", "text": f"Review failed: {e}"}],
})
return JSONResponse(content={
"jsonrpc": jsonrpc,
"id": req_id,
"result": task,
})
def handle_tasks_get(jsonrpc: str, req_id: int, params: dict):
task_id = params.get("id", "")
task = tasks.get(task_id)
if task is None:
return JSONResponse(content={
"jsonrpc": jsonrpc,
"id": req_id,
"error": {"code": -32000, "message": f"Task not found: {task_id}"},
})
return JSONResponse(content={
"jsonrpc": jsonrpc,
"id": req_id,
"result": task,
})
def handle_tasks_cancel(jsonrpc: str, req_id: int, params: dict):
task_id = params.get("id", "")
task = tasks.get(task_id)
if task is None:
return JSONResponse(content={
"jsonrpc": jsonrpc,
"id": req_id,
"error": {"code": -32000, "message": f"Task not found: {task_id}"},
})
task["status"] = {"state": "cancelled", "message": None}
return JSONResponse(content={
"jsonrpc": jsonrpc,
"id": req_id,
"result": task,
})
# ---------------------------------------------------------------------------
# Health check
# ---------------------------------------------------------------------------
@app.get("/health")
async def health():
return {"status": "ok", "agent": "langchain-code-reviewer", "tasks": len(tasks)}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
if __name__ == "__main__":
print(f"Starting LangChain Code Review Agent on {HOST}:{PORT}")
print(f"Agent Card: {BASE_URL}/.well-known/agent.json")
print(f"A2A endpoint: {BASE_URL}/a2a")
uvicorn.run(app, host=HOST, port=PORT)