Skip to content

Test a script locally

A script runs on the platform in a sandbox started from one public image. You can pull that image and run your script in it on your own machine, without network access as on the platform, before you register a version. What runs there is what the platform runs: the same Python and Node.js, the same libraries at the same versions (What the sandbox image ships).

A local test checks your program. It does not test the argument schema, the limits of your key or the files a turn attaches; call the script directly for that once the version is registered.

What you need

  • Docker, or another tool that runs OCI images.
  • The release your platform runs. Every release publishes its sandbox image as ghcr.io/artific-ai/artific-core/sandbox:<version>, such as sandbox:1.42.0. The version is the info.version your deployment's /api/openapi.json reports (Versions). Pulling the image needs no sign-in.
IMAGE=ghcr.io/artific-ai/artific-core/sandbox:<version>
docker pull "$IMAGE"

When no image is published under that version, your platform runs a build ahead of its release, as a test environment does. Ask your operator for the exact image, ghcr.io/artific-ai/artific-core/sandbox@sha256:<digest>, and use that as IMAGE in the same way.

To check that you pulled the image your platform runs, compare its Python libraries with the pypi entries GET /packages answers with approval: image. The listing also names pip itself, which is the image's installer and not on that list:

docker run --rm "$IMAGE" python3 -m pip list --format=freeze

The image is built for Linux on x86-64. On a machine with another processor, such as a Mac with Apple silicon, add --platform linux/amd64 to every docker command on this page.

Run the script

Run from the directory that holds your entrypoint. The command mounts that directory as /workspace and runs the script there. On the platform a script runs in a directory of its own below /workspace; relative paths to its own files work the same in both. Write only below /workspace: the platform may make every other path read-only. The command gives the script what a call gives it (What the script receives): the arguments on standard input and in the file ARTIFIC_ARGUMENTS names, and a directory for the files it returns.

echo '{"slides": ["Plan", "Budget"]}' > arguments.json
mkdir -p out
docker run --rm -i --network none \
  -v "$PWD:/workspace" \
  -e ARTIFIC_ARGUMENTS=/workspace/arguments.json \
  -e ARTIFIC_OUTPUTS=/workspace/out \
  "$IMAGE" python3 main.py < arguments.json

For a Node.js entrypoint, end the command with node index.js. The files the script wrote are in out/, and what it printed on standard output and standard error is what the model reads (What a call does). mkdir -p out keeps the files of an earlier run; Core empties the directory before every call.

A script that reads a turn's attachments finds them under ARTIFIC_INPUTS, as <file id>/<file name>. Put test files in inputs/<file id>/<file name> beside your entrypoint and add -e ARTIFIC_INPUTS=/workspace/inputs to the command.

Part of the command Why
--network none the sandbox has no network; a script that needs one fails here as it fails on the platform
-v "$PWD:/workspace" the script runs in its own directory, as an ordinary user, not root, whose home is /workspace
-i and < arguments.json the arguments arrive on standard input

The image runs as user id 1000. On Linux, when your own user id is another one, the script cannot write to the mounted directory; add --user "$(id -u):$(id -g)".

With packages you approved

A library that is not in the image reaches a script as an approved package: a wheel or an npm tarball, installed in the sandbox from the file, offline. Do the same locally with the files you would upload for approval, with every dependency that is not in the image. Put the wheels in wheels/ and the tarballs in tarballs/ beside your entrypoint.

Python:

docker run --rm --network none -v "$PWD:/workspace" "$IMAGE" sh -c '
  python3 -m pip install --no-index --no-deps --no-compile --no-cache-dir \
    --disable-pip-version-check --quiet --target .packages/python ./wheels/*.whl &&
  PYTHONPATH=.packages/python python3 main.py'

Node.js:

docker run --rm --network none -v "$PWD:/workspace" "$IMAGE" sh -c '
  npm install --offline --ignore-scripts --no-bin-links --no-audit --no-fund --no-save \
    --no-package-lock --cache /workspace/.packages/.npm-cache \
    --prefix .packages/node ./tarballs/*.tgz &&
  NODE_PATH=.packages/node/node_modules:/opt/artific/node/node_modules node index.js'

These are the install commands Core runs on a call, with the same options. --no-deps and --offline mean nothing is resolved or fetched: a dependency you left out fails at the script's own import or require, here as on the platform. Write ./tarballs/, with the leading ./; npm reads a bare tarballs/name.tgz as a repository on GitHub. Add the -i, -e and < arguments.json parts of Run the script when the script reads arguments.

A local test leaves files in your directory that are not the script's: arguments.json, out/, inputs/, .packages/, and dot-directories libraries write to the home directory, such as .cache/ and .config/. Register only the script's own files.

When a local test differs

What you see Cause
ModuleNotFoundError or Cannot find module for a library on the reference page the image you pulled is not the one your platform runs; pull the tag of the release your platform runs
the same error for another library it is not in the image: approve it and install it as above
No matching distribution found or a wheel that is "not a supported wheel on this platform" the wheel is built for another platform or Python than the sandbox's (Runtimes)
Permission denied writing a file your user id is not 1000; add --user "$(id -u):$(id -g)"