npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

webmap_ai

v0.1.2

Published

Provider-neutral AI assistant infrastructure for web maps.

Downloads

319

Readme

webmap_ai

CI

Provider-neutral AI assistant infrastructure for web maps.

webmap_ai brings an ArcGIS Assistant-style interaction model to non-Esri maps — without depending on Esri's AI components or map stack. It lets any web map expose a safe, typed, inspectable set of map-aware tools to an LLM.

The assistant is not tied to a single provider. The same tool router drives many map runtimes:

  • MapLibre
  • Leaflet
  • OpenLayers
  • custom canvas maps
  • Three.js-based spatial scenes

Architecture

webmap_ai is built from a few composable pieces:

  • a reusable assistant panel
  • a bounded tool router
  • provider-specific map adapters
  • typed command schemas
  • inspectable backend query and action flows

The map adapter

The map adapter is the seam that lets the assistant talk to different map runtimes through one stable interface. Implement it once per map library and the rest of the stack works unchanged:

export interface MapAssistantAdapter {
  getMapState(): Promise<MapState>;
  listLayers(): Promise<LayerSummary[]>;
  getLayerSchema(layerId: string): Promise<LayerSchema>;
  queryVisibleFeatures(args: QueryVisibleFeaturesArgs): Promise<FeatureSample[]>;
  queryFeatures(args: QueryFeaturesArgs): Promise<FeatureSample[]>;
  setView(args: SetViewArgs): Promise<void>;
  selectFeatures(args: SelectFeaturesArgs): Promise<void>;
  clearSelection(): Promise<void>;
  setLayerVisibility(layerId: string, visible: boolean): Promise<void>;
  setFilter(args: SetFilterArgs): Promise<void>;
}

This gives the model a controlled toolbox instead of unrestricted UI control.

What's included

The provider-neutral TypeScript core:

  • src/adapter.tsMapAssistantAdapter contract
  • src/types.ts — shared map/layer/query types
  • src/tools.ts — typed assistant tool-call schema
  • src/router.ts — bounded tool router/executor
  • src/adapters/memory.ts — in-memory adapter for local demos/tests
  • src/adapters/maplibre.ts — adapter that drives a live MapLibre map

A runnable MapLibre demo lives in demo/, and a FastAPI backend for LLM tool-calling lives in backend/.

Getting started

Install

npm install
npm run build

Minimal usage

import { MapAssistantRouter, MemoryMapAssistantAdapter } from "webmap_ai";

const adapter = new MemoryMapAssistantAdapter({
  mapState: {
    bounds: { west: -123, south: 37, east: -121, north: 38 },
    center: { lng: -122, lat: 37.5 },
    zoom: 9,
    selectedFeatureIds: [],
  },
  layers: [
    {
      summary: { id: "cities", name: "Cities", type: "geojson", visible: true },
      schema: { layerId: "cities", fields: [{ name: "name", type: "string" }] },
      features: [{ id: "1", layerId: "cities", properties: { name: "Oakland" } }],
    },
  ],
});

const router = new MapAssistantRouter(adapter);

const response = await router.run({
  message: "What layers are loaded?",
  toolCalls: [{ name: "list_layers" }, { name: "get_map_state" }],
});

With a live MapLibre map

MapLibreMapAssistantAdapter wraps an existing MapLibre map instance, so the same router drives real map reads and writes:

import maplibregl from "maplibre-gl";
import { MapAssistantRouter, MapLibreMapAssistantAdapter } from "webmap_ai";

const map = new maplibregl.Map({
  container: "map",
  style: "https://demotiles.maplibre.org/style.json",
  center: [-122, 37.5],
  zoom: 9,
});

const adapter = new MapLibreMapAssistantAdapter(map);
const router = new MapAssistantRouter(adapter);

const response = await router.run({
  message: "Zoom to the cities layer and tell me what's visible.",
  toolCalls: [{ name: "get_map_state" }, { name: "list_layers" }],
});

