File size: 9,908 Bytes
afb1117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4fafc3c
afb1117
 
4fafc3c
afb1117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4fafc3c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
afb1117
 
 
 
 
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
"""

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", feedback_tracking=None):
        self.base_url = base_url
        self.client = httpx.AsyncClient(timeout=10.0)
        self.feedback_tracking = feedback_tracking  # NEW: Feedback tracking
    
    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"\n🔧 ===== TOOL EXECUTION =====")
        print(f"Tool: {tool_name}")
        print(f"Arguments: {arguments}")
        print(f"Access Token: {'✅ Present' if access_token else '❌ Missing'}")
        if access_token:
            print(f"Token preview: {access_token[:30]}...")
        
        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":
                print(f"→ Calling _get_purchased_events with:")
                print(f"   user_id: {arguments.get('user_id')}")
                print(f"   access_token: {'✅' if access_token else '❌'}")
                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)"""
        print(f"\n🎫 ===== GET PURCHASED EVENTS =====")
        print(f"User ID: {user_id}")
        print(f"Access Token: {'✅ Present' if access_token else '❌ Missing'}")
        
        if not user_id:
            print("⚠️ No user_id provided, returning empty list")
            return []
            
        try:
            url = f"{self.base_url}/event/get-purchase-event-by-user-id/{user_id}"
            print(f"🔍 API URL: {url}")
            
            # Add Authorization header if access_token provided
            headers = {}
            if access_token:
                headers["Authorization"] = f"Bearer {access_token}"
                print(f"🔐 Authorization Header Added:")
                print(f"   Bearer {access_token[:30]}...")
            else:
                print(f"⚠️ No access_token - calling API without auth")
            
            print(f"📡 Headers: {headers}")
            print(f"🚀 Calling API...")
            
            response = await self.client.get(url, headers=headers)
            
            print(f"📥 Response Status: {response.status_code}")
            print(f"📦 Response Headers: {dict(response.headers)}")
            
            if response.status_code == 200:
                data = response.json()
                print(f"✅ Success! Data keys: {list(data.keys())}")
                events = data.get("data", [])
                print(f"📊 Found {len(events)} purchased events")
                return events
            else:
                print(f"❌ API Error: {response.status_code}")
                print(f"Response body: {response.text[:500]}")
                return []
                
        except Exception as e:
            print(f"⚠️ Exception in _get_purchased_events: {type(e).__name__}: {e}")
            import traceback
            traceback.print_exc()
            return []

    async def _save_feedback(self, event_id: str, rating: int, comment: str, user_id: str = None, event_code: str = None) -> Dict:
        """Save feedback and mark as completed in tracking system"""
        print(f"\n📝 ===== SAVE FEEDBACK =====")
        print(f"Event ID: {event_id}")
        print(f"Event Code: {event_code}")
        print(f"User ID: {user_id}")
        print(f"Rating: {rating}")
        print(f"Comment: {comment}")
        
        # TODO: Implement real API call to save feedback
        # For now, just mark in tracking system
        if self.feedback_tracking and user_id and event_code:
            success = self.feedback_tracking.mark_feedback_given(
                user_id=user_id,
                event_code=event_code,
                rating=rating,
                comment=comment
            )
            if success:
                print(f"✅ Feedback tracked in database")
            else:
                print(f"⚠️ Failed to track feedback")
        
        return {"success": True, "message": "Feedback recorded"}

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