This guide is for scientists who have a pest or disease model and want it to run inside the AgStack Digital Public Infrastructure (DPI) — discoverable by anyone, runnable over an API and by AI agents, fed by governed weather data, and able to publish its results back into the shared data plane.
You do not fork this repository. You publish a small pip package; the runtime
finds your model automatically. A complete, working example lives in
examples/model-template/.
you write this the DPI runtime provides this
┌─────────────────────┐ ┌──────────────────────────────────────────┐
│ PestModelBase │ │ GeoID resolution (AR2) │
│ .calculate(wx) -> │ <─── │ weather as BITEs (Pancake/TerraPipe) │
│ ModelResult │ │ REST /calculate + MCP run_forecast │
└─────────────────────┘ │ result published as a PEST_DISEASE BITE │
└──────────────────────────────────────────┘
Your model is pure computation: weather in, risk out. Everything else — identity, consent, data fetching, transport, aggregation — is the platform's job.
Subclass PestModelBase and implement validate_parameters and calculate.
from typing import ClassVar
from uuid import UUID
from agstack_pnd.foundation.base_model import PestModelBase
from agstack_pnd.foundation.types import (
DailyScore, ModelLayer, ModelMetadata, ModelResult, WeatherDataFrame,
)
class MyModel(PestModelBase):
metadata: ClassVar[ModelMetadata] = ModelMetadata(
uuid=UUID("<uuid4-you-generate-once>"),
name="my_model",
display_name="My Model",
version="0.1.0",
layer=ModelLayer.WEATHER, # or PEST / DISEASE
description="What it does, in one sentence.",
required_weather_fields=["air_temperature", "relative_humidity"],
citation="Author et al. (Year). Journal.",
author="Your Lab",
)
def validate_parameters(self, parameters): ...
def calculate(self, weather_data, parameters=None, threat=None) -> ModelResult: ...Rules that keep the DPI trustworthy:
- Stable identity. Generate
metadata.uuidonce (python -c "import uuid; print(uuid.uuid4())") and never change it. Bumpversionwhen the science changes. - Declare inputs. Everything you read must be in
required_weather_fieldsso the runtime fetches exactly those fields. Supported names includeair_temperature,air_temperature_min/max,relative_humidity,precipitation,wind_speed,dew_point. - No side effects. Do not call the network or read files in
calculate.
In your pyproject.toml:
[project.entry-points."agstack_pnd.models"]
my_model = "my_package.my_module:MyModel"That single table is the entire integration contract. On install, agstack-pnd's
ModelRegistry discovers your class through this entry point.
Build a WeatherDataFrame from numpy arrays and assert on the ModelResult.
See examples/model-template/tests/.
pip install -e .
pytestInstall your wheel alongside agstack-pnd and start the server (or use the
dpi-demo stack). Then:
# REST
curl -X POST localhost:8080/api/v1/calculate -H 'content-type: application/json' -d '{
"model_uuid": "<your-uuid>", "geo_id": "<geoid>",
"start_date": "2026-07-01", "end_date": "2026-07-14",
"weather_provider": "dpi", "publish": true
}'weather_provider: "dpi"makes the runtime read weather BITEs (ingested from TerraPipe by Pancake's TAP worker) keyed by your field's GeoID — you never touch a weather API.field_grant(an AR2 SD-JWT credential) authorizes access to a private field's exact geometry and, when enabled, its grant-gated weather.publish: truewrites your result back as aPEST_DISEASEBITE, so other services can join your risk to soil/satellite/insurance data on the GeoID, and the district-level/aggregateendpoint can roll it into population statistics.
AI agents get the same capability through the MCP run_forecast tool.
Publish the wheel to PyPI (or a private index) and share the package name. Anyone
who pip installs it gets your model registered in their agstack-pnd instance.
- Deterministic: same inputs → same
ModelResult. - Documented: real
citation,author, and a cleardescription. - Honest metadata:
required_weather_fieldsmatches what you actually read. - Tested: unit tests that a reviewer can run in seconds.