cognix-sdk 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cognix_sdk-0.1.0/.gitignore +51 -0
- cognix_sdk-0.1.0/LICENSE +141 -0
- cognix_sdk-0.1.0/PKG-INFO +160 -0
- cognix_sdk-0.1.0/PUBLISHING.md +219 -0
- cognix_sdk-0.1.0/README.md +129 -0
- cognix_sdk-0.1.0/THIRD-PARTY-NOTICES.md +305 -0
- cognix_sdk-0.1.0/examples/byo_sklearn.py +116 -0
- cognix_sdk-0.1.0/hatch_build.py +52 -0
- cognix_sdk-0.1.0/pyproject.toml +89 -0
- cognix_sdk-0.1.0/src/cgx/__init__.py +35 -0
- cognix_sdk-0.1.0/src/cgx/bundle.py +340 -0
- cognix_sdk-0.1.0/src/cgx/onnx.py +94 -0
- cognix_sdk-0.1.0/src/cgx/parity.py +99 -0
- cognix_sdk-0.1.0/src/cgx/starlark.py +49 -0
- cognix_sdk-0.1.0/tests/test_bundle.py +156 -0
- cognix_sdk-0.1.0/tests/test_published_surface.py +148 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# CogniX — un repo, un módulo, una versión.
|
|
2
|
+
# Las cinco features viven aquí dentro con su historia (git filter-repo, 2026-07-29).
|
|
3
|
+
# La cabecera anterior decía que cada miembro era un repo aparte: ya no lo es.
|
|
4
|
+
|
|
5
|
+
# Local / temporales
|
|
6
|
+
.claude/settings.local.json
|
|
7
|
+
.DS_Store
|
|
8
|
+
*.swp
|
|
9
|
+
scratchpad/
|
|
10
|
+
|
|
11
|
+
# Python — el workspace uv pone UN entorno en la raiz (antes habia uno por feature, y
|
|
12
|
+
# cada .gitignore de miembro lo ignoraba por su cuenta). Son ~5,5 GB: si esto falta,
|
|
13
|
+
# el primer `git add -A` intenta versionar el entorno entero.
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
__pycache__/
|
|
17
|
+
*.py[cod]
|
|
18
|
+
*.egg-info/
|
|
19
|
+
# Ficheros de trabajo por sesión (encargos cross-repo): locales, no se versionan (§6).
|
|
20
|
+
SSO-NEXT-SESSION.md
|
|
21
|
+
|
|
22
|
+
# Portal (Fase 1b) — runtime artefacts y config con secretos.
|
|
23
|
+
# El binario, la base de datos de usuarios y la config con admin keys NO se versionan;
|
|
24
|
+
# se versiona portal.example.json como plantilla.
|
|
25
|
+
portal.json
|
|
26
|
+
cognix-portal.db
|
|
27
|
+
*.db
|
|
28
|
+
/cognix-portal
|
|
29
|
+
/cognix-portal.exe
|
|
30
|
+
|
|
31
|
+
# Portal SPA — dependencias y build.
|
|
32
|
+
web/node_modules/
|
|
33
|
+
web/dist/
|
|
34
|
+
|
|
35
|
+
# Diagramas de estrategia — el HTML (+ render.sh) son la fuente; el PNG/PDF se regeneran.
|
|
36
|
+
docs/estrategia/**/*.png
|
|
37
|
+
docs/estrategia/**/*.pdf
|
|
38
|
+
|
|
39
|
+
# Binaries built by the root Taskfile (task build).
|
|
40
|
+
/bin/
|
|
41
|
+
|
|
42
|
+
# The umbrella binary when built at the repo root by hand (task build uses bin/).
|
|
43
|
+
/cognix
|
|
44
|
+
|
|
45
|
+
# Compiled example binaries. A 22 MB ELF was tracked in decidix/examples/eino-router,
|
|
46
|
+
# imported with that repo history: it is a build artifact, it is machine-specific, and it
|
|
47
|
+
# dwarfs every source file in the tree.
|
|
48
|
+
decidix/examples/*/eino-router
|
|
49
|
+
|
|
50
|
+
# Cross-compiled cgx binaries staged for the PyPI wheels (scripts/build-wheels.py).
|
|
51
|
+
/build/
|
cognix_sdk-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
CogniX Software License Agreement
|
|
2
|
+
Copyright (c) 2026 Carlos Prados. All rights reserved.
|
|
3
|
+
|
|
4
|
+
This is not open source software. Read this before installing or using it.
|
|
5
|
+
|
|
6
|
+
1. DEFINITIONS
|
|
7
|
+
|
|
8
|
+
"Licensor" means Carlos Prados, the copyright holder.
|
|
9
|
+
|
|
10
|
+
"Software" means the `cognix-sdk` Python package, the `cgx` executable distributed
|
|
11
|
+
with it, and any documentation, examples or files supplied with either.
|
|
12
|
+
|
|
13
|
+
"Platform" means the CogniX inference, training and serving products operated or
|
|
14
|
+
licensed by the Licensor, including any host system into which they are embedded.
|
|
15
|
+
|
|
16
|
+
"Bundle" means a directory in the format the Software produces and validates: a
|
|
17
|
+
manifest, a model file, a feature extractor and its samples.
|
|
18
|
+
|
|
19
|
+
"You" means the individual or legal entity exercising the rights granted here.
|
|
20
|
+
|
|
21
|
+
2. GRANT OF LICENSE
|
|
22
|
+
|
|
23
|
+
Subject to Your compliance with this Agreement, the Licensor grants You a
|
|
24
|
+
non-exclusive, non-transferable, non-sublicensable, revocable, worldwide,
|
|
25
|
+
royalty-free license to install and use the Software, for Your internal purposes
|
|
26
|
+
only, in order to:
|
|
27
|
+
|
|
28
|
+
(a) create, inspect and validate Bundles intended for deployment on the Platform;
|
|
29
|
+
(b) evaluate the Software and the Platform, including before entering into any
|
|
30
|
+
commercial agreement with the Licensor; and
|
|
31
|
+
(c) develop and test Your own models and feature extractors against the Bundle
|
|
32
|
+
format.
|
|
33
|
+
|
|
34
|
+
No fee is charged for this license. It confers no right to the Platform itself,
|
|
35
|
+
which is licensed separately.
|
|
36
|
+
|
|
37
|
+
3. RESTRICTIONS
|
|
38
|
+
|
|
39
|
+
You may not, and may not permit any third party to:
|
|
40
|
+
|
|
41
|
+
(a) distribute, publish, sell, rent, lease, lend, host or otherwise make the
|
|
42
|
+
Software available to any third party, in whole or in part, whether alone or
|
|
43
|
+
bundled with other software;
|
|
44
|
+
(b) modify, adapt, translate or create derivative works of the Software;
|
|
45
|
+
(c) reverse engineer, decompile or disassemble the Software, or attempt to derive
|
|
46
|
+
its source code, except and only to the extent that this restriction is
|
|
47
|
+
expressly prohibited by applicable law that cannot be waived by agreement (see
|
|
48
|
+
Section 4);
|
|
49
|
+
(d) use the Software, or any knowledge of the Bundle format obtained from it, to
|
|
50
|
+
develop, or to assist any third party in developing, a product or service that
|
|
51
|
+
competes with the Software or the Platform;
|
|
52
|
+
(e) implement, or assist any third party in implementing, an inference, scoring or
|
|
53
|
+
serving system that consumes Bundles, other than the Platform;
|
|
54
|
+
(f) remove, obscure or alter any copyright, trademark or other proprietary notice
|
|
55
|
+
in or displayed by the Software; or
|
|
56
|
+
(g) circumvent any technical measure that limits the functionality of the Software.
|
|
57
|
+
|
|
58
|
+
For the avoidance of doubt: models You create remain Yours, and nothing in this
|
|
59
|
+
Agreement restricts what You do with a model of Your own in any system, provided
|
|
60
|
+
You do not do so by means of the Software or of a Bundle-consuming implementation
|
|
61
|
+
prohibited by Section 3(e).
|
|
62
|
+
|
|
63
|
+
4. MANDATORY LEGAL RIGHTS
|
|
64
|
+
|
|
65
|
+
Nothing in this Agreement excludes or limits any right You have under applicable
|
|
66
|
+
law that cannot be excluded or limited by agreement. In particular, where Directive
|
|
67
|
+
2009/24/EC of the European Parliament and of the Council, or the national law
|
|
68
|
+
implementing it, grants You a right to decompile the Software to achieve
|
|
69
|
+
interoperability with an independently created program, that right applies
|
|
70
|
+
notwithstanding Section 3(c), on the conditions that Directive lays down.
|
|
71
|
+
|
|
72
|
+
5. OWNERSHIP
|
|
73
|
+
|
|
74
|
+
The Software is licensed, not sold. The Licensor and its suppliers retain all
|
|
75
|
+
right, title and interest in and to the Software, including all intellectual
|
|
76
|
+
property rights. All rights not expressly granted in Section 2 are reserved.
|
|
77
|
+
|
|
78
|
+
6. THIRD-PARTY COMPONENTS
|
|
79
|
+
|
|
80
|
+
The `cgx` executable statically links open source components which are licensed to
|
|
81
|
+
You under their own terms, not under this Agreement. Those terms are reproduced in
|
|
82
|
+
the accompanying THIRD-PARTY-NOTICES.md file and prevail over this Agreement in
|
|
83
|
+
respect of those components.
|
|
84
|
+
|
|
85
|
+
7. FEEDBACK
|
|
86
|
+
|
|
87
|
+
If You send the Licensor suggestions, bug reports or other feedback about the
|
|
88
|
+
Software, the Licensor may use it without restriction or obligation to You. This
|
|
89
|
+
does not give the Licensor any right in Your models, Your data or Your Bundles.
|
|
90
|
+
|
|
91
|
+
8. NO SUPPORT
|
|
92
|
+
|
|
93
|
+
This Agreement does not oblige the Licensor to provide support, maintenance,
|
|
94
|
+
updates or new versions of the Software.
|
|
95
|
+
|
|
96
|
+
9. TERM AND TERMINATION
|
|
97
|
+
|
|
98
|
+
This Agreement takes effect when You first install or use the Software and
|
|
99
|
+
continues until terminated. It terminates automatically, without notice, if You
|
|
100
|
+
breach any of its terms. On termination You must stop using the Software and delete
|
|
101
|
+
all copies of it in Your possession. Sections 3, 5, 10, 11 and 12 survive
|
|
102
|
+
termination.
|
|
103
|
+
|
|
104
|
+
10. NO WARRANTY
|
|
105
|
+
|
|
106
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
107
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
108
|
+
FOR A PARTICULAR PURPOSE, ACCURACY AND NON-INFRINGEMENT. THE SOFTWARE VALIDATES
|
|
109
|
+
BUNDLES; IT DOES NOT WARRANT THAT ANY MODEL, PREDICTION OR DECISION PRODUCED WITH
|
|
110
|
+
ITS HELP IS CORRECT, SAFE OR FIT FOR ANY PURPOSE.
|
|
111
|
+
|
|
112
|
+
11. LIMITATION OF LIABILITY
|
|
113
|
+
|
|
114
|
+
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSOR SHALL NOT BE
|
|
115
|
+
LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES,
|
|
116
|
+
OR FOR ANY LOSS OF PROFITS, REVENUE, DATA OR BUSINESS, ARISING OUT OF OR RELATED
|
|
117
|
+
TO THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE
|
|
118
|
+
LICENSOR'S TOTAL AGGREGATE LIABILITY UNDER THIS AGREEMENT SHALL NOT EXCEED ONE
|
|
119
|
+
HUNDRED EUROS (EUR 100).
|
|
120
|
+
|
|
121
|
+
Nothing in this Section limits liability for death or personal injury caused by
|
|
122
|
+
negligence, for fraud, or for any other liability that cannot be limited under
|
|
123
|
+
applicable law.
|
|
124
|
+
|
|
125
|
+
12. GOVERNING LAW AND JURISDICTION
|
|
126
|
+
|
|
127
|
+
This Agreement is governed by the laws of Spain, without regard to its conflict of
|
|
128
|
+
law rules. The courts of the city of Madrid, Spain, shall have exclusive
|
|
129
|
+
jurisdiction over any dispute arising out of or in connection with it, except that
|
|
130
|
+
the Licensor may seek injunctive relief in any competent court. This Section does
|
|
131
|
+
not deprive a consumer of the protection of the mandatory rules of the law of the
|
|
132
|
+
country in which they are resident.
|
|
133
|
+
|
|
134
|
+
13. ENTIRE AGREEMENT
|
|
135
|
+
|
|
136
|
+
This Agreement is the entire agreement between You and the Licensor regarding the
|
|
137
|
+
Software and supersedes any prior understanding on that subject. If any provision
|
|
138
|
+
is held unenforceable, it shall be modified to the minimum extent necessary to
|
|
139
|
+
make it enforceable, and the remaining provisions shall remain in full force.
|
|
140
|
+
|
|
141
|
+
Contact: carlosprados@users.noreply.github.com
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cognix-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Empaqueta tu modelo como un bundle de CogniX y compruébalo antes de publicarlo
|
|
5
|
+
Project-URL: Homepage, https://github.com/carlosprados/cognix
|
|
6
|
+
Project-URL: Documentation, https://github.com/carlosprados/cognix/blob/main/sdk/README.md
|
|
7
|
+
Project-URL: Source, https://github.com/carlosprados/cognix
|
|
8
|
+
Author: Carlos Prados
|
|
9
|
+
License-Expression: LicenseRef-Proprietary
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
License-File: THIRD-PARTY-NOTICES.md
|
|
12
|
+
Keywords: inference,iot,mlops,model-packaging,onnx,starlark
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: Other/Proprietary License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: numpy>=1.26.0
|
|
26
|
+
Provides-Extra: sklearn
|
|
27
|
+
Requires-Dist: onnx>=1.16.0; extra == 'sklearn'
|
|
28
|
+
Requires-Dist: scikit-learn>=1.5.0; extra == 'sklearn'
|
|
29
|
+
Requires-Dist: skl2onnx>=1.17.0; extra == 'sklearn'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# cognix-sdk
|
|
33
|
+
|
|
34
|
+
> Se instala `cognix-sdk` y se importa `cgx`, como `pillow`→`PIL`. En PyPI, `cognix` es
|
|
35
|
+
> de otro proyecto y `cgx` está reservado por alguien más; `cgx` es el identificador que
|
|
36
|
+
> CogniX ya usa en sus API keys (`cgx_<feature>_…`), así que se queda en el módulo y en
|
|
37
|
+
> el binario.
|
|
38
|
+
|
|
39
|
+
Empaqueta tu modelo como un bundle de CogniX y **compruébalo en tu máquina** antes de
|
|
40
|
+
publicarlo.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uv add cognix-sdk
|
|
44
|
+
uv add "cognix-sdk[sklearn]" # si quieres exportar un modelo de scikit-learn a ONNX
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Qué problema resuelve
|
|
48
|
+
|
|
49
|
+
El modelo lo entrenas tú, con lo que quieras. Lo que hay que hacer bien es el **artefacto**,
|
|
50
|
+
y ahí hay una trampa que no da ningún error:
|
|
51
|
+
|
|
52
|
+
> el extractor de features se escribe **dos veces** — tu `featurize()` en Python para
|
|
53
|
+
> entrenar, y `features.star` (Starlark) que evalúa Go en producción. Cuando se separan, el
|
|
54
|
+
> modelo sigue contestando: sólo que **sobre otra entrada**.
|
|
55
|
+
|
|
56
|
+
No hay excepción, ni traza, ni métrica que baje. Este paquete existe para que eso falle en
|
|
57
|
+
tu portátil.
|
|
58
|
+
|
|
59
|
+
## Uso
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import cgx
|
|
63
|
+
from cgx import starlark
|
|
64
|
+
import pandas as pd
|
|
65
|
+
from sklearn.linear_model import LogisticRegression
|
|
66
|
+
from sklearn.preprocessing import StandardScaler
|
|
67
|
+
|
|
68
|
+
# 1. tus datos (exportados con: wolfctl exports download-export --id <uuid> > datos.parquet)
|
|
69
|
+
df = pd.DataFrame([json.loads(r) for r in pd.read_parquet("datos.parquet")["RESULT"]])
|
|
70
|
+
schema = ["current_a", "vibration", "temperature"]
|
|
71
|
+
|
|
72
|
+
# 2. tu modelo, como lo entrenas siempre
|
|
73
|
+
scaler = StandardScaler().fit(df[schema].to_numpy())
|
|
74
|
+
clf = LogisticRegression(max_iter=1000).fit(scaler.transform(df[schema].to_numpy()), df["fault_label"])
|
|
75
|
+
|
|
76
|
+
# 3. el artefacto
|
|
77
|
+
onnx = cgx.to_onnx(clf, n_features=len(schema))
|
|
78
|
+
|
|
79
|
+
b = cgx.Bundle(
|
|
80
|
+
domain="motor_faults",
|
|
81
|
+
feature="classix",
|
|
82
|
+
schema=schema,
|
|
83
|
+
featurizer=starlark.identity_featurizer(schema),
|
|
84
|
+
starlark=starlark.identity("motor_faults", schema),
|
|
85
|
+
onnx_path=onnx,
|
|
86
|
+
output_names=cgx.onnx.scores_output(onnx), # el tensor de scores, no `label`
|
|
87
|
+
output_kind="probabilities", # skl2onnx ya las emite normalizadas
|
|
88
|
+
payloads=df[schema].head(8).to_dict("records"), # payloads representativos
|
|
89
|
+
labels=sorted(df["fault_label"].unique()),
|
|
90
|
+
mean=scaler.mean_.tolist(),
|
|
91
|
+
std=scaler.scale_.tolist(),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
b.check() # escribe el bundle y lo verifica; levanta BundleError si algo no cuadra
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`check()` hace dos cosas, en este orden:
|
|
98
|
+
|
|
99
|
+
1. **En Python**: que tu `featurize()` devuelva tantos números como features declaras, para
|
|
100
|
+
cada payload. Un fallo aquí es tuyo y se ve al instante.
|
|
101
|
+
2. **Con el binario `cgx`**, que viene dentro de este paquete: valida el manifest contra el
|
|
102
|
+
contrato de la feature y corre `verify-parity` — tu Python contra el Starlark real.
|
|
103
|
+
|
|
104
|
+
El paso 2 lo hace **el binario**, no una reimplementación de Starlark en Python. Un segundo
|
|
105
|
+
intérprete sería una segunda implementación que puede diferir del motor de producción, o
|
|
106
|
+
sea el mismo problema que esto viene a detectar, movido un nivel. Si el binario no está en
|
|
107
|
+
el PATH, `check()` lo dice y **no finge un OK**.
|
|
108
|
+
|
|
109
|
+
Falta una tercera comprobación, `verify-bundle`, que reproduce el resultado completo
|
|
110
|
+
—clase, probabilidades, puerta OOD— contra una fixture grabada al entrenar. Esa ejecuta el
|
|
111
|
+
modelo, o sea el motor, y vive en el binario `cognix`: si lo tienes en el PATH, `check()`
|
|
112
|
+
lo usa; si no, dice exactamente qué queda sin cubrir. Un bundle traído de fuera casi nunca
|
|
113
|
+
trae esa fixture de todos modos, porque la escribe el trainer de la feature.
|
|
114
|
+
|
|
115
|
+
Y ya se puede servir:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
cognix classix serve --bundles dist
|
|
119
|
+
cognix classix infer --bundle dist/motor_faults --json '{"current_a":18.5,"vibration":1.2,"temperature":97.0}'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Si tu featurización no es la identidad
|
|
123
|
+
|
|
124
|
+
`starlark.identity()` sólo cubre el caso "cada feature es un campo numérico del payload".
|
|
125
|
+
Para cualquier otra cosa —una razón entre dos campos, un one-hot, una saturación— escribes
|
|
126
|
+
tu `features.star` a mano y se lo pasas en `starlark=`.
|
|
127
|
+
|
|
128
|
+
⛔ **No hay traducción automática de Python a Starlark, y es deliberado.** Traducir un
|
|
129
|
+
`featurize()` cualquiera leyendo su AST es la funcionalidad que enamora y la que puede
|
|
130
|
+
traducir mal **en silencio** — exactamente la clase de fallo que este SDK existe para cazar.
|
|
131
|
+
`check()` funciona con el extractor que sea, y esa es la garantía que importa.
|
|
132
|
+
|
|
133
|
+
## Lo que este paquete NO es
|
|
134
|
+
|
|
135
|
+
**No es un cliente de la API REST de WolfOps**, y no lo será a mano: un wrapper de ~100
|
|
136
|
+
endpoints envejece con cada endpoint nuevo y se convierte en otro sitio donde algo se queda
|
|
137
|
+
fuera en silencio. Para los datos ya tienes dos líneas que no hay que mantener:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
wolfctl exports download-export --id <uuid> > datos.parquet
|
|
141
|
+
```
|
|
142
|
+
```python
|
|
143
|
+
df = pd.read_parquet("datos.parquet")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Su contrato es el **formato del bundle** (v3), que está versionado y cambia a propósito, no
|
|
147
|
+
una superficie que cambia cada semana.
|
|
148
|
+
|
|
149
|
+
## Referencia
|
|
150
|
+
|
|
151
|
+
| | |
|
|
152
|
+
|---|---|
|
|
153
|
+
| `Bundle(...)` | el artefacto: `.manifest()`, `.payload_schema()`, `.save(dir)`, `.check(dir)` |
|
|
154
|
+
| `to_onnx(model, n_features)` | export de scikit-learn con el opset y la salida que espera el motor |
|
|
155
|
+
| `starlark.identity(domain, schema)` | el `features.star` del caso identidad |
|
|
156
|
+
| `starlark.identity_featurizer(schema)` | su gemelo Python, para pasarlo a `featurizer=` |
|
|
157
|
+
|
|
158
|
+
El bloque de tarea de cada feature (calibración, intervalos conformes, puerta OOD,
|
|
159
|
+
fallbacks) se declara en `extra_manifest=`: es lo específico de cada una y pertenece a la
|
|
160
|
+
feature, no a este paquete.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Publicar el SDK en PyPI
|
|
2
|
+
|
|
3
|
+
Ya tienes cuenta. Queda **una cosa que solo puedes hacer tú** (autorizar este repo, 5
|
|
4
|
+
minutos) y después publicar es un comando:
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
task sdk:release:test -- 0.1.0.dev1 # ensayo en TestPyPI
|
|
8
|
+
task sdk:release -- 0.1.0 # de verdad
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`scripts/release-sdk.py` comprueba antes todo lo que se puede comprobar en local —árbol
|
|
12
|
+
limpio, rama al día, versión libre en el índice, `go test`, `pytest`, los wheels
|
|
13
|
+
construidos y el wheel **instalado en un venv vacío** verificando un bundle de verdad— y
|
|
14
|
+
solo entonces pregunta. El push del tag es lo que dispara la publicación.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## ⛔ Antes de nada: publicar es hacer público el nombre
|
|
19
|
+
|
|
20
|
+
Un paquete en PyPI es **visible para cualquiera**, con su README, y aparece en buscadores.
|
|
21
|
+
Eso adelanta el disclosure que aplazaste a Q4 2026 por el conflicto con Amplía — el mismo
|
|
22
|
+
motivo por el que el post de julio salió sin nombres.
|
|
23
|
+
|
|
24
|
+
Por eso nada se publica sin el tag `sdk-v*`: hasta que lo lances, aquí no pasa nada. Para
|
|
25
|
+
probar el flujo completo antes de esa fecha está **TestPyPI**, que es un índice separado,
|
|
26
|
+
no reserva el nombre en el PyPI real y nadie lo mira.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 1. Autorizar este repositorio (Trusted Publishing) — lo único pendiente
|
|
31
|
+
|
|
32
|
+
En PyPI, en los ajustes de tu **cuenta** — <https://pypi.org/manage/account/publishing/> —,
|
|
33
|
+
formulario **GitHub**, *Add*.
|
|
34
|
+
|
|
35
|
+
⚠️ Es a nivel de cuenta, no de proyecto: la página de *Your projects → Publishing* solo
|
|
36
|
+
existe para proyectos ya creados, y `cognix-sdk` todavía no existe. Eso es lo que significa
|
|
37
|
+
*pending*: la autorización queda esperando, y el proyecto se crea con la primera
|
|
38
|
+
publicación que la use.
|
|
39
|
+
|
|
40
|
+
Rellena **exactamente** esto:
|
|
41
|
+
|
|
42
|
+
| campo | valor |
|
|
43
|
+
|---|---|
|
|
44
|
+
| PyPI Project Name | `cognix-sdk` |
|
|
45
|
+
| Owner | `carlosprados` |
|
|
46
|
+
| Repository name | `cognix` |
|
|
47
|
+
| Workflow name | `release-sdk.yml` |
|
|
48
|
+
| Environment name | `pypi` |
|
|
49
|
+
|
|
50
|
+
Repite en **TestPyPI** con Environment name = `testpypi`, en
|
|
51
|
+
<https://test.pypi.org/manage/account/publishing/>.
|
|
52
|
+
|
|
53
|
+
⚠️ **TestPyPI es un sitio distinto con cuentas distintas.** Tu usuario de PyPI no vale
|
|
54
|
+
allí: hay que registrarse aparte (y activar 2FA aparte). No es un entorno de pruebas del
|
|
55
|
+
mismo servicio, es otro índice.
|
|
56
|
+
|
|
57
|
+
⚠️ Los cinco campos tienen que coincidir carácter a carácter. Si algo no cuadra, el error
|
|
58
|
+
de PyPI es genérico (*"not a trusted publisher"*) y no dice cuál de los cinco falla —
|
|
59
|
+
compáralos uno a uno contra `.github/workflows/release-sdk.yml`.
|
|
60
|
+
|
|
61
|
+
⚠️ **No crees ningún token de API.** No hace falta ninguno y es el peor activo que puedes
|
|
62
|
+
tener suelto: un token de PyPI filtrado permite subir cualquier versión de tu paquete a
|
|
63
|
+
quien lo tenga. Trusted Publishing es OIDC — PyPI verifica que la petición viene de este
|
|
64
|
+
repositorio y este workflow, y emite un permiso de un solo uso.
|
|
65
|
+
|
|
66
|
+
### Y los dos entornos de GitHub
|
|
67
|
+
|
|
68
|
+
En el repo: **Settings → Environments → New environment**, dos, llamados `pypi` y
|
|
69
|
+
`testpypi`. Sin secretos dentro: son solo el nombre que PyPI verifica.
|
|
70
|
+
|
|
71
|
+
En `pypi`, **Deployment branches and tags → Selected branches and tags**, y una regla de
|
|
72
|
+
tipo **Tag** con el patrón `sdk-v*`. Al PyPI real solo se llega por un tag; un
|
|
73
|
+
`workflow_dispatch` apuntando ahí desde una rama lo bloquea GitHub, no la confianza.
|
|
74
|
+
|
|
75
|
+
`testpypi` se queda en *No restriction*: el ensayo se lanza con `workflow_dispatch` desde
|
|
76
|
+
una rama, y restringirlo a tags lo dejaría sin poder ejecutarse nunca.
|
|
77
|
+
|
|
78
|
+
⛔ **No hay revisor humano, y no es un olvido.** *Required reviewers* y las demás
|
|
79
|
+
*deployment protection rules* exigen plan Pro/Team/Enterprise cuando el repositorio es
|
|
80
|
+
privado — comprobado el 2026-08-01 en este repo: `protection_rules: []`, y la sección ni
|
|
81
|
+
aparece en la página. Publicar en PyPI es **irreversible** (una versión no se reemplaza,
|
|
82
|
+
solo se borra, y el número queda quemado), así que lo que hace de red son tres cosas
|
|
83
|
+
automáticas: sin tag `sdk-v*` no se dispara nada, `release-sdk.py` obliga a teclear la
|
|
84
|
+
versión completa tras sus seis comprobaciones, y el workflow verifica que el tag y
|
|
85
|
+
`sdk/pyproject.toml` coinciden antes de construir.
|
|
86
|
+
|
|
87
|
+
Si algún día el repo se hace público —o sea, en el disclosure— las protection rules pasan
|
|
88
|
+
a estar disponibles gratis: entonces sí, añade el revisor.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 2. El ensayo
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
task sdk:release:test -- 0.1.0.dev1
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Lanza el workflow contra TestPyPI. Cuando termine:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
uv pip install --index-url https://test.pypi.org/simple/ \
|
|
102
|
+
--extra-index-url https://pypi.org/simple/ cognix-sdk==0.1.0.dev1
|
|
103
|
+
python -c "import cgx; print(cgx.FEATURES)" && cgx version
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
El `--extra-index-url` hace falta porque TestPyPI no tiene numpy, y sin él la instalación
|
|
107
|
+
falla por algo que no tiene nada que ver con tu paquete.
|
|
108
|
+
|
|
109
|
+
⚠️ Una versión de TestPyPI tampoco se puede repetir: usa `0.1.0.dev1`, `dev2`… y reserva
|
|
110
|
+
`0.1.0` para el real.
|
|
111
|
+
|
|
112
|
+
## 3. Publicar
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
task sdk:release -- 0.1.0
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Comprueba después:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
uv pip install cognix-sdk && python -c "import cgx; print(cgx.FEATURES)" && cgx version
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Qué se sube, exactamente
|
|
127
|
+
|
|
128
|
+
**Una sola distribución, `cognix-sdk`**, con nueve artefactos:
|
|
129
|
+
|
|
130
|
+
| artefacto | para quién |
|
|
131
|
+
|---|---|
|
|
132
|
+
| 4 wheels Linux (`manylinux` y `musllinux`, x86_64 y aarch64) | glibc y Alpine |
|
|
133
|
+
| 2 wheels macOS (arm64, x86_64) | Apple Silicon e Intel |
|
|
134
|
+
| 1 wheel Windows (`win_amd64`) | |
|
|
135
|
+
| 1 wheel `py3-none-any` **sin binario** | cualquier otra plataforma |
|
|
136
|
+
| 1 sdist | quien lo exija |
|
|
137
|
+
|
|
138
|
+
pip elige siempre el más específico que le encaje. Los siete primeros llevan dentro el
|
|
139
|
+
binario **`cgx`**, que aterriza en el `bin/` del entorno: `pip install cognix-sdk` y `check()`
|
|
140
|
+
funciona sin más pasos. En una plataforma sin wheel propio se instala el puro y el SDK
|
|
141
|
+
dice, en voz alta, qué comprobación no puede hacer — mejor que un `pip install` que falla.
|
|
142
|
+
|
|
143
|
+
Se compila todo desde un runner: `cgx` es Go sin cgo, así que cross-compilar es una
|
|
144
|
+
variable de entorno. Es la otra razón por la que el binario público no enlaza ONNX
|
|
145
|
+
Runtime — con cgo harían falta siete runners y una copia del runtime nativo por
|
|
146
|
+
plataforma.
|
|
147
|
+
|
|
148
|
+
### ⛔ El binario que se publica es `cgx`, nunca `cognix`
|
|
149
|
+
|
|
150
|
+
`cognix` lleva los cinco motores, ONNX Runtime, el control plane y el servidor: **es el
|
|
151
|
+
producto, y es privado** (regla de Charlie, 2026-08-01). `cgx` lleva el *contrato* del
|
|
152
|
+
bundle —los tipos del manifest y su validación, más el featurizador Starlark— y con eso
|
|
153
|
+
cubre `verify-parity`, `bundle inspect` y `schema`.
|
|
154
|
+
|
|
155
|
+
Lo único que queda fuera es `verify-bundle`, que reproduce predicciones grabadas pasando
|
|
156
|
+
por el modelo: eso **es** el motor. `cgx` declara el comando para rechazarlo con un mensaje
|
|
157
|
+
que explica dónde está, y el SDK usa `cognix` para esa comprobación si lo encuentra en el
|
|
158
|
+
PATH. No es una mutilación: un bundle traído de fuera casi nunca trae esa fixture, porque
|
|
159
|
+
la escribe el trainer de la feature.
|
|
160
|
+
|
|
161
|
+
El guardarraíl es `cmd/cgx/deps_test.go`, una **lista blanca** de paquetes del repo que el
|
|
162
|
+
binario público puede enlazar. Falla el build nombrando al intruso, y ya cazó uno el primer
|
|
163
|
+
día: `clikit` arrastraba el almacén de API keys (y sqlite) dentro del binario público.
|
|
164
|
+
|
|
165
|
+
## Versiones
|
|
166
|
+
|
|
167
|
+
`sdk/pyproject.toml` lleva la versión y **el tag tiene que coincidir**: `sdk-v0.1.0` ↔
|
|
168
|
+
`version = "0.1.0"`. El script lo comprueba en local y el workflow lo vuelve a comprobar.
|
|
169
|
+
|
|
170
|
+
El contrato del SDK es el **formato del bundle** (v3 hoy), no la API REST:
|
|
171
|
+
|
|
172
|
+
- **parche** (`0.1.1`) — arreglos, mensajes, validaciones nuevas;
|
|
173
|
+
- **menor** (`0.2.0`) — funcionalidad nueva compatible;
|
|
174
|
+
- **mayor** (`1.0.0`) — cuando el formato del bundle cambie de versión, porque un bundle
|
|
175
|
+
escrito por el SDK dejaría de cargar en el motor anterior. Es el único cambio que rompe
|
|
176
|
+
de verdad a un usuario.
|
|
177
|
+
|
|
178
|
+
## ⛔ El nombre: distribución `cognix-sdk`, módulo `cgx`
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
pip install cognix-sdk # la distribución
|
|
182
|
+
import cgx # el módulo
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
No coinciden a propósito, como `pillow`→`PIL`. Los dos candidatos naturales estaban
|
|
186
|
+
cogidos, y ninguno es reclamable:
|
|
187
|
+
|
|
188
|
+
> **`cognix` 0.2.5** — *"AI-powered CLI development assistant"*, de Shichiroh.Shigeh.
|
|
189
|
+
> Proyecto **activo**: 0.1.0 en agosto de 2025, 0.2.5 en febrero de 2026.
|
|
190
|
+
> <https://github.com/cognix-dev/cognix>
|
|
191
|
+
>
|
|
192
|
+
> **`cgx`** — registrado en PyPI, **cero versiones publicadas**. Un nombre reservado que
|
|
193
|
+
> nunca se usó, pero ocupado igual.
|
|
194
|
+
|
|
195
|
+
El módulo se queda en `cgx` porque es el identificador que el producto ya usa (las API
|
|
196
|
+
keys son `cgx_<feature>_<hex>`, el prefijo de issues es `[CGX]`) y porque llamarlo
|
|
197
|
+
`cognix` pondría un directorio `cognix/` en el `site-packages` de quien tenga el otro
|
|
198
|
+
paquete: dos distribuciones escribiendo la misma ruta se sobreescriben y el import
|
|
199
|
+
devuelve el que ganó la carrera.
|
|
200
|
+
|
|
201
|
+
### ⛔ Cómo se comprueba si un nombre está libre
|
|
202
|
+
|
|
203
|
+
Con **`https://pypi.org/simple/<nombre>/`** y solo con eso: 200 existe, 404 libre.
|
|
204
|
+
|
|
205
|
+
| endpoint | por qué NO vale |
|
|
206
|
+
|---|---|
|
|
207
|
+
| `pypi.org/pypi/<n>/json` | devuelve **404 también para un nombre registrado sin releases** — el caso ambiguo, y justo el de `cgx` |
|
|
208
|
+
| `pypi.org/project/<n>/` | tiene un desafío JavaScript delante: **200 siempre**, exista o no |
|
|
209
|
+
|
|
210
|
+
Se descubrió por las malas: `cgx` se dio por libre con el endpoint JSON y el formulario
|
|
211
|
+
del pending publisher contestó *"This project already exists"*.
|
|
212
|
+
|
|
213
|
+
⛔ **Y un pending publisher NO reserva el nombre**: medido el 2026-08-01, el índice seguía
|
|
214
|
+
dando 404 después de crearlo. El nombre no es tuyo hasta la primera publicación — con el
|
|
215
|
+
disclosure aplazado, es tiempo expuesto. Si eso llega a preocupar, la salida es publicar
|
|
216
|
+
un `0.0.1` vacío que solo ocupe el nombre, y eso YA es el disclosure.
|
|
217
|
+
|
|
218
|
+
⚠️ Y **TestPyPI es otro índice con otras cuentas**: que un nombre esté libre allí no dice
|
|
219
|
+
nada del PyPI real.
|