Library / SDK
opengeos/GeoAgent avatar
opengeos/GeoAgent

GeoAgent puts one agent layer under every geospatial Python package

A multimodal AI agent for geospatial data analysis and interactive visualization

540 stars97 forksPythonMIT

At a glance

What is it?
A Python library that wraps Strands Agents with geospatial context, structured tool metadata, optional adapters for leafmap, anymap, STAC, NASA Earthdata, geoai and QGIS, plus confirmation hooks that pause before destructive or expensive work. Core installs only strands-agents and pydantic; everything geospatial is an extra.
Who is it for?
Adopt GeoAgent when you maintain a geospatial Python package or QGIS plugin and want it reachable from a language model without writing an agent stack of your own, since the whole argument is that leafmap, anymap, geoai, geemap, STAC and Earthdata integrations should not each maintain their own. The cost is a dependency on Strands Agents and a Python floor of 3.11.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • opengeos/GeoAgent540 ★ · +54
Star history
Repository2026-09-112026-11-14
opengeos/GeoAgent486540

54 Stars in the last 64 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

One agent layer under leafmap, anymap, STAC and QGIS

The problem being solved is duplication. Several geospatial libraries need the same agent features: binding an agent to a live map, a QGIS session, a dataset or a workflow object, exposing package functions as structured tools, and supporting multiple model providers. Doing that inside each library means every project maintains its own agent framework, and the interfaces drift apart. GeoAgent centralizes that layer instead, and it is built on Strands Agents rather than on a new agent loop, which is why the core dependency list is as short as it is. On top of Strands it adds geospatial context, structured tool metadata, optional package adapters, provider configuration and confirmation hooks that pause the agent before destructive, expensive or otherwise irreversible operations. The stated beneficiaries are concrete: leafmap, anymap, geoai, geemap, STAC and NASA Earthdata can each expose their tools to large language models through one consistent interface. The distribution story is broad as well, with the package on PyPI, a conda-forge feedstock, a QGIS plugin listing, a Colab badge and a Binder link, so the same library is reachable whether you work in a notebook, a desktop GIS or a server environment. The packaging metadata names the intended audience as developers and science or research users, and its keyword list is a decent description of the scope: GeoAgent, geospatial, AI, agent, STAC, remote sensing, earth observation, QGIS, leafmap, anymap and strands-agents. Author and maintainer are the same person who writes the leafmap and geemap work the project builds on, which is worth knowing when you are judging how quickly a QGIS or notebook integration will move.

@geo_tool and GeoToolRegistry turn a function into an agent tool

The core abstractions are small and worth reading in the source rather than the marketing. GeoAgent is a high-level facade around a Strands Agent. GeoAgentConfig holds provider, model, temperature, token and client settings, which is where sampling and client configuration live rather than in the constructor call sites. GeoAgentContext carries the runtime objects bound to the agent, such as a map instance or the QGIS interface object, so tools can reach a live session instead of a detached copy. The decorator @geo_tool is the piece that does the work: it turns an ordinary Python function into a Strands tool carrying GeoAgent metadata, which is what lets a library expose its own functions without re-describing them as tools. GeoToolRegistry is where tool metadata, safety flags, categories and fast-mode filtering are held. Those four attributes are the ones that decide behaviour at runtime. Safety flags are what a confirmation hook can consult before letting a call through, categories are what let a caller narrow a large geospatial library down to the handful of operations a task needs, and fast-mode filtering is the mechanism for excluding slower tools when a model or a budget cannot afford them. For a package author the practical consequence is that the decision about what an agent may do is data you register once, not branching logic scattered through the tool bodies. The registry is also what makes one agent reusable across libraries: two packages can register tools with the same category names and the same safety flags, and a caller can then narrow both without knowing which package produced a given tool.

The factories decide what the agent is allowed to see

Binding is done by factories rather than by hand-written setup, and the list of them maps closely onto the ecosystems the project wants to serve. for_leafmap binds tools to a leafmap.Map-compatible object, and for_anymap does the same for an anymap.Map-compatible object, which is the pattern for a live map widget in a notebook or a web app. for_qgis binds to qgis.utils.iface and an optional QgsProject, so a plugin run inside QGIS gets the running application rather than a new instance. for_stac binds STAC catalog search and asset tools and adds optional QGIS loading on top, and for_nasa_opera binds NASA OPERA search and display tools together with the QGIS tools. create_agent is the escape hatch for custom tools or a package-specific integration that does not fit any of those shapes. The split matters more than the count. A map-bound agent and a QGIS-bound agent have different failure modes and different places to write results, so a tool written against GeoAgentContext has to know which of the two it is running inside. The factories make that explicit at construction time rather than leaving it to be discovered at runtime, and GeoAgentContext is the object that carries the answer into the tool.

