Spaces:
Running
Running
| """ | |
| 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) -> Any: | |
| """ | |
| Execute a tool by name with arguments | |
| """ | |
| 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")) | |
| 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) -> List[Dict]: | |
| """Call API to get purchased events for user""" | |
| 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}") | |
| response = await self.client.get(url) | |
| 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() | |