fix(chat): add saved AI defaults and harden suggestions
This commit is contained in:
44
.memory/knowledge/domain/ai-configuration.md
Normal file
44
.memory/knowledge/domain/ai-configuration.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# AI Configuration Domain
|
||||
|
||||
## WS1 Configuration Infrastructure
|
||||
|
||||
### WS1-F1: Instance-level env vars and key fallback
|
||||
- `settings.py`: `VOYAGE_AI_PROVIDER`, `VOYAGE_AI_MODEL`, `VOYAGE_AI_API_KEY`
|
||||
- `get_llm_api_key(user, provider)` falls back to instance key only when provider matches `VOYAGE_AI_PROVIDER`
|
||||
- Fallback chain: user key -> matching-provider instance key -> error
|
||||
- See [tech-stack.md](../tech-stack.md#server-side-env-vars-from-settingspy), [decisions.md](../../decisions.md#ws1-configuration-infrastructure-backend-review)
|
||||
|
||||
### WS1-F2: UserAISettings model
|
||||
- `integrations/models.py`: `UserAISettings` (OneToOneField to user) with `preferred_provider` and `preferred_model`
|
||||
- Endpoint: `/api/integrations/ai-settings/` (upsert pattern)
|
||||
- Migration: `0008_useraisettings.py`
|
||||
|
||||
### WS1-F3: Provider catalog enhancement
|
||||
- `get_provider_catalog(user=None)` adds `instance_configured` and `user_configured` booleans
|
||||
- User API keys prefetched once per request (no N+1)
|
||||
- `ChatProviderCatalogEntry` TypeScript type updated with both fields
|
||||
|
||||
### Frontend Provider Selection (Fixed)
|
||||
- No longer hardcodes `selectedProvider = 'openai'`; auto-selects first usable provider
|
||||
- Filtered to configured+usable entries only (`available_for_chat && (user_configured || instance_configured)`)
|
||||
- Warning alert + Settings link when no providers configured
|
||||
- Model selection uses dropdown from `GET /api/chat/providers/{provider}/models/`
|
||||
|
||||
## Known Frontend Gaps
|
||||
|
||||
### Root Cause of User-Facing LLM Errors
|
||||
Three compounding issues (all resolved):
|
||||
1. ~~Hardcoded `'openai'` default~~ (fixed: auto-selects first usable)
|
||||
2. ~~No provider status feedback~~ (fixed: catalog fields consumed)
|
||||
3. ~~`UserAISettings.preferred_provider` never loaded~~ (fixed: Settings UI saves/loads DB defaults; chat initializes from saved prefs)
|
||||
4. `FIELD_ENCRYPTION_KEY` not set disables key storage (env-dependent)
|
||||
5. ~~TypeScript type missing fields~~ (fixed)
|
||||
|
||||
## Key Edit Reference Points
|
||||
| Feature | File | Location |
|
||||
|---|---|---|
|
||||
| AI env vars | `backend/server/main/settings.py` | after `FIELD_ENCRYPTION_KEY` |
|
||||
| Fallback key | `backend/server/chat/llm_client.py` | `get_llm_api_key()` |
|
||||
| UserAISettings model | `backend/server/integrations/models.py` | after UserAPIKey |
|
||||
| Catalog user flags | `backend/server/chat/llm_client.py` | `get_provider_catalog()` |
|
||||
| Provider view | `backend/server/chat/views/__init__.py` | `ChatProviderCatalogViewSet` |
|
||||
62
.memory/knowledge/domain/collections-and-sharing.md
Normal file
62
.memory/knowledge/domain/collections-and-sharing.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Collections & Sharing Domain
|
||||
|
||||
## Collection Sharing Architecture
|
||||
|
||||
### Data Model
|
||||
- `Collection.shared_with` - `ManyToManyField(User, related_name='shared_with', blank=True)` - the primary access grant
|
||||
- `CollectionInvite` - staging table for pending invites: `(collection FK, invited_user FK, unique_together)`; prevents self-invite and double-invite
|
||||
- Both models in `backend/server/adventures/models.py`
|
||||
|
||||
### Access Flow (Invite -> Accept -> Membership)
|
||||
1. Owner calls `POST /api/collections/{id}/share/{uuid}/` -> creates `CollectionInvite`
|
||||
2. Invited user sees pending invites via `GET /api/collections/invites/`
|
||||
3. Invited user calls `POST /api/collections/{id}/accept-invite/` -> adds to `shared_with`, deletes invite
|
||||
4. Owner revokes: `POST /api/collections/{id}/revoke-invite/{uuid}/`
|
||||
5. User declines: `POST /api/collections/{id}/decline-invite/`
|
||||
6. Owner removes: `POST /api/collections/{id}/unshare/{uuid}/` - removes user's locations from collection
|
||||
7. User self-removes: `POST /api/collections/{id}/leave/` - removes their locations
|
||||
|
||||
### Permission Classes
|
||||
- `CollectionShared` - full access for owner and `shared_with` members; invite actions for invitees; anonymous read for `is_public`
|
||||
- `IsOwnerOrSharedWithFullAccess` - child-object viewsets; traverses `obj.collections`/`obj.collection` to check `shared_with`
|
||||
- `ContentImagePermission` - delegates to `IsOwnerOrSharedWithFullAccess` on `content_object`
|
||||
|
||||
### Key Design Constraints
|
||||
- No role differentiation - all shared users have same write access
|
||||
- On unshare/leave, departing user's locations are removed from collection (not deleted)
|
||||
- `duplicate` action creates a private copy with no `shared_with` transfer
|
||||
|
||||
## Itinerary Architecture
|
||||
|
||||
### Primary Component
|
||||
`CollectionItineraryPlanner.svelte` (~3818 lines) - monolith rendering the entire itinerary view and all modals.
|
||||
|
||||
### The "Add" Button
|
||||
DaisyUI dropdown at bottom of each day card with "Link existing item" and "Create new" submenu (Location, Lodging, Transportation, Note, Checklist).
|
||||
|
||||
### Day Suggestions Modal (WS3)
|
||||
- Component: `ItinerarySuggestionModal.svelte`
|
||||
- Props: `collection`, `user`, `targetDate`, `displayDate`
|
||||
- Events: `close`, `addItem { type: 'location', itemId, updateDate }`
|
||||
- 3-step UX: category selection -> filters -> results from `POST /api/chat/suggestions/day/`
|
||||
|
||||
## User Recommendation Preference Profile
|
||||
Backend-only feature: model, API, and system-prompt integration exist, but **no frontend UI** built yet.
|
||||
|
||||
### Auto-learned preference updates
|
||||
- `backend/server/integrations/utils/auto_profile.py` derives profile from user history
|
||||
- Fields: `interests` (from activities + locations), `trip_style` (from lodging), `notes` (frequently visited regions)
|
||||
- `ChatViewSet.send_message()` invokes `update_auto_preference_profile(request.user)` at method start
|
||||
|
||||
### Model
|
||||
`UserRecommendationPreferenceProfile` (OneToOne -> `CustomUser`): `cuisines`, `interests` (JSONField), `trip_style`, `notes`, timestamps.
|
||||
|
||||
### System Prompt Integration
|
||||
- Single-user: labeled as auto-inferred with structured markdown section
|
||||
- Shared collections: `get_aggregated_preferences(collection)` appends per-participant preferences
|
||||
- Missing profiles skipped gracefully
|
||||
|
||||
### Frontend Gap
|
||||
- No settings tab for manual preference editing
|
||||
- TypeScript type available as `UserRecommendationPreferenceProfile` in `src/lib/types.ts`
|
||||
- See [Plan: AI travel agent redesign](../../plans/ai-travel-agent-redesign.md#ws2-user-preference-learning)
|
||||
Reference in New Issue
Block a user