@tomtom-org/maps-sdk-plugin-agent-toolkit 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,520 @@
1
+ # TomTom Maps SDK - Agent Toolkit Plugin
2
+
3
+ A headless conversational agent that gives Large Language Models tool-based control over a [TomTom Map](https://developer.tomtom.com/) and TomTom location services, powered by [Vercel AI SDK v6](https://ai-sdk.dev/).
4
+
5
+ No UI is included — bring your own chat interface. No LLM provider is bundled — supply any AI SDK-compatible model.
6
+
7
+ > **Full documentation** — guides, architecture diagrams, and tutorials are available at [docs.tomtom.com](https://docs.tomtom.com/maps-sdk-js/guides/plugins/ai-agent).
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pnpm add @tomtom-org/maps-sdk @tomtom-org/maps-sdk-plugin-agent-toolkit ai zod maplibre-gl
13
+ ```
14
+
15
+ Install at least one AI SDK provider:
16
+
17
+ ```bash
18
+ # Pick one (or more)
19
+ pnpm add @ai-sdk/openai
20
+ pnpm add @ai-sdk/anthropic
21
+ pnpm add @ai-sdk/azure
22
+ ```
23
+
24
+ ## Quick start
25
+
26
+ ```typescript
27
+ import { TomTomMap } from '@tomtom-org/maps-sdk/map';
28
+ import { createMapAgent } from '@tomtom-org/maps-sdk-plugin-agent-toolkit';
29
+ import { openai } from '@ai-sdk/openai';
30
+
31
+ // 1. Create a TomTom map
32
+ const map = new TomTomMap({
33
+ mapLibre: { container: 'map', center: [4.9, 52.4], zoom: 10 },
34
+ });
35
+
36
+ // 2. Create the agent
37
+ const agent = createMapAgent(map, {
38
+ model: openai('gpt-4o'),
39
+ });
40
+
41
+ // 3. Send a message
42
+ const result = await agent.generate({
43
+ messages: [{ role: 'user', content: 'Find coffee shops near Dam Square, Amsterdam' }],
44
+ });
45
+
46
+ console.log(result.text);
47
+ ```
48
+
49
+ ### With React (`useChat`)
50
+
51
+ ```typescript
52
+ import { DirectChatTransport } from 'ai/react';
53
+ import { useChat } from 'ai/react';
54
+
55
+ const agent = createMapAgent(map, { model: openai('gpt-4o') });
56
+
57
+ function ChatPanel() {
58
+ const { messages, sendMessage } = useChat({
59
+ transport: new DirectChatTransport({ agent }),
60
+ });
61
+
62
+ return (
63
+ <div>
64
+ {messages.map((m) => (
65
+ <div key={m.id}>{m.content}</div>
66
+ ))}
67
+ <input onKeyDown={(e) => e.key === 'Enter' && sendMessage(e.target.value)} />
68
+ </div>
69
+ );
70
+ }
71
+ ```
72
+
73
+ See [`examples/map-chat-agent`](../../examples/map-chat-agent) for a full working example.
74
+
75
+ ## Tools reference
76
+
77
+ The plugin ships 53 tools organized into five categories. All tools are included by default and can be individually removed or replaced via the [`tools` option](#composing-tool-sets).
78
+
79
+ ### Service tools — fetch data from TomTom APIs
80
+
81
+ | Tool | Description |
82
+ |---|---|
83
+ | `locatePlace` | Resolve a location string (landmark, city, address) to a place; optionally stage as a waypoint |
84
+ | `reverseGeocode` | Convert `[longitude, latitude]` coordinates to an address |
85
+ | `discoverPlaces` | Search for places by text query or POI category within an area |
86
+ | `setRouteLocations` | Set waypoints by resolving location queries and calculate a route |
87
+ | `setRouteParameters` | Configure route options (alternatives, avoidances, departure time) and recalculate |
88
+ | `addStopToRoute` | Insert a stop at the optimal position in the current route |
89
+ | `removeStopFromRoute` | Remove a stop by index from the current route |
90
+ | `getPOICategoryCodes` | Look up TomTom POI category codes from natural-language names |
91
+ | `getTrafficIncidents` | Fetch traffic incidents by bounding box or IDs |
92
+ | `getTrafficAreaAnalytics` | Fetch historical traffic analytics (speed, congestion, travel time) for an area |
93
+ | `queryTrafficAnalytics` | Query cached analytics data or check what is currently displayed |
94
+ | `searchAlongRoute` | Search for POIs along a calculated route, ranked by detour cost |
95
+ | `findReachableArea` | Calculate isochrone/isodistance polygons from a point |
96
+
97
+ ### TomTom Map tools — render and inspect map state
98
+
99
+ | Tool | Description |
100
+ |---|---|
101
+ | `showPlaces` | Display the most recent place results as markers on the map |
102
+ | `showRoute` | Render the most recently calculated route and fit the camera |
103
+ | `showTrafficAreaAnalytics` | Visualize traffic analytics as hexgrid, heatmap, or tiles |
104
+ | `showWaypoints` | Display staged waypoint markers without the route line |
105
+ | `clearMap` | Remove displayed places, routes, or all features |
106
+ | `getShownPlaces` | List places currently displayed on the map |
107
+ | `getShownRoutes` | List routes currently rendered on the map |
108
+ | `getShownWaypoints` | List waypoint markers currently shown |
109
+ | `getShownRouteSections` | List sections (toll, country, motorway, etc.) of the shown route |
110
+ | `getShownRouteTrafficIncidents` | List traffic incidents overlaid on the shown route |
111
+ | `getShownIncidents` | List real-time incidents visible in the current viewport |
112
+ | `getStandardMapStyles` | List available standard map style presets |
113
+ | `setMapStandardStyle` | Switch map style (light, dark, satellite, driving, etc.) |
114
+ | `setRouteTheme` | Color the route line and waypoint icons |
115
+ | `setLanguage` | Change the language for map labels and API responses |
116
+ | `toggleBaseMapLayerGroups` | Show/hide named layer groups (buildings3D, roadLabels, water, etc.) |
117
+ | `togglePOIs` | Show/hide built-in map POI icons with optional category filtering |
118
+ | `toggleTrafficFlow` | Toggle real-time traffic flow overlay |
119
+ | `toggleTrafficIncidents` | Toggle real-time traffic incident markers |
120
+ | `fitRouteSection` | Fit the camera to a specific route section by type and ID |
121
+
122
+ ### MapLibre tools — direct map engine access
123
+
124
+ | Tool | Description |
125
+ |---|---|
126
+ | `flyTo` | Move the camera to a position or bounding box |
127
+ | `getViewport` | Get current map center, zoom, and bounding box |
128
+ | `getMapStyleLayers` | List MapLibre layer IDs with their paint/layout properties |
129
+ | `setLayoutProperties` | Set MapLibre layout properties on named layers |
130
+ | `setPaintProperties` | Set MapLibre paint properties (colors, widths, opacity) on named layers |
131
+ | `setPitchBearing` | Tilt (pitch) and/or rotate (bearing) the camera |
132
+ | `zoomInOrOut` | Adjust the zoom level by a delta |
133
+ | `executeMaplibreCode` | Execute arbitrary MapLibre JS code for maximum flexibility |
134
+
135
+ ### State tools — recall session history
136
+
137
+ | Tool | Description |
138
+ |---|---|
139
+ | `recallPlaces` | Retrieve the history of place lookups from this session |
140
+ | `recallRoutes` | Retrieve previously calculated routes from this session |
141
+ | `recallRanges` | Retrieve stored reachable range results |
142
+ | `getCurrentWaypoints` | Get the staged waypoint slots (origin, stops, destination) |
143
+
144
+ ### Utility tools — pure computation helpers
145
+
146
+ | Tool | Description |
147
+ |---|---|
148
+ | `calculateBBox` | Compute a bounding box from GeoJSON features or tool results |
149
+ | `formatDistance` | Format meters into a human-readable string (e.g. "2.5 km") |
150
+ | `formatDuration` | Format seconds into a human-readable string (e.g. "1 h 30 min") |
151
+ | `getCurrentLocation` | Get the user's physical GPS location from the browser |
152
+ | `getRouteProgress` | Get the position along the route at a given time or distance |
153
+ | `getSectionProgress` | Get the distance and travel time for route sections by type |
154
+ | `getSectionBBox` | Get the bounding box of a specific route section |
155
+ | `help` | List available capabilities in summary or searchable detail mode |
156
+
157
+ ## Customization
158
+
159
+ ### `createMapAgent` options
160
+
161
+ ```typescript
162
+ // Define custom state by extending ToolState
163
+ interface MyState extends ToolState {
164
+ fleet: FleetState;
165
+ }
166
+
167
+ const agent = createMapAgent<MyState>(map, {
168
+ // Required: AI SDK language model instance
169
+ model: openai('gpt-4o'),
170
+
171
+ // Include built-in defaults (default: true). Set false for custom-only.
172
+ includeDefaultTools: true,
173
+
174
+ // Add, replace, or remove tools (merged with defaults)
175
+ tools: { myCustomTool: weatherTool },
176
+
177
+ // Append to the built-in system prompt
178
+ systemPromptSuffix: 'Always respond in Spanish. Use metric units.',
179
+
180
+ // Or replace it entirely (systemPromptSuffix is ignored when this is set)
181
+ systemPrompt: 'You are a delivery route planner...',
182
+
183
+ // Custom state slices — only custom fields needed, built-in slices are created automatically
184
+ state: { fleet: new FleetState() },
185
+
186
+ // Intent classifier: omit for default LLM-based, false to disable
187
+ classifier: createDefaultClassifier({ model: openai('gpt-4o-mini') }),
188
+
189
+ // Observe classifier decisions
190
+ onClassify: (result) => console.log('Selected tools:', result?.activeToolNames),
191
+
192
+ // Custom prepareStep hook (composed with internal classification)
193
+ prepareStep: async (stepInfo) => ({ toolChoice: 'auto' }),
194
+
195
+ // Disable structured output schemas for providers that don't support them
196
+ outputSchemas: false,
197
+
198
+ // Max tool-loop iterations (default: 10)
199
+ maxSteps: 15,
200
+ });
201
+ ```
202
+
203
+ ### Composing tool sets
204
+
205
+ The `tools` option is merged with the built-in defaults. Use `false` to exclude a tool.
206
+
207
+ ```typescript
208
+ import { createMapAgent } from '@tomtom-org/maps-sdk-plugin-agent-toolkit';
209
+
210
+ // Add custom tools (defaults included automatically)
211
+ createMapAgent(map, { model, tools: { getWeather: myWeatherTool } });
212
+
213
+ // Remove specific defaults
214
+ createMapAgent(map, { model, tools: { setMapStandardStyle: false, setLanguage: false } });
215
+
216
+ // Replace a default tool (full ToolEntry required)
217
+ createMapAgent(map, { model, tools: { discoverPlaces: myCustomSearchTool } });
218
+
219
+ // Mix: add, remove, and replace in one call
220
+ createMapAgent(map, {
221
+ model,
222
+ tools: {
223
+ setLanguage: false,
224
+ discoverPlaces: myCustomSearchTool,
225
+ getWeather: myWeatherTool,
226
+ },
227
+ });
228
+
229
+ // No defaults — only custom tools
230
+ createMapAgent(map, { model, includeDefaultTools: false, tools: { myTool } });
231
+ ```
232
+
233
+ ### Defining custom tools
234
+
235
+ Use `satisfies` for type-safe custom tool definitions:
236
+
237
+ ```typescript
238
+ import { type ToolEntry, createMapAgent } from '@tomtom-org/maps-sdk-plugin-agent-toolkit';
239
+ import { z } from 'zod';
240
+
241
+ const fleetTools = {
242
+ trackVehicle: {
243
+ description: 'Track a vehicle by ID on the map.',
244
+ inputSchema: z.object({ vehicleId: z.string() }),
245
+ execute: async ({ vehicleId }, state) => {
246
+ // state is the full ToolState — access state.places, state.routing, etc.
247
+ const position = await fetchVehiclePosition(vehicleId);
248
+ return { vehicleId, position };
249
+ },
250
+ tags: ['fleet'],
251
+ relatedTools: ['showPlaces'],
252
+ },
253
+ } satisfies Record<string, ToolEntry>;
254
+
255
+ // Pass directly — merged with defaults automatically
256
+ createMapAgent(map, { model, tools: fleetTools });
257
+ ```
258
+
259
+ ### Tool entry shape
260
+
261
+ Every tool (built-in or custom) follows the `ToolEntry` interface:
262
+
263
+ ```typescript
264
+ type ToolEntry<S extends ToolState = ToolState> = {
265
+ description: string; // Operational contract for the LLM
266
+ inputSchema: z.ZodType; // Zod schema for input validation
267
+ outputSchema?: z.ZodType; // Optional structured output schema
268
+ execute: (input, state: S) => Promise<any>;
269
+
270
+ // Classifier metadata (optional)
271
+ classificationPrompt?: string; // One-liner for the intent classifier
272
+ tags?: string[]; // Category tags (e.g. 'location', 'route')
273
+ examples?: string[]; // Code examples
274
+ examplePrompts?: string[]; // Natural language prompt examples
275
+ relatedTools?: string[]; // Tools often used together
276
+ dependsOn?: string[]; // Tools that must run before this one
277
+ };
278
+ ```
279
+
280
+ ## State management
281
+
282
+ `ToolState` is organized by feature area. Each slice manages lazy-initialized map modules and an append-only history of results produced during the session. Built-in slices: `PlacesState`, `RoutingState`, `BaseMapState`, `TrafficState`, `RangeState`, `MapPOIsState`.
283
+
284
+ Create state manually for advanced scenarios:
285
+
286
+ ```typescript
287
+ import { createToolState, type ToolState } from '@tomtom-org/maps-sdk-plugin-agent-toolkit';
288
+
289
+ // Base state
290
+ const state = createToolState(map);
291
+
292
+ // With custom slices
293
+ const state = createToolState(map, {
294
+ fleet: new FleetState(),
295
+ weather: new WeatherCache(),
296
+ });
297
+ ```
298
+
299
+ Custom slices are merged alongside built-in state. When using `createMapAgent`, define an interface extending `ToolState` and pass the type parameter — only custom slices need to be provided:
300
+
301
+ ```typescript
302
+ interface MyState extends ToolState {
303
+ fleet: FleetState;
304
+ }
305
+
306
+ const agent = createMapAgent<MyState>(map, {
307
+ model,
308
+ state: { fleet: new FleetState() },
309
+ });
310
+
311
+ agent.state.fleet; // FleetState — custom slice
312
+ agent.state.places; // PlacesState — built-in slice
313
+ ```
314
+
315
+ ## Intent classifier
316
+
317
+ The intent classifier is an optional per-turn optimization that selects which tools the LLM sees, reducing noise and improving accuracy.
318
+
319
+ ```typescript
320
+ import { createMapAgent, createDefaultClassifier } from '@tomtom-org/maps-sdk-plugin-agent-toolkit';
321
+
322
+ // Default: uses the main model for classification
323
+ const agent = createMapAgent(map, { model: openai('gpt-4o') });
324
+
325
+ // Use a cheaper model for classification
326
+ const agent = createMapAgent(map, {
327
+ model: openai('gpt-4o'),
328
+ classifier: createDefaultClassifier({ model: openai('gpt-4o-mini') }),
329
+ });
330
+
331
+ // Disable classification entirely (all tools always visible)
332
+ const agent = createMapAgent(map, {
333
+ model: openai('gpt-4o'),
334
+ classifier: false,
335
+ });
336
+ ```
337
+
338
+ The classifier prompt is built dynamically from each tool's `classificationPrompt` and `relatedTools` metadata, so it stays in sync automatically when tools are added or renamed.
339
+
340
+ ## System prompt
341
+
342
+ The built-in `BASE_SYSTEM_PROMPT` teaches the LLM coordinate conventions, tool execution patterns, location reference rules, and response formatting. You can extend or replace it:
343
+
344
+ ```typescript
345
+ import { createMapAgent, BASE_SYSTEM_PROMPT } from '@tomtom-org/maps-sdk-plugin-agent-toolkit';
346
+
347
+ // Append additional instructions
348
+ const agent = createMapAgent(map, {
349
+ model,
350
+ systemPromptSuffix: 'Always use metric units. Respond in Dutch.',
351
+ });
352
+
353
+ // Full replacement
354
+ const agent = createMapAgent(map, {
355
+ model,
356
+ systemPrompt: BASE_SYSTEM_PROMPT + '\n\nYou are a delivery fleet dispatcher...',
357
+ });
358
+ ```
359
+
360
+ ## Advanced usage
361
+
362
+ ### Manual agent setup
363
+
364
+ Bypass `createMapAgent` for maximum control:
365
+
366
+ ```typescript
367
+ import { ToolLoopAgent, stepCountIs, tool } from 'ai';
368
+ import { createToolState, DEFAULT_TOOLS, BASE_SYSTEM_PROMPT } from '@tomtom-org/maps-sdk-plugin-agent-toolkit';
369
+
370
+ const state = createToolState(map);
371
+ const { help, ...toolEntries } = DEFAULT_TOOLS; // customize as needed
372
+
373
+ // Convert ToolEntry records to AI SDK tools (bind state)
374
+ const tools = Object.fromEntries(
375
+ Object.entries(toolEntries).map(([name, entry]) => [
376
+ name,
377
+ tool({
378
+ description: entry.description,
379
+ inputSchema: entry.inputSchema,
380
+ outputSchema: entry.outputSchema,
381
+ execute: async (input) => entry.execute(input, state),
382
+ }),
383
+ ]),
384
+ );
385
+
386
+ const agent = new ToolLoopAgent({
387
+ model: openai('gpt-4o'),
388
+ tools,
389
+ instructions: BASE_SYSTEM_PROMPT + '\n\nCustom instructions...',
390
+ stopWhen: stepCountIs(15),
391
+ });
392
+ ```
393
+
394
+ ### Wrapping default tools
395
+
396
+ Add logging, analytics, or custom behavior around existing tools:
397
+
398
+ ```typescript
399
+ import { DEFAULT_TOOLS, createMapAgent } from '@tomtom-org/maps-sdk-plugin-agent-toolkit';
400
+
401
+ const wrappedDiscover = {
402
+ ...DEFAULT_TOOLS.discoverPlaces,
403
+ execute: async (input, state) => {
404
+ console.log('[Analytics] Search:', input.query);
405
+ const result = await DEFAULT_TOOLS.discoverPlaces.execute(input, state);
406
+ console.log('[Analytics] Found:', result.count, 'places');
407
+ return result;
408
+ },
409
+ };
410
+
411
+ createMapAgent(map, { model, tools: { discoverPlaces: wrappedDiscover } });
412
+ ```
413
+
414
+ ### Common configuration patterns
415
+
416
+ **Search-only agent (no routing):**
417
+
418
+ ```typescript
419
+ const agent = createMapAgent(map, {
420
+ model,
421
+ tools: {
422
+ setRouteLocations: false,
423
+ setRouteParameters: false,
424
+ addStopToRoute: false,
425
+ removeStopFromRoute: false,
426
+ showRoute: false,
427
+ searchAlongRoute: false,
428
+ },
429
+ });
430
+ ```
431
+
432
+ **Locked visual appearance:**
433
+
434
+ ```typescript
435
+ const agent = createMapAgent(map, {
436
+ model,
437
+ tools: {
438
+ setMapStandardStyle: false,
439
+ setLanguage: false,
440
+ setLayoutProperties: false,
441
+ setPaintProperties: false,
442
+ toggleBaseMapLayerGroups: false,
443
+ },
444
+ });
445
+ ```
446
+
447
+ ### The `MapAgentInstance`
448
+
449
+ `createMapAgent` returns a `ToolLoopAgent` (usable directly with `DirectChatTransport`) with two extra properties:
450
+
451
+ ```typescript
452
+ const agent = createMapAgent(map, { model });
453
+
454
+ agent.state; // Live state — typed as CS when custom state is provided, ToolState otherwise
455
+ agent.destroy(); // Reset all state slices (call on unmount)
456
+ ```
457
+
458
+ ## Public API exports
459
+
460
+ ```typescript
461
+ // Main factory
462
+ export { createMapAgent } from './create-map-agent';
463
+
464
+ // State
465
+ export { createToolState, type StateSlice } from './state';
466
+
467
+ // System prompt
468
+ export { BASE_SYSTEM_PROMPT, buildSystemPrompt } from './system-prompt';
469
+
470
+ // Tool registry
471
+ export { DEFAULT_TOOLS, TOOL_NAMES, type ToolName } from './tools';
472
+
473
+ // Tool composition
474
+ export { resolveTools } from './resolve-tools';
475
+
476
+ // Classifier
477
+ export {
478
+ type ClassificationResult,
479
+ classifyUserIntent,
480
+ createDefaultClassifier,
481
+ extractLastUserText,
482
+ } from './utils/intent-classifier';
483
+
484
+ // Types
485
+ export type {
486
+ Classifier,
487
+ ClassifierContext,
488
+ MapAgentInstance,
489
+ MapAgentOptions,
490
+ ToolEntry,
491
+ ToolMetadata,
492
+ ToolNameHint,
493
+ ToolState,
494
+ } from './types';
495
+ ```
496
+
497
+ ## Dependencies
498
+
499
+ | Type | Package | Purpose |
500
+ |---|---|---|
501
+ | Peer | `@tomtom-org/maps-sdk` | TomTom Maps SDK (types, services, map modules) |
502
+ | Peer | `ai@^6.0.0` | Vercel AI SDK (ToolLoopAgent, tool types) |
503
+ | Peer | `zod@^3.23.0` | Schema validation |
504
+ | Peer | `maplibre-gl@^5.0.0` | Map rendering engine |
505
+
506
+ ## Design principles
507
+
508
+ 1. **Client-side only** — uses AI SDK's `DirectChatTransport` + `ToolLoopAgent`. The consumer provides the model; no server infrastructure is required.
509
+ 2. **No bundled LLM provider** — supply any AI SDK-compatible `LanguageModel`. Keeps the package provider-agnostic.
510
+ 3. **Token-efficient** — all service responses are summarized before reaching the LLM. Full GeoJSON stays in `ToolState`.
511
+ 4. **Lazy module initialization** — map modules are instantiated on first use and cached in state.
512
+ 5. **Coordinate convention** — always `[longitude, latitude]` per GeoJSON standard, enforced throughout.
513
+ 6. **Task-oriented tools** — tool boundaries follow user tasks, not SDK API surface. See [ENGINEERING-GUIDELINES.md](./ENGINEERING-GUIDELINES.md).
514
+
515
+ ## References
516
+
517
+ - [AI SDK v6 documentation](https://ai-sdk.dev/)
518
+ - [TomTom Maps SDK documentation](https://developer.tomtom.com/)
519
+ - [Engineering guidelines](./ENGINEERING-GUIDELINES.md) — tool design standards, state management, system prompt structure
520
+ - [Example application](../../examples/map-chat-agent) — full chat interface implementation
@@ -0,0 +1 @@
1
+ No third parties dependencies