Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions app/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,30 @@ async function submit(formData?: FormData, skip?: boolean) {
const analysisResult = await streamResult.object;
summaryStream.done(analysisResult.summary || 'Analysis complete.');

if (analysisResult.geoJson) {
// Reconstruct standard GeoJSON from flattened schema if present
let geoJson: FeatureCollection | null = null;
if (analysisResult.geoJson && analysisResult.geoJson.features) {
geoJson = {
type: 'FeatureCollection',
features: analysisResult.geoJson.features.map(f => ({
type: 'Feature',
geometry: {
type: f.geometryType as any,
coordinates: f.coordinates as any
},
properties: {
name: f.name,
description: f.description
}
}))
};
}

if (geoJson) {
uiStream.append(
<GeoJsonLayer
id={groupeId}
data={analysisResult.geoJson as FeatureCollection}
data={geoJson}
/>
);
}
Expand Down Expand Up @@ -171,6 +190,7 @@ async function submit(formData?: FormData, skip?: boolean) {
role: 'assistant',
content: JSON.stringify({
...analysisResult,
geoJson: geoJson, // Use reconstructed GeoJSON for storage/UI
image: dataUrl,
mapboxImage: mapboxDataUrl,
googleImage: googleDataUrl
Expand Down
39 changes: 2 additions & 37 deletions lib/agents/resolution-search.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,10 @@
import { CoreMessage, streamObject } from 'ai'
import { getModel } from '@/lib/utils'
import { z } from 'zod'
import { tavily } from '@tavily/core'
import { resolutionSearchSchema } from '@/lib/schema/resolution-search'

// This agent is now a pure data-processing module, with no UI dependencies.

// Define the schema for the structured response from the AI.
const resolutionSearchSchema = z.object({
summary: z.string().describe('A detailed text summary of the analysis, including land feature classification, points of interest, relevant current news, and temporal context.'),
geoJson: z.object({
type: z.literal('FeatureCollection'),
features: z.array(z.object({
type: z.literal('Feature'),
geometry: z.object({
type: z.string(), // e.g., 'Point', 'Polygon'
coordinates: z.any(),
}),
properties: z.object({
name: z.string(),
description: z.string().optional(),
}),
})),
}).describe('A GeoJSON object containing points of interest and classified land features to be overlaid on the map.'),
extractedCoordinates: z.object({
latitude: z.number(),
longitude: z.number()
}).optional().describe('The extracted geocoordinates of the center of the image.'),
cogInfo: z.object({
applicable: z.boolean(),
description: z.string().optional()
}).optional().describe('Information about whether Cloud Optimized GeoTIFF (COG) data is applicable or available for this area.'),
newsContext: z.object({
hasRecentNews: z.boolean(),
newsItems: z.array(z.object({
title: z.string(),
summary: z.string(),
relevance: z.string()
})).optional()
}).optional().describe('Recent news and events relevant to the analyzed location.')
})

export interface DrawnFeature {
id: string;
type: 'Polygon' | 'LineString';
Expand Down Expand Up @@ -162,7 +127,7 @@ Use these user-drawn areas/lines as primary areas of interest for your analysis.
4. **Coordinate Extraction:** If possible, confirm or refine the geocoordinates (latitude/longitude) of the center of the image.
5. **COG Applicability:** Determine if this location would benefit from Cloud Optimized GeoTIFF (COG) analysis for high-precision temporal or spectral data.
6. **News Integration:** Reference any recent news or events that may be relevant to the current state of the location.
7. **Structured Output:** Return your findings in a structured JSON format including summary, geoJson, and newsContext.
7. **Structured Output:** Return your findings in a structured JSON format including summary, geoJson (if any), news context, and any extracted coordinates or COG information. Use the provided schema.

Your analysis should be based on the visual information in the image, the temporal context provided, and your general knowledge. Do not attempt to access external websites or perform web searches beyond what has been provided.

Expand Down
41 changes: 41 additions & 0 deletions lib/schema/resolution-search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { z } from 'zod'

// This schema is designed to be compatible with xAI's OpenAI-compatible endpoint.
// We use a flattened structure and avoid z.literal (which generates JSON Schema 'const')
// and z.any() to ensure maximum compatibility with xAI's schema validation.
// This follows the pattern established in lib/schema/geospatial.tsx.

export const resolutionSearchSchema = z.object({
summary: z.string().describe('A detailed text summary of the analysis, including land feature classification, points of interest, relevant current news, and temporal context.'),

// geoJson is optional so the model is not forced to produce features when none are found.
geoJson: z.object({
type: z.string().describe("Must be 'FeatureCollection'"),
features: z.array(z.object({
type: z.string().describe("Must be 'Feature'"),
geometryType: z.string().describe("The type of geometry, e.g., 'Point', 'Polygon'"),
coordinates: z.array(z.number())
.or(z.array(z.array(z.number())))
.or(z.array(z.array(z.array(z.number()))))
.describe('Coordinates for the geometry'),
name: z.string().describe('Name of the feature or point of interest'),
description: z.string().optional().describe('Description of the feature')
}))
}).optional().describe('A collection of points of interest and classified land features to be overlaid on the map.'),

// Flattened top-level fields for better xAI compatibility
extractedLatitude: z.number().optional().describe('The extracted latitude of the center of the image.'),
extractedLongitude: z.number().optional().describe('The extracted longitude of the center of the image.'),

cogApplicable: z.boolean().optional().describe('Whether Cloud Optimized GeoTIFF (COG) data is applicable for this area.'),
cogDescription: z.string().optional().describe('Description of COG data availability or benefits.'),

hasRecentNews: z.boolean().optional().describe('Whether there is recent news relevant to the location.'),
newsItems: z.array(z.object({
title: z.string(),
summary: z.string(),
relevance: z.string()
})).optional().describe('List of recent news items relevant to the location.')
})

export type ResolutionSearch = z.infer<typeof resolutionSearchSchema>;