minhvtt commited on
Commit
b2a9847
·
verified ·
1 Parent(s): cd1985a

Update tools_service.py

Browse files
Files changed (1) hide show
  1. tools_service.py +203 -182
tools_service.py CHANGED
@@ -1,182 +1,203 @@
1
- """
2
- Tools Service for LLM Function Calling
3
- HuggingFace-compatible với prompt engineering
4
- """
5
- import httpx
6
- from typing import List, Dict, Any, Optional
7
- import json
8
- import asyncio
9
-
10
-
11
- class ToolsService:
12
- """
13
- Manages external API tools that LLM can call via prompt engineering
14
- """
15
-
16
- def __init__(self, base_url: str = "https://www.festavenue.site"):
17
- self.base_url = base_url
18
- self.client = httpx.AsyncClient(timeout=10.0)
19
-
20
- def get_tools_prompt(self) -> str:
21
- """
22
- Return prompt instruction for HuggingFace LLM về available tools
23
- """
24
- return """
25
- AVAILABLE TOOLS:
26
- Bạn có thể sử dụng các công cụ sau để lấy thông tin chi tiết:
27
-
28
- 1. get_event_details(event_code: str)
29
- - Mô tả: Lấy thông tin đầy đủ về một sự kiện từ hệ thống
30
- - Khi nào dùng: Khi user hỏi về ngày giờ chính xác, địa điểm cụ thể, thông tin liên hệ, hoặc chi tiết khác về một sự kiện
31
- - Tham số: event_code (mã sự kiện, ví dụ: "Y-CONCERT", "EVT001")
32
- - Ví dụ: User hỏi "Ngày tổ chức Y-CONCERT là khi nào?" → Dùng get_event_details("Y-CONCERT")
33
-
34
- CÚ PHÁP GỌI TOOL:
35
- Khi bạn cần gọi tool, hãy trả lời CHÍNH XÁC theo format JSON này:
36
- ```json
37
- {
38
- "tool_call": true,
39
- "function_name": "get_event_details",
40
- "arguments": {
41
- "event_code": "Y-CONCERT"
42
- },
43
- "reason": "Cần lấy thông tin chính xác về ngày giờ tổ chức"
44
- }
45
- ```
46
-
47
- QUAN TRỌNG:
48
- - CHỈ trả JSON khi BẮT BUỘC cần gọi tool
49
- - Nếu có thể trả lời từ context sẵn có, đừng gọi tool
50
- - Sau khi nhận kết quả từ tool, hãy trả lời user bằng ngôn ngữ tự nhiên
51
- """
52
-
53
- async def parse_and_execute(self, llm_response: str) -> Optional[Dict[str, Any]]:
54
- """
55
- Parse LLM response và execute tool nếu có
56
-
57
- Returns:
58
- None nếu không có tool call
59
- Dict với tool result nếu có tool call
60
- """
61
- # Try to extract JSON from response
62
- try:
63
- # Tìm JSON block trong response
64
- if "```json" in llm_response:
65
- json_start = llm_response.find("```json") + 7
66
- json_end = llm_response.find("```", json_start)
67
- json_str = llm_response[json_start:json_end].strip()
68
- elif "{" in llm_response and "}" in llm_response:
69
- # Fallback: tìm JSON object đầu tiên
70
- json_start = llm_response.find("{")
71
- json_end = llm_response.rfind("}") + 1
72
- json_str = llm_response[json_start:json_end]
73
- else:
74
- return None
75
-
76
- tool_call = json.loads(json_str)
77
-
78
- # Validate tool call structure
79
- if not tool_call.get("tool_call"):
80
- return None
81
-
82
- function_name = tool_call.get("function_name")
83
- arguments = tool_call.get("arguments", {})
84
-
85
- # Execute tool
86
- if function_name == "get_event_details":
87
- result = await self._get_event_details(arguments.get("event_code"))
88
- return {
89
- "function": function_name,
90
- "arguments": arguments,
91
- "result": result
92
- }
93
- else:
94
- return {
95
- "function": function_name,
96
- "arguments": arguments,
97
- "result": {"success": False, "error": f"Unknown function: {function_name}"}
98
- }
99
-
100
- except (json.JSONDecodeError, KeyError, ValueError) as e:
101
- # Không phải tool call, response bình thường
102
- return None
103
-
104
- async def _get_event_details(self, event_code: str) -> Dict[str, Any]:
105
- """
106
- Call getEventByEventCode API
107
- """
108
- try:
109
- response = await self.client.get(
110
- f"https://www.festavenue.site/event/get-event-by-event-code",
111
- params={"eventCode": event_code}
112
- )
113
- response.raise_for_status()
114
- data = response.json()
115
-
116
- # Extract relevant fields
117
- event = data.get("data", {})
118
-
119
- if not event:
120
- return {
121
- "success": False,
122
- "error": "Event not found",
123
- "message": f"Không tìm thấy sự kiện với mã {event_code}"
124
- }
125
-
126
- # Extract location với nested address structure
127
- location_data = event.get("location", {})
128
- location = {
129
- "address": {
130
- "street": location_data.get("address", {}).get("street", ""),
131
- "city": location_data.get("address", {}).get("city", ""),
132
- "state": location_data.get("address", {}).get("state", ""),
133
- "postalCode": location_data.get("address", {}).get("postalCode", ""),
134
- "country": location_data.get("address", {}).get("country", "")
135
- },
136
- "coordinates": {
137
- "latitude": location_data.get("coordinates", {}).get("latitude"),
138
- "longitude": location_data.get("coordinates", {}).get("longitude")
139
- }
140
- }
141
-
142
- # Build event URL
143
- event_code = event.get("eventCode")
144
- event_url = f"https://www.festavenue.site/user/event/{event_code}" if event_code else None
145
-
146
- return {
147
- "success": True,
148
- "event_code": event_code,
149
- "event_name": event.get("eventName"),
150
- "event_url": event_url, # NEW: Direct link to event page
151
- "description": event.get("description"),
152
- "short_description": event.get("shortDescription"),
153
- "start_time": event.get("startTimeEventTime"),
154
- "end_time": event.get("endTimeEventTime"),
155
- "start_sale": event.get("startTicketSaleTime"),
156
- "end_sale": event.get("endTicketSaleTime"),
157
- "location": location, # Full nested structure
158
- "contact": {
159
- "email": event.get("publicContactEmail"),
160
- "phone": event.get("publicContactPhone"),
161
- "website": event.get("website")
162
- },
163
- "capacity": event.get("capacity"),
164
- "hashtags": event.get("hashtags", [])
165
- }
166
-
167
- except httpx.HTTPStatusError as e:
168
- return {
169
- "success": False,
170
- "error": f"HTTP {e.response.status_code}",
171
- "message": f"API trả về lỗi khi truy vấn sự kiện {event_code}"
172
- }
173
- except Exception as e:
174
- return {
175
- "success": False,
176
- "error": str(e),
177
- "message": "Không thể kết nối đến API để lấy thông tin sự kiện"
178
- }
179
-
180
- async def close(self):
181
- """Close HTTP client"""
182
- await self.client.aclose()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Tools Service for LLM Function Calling
3
+ HuggingFace-compatible với prompt engineering
4
+ """
5
+ import httpx
6
+ from typing import List, Dict, Any, Optional
7
+ import json
8
+ import asyncio
9
+
10
+
11
+ class ToolsService:
12
+ """
13
+ Manages external API tools that LLM can call via prompt engineering
14
+ """
15
+
16
+ def __init__(self, base_url: str = "https://www.festavenue.site"):
17
+ self.base_url = base_url
18
+ self.client = httpx.AsyncClient(timeout=10.0)
19
+
20
+ def get_tools_prompt(self) -> str:
21
+ """
22
+ Return prompt instruction for HuggingFace LLM về available tools
23
+ """
24
+ return """
25
+ AVAILABLE TOOLS:
26
+ Bạn có thể sử dụng các công cụ sau để lấy thông tin chi tiết:
27
+
28
+ 1. get_event_details(event_code: str)
29
+ - Mô tả: Lấy thông tin đầy đủ về một sự kiện từ hệ thống
30
+ - Khi nào dùng: Khi user hỏi về ngày giờ chính xác, địa điểm cụ thể, thông tin liên hệ, hoặc chi tiết khác về một sự kiện
31
+ - Tham số: event_code (mã sự kiện, ví dụ: "Y-CONCERT", "EVT001")
32
+ - Ví dụ: User hỏi "Ngày tổ chức Y-CONCERT là khi nào?" → Dùng get_event_details("Y-CONCERT")
33
+
34
+ CÚ PHÁP GỌI TOOL:
35
+ Khi bạn cần gọi tool, hãy trả lời CHÍNH XÁC theo format JSON này:
36
+ ```json
37
+ {
38
+ "tool_call": true,
39
+ "function_name": "get_event_details",
40
+ "arguments": {
41
+ "event_code": "Y-CONCERT"
42
+ },
43
+ "reason": "Cần lấy thông tin chính xác về ngày giờ tổ chức"
44
+ }
45
+ ```
46
+
47
+ QUAN TRỌNG:
48
+ - CHỈ trả JSON khi BẮT BUỘC cần gọi tool
49
+ - Nếu có thể trả lời từ context sẵn có, đừng gọi tool
50
+ - Sau khi nhận kết quả từ tool, hãy trả lời user bằng ngôn ngữ tự nhiên
51
+ """
52
+
53
+ async def parse_and_execute(self, llm_response: str) -> Optional[Dict[str, Any]]:
54
+ """
55
+ Parse LLM response và execute tool nếu có
56
+
57
+ Returns:
58
+ None nếu không có tool call
59
+ Dict với tool result nếu có tool call
60
+ """
61
+ # Try to extract JSON from response
62
+ try:
63
+ # Tìm JSON block trong response
64
+ if "```json" in llm_response:
65
+ json_start = llm_response.find("```json") + 7
66
+ json_end = llm_response.find("```", json_start)
67
+ json_str = llm_response[json_start:json_end].strip()
68
+ elif "{" in llm_response and "}" in llm_response:
69
+ # Fallback: tìm JSON object đầu tiên
70
+ json_start = llm_response.find("{")
71
+ json_end = llm_response.rfind("}") + 1
72
+ json_str = llm_response[json_start:json_end]
73
+ else:
74
+ return None
75
+
76
+ tool_call = json.loads(json_str)
77
+
78
+ # Handle nested structure from HF API
79
+ # {"name": "tool_call", "arguments": {"tool_call": true, ...}}
80
+ if "name" in tool_call and "arguments" in tool_call:
81
+ tool_call = tool_call["arguments"] # Unwrap
82
+
83
+ # Validate tool call structure
84
+ if not tool_call.get("tool_call"):
85
+ return None
86
+
87
+ function_name = tool_call.get("function_name")
88
+ arguments = tool_call.get("arguments", {})
89
+
90
+ # Execute tool
91
+ if function_name == "get_event_details":
92
+ result = await self._get_event_details(arguments.get("event_code"))
93
+ return {
94
+ "function": function_name,
95
+ "arguments": arguments,
96
+ "result": result
97
+ }
98
+ else:
99
+ return {
100
+ "function": function_name,
101
+ "arguments": arguments,
102
+ "result": {"success": False, "error": f"Unknown function: {function_name}"}
103
+ }
104
+
105
+ except (json.JSONDecodeError, KeyError, ValueError) as e:
106
+ # Không phải tool call, response bình thường
107
+ return None
108
+
109
+ async def _get_event_details(self, event_code: str) -> Dict[str, Any]:
110
+ """
111
+ Call getEventByEventCode API
112
+ """
113
+ print(f"\n=== CALLING API get_event_details ===")
114
+ print(f"Event Code: {event_code}")
115
+
116
+ try:
117
+ url = f"https://www.festavenue.site/event/get-event-by-event-code"
118
+ params = {"eventCode": event_code}
119
+
120
+ print(f"URL: {url}")
121
+ print(f"Params: {params}")
122
+
123
+ response = await self.client.get(url, params=params)
124
+
125
+ print(f"Status Code: {response.status_code}")
126
+
127
+ response.raise_for_status()
128
+ data = response.json()
129
+
130
+ print(f"Response Data Keys: {list(data.keys()) if data else 'None'}")
131
+ print(f"Has 'data' field: {'data' in data}")
132
+
133
+ # Extract relevant fields
134
+ event = data.get("data", {})
135
+
136
+ if not event:
137
+ return {
138
+ "success": False,
139
+ "error": "Event not found",
140
+ "message": f"Không tìm thấy sự kiện với mã {event_code}"
141
+ }
142
+
143
+ # Extract location với nested address structure
144
+ location_data = event.get("location", {})
145
+ location = {
146
+ "address": {
147
+ "street": location_data.get("address", {}).get("street", ""),
148
+ "city": location_data.get("address", {}).get("city", ""),
149
+ "state": location_data.get("address", {}).get("state", ""),
150
+ "postalCode": location_data.get("address", {}).get("postalCode", ""),
151
+ "country": location_data.get("address", {}).get("country", "")
152
+ },
153
+ "coordinates": {
154
+ "latitude": location_data.get("coordinates", {}).get("latitude"),
155
+ "longitude": location_data.get("coordinates", {}).get("longitude")
156
+ }
157
+ }
158
+
159
+ # Build event URL
160
+ event_code = event.get("eventCode")
161
+ event_url = f"https://www.festavenue.site/user/event/{event_code}" if event_code else None
162
+
163
+ return {
164
+ "success": True,
165
+ "event_code": event_code,
166
+ "event_name": event.get("eventName"),
167
+ "event_url": event_url, # NEW: Direct link to event page
168
+ "description": event.get("description"),
169
+ "short_description": event.get("shortDescription"),
170
+ "start_time": event.get("startTimeEventTime"),
171
+ "end_time": event.get("endTimeEventTime"),
172
+ "start_sale": event.get("startTicketSaleTime"),
173
+ "end_sale": event.get("endTicketSaleTime"),
174
+ "location": location, # Full nested structure
175
+ "contact": {
176
+ "email": event.get("publicContactEmail"),
177
+ "phone": event.get("publicContactPhone"),
178
+ "website": event.get("website")
179
+ },
180
+ "capacity": event.get("capacity"),
181
+ "hashtags": event.get("hashtags", [])
182
+ }
183
+
184
+ print(f"Successfully extracted event data for: {event.get('eventName')}")
185
+ print(f"=== API CALL COMPLETE ===")
186
+ return result
187
+
188
+ except httpx.HTTPStatusError as e:
189
+ return {
190
+ "success": False,
191
+ "error": f"HTTP {e.response.status_code}",
192
+ "message": f"API trả về lỗi khi truy vấn sự kiện {event_code}"
193
+ }
194
+ except Exception as e:
195
+ return {
196
+ "success": False,
197
+ "error": str(e),
198
+ "message": "Không thể kết nối đến API để lấy thông tin sự kiện"
199
+ }
200
+
201
+ async def close(self):
202
+ """Close HTTP client"""
203
+ await self.client.aclose()