Skip to content

Flatten Schemas for AI Compatibility#633

Closed
ngoiyaeric wants to merge 1 commit into
mainfrom
flatten-schemas-compatibility-9024415414590610865
Closed

Flatten Schemas for AI Compatibility#633
ngoiyaeric wants to merge 1 commit into
mainfrom
flatten-schemas-compatibility-9024415414590610865

Conversation

@ngoiyaeric

@ngoiyaeric ngoiyaeric commented May 31, 2026

Copy link
Copy Markdown
Collaborator

Audited and refactored schemas for better AI compatibility. Created documentation guidelines, flattened the geospatial query schema, and simplified the related queries schema. Updated all downstream consumers in tools and UI components to handle the new flattened structures.


PR created automatically by Jules for task 9024415414590610865 started by @ngoiyaeric

Summary by CodeRabbit

  • Documentation

    • Added AI Schema Compatibility Guidelines for structured output handling.
  • Refactor

    • Simplified related queries and suggestions data format for consistency.
    • Improved geospatial location field extraction and coordinate handling.

This commit implements schema flattening and documentation to ensure
compatibility with OpenAI-compatible endpoints (xAI, Gemini, etc.).

Key changes:
1. Created lib/schema/README.md with AI schema compatibility rules.
2. Flattened 'coordinates' into 'latitude' and 'longitude' in
   geospatialQuerySchema and updated geospatial tool.
3. Simplified relatedSchema from array of objects to array of strings
   and updated all UI consumers (SearchRelated, SuggestionsDropdown).
4. Fixed type mismatches and build errors resulting from schema changes.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@vercel

vercel Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
qcx Ready Ready Preview, Comment May 31, 2026 11:01am

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR simplifies two schema structures: related queries items flatten from { query: string }[] to string[], and geospatial coordinates flatten from coordinates: { latitude, longitude } to top-level optional latitude and longitude fields. All downstream consumers—AI prompts, tools, and UI components—are updated to handle the new shapes.

Changes

Data Shape Simplification for Related Queries and Geospatial Input

Layer / File(s) Summary
Related Queries Schema and Model Integration
lib/schema/related.tsx, lib/agents/query-suggestor.tsx, app/actions.tsx
relatedSchema simplifies items to plain string[]. AI model prompt updated to request strings instead of objects with query fields. Type usage in action serialization reflects the flattened shape.
Related Queries Component Updates
components/search-related.tsx, components/search-related-optimized.tsx, components/suggestions-dropdown.tsx
Search and suggestions components updated to filter and map items as strings directly. Click handlers pass query strings instead of accessing nested item.query properties.
Geospatial Schema and Tool Implementation
lib/schema/geospatial.tsx, lib/agents/tools/geospatial.tsx
Geospatial schema flattened to top-level optional latitude/longitude fields. Tool implementation updated to destructure, validate, and use these fields for MCP reverse-search arguments and Google Static Map URL generation.
Schema Compatibility Guidelines
lib/schema/README.md
New documentation outlines compatibility rules for Zod schemas in structured AI output, with pointers to geospatial and related query implementations as examples.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • QueueLab/QCX#620: Concurrent updates to lib/schema/geospatial.tsx and geospatial tool implementation with overlapping coordinate representation changes.
  • QueueLab/QCX#220: Earlier geospatial updates to schema and tool argument handling that establish the baseline for this PR's coordinate flattening.

Suggested labels

Review effort 3/5


