Files
voyage/.memory/plans/ai-travel-agent-redesign.md
alex wiesner c4d39f2812 changes
2026-03-13 20:15:22 +00:00

344 lines
16 KiB
Markdown

---
title: ai-travel-agent-redesign
type: note
permalink: voyage/plans/ai-travel-agent-redesign
---
# AI Travel Agent Redesign Plan
## Vision Summary
Redesign the AI travel agent with two context-aware entry points, user preference learning, flexible provider configuration, extensibility for future integrations, web search capability, and multi-user collection support.
---
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ ENTRY POINTS │
├─────────────────────────────┬───────────────────────────────────┤
│ Day-Level Suggestions │ Collection-Level Chat │
│ (new modal) │ (improved Recommendations tab) │
│ - Category filters │ - Context-aware │
│ - Sub-filters │ - Add to itinerary actions │
│ - Add to day action │ │
└─────────────────────────────┴───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ AGENT CORE │
├─────────────────────────────────────────────────────────────────┤
│ - LiteLLM backend (streaming SSE) │
│ - Tool calling (place search, web search, itinerary actions) │
│ - Multi-user preference aggregation │
│ - Context injection (collection, dates, location) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ CONFIGURATION LAYERS │
├─────────────────────────────────────────────────────────────────┤
│ Instance (.env) → VOYAGE_AI_PROVIDER │
│ → VOYAGE_AI_MODEL │
│ → VOYAGE_AI_API_KEY │
│ User (DB) → UserAPIKey.per-provider keys │
│ → UserAISettings.model preference │
│ Fallback: User key → Instance key → Error │
└─────────────────────────────────────────────────────────────────┘
```
---
## Workstreams
### WS1: Configuration Infrastructure
**Goal**: Support both instance-level and user-level provider/model configuration with proper fallback.
#### WS1-F1: Instance-level configuration
- Add env vars to `settings.py`:
- `VOYAGE_AI_PROVIDER` (default: `openai`)
- `VOYAGE_AI_MODEL` (default: `gpt-4o-mini`)
- `VOYAGE_AI_API_KEY` (optional global key)
- Update `llm_client.py` to read instance defaults
- Add fallback chain: user key → instance key → error
#### WS1-F2: User-level model preferences
- Add `UserAISettings` model (OneToOne → CustomUser):
- `preferred_provider` (CharField)
- `preferred_model` (CharField)
- Create API endpoint: `POST /api/ai/settings/`
- Add UI in Settings → AI section for model selection
#### WS1-F3: Provider catalog enhancement
- Extend provider catalog response to include:
- `instance_configured`: bool (has instance key)
- `user_configured`: bool (has user key)
- Update frontend to show configuration status per provider
**Files**: `settings.py`, `llm_client.py`, `integrations/models.py`, `integrations/views/`, `frontend/src/routes/settings/`
---
### WS2: User Preference Learning
**Goal**: Capture and use user preferences in AI recommendations.
#### WS2-F1: Preference UI
- Add "AI Preferences" tab to Settings page
- Form fields: cuisines, interests, trip_style, notes
- Use tag input for cuisines/interests (better UX than free text)
- Connect to existing `/api/integrations/recommendation-preferences/`
Implementation notes (2026-03-08):
- Implemented in `frontend/src/routes/settings/+page.svelte` as `travel_preferences` section in the existing settings sidebar, with `savePreferences(event)` posting to `/api/integrations/recommendation-preferences/`.
- `interests` conversion is string↔array at UI boundary: load via `(profile.interests || []).join(', ')`; save via `.split(',').map((s) => s.trim()).filter(Boolean)`.
- SSR preload added in `frontend/src/routes/settings/+page.server.ts` using parallel fetch with API keys; returns `props.recommendationProfile` as first list element or `null`.
- Frontend typing added in `frontend/src/lib/types.ts` (`UserRecommendationPreferenceProfile`) and i18n strings added under `settings` in `frontend/src/locales/en.json`.
- See backend capability reference in [Project Knowledge — User Recommendation Preference Profile](../knowledge.md#user-recommendation-preference-profile).
#### WS2-F2: Preference injection
- Enhance `get_system_prompt()` to format preferences better
- Add preference summary in system prompt (structured, not just appended)
#### WS2-F3: Multi-user aggregation
- New function: `get_aggregated_preferences(collection)`
- Returns combined preferences from all `collection.shared_with` users + owner
- Format: "Party preferences: User A likes X, User B prefers Y..."
- Inject into system prompt for shared collections
**Files**: `frontend/src/routes/settings/`, `chat/llm_client.py`, `integrations/models.py`
---
### WS3: Day-Level Suggestions Modal
**Goal**: Add "Suggest" option to itinerary day "Add" dropdown with category filters.
#### WS3-F1: Suggestion modal component
- Create `ItinerarySuggestionModal.svelte`
- Two-step flow:
1. **Category selection**: Restaurant, Activity, Event, Lodging
2. **Filter refinement**:
- Restaurant: cuisine type, price range, dietary restrictions
- Activity: type (outdoor, cultural, etc.), duration
- Event: type, date/time preference
- Lodging: type, amenities
- "Any/Surprise me" option for each filter
#### WS3-F2: Add button integration
- Add "Get AI suggestions" option to `CollectionItineraryPlanner.svelte` Add dropdown
- Opens suggestion modal with target date pre-set
- Modal receives: `collectionId`, `targetDate`, `collectionLocation` (for context)
#### WS3-F3: Suggestion results display
- Show 3-5 suggestions as cards with:
- Name, description, why it fits preferences
- "Add to this day" button
- "Add to different day" option
- On add: **direct REST API call** to `/api/itineraries/` (not agent tool)
- User must approve each item individually - no bulk/auto-add
- Close modal and refresh itinerary on success
#### WS3-F4: Backend suggestion endpoint
- New endpoint: `POST /api/ai/suggestions/day/`
- Params: `collection_id`, `date`, `category`, `filters`, `location_context`
- Returns structured suggestions (not chat, direct JSON)
- Uses agent internally but returns parsed results
**Files**: `CollectionItineraryPlanner.svelte`, `ItinerarySuggestionModal.svelte` (new), `chat/views.py`, `chat/agent_tools.py`
---
### WS3.5: Insertion Flow Clarification
**Two insertion paths exist:**
| Path | Entry Point | Mechanism | Use Case |
|------|-------------|-----------|----------|
| **User-approved** | Suggestions modal | Direct REST API call to `/api/itineraries/` | Day-level suggestions, user reviews and clicks Add |
| **Agent-initiated** | Chat (Recommendations tab) | `add_to_itinerary` tool via SSE streaming | Conversational adds when user says "add that place" |
**Why two paths:**
- Modal: Faster, simpler UX - no round-trip through agent, user stays in control
- Chat: Natural conversation flow - agent can add as part of dialogue
**No changes needed to agent tools** - `add_to_itinerary` already exists in `agent_tools.py` and works for chat-initiated adds.
---
### WS4: Collection-Level Chat Improvements
**Goal**: Make Recommendations tab chat context-aware and action-capable.
#### WS4-F1: Context injection
- Pass collection context to `AITravelChat.svelte`:
- `collectionId`, `collectionName`, `startDate`, `endDate`
- `destination` (from collection locations or user input)
- Inject into system prompt: "You are helping plan a trip to X from Y to Z"
Implementation notes (2026-03-08):
- `frontend/src/lib/components/AITravelChat.svelte` now exposes optional context props (`collectionId`, `collectionName`, `startDate`, `endDate`, `destination`) and includes them in `POST /api/chat/conversations/{id}/send_message/` payload.
- `frontend/src/routes/collections/[id]/+page.svelte` now passes collection context into `AITravelChat`; destination is derived via `deriveCollectionDestination(...)` from `city/country/location/name` on the first usable location.
- `backend/server/chat/views/__init__.py::ChatViewSet.send_message()` now accepts the same optional fields, resolves `collection_id` (owner/shared access only), and appends a `## Trip Context` block to the system prompt before streaming.
- Related architecture note: [Project Knowledge — AI Chat](../knowledge.md#ai-chat-collections--recommendations).
#### WS4-F2: Quick action buttons
- Add preset prompts above chat input:
- "Suggest restaurants for this trip"
- "Find activities near [destination]"
- "What should I pack for [dates]?"
- Pre-fill input on click
#### WS4-F3: Add-to-itinerary from chat
- When agent suggests a place, show "Add to itinerary" button
- User selects date → calls `add_to_itinerary` tool
- Visual feedback on success
Implementation notes (2026-03-08):
- Implemented in `frontend/src/lib/components/AITravelChat.svelte` as an MVP direct frontend flow (no agent round-trip):
- Adds `Add to Itinerary` button to `search_places` result cards when `collectionId` exists.
- Opens a date picker modal (`showDateSelector`, `selectedPlaceToAdd`, `selectedDate`) constrained by trip date range (`min={startDate}`, `max={endDate}`).
- On confirm, creates a location via `POST /api/locations/` then creates itinerary entry via `POST /api/itineraries/`.
- Dispatches `itemAdded { locationId, date }` and shows success toast (`added_successfully`).
- Guards against missing/invalid coordinates by disabling add action unless lat/lon parse successfully.
- i18n keys added in `frontend/src/locales/en.json`: `add_to_itinerary`, `add_to_which_day`, `added_successfully`.
#### WS4-F4: Improved UI
- Remove generic "robot" branding, use travel-themed design
- Show collection name in header
- Better tool result display (cards instead of raw JSON)
Implementation notes (2026-03-08):
- `frontend/src/lib/components/AITravelChat.svelte` header now uses travel branding with `✈️` and renders `Travel Assistant · {collectionName}` when collection context is present; destination is shown as a subtitle when provided.
- Robot icon usage in chat UI was replaced with travel-themed emoji (`✈️`, `🌍`, `🗺️`) while keeping existing layout structure.
- SSE `tool_result` chunks are now attached to the in-flight assistant message via `tool_results` and rendered inline as structured cards for `search_places` and `web_search`, with JSON `<pre>` fallback for unknown tools.
- Legacy persisted `role: 'tool'` messages are still supported via JSON parsing fallback and use the same card rendering logic.
- i18n root keys added in `frontend/src/locales/en.json`: `travel_assistant`, `quick_actions`.
See [Project Knowledge — WS4-F4 Chat UI Rendering](../knowledge.md#ws4-f4-chat-ui-rendering).
**Files**: `AITravelChat.svelte`, `chat/views.py`, `chat/llm_client.py`
---
### WS5: Web Search Capability
**Goal**: Enable agent to search the web for current information.
#### WS5-F1: Web search tool
- Add `web_search` tool to `agent_tools.py`:
- Uses DuckDuckGo (free, no API key) or Brave Search API (env var)
- Returns top 5 results with titles, snippets, URLs
- Tool schema:
```python
{
"name": "web_search",
"description": "Search the web for current information about destinations, events, prices, etc.",
"parameters": {
"query": "string - search query",
"location_context": "string - optional location to bias results"
}
}
```
#### WS5-F2: Tool integration
- Register in `AGENT_TOOLS` list
- Add to `execute_tool()` dispatcher
- Handle rate limiting gracefully
**Files**: `chat/agent_tools.py`, `requirements.txt` (add `duckduckgo-search`)
---
### WS6: Extensibility Architecture
**Goal**: Design for easy addition of future integrations.
#### WS6-F1: Plugin tool registry
- Refactor `agent_tools.py` to use decorator-based registration:
```python
@agent_tool(name="web_search", description="...")
def web_search(query: str, location_context: str = None):
...
```
- Tools auto-register on import
- Easy to add new tools in separate files
#### WS6-F2: Integration hooks
- Create `chat/integrations/` directory for future:
- `tripadvisor.py` - TripAdvisor API integration
- `flights.py` - Flight search (Skyscanner, etc.)
- `weather.py` - Enhanced weather data
- Each integration exports tools via decorator
#### WS6-F3: Capability discovery
- Endpoint: `GET /api/ai/capabilities/`
- Returns list of available tools/integrations
- Frontend can show "Powered by X, Y, Z" dynamically
**Files**: `chat/tools/` (new directory), `chat/agent_tools.py` (refactor)
---
## File Changes Summary
### New Files
- `frontend/src/lib/components/collections/ItinerarySuggestionModal.svelte`
- `backend/server/chat/tools/__init__.py`
- `backend/server/chat/tools/web_search.py`
- `backend/server/integrations/models.py` (add UserAISettings)
- `backend/server/integrations/views/ai_settings_view.py`
### Modified Files
- `backend/server/main/settings.py` - Add AI env vars
- `backend/server/chat/llm_client.py` - Config fallback, preference aggregation
- `backend/server/chat/views.py` - New suggestion endpoint, context injection
- `backend/server/chat/agent_tools.py` - Web search tool, refactor
- `frontend/src/lib/components/AITravelChat.svelte` - Context awareness, actions
- `frontend/src/lib/components/collections/CollectionItineraryPlanner.svelte` - Add button
- `frontend/src/routes/settings/+page.svelte` - AI preferences UI, model selection
- `frontend/src/routes/collections/[id]/+page.svelte` - Pass collection context
---
## Migration Path
1. **Phase 1 - Foundation** (WS1, WS2)
- Configuration infrastructure
- Preference UI
- No user-facing changes to chat yet
2. **Phase 2 - Day Suggestions** (WS3)
- New modal, new entry point
- Backend suggestion endpoint
- Can ship independently
3. **Phase 3 - Chat Improvements** (WS4, WS5)
- Context-aware chat
- Web search capability
- Better UX
4. **Phase 4 - Extensibility** (WS6)
- Plugin architecture
- Future integration prep
---
## Decisions (Confirmed)
| Decision | Choice | Rationale |
|----------|--------|-----------|
| Web search provider | **DuckDuckGo** | Free, no API key, good enough for travel info |
| Suggestion API | **Dedicated REST endpoint** | Simpler, faster, returns JSON directly |
| Multi-user conflicts | **List all preferences** | Transparency - AI navigates differing preferences |
---
## Out of Scope
- WSGI→ASGI migration (keep current async-in-sync pattern)
- Role-based permissions (all shared users have same access)
- Real-time collaboration (WebSocket sync)
- Mobile-specific optimizations