Spaces:
Sleeping
Sleeping
File size: 6,707 Bytes
51769a3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | """
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,
)
|