🐰 Two shapes flattened with a hop,
Strings now dance where objects stopped,
Lat and longitude take the stage,
Schema simplifies each page,
Cleaner code makes rabbits blessed!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of the PR—flattening schemas for improved AI compatibility—which is consistently reflected across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch flatten-schemas-compatibility-9024415414590610865

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/search-related.tsx`:
- Line 53: The filter currently in the search-related rendering uses query =>
query !== '' which allows null/undefined through; change the filter to remove
all falsy values (e.g., use a truthy check like Boolean(query) or query =>
!!query) so undefined/null are filtered out the same way as
suggestions-dropdown.tsx; update the filter applied to the array being mapped in
components/search-related.tsx (the expression using ?.filter(...)) to use the
truthy check so no blank button is rendered and onClick never submits an empty
related_query.

In `@lib/agents/tools/geospatial.tsx`:
- Around line 244-245: The Gemini branch currently builds its prompt from
"location || query" which breaks reverse geocoding because flattened reverse
inputs only have latitude/longitude; update the code in the GeospatialTool
execution path (look for the destructuring of queryType, includeMap, latitude,
longitude and the mapProvider usage) to detect queryType === 'reverse' and
construct the Gemini input from the coordinate pair (e.g., `${latitude},
${longitude}`) or skip the Gemini prompt branch for reverse queries so the
reverse-geocoding flow is used; apply the same change where the prompt is built
around lines referencing "location || query" (also at the corresponding 262-263
area).

In `@lib/schema/geospatial.tsx`:
- Around line 32-35: Update the latitude/longitude field descriptions in
lib/schema/geospatial.tsx to match the executor behavior in
lib/agents/tools/geospatial.tsx: explicitly state that coordinates are optional
in general but are required when queryType === 'map' (or that 'map' queries
require a full location object), so the schema text for latitude and longitude
reflects the executor's requirement and avoids implying map-centering works with
coordinates-only payloads.
- Around line 32-35: The latitude and longitude fields are independently
optional which allows partial coordinates to pass validation; update the Zod
schema that defines these fields (the object containing latitude and longitude
in lib/schema/geospatial.tsx) to add a cross-field refinement (using .refine or
.superRefine on the parent schema) that enforces either both latitude and
longitude are present or both are absent and returns a clear validation error
when one is provided without the other; reference the latitude and longitude
field names in the refinement to check presence and reject partial coordinate
payloads before tool execution.

In `@lib/schema/README.md`:
- Around line 15-21: Add a blank line immediately after each subheading line
shown in the diff—specifically after the "### Flat Schema with Conditional
Requirements", "### Avoiding Const and Nested GeoJSON", and "### Runtime
Reconstruction" headings in lib/schema/README.md—so that markdownlint rule MD022
is satisfied; edit the file to insert a single empty line following each of
those heading lines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a2f4c0ae-c00b-4ba9-aa88-3464f18d4a2e

📥 Commits

Reviewing files that changed from the base of the PR and between b110b93 and b796222.

📒 Files selected for processing (9)
  • app/actions.tsx
  • components/search-related-optimized.tsx
  • components/search-related.tsx
  • components/suggestions-dropdown.tsx
  • lib/agents/query-suggestor.tsx
  • lib/agents/tools/geospatial.tsx
  • lib/schema/README.md
  • lib/schema/geospatial.tsx
  • lib/schema/related.tsx
📜 Review details
🧰 Additional context used
🪛 markdownlint-cli2 (0.22.1)
lib/schema/README.md

[warning] 15-15: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 18-18: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 21-21: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🔇 Additional comments (5)
components/search-related-optimized.tsx (1)

53-53: Same falsy-filter concern as flagged in components/search-related.tsx (line 53): replace query !== '' with a truthy filter to drop undefined/null during partial streaming.

lib/schema/related.tsx (1)

4-12: LGTM!

lib/agents/query-suggestor.tsx (1)

60-60: LGTM!

app/actions.tsx (1)

32-34: LGTM!

components/suggestions-dropdown.tsx (1)

26-26: LGTM!

Also applies to: 50-50, 95-109

return data?.items
?.filter(item => item?.query !== '')
.map((item, index) => (
?.filter(query => query !== '')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Filter out falsy items, not just empty strings.

query => query !== '' lets undefined/null through, which can occur for not-yet-filled elements during partial streamObject streaming. That renders a blank button whose onClick submits an empty related_query. suggestions-dropdown.tsx already uses a truthy filter; align here for consistency.

🛡️ Proposed fix
-      ?.filter(query => query !== '')
+      ?.filter((query): query is string => Boolean(query))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
?.filter(query => query !== '')
?.filter((query): query is string => Boolean(query))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/search-related.tsx` at line 53, The filter currently in the
search-related rendering uses query => query !== '' which allows null/undefined
through; change the filter to remove all falsy values (e.g., use a truthy check
like Boolean(query) or query => !!query) so undefined/null are filtered out the
same way as suggestions-dropdown.tsx; update the filter applied to the array
being mapped in components/search-related.tsx (the expression using
?.filter(...)) to use the truthy check so no blank button is rendered and
onClick never submits an empty related_query.

