Metadata-Version: 2.4
Name: cognix-sdk
Version: 0.1.3
Summary: Empaqueta tu modelo como un bundle de CogniX y compruébalo antes de publicarlo
Author: Carlos Prados
License-Expression: LicenseRef-Proprietary
License-File: LICENSE
License-File: THIRD-PARTY-NOTICES.md
Keywords: inference,iot,mlops,model-packaging,onnx,starlark
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: numpy>=1.26.0
Provides-Extra: sklearn
Requires-Dist: onnx>=1.16.0; extra == 'sklearn'
Requires-Dist: scikit-learn>=1.5.0; extra == 'sklearn'
Requires-Dist: skl2onnx>=1.17.0; extra == 'sklearn'
Description-Content-Type: text/markdown

# cognix-sdk

> Se instala `cognix-sdk` y se importa `cgx`, como `pillow`→`PIL`. En PyPI, `cognix` es
> de otro proyecto y `cgx` está reservado por alguien más; `cgx` es el identificador que
> CogniX ya usa en sus API keys (`cgx_<feature>_…`), así que se queda en el módulo y en
> el binario.

Empaqueta tu modelo como un bundle de CogniX y **compruébalo en tu máquina** antes de
publicarlo.

```bash
uv add cognix-sdk
uv add "cognix-sdk[sklearn]"   # si quieres exportar un modelo de scikit-learn a ONNX
```

## Qué problema resuelve

El modelo lo entrenas tú, con lo que quieras. Lo que hay que hacer bien es el **artefacto**,
y ahí hay una trampa que no da ningún error:

> el extractor de features se escribe **dos veces** — tu `featurize()` en Python para
> entrenar, y `features.star` (Starlark) que evalúa Go en producción. Cuando se separan, el
> modelo sigue contestando: sólo que **sobre otra entrada**.

No hay excepción, ni traza, ni métrica que baje. Este paquete existe para que eso falle en
tu portátil.

## Uso

```python
import cgx
from cgx import starlark
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler

# 1. tus datos (exportados con: wolfctl exports download-export --id <uuid> > datos.parquet)
df = pd.DataFrame([json.loads(r) for r in pd.read_parquet("datos.parquet")["RESULT"]])
schema = ["current_a", "vibration", "temperature"]

# 2. tu modelo, como lo entrenas siempre
scaler = StandardScaler().fit(df[schema].to_numpy())
clf = LogisticRegression(max_iter=1000).fit(scaler.transform(df[schema].to_numpy()), df["fault_label"])

# 3. el artefacto
onnx = cgx.to_onnx(clf, n_features=len(schema))

b = cgx.Bundle(
    domain="motor_faults",
    feature="classix",
    schema=schema,
    featurizer=starlark.identity_featurizer(schema),
    starlark=starlark.identity("motor_faults", schema),
    onnx_path=onnx,
    output_names=cgx.onnx.scores_output(onnx),   # el tensor de scores, no `label`
    output_kind="probabilities",                    # skl2onnx ya las emite normalizadas
    payloads=df[schema].head(8).to_dict("records"),   # payloads representativos
    labels=sorted(df["fault_label"].unique()),
    mean=scaler.mean_.tolist(),
    std=scaler.scale_.tolist(),
)

b.check()        # escribe el bundle y lo verifica; levanta BundleError si algo no cuadra
```

`check()` hace dos cosas, en este orden:

1. **En Python**: que tu `featurize()` devuelva tantos números como features declaras, para
   cada payload. Un fallo aquí es tuyo y se ve al instante.
2. **Con el binario `cgx`**, que viene dentro de este paquete: valida el manifest contra el
   contrato de la feature y corre `verify-parity` — tu Python contra el Starlark real.

El paso 2 lo hace **el binario**, no una reimplementación de Starlark en Python. Un segundo
intérprete sería una segunda implementación que puede diferir del motor de producción, o
sea el mismo problema que esto viene a detectar, movido un nivel. Si el binario no está en
el PATH, `check()` lo dice y **no finge un OK**.

Falta una tercera comprobación, `verify-bundle`, que reproduce el resultado completo
—clase, probabilidades, puerta OOD— contra una fixture grabada al entrenar. Esa ejecuta el
modelo, o sea el motor, y vive en el binario `cognix`: si lo tienes en el PATH, `check()`
lo usa; si no, dice exactamente qué queda sin cubrir. Un bundle traído de fuera casi nunca
trae esa fixture de todos modos, porque la escribe el trainer de la feature.

Y ya se puede servir:

```bash
cognix classix serve --bundles dist
cognix classix infer --bundle dist/motor_faults --json '{"current_a":18.5,"vibration":1.2,"temperature":97.0}'
```

## Si tu featurización no es la identidad

`starlark.identity()` sólo cubre el caso "cada feature es un campo numérico del payload".
Para cualquier otra cosa —una razón entre dos campos, un one-hot, una saturación— escribes
tu `features.star` a mano y se lo pasas en `starlark=`.

⛔ **No hay traducción automática de Python a Starlark, y es deliberado.** Traducir un
`featurize()` cualquiera leyendo su AST es la funcionalidad que enamora y la que puede
traducir mal **en silencio** — exactamente la clase de fallo que este SDK existe para cazar.
`check()` funciona con el extractor que sea, y esa es la garantía que importa.

## Lo que este paquete NO es

**No es un cliente de la API REST de WolfOps**, y no lo será a mano: un wrapper de ~100
endpoints envejece con cada endpoint nuevo y se convierte en otro sitio donde algo se queda
fuera en silencio. Para los datos ya tienes dos líneas que no hay que mantener:

```bash
wolfctl exports download-export --id <uuid> > datos.parquet
```
```python
df = pd.read_parquet("datos.parquet")
```

Su contrato es el **formato del bundle** (v3), que está versionado y cambia a propósito, no
una superficie que cambia cada semana.

## Referencia

| | |
|---|---|
| `Bundle(...)` | el artefacto: `.manifest()`, `.payload_schema()`, `.save(dir)`, `.check(dir)` |
| `to_onnx(model, n_features)` | export de scikit-learn con el opset y la salida que espera el motor |
| `starlark.identity(domain, schema)` | el `features.star` del caso identidad |
| `starlark.identity_featurizer(schema)` | su gemelo Python, para pasarlo a `featurizer=` |

El bloque de tarea de cada feature (calibración, intervalos conformes, puerta OOD,
fallbacks) se declara en `extra_manifest=`: es lo específico de cada una y pertenece a la
feature, no a este paquete.
