# Endpoints y contratos

## Endpoint principal

```http
POST /controlador.php
Content-Type: application/json
```

Request:

```json
{
  "accion": "ia.logistica.preguntar",
  "pregunta": "que envios estan demorados hoy?",
  "contextoUi": {
    "modulo": "envios.listado",
    "filtros": {},
    "seleccion": []
  }
}
```

`contextoUi` es opcional para el razonamiento. El agente consulta globalmente por defecto y solo usa la vista activa cuando la pregunta contiene referencias como "estos", "esta pantalla", "lo que estoy viendo", "filtrados" o "seleccionados".

Response:

```json
{
  "ok": true,
  "respuesta": "Tenes 12 envios con riesgo operativo...",
  "intencion": "analisis_envios",
  "fuentes": [
    { "tipo": "planner", "nombre": "global" },
    { "tipo": "db", "nombre": "envios" }
  ],
  "acciones": [],
  "debug": {
    "modelo": "qwen2.5:7b-instruct",
    "plan": {
      "dominio": "envios",
      "intencion": "conteo_envios",
      "base": "global",
      "estado": { "did": 7, "nombre": "A retirar" }
    }
  }
}
```

Ejemplos esperados:

```text
Usuario: cuantos envios a retirar tengo?
Respuesta: Tenes 15 envios en estado A retirar.

Usuario: de 1 semana para atras cuanto tengo a retirar?
Respuesta: Tenes 15 envios en estado A retirar entre 2026-06-01 y 2026-06-08.

Usuario: cuantos envios tuve?
Respuesta: Para verlo bien, decime el rango de fechas y si queres contar por fecha de estado, venta o carga.

Usuario: abrime envios filtrado por el cliente mongocho
Respuesta: Tenes 0 envios.
Accion: abrir `envios.listado` con `{ "filtros": { "nombre": "mongocho" }, "page": 1 }`.

Usuario: decime cuantos pendientes tengo por zonas?
Respuesta: Tenes 82 envios pendientes por zona: Sin zona: 73, Sur: 7, Norte: 2.
Capacidad: `informes.porzonas`.
Accion: abrir `informes.porzonas`.

Usuario: podes crear una ruta para envios de zona sur, para el chofer teta? si no los tiene asignacelos.
Respuesta: Zona Sur: encontre 7 envios. 0 ya estan asignados a Chofer Teta. 7 estan sin asignar y puedo prepararlos para asignar a Chofer Teta con confirmacion.
Capacidad: `ruteate.preparar_asignacion_zona_chofer`.
Acciones: previsualizar `ruteate.rutear` y confirmar `ruteate.asignacionProcesar`.
```

## AppApi

Metodo propuesto:

```js
async iaLogisticaPreguntar({ pregunta, contextoUi = {} }) {
  const d = await this._post('ia.logistica.preguntar', { pregunta, contextoUi });
  return {
    respuesta: d.respuesta || '',
    intencion: d.intencion || '',
    fuentes: d.fuentes || [],
    acciones: d.acciones || [],
    debug: d.debug || {},
  };
}
```

## Dispatch PHP

En `empresa_demo/controlador.php`:

```php
if ($modulo === 'ia') {
    $c = new ControladorIa(db(), (int)$_SESSION['uid']);
    switch ($metodo) {
        case 'logistica.preguntar':
            salir(['ok' => true] + $c->preguntarLogistica($in));
    }
}
```

## Controlador

Archivo runtime:

```text
back/sistema/controlador_ia.php
```

Metodos esperados:

```php
class ControladorIa
{
    public function preguntarLogistica(array $in): array;
    private function detectarIntencion(string $pregunta, array $ui): string;
    private function armarContexto(string $intencion, array $ui): array;
    private function llamarModelo(array $messages): string;
}
```

## Ollama

Variables:

```text
LD_IA_OLLAMA_URL=http://127.0.0.1:11434
LD_IA_LOGISTICA_MODELO=qwen2.5:7b-instruct
LD_IA_TIMEOUT=150
LD_IA_MAX_ENVIOS=60
```

Request a Ollama:

```json
{
  "model": "qwen2.5:7b-instruct",
  "stream": false,
  "messages": [
    { "role": "system", "content": "..." },
    { "role": "user", "content": "..." }
  ],
  "options": {
    "temperature": 0.2,
    "num_ctx": 8192
  }
}
```

## Prompt base

```text
Sos LightDatai, un agente logistico para operadores de un TMS.
Responde en espanol claro, breve y accionable.
Usa solo CONTEXTO_DOCUMENTAL y CONTEXTO_OPERATIVO.
Si falta un dato, decilo.
No inventes tracking, estados, choferes, zonas ni cantidades.
No ejecutes cambios. Solo propone acciones para confirmar en UI.
```

## Errores

Modelo caido:

```json
{
  "ok": false,
  "error": "No pude consultar el modelo local",
  "codigo": "IA_MODELO_NO_DISPONIBLE"
}
```

Pregunta vacia:

```json
{
  "ok": false,
  "error": "Falta pregunta",
  "codigo": "IA_PREGUNTA_VACIA"
}
```

Sin datos:

```json
{
  "ok": true,
  "respuesta": "No encontre datos suficientes con los filtros actuales.",
  "intencion": "analisis_envios",
  "fuentes": [],
  "acciones": []
}
```
