File size: 7,461 Bytes
6597779
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5f0e47d
6597779
 
5f0e47d
 
 
 
 
6597779
 
 
 
 
 
 
 
5f0e47d
 
 
 
6597779
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5f0e47d
 
6597779
 
 
 
 
 
 
5f0e47d
 
 
 
 
 
 
6597779
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Tools Service for LLM Function Calling
HuggingFace-compatible với prompt engineering
"""
import httpx
from typing import List, Dict, Any, Optional
import json
import asyncio


class ToolsService:
    """
    Manages external API tools that LLM can call via prompt engineering
    """
    
    def __init__(self, base_url: str = "https://hoalacrent.io.vn/api/v0"):
        self.base_url = base_url
        self.client = httpx.AsyncClient(timeout=10.0)
    
    def get_tools_definition(self) -> List[Dict]:
        """
        Return list of tool definitions (OpenAI format style)
        Used for constructing system prompt
        """
        return [
            {
                "name": "search_events",
                "description": "Tìm kiếm sự kiện phù hợp theo từ khóa, vibe, hoặc thời gian.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "query": {"type": "string", "description": "Từ khóa tìm kiếm (VD: 'nhạc rock', 'hài kịch')"},
                        "vibe": {"type": "string", "description": "Vibe/Mood (VD: 'chill', 'sôi động', 'hẹn hò')"},
                        "time": {"type": "string", "description": "Thời gian (VD: 'cuối tuần này', 'tối nay')"}
                    }
                }
            },
            {
                "name": "get_event_details",
                "description": "Lấy thông tin chi tiết (giá, địa điểm, thời gian) của sự kiện.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "event_id": {"type": "string", "description": "ID của sự kiện (MongoDB ID)"}
                    },
                    "required": ["event_id"]
                }
            },
            {
                "name": "get_purchased_events",
                "description": "Kiểm tra lịch sử các sự kiện user đã mua vé hoặc tham gia.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "user_id": {"type": "string", "description": "ID của user"}
                    },
                    "required": ["user_id"]
                }
            },
            {
                "name": "save_feedback",
                "description": "Lưu đánh giá/feedback của user về sự kiện.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "event_id": {"type": "string", "description": "ID sự kiện"},
                        "rating": {"type": "integer", "description": "Số sao đánh giá (1-5)"},
                        "comment": {"type": "string", "description": "Nội dung nhận xét"}
                    },
                    "required": ["event_id", "rating"]
                }
            },
            {
                "name": "save_lead",
                "description": "Lưu thông tin khách hàng quan tâm (Lead).",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "email": {"type": "string"},
                        "phone": {"type": "string"},
                        "interest": {"type": "string"}
                    }
                }
            }
        ]

    async def execute_tool(self, tool_name: str, arguments: Dict, access_token: Optional[str] = None) -> Any:
        """
        Execute a tool by name with arguments
        
        Args:
            tool_name: Name of the tool
            arguments: Tool arguments
            access_token: JWT token for authenticated API calls
        """
        print(f"🔧 Executing Tool: {tool_name} with args: {arguments}")
        
        try:
            if tool_name == "get_event_details":
                return await self._get_event_details(arguments.get("event_id") or arguments.get("event_code"))
            
            elif tool_name == "get_purchased_events":
                return await self._get_purchased_events(
                    arguments.get("user_id"),
                    access_token=access_token  # Pass access_token
                )
            
            elif tool_name == "save_feedback":
                return await self._save_feedback(
                    arguments.get("event_id"),
                    arguments.get("rating"),
                    arguments.get("comment")
                )
            
            elif tool_name == "search_events":
                # Note: This usually requires RAG service, so we return a special signal
                # The Agent Service will handle RAG search
                return {"action": "run_rag_search", "query": arguments}
            
            elif tool_name == "save_lead":
                # Placeholder for lead saving
                return {"success": True, "message": "Lead saved successfully"}
            
            else:
                return {"error": f"Unknown tool: {tool_name}"}
                
        except Exception as e:
            print(f"⚠️ Tool Execution Error: {e}")
            return {"error": str(e)}

    async def _get_event_details(self, event_id: str) -> Dict:
        """Call API to get event details"""
        if not event_id:
            return {"error": "Missing event_id"}
            
        try:
            url = f"{self.base_url}/event/get-event-by-id"
            
            response = await self.client.get(url, params={"id": event_id})
            if response.status_code == 200:
                data = response.json()
                if data.get("success"):
                    return data.get("data")
            return {"error": "Event not found", "details": response.text}
        except Exception as e:
            return {"error": str(e)}

    async def _get_purchased_events(self, user_id: str, access_token: Optional[str] = None) -> List[Dict]:
        """Call API to get purchased events for user (requires auth)"""
        if not user_id:
            return []
            
        try:
            url = f"{self.base_url}/event/get-purchase-event-by-user-id/{user_id}"
            print(f"🔍 Calling API: {url}")
            
            # Add Authorization header if access_token provided
            headers = {}
            if access_token:
                headers["Authorization"] = f"Bearer {access_token}"
                print(f"🔐 Using access_token for authentication")
            
            response = await self.client.get(url, headers=headers)
            if response.status_code == 200:
                data = response.json()
                # API returns {data: [...]}
                return data.get("data", [])
            
            print(f"⚠️ API Error: {response.status_code} - {response.text}")
            return []
        except Exception as e:
            print(f"⚠️ API Exception: {e}")
            return []

    async def _save_feedback(self, event_id: str, rating: int, comment: str) -> Dict:
        """Save feedback (Mock or Real API)"""
        # TODO: Implement real API call when available
        print(f"📝 Saving Feedback: Event={event_id}, Rating={rating}, Comment={comment}")
        return {"success": True, "message": "Feedback recorded"}

    async def close(self):
        """Close HTTP client"""
        await self.client.aclose()