Spaces:
Sleeping
Documento de Definición Técnica — AgroVisión (Plataforma Completa)
Propósito del documento: Servir como única fuente de verdad técnica y funcional para desarrolladores, arquitectos y sistemas de inteligencia artificial. Define la arquitectura modular, flujos de datos, APIs, modelos de base de datos, lógica de negocio y configuraciones del sistema de la plataforma AgroVisión.
Stack confirmado: UI en Astro + Tailwind (SPA estática), backend/gateway en FastAPI (sirve la UI en
/y la API en/api), base de datos en Supabase (PostgreSQL + PostGIS + Storage + Queues/PGMQ), agente conversacional con Groq (Llama 3) y teledetección con Copernicus Sentinel-2 / NASA POWER / Open-Meteo. Despliegue gratuito: Hugging Face Spaces (Docker); alternativa Render.⚠️ Actualización (Fases 8 y 10): este documento se redactó con la UI original en Shiny for Python. Desde la Fase 8 la UI es Astro + Tailwind servida por el gateway, y en la Fase 10 Shiny se eliminó por completo (sin
/shiny) y el despliegue pasó a Hugging Face Spaces (shinyapps.io quedó descartado: solo hospeda Shiny, no FastAPI). Donde el texto diga "Shiny / reactive.value / WebSocket / ShinyApps.io", léase: SPA Astro en el navegador / estado en memoria del navegador / HF Spaces. La arquitectura vigente está enarchitecture_agrovision.md(incl. §8 Seguridad).Licencia y modelo: AgroVisión es open-source bajo AGPL-3.0 (lo que permite usar YOLO26). El modelo de conteo es agnóstico: la app consume el artefacto
agrovision-plantcount(multi-candidato YOLO26/RF-DETR) por contrato, vía un adaptador de inferencia. El módulo de conteo arranca en standby (flagCOUNTING_ENABLED=false) hasta que el repo del modelo (proyecto separado) publique el artefacto.Documentos relacionados:
- El modelo de conteo se desarrolla en un repo separado (produce los pesos predeterminados).
- Investigación base:
Plan Detallado Data Science Agrícola.md- Mockup interactivo:
agrovisi_n_spa_prototype.html
0. Resumen Ejecutivo
AgroVisión es una plataforma de monitoreo agronómico de precisión que unifica tres pilares tecnológicos en una sola interfaz: (1) teledetección satelital con índices vegetativos sobre Sentinel-2, (2) visión computacional para el conteo automatizado de plantas a partir de ortomosaicos RGB capturados por drones, y (3) un agente conversacional (RAG) que contextualiza el historial de las parcelas con datos climáticos y de campo.
El sistema se diseña sobre una arquitectura de código abierto, sin servidores de costo fijo y desplegable íntegramente en capa gratuita, bajo un paradigma estricto de "Trae tu Propia Llave" (Bring Your Own Keys, BYOK) con cero persistencia de credenciales.
- Propósito: Centralizar fuentes geoespaciales (satélite, dron, clima) para optimizar decisiones agronómicas en campo, sin costos de infraestructura y sin exponer claves propietarias.
- Usuarios Objetivos: Agrónomos, productores y consultores agrícolas.
- Casos de Uso Core:
- El usuario delimita una parcela sobre un mapa interactivo (polígono EPSG:4326) y consulta la serie temporal de NDVI de Sentinel-2 cruzada con clima de NASA POWER / Open-Meteo.
- El usuario sube un ortomosaico RGB del dron; el sistema ejecuta el modelo de conteo predeterminado (agnóstico: YOLO26/RF-DETR, entrenado en un repo aparte) y devuelve conteo de plantas, densidad por hectárea, malezas y fallas de siembra.
- El usuario conversa con el agente AI, que invoca funciones tipadas (function calling) sobre la base de datos y las APIs externas para producir un diagnóstico agronómico argumentado.
- El usuario introduce sus credenciales en la última pestaña; estas viven solo en memoria de sesión, no se guardan ni almacenan, y se borran al refrescar la página.
Decisión de diseño clave (efimeralidad): A diferencia del mockup original (que usaba
localStorage), esta plataforma mantiene credenciales y datos del usuario exclusivamente en el estado reactivo en memoria de la sesión de Shiny. Cada conexión WebSocket es una sesión aislada; al refrescar/cerrar la pestaña, la sesión se destruye y todo dato se pierde. Esto elimina cualquier vector de persistencia accidental de secretos.
1. Arquitectura de Componentes (Modular y Desacoplada)
La plataforma es un gateway único: el backend FastAPI sirve la UI Astro estática en / y la API en /api (mismo origen). (El diseño original separaba UI Shiny y backend; desde la Fase 10 es un solo servicio — ver banner arriba.) El cómputo pesado (inferencia de visión, descargas satelitales) queda aislado en services/.
flowchart TD
classDef frontend fill:#BFDBFE,stroke:#2563EB,color:#1E3A8A
classDef gateway fill:#FDE047,stroke:#EAB308,color:#713F12
classDef service fill:#BBF7D0,stroke:#16A34A,color:#14532D
classDef broker fill:#FED7AA,stroke:#EA580C,color:#7C2D12
classDef db fill:#E9D5FF,stroke:#7C3AED,color:#4C1D95
classDef ext fill:#FECACA,stroke:#DC2626,color:#7F1D1D
UI["💻 Capa Cliente (UI)<br/>Astro + Tailwind (SPA estática)<br/>6 vistas · Leaflet-draw · Chart.js<br/>Estado efímero en memoria del navegador<br/>Servida por el gateway en /"]:::frontend
GW["🔀 Gateway / BFF<br/>FastAPI + Starlette (ASGI)<br/>sirve UI en / + API en /api · rate limiting<br/>proxy efímero de llaves · Host: HF Spaces"]:::gateway
WK["⚙️ Worker Asíncrono<br/>Consumidor PGMQ (vt=120)<br/>Inferencia RF-DETR-Nano (CPU)<br/>Mismo servicio/proceso de fondo en Render"]:::service
Q["✉️ Cola de Mensajes<br/>Supabase Queues (PGMQ)<br/>count_tasks"]:::broker
DB[("🗄️ Base de Datos<br/>Supabase PostgreSQL + PostGIS<br/>fields · ndvi_timeseries · plant_counts · chat_messages")]:::db
ST[("🗄️ Almacenamiento<br/>Supabase Storage (bucket privado)<br/>Signed URLs efímeras")]:::db
MODEL["🧠 Modelo Predeterminado<br/>RF-DETR-Nano (.pt / ONNX)<br/>Empaquetado en la imagen del backend<br/>Producido por el repo del modelo"]:::service
EXT["🌐 APIs Externas (BYOK)<br/>Copernicus CDSE (Sentinel-2)<br/>NASA POWER · Open-Meteo<br/>Groq (Llama 3)"]:::ext
UI <-->|"HTTPS + headers X-User-*-Key<br/>(WebSocket interno de Shiny)"| GW
GW <-->|REST| DB
GW -->|"Encola ortomosaico"| Q
Q <--> WK
WK -->|"Carga pesos"| MODEL
WK <--> ST
WK <--> DB
GW <-->|"Proxy efímero de credenciales"| EXT
1.1 Glosario de Módulos e Infraestructura
| Componente | Descripción de Responsabilidad | Tecnologías | Estrategia de Resiliencia / Despliegue |
|---|---|---|---|
| Capa Cliente (UI) | Interfaz interactiva de 6 módulos. Gestiona estado local efímero (credenciales, parcela activa, resultados) y orquesta llamadas al gateway. | Astro + Tailwind (SPA estática), Leaflet-draw, Chart.js (CDN) | Compilada a estático y servida por el gateway en /. Estado en memoria del navegador; refrescar = reset total. |
| Gateway / BFF | Punto de entrada único. Sirve la UI Astro, valida CORS, aplica rate limiting, recibe llaves por cabecera, orquesta teledetección, encola inferencia y proxy del LLM. | FastAPI, Starlette, httpx |
1 contenedor en Hugging Face Spaces (Docker). Sin secretos persistidos. |
| Worker Asíncrono | Consume la cola PGMQ, ejecuta inferencia RF-DETR-Nano sobre ortomosaicos, persiste conteos y genera Signed URLs. | FastAPI background task / proceso, onnxruntime, OpenCV, pgmq-sqlalchemy (asyncpg) |
Reintento automático por visibility timeout (vt=120). Tolerante a reinicios del contenedor gratuito. |
| Base de Datos + GIS | Almacén relacional/espacial del dominio (parcelas, series NDVI, conteos, chat). | Supabase PostgreSQL + PostGIS | Supabase Free (500 MB DB). Índices GIST/GIN. Modelo BYOK (proyecto del usuario). Keep-alive contra pausa a 7 días. |
| Almacenamiento de Objetos | Custodia privada de ortomosaicos e imágenes de resultado. | Supabase Storage | Bucket privado; visualización solo vía Signed URLs (expires_in=600). |
| Cola de Mensajes | Desacopla la subida de imágenes pesadas de la inferencia para evitar timeouts. | Supabase Queues (PGMQ / pgmq) | Transaccional ACID embebido en Postgres; sin broker externo (no Redis). |
| Modelo de Visión | Pesos preentrenados de conteo; solo inferencia, nunca entrenamiento. Se carga vía adaptador de inferencia (onnxruntime o ultralytics según la arquitectura ganadora) — agnóstico al modelo. Arranca en standby. |
agrovision-plantcount (ONNX; multi-candidato YOLO26/RF-DETR) |
Artefacto predeterminado de Hugging Face Hub (build); AGPL-3.0 aceptada (app open-source). |
2. Flujo de Datos e Integración
2.1 Orígenes y Destinos de Datos
- Entradas del Sistema:
- Inputs de Usuario: Polígonos de parcela (mapa interactivo → GeoJSON EPSG:4326), carga de ortomosaico RGB del dron (
ui.input_file), consultas al chat, credenciales BYOK (Groq, Copernicus, Supabase URL + anon key). - Sincronizaciones Externas: Sentinel-2 L2A (Copernicus CDSE vía STAC), NASA POWER (REST por coordenadas), Open-Meteo (REST público sin llave), Groq (inferencia LLM).
- Inputs de Usuario: Polígonos de parcela (mapa interactivo → GeoJSON EPSG:4326), carga de ortomosaico RGB del dron (
- Salidas del Sistema:
- Visualizaciones: KPIs, series NDVI vs. clima (Plotly), ortomosaico con bounding boxes (Signed URL efímera), respuestas del agente.
- Persistencia opcional (Supabase del usuario):
fields,ndvi_timeseries,plant_counts,chat_messages.
2.2 Grafo de Dependencias Reactivas y Propagación
Shiny es un framework reactivo: los input.* disparan @reactive.calc y @render.* automáticamente. El flujo de una operación de conteo es:
flowchart LR
A["input.file_upload<br/>(ortomosaico RGB)"] --> B["@reactive.calc<br/>validación + lectura datapath"]
B --> C["POST /api/count<br/>(httpx, headers BYOK)"]
C --> D["FastAPI: subir a Storage<br/>+ pgmq.send(count_tasks)"]
D --> E["Worker: pgmq.read(vt=120)<br/>inferencia RF-DETR-Nano"]
E --> F["plant_counts + Signed URL"]
F --> G["Polling GET /api/count/{id}"]
G --> H["@render.ui / @render.plot<br/>conteo, densidad, overlay"]
Nota de efimeralidad en el flujo: las credenciales viajan en cada petición como cabeceras
X-User-Groq-Key,X-User-Copernicus-Secret, etc. El gateway las usa en la ejecución y las descarta; nunca se escriben a disco, log ni base de datos. En la UI residen en unreactive.valueque muere con la sesión.
3. Modelo de Datos (Bases de Datos)
El esquema es BYOK: el usuario provee su propio proyecto Supabase (URL + anon key, inyectados por sesión). El repo entrega las migraciones para crearlo en una cuenta gratuita. La persistencia es opcional: la plataforma funciona en modo efímero (sin BD) y solo escribe cuando el usuario configura Supabase.
3.1 Diagrama Entidad-Relación (ERD)
erDiagram
USUARIO ||--o{ FIELDS : "posee"
FIELDS ||--o{ NDVI_TIMESERIES : "registra"
FIELDS ||--o{ PLANT_COUNTS : "asocia"
USUARIO ||--o{ PLANT_COUNTS : "ejecuta"
USUARIO ||--o{ CHAT_MESSAGES : "conversa"
USUARIO {
uuid id PK
string email "auth.users (Supabase Auth)"
timestamp created_at
}
FIELDS {
uuid id PK
uuid user_id FK
string name
geometry geom "Polygon, 4326 (GIST)"
timestamp created_at
}
NDVI_TIMESERIES {
int id PK
uuid field_id FK
date date
float mean_ndvi
float min_ndvi
float max_ndvi
float cloud_cover
string source "sentinel2"
}
PLANT_COUNTS {
int id PK
uuid user_id FK
uuid field_id FK
string image_url "Storage privado"
int count
jsonb result_json "boxes + clases (GIN)"
timestamp processed_at
}
CHAT_MESSAGES {
int id PK
uuid user_id FK
string session_id
string role "user|assistant"
string content
timestamp created_at
}
3.2 DDL de Referencia (PostGIS)
-- Extensión espacial
create extension if not exists postgis;
-- 1. Parcelas / Lotes agrícolas
create table fields (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users on delete cascade,
name text not null,
geom geometry(Polygon, 4326) not null, -- WGS84
created_at timestamp default now()
);
create index fields_geom_gist_idx on fields using gist (geom);
-- 2. Serie temporal de índices vegetativos (Sentinel-2)
create table ndvi_timeseries (
id serial primary key,
field_id uuid references fields on delete cascade,
date date not null,
mean_ndvi float not null,
min_ndvi float,
max_ndvi float,
cloud_cover float,
source text default 'sentinel2',
constraint unique_field_date unique (field_id, date)
);
create index ndvi_field_date_idx on ndvi_timeseries (field_id, date);
-- 3. Conteos por dron
create table plant_counts (
id serial primary key,
user_id uuid references auth.users on delete cascade,
field_id uuid references fields on delete set null,
image_url text not null, -- ruta en Storage privado
count integer not null,
result_json jsonb, -- {"boxes": [[x1,y1,x2,y2,conf,cls]], "classes": {...}}
processed_at timestamp default now()
);
create index plant_counts_json_gin_idx on plant_counts using gin (result_json);
-- 4. Memoria conversacional
create table chat_messages (
id serial primary key,
user_id uuid references auth.users on delete cascade,
session_id text not null,
role text not null check (role in ('user', 'assistant')),
content text not null,
created_at timestamp default now()
);
create index chat_session_history_idx on chat_messages (session_id, created_at);
3.3 Políticas de Integridad y Seguridad
- Row Level Security (RLS): cada tabla se filtra por
auth.uid() = user_idpara aislar productores en el mismo proyecto Supabase. - Storage privado + Signed URLs: el bucket
drone-imageses privado; toda visualización usa enlaces firmados con expiración de 10 minutos. - Canonicalización:
fields.namese normaliza a Title Case;sourceen minúsculas. - Geometría:
geomsiempre en SRID 4326; validaciónST_IsValidantes de insertar.
4. Contratos de API (Endpoints Críticos)
El gateway FastAPI es el único servicio backend expuesto. Todas las llaves del usuario viajan en cabeceras X-User-* y se descartan tras la ejecución.
4.1 Endpoints de la API
| Método | Path | Descripción | Entrada | Respuesta Exitosa |
|---|---|---|---|---|
GET |
/api/status |
Healthcheck y versión. | — | {"status":"ok","version":"1.0.0"} |
POST |
/api/ndvi |
Calcula estadística zonal NDVI de Sentinel-2 para un polígono y rango de fechas. | {"geojson":{...},"start":"YYYY-MM-DD","end":"YYYY-MM-DD"} + X-User-Copernicus-Secret |
{"series":[{"date":"...","mean_ndvi":0.72,"cloud_cover":4.1}]} |
POST |
/api/count |
Encola un ortomosaico para conteo. Retorna id de tarea. | multipart/form-data (imagen) + field_id? |
{"task_id":"uuid","status":"queued"} |
GET |
/api/count/{task_id} |
Sondea el estado/resultado de la inferencia. | — | {"status":"done","count":124,"density":72400,"weeds":12,"failures":1.2,"overlay_url":"<signed>"} |
POST |
/api/chat |
Conversa con el agente RAG (streaming SSE). | {"session_id":"...","message":"..."} + X-User-Groq-Key |
text/event-stream (tokens + logs de herramientas) |
POST |
/api/weather |
Historial agroclimático por coordenadas (NASA POWER / Open-Meteo). | {"lat":-34.6,"lon":-58.4,"start":"...","end":"..."} |
{"temp":[...],"precip":[...],"radiation":[...]} |
CORS:
CORSMiddlewareconallow_origins=[<dominio ShinyApps.io>, "http://localhost:8000"],allow_credentials=True,allow_methods=["*"],allow_headers=["*"]. El comodín en headers es necesario para inyectarX-User-Groq-Key,X-User-Copernicus-Secret, etc.
4.2 Esquema de Mensajes Asíncronos (PGMQ)
- Cola:
count_tasks - Productor:
POST /api/count→ sube a Storage +pgmq.send. - Consumidor: worker con
pgmq.read("count_tasks", vt=120); al éxitopgmq.archive.
{
"task_id": "uuid-v4",
"event_type": "drone.count.requested",
"timestamp": "2026-06-03T12:00:00Z",
"actor_id": "user_uuid",
"data": {
"image_path": "raw/user_99/field_a.png",
"field_id": "field_uuid",
"model": "agrovision-plantcount"
}
}
Si el contenedor gratuito colapsa o excede el límite, el mensaje vuelve a ser visible al expirar vt=120 y se reintenta automáticamente (tolerancia a fallos).
5. Lógica de Negocio, Motores y Fórmulas
5.1 Índice de Vegetación de Diferencia Normalizada (NDVI)
A partir de las bandas de reflectancia Sentinel-2 L2A (B08 NIR ~842 nm, B04 Rojo ~665 nm):
El gateway descarga los rásters intersecados por el GeoJSON con pystac-client (Copernicus CDSE) y calcula estadística zonal (media, min, max, % nubes) por fecha.
5.2 Distancia de Muestreo Terrestre (GSD)
Relaciona la resolución espacial con la geometría de vuelo del dron:
Donde $S_w$ = ancho del sensor (mm), $H$ = altura de vuelo (m), $f$ = distancia focal (mm), $I_w$ = ancho de la imagen (px). Un vuelo a baja altitud (~15–30 m) produce un GSD nominal ≈ 1.2 cm/px, suficiente para segmentar plantas y brotes tempranos. El GSD se usa para convertir conteos a densidad real.
5.3 Densidad de Siembra
PostGIS calcula el área de la parcela en m²; el worker divide el conteo más reciente entre el área para obtener plantas por hectárea.
5.4 Herramientas del Agente (Function Calling)
El agente (Llama 3 70B vía Groq) no especula: traduce la intención del usuario en llamadas a funciones tipadas.
| Herramienta | Firma | Acción |
|---|---|---|
get_vegetation_index_trend |
(field_name, start_date, end_date) -> str |
Une fields ⋈ ndvi_timeseries; calcula ΔNDVI y diagnóstico. |
get_weather_context |
(lat, lon, start_date, end_date) -> dict |
Llama NASA POWER: radiación, humedad, temperaturas extremas. |
get_field_planting_density |
(field_name) -> str |
ST_Area de la parcela + último conteo → densidad pl/Ha. |
Ejemplo de plan de 3 pasos ante "¿Por qué bajó el NDVI de mi Lote A en agosto?": (1) get_vegetation_index_trend confirma la caída; (2) get_weather_context detecta déficit hídrico/exceso térmico; (3) sintetiza una respuesta agronómica que asocia ambos.
5.5 Reglas de Validación
- Rango razonable NDVI: valores fuera de $[-1, 1]$ disparan warning, no error duro.
- Tamaño de imagen:
ui.input_filelimita el ortomosaico (p. ej. ≤ 50 MB) para no saturar Render/Storage. - Cobertura de nubes: escenas con
cloud_cover > 60%se marcan como baja confianza. - Credenciales ausentes: si falta la llave requerida por un módulo, la UI muestra un toast que redirige a la pestaña de Credenciales.
6. Interfaz de Usuario (UI/UX) y Estados Reactivos
La UI es una SPA de 5 módulos construida con ui.page_navbar (un nav_panel por módulo), replicando el mockup. Paleta: Warm Neutrals & Deep Canopy (verde #15803D sobre stone #FAF9F6).
6.1 Módulos (pestañas)
- Resumen de Campo — KPIs (NDVI promedio, densidad, temperatura de suelo, alertas del agente) + gráfica de evolución + diagnóstico del asistente. Selector de parcela activa en el topbar.
- Teledetección (GIS) — Mapa interactivo (
ipyleaflet) para dibujar polígonos EPSG:4326 + serie temporal NDVI vs. precipitación (Plotly de doble eje). Explica la fórmula NDVI. - Detección Dron (Conteo) — arranca en standby: mientras
COUNTING_ENABLED=false, muestra un aviso "Módulo en preparación — disponible cuando el modelo de conteo esté publicado". Al activarse: carga de ortomosaico (ui.input_file), botón "Iniciar Conteo", visualizador con bounding boxes y panel de métricas (total plantas/arbustos, densidad, malezas, fallas). El modelo es predeterminado y agnóstico (definido en el repo del modelo; multi-candidato YOLO26/RF-DETR; cultivo inicial: arándano). - Asistente Agentivo (RAG) — Consola de chat con sugerencias precargadas, historial y logs de ejecución del agente ("Pensando…", "Llamando herramienta…").
- Credenciales & APIs — Formularios para Groq, Copernicus/Sentinel Hub, Supabase URL + anon key, con barras de cuota informativas.
6.2 Layout (Wireframe ASCII)
┌───────────────────────────────────────────────────────────────────────────┐
│ 🌱 AgroVisión [Parcela Activa: Lote Norte ▾] [👤 Agrónomo] │
├──────────────┬────────────────────────────────────────────────────────────┤
│ NAV (navset)│ PANEL ACTIVO (nav_panel) │
│ ─────────── │ ───────────────────────────────────────────────────────── │
│ ▸ Resumen │ ┌─ KPI ─┐ ┌─ KPI ─┐ ┌─ KPI ─┐ ┌─ KPI ─┐ │
│ ▸ Teledet. │ │ NDVI │ │ Dens. │ │ Temp. │ │Alertas│ │
│ ▸ Dron YOLO │ └───────┘ └───────┘ └───────┘ └───────┘ │
│ ▸ Asistente │ [ Mapa GIS / Visualizador / Chat según módulo ] │
│ ▸ Credenciales│ [ Gráficas Plotly · overlay de detección ] │
│ │ │
│ ● Supabase │ │
│ (sesión) │ │
└──────────────┴────────────────────────────────────────────────────────────┘
6.3 Comportamientos Reactivos y Efimeralidad
- Estado por sesión:
reactive.valueguarda credenciales, parcela activa y resultados en memoria. No haylocalStorageni cookies persistentes. - Reset al refrescar: recargar la pestaña abre una nueva sesión WebSocket → todo el estado se reinicia y las llaves desaparecen. La pestaña de Credenciales lo advierte de forma destacada:
⚠️ Tus credenciales se usan solo durante esta sesión. No se guardan ni almacenan. Si actualizas o cierras la página, todos los datos y llaves se borrarán y deberás reingresarlos.
- Polling no bloqueante: tras
POST /api/count, la UI sondeaGET /api/count/{id}conreactive.invalidate_laterhastastatus="done", mostrando un overlay de progreso. - Debounce: inputs de fechas/coordenadas aplican retardo antes de llamar al gateway.
7. Configuración de Entornos y Políticas de Despliegue
7.1 Variables de Entorno (.env.example)
# --- General ---
APP_ENV=development # development | production
LOG_LEVEL=info
# --- Gateway (FastAPI sirve UI Astro en / + API en /api) ---
API_PORT=8000
ALLOWED_ORIGINS=http://localhost:4321 # solo para el dev server de Astro (cross-origin);
# en HF Spaces UI y API comparten origen
RATE_LIMIT_PER_MIN=120 # rate limiting de /api (anti-abuso); 0 desactiva
# --- Despliegue (Hugging Face Spaces) ---
HF_TOKEN= # token write
HF_SPACE_ID= # <usuario>/<space>
# --- Modelo predeterminado (descargado de Hugging Face Hub en el build) ---
MODEL_PATH=/app/models/agrovision-plantcount-v2.0.0.onnx
MODEL_VERSION=2.0.0
HF_MODEL_REPO=<org>/agrovision-plantcount # repo de modelos en Hugging Face Hub
COUNTING_ENABLED=false # standby: el módulo de conteo se activa cuando el modelo esté publicado
# --- NOTA DE SEGURIDAD ---
# Las credenciales de usuario (Groq, Copernicus, Supabase) NO van aquí en producción.
# Se inyectan por sesión desde la UI vía cabeceras X-User-*-Key y se descartan.
# Estas variables solo sirven para desarrollo local con datos de prueba.
DEV_GROQ_API_KEY=
DEV_COPERNICUS_CLIENT_ID=
DEV_COPERNICUS_CLIENT_SECRET=
DEV_SUPABASE_URL=
DEV_SUPABASE_ANON_KEY=
7.2 Dockerización y docker-compose (Local)
Hay un solo Dockerfile (multi-stage: Node compila Astro → Python/uv corre el gateway que sirve UI + API). El docker-compose.yml actual levanta solo ese servicio:
# docker-compose.yml (real)
services:
api:
build: { context: ., dockerfile: Dockerfile }
ports: ["8000:8000"] # sirve UI en / y API en /api
environment: [COUNTING_ENABLED=false]
volumes: ["./models:/app/models:ro", "./sample_data:/app/sample_data:ro"]
Ya no hay servicio
uiaparte (la UI Astro la sirve el gateway). ElDockerfilede la raíz es el equivalente endurecido para Hugging Face Spaces (usuario 1000,app_port8000). Las imágenes de dron de ejemplo viven ensample_data/para probar el conteo (mock).
7.3 Despliegue en Producción (Capa Gratuita)
| Componente | Plataforma Gratuita | Límites Relevantes | Caveat |
|---|---|---|---|
| Gateway (UI Astro + API) | Hugging Face Spaces (SDK Docker) | CPU básica 2 vCPU · 16 GB RAM · duerme a 48 h | 1 contenedor; HF construye el Dockerfile al hacer git push. Cold start 30–60 s. |
| Alternativa: gateway | Render Free | 512 MB RAM · 0.1 CPU · duerme a 15 min · 750 h/mes | mismo Dockerfile (raíz) + render.yaml. |
| BD / Storage / Colas | Supabase Free | 500 MB DB · 1 GB Storage · pausa a 7 días sin actividad | Keep-alive (cron ligero) para evitar la pausa. |
| LLM | Groq Free | Rate limit por minuto/día | BYOK: la llave la pone el usuario. |
| Satélite / Clima | Copernicus CDSE · NASA POWER · Open-Meteo | Cuotas públicas | Open-Meteo no requiere llave. |
Pipeline de despliegue (resumen):
# 1. Gateway (UI Astro + API) -> Hugging Face Spaces (SDK Docker)
# HF construye el Dockerfile de la raíz; un git push basta.
.\scripts\deploy_hf.ps1 -Force # primer deploy
# 2. BD -> aplicar migraciones PostGIS al proyecto Supabase del usuario
uv run python -m backend.db.migrate
Un solo origen: UI y API se sirven desde el mismo gateway, así que no hay CORS entre front y back ni
API_BASE_URLapuntando a otro host. La "Regla de Oro" (rutas relativas, hash-routing, CSS inline) se conserva como salvaguarda para sub-paths. Detalle del despliegue enejecucion.md§5.
7.4 Gestión del Modelo Predeterminado
- AgroVisión no entrena: consume pesos producidos por el repo del modelo de conteo (proyecto separado).
- El artefacto
agrovision-plantcount-v2.0.0.onnx(RF-DETR-Nano exportado a ONNX) se publica en Hugging Face Hub y se descarga conhf_hub_downloaddurante el build de la imagen del backend, copiándose a/app/models/(MODEL_VERSIONfija la trazabilidad). El nombre es de marca, desacoplado de la arquitectura interna. - Contrato de inferencia que el worker espera: entrada = tile RGB; salida =
{"boxes":[[x1,y1,x2,y2,conf,cls]], "count":int}compatible conplant_counts.result_json.
Apéndice A — Seguridad de Credenciales (Resumen)
- Cero persistencia: ninguna llave se guarda en disco, base de datos, log ni
localStorage. - Solo memoria de sesión:
reactive.valueen Shiny; muere al refrescar/cerrar. - Transmisión por cabecera:
X-User-*-Keysobre HTTPS; el gateway es un proxy efímero. - Aviso explícito al usuario: la pestaña de Credenciales declara que nada se guarda y que refrescar borra todo.
- Aislamiento por sesión: cada WebSocket de Shiny es independiente; no hay fuga cruzada entre usuarios concurrentes.
Apéndice B — Fuentes
Basado en Plan Detallado Data Science Agrícola.md (41 fuentes citadas, incl. Copernicus CDSE, NASA POWER, Supabase Queues/PGMQ, RF-DETR (Apache 2.0), SAM) y verificación de límites de capa gratuita (ShinyApps.io, Render, Supabase).