# LocalMate API Documentation > **Base URL:** `https://cuong2004-localmate.hf.space/api/v1` > **Swagger UI:** `/docs` | **ReDoc:** `/redoc` > **Version:** 0.3.0 --- ## 📋 Table of Contents 1. [Chat API](#chat-api) 2. [User Profile API](#user-profile-api) 3. [Itineraries API](#itineraries-api) 4. [Trip Planner API](#trip-planner-api) 5. [Utility Endpoints](#utility-endpoints) 6. [Models](#models) --- ## Chat API ### POST `/chat` Main endpoint for interacting with the AI assistant. **Request:** ```json { "message": "Quán cafe view đẹp gần Mỹ Khê", "user_id": "user_123", "session_id": "default", "provider": "MegaLLM", "model": "deepseek-ai/deepseek-v3.1-terminus", "image_url": null, "react_mode": false, "max_steps": 5 } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `message` | string | ✅ | User's question in Vietnamese | | `user_id` | string | ❌ | User ID for session (default: "anonymous") | | `session_id` | string | ❌ | Session ID (default: "default") | | `provider` | string | ❌ | "Google" or "MegaLLM" (default: "MegaLLM") | | `model` | string | ❌ | LLM model name | | `image_url` | string | ❌ | Base64 image for visual search | | `react_mode` | boolean | ❌ | Enable multi-step reasoning (default: false) | | `max_steps` | integer | ❌ | Max reasoning steps 1-10 (default: 5) | **Response:** ```json { "response": "Mình gợi ý 3 quán cafe rất đẹp gần Mỹ Khê...", "status": "success", "provider": "MegaLLM", "model": "deepseek-ai/deepseek-v3.1-terminus", "user_id": "user_123", "session_id": "default", "places": [ { "place_id": "cafe_001", "name": "Cabanon Palace", "category": "restaurant", "lat": 16.06, "lng": 108.24, "rating": 4.8, "address": "123 Võ Nguyên Giáp" }, { "place_id": "cafe_002", "name": "Be Man Restaurant", "category": "restaurant", "lat": 16.07, "lng": 108.25, "rating": 4.5, "address": "456 Phạm Văn Đồng" } ], "tools_used": ["find_nearby_places"], "duration_ms": 5748.23 } ``` > **Note:** `places` array contains LLM-selected places with full details. FE can render these as cards separately from text response. --- ### POST `/chat/clear` Clear conversation history. **Request:** ```json { "user_id": "user_123", "session_id": "default" } ``` **Response:** ```json { "status": "success", "message": "Cleared session 'default' for user_123" } ``` --- ### GET `/chat/history/{user_id}` Get chat session metadata (list of sessions, counts). **Response:** ```json { "user_id": "user_123", "sessions": ["default", "trip_planning"], "current_session": "default", "message_count": 12 } ``` --- ### GET `/chat/messages/{user_id}` Get actual chat messages from a session. **Query Params:** `?session_id=default` **Response:** ```json { "user_id": "user_123", "session_id": "default", "messages": [ { "role": "user", "content": "Tìm quán cafe gần Mỹ Khê", "timestamp": "2025-01-01T10:00:00" }, { "role": "assistant", "content": "Dựa trên yêu cầu của bạn...", "timestamp": "2025-01-01T10:00:05" } ], "count": 2 } ``` --- ### POST `/image-search` Search places by uploaded image. **Request:** `multipart/form-data` - `image`: File (required) - `limit`: integer (default: 10) **Response:** ```json { "results": [ { "place_id": "cafe_123", "name": "Nhớ Một Người", "category": "Cafe", "rating": 4.9, "similarity": 0.85, "image_url": "https://..." } ], "total": 5 } ``` --- ## Upload API Base path: `/upload` ### POST `/upload/image` Upload image to Supabase Storage and get public URL. > Use this to get an image URL for the `/chat` endpoint's `image_url` parameter. **Request:** `multipart/form-data` - `file`: Image file (required) - `user_id`: string (optional, for organizing uploads) **Supported formats:** JPEG, PNG, WebP, GIF **Max size:** 10MB **Response:** ```json { "url": "https://xxx.supabase.co/storage/v1/object/public/image/user123/20250101_120000_abc123.jpg", "path": "user123/20250101_120000_abc123.jpg", "size": 245678, "content_type": "image/jpeg" } ``` **Usage Flow:** ``` 1. POST /upload/image → get URL 2. POST /chat { image_url: URL } → visual search ``` --- ## User Profile API Base path: `/users` ### GET `/users/me` Get current user's profile. **Query:** `?user_id=uuid-here` **Response:** ```json { "profile": { "id": "uuid-here", "full_name": "Nguyen Van A", "phone": "0901234567", "role": "tourist", "locale": "vi_VN", "avatar_url": "https://...", "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-01T00:00:00Z" }, "message": "Profile retrieved" } ``` --- ### PUT `/users/me` Update current user's profile. **Query:** `?user_id=uuid-here` **Request:** ```json { "full_name": "Nguyen Van B", "phone": "0909876543", "locale": "en_US", "avatar_url": "https://..." } ``` --- ### GET `/users/{user_id}` Get user profile by ID (admin only). --- ## Itineraries API Base path: `/itineraries` > Multi-day trip planning with persistent storage (Supabase) ### POST `/itineraries` Create new itinerary. **Query:** `?user_id=uuid-here` **Request:** ```json { "title": "Da Nang 3 Days Trip", "start_date": "2025-02-01", "end_date": "2025-02-03", "total_days": 3, "total_budget": 5000000, "currency": "VND" } ``` **Response:** ```json { "itinerary": { "id": "itinerary-uuid", "user_id": "user-uuid", "title": "Da Nang 3 Days Trip", "start_date": "2025-02-01", "end_date": "2025-02-03", "total_days": 3, "total_budget": 5000000, "currency": "VND", "stops": [], "created_at": "...", "updated_at": "..." }, "message": "Itinerary created" } ``` --- ### GET `/itineraries` List user's itineraries. **Query:** `?user_id=uuid-here` **Response:** ```json [ { "id": "itinerary-uuid", "title": "Da Nang 3 Days Trip", "start_date": "2025-02-01", "end_date": "2025-02-03", "total_days": 3, "stop_count": 8, "created_at": "..." } ] ``` --- ### GET `/itineraries/{itinerary_id}` Get itinerary with all stops. **Query:** `?user_id=uuid-here` --- ### PUT `/itineraries/{itinerary_id}` Update itinerary details. **Request:** ```json { "title": "Updated Title", "total_budget": 6000000 } ``` --- ### DELETE `/itineraries/{itinerary_id}` Delete itinerary and all stops. --- ### POST `/itineraries/{itinerary_id}/stops` Add stop to itinerary. **Request:** ```json { "place_id": "cafe_123", "day_index": 1, "order_index": 1, "arrival_time": "2025-02-01T09:00:00Z", "stay_minutes": 60, "notes": "Morning coffee", "tags": ["cafe", "breakfast"] } ``` --- ### PUT `/itineraries/{itinerary_id}/stops/{stop_id}` Update stop. --- ### DELETE `/itineraries/{itinerary_id}/stops/{stop_id}` Remove stop. --- ## Trip Planner API Base path: `/planner` ### POST `/planner/create` Create a new trip plan. **Query:** `?user_id=user_123` **Request:** ```json { "name": "My Da Nang Trip" } ``` **Response:** ```json { "plan_id": "plan_abc123", "name": "My Da Nang Trip", "message": "Created plan 'My Da Nang Trip'" } ``` --- ### GET `/planner/{plan_id}` Get a plan by ID. **Query:** `?user_id=user_123` **Response:** ```json { "plan": { "plan_id": "plan_abc123", "name": "My Da Nang Trip", "items": [ { "item_id": "item_1", "order": 0, "name": "FIRGUN CORNER COFFEE", "category": "Cafe", "lat": 16.06, "lng": 108.22, "rating": 4.5, "distance_from_prev_km": null } ], "total_distance_km": 0, "estimated_duration_min": 0 }, "message": "Plan retrieved" } ``` --- ### POST `/planner/{plan_id}/add` Add a place to the plan. **Query:** `?user_id=user_123` **Request:** ```json { "place": { "place_id": "cafe_123", "name": "FIRGUN CORNER COFFEE", "category": "Cafe", "lat": 16.06, "lng": 108.22, "rating": 4.5 }, "notes": "Morning coffee" } ``` **Response:** `PlanItem` object --- ### DELETE `/planner/{plan_id}/remove/{item_id}` Remove a place from the plan. **Query:** `?user_id=user_123` **Response:** ```json { "status": "success", "message": "Removed item item_1" } ``` --- ### PUT `/planner/{plan_id}/reorder` Reorder places manually. **Query:** `?user_id=user_123` **Request:** ```json { "new_order": ["item_3", "item_1", "item_2"] } ``` --- ### POST `/planner/{plan_id}/optimize` Optimize route using TSP algorithm. **Query:** `?user_id=user_123&start_index=0` **Response:** ```json { "plan_id": "plan_abc123", "items": [...], "total_distance_km": 12.5, "estimated_duration_min": 45, "distance_saved_km": 3.2, "message": "Route optimized! Total: 12.5km, ~45min" } ``` --- ### DELETE `/planner/{plan_id}` Delete a plan. **Query:** `?user_id=user_123` --- ### POST `/planner/{plan_id}/get-plan` Generate an optimized Smart Plan with social media research and optimal timing. **Query:** `?user_id=user_123` **Request:** ```json { "include_social_research": true, "freshness": "pw" } ``` | Field | Type | Description | |-------|------|-------------| | `include_social_research` | boolean | Enable Brave Search for reviews (default: true) | | `freshness` | string | "pw" = past week, "pm" = past month | **Response:** ```json { "plan": { "itinerary_id": "plan_abc123", "title": "Da Nang Test Trip", "total_days": 1, "days": [ { "day_index": 1, "date": null, "places": [ { "place_id": "my_khe_1", "name": "Bãi biển Mỹ Khê", "category": "beach", "lat": 16.0544, "lng": 108.245, "recommended_time": "05:30", "suggested_duration_min": 90, "tips": [ "Đón bình minh đẹp nhất vào khoảng 5h30 - 6h00 sáng.", "Xem ngư dân kéo lưới và mua hải sản tươi sống." ], "highlights": "Summary from social research...", "social_mentions": ["[TikTok] Review Mỹ Khê..."], "order": 1 }, { "place_id": "cau_rong_1", "name": "Cầu Rồng", "category": "attraction", "recommended_time": "20:30", "suggested_duration_min": 60, "tips": [ "Nếu đi vào Thứ 7 hoặc Chủ Nhật, Rồng sẽ phun lửa và nước vào lúc 21:00.", "Đứng ở phía đầu Rồng để xem rõ nhất." ], "order": 2 } ], "day_summary": "Ngày 1: 2 địa điểm", "day_distance_km": 3.57 } ], "summary": "Lịch trình với 2 địa điểm trong 1 ngày.", "total_distance_km": 3.57, "estimated_total_duration_min": 150, "generated_at": "2025-12-20T06:20:59" }, "research_count": 5, "generation_time_ms": 9486.47, "message": "Smart plan generated with 5 social mentions" } ``` > **Features:** > - **Optimal Timing**: Da Nang local knowledge (Dragon Bridge 21h, Mỹ Khê sunrise) > - **Social Research**: Pulls reviews from TikTok, Facebook, Reddit via Brave API > - **LLM Enhancement**: Uses Gemini 3 Flash for tips and scheduling > - **Distance Calculation**: Haversine formula for route distances --- ### POST `/itineraries/{itinerary_id}/get-plan` Same as above, but for multi-day itineraries (persistent storage). **Query:** `?user_id=uuid-here` **Request/Response:** Same format as planner get-plan --- ## Utility Endpoints ### GET `/health` Health check. Returns `{"status": "ok"}` ### POST `/nearby` Find nearby places (direct Neo4j query). **Request:** ```json { "lat": 16.0626442, "lng": 108.2462143, "max_distance_km": 3.0, "category": "cafe", "limit": 10 } ``` --- ## Models ### Place Object ```typescript interface Place { place_id: string; name: string; category?: string; lat?: number; lng?: number; rating?: number; description?: string; } ``` ### PlanItem ```typescript interface PlanItem { item_id: string; order: number; name: string; category?: string; lat: number; lng: number; rating?: number; notes?: string; distance_from_prev_km?: number; } ``` ### WorkflowStep ```typescript interface WorkflowStep { step: string; tool?: string; purpose: string; results: number; } ``` ### SmartPlan (Get Plan Response) ```typescript interface SmartPlan { itinerary_id: string; title: string; total_days: number; days: DayPlan[]; summary: string; total_distance_km: number; estimated_total_duration_min: number; generated_at: string; } interface DayPlan { day_index: number; date?: string; places: PlaceDetail[]; day_summary: string; day_distance_km: number; } interface PlaceDetail { place_id: string; name: string; category: string; lat: number; lng: number; recommended_time: string; // "05:30", "21:00" suggested_duration_min: number; tips: string[]; // ["Xem cầu rồng phun lửa..."] highlights: string; // Summary from social research social_mentions: string[]; // ["[TikTok] Review..."] order: number; } ``` --- ## Usage Examples ### JavaScript/Fetch ```javascript // Chat const response = await fetch('https://cuong2004-localmate.hf.space/api/v1/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: 'Quán cafe view đẹp gần Mỹ Khê', user_id: 'my_user', react_mode: false }) }); const data = await response.json(); console.log(data.response); ``` ### cURL ```bash curl -X POST "https://cuong2004-localmate.hf.space/api/v1/chat" \ -H "Content-Type: application/json" \ -d '{"message": "Nhà hàng hải sản ngon", "user_id": "test"}' ``` --- ## Error Responses | Status | Description | |--------|-------------| | 400 | Bad Request - Invalid parameters | | 404 | Not Found - Resource doesn't exist | | 422 | Validation Error - Check request body | | 500 | Server Error - Check logs | ```json { "detail": "Plan not found" } ```