File size: 3,773 Bytes
b336134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
WebSocket endpoint β€” receives natural-language commands, returns results.

Protocol:
  Client β†’ Server:  {"command": "salary ko 10% badhao"}
  Server β†’ Client:  {"status": "success|error|unresolved", "message": "...", "diff": {...}, "suggestions": [...]}
"""
from __future__ import annotations

import asyncio
from fastapi import WebSocket, WebSocketDisconnect

from core.parser.fallback import parse_intent_hybrid
from core.validator import validate_intent
from core.execution.mvcc_executor import execute_mvcc_command
from core.column_registry import column_registry
from services import session_manager, audit_service


async def websocket_handler(ws: WebSocket, session_id: str) -> None:
    await ws.accept()

    # Verify session exists
    meta = session_manager.get(session_id)
    if meta is None:
        await ws.send_json({
            "status": "error",
            "message": "Session nahi mila. Pehle file upload karo.",
        })
        await ws.close()
        return

    try:
        while True:
            # ── Receive command ─────────────────────────────────────
            data = await ws.receive_json()
            command: str = data.get("command", "").strip()
            if not command:
                await ws.send_json({
                    "status": "error",
                    "message": "Empty command",
                })
                continue

            # ── Parse intent ────────────────────────────────────────
            intent = parse_intent_hybrid(session_id, command)

            if intent is None:
                # Unresolved β€” suggest available columns
                cols = column_registry.get_columns(session_id)
                await ws.send_json({
                    "status": "unresolved",
                    "message": "Command samajh nahi aaya. Column ka naam check karo.",
                    "suggestions": cols,
                })
                continue

            # ── Validate ────────────────────────────────────────────
            error = validate_intent(session_id, intent)
            if error:
                await ws.send_json({
                    "status": "error",
                    "message": error,
                })
                continue

            # ── Execute (offload to thread for large files) ─────────
            try:
                result = await asyncio.to_thread(execute_mvcc_command, session_id, intent)
            except Exception as exc:
                await ws.send_json({
                    "status": "error",
                    "message": f"Execution error: {exc}",
                })
                continue

            # ── Audit log ───────────────────────────────────────────
            await audit_service.log_command(
                session_id=session_id,
                command=command,
                intent=intent,
                diff=result.get("diff"),
            )

            # Touch session
            session_manager.touch(session_id)

            # ── Send result ─────────────────────────────────────────
            await ws.send_json({
                "status": "success",
                "message": result["message"],
                "diff": result.get("diff", {}),
            })

    except WebSocketDisconnect:
        pass  # Client disconnected, clean exit