diff --git a/CC BY-NC 4.0.docx b/CC BY-NC 4.0.docx new file mode 100644 index 00000000..0d8f7d55 Binary files /dev/null and b/CC BY-NC 4.0.docx differ diff --git a/app/actions.tsx b/app/actions.tsx index 9e0ee20a..74ce94e9 100644 --- a/app/actions.tsx +++ b/app/actions.tsx @@ -12,7 +12,7 @@ import type { FeatureCollection } from 'geojson' import { Spinner } from '@/components/ui/spinner' import { Section } from '@/components/section' import { FollowupPanel } from '@/components/followup-panel' -import { inquire, researcher, taskManager, querySuggestor, resolutionSearch, type DrawnFeature } from '@/lib/agents' +import { inquire, researcher, taskManager, querySuggestor, resolutionSearch } from '@/lib/agents' // Removed import of useGeospatialToolMcp as it no longer exists and was incorrectly used here. // The geospatialTool (if used by agents like researcher) now manages its own MCP client. import { writer } from '@/lib/agents/writer' @@ -27,6 +27,7 @@ import { CopilotDisplay } from '@/components/copilot-display' import RetrieveSection from '@/components/retrieve-section' import { VideoSearchSection } from '@/components/video-search-section' import { MapQueryHandler } from '@/components/map/map-query-handler' // Add this import +import { GraphSection } from '@/components/graph-section' // Define the type for related queries type RelatedQueries = { @@ -46,14 +47,6 @@ async function submit(formData?: FormData, skip?: boolean) { if (action === 'resolution_search') { const file = formData?.get('file') as File; const timezone = (formData?.get('timezone') as string) || 'UTC'; - const drawnFeaturesString = formData?.get('drawnFeatures') as string; - let drawnFeatures: DrawnFeature[] = []; - try { - drawnFeatures = drawnFeaturesString ? JSON.parse(drawnFeaturesString) : []; - } catch (e) { - console.error('Failed to parse drawnFeatures:', e); - } - if (!file) { throw new Error('No file provided for resolution search.'); } @@ -95,18 +88,8 @@ async function submit(formData?: FormData, skip?: boolean) { async function processResolutionSearch() { try { - // Call the simplified agent, which now returns a stream. - const streamResult = await resolutionSearch(messages, timezone, drawnFeatures); - - let fullSummary = ''; - for await (const partialObject of streamResult.partialObjectStream) { - if (partialObject.summary) { - fullSummary = partialObject.summary; - summaryStream.update(fullSummary); - } - } - - const analysisResult = await streamResult.object; + // Call the simplified agent, which now returns data directly. + const analysisResult = await resolutionSearch(messages, timezone) as any; // Mark the summary stream as done with the result. summaryStream.done(analysisResult.summary || 'Analysis complete.'); @@ -315,7 +298,11 @@ async function submit(formData?: FormData, skip?: boolean) { image: dataUrl, mimeType: file.type }) - } else if (file.type === 'text/plain') { + } else if ( + file.type === 'text/plain' || + file.type === 'text/csv' || + file.type === 'application/json' + ) { const textContent = Buffer.from(buffer).toString('utf-8') const existingTextPart = messageParts.find(p => p.type === 'text') if (existingTextPart) { @@ -669,8 +656,8 @@ export const getUIStateFromAIState = (aiState: AIState): UIState => { } break case 'assistant': - const answer = createStreamableValue() - answer.done(content) + const answer = createStreamableValue(content as string) + answer.done(content as string) switch (type) { case 'response': return { @@ -682,7 +669,9 @@ export const getUIStateFromAIState = (aiState: AIState): UIState => { ) } case 'related': - const relatedQueries = createStreamableValue() + const relatedQueries = createStreamableValue({ + items: [] + }) relatedQueries.done(JSON.parse(content as string)) return { id, @@ -721,7 +710,7 @@ export const getUIStateFromAIState = (aiState: AIState): UIState => { case 'tool': try { const toolOutput = JSON.parse(content as string) - const isCollapsed = createStreamableValue() + const isCollapsed = createStreamableValue(true) isCollapsed.done(true) if ( @@ -735,7 +724,9 @@ export const getUIStateFromAIState = (aiState: AIState): UIState => { } } - const searchResults = createStreamableValue() + const searchResults = createStreamableValue( + JSON.stringify(toolOutput) + ) searchResults.done(JSON.stringify(toolOutput)) switch (name) { case 'search': @@ -758,6 +749,32 @@ export const getUIStateFromAIState = (aiState: AIState): UIState => { ), isCollapsed: isCollapsed.value } + case 'dataAnalysis': + return { + id, + component: ( + <> + + {toolOutput.geospatial && toolOutput.geospatial.length > 0 && ( + + )} + + ), + isCollapsed: isCollapsed.value + } default: console.warn( `Unhandled tool result in getUIStateFromAIState: ${name}` diff --git a/app/layout.tsx b/app/layout.tsx index a092d4fe..bddadc19 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -12,7 +12,10 @@ import { SpeedInsights } from "@vercel/speed-insights/next" import { Toaster } from '@/components/ui/sonner' import { MapToggleProvider } from '@/components/map-toggle-context' import { ProfileToggleProvider } from '@/components/profile-toggle-context' +import { UsageToggleProvider } from '@/components/usage-toggle-context' import { CalendarToggleProvider } from '@/components/calendar-toggle-context' +import { HistoryToggleProvider } from '@/components/history-toggle-context' +import { HistorySidebar } from '@/components/history-sidebar' import { MapLoadingProvider } from '@/components/map-loading-context'; import ConditionalLottie from '@/components/conditional-lottie'; import { MapProvider as MapContextProvider } from '@/components/map/map-context' @@ -70,28 +73,33 @@ export default function RootLayout({ )} > - - - - - -
- - {children} - -