--- slug: aishipbox-modelartsstudio-operator displayName: aishipbox — ModelArts Studio 算子与算法服务 version: 1.1.1 summary: 用 aishipbox CLI 开发 ModelArts Studio 自定义数据加工算子(op)与算法服务(algo):安装、选型、创建时不可回退的参数,以及跨项目的实测坑。 license: MIT author: xuopoj homepage: https://github.com/xuopoj/ascend-delivery name: aishipbox-modelartsstudio-operator description: Use when building, scaffolding, running, or packaging a ModelArts Studio custom data-processing operator (自定义数据加工算子) or algorithm service (算法服务) with the aishipbox CLI — installing it, choosing between `op` and `algo`, `new/run/pack/debug/download/stubs/install-deps`, manifest.yml validation errors, obs_input/obs_output mock runs, or dependency bundling. Covers the decisions made before a project exists; the per-project AGENTS.md covers the code you write inside one. --- # aishipbox CLI for two ModelArts project types. Covers install → choose a surface → create with the right flags → hand off. **`new` generates the API reference for you.** Every project gets an `AGENTS.md` documenting its own contract — the process/handler signature, the objects you're handed, logging, dependencies, and the full manifest field spec. **Read the project's AGENTS.md before implementing.** It ships with the installed tool version, so it stays correct as the tool changes. This skill deliberately does not copy it. What follows is what AGENTS.md structurally can't cover: the decisions made *before* a project exists, and the cross-project gotchas. ## Install Public PyPI package, Python >=3.10. Install as a tool, not into a project venv: ```bash uv tool install aishipbox # upgrade: uv tool upgrade aishipbox ``` Verify with `uv tool list`, **not** `aishipbox --version` — the version string is hardcoded in `__init__.py` and lags the real release (a 0.1.4 install reports `0.1.0`). `uv` is a hard runtime requirement, not a fallback — the CLI errors rather than dropping to `python -m venv`. ### `aishipbox: command not found` right after installing The install succeeded; the shell just can't see it yet. `uv tool install` puts binaries in a directory that has to be on PATH. - **macOS / Linux** — it's `~/.local/bin`. `uv tool update-shell` appends it to your shell profile. **Then open a new shell** (or `source ~/.zshrc`): the running one keeps its old PATH. - **Windows** — same cause, worse ergonomics. `uv tool update-shell` edits the *user* PATH in the registry, and **already-open processes never see it** — Windows reads PATH at process start. The current terminal is stale, and so is everything spawned from it, which is why a VS Code integrated terminal can still fail long after a fresh PowerShell works. Close and reopen the terminal; for VS Code, restart the editor itself, not just the terminal pane. Verify with `where aishipbox` (PowerShell: `Get-Command aishipbox`). If a new shell still can't find it, `uv tool dir` prints where the binaries went — add that `bin` (Windows: `Scripts`) directory to PATH by hand. This applies to `uv` itself too: install uv, then fail to find `uv`, same cause. ### Behind a corporate proxy Two different failures with two different fixes — the symptoms look alike, so identify which one you have before reaching for a flag. **Nothing reaches the network at all** (connection refused/timeout, `pip` and `uv` both hang): you need proxy variables. There is no CLI flag for this and none is needed — the CLI never overrides the environment, so anything exported in your shell reaches `uv`, `pip`, the interpreter download, and your operator code at runtime. ```bash export HTTPS_PROXY=http://proxy.corp.example:8080 # macOS / Linux export HTTP_PROXY=$HTTPS_PROXY export NO_PROXY=localhost,127.0.0.1,.internal.example # keep internal hosts direct ``` ```powershell $env:HTTPS_PROXY = "http://proxy.corp.example:8080" # Windows, current session $env:HTTP_PROXY = $env:HTTPS_PROXY ``` Set them **before** `op new` / `algo new` / `op download` — those are the commands that fetch. A one-off is fine too: `HTTPS_PROXY=... aishipbox op new …`. If the proxy needs credentials, percent-encode any `@` or `:` in the password, and prefer a config file or credential helper over baking them into a shell profile. **The connection is made but the certificate is rejected** — see below. Proxy variables won't help there, and TLS flags won't help here. ### Interpreter download fails on TLS `op new` / `algo new` provision a venv, which may download a managed CPython. A MITM proxy or self-signed cert breaks that — the tell is `invalid peer certificate` or `UnknownIssuer` in uv's output, not a timeout. Escalate in order: 1. **`--native-tls`** — use the system cert store. Usually enough: the proxy's root CA is already OS-trusted, just absent from uv's bundled cert set. Gives up nothing. 2. **`--insecure`** — only if that fails. Passes `--allow-insecure-host` for the three interpreter-download hosts (`github.com`, `objects.githubusercontent.com`, `astral.sh`). PyPI is **not** among them, so package installs stay verified. You are trusting the network for the interpreter download. Trusted networks only. The friendly "TLS 下载失败" hint only fires when **neither** flag was passed. Retry with `--native-tls`, fail again, and you get the generic `uv venv 失败` — still TLS, just no longer labeled as such. ## Which surface Two sibling project types. `.aishipbox.toml` records which one, and routes every in-project command; never infer type from filenames. | | `op` — 自定义算子 | `algo` — 算法服务 | |---|---|---| | What | data-processing operator in a pipeline | long-running HTTP inference service | | Python | 3.10 | 3.9 | | You write | `program_package/process.py` | `main.py` | | Entry | `Process.__call__` | a `rest.process_base` subclass | | Local run | mock over `obs_input/` → `obs_output/` | HTTP server on `:8080` | | Pack | `program_package/.tar` (uncompressed, nested under `/`) | `.tar.gz` (flat) | | Config | `manifest.yml` | none | ```bash aishipbox op new|run|pack|debug|download aishipbox algo new|run|pack|debug|stubs|install-deps ``` ## Creating a project **Always pass `--yes` when running non-interactively.** Without a TTY and without `--yes`, both surfaces error out rather than hanging on the wizard. `--yes` fills every unprovided field with defaults — including ones you may not want silently chosen (see below). ### `op new` ```bash aishipbox op new my_op --yes --category 数据转换 --modal IMAGE --format JSONL ``` `aishipbox op new --help` is authoritative. The flags that are painful to change later: | Flag | Default | Note | |---|---|---| | `--id` | project name | letters first, `[A-Za-z0-9_]`, ≤128 — **immutable after creation** | | `--category` | 其他 | single choice | | `--modal` / `--format` / `--language` | OTHER / OTHER / zh | repeatable; `format` must be non-empty | | `--cpu-arch` | ARM | **repeatable**; `ARM` and/or `X86`, case-sensitive — drives `op download` targets | | `--auto-data-loading` | false | picks the process.py mode | | `--skeleton` | transform | `transform` = moxing copy example (**mode-two code**), `blank` = empty | **`--skeleton` must match `--auto-data-loading` — nothing couples them.** The defaults happen to pair correctly (`transform` + `false` = mode two). But ask for `--auto-data-loading true` and leave `--skeleton` alone and you get mode-two moxing I/O inside a mode-one project. The interactive wizard asks the two questions independently, so it has the same trap. For mode one: `--auto-data-loading true --skeleton blank`. **`--cpu-arch` is repeatable, and that changes `op download`.** With both arches set, `download` fetches a wheel per platform tag and **fails if the two resolve to different versions** ("不同 cpu-arch 解析出不同版本") — pin the version or handle the arches in separate projects. Only declare the arches you ship. ### `algo new` ```bash aishipbox algo new my_service --yes -t basic ``` | Flag | Default | Note | |---|---|---| | `-t/--template` | `basic` under `--yes` | `basic` / `predict` / `cv` — differ only in preinstalled deps | | project name | — | **hyphens are rejected**; use underscores (`op` has no such rule) | `--yes` picks `basic` **silently**. If you wanted `cv`, say so explicitly — there is no prompt and no warning. Then `aishipbox algo stubs` installs the `rest` package so `rest.process_base` resolves in your IDE. It is a **local stub only** — `algo pack` excludes `rest/`, and the platform provides the real module at runtime. Don't ship it, don't vendor it, don't be surprised it's missing from the tarball. ## Develop, run, pack ```bash aishipbox op run # local mock; obs_input/ → obs_output/ aishipbox op run --obs # real OBS; fill .env from .env.example first aishipbox op run --debug # wait for VS Code to attach (default port 5678) aishipbox algo run # HTTP on 127.0.0.1:8080 (-p / --host to change) aishipbox debug # write .vscode/launch.json for the above ``` Both surfaces always exec the **project's** `.venv/bin/python`, never the interpreter running the CLI. A package you installed globally is not available to your operator. For `op`, before implementing: `cat manifest.yml` → check `runtime.auto-data-loading` to know which mode you're in → read the project's `AGENTS.md` for that mode's contract. ```bash aishipbox op pack # validate + tar to program_package/.tar aishipbox op pack --force # overwrite an existing tar aishipbox algo pack # tar.gz at the service dir — NO --force flag ``` **`op pack` refuses to overwrite without `--force`; `algo pack` overwrites silently.** Same word, opposite safety. **Re-run `pack` after any `manifest.yml` or dependency change** — validation lives inside pack, there is no separate lint step. Validation errors name the exact slot (`arguments[0].type ...`). The op tar holds only what the platform needs, nested under `/`: `manifest.yml`, `program_package/process.py`, `program_package/dependency/`. Test data (`obs_input/`, `obs_output/`), `.venv/`, and `AGENTS.md` are excluded — so a passing local run proves nothing about a file you forgot to reference from `process.py`. ## Dependencies (op) The op platform image preinstalls exactly **three** packages, and `op new` pins those same versions into `.venv/` so local matches platform: ``` pandas==1.3.5 numpy==1.26.4 pyarrow==18.0.0 ``` Everything else you import must be bundled. `requirements.txt` is for those **extra** deps only, because the platform installs with: ```bash pip install --no-index --find-links=./dependency -r ./dependency/requirements.txt ``` `--no-index` means pip can only use wheels sitting in `dependency/` — it cannot reach PyPI. **Never list one of the three presets**: if the platform's version drifts, pip finds no wheel and the run breaks. `op download` refuses them outright ("已是平台预置依赖"), matching on the PEP 503 normalized name, so `Pillow`, `pillow`, and `pil.low` are all the same package to it. ```bash aishipbox op download requests # or pillow==10.4.0 ``` Resolves the wheel target from `manifest.yml > runtime > cpu-arch` (`ARM` → `manylinux2014_aarch64`, `X86` → `manylinux2014_x86_64`), downloads the Linux + Python 3.10 wheel with `--no-deps` into `dependency/`, and writes `==` into `requirements.txt`. Then re-`pack`. `--no-deps` is deliberate — transitive resolution would drag the preset packages into the bundle. **It also means you get one wheel, not a dependency tree**: a package with its own requirements needs each one downloaded explicitly. For anything more complex, drop `.whl` files into `dependency/` manually or script it in `install.sh`. `algo` has no equivalent bundling step — `aishipbox algo install-deps` installs a fixed set of common hosting-environment packages (numpy, pandas, scipy, scikit-learn, requests, flask, opencv-python, pillow, pyyaml, pydantic, aiohttp, openpyxl, lxml) into the local venv for parity. It is a **local dev convenience**, not a manifest of what the platform provides. ## Operator arguments (op) **No CLI command adds an operator argument.** Hand-edit `manifest.yml > arguments`, then re-`pack` to validate. Rules the validator enforces: - `type` ∈ `STRING`, `INT`, `FLOAT`, `BOOLEAN`, `ENUM`, `LIST`, `OBS`, `DYNAMIC_INPUT_LIST` - `INT` / `FLOAT` must **explicitly** set `between` (true or false) - `ENUM` / `LIST` / `BOOLEAN` must provide at least one `items` entry **The first line of `op run` output is `算子参数:{...}`** — the resolved values. Check it first when a parameter seems ignored. ## Common mistakes | Mistake | Consequence | |---|---| | Not reading the project's generated AGENTS.md | Guessing at the mode contract and manifest schema | | Same terminal after `uv tool update-shell` | Still "command not found" — PATH is read at process start; on Windows restart VS Code, not just the pane | | Reaching for `--native-tls` on a timeout | Wrong failure: no proxy vars set. TLS flags fix rejected certs, not unreachable networks | | `new` non-interactive without `--yes` | Errors instead of scaffolding | | `--auto-data-loading true` with default `--skeleton transform` | Mode-two moxing code in a mode-one project | | Trusting `aishipbox --version` | Hardcoded, lags the real version — use `uv tool list` | | Listing pandas/numpy/pyarrow in `requirements.txt` | Platform pip fails under `--no-index` | | Assuming `op download` pulls dependencies too | `--no-deps`; transitive requirements silently missing at runtime | | Two `--cpu-arch` values, unpinned version | `download` fails when the arches resolve differently | | Editing `manifest.yml` without re-`pack` | Stale tar; validation never runs | | Expecting `algo pack --force` | No such flag — it already overwrote your tarball | | Shipping `rest/` with an algo service | Excluded by design; the platform provides it | | Hyphen in an algo project name | Rejected — use underscores | | Mixing file types in `obs_input/` (op mode one) | Runner errors immediately | | `logging.basicConfig` instead of the platform logger | Platform log collection breaks | | Changing `.aishipbox.toml` `schema_version`/`type` | Project no longer recognized | | Any bucket but `obs://input/` or `obs://output/` in a mock run | Errors immediately |