Extras keep the geospatial stacks optional, and vLLM is the one left out

Core installation is one command and pulls only the agent framework dependencies:

bash
pip install GeoAgent

What it installs is strands-agents and pydantic, and everything else is an extra. Provider extras cover openai, anthropic, gemini, ollama, litellm, openrouter and openai-compatible, with providers bundling most of them and all bundling most optional integrations. Geospatial extras cover leafmap, anymap, stac, earthdata, nasa-opera, geoai, geemap, earthengine and ui, plus a browser extra that provides a FastAPI WebSocket backend for browser MapLibre apps. Extras compose, and the documented combinations show the intended granularity:

bash
pip install "GeoAgent[leafmap,openai]"
pip install "GeoAgent[anymap,anthropic]"
pip install "GeoAgent[stac,earthdata,openai]"

The one exception is vLLM. It is not part of providers or all, and the reason is written down in the packaging configuration: the strands-vllm integration pins openai below 2.0, which conflicts with the openai 2.0 or newer that the default codex provider needs. The documented workaround is to point the openai-compatible provider at a vLLM server's /v1 URL instead, which is a real constraint rather than an oversight, and one worth knowing before you plan a deployment around it. The bedrock extra is also declared empty, so it contributes no packages of its own, and the versions pinned underneath the rest are worth reading before you pin anything yourself: openai 2.0 or newer for the openai, openrouter and openai-compatible extras, anthropic 0.40 or newer, google-genai 1.0 or newer alongside the gemini integration of Strands, pystac-client and planetary-computer for STAC, and earthengine-api with geemap for Earth Engine. There is also a console script, geoagent, wired to the package CLI entry point, so the install gives you a command as well as an import.

Provider selection is environment variables, with Codex OAuth beside the OpenAI key

When no provider is passed explicitly, GeoAgent picks one from the environment. OpenAI reads OPENAI_API_KEY with an optional OPENAI_MODEL, Anthropic reads ANTHROPIC_API_KEY with an optional ANTHROPIC_MODEL, and the ChatGPT or Codex OAuth route reads OPENAI_CODEX_ACCESS_TOKEN with an optional OPENAI_CODEX_MODEL. Gemini follows the same pattern. The design point is that the choice of provider is configuration rather than code, which is what lets the same package serve a hosted model in one deployment and a local one in another. The wider provider list in the same section is longer than the environment table: OpenAI, ChatGPT and Codex OAuth, Anthropic, Google Gemini, Bedrock, OpenRouter, LiteLLM, any OpenAI-compatible server, vLLM, and local Ollama models. Multimodal input is conditioned on the provider rather than assumed, since the documentation limits pasted images and screenshots in plugin workflows to providers that support vision. That is a sensible boundary, and it also means an agent configured differently on two machines can behave differently on the same task, so the provider in use is worth pinning in whatever you run this inside. Keeping the optional geospatial stacks optional is the same philosophy applied to Python rather than to models: a machine that only reads STAC catalogs should not have to install a QGIS-facing stack to do it, and the extras table is how you keep that promise enforceable.

Confirmation hooks are the answer to a language model holding a delete key

The safety story is a single idea applied consistently: the agent pauses before doing anything destructive, expensive or irreversible. The named cases are deleting layers, saving files and running expensive processing jobs. In a geospatial session those are the three operations where a wrong call costs real work, since a layer can hold hours of digitizing, a save can overwrite the only copy of a project, and an expensive job can occupy a machine for a long time. The mechanism behind it is the safety flag on the registered tool rather than a prompt written into a docstring, so the same decision applies whether the tool came from a built-in factory or from a package's own @geo_tool function. That is the part worth checking when you adopt it for your own package. Registering a tool is easy, and forgetting to mark the destructive one is the failure mode that matters, because a tool that is not flagged does not pause. The registry holds categories and fast-mode filtering alongside the flags, so the same metadata that drives safety also drives which tools a model is offered, which makes the registration step the place where a package's risk surface is actually decided.

