feat(ai): implement agent-redesign plan with enhanced AI travel features
Phase 1 - Configuration Infrastructure (WS1): - Add instance-level AI env vars (VOYAGE_AI_PROVIDER, VOYAGE_AI_MODEL, VOYAGE_AI_API_KEY) - Implement fallback chain: user key → instance key → error - Add UserAISettings model for per-user provider/model preferences - Enhance provider catalog with instance_configured and user_configured flags - Optimize provider catalog to avoid N+1 queries Phase 1 - User Preference Learning (WS2): - Add Travel Preferences tab to Settings page - Improve preference formatting in system prompt with emoji headers - Add multi-user preference aggregation for shared collections Phase 2 - Day-Level Suggestions Modal (WS3): - Create ItinerarySuggestionModal with 3-step flow (category → filters → results) - Add AI suggestions button to itinerary Add dropdown - Support restaurant, activity, event, and lodging categories - Backend endpoint POST /api/chat/suggestions/day/ with context-aware prompts Phase 3 - Collection-Level Chat Improvements (WS4): - Inject collection context (destination, dates) into chat system prompt - Add quick action buttons for common queries - Add 'Add to itinerary' button on search_places results - Update chat UI with travel-themed branding and improved tool result cards Phase 3 - Web Search Capability (WS5): - Add web_search agent tool using DuckDuckGo - Support location_context parameter for biased results - Handle rate limiting gracefully Phase 4 - Extensibility Architecture (WS6): - Implement decorator-based @agent_tool registry - Convert existing tools to use decorators - Add GET /api/chat/capabilities/ endpoint for tool discovery - Refactor execute_tool() to use registry pattern
This commit is contained in:
@@ -256,6 +256,29 @@
|
||||
// Enforce recommendations visibility only for owner/shared users
|
||||
$: availableViews.recommendations = !!canModifyCollection;
|
||||
|
||||
function deriveCollectionDestination(current: Collection | null): string | undefined {
|
||||
if (!current?.locations?.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const firstLocation = current.locations.find((loc) =>
|
||||
Boolean(loc.city?.name || loc.country?.name || loc.location || loc.name)
|
||||
);
|
||||
if (!firstLocation) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const cityName = firstLocation.city?.name;
|
||||
const countryName = firstLocation.country?.name;
|
||||
if (cityName && countryName) {
|
||||
return `${cityName}, ${countryName}`;
|
||||
}
|
||||
|
||||
return cityName || countryName || firstLocation.location || firstLocation.name || undefined;
|
||||
}
|
||||
|
||||
$: collectionDestination = deriveCollectionDestination(collection);
|
||||
|
||||
// Build calendar events from collection visits
|
||||
type TimezoneMode = 'event' | 'local';
|
||||
|
||||
@@ -1261,7 +1284,14 @@
|
||||
<!-- Recommendations View -->
|
||||
{#if currentView === 'recommendations'}
|
||||
<div class="space-y-8">
|
||||
<AITravelChat embedded={true} />
|
||||
<AITravelChat
|
||||
embedded={true}
|
||||
collectionId={collection.id}
|
||||
collectionName={collection.name}
|
||||
startDate={collection.start_date || undefined}
|
||||
endDate={collection.end_date || undefined}
|
||||
destination={collectionDestination}
|
||||
/>
|
||||
<CollectionRecommendationView bind:collection user={data.user} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user