H-script-AI / api /agent.py
RoxieRoller's picture
Upload 27 files
51769a3 verified
Raw History Blame Contribute Delete
6.71 kB
"""
POST /ai/agent
Agentic multi-turn AI with tool calling.
Agent can: run code, explain, fix, read editor content.
Fixes are SUGGESTED only β€” user manually accepts.
"""
import os, httpx
from fastapi import APIRouter
from pydantic import BaseModel
from core.gemini_client import ask_gemini, HSCRIPT_SYSTEM_PROMPT
router = APIRouter()
BACKEND_URL = os.getenv("BACKEND_URL", "http://localhost:5000")
# ── Tool definitions for Gemini ────────────────────────────
TOOLS = [
{
"name": "run_code",
"description": "Execute H-Script code and return the output and any errors",
"parameters": {
"type": "object",
"properties": {
"code": {"type": "string", "description": "The H-Script code to run"}
},
"required": ["code"]
}
},
{
"name": "explain_code",
"description": "Explain what a piece of H-Script code does",
"parameters": {
"type": "object",
"properties": {
"code": {"type": "string", "description": "The H-Script code to explain"}
},
"required": ["code"]
}
},
{
"name": "suggest_fix",
"description": "Suggest a fix for broken H-Script code given an error message",
"parameters": {
"type": "object",
"properties": {
"code": {"type": "string", "description": "The broken H-Script code"},
"error": {"type": "string", "description": "The error message"}
},
"required": ["code", "error"]
}
},
]
# ── Tool execution ─────────────────────────────────────────
async def execute_tool(name: str, args: dict) -> dict:
if name == "run_code":
try:
async with httpx.AsyncClient(timeout=10) as client:
r = await client.post(f"{BACKEND_URL}/api/run", json={"code": args["code"]})
return r.json()
except Exception as e:
return {"output": [], "errors": [str(e)]}
elif name == "explain_code":
explanation = await ask_gemini(
f"Explain this H-Script code in simple Hinglish:\n```\n{args['code']}\n```"
)
return {"explanation": explanation}
elif name == "suggest_fix":
fix = await ask_gemini(
f"Fix this H-Script code.\nCode:\n```\n{args['code']}\n```\nError: {args['error']}\n"
"Return ONLY the fixed code in a code block, then EXPLANATION: [what you fixed]"
)
return {"suggestion": fix}
return {"error": f"Unknown tool: {name}"}
# ── Request / Response models ──────────────────────────────
class Message(BaseModel):
role: str # 'user' | 'assistant'
content: str
class AgentRequest(BaseModel):
message: str
code: str | None = None # current editor content
history: list[Message] = []
class AgentResponse(BaseModel):
reply: str
action: str | None = None # 'fix' | 'explain' | 'run' | None
result: dict | None = None # tool result if any
history: list[Message] # updated conversation history
# ── Agent endpoint ─────────────────────────────────────────
@router.post("/agent", response_model=AgentResponse)
async def agent_chat(body: AgentRequest):
# Build conversation context
history_text = ""
for msg in body.history[-10:]: # last 10 messages for context
role = "User" if msg.role == "user" else "Assistant"
history_text += f"{role}: {msg.content}\n"
code_context = ""
if body.code:
code_context = f"\nCurrent editor code:\n```\n{body.code}\n```\n"
# Available tools description
tools_desc = """
You have access to these tools (call them when needed):
- run_code(code) β†’ execute H-Script and see output
- explain_code(code) β†’ explain code
- suggest_fix(code, error) β†’ suggest a fix (user will manually accept)
To call a tool, write: TOOL_CALL: tool_name(arg1="value1", arg2="value2")
"""
prompt = f"""{HSCRIPT_SYSTEM_PROMPT}
{tools_desc}
{code_context}
Conversation so far:
{history_text}
User: {body.message}
Think step by step. If you need to run code or use a tool, do it.
Then give your final response to the user.
"""
raw_reply = ""
tool_used = None
tool_result = None
action = None
try:
# First pass β€” ask Gemini
raw_reply = await ask_gemini(prompt)
# Check if Gemini wants to use a tool
if "TOOL_CALL:" in raw_reply:
import re
match = re.search(r'TOOL_CALL:\s*(\w+)\(([^)]*)\)', raw_reply)
if match:
tool_name = match.group(1)
args_str = match.group(2)
# Parse simple key="value" args
args = {}
for m in re.finditer(r'(\w+)\s*=\s*"([^"]*)"', args_str):
args[m.group(1)] = m.group(2)
# Also handle code blocks in args (multiline)
if not args and body.code:
if tool_name in ("run_code", "explain_code"):
args = {"code": body.code}
elif tool_name == "suggest_fix":
args = {"code": body.code, "error": ""}
tool_result = await execute_tool(tool_name, args)
tool_used = tool_name
# Map tool name to action
action = {"run_code": "run", "explain_code": "explain", "suggest_fix": "fix"}.get(tool_name)
# Second pass β€” Gemini reflects on tool result
reflect_prompt = f"""{HSCRIPT_SYSTEM_PROMPT}
User asked: {body.message}
{code_context}
You used tool '{tool_name}' and got this result:
{tool_result}
Now give a helpful, friendly final response to the user based on this result.
If it's a fix suggestion, present it clearly and say "You can accept or reject this fix."
"""
raw_reply = await ask_gemini(reflect_prompt)
except Exception as e:
raw_reply = f"Oops bhai, kuch toh gadbad ho gayi πŸ˜… ({str(e)})"
# Update history
updated_history = list(body.history) + [
Message(role="user", content=body.message),
Message(role="assistant", content=raw_reply),
]
return AgentResponse(
reply=raw_reply,
action=action,
result=tool_result,
history=updated_history,
)