Spaces:
Sleeping
Sleeping
ci: deploy terramind_tim (cf4492f1e576)
Browse files- README.package.md +2 -0
- nutonic_terramind_tim_local/gradio_panel.py +2 -1
- nutonic_terramind_tim_local/inputs_build.py +6 -0
- nutonic_terramind_tim_local/run.py +52 -0
- nutonic_terramind_tim_local/space_api.py +2 -1
- nutonic_terramind_tim_local/tim_defaults.py +6 -0
- pyproject.toml +2 -2
- requirements.txt +1 -1
README.package.md
CHANGED
|
@@ -6,6 +6,8 @@ Optional **GPU/CPU** batch tool under `inference/*` (not the thin game `server/`
|
|
| 6 |
|
| 7 |
**Authority:** `rules/06-server-vlm-tim-and-on-device-ml.md`, `rules/12-python-gradio-terramind-server.md`, `docs/PRO-TAB-VLM-ORCHESTRATION-SPEC.md` (Coordinates → `ai_lat` / `ai_lon` for catalog pipelines).
|
| 8 |
|
|
|
|
|
|
|
| 9 |
## Install (separate venv recommended)
|
| 10 |
|
| 11 |
```bash
|
|
|
|
| 6 |
|
| 7 |
**Authority:** `rules/06-server-vlm-tim-and-on-device-ml.md`, `rules/12-python-gradio-terramind-server.md`, `docs/PRO-TAB-VLM-ORCHESTRATION-SPEC.md` (Coordinates → `ai_lat` / `ai_lon` for catalog pipelines).
|
| 8 |
|
| 9 |
+
**Default backbone:** NU:TONIC pins the largest TerraMind-1.0 TIM variant as ``nutonic_terramind_tim_local.tim_defaults.DEFAULT_TIM_MODEL_ID`` (**``terramind_v1_large_tim``**). Example YAMLs and the HF Space / Gradio demos use that unless you override ``model_id``.
|
| 10 |
+
|
| 11 |
## Install (separate venv recommended)
|
| 12 |
|
| 13 |
```bash
|
nutonic_terramind_tim_local/gradio_panel.py
CHANGED
|
@@ -5,6 +5,7 @@ from typing import Any
|
|
| 5 |
import gradio as gr
|
| 6 |
|
| 7 |
from nutonic_terramind_tim_local.space_api import health, tim_infer
|
|
|
|
| 8 |
|
| 9 |
|
| 10 |
def _infer_from_json(req: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -26,7 +27,7 @@ def build_gradio_blocks() -> gr.Blocks:
|
|
| 26 |
value={
|
| 27 |
"analysis_profile": "brief_only",
|
| 28 |
"config": {
|
| 29 |
-
"model_id":
|
| 30 |
"pretrained": True,
|
| 31 |
"modalities": ["RGB"],
|
| 32 |
"tim_modalities": ["LULC", "location"],
|
|
|
|
| 5 |
import gradio as gr
|
| 6 |
|
| 7 |
from nutonic_terramind_tim_local.space_api import health, tim_infer
|
| 8 |
+
from nutonic_terramind_tim_local.tim_defaults import DEFAULT_TIM_MODEL_ID
|
| 9 |
|
| 10 |
|
| 11 |
def _infer_from_json(req: dict[str, Any]) -> dict[str, Any]:
|
|
|
|
| 27 |
value={
|
| 28 |
"analysis_profile": "brief_only",
|
| 29 |
"config": {
|
| 30 |
+
"model_id": DEFAULT_TIM_MODEL_ID,
|
| 31 |
"pretrained": True,
|
| 32 |
"modalities": ["RGB"],
|
| 33 |
"tim_modalities": ["LULC", "location"],
|
nutonic_terramind_tim_local/inputs_build.py
CHANGED
|
@@ -418,4 +418,10 @@ def _build_inputs(
|
|
| 418 |
}
|
| 419 |
for label, (_tensor, meta) in temporal_stac.items()
|
| 420 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 421 |
return out, aux
|
|
|
|
| 418 |
}
|
| 419 |
for label, (_tensor, meta) in temporal_stac.items()
|
| 420 |
}
|
| 421 |
+
try:
|
| 422 |
+
s2p = stac_s2_params_from_cfg(in_cfg, row)
|
| 423 |
+
if s2p.get("lat") is not None and s2p.get("lon") is not None:
|
| 424 |
+
aux["request_wgs84"] = {"latitude": float(s2p["lat"]), "longitude": float(s2p["lon"])}
|
| 425 |
+
except Exception:
|
| 426 |
+
pass
|
| 427 |
return out, aux
|
nutonic_terramind_tim_local/run.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
|
|
|
| 5 |
import os
|
| 6 |
import random
|
| 7 |
import sys
|
|
@@ -73,6 +74,54 @@ def _set_ensemble_iteration_seed(seed: int) -> None:
|
|
| 73 |
torch.cuda.manual_seed_all(s)
|
| 74 |
|
| 75 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
def _pair_from_wgs(wgs: Mapping[str, Any] | None) -> tuple[float | None, float | None]:
|
| 77 |
if not wgs or not isinstance(wgs, dict):
|
| 78 |
return None, None
|
|
@@ -165,6 +214,9 @@ def _export_row(
|
|
| 165 |
tim_modality_outputs = build_tim_modality_outputs(
|
| 166 |
model, tim_raw, tensor_sample_limit=sample_limit, policy=tim_policy
|
| 167 |
)
|
|
|
|
|
|
|
|
|
|
| 168 |
profile = str(
|
| 169 |
analysis_profile
|
| 170 |
or export_cfg.get("analysis_profile")
|
|
|
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
| 5 |
+
import math
|
| 6 |
import os
|
| 7 |
import random
|
| 8 |
import sys
|
|
|
|
| 74 |
torch.cuda.manual_seed_all(s)
|
| 75 |
|
| 76 |
|
| 77 |
+
def _haversine_km(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
|
| 78 |
+
r = 6371.0
|
| 79 |
+
p1, p2 = math.radians(lat1), math.radians(lat2)
|
| 80 |
+
dphi = math.radians(lat2 - lat1)
|
| 81 |
+
dl = math.radians(lon2 - lon1)
|
| 82 |
+
a = math.sin(dphi / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(dl / 2) ** 2
|
| 83 |
+
return 2 * r * math.asin(min(1.0, math.sqrt(max(0.0, a))))
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def _sanitize_coordinates_in_row(
|
| 87 |
+
row: dict[str, Any],
|
| 88 |
+
*,
|
| 89 |
+
inputs_aux: Mapping[str, Any] | None,
|
| 90 |
+
max_decode_drift_km: float = 250.0,
|
| 91 |
+
) -> None:
|
| 92 |
+
"""If decoded TiM WGS84 is far from the requested batch lat/lon, replace with request (bad decode guard)."""
|
| 93 |
+
if not isinstance(inputs_aux, Mapping):
|
| 94 |
+
return
|
| 95 |
+
req = inputs_aux.get("request_wgs84")
|
| 96 |
+
if not isinstance(req, dict):
|
| 97 |
+
return
|
| 98 |
+
try:
|
| 99 |
+
elat = float(req["latitude"])
|
| 100 |
+
elon = float(req["longitude"])
|
| 101 |
+
except (KeyError, TypeError, ValueError):
|
| 102 |
+
return
|
| 103 |
+
tmo = row.get("tim_modality_outputs")
|
| 104 |
+
if not isinstance(tmo, dict):
|
| 105 |
+
return
|
| 106 |
+
coord = tmo.get("Coordinates")
|
| 107 |
+
if not isinstance(coord, dict):
|
| 108 |
+
return
|
| 109 |
+
try:
|
| 110 |
+
clat = float(coord["latitude"]) # type: ignore[arg-type]
|
| 111 |
+
clon = float(coord["longitude"]) # type: ignore[arg-type]
|
| 112 |
+
except (KeyError, TypeError, ValueError):
|
| 113 |
+
return
|
| 114 |
+
drift = _haversine_km(clat, clon, elat, elon)
|
| 115 |
+
if drift <= max_decode_drift_km:
|
| 116 |
+
return
|
| 117 |
+
coord["latitude"] = elat
|
| 118 |
+
coord["longitude"] = elon
|
| 119 |
+
coord["replaced_decode_invalid"] = True
|
| 120 |
+
coord["original_decode_km_drift"] = round(float(drift), 2)
|
| 121 |
+
coord["original_latitude"] = clat
|
| 122 |
+
coord["original_longitude"] = clon
|
| 123 |
+
|
| 124 |
+
|
| 125 |
def _pair_from_wgs(wgs: Mapping[str, Any] | None) -> tuple[float | None, float | None]:
|
| 126 |
if not wgs or not isinstance(wgs, dict):
|
| 127 |
return None, None
|
|
|
|
| 214 |
tim_modality_outputs = build_tim_modality_outputs(
|
| 215 |
model, tim_raw, tensor_sample_limit=sample_limit, policy=tim_policy
|
| 216 |
)
|
| 217 |
+
row_prelim: dict[str, Any] = {"tim_modality_outputs": tim_modality_outputs}
|
| 218 |
+
_sanitize_coordinates_in_row(row_prelim, inputs_aux=inputs_aux)
|
| 219 |
+
tim_modality_outputs = row_prelim["tim_modality_outputs"]
|
| 220 |
profile = str(
|
| 221 |
analysis_profile
|
| 222 |
or export_cfg.get("analysis_profile")
|
nutonic_terramind_tim_local/space_api.py
CHANGED
|
@@ -9,6 +9,7 @@ from fastapi import Body, FastAPI, HTTPException
|
|
| 9 |
|
| 10 |
from nutonic_terramind_tim_local.spaces_zero import apply_zero_gpu
|
| 11 |
from nutonic_terramind_tim_local.terramind_patches import terramind_patch_diagnostics
|
|
|
|
| 12 |
|
| 13 |
app = FastAPI(title="NU:TONIC TerraMind TiM local (Space)", version="0.1.0")
|
| 14 |
|
|
@@ -39,7 +40,7 @@ def tim_export(
|
|
| 39 |
examples=[
|
| 40 |
{
|
| 41 |
"config": {
|
| 42 |
-
"model_id":
|
| 43 |
"pretrained": True,
|
| 44 |
"modalities": ["RGB"],
|
| 45 |
"tim_modalities": ["LULC", "location"],
|
|
|
|
| 9 |
|
| 10 |
from nutonic_terramind_tim_local.spaces_zero import apply_zero_gpu
|
| 11 |
from nutonic_terramind_tim_local.terramind_patches import terramind_patch_diagnostics
|
| 12 |
+
from nutonic_terramind_tim_local.tim_defaults import DEFAULT_TIM_MODEL_ID
|
| 13 |
|
| 14 |
app = FastAPI(title="NU:TONIC TerraMind TiM local (Space)", version="0.1.0")
|
| 15 |
|
|
|
|
| 40 |
examples=[
|
| 41 |
{
|
| 42 |
"config": {
|
| 43 |
+
"model_id": DEFAULT_TIM_MODEL_ID,
|
| 44 |
"pretrained": True,
|
| 45 |
"modalities": ["RGB"],
|
| 46 |
"tim_modalities": ["LULC", "location"],
|
nutonic_terramind_tim_local/tim_defaults.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Canonical TerraMind TiM backbone id for NU:TONIC (TerraTorch ``BACKBONE_REGISTRY``)."""
|
| 2 |
+
|
| 3 |
+
# Largest TerraMind-1.0 TIM variant (IBM ``terratorch`` registry). Prefer this unless profiling or CI forces smaller.
|
| 4 |
+
DEFAULT_TIM_MODEL_ID = "terramind_v1_large_tim"
|
| 5 |
+
|
| 6 |
+
__all__ = ["DEFAULT_TIM_MODEL_ID"]
|
pyproject.toml
CHANGED
|
@@ -13,7 +13,7 @@ dependencies = [
|
|
| 13 |
"terratorch>=1.1.1,<1.3",
|
| 14 |
"PyYAML>=6.0.1",
|
| 15 |
"numpy>=1.26.0",
|
| 16 |
-
"Pillow>=
|
| 17 |
# Align with TerraTorch → peft: older peft imports HybridCache from transformers;
|
| 18 |
# transformers 5.2+ reshaped exports — peft 0.18.1+ avoids the stale import path.
|
| 19 |
"peft>=0.18.1",
|
|
@@ -31,7 +31,7 @@ s2 = ["pystac-client>=0.7.0", "rasterio>=1.3.9", "boto3>=1.34.0"]
|
|
| 31 |
space = [
|
| 32 |
"fastapi>=0.115.0",
|
| 33 |
"uvicorn[standard]>=0.32.0",
|
| 34 |
-
"gradio>=
|
| 35 |
"spaces>=0.44.0,<1",
|
| 36 |
]
|
| 37 |
|
|
|
|
| 13 |
"terratorch>=1.1.1,<1.3",
|
| 14 |
"PyYAML>=6.0.1",
|
| 15 |
"numpy>=1.26.0",
|
| 16 |
+
"Pillow>=12.2.0",
|
| 17 |
# Align with TerraTorch → peft: older peft imports HybridCache from transformers;
|
| 18 |
# transformers 5.2+ reshaped exports — peft 0.18.1+ avoids the stale import path.
|
| 19 |
"peft>=0.18.1",
|
|
|
|
| 31 |
space = [
|
| 32 |
"fastapi>=0.115.0",
|
| 33 |
"uvicorn[standard]>=0.32.0",
|
| 34 |
+
"gradio>=6.7.0",
|
| 35 |
"spaces>=0.44.0,<1",
|
| 36 |
]
|
| 37 |
|
requirements.txt
CHANGED
|
@@ -2,7 +2,7 @@ torch>=2.2.0
|
|
| 2 |
terratorch>=1.1.1,<1.3
|
| 3 |
PyYAML>=6.0.1
|
| 4 |
numpy>=1.26.0
|
| 5 |
-
Pillow>=
|
| 6 |
peft>=0.18.1
|
| 7 |
transformers>=4.48.0
|
| 8 |
urllib3>=2.6.3
|
|
|
|
| 2 |
terratorch>=1.1.1,<1.3
|
| 3 |
PyYAML>=6.0.1
|
| 4 |
numpy>=1.26.0
|
| 5 |
+
Pillow>=12.2.0
|
| 6 |
peft>=0.18.1
|
| 7 |
transformers>=4.48.0
|
| 8 |
urllib3>=2.6.3
|