LocalMate API Documentation
Base URL:
https://cuong2004-localmate.hf.space/api/v1
Swagger UI:/docs| ReDoc:/redoc
Version: 0.3.0
📋 Table of Contents
Chat API
POST /chat
Main endpoint for interacting with the AI assistant.
Request:
{
"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:
{
"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:
placesarray contains LLM-selected places with full details. FE can render these as cards separately from text response.
POST /chat/clear
Clear conversation history.
Request:
{
"user_id": "user_123",
"session_id": "default"
}
Response:
{
"status": "success",
"message": "Cleared session 'default' for user_123"
}
GET /chat/history/{user_id}
Get chat session metadata (list of sessions, counts).
Response:
{
"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:
{
"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:
{
"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
/chatendpoint'simage_urlparameter.
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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
[
{
"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:
{
"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:
{
"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:
{
"name": "My Da Nang Trip"
}
Response:
{
"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:
{
"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:
{
"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:
{
"status": "success",
"message": "Removed item item_1"
}
PUT /planner/{plan_id}/reorder
Reorder places manually.
Query: ?user_id=user_123
Request:
{
"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:
{
"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:
{
"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:
{
"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:
{
"lat": 16.0626442,
"lng": 108.2462143,
"max_distance_km": 3.0,
"category": "cafe",
"limit": 10
}
Models
Place Object
interface Place {
place_id: string;
name: string;
category?: string;
lat?: number;
lng?: number;
rating?: number;
description?: string;
}
PlanItem
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
interface WorkflowStep {
step: string;
tool?: string;
purpose: string;
results: number;
}
SmartPlan (Get Plan Response)
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
// 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
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 |
{
"detail": "Plan not found"
}