Comment on lines +244 to 245
const { queryType, includeMap = true, latitude, longitude } = params;
console.log('[GeospatialTool] Execute called with:', params, 'and map provider:', mapProvider);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle reverse queries in the Gemini branch.

After flattening, reverse inputs only carry latitude/longitude, but this path still builds its prompt from location || query. In Google mode, a reverse request now becomes Find the location for: undefined and falls into the generic error path instead of reverse-geocoding. Build the Gemini input from the coordinate pair for queryType === 'reverse', or bypass this branch for that query type.

Suggested fix
-        const searchText = (params as any).location || (params as any).query;
+        const searchText =
+          queryType === 'reverse' && latitude !== undefined && longitude !== undefined
+            ? `${latitude},${longitude}`
+            : params.location || params.query;
+
+        if (!searchText) {
+          throw new Error(`'${queryType}' query is not supported by the Gemini grounding path`);
+        }
         const prompt = `Find the location for: ${searchText}`;

Also applies to: 262-263

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/agents/tools/geospatial.tsx` around lines 244 - 245, The Gemini branch
currently builds its prompt from "location || query" which breaks reverse
geocoding because flattened reverse inputs only have latitude/longitude; update
the code in the GeospatialTool execution path (look for the destructuring of
queryType, includeMap, latitude, longitude and the mapProvider usage) to detect
queryType === 'reverse' and construct the Gemini input from the coordinate pair
(e.g., `${latitude}, ${longitude}`) or skip the Gemini prompt branch for reverse
queries so the reverse-geocoding flow is used; apply the same change where the
prompt is built around lines referencing "location || query" (also at the
corresponding 262-263 area).

Comment thread lib/schema/geospatial.tsx
Comment on lines +32 to +35
latitude: z.number().min(-90).max(90).optional()
.describe("Latitude for reverse geocoding or map centering (used by 'reverse', optional for 'search')"),
longitude: z.number().min(-180).max(180).optional()
.describe("Longitude for reverse geocoding or map centering (used by 'reverse', optional for 'search')"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep the field descriptions aligned with the executor.

These descriptions mention map centering, but lib/agents/tools/geospatial.tsx still rejects queryType === 'map' unless location is present. That mismatch makes the schema more likely to steer the model into coordinate-only map payloads that fail at runtime.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/schema/geospatial.tsx` around lines 32 - 35, Update the
latitude/longitude field descriptions in lib/schema/geospatial.tsx to match the
executor behavior in lib/agents/tools/geospatial.tsx: explicitly state that
coordinates are optional in general but are required when queryType === 'map'
(or that 'map' queries require a full location object), so the schema text for
latitude and longitude reflects the executor's requirement and avoids implying
map-centering works with coordinates-only payloads.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Require latitude and longitude together.

Flattening made the two coordinate fields independently optional, so malformed payloads like { queryType: 'reverse', latitude: 40.7 } now pass schema validation and only fail later in lib/agents/tools/geospatial.tsx. Add a cross-field refinement so partial coordinates are rejected before tool execution.

