Skip to content

Releases: SchmidtDSE/glen

v3.38.0

Choose a tag to compare

@cboettig cboettig released this 03 Nov 17:46
57d6f51

Raster time series

A raster layer published as one COG per date can now play through those dates (#421, #432). Give the asset a control with "bind": "frame" and a frames source. The source is either a STAC frame-list asset in the same collection or "list", which lists the folder that holds the layer's COG. The layer then gets a player in the overlay rail. It has play and pause, back and forward, a slider over the dates, and the date on screen. window, start/end and step choose which frames play, and frame_ms sets the speed.

"control": { "bind": "frame", "frames": "ssta-cog-frames", "window": "P90D", "step": 4 }

The map never goes blank between frames. The player keeps the next frames loading at zero opacity, and a new frame replaces the old one only once its tiles have been drawn. On a slow connection playback slows down to wait for each frame. preload (default 3) and max_wait_ms (default 8000) tune this.

The agent controls the player with a new set_time tool, which apps get only when they configure a time series. get_map_state reports each time series' current date and the dates available.

The full reference is the "Raster time series" section of docs/guide/configuration.md.

Raster bands and units

  • Band selection (#357). bidx picks the band of a multi-band COG. The band's STAC nodata and classification:classes are read from the chosen band.
  • Legend classes on rasters (#350). Config legend_classes replace a raster's STAC classes and also build its colormap, so a class left out renders transparent. A class whose value is the band's nodata is always left out.
  • Unscale. A band whose STAC raster:bands entry gives a scale or offset is now unscaled by TiTiler, so rescale and the legend are in real units. Set "unscale": false to keep the old behaviour.

Migration

For a raster whose STAC declares a scale other than 1 or an offset other than 0, rescale is now read in real units. An app with such a layer must change its rescale or set "unscale": false when it bumps its pin. All 60 raster collections in bosl-high-seas were checked, and none declares a scale or offset. Only that app's new Coral Reef Watch layers are affected, and they were configured for this release.

No other configuration changes are needed. Apps without a control on a raster are unaffected by the time series.

Other changes

  • The docs and README link the template at its new home, SchmidtDSE/glen-template (#425).

Full changelog: v3.37.0...v3.38.0

v3.37.0

Choose a tag to compare

@cboettig cboettig released this 02 Nov 17:41
99e6ae2

The repo moved to SchmidtDSE/glen

boettiger-lab/geo-agent is now SchmidtDSE/glen (2026-11-02). Change your CDN links:

https://cdn.jsdelivr.net/gh/boettiger-lab/geo-agent@<version>/app/...
→ https://cdn.jsdelivr.net/gh/SchmidtDSE/glen@<version>/app/...
  • Nothing breaks today. The old path still serves every version through GitHub's redirect, but load new releases from the new path.
  • Docs moved to https://schmidtdse.github.io/glen/. The old docs URL returns 404 because GitHub Pages does not redirect.
  • The template is now SchmidtDSE/glen-template.
  • Version reporting: before this release, an app loaded from the new path reported its version as dev (in the X-Client header and in exported reports). v3.37.0 reads the version from either path.
  • No other behaviour changes from v3.36.0.

What's Changed

  • Serve the docs from /glen/ after the move to SchmidtDSE by @cboettig in #423
  • Point the repo at its new home, SchmidtDSE/glen by @cboettig in #424

Full Changelog: v3.36.0...v3.37.0

v3.36.0

Choose a tag to compare

@cboettig cboettig released this 25 Oct 16:49
2b63ef3

MCP

Several MCP servers per app, with optional ones (#405, closes #338). An app can now give the agent tools from more than one MCP server, for example duckdb-mcp plus glen-sdm's species distribution models. This is opt-in: an app with no mcp_servers connects to one server exactly as before.

"mcp_servers": [
  { "url": "https://duckdb-mcp.nrp-nautilus.io/mcp" },
  { "url": "https://sdm-mcp.nrp-nautilus.io/mcp", "optional": true }
]
  • Primary server: the first entry is the data engine. Only it gets the inline STAC lookup, the fallback query tool and the geospatial-analyst prompt, and get_schema and render_chart query it.
  • Single-server keys: mcp_url and mcp_auth_token, and mcp_server_url from a deploy-time config.json, set the primary server only. A deployment that injects the duckdb-mcp URL keeps the other servers listed in layers-input.json.
  • Optional servers: "optional": true keeps a server from holding up boot. Its tools join when it connects, and if it can't be reached they are left out with a console warning.
  • Per-server behaviour: auth_token is set per server, each server reconnects on its own, and calls go to the server that listed the tool. On a tool-name collision the server listed first wins.

See "Multiple MCP servers" in the configuration guide.

Map

One swatch for a hex layer with a single value (#406). A hex layer whose cells all hold the same value, such as a modelled species range where every cell is 1, now shows one swatch labelled with the layer's name instead of a colorbar from 1 to 1. A single-value GeoJSON hex layer is now painted in the palette's top colour, because the middle colour can be near-white. The server must report the constant domain (min: 1, max: 1) for this to apply.

add_hex_tile_layer description (#404). The tool description no longer says register_hex_tiles can return GeoJSON, since it always builds vector tiles. It now says format: "geojson" and geojson_url come from the tool that produced the layer, such as a species distribution model's artifacts.map_layers, and that a ready argument object should be passed unchanged.

Voice input

Transcription docs and a quieter mic icon (#410, closes #408). The configuration guide's voice examples now use a working transcription model (gemma-small) and give a one-request check for whether a model accepts audio. The mic button is a muted line icon beside Send that turns red while recording, replacing the emoji, and the fallback transcription endpoint points at open-llm-proxy.nrp-nautilus.io.

Development

  • pnpm, with no install scripts (#413). Development dependencies are installed with pnpm (corepack pnpm install). No dependency runs an install script, a version must be a week old before it installs, and a release with weaker provenance than earlier ones is refused. None of these packages is served to users.
  • DISCLOSURE.md (#400) states how LLM coding tools were used in this repository.

Upgrading

No config changes are required; bump the pin to @v3.36.0. To add a second MCP server, list both in mcp_servers.

v3.35.0

Choose a tag to compare

@cboettig cboettig released this 08 Oct 16:45

Agent

Optional max_tokens, and a clear stop at the output limit (#399, closes #387).

  • New setting: max_tokens, set per model or app-wide, with null on a model to opt it out. No default: nothing changes until an app sets it. A numeric string from a generated config.json is accepted.

    "llm_models": [{ "value": "anthropic/claude-sonnet-5", "max_tokens": 8192 }]

    It bounds runaway generations (proxy logs show responses running 30+ minutes to a 131k-token ceiling, open-llm-proxy#150) and stops OpenRouter reserving credit for a model's whole output ceiling, which caused 402s on small balances (#386).

  • Responses that hit the limit: this applies to the app's cap, or the model's own ceiling when none is set.

    • A cut-off text answer is shown with a note.
    • No tool call from a cut-off response runs, since its arguments may be half-written.
    • A response where reasoning spent the budget ends with a clear error.

    Detection uses finish_reason, OpenRouter's native_finish_reason, and whether the completion count reached the cap. GLM-5.2 reports a cut-off tool call as tool_calls, which finish_reason alone would miss.

See "Capping response length" in the configuration guide.

Upgrading

No required config changes; bump the pin to @v3.35.0. To use the cap, add max_tokens to a model or to the top-level config.

v3.34.2

Choose a tag to compare

@cboettig cboettig released this 06 Oct 18:54

Fix

The agent now treats SQL Error: results as failures (#398, closes #395). mcp-data-server returns SQL failures as text rather than raising, and the agent's failure check only matched results starting with Error. So the repeated-failure guard from #313 never fired on SQL: a model resending the same broken query looped to the iteration cap. Now the first identical repeat gets a nudge quoting the error, and the second ends the turn at a checkpoint. A model trying different queries is unaffected.

The agent's check and the export's were two copies of one rule. They now share isFailedToolResult() in tool-registry.js.

Upgrading

No config changes; bump the pin to @v3.34.2.

v3.34.1

Choose a tag to compare

@cboettig cboettig released this 06 Oct 18:50

Fix

SQL passed as sql was missed (#397). mcp-data-server's query tool takes sql_query and register_hex_tiles takes sql, and each accepts the other's name (mcp-data-server#321). Models use both. A query call with {"sql": …} lost its SQL in three places:

  • The chat panel's SQL disclosure on the result.
  • The agent's per-turn SQL record.
  • The exported report, which filed the query under "Consulted". It got no code chunk, and with no runnable query the .R / .py / .qmd / .ipynb download buttons were hidden.

Seen on ca-30x30 with deepseek-v4-flash (v3.34.0).

The cause was four separate copies of the rule for which argument holds the SQL. They now share one, sqlArg() in tool-registry.js: remote tools read sql_query, sql, query, and local tools read sql only. The chat panel no longer shows geocode's place-name query as SQL. test/sql-arg.test.js covers every name against both remote tools at every site, including the export's chunks, download buttons, downloaded files and data-source citations.

Upgrading

No config changes; bump the pin to @v3.34.1.

v3.34.0

Choose a tag to compare

@cboettig cboettig released this 06 Oct 18:19

Panel

Action buttons moved to the top of the panel (#396). Globe, Hex grid, Layer to back, Upload GeoJSON and Export session now sit in a row directly under the basemap buttons, above the layer list. Before, they were below the list, and on apps with many layers they scrolled out of sight, so people could not find Export. Upload status messages now appear just under the row, beside the button.

Upgrading

No config changes; bump the pin to @v3.34.0. The move affects every app with a layer panel. Apps whose own CSS targets .panel-actions should check the new position.

v3.33.0

Choose a tag to compare

@cboettig cboettig released this 06 Oct 16:29

Exported session report

Download the session as code (#394). Beside Print / PDF, the report now offers the whole session as one runnable file:

  • .R script, one RStudio outline section per question
  • .py script with # %% cells
  • .qmd Quarto document with R chunks
  • .ipynb Jupyter notebook with Python cells

Each query gets its own variable (df1, df2, …). The notebooks include the model's answers as prose between the chunks. Files are embedded in the report, so it stays a single file that works offline.

Data sources (#394). A new section cites each dataset the session queried or showed on the map. Each entry comes from its STAC record: title, producers, sci:citation (or the DOI / cite-as link), license, About and STAC links. The same list ends every downloaded file. Citation quality follows the catalog's metadata; a record with no citation says so.

Speed note. The disclaimer now notes that GLEN runs queries on high-speed servers next to the data, so re-running large queries on a personal computer may be much slower.

Controls, regrouped. The report's controls (Code, Code as, Download, Print) are now a labelled, aligned grid under an Options heading in the left rail, above Contents. Below 1480px they become a panel above the document. The rail previously started off-screen at its old 1360px breakpoint.

Fixes to re-running exported code

These affected every export, not just the new downloads:

  • Anonymous S3 access. The setup block now writes KEY_ID '', SECRET '' explicitly. Without them, DuckDB uses any AWS credentials in the reader's environment (e.g. ~/.Renviron), and the public bucket answers 403 InvalidAccessKeyId.
  • h3 extension. The setup now runs INSTALL h3 FROM community; LOAD h3;, since the agent's queries call h3_* functions.
  • Failed queries shown as results. The export now recognises the MCP server's SQL Error: … as a failure, so failed queries go only to the session log. The same gap in the agent loop is tracked in #395.

Upgrading

No config changes. Apps pick this up by bumping their pin to @v3.33.0. Nothing changes in the live app; only the exported file changes.

v3.32.0 — exports that read like a report

Choose a tag to compare

@cboettig cboettig released this 05 Oct 04:52

The saved export is now a session report that reads like a Quarto or R Markdown document, not a copy of the chat panel. It records which model produced it. Also a fix for private deployments that opt out of the export in config.json, and a docs catch-up.

See it: sample report, re-exported from a real TPL Protected Lands Explorer session.

The export is a session report (#388, #389)

  • One section per question, headed by the user's words verbatim, with a table of contents (floating on wide screens).
  • Code chunks with output. Each SQL-carrying call (query, register_hex_tiles, render_chart, filter_by_query) is a folded code chunk with its output beneath it, in the order it ran, followed by the model's answer as prose. Show all / Hide all code plus the existing R · Python · SQL switch.
  • Charts are figures. Each render_chart the session drew is re-drawn at report width as a captioned figure under its chunk, even if the user closed its panel. A chart drawn from rows the model passed inline (rather than SQL) says so.
  • Intermediate calls are summarised, not dumped. Schema and catalog lookups become one Consulted … line, map actions one line each, and failed or retried calls a count.
  • A session log appendix, collapsed, keeps every call with its arguments and result, lookups and failures included. The body is curated; the log shows nothing was hidden. It stays folded when printed.
  • Model disclosure. Under the title: "Outputs generated with GLEN using <model> on <date>", plus a note that the prose was written by the model. The model is recorded per question, so a mid-session switch shows, and each section names its own when more than one was used. The link goes to the project docs site; set export.project_url to point it elsewhere.
  • Colophon with the GLEN version the app is pinned to, read off the jsDelivr URL.
  • Fixed: every query used to appear twice (once in the call row, once in the result row).

The export is now built from a structured record of the session rather than a clone of the chat DOM. The renderer is a new DOM-free module, app/export-report.js, with 99% line coverage.

Fix

  • "export" in config.json is now honoured (#393). The guide told private deployments to opt out with "export": false in the deploy-time config.json, but main.js never merged that key. An app that opted out only in its ConfigMap still offered the export. If you run a private app, check it actually has the export off after upgrading.

Docs (#392)

  • New sections: GeoJSON upload (upload_enabled), including the bucket's anonymous-PUT + CORS requirement, and the read-tool memo (tool_memo).
  • A table of the config.json keys the app reads.
  • The top-level fields table now lists every opt-in feature block.
  • Corrected deployment guidance that predated version pinning, and updated stale example pins.

Upgrading

Drop-in from v3.31.x. No config changes needed.

  • Exported files are now named glen-session-YYYY-MM-DD-HHMM.html (was glen-chat-…).
  • ChatUI takes an optional fifth constructor argument, the ChartRenderer. main.js passes it, so this only matters to custom bootstraps.
  • Moving live chart panels into the sidebar is planned separately (#391); they still float over the map in this release.

Full changelog: v3.31.0...v3.32.0

v3.31.0 — one checkbox for CONUS/Alaska layer pairs

Choose a tag to compare

@cassiebuhler cassiebuhler released this 02 Oct 23:58

One panel checkbox can now drive several layers, for variables that ship as separate CONUS and Alaska rasters. Opt-in; no config changes needed otherwise.

Toggle groups (#349, #390)

  • New asset-level toggle_group. Layers sharing a value get one panel row, labelled with the value, whose checkbox shows and hides every member. Members can come from different collections.
  • Members stay separate layers. Each keeps its own source, colormap/rescale, layer_id and legend, so CONUS and Alaska can keep different stretches. The agent can still show one half alone; the row's checkbox then shows as indeterminate.
  • Versioned members share one dropdown over all their labels. Picking a label switches every member that has it. A member without that label is hidden and named on the row ("2024 not available for: …"), and comes back when a label it has is picked.
  • Identical legends merge. Visible members whose legends would draw the same thing (same classes and colours, or same colormap, range and units) share one legend entry titled with the group name. Different stretches keep separate colorbars.

See "Toggle groups" in the configuration guide.

Upgrading

Drop-in from v3.30.x. Without toggle_group, behaviour is unchanged.

Full changelog: v3.30.2...v3.31.0