Entry point — main.py
Every INPUT_OUTPUT Python tool has a main.py at the root of its repo. This is the file the sandbox runs, and it must define a top-level function called process. On every run, the sandbox calls process, passes it the user's input and settings, and renders whatever it returns into the output panel.
This page applies to
INPUT_OUTPUTtools.IFRAMEtools have noprocessfunction — they render a web page instead. Seeartifuncs.jsonfor the difference.
The process function
def process(input: dict, settings: dict) -> dict:
return {}
That's the whole contract. The sandbox loads main.py, looks for process, and calls it once per run. If main.py has no process function, the tool fails to start with "Tool missing process() function".
process is synchronous — return the result directly, don't await it.
What it receives
process is called with two arguments, both plain dicts:
| Argument | Where it comes from | Keyed by |
|---|---|---|
input | The per-run input form the user fills in | Your fields.input names from artifuncs.json |
settings | The Settings panel — values that persist between runs | Your settings names from artifuncs.json |
Each key matches a field name you declared in artifuncs.json, and each value has the type of that field (see Fields). For example, given:
"fields": {
"input": {
"a": { "type": "number", "default": 0 },
"b": { "type": "number", "default": 0 }
}
},
"settings": {
"precision": { "type": "number", "label": "Decimal places", "default": 2 }
}
process receives:
input == { "a": 3, "b": 4 }
settings == { "precision": 2 }
What it returns
Return a dict whose keys match your fields.output declarations. The sandbox maps each key onto the matching output field and renders it. Keys that don't match an output field are ignored; missing keys leave that output empty.
def process(input, settings):
total = input["a"] + input["b"]
return { "sum": round(total, settings["precision"]) }
Returning an empty {} is valid — it just produces empty outputs.
If process raises, the run fails and the exception's type and message are streamed back to the UI instead of a result, so let errors propagate rather than swallowing them.
Logging
Anything you print() is captured and streamed to the run's log panel as a DEBUG line. For levelled logging, use the SDK's log object:
from artifuncs.sandbox_sdk import log
def process(input, settings):
log.info("processing started")
log.debug(f"a={input['a']} b={input['b']}")
return { "sum": input["a"] + input["b"] }
log exposes debug, info, warn, and error. See Running tools for how logs reach the UI.
Working with files
When your tool has file inputs or outputs, read and write them through the SDK rather than touching the filesystem directly — it sandboxes paths to the tool's files directory:
from artifuncs.sandbox_sdk import get_file_content, save_file_content, list_files
def process(input, settings):
data = get_file_content(input["upload"])
save_file_content("result.txt", b"...")
return { "files": list_files() }
Dependencies
Third-party packages go in requirements.txt next to main.py; the sandbox installs them when the tool is set up. Import them in main.py as usual. See Dependencies for the full story on both languages.
JavaScript tools
JavaScript tools use index.js instead of main.py, and export the same process contract. The function may be sync or async:
export async function process(input, settings) {
return { sum: input.a + input.b }
}
Dependencies are declared in package.json. Everything else on this page — the arguments, the return shape, logging via console.log/print, and field-name mapping — works the same way.