QGIS stays a system install, and Python has to be 3.11 or newer

Two constraints sit outside pip. For QGIS, GeoAgent has to be installed into the Python environment that QGIS itself uses, because a plugin runs inside the desktop application's interpreter rather than your shell's; the GeoAgent[qgis] extra exists only as a marker, and QGIS is supplied by the desktop application or the system package manager, which is also why the all extra does not include it. The second constraint is the interpreter floor: the packaging metadata requires Python 3.11 or newer and the classifiers cover 3.11, 3.12 and 3.13. The repository layout follows the same separation. The geoagent/ directory holds the package, qgis_geoagent/ holds the QGIS side, docs/ is driven by mkdocs.yml, and scripts/, tests/ and examples/ sit alongside a requirements.txt that keeps a lightweight core plus pytest and ruff for CI and editable installs, with the optional stacks living in the extras instead. The examples directory is where the less obvious integrations show up: a browser MapLibre app, TypeScript and Node variants of the same idea, an hsae_demo.py and a nasa_opera_qgis.py. A MANIFEST.in and a requirements_dev.txt sit next to the runtime requirements file, and a pre-commit configuration is checked in, so the repository expects linting and packaging hygiene to be enforced rather than left to the installer. Version 1.9.0 is both the current release tag and the version in the packaging metadata, published on 9 August 2026, with the default branch last pushed on 27 September 2026.

Editorial conclusion

Adopt GeoAgent when you maintain a geospatial Python package or QGIS plugin and want it reachable from a language model without writing an agent stack of your own, since the whole argument is that leafmap, anymap, geoai, geemap, STAC and Earthdata integrations should not each maintain their own. The cost is a dependency on Strands Agents and a Python floor of 3.11. Do not adopt it expecting a bundled QGIS, because QGIS remains something the desktop application or system package manager provides, and GeoAgent has to be installed into the Python environment QGIS itself uses. Four things to check first. Which provider extra matches your account, and whether you want the hosted OpenAI-compatible path or a local Ollama. Whether you need vLLM, which is deliberately excluded from the providers and all extras because of an openai version conflict, and therefore has to be reached through the openai-compatible provider pointed at a vLLM /v1 URL. Which tools you register, since the registry carries safety flags and categories that decide what the agent may reach for. And whether the confirmation hooks are wired to your destructive operations, since the hook is what stands between a language model and a delete.

Frequently asked questions

What is GeoAgent?

A shared AI agent layer for geospatial Python packages, live map widgets and QGIS plugins, built on Strands Agents. It gives packages such as leafmap, anymap, geoai, geemap, STAC and NASA Earthdata one consistent way to expose their tools to large language models instead of each maintaining its own agent stack.

How do I install GeoAgent with a model provider and a map library?

Core is pip install GeoAgent, which installs only strands-agents and pydantic, and extras compose from there, for example pip install "GeoAgent[leafmap,openai]" or pip install "GeoAgent[stac,earthdata,openai]". Provider detection without an explicit provider comes from environment variables such as OPENAI_API_KEY, ANTHROPIC_API_KEY or OPENAI_CODEX_ACCESS_TOKEN, each with an optional model variable.

Why is vLLM not part of the providers or all extras in GeoAgent?

The strands-vllm integration pins openai below version 2.0, which conflicts with the openai 2.0 or newer required by the default OpenAI Codex provider. The documented route is to point the openai-compatible provider at a vLLM server's /v1 URL instead.

How does GeoAgent keep an agent from destructive operations?

GeoToolRegistry holds safety flags alongside tool metadata, categories and fast-mode filtering, and GeoAgent adds confirmation hooks that pause the agent before destructive, expensive or irreversible work such as deleting layers, saving files or running expensive processing jobs. Multimodal plugin workflows with pasted images and screenshots are limited to providers that support vision.

Does pip installing GeoAgent give me QGIS?

No. QGIS remains something the desktop application or system package manager provides, and the GeoAgent[qgis] extra is only a marker. For QGIS use you install GeoAgent into the Python environment that QGIS itself runs, and for_qgis binds tools to qgis.utils.iface with an optional QgsProject.

Official sources

  1. License: MIT
  2. opengeos/GeoAgent on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/opengeos-geoagent.svg)](https://hysenlabs.com/projects/opengeos-geoagent)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/opengeos-geoagent.svg)](https://hysenlabs.com/projects/opengeos-geoagent)

Was this useful?