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,
    )