LocalMate / docs /API_REFERENCE.md
Cuong2004's picture
fix intent and add plan
51ba917
# LocalMate API Documentation
> **Base URL:** `https://huggingface.co/proxy/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://huggingface.co/proxy/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://huggingface.co/proxy/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"
}
```