Suggested fix
 export const geospatialQuerySchema = z.object({
   queryType: z.enum(['search', 'geocode', 'reverse', 'directions', 'distance', 'map'])
@@
   includeMap: z.boolean()
     .optional()
     .default(true)
     .describe("Whether to include a map preview/URL in the response"),
-});
+}).superRefine(({ latitude, longitude }, ctx) => {
+  if ((latitude === undefined) !== (longitude === undefined)) {
+    ctx.addIssue({
+      code: z.ZodIssueCode.custom,
+      path: [latitude === undefined ? 'latitude' : 'longitude'],
+      message: 'latitude and longitude must be provided together',
+    });
+  }
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
latitude: z.number().min(-90).max(90).optional()
.describe("Latitude for reverse geocoding or map centering (used by 'reverse', optional for 'search')"),
longitude: z.number().min(-180).max(180).optional()
.describe("Longitude for reverse geocoding or map centering (used by 'reverse', optional for 'search')"),
export const geospatialQuerySchema = z.object({
queryType: z.enum(['search', 'geocode', 'reverse', 'directions', 'distance', 'map'])
.describe("Type of geospatial query"),
latitude: z.number().min(-90).max(90).optional()
.describe("Latitude for reverse geocoding or map centering (used by 'reverse', optional for 'search')"),
longitude: z.number().min(-180).max(180).optional()
.describe("Longitude for reverse geocoding or map centering (used by 'reverse', optional for 'search')"),
includeMap: z.boolean()
.optional()
.default(true)
.describe("Whether to include a map preview/URL in the response"),
}).superRefine(({ latitude, longitude }, ctx) => {
if ((latitude === undefined) !== (longitude === undefined)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: [latitude === undefined ? 'latitude' : 'longitude'],
message: 'latitude and longitude must be provided together',
});
}
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/schema/geospatial.tsx` around lines 32 - 35, The latitude and longitude
fields are independently optional which allows partial coordinates to pass
validation; update the Zod schema that defines these fields (the object
containing latitude and longitude in lib/schema/geospatial.tsx) to add a
cross-field refinement (using .refine or .superRefine on the parent schema) that
enforces either both latitude and longitude are present or both are absent and
returns a clear validation error when one is provided without the other;
reference the latitude and longitude field names in the refinement to check
presence and reject partial coordinate payloads before tool execution.

Comment thread lib/schema/README.md
Comment on lines +15 to +21
### Flat Schema with Conditional Requirements
See `lib/schema/geospatial.tsx` for how `queryType` guides the use of other optional top-level fields.

### Avoiding Const and Nested GeoJSON
See `lib/schema/resolution-search.ts` for a flattened GeoJSON structure that avoids `z.literal()` and `z.any()`.

### Runtime Reconstruction

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add missing blank lines after subheadings (MD022).

markdownlint flags missing blank lines below headings at Line 15, Line 18, and Line 21. This can cause lint failures in CI/doc checks.

Proposed fix
 ### Flat Schema with Conditional Requirements
+
 See `lib/schema/geospatial.tsx` for how `queryType` guides the use of other optional top-level fields.
 
 ### Avoiding Const and Nested GeoJSON
+
 See `lib/schema/resolution-search.ts` for a flattened GeoJSON structure that avoids `z.literal()` and `z.any()`.
 
 ### Runtime Reconstruction
+
 See `app/actions.tsx` (specifically the `processResolutionSearch` function) for how flattened features are reconstructed into a standard `FeatureCollection`.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 15-15: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 18-18: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 21-21: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/schema/README.md` around lines 15 - 21, Add a blank line immediately
after each subheading line shown in the diff—specifically after the "### Flat
Schema with Conditional Requirements", "### Avoiding Const and Nested GeoJSON",
and "### Runtime Reconstruction" headings in lib/schema/README.md—so that
markdownlint rule MD022 is satisfied; edit the file to insert a single empty
line following each of those heading lines.

@ngoiyaeric ngoiyaeric closed this Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants