Skip to content

Commit 486d41d

Browse files
committed
Add support for interactive maps using Leaflet.js and update documentation
1 parent 8dbca7c commit 486d41d

File tree

6 files changed

+387
-11
lines changed

6 files changed

+387
-11
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ The tester includes automatic cleanup with shutdown handlers to properly close M
215215
- **Rate Limiting** - Built-in rate limiting for API usage control
216216
- **Metrics & Observability** - Comprehensive runtime metrics for monitoring and cost tracking
217217
- **Enhanced Visual Output** - UTF-8 box-drawing characters, ANSI color codes, and emoji for rich terminal displays (`useascii=true`)
218+
- **Interactive Maps** - Ask the agent to return Leaflet map snippets for geographic prompts, rendered directly in the console transcript and web UI (`usemaps=true`)
218219

219220
## Documentation
220221

@@ -265,6 +266,7 @@ Mini-A ships with complementary components:
265266
| `chatbotmode` | Conversational assistant mode | `false` |
266267
| `useplanning` | Enable task planning workflow with validation and dynamic replanning | `false` |
267268
| `useascii` | Enable enhanced UTF-8/ANSI visual output with colors and emojis | `false` |
269+
| `usemaps` | Encourage Leaflet-based interactive map outputs for geographic data | `false` |
268270
| `mode` | Apply preset from `mini-a-modes.yaml` or `~/.openaf-mini-a_modes.yaml` | - |
269271
| `modelman` | Launch the interactive model definitions manager | `false` |
270272
| `maxsteps` | Maximum steps before forcing final answer | `15` |

USAGE.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ Set `OAF_MINI_A_MODE=<name>` to pick a default preset when you do not supply `mo
179179
- **`shellutils`** – Shell plus the Mini Utils Tool MCP utilities (`useutils=true usetools=true`).
180180
- **`chatbot`** – Lightweight conversational mode (`chatbotmode=true`).
181181
- **`web`** – Browser UI with tool registration (`usetools=true`).
182-
- **`webfull`** – Web UI with history, attachments, diagrams, charts, ASCII sketches, and planning enabled (`usetools=true usediagrams=true usecharts=true useascii=true usehistory=true useattach=true historykeep=true useplanning=true`).
182+
- **`webfull`** – Web UI with history, attachments, diagrams, charts, and ASCII sketches enabled (`usetools=true usediagrams=true usecharts=true useascii=true usehistory=true useattach=true historykeep=true useplanning=true`). Add `usemaps=true` if you also want interactive map guidance in this preset.
183183

184184
### Creating Custom Presets
185185

@@ -254,7 +254,7 @@ Optional flags when starting the server:
254254

255255
- `showexecs=true` to show executed commands in the interaction stream
256256
- `logpromptheaders=origin,referer` to log selected incoming headers for debugging
257-
- `usediagrams=false` / `usecharts=false` / `useascii=false` to disable Mermaid, Chart.js, or ASCII sketch guidance when running headless
257+
- `usediagrams=false` / `usecharts=false` / `useascii=false` / `usemaps=false` to disable Mermaid, Chart.js, ASCII sketch, or Leaflet map guidance when running headless
258258
- `usehistory=true` to expose the history side panel and persist conversations on disk
259259
- `historypath=/tmp/mini-a-history` / `historyretention=600` / `historykeep=true` to manage history storage (see comments in `mini-a-web.yaml`)
260260
- `historys3bucket=my-bucket historys3prefix=sessions/` to mirror history JSON files to S3 (supports `historys3url`, `historys3accesskey`, `historys3secret`, `historys3region`, `historys3useversion1`, `historys3ignorecertcheck`). History is uploaded at optimized checkpoints: immediately after user prompts and when final answers are provided, rather than on every interaction event
@@ -332,7 +332,7 @@ docker run -d --rm \
332332
-e OAF_MODEL="(type: openai, key: '...', url: 'https://api.groq.com/openai', model: openai/gpt-oss-20b, timeout: 900000, temperature: 0)" \
333333
-p 12345:12345 \
334334
openaf/oaf:edge \
335-
onport=12345 usecharts=true usediagrams=true usetools=true mcpproxy=true
335+
onport=12345 usecharts=true usediagrams=true usemaps=true usetools=true mcpproxy=true
336336
```
337337

338338
**Web interface with history and attachments:**
@@ -346,7 +346,7 @@ docker run -d --rm \
346346
openaf/oaf:edge \
347347
onport=12345 chatbotmode=true \
348348
usehistory=true historykeep=true historypath=/tmp/history \
349-
useattach=true usediagrams=true usecharts=true
349+
useattach=true usediagrams=true usecharts=true usemaps=true
350350
```
351351

352352
#### Pattern 3: Goal-Based Execution
@@ -590,6 +590,7 @@ Only when every stage returns an empty list (or errors) does Mini-A log the issu
590590
- **ANSI color codes**: Semantic highlighting for errors (red), success (green), warnings (yellow), info (blue/cyan), with support for bold, underline, backgrounds, and combined styles. Colors are applied outside markdown code blocks for proper terminal rendering
591591
- **Markdown tables**: Preferred format for tabular data with colored cell content for enhanced readability
592592
- **Progress indicators**: Block characters (█▓▒░), fractions (▏▎▍▌▋▊▉), spinners (⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏), and percentage displays with color gradients
593+
- **`usemaps`** (boolean, default: false): Prime the model to emit ```leaflet``` blocks that describe interactive maps (center coordinates, zoom, markers, layers). The console transcript preserves the fenced JSON, and the web UI auto-renders the configuration with Leaflet tiles, themed popups, and transparent markers.
593594

594595
#### Libraries and Extensions
595596
- **`libs`** (string): Comma-separated list of additional OpenAF libraries to load
@@ -607,7 +608,7 @@ Only when every stage returns an empty list (or errors) does Mini-A log the issu
607608
- `shellutils` – Adds the Mini File Tool helpers as an MCP (`useutils=true usetools=true`) exposing `init`, `filesystemQuery`, and `filesystemModify` actions.
608609
- `chatbot` – Switches to conversational mode (`chatbotmode=true`).
609610
- `web` – Optimizes for the browser UI with MCP tools registered (`usetools=true`).
610-
- `webfull` – Turns on diagrams, charts, ASCII sketches, attachments, history retention, and planning for the web UI (`usetools=true usediagrams=true usecharts=true useascii=true usehistory=true useattach=true historykeep=true useplanning=true`).
611+
- `webfull` – Turns on diagrams, charts, ASCII sketches, attachments, history retention, and planning for the web UI (`usetools=true usediagrams=true usecharts=true useascii=true usehistory=true useattach=true historykeep=true useplanning=true`). Add `usemaps=true` when you also want interactive maps baked into this preset.
611612

612613
Extend or override these presets by editing the YAML file—Mini-A reloads it on each run.
613614

mini-a-modes.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ modes:
8383
params :
8484
usediagrams: true
8585
usecharts : true
86+
usemaps : true
8687
useascii : false
8788
usetools : true
8889
usehistory : true

mini-a.js

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -469,17 +469,19 @@ MiniA.buildVisualKnowledge = function(options) {
469469
var useDiagrams = _$(toBoolean(options.useDiagrams), "options.useDiagrams").isBoolean().default(false)
470470
var useCharts = _$(toBoolean(options.useCharts), "options.useCharts").isBoolean().default(false)
471471
var useAscii = _$(toBoolean(options.useAscii), "options.useAscii").isBoolean().default(false)
472+
var useMaps = _$(toBoolean(options.useMaps), "options.useMaps").isBoolean().default(false)
472473

473-
if (!useDiagrams && !useCharts && !useAscii) return ""
474+
if (!useDiagrams && !useCharts && !useAscii && !useMaps) return ""
474475

475476
var existingKnowledge = isString(options.existingKnowledge) ? options.existingKnowledge : ""
476477
// Check if visual guidance already exists AND matches current flags
477478
if (existingKnowledge.indexOf("Visual output guidance (concise):") >= 0) {
478479
var hasDiagrams = existingKnowledge.indexOf("Diagrams:") >= 0
479480
var hasCharts = existingKnowledge.indexOf("Charts (strict format):") >= 0
480481
var hasAscii = existingKnowledge.indexOf("ASCII/UTF-8 visuals") >= 0
482+
var hasMaps = existingKnowledge.indexOf("Interactive Maps:") >= 0
481483
// Only return early if existing guidance matches current flags
482-
if (useDiagrams === hasDiagrams && useCharts === hasCharts && useAscii === hasAscii) {
484+
if (useDiagrams === hasDiagrams && useCharts === hasCharts && useAscii === hasAscii && useMaps === hasMaps) {
483485
return ""
484486
}
485487
}
@@ -490,7 +492,7 @@ MiniA.buildVisualKnowledge = function(options) {
490492
"Visual output guidance (concise):\n\n" +
491493
"- Default to including a diagram, chart, or UTF-8/ANSI visual whenever structure, flow, hierarchy, metrics, or comparisons are involved.\n" +
492494
"- Always pair the visual with a short caption (1-2 sentences) summarizing the insight.\n" +
493-
"- In your explanatory text and captions, refer only to the visual type (e.g., 'diagram', 'chart', 'table') without mentioning the technical implementation (Mermaid, Chart.js, ANSI codes, etc.)."
495+
"- In your explanatory text and captions, refer only to the visual type (e.g., 'diagram', 'chart', 'table', 'map') without mentioning the technical implementation (Mermaid, Chart.js, Leaflet, ANSI codes, etc.)."
494496
)
495497

496498
if (useDiagrams) {
@@ -579,6 +581,45 @@ MiniA.buildVisualKnowledge = function(options) {
579581
)
580582
}
581583

584+
if (useMaps) {
585+
visualParts.push(
586+
"Interactive Maps:\n" +
587+
" - Use ```leaflet``` fences to define interactive maps with Leaflet.js (v1.9.4).\n" +
588+
" - Provide map configuration as JSON with the following structure:\n" +
589+
" • center: [lat, lon] - Map center coordinates (required)\n" +
590+
" • zoom: number - Initial zoom level 1-18 (required, default: 13)\n" +
591+
" • markers: array of {lat, lon, popup?, icon?} - Points of interest (optional)\n" +
592+
" • layers: array of layer definitions (optional)\n" +
593+
" • options: {scrollWheelZoom?, dragging?, etc.} - Map interaction options (optional)\n" +
594+
" - Available marker icon types: 'default', 'red', 'green', 'blue', 'orange', 'yellow', 'violet', 'grey', 'black'\n" +
595+
" - Supported layer types:\n" +
596+
" • circle: {type: 'circle', center: [lat, lon], radius: meters, color?, fillColor?, fillOpacity?}\n" +
597+
" • polyline: {type: 'polyline', points: [[lat, lon], ...], color?, weight?}\n" +
598+
" • polygon: {type: 'polygon', points: [[lat, lon], ...], color?, fillColor?, fillOpacity?}\n" +
599+
" • rectangle: {type: 'rectangle', bounds: [[lat1, lon1], [lat2, lon2]], color?, fillColor?}\n" +
600+
" - Example configuration:\n" +
601+
" ```leaflet\n" +
602+
" {\n" +
603+
" \"center\": [51.505, -0.09],\n" +
604+
" \"zoom\": 13,\n" +
605+
" \"markers\": [\n" +
606+
" {\"lat\": 51.5, \"lon\": -0.09, \"popup\": \"London\", \"icon\": \"red\"},\n" +
607+
" {\"lat\": 51.51, \"lon\": -0.1, \"popup\": \"Nearby location\"}\n" +
608+
" ],\n" +
609+
" \"layers\": [\n" +
610+
" {\"type\": \"circle\", \"center\": [51.508, -0.11], \"radius\": 500, \"color\": \"red\", \"fillOpacity\": 0.3}\n" +
611+
" ]\n" +
612+
" }\n" +
613+
" ```\n" +
614+
" - CRITICAL RULES:\n" +
615+
" • Coordinates must be in [latitude, longitude] format with valid ranges: lat [-90, 90], lon [-180, 180]\n" +
616+
" • Use only static configuration values (no functions or callbacks)\n" +
617+
" • Provide complete data inline (no external fetching)\n" +
618+
" • Keep JSON valid and properly formatted\n" +
619+
" - Use maps for: geographic data, location visualization, spatial relationships, route planning, regional analysis, facility locations, coverage areas"
620+
)
621+
}
622+
582623
var checklist = "\n\nVisual selection checklist:"
583624
var nextIndex = 1
584625
if (useDiagrams) {
@@ -603,6 +644,12 @@ MiniA.buildVisualKnowledge = function(options) {
603644
checklist += "\n" + nextIndex + ". When visuals are optional but helpful -> ANSI-enhanced ASCII table or emoticon map as fallback."
604645
nextIndex++
605646
}
647+
if (useMaps) {
648+
checklist += "\n" + nextIndex + ". Geographic data or locations -> interactive map with markers and layers."
649+
nextIndex++
650+
checklist += "\n" + nextIndex + ". Spatial relationships or coverage areas -> map with circles, polygons, or polylines."
651+
nextIndex++
652+
}
606653
checklist += "\n\nIf no visual type above applies to the user's request (e.g., purely narrative or conversational queries), you may provide text-only output without explanation."
607654

608655
visualParts.push(checklist)
@@ -3668,7 +3715,7 @@ MiniA.prototype._handlePlanUpdate = function() {
36683715
MiniA.prototype._cleanCodeBlocks = function(text) {
36693716
if (!isString(text)) return text
36703717
var trimmed = String(text).trim()
3671-
const isVisualBlock = trimmed.startsWith("```chart") || trimmed.startsWith("```mermaid");
3718+
const isVisualBlock = trimmed.startsWith("```chart") || trimmed.startsWith("```mermaid") || trimmed.startsWith("```leaflet");
36723719
if (trimmed.startsWith("```") && trimmed.endsWith("```") && !isVisualBlock) {
36733720
return trimmed.replace(/^```+[\w]*\n/, "").replace(/```+$/, "").trim()
36743721
}
@@ -3957,8 +4004,8 @@ MiniA.prototype._processFinalAnswer = function(answer, args) {
39574004
if (codeBlockMatch) {
39584005
var lang = (codeBlockMatch[1] || "").toLowerCase()
39594006
var body = codeBlockMatch[2]
3960-
// Preserve fences for visual languages like chart/chartjs and mermaid so the UI can render them
3961-
if (lang === "chart" || lang === "chartjs" || lang === "chart.js" || lang === "mermaid") {
4007+
// Preserve fences for visual languages like chart/chartjs, mermaid, and leaflet so the UI can render them
4008+
if (lang === "chart" || lang === "chartjs" || lang === "chart.js" || lang === "mermaid" || lang === "leaflet") {
39624009
// keep original fenced block
39634010
answer = trimmed
39644011
} else {
@@ -6676,6 +6723,7 @@ MiniA.prototype.init = function(args) {
66766723
args.usediagrams = _$(toBoolean(args.usediagrams), "args.usediagrams").isBoolean().default(false)
66776724
args.usecharts = _$(toBoolean(args.usecharts), "args.usecharts").isBoolean().default(false)
66786725
args.useascii = _$(toBoolean(args.useascii), "args.useascii").isBoolean().default(false)
6726+
args.usemaps = _$(toBoolean(args.usemaps), "args.usemaps").isBoolean().default(false)
66796727
args.chatbotmode = _$(toBoolean(args.chatbotmode), "args.chatbotmode").isBoolean().default(args.chatbotmode)
66806728
args.useplanning = _$(toBoolean(args.useplanning), "args.useplanning").isBoolean().default(args.useplanning)
66816729
args.planmode = _$(toBoolean(args.planmode), "args.planmode").isBoolean().default(false)
@@ -6710,6 +6758,7 @@ MiniA.prototype.init = function(args) {
67106758
useDiagrams: args.usediagrams,
67116759
useCharts: args.usecharts,
67126760
useAscii: args.useascii,
6761+
useMaps: args.usemaps,
67136762
existingKnowledge: baseKnowledge
67146763
})
67156764
if (visualKnowledge.length > 0) {

mini-a.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ help:
155155
example : "true"
156156
mandatory: false
157157
options : ["true", "false"]
158+
- name : usemaps
159+
desc : Encourage Leaflet.js interactive maps by priming the agent with map guidance
160+
example : "true"
161+
mandatory: false
162+
options : ["true", "false"]
158163
- name : mcpdynamic
159164
desc : Enable dynamic tool selection instead of registering all MCP tools (analyzes goal to select relevant tools)
160165
example : "false"
@@ -339,6 +344,7 @@ jobs:
339344
usemermaid : usemermaid
340345
usecharts : usecharts
341346
useascii : useascii
347+
usemaps : usemaps
342348
mcpdynamic : mcpdynamic
343349
mcplazy : mcplazy
344350
useplanning : useplanning

0 commit comments

Comments
 (0)