fix place empty
Browse files- app/agent/mmca_agent.py +3 -1
- app/api/router.py +18 -9
- docs/TECH_STACK.md +112 -0
- tests/react_comparison_report.md +20 -29
app/agent/mmca_agent.py
CHANGED
|
@@ -82,6 +82,7 @@ class ChatResult:
|
|
| 82 |
workflow: AgentWorkflow
|
| 83 |
tools_used: list[str] = field(default_factory=list)
|
| 84 |
total_duration_ms: float = 0
|
|
|
|
| 85 |
|
| 86 |
|
| 87 |
class MMCAAgent:
|
|
@@ -233,7 +234,8 @@ class MMCAAgent:
|
|
| 233 |
response=response,
|
| 234 |
workflow=workflow,
|
| 235 |
tools_used=workflow.tools_used,
|
| 236 |
-
total_duration_ms=total_duration
|
|
|
|
| 237 |
)
|
| 238 |
|
| 239 |
def _detect_intent(self, message: str, image_url: str | None) -> str:
|
|
|
|
| 82 |
workflow: AgentWorkflow
|
| 83 |
tools_used: list[str] = field(default_factory=list)
|
| 84 |
total_duration_ms: float = 0
|
| 85 |
+
tool_results: list = field(default_factory=list) # List of ToolCall with results
|
| 86 |
|
| 87 |
|
| 88 |
class MMCAAgent:
|
|
|
|
| 234 |
response=response,
|
| 235 |
workflow=workflow,
|
| 236 |
tools_used=workflow.tools_used,
|
| 237 |
+
total_duration_ms=total_duration,
|
| 238 |
+
tool_results=tool_results, # Include tool results for place extraction
|
| 239 |
)
|
| 240 |
|
| 241 |
def _detect_intent(self, message: str, image_url: str | None) -> str:
|
app/api/router.py
CHANGED
|
@@ -407,19 +407,28 @@ async def chat(
|
|
| 407 |
|
| 408 |
# Extract places from tool results if available
|
| 409 |
places = []
|
| 410 |
-
if
|
| 411 |
-
#
|
| 412 |
-
places = await enrich_places_from_ids(result.selected_place_ids, db)
|
| 413 |
-
elif hasattr(result, 'tool_results') and result.tool_results:
|
| 414 |
-
# Fallback: extract all place_ids from tool results
|
| 415 |
place_ids = []
|
| 416 |
-
for
|
| 417 |
-
|
| 418 |
-
|
|
|
|
|
|
|
| 419 |
if isinstance(item, dict) and 'place_id' in item:
|
| 420 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 421 |
if place_ids:
|
| 422 |
places = await enrich_places_from_ids(place_ids[:5], db) # Limit to top 5
|
|
|
|
|
|
|
|
|
|
|
|
|
| 423 |
|
| 424 |
return ChatResponse(
|
| 425 |
response=result.response,
|
|
|
|
| 407 |
|
| 408 |
# Extract places from tool results if available
|
| 409 |
places = []
|
| 410 |
+
if result.tool_results:
|
| 411 |
+
# Extract place_ids from ToolCall objects
|
|
|
|
|
|
|
|
|
|
| 412 |
place_ids = []
|
| 413 |
+
distance_map = {} # Store distance info for nearby places
|
| 414 |
+
for tool_call in result.tool_results:
|
| 415 |
+
# ToolCall has .result attribute which is a list of dicts
|
| 416 |
+
if tool_call.result:
|
| 417 |
+
for item in tool_call.result:
|
| 418 |
if isinstance(item, dict) and 'place_id' in item:
|
| 419 |
+
pid = item['place_id']
|
| 420 |
+
if pid not in place_ids: # Avoid duplicates
|
| 421 |
+
place_ids.append(pid)
|
| 422 |
+
# Capture distance if available (from find_nearby_places)
|
| 423 |
+
if 'distance_km' in item:
|
| 424 |
+
distance_map[pid] = item['distance_km']
|
| 425 |
+
|
| 426 |
if place_ids:
|
| 427 |
places = await enrich_places_from_ids(place_ids[:5], db) # Limit to top 5
|
| 428 |
+
# Add distance info to places
|
| 429 |
+
for place in places:
|
| 430 |
+
if place.place_id in distance_map:
|
| 431 |
+
place.distance_km = distance_map[place.place_id]
|
| 432 |
|
| 433 |
return ChatResponse(
|
| 434 |
response=result.response,
|
docs/TECH_STACK.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Tech Stack Backend - LocalMate Da Nang
|
| 2 |
+
|
| 3 |
+
> **Version**: 0.2.0
|
| 4 |
+
> **Python**: ≥3.11
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## 🚀 Core Framework
|
| 9 |
+
|
| 10 |
+
| Công nghệ | Phiên bản | Mô tả |
|
| 11 |
+
|-----------|-----------|-------|
|
| 12 |
+
| **FastAPI** | ≥0.115.0 | Web framework chính, async API |
|
| 13 |
+
| **Uvicorn** | ≥0.32.0 | ASGI server |
|
| 14 |
+
| **Pydantic** | ≥2.10.0 | Data validation & settings management |
|
| 15 |
+
| **pydantic-settings** | ≥2.6.0 | Configuration management |
|
| 16 |
+
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
## 🗄️ Databases
|
| 20 |
+
|
| 21 |
+
| Công nghệ | Phiên bản | Mục đích |
|
| 22 |
+
|-----------|-----------|----------|
|
| 23 |
+
| **PostgreSQL** (asyncpg) | ≥0.30.0 | Primary database (async driver) |
|
| 24 |
+
| **SQLAlchemy** | ≥2.0.0 | ORM & database toolkit |
|
| 25 |
+
| **Supabase** | ≥2.10.0 | Backend-as-a-Service (Auth, Storage, Realtime) |
|
| 26 |
+
| **Neo4j** | ≥5.26.0 | Graph database cho knowledge graph |
|
| 27 |
+
| **pgvector** | ≥0.3.0 | Vector embeddings cho semantic search |
|
| 28 |
+
| **greenlet** | ≥3.0.0 | Concurrency support cho SQLAlchemy |
|
| 29 |
+
|
| 30 |
+
---
|
| 31 |
+
|
| 32 |
+
## 🤖 AI/ML
|
| 33 |
+
|
| 34 |
+
| Công nghệ | Phiên bản | Mục đích |
|
| 35 |
+
|-----------|-----------|----------|
|
| 36 |
+
| **Google GenAI** | ≥1.0.0 | LLM integration (Gemini models) |
|
| 37 |
+
| **PyTorch** | ≥2.0.0 | Deep learning framework |
|
| 38 |
+
| **OpenCLIP** | ≥2.24.0 | Image embedding (SigLIP local model) |
|
| 39 |
+
| **Pillow** | ≥10.0.0 | Image processing & manipulation |
|
| 40 |
+
|
| 41 |
+
---
|
| 42 |
+
|
| 43 |
+
## 🔐 Authentication & Security
|
| 44 |
+
|
| 45 |
+
| Công nghệ | Phiên bản | Mục đích |
|
| 46 |
+
|-----------|-----------|----------|
|
| 47 |
+
| **PyJWT** | ≥2.9.0 | JWT token encoding/decoding |
|
| 48 |
+
| **Supabase Auth** | - | User authentication & authorization |
|
| 49 |
+
|
| 50 |
+
---
|
| 51 |
+
|
| 52 |
+
## 🔧 Utilities
|
| 53 |
+
|
| 54 |
+
| Công nghệ | Phiên bản | Mục đích |
|
| 55 |
+
|-----------|-----------|----------|
|
| 56 |
+
| **httpx** | ≥0.28.0 | Async HTTP client |
|
| 57 |
+
| **python-dotenv** | ≥1.0.0 | Environment variables management |
|
| 58 |
+
| **python-multipart** | ≥0.0.9 | Multipart form data (file uploads) |
|
| 59 |
+
|
| 60 |
+
---
|
| 61 |
+
|
| 62 |
+
## 🏗️ Architecture Patterns
|
| 63 |
+
|
| 64 |
+
### Agent Architecture
|
| 65 |
+
- **ReAct Agent**: Reasoning + Acting pattern cho AI agent
|
| 66 |
+
- **MCP (Model Context Protocol)**: Standardized protocol cho tool calling
|
| 67 |
+
|
| 68 |
+
### Project Structure
|
| 69 |
+
```
|
| 70 |
+
app/
|
| 71 |
+
├── agent/ # ReAct Agent implementation
|
| 72 |
+
├── api/ # API routes & endpoints
|
| 73 |
+
├── auth/ # Authentication logic
|
| 74 |
+
├── core/ # Core configurations
|
| 75 |
+
├── itineraries/ # Itinerary management
|
| 76 |
+
├── mcp/ # MCP tools & handlers
|
| 77 |
+
├── planner/ # Trip planning logic
|
| 78 |
+
├── shared/ # Shared utilities & models
|
| 79 |
+
├── upload/ # File upload handling
|
| 80 |
+
├── users/ # User management
|
| 81 |
+
└── main.py # Application entry point
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
---
|
| 85 |
+
|
| 86 |
+
## 🐳 DevOps & Deployment
|
| 87 |
+
|
| 88 |
+
| Công nghệ | Mục đích |
|
| 89 |
+
|-----------|----------|
|
| 90 |
+
| **Docker** | Containerization |
|
| 91 |
+
| **Dockerfile** | Container build configuration |
|
| 92 |
+
|
| 93 |
+
---
|
| 94 |
+
|
| 95 |
+
## 🧪 Development & Testing
|
| 96 |
+
|
| 97 |
+
| Công nghệ | Phiên bản | Mục đích |
|
| 98 |
+
|-----------|-----------|----------|
|
| 99 |
+
| **pytest** | ≥8.0.0 | Testing framework |
|
| 100 |
+
| **pytest-asyncio** | ≥0.24.0 | Async test support |
|
| 101 |
+
| **httpx** | ≥0.28.0 | HTTP testing client |
|
| 102 |
+
| **Hatchling** | - | Build system |
|
| 103 |
+
|
| 104 |
+
---
|
| 105 |
+
|
| 106 |
+
## 📊 Key Features
|
| 107 |
+
|
| 108 |
+
1. **Multi-Modal AI**: Text + Image understanding via Google Gemini & SigLIP
|
| 109 |
+
2. **Semantic Search**: Vector similarity search với pgvector
|
| 110 |
+
3. **Knowledge Graph**: Neo4j cho entity relationships
|
| 111 |
+
4. **Real-time**: Supabase realtime capabilities
|
| 112 |
+
5. **Async-first**: Full async/await support throughout the stack
|
tests/react_comparison_report.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
# LocalMate Agent Test Report
|
| 2 |
|
| 3 |
-
**Generated:** 2025-12-18
|
| 4 |
|
| 5 |
## Summary
|
| 6 |
|
|
@@ -8,7 +8,7 @@
|
|
| 8 |
|--------|-------------|------------|
|
| 9 |
| Total Tests | 1 | 1 |
|
| 10 |
| Success | 1 | 1 |
|
| 11 |
-
| Avg Duration |
|
| 12 |
|
| 13 |
---
|
| 14 |
|
|
@@ -21,49 +21,40 @@
|
|
| 21 |
#### Single Mode
|
| 22 |
|
| 23 |
- **Status:** ✅ Success
|
| 24 |
-
- **Duration:**
|
| 25 |
- **Tools Used:** find_nearby_places
|
| 26 |
|
| 27 |
**Workflow:**
|
| 28 |
-
|
| 29 |
-
Tool: `None` | Results: 0
|
| 30 |
-
- Tool Planning
|
| 31 |
-
Tool: `None` | Results: 0
|
| 32 |
-
- Execute find_nearby_places
|
| 33 |
-
Tool: `find_nearby_places` | Results: 5
|
| 34 |
-
- LLM Synthesis
|
| 35 |
-
Tool: `None` | Results: 0
|
| 36 |
|
| 37 |
**Response Preview:**
|
| 38 |
-
> Chào bạn! Mình
|
| 39 |
|
| 40 |
-
|
| 41 |
|
| 42 |
-
1.
|
|
|
|
|
|
|
| 43 |
|
| 44 |
#### ReAct Mode
|
| 45 |
|
| 46 |
- **Status:** ✅ Success
|
| 47 |
-
- **Duration:**
|
| 48 |
- **Tools Used:** get_location_coordinates, find_nearby_places
|
| 49 |
-
- **Steps:**
|
| 50 |
-
- **Intent Detected:**
|
| 51 |
|
| 52 |
**Workflow Steps:**
|
| 53 |
-
|
| 54 |
-
Tool: `get_location_coordinates` | Results: 0
|
| 55 |
-
- Step 2: Đã có tọa độ của bãi biển Mỹ Khê, bây giờ cần tìm ...
|
| 56 |
-
Tool: `find_nearby_places` | Results: 5
|
| 57 |
-
- Step 3: Tôi đã có tọa độ của bãi biển Mỹ Khê và danh sách ...
|
| 58 |
-
Tool: `None` | Results: 0
|
| 59 |
|
| 60 |
**Response Preview:**
|
| 61 |
-
>
|
| 62 |
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
|
|
|
| 67 |
|
| 68 |
---
|
| 69 |
|
|
@@ -73,7 +64,7 @@
|
|
| 73 |
|
| 74 |
| Test | Single Mode Tools | ReAct Mode Tools | ReAct Steps |
|
| 75 |
|------|-------------------|------------------|-------------|
|
| 76 |
-
| 2 | find_nearby_places | get_location_coordinates, find_nearby_places |
|
| 77 |
|
| 78 |
|
| 79 |
### Key Observations
|
|
|
|
| 1 |
# LocalMate Agent Test Report
|
| 2 |
|
| 3 |
+
**Generated:** 2025-12-18 02:27:42
|
| 4 |
|
| 5 |
## Summary
|
| 6 |
|
|
|
|
| 8 |
|--------|-------------|------------|
|
| 9 |
| Total Tests | 1 | 1 |
|
| 10 |
| Success | 1 | 1 |
|
| 11 |
+
| Avg Duration | 12627ms | 15080ms |
|
| 12 |
|
| 13 |
---
|
| 14 |
|
|
|
|
| 21 |
#### Single Mode
|
| 22 |
|
| 23 |
- **Status:** ✅ Success
|
| 24 |
+
- **Duration:** 12627ms
|
| 25 |
- **Tools Used:** find_nearby_places
|
| 26 |
|
| 27 |
**Workflow:**
|
| 28 |
+
No steps recorded
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
**Response Preview:**
|
| 31 |
+
> Chào bạn! Mình tìm được một số nhà hàng ngon gần bãi biển Mỹ Khê cho bạn đây:
|
| 32 |
|
| 33 |
+
🏆 **Top 3 nhà hàng được đánh giá cao nhất:**
|
| 34 |
|
| 35 |
+
1. **Cabanon Palace** - Nhà hàng Pháp
|
| 36 |
+
* ⭐️ Rating: 4.8/5
|
| 37 |
+
* 📍 Cách biể...
|
| 38 |
|
| 39 |
#### ReAct Mode
|
| 40 |
|
| 41 |
- **Status:** ✅ Success
|
| 42 |
+
- **Duration:** 15080ms
|
| 43 |
- **Tools Used:** get_location_coordinates, find_nearby_places
|
| 44 |
+
- **Steps:** 0
|
| 45 |
+
- **Intent Detected:** N/A
|
| 46 |
|
| 47 |
**Workflow Steps:**
|
| 48 |
+
No steps recorded
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
**Response Preview:**
|
| 51 |
+
> Chào bạn! Mình tìm được những nhà hàng ngon và gần bãi biển Mỹ Khê nhất cho bạn đây:
|
| 52 |
|
| 53 |
+
🏆 **Top 3 nhà hàng được đánh giá cao nhất:**
|
| 54 |
+
|
| 55 |
+
1. **Cabanon Palace** - Nhà hàng Pháp
|
| 56 |
+
* ⭐️ Rating: 4.8/5
|
| 57 |
+
* 📍 C...
|
| 58 |
|
| 59 |
---
|
| 60 |
|
|
|
|
| 64 |
|
| 65 |
| Test | Single Mode Tools | ReAct Mode Tools | ReAct Steps |
|
| 66 |
|------|-------------------|------------------|-------------|
|
| 67 |
+
| 2 | find_nearby_places | get_location_coordinates, find_nearby_places | 0 |
|
| 68 |
|
| 69 |
|
| 70 |
### Key Observations
|