Demo app

A runnable MapLibre demo with a right-side assistant panel lives in demo/:

cd demo
npm install
npm run dev

Then open http://localhost:5173. The demo works with no API key (keyword-based tool inference through the real router), or you can wire it to the FastAPI backend or an OpenRouter key for full LLM tool-calling. See demo/README.md for the options.

Design principles

  • Provider-neutral, not Esri-dependent
  • Typed tool calls over vague agent behavior
  • Read-only before write-capable
  • Deterministic query and stats logic in code, not in the model
  • Human confirmation for consequential actions
  • Reusable across many map products, not trapped inside one demo

Backend

The backend/ Python package provides a FastAPI server that forwards map-aware chat messages to an LLM (OpenAI or Anthropic) using tool-calling.

Quick start

# 1. Install dependencies
pip install -r requirements.txt

# 2. Copy and fill in environment variables
cp .env.example .env
# then set OPENAI_API_KEY (or ANTHROPIC_API_KEY)

# 3. Run the development server
uvicorn backend.main:app --reload

POST /chat

Request

{
  "message": "What layers are loaded?",
  "map_context": {
    "bbox": { "west": -123, "south": 37, "east": -121, "north": 38 },
    "zoom": 9,
    "visible_layers": ["cities", "roads"],
    "selected_feature_ids": []
  }
}

Response

{
  "text": "I'll check what layers are available.",
  "tool_calls": [
    { "name": "list_layers", "args": {} },
    { "name": "get_map_state", "args": {} }
  ]
}

The tool_calls list is returned as structured commands for the frontend to execute via the TypeScript MapAssistantRouter. The backend does not execute tools itself – it stays stateless and lets the browser-side adapter do all map interactions.

Environment variables

| Variable | Required | Default | Description | |---|---|---|---| | LLM_PROVIDER | No | openai | openai or anthropic | | OPENAI_API_KEY | When LLM_PROVIDER=openai | — | OpenAI API key | | OPENAI_MODEL | No | gpt-4o-mini | OpenAI model name | | ANTHROPIC_API_KEY | When LLM_PROVIDER=anthropic | — | Anthropic API key | | ANTHROPIC_MODEL | No | claude-3-5-haiku-latest | Anthropic model name |

Running tests

TypeScript package (router + adapters), powered by Vitest:

npm install
npm run typecheck   # type-only check (no emit)
npm test            # run the runtime test suite
npm run test:watch  # watch mode during development

Python backend:

python -m pytest backend/tests/ -v

Releasing

Continuous integration (.github/workflows/ci.yml) runs npm ci, typecheck, build, and test on every push and pull request.

Publishing to npm is automated by .github/workflows/release.yml and triggered by pushing a version tag:

# 1. Bump the version and create a matching git tag
npm version patch        # or: minor / major

# 2. Push the commit and the tag
git push --follow-tags

The release workflow re-runs the build and tests, validates the package with npm publish --dry-run, then publishes to npm using trusted publishing (OIDC) — no long-lived npm token is stored in the repository. Provenance attestations are generated automatically.

One-time setup on npmjs.com: open the package's Settings → Trusted Publisher, choose GitHub Actions, and configure:

| Field | Value | |---|---| | Organization or user | danmaps | | Repository | webmap_ai | | Workflow filename | release.yml | | Environment | (leave blank) |

You can add the trusted publisher before the package's first publish — just enter the package name (webmap_ai) when prompted. The workflow already requests the required id-token: write permission and upgrades npm to a version that supports OIDC.

To preview what would be published without releasing:

npm run build
npm publish --dry-run

Status

Provider-neutral TypeScript core (router, tool schema, memory + MapLibre adapters), a FastAPI backend with LLM tool-calling, a MapLibre demo app, and a CI + trusted-publishing release pipeline. Read-only tools are the current focus, with reversible write actions rolling out behind explicit confirmation.