-
-
Couldn't load subscription status.
- Fork 6
feat: Add drawing tool #213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c9a013f
cbfe14d
e20012d
9eb8ec3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,17 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| import { drawingSchema } from '@/lib/schema/drawing'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import { createStreamableUI } from 'ai/rsc'; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| export const drawingTool = ({ | ||||||||||||||||||||||||||||||||||||||||||||||||
| uiStream, | ||||||||||||||||||||||||||||||||||||||||||||||||
| }: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| uiStream: ReturnType<typeof createStreamableUI>; | ||||||||||||||||||||||||||||||||||||||||||||||||
| }) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||
| description: 'Draw GeoJSON features on the map. Use this tool to draw points, lines, and polygons.', | ||||||||||||||||||||||||||||||||||||||||||||||||
| parameters: drawingSchema, | ||||||||||||||||||||||||||||||||||||||||||||||||
| execute: async ({ geojson }: { geojson: any }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'DRAWING', | ||||||||||||||||||||||||||||||||||||||||||||||||
| geojson, | ||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+9
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Type the execute input and validate at runtime as a safety net parameters uses drawingSchema for LLM tool calling, but adding an explicit runtime parse inside execute makes the tool robust against malformed inputs and keeps TypeScript happy. Apply this diff: -import { drawingSchema } from '@/lib/schema/drawing';
+import { drawingSchema } from '@/lib/schema/drawing';
+import type { z } from 'zod';
@@
- execute: async ({ geojson }: { geojson: any }) => {
- return {
- type: 'DRAWING',
- geojson,
- };
- },
+ execute: async (input: z.infer<typeof drawingSchema>) => {
+ // Safety net: validate again at runtime
+ const parsed = drawingSchema.parse(input);
+ return {
+ type: 'DRAWING' as const,
+ geojson: parsed.geojson,
+ };
+ },If you prefer a softer failure mode, use safeParse and return an error to the UI stream instead of throwing. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| import { z } from 'zod'; | ||
|
|
||
| // GeoJSON Position (longitude, latitude) | ||
| const PositionSchema = z.tuple([z.number(), z.number()]); | ||
|
|
||
| // Geometry Schemas | ||
| const PointSchema = z.object({ | ||
| type: z.literal('Point'), | ||
| coordinates: PositionSchema, | ||
| }); | ||
|
|
||
| const MultiPointSchema = z.object({ | ||
| type: z.literal('MultiPoint'), | ||
| coordinates: z.array(PositionSchema), | ||
| }); | ||
|
|
||
| const LineStringSchema = z.object({ | ||
| type: z.literal('LineString'), | ||
| coordinates: z.array(PositionSchema).min(2, { message: "LineString must have at least two positions." }), | ||
| }); | ||
|
|
||
| const MultiLineStringSchema = z.object({ | ||
| type: z.literal('MultiLineString'), | ||
| coordinates: z.array(z.array(PositionSchema).min(2)), | ||
| }); | ||
|
|
||
| // A LinearRing is a closed LineString with four or more positions. | ||
| const LinearRingSchema = z.array(PositionSchema).min(4, { message: "LinearRing must have at least four positions." }) | ||
| .refine(positions => { | ||
| const first = positions[0]; | ||
| const last = positions[positions.length - 1]; | ||
| return first[0] === last[0] && first[1] === last[1]; | ||
| }, { message: "The first and last positions of a LinearRing must be identical." }); | ||
|
|
||
| const PolygonSchema = z.object({ | ||
| type: z.literal('Polygon'), | ||
| coordinates: z.array(LinearRingSchema), | ||
| }); | ||
|
|
||
| const MultiPolygonSchema = z.object({ | ||
| type: z.literal('MultiPolygon'), | ||
| coordinates: z.array(z.array(LinearRingSchema)), | ||
| }); | ||
|
|
||
| const GeometrySchema = z.union([ | ||
| PointSchema, | ||
| MultiPointSchema, | ||
| LineStringSchema, | ||
| MultiLineStringSchema, | ||
| PolygonSchema, | ||
| MultiPolygonSchema, | ||
| ]); | ||
|
|
||
| // Feature and FeatureCollection Schemas | ||
| const FeatureSchema = z.object({ | ||
| type: z.literal('Feature'), | ||
| geometry: GeometrySchema, | ||
| properties: z.record(z.string(), z.any()).nullable(), | ||
| }); | ||
|
|
||
| const FeatureCollectionSchema = z.object({ | ||
| type: z.literal('FeatureCollection'), | ||
| features: z.array(FeatureSchema), | ||
| }); | ||
|
|
||
| // The main schema for the drawing tool | ||
| export const drawingSchema = z.object({ | ||
| geojson: FeatureCollectionSchema.describe("A valid GeoJSON FeatureCollection object to be drawn on the map."), | ||
| }); | ||
|
|
||
| export type Drawing = z.infer<typeof drawingSchema>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not commit .env with credentials; replace with .env.example and ignore .env
Committing environment files with secrets (even local defaults) is a security risk and makes accidental leaks likely. Also, dotenv-linter flags the quoted value. Recommended:
Proposed changes:
Delete the committed secret (leaves the file empty if you don’t remove the file outright):
-DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"Add a .env.example (outside this hunk) with placeholders:
And ignore .env (outside this hunk):
If, for some reason, this file must stay, at minimum remove quotes to satisfy dotenv-linter:
I can open a follow-up PR to codify this workflow (add .env.example, update docs).
📝 Committable suggestion
🧰 Tools
🪛 dotenv-linter (3.3.0)
[warning] 1-1: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
🤖 Prompt for AI Agents