[{"data":1,"prerenderedAt":662},["ShallowReactive",2],{"docs-/docs/development/entry-point":3},{"id":4,"title":5,"body":6,"description":655,"extension":656,"meta":657,"navigation":450,"path":658,"seo":659,"stem":660,"__hash__":661},"docs/docs/development/entry-point.md","Entry point",{"type":7,"value":8,"toc":645},"minimark",[9,18,36,60,68,91,111,120,124,129,190,203,346,351,366,370,384,404,411,417,421,436,471,495,499,502,535,539,556,560,573,626,641],[10,11,13,14],"h1",{"id":12},"entry-point-mainpy","Entry point — ",[15,16,17],"code",{},"main.py",[19,20,21,22,25,26,28,29,32,33,35],"p",{},"Every ",[15,23,24],{},"INPUT_OUTPUT"," Python tool has a ",[15,27,17],{}," at the root of its repo. This is the file the sandbox runs, and it must define a top-level function called ",[15,30,31],{},"process",". On every run, the sandbox calls ",[15,34,31],{},", passes it the user's input and settings, and renders whatever it returns into the output panel.",[37,38,39],"blockquote",{},[19,40,41,42,44,45,48,49,51,52,59],{},"This page applies to ",[15,43,24],{}," tools. ",[15,46,47],{},"IFRAME"," tools have no ",[15,50,31],{}," function — they render a web page instead. See ",[53,54,56],"a",{"href":55},"/docs/development/artifuncs-json",[15,57,58],{},"artifuncs.json"," for the difference.",[61,62,64,65,67],"h2",{"id":63},"the-process-function","The ",[15,66,31],{}," function",[69,70,75],"pre",{"className":71,"code":72,"language":73,"meta":74,"style":74},"language-python shiki shiki-themes github-light github-dark","def process(input: dict, settings: dict) -> dict:\n    return {}\n","python","",[15,76,77,85],{"__ignoreMap":74},[78,79,82],"span",{"class":80,"line":81},"line",1,[78,83,84],{},"def process(input: dict, settings: dict) -> dict:\n",[78,86,88],{"class":80,"line":87},2,[78,89,90],{},"    return {}\n",[19,92,93,94,96,97,99,100,102,103,105,106,110],{},"That's the whole contract. The sandbox loads ",[15,95,17],{},", looks for ",[15,98,31],{},", and calls it once per run. If ",[15,101,17],{}," has no ",[15,104,31],{}," function, the tool fails to start with ",[107,108,109],"em",{},"\"Tool missing process() function\"",".",[19,112,113,115,116,119],{},[15,114,31],{}," is synchronous — return the result directly, don't ",[15,117,118],{},"await"," it.",[61,121,123],{"id":122},"what-it-receives","What it receives",[19,125,126,128],{},[15,127,31],{}," is called with two arguments, both plain dicts:",[130,131,132,148],"table",{},[133,134,135],"thead",{},[136,137,138,142,145],"tr",{},[139,140,141],"th",{},"Argument",[139,143,144],{},"Where it comes from",[139,146,147],{},"Keyed by",[149,150,151,171],"tbody",{},[136,152,153,159,162],{},[154,155,156],"td",{},[15,157,158],{},"input",[154,160,161],{},"The per-run input form the user fills in",[154,163,164,165,168,169],{},"Your ",[15,166,167],{},"fields.input"," names from ",[15,170,58],{},[136,172,173,178,184],{},[154,174,175],{},[15,176,177],{},"settings",[154,179,64,180,183],{},[107,181,182],{},"Settings"," panel — values that persist between runs",[154,185,164,186,168,188],{},[15,187,177],{},[15,189,58],{},[19,191,192,193,197,198,202],{},"Each key matches a field name you declared in ",[53,194,195],{"href":55},[15,196,58],{},", and each value has the type of that field (see ",[53,199,201],{"href":200},"/docs/development/fields","Fields","). For example, given:",[69,204,208],{"className":205,"code":206,"language":207,"meta":74,"style":74},"language-json shiki shiki-themes github-light github-dark","\"fields\": {\n  \"input\": {\n    \"a\": { \"type\": \"number\", \"default\": 0 },\n    \"b\": { \"type\": \"number\", \"default\": 0 }\n  }\n},\n\"settings\": {\n  \"precision\": { \"type\": \"number\", \"label\": \"Decimal places\", \"default\": 2 }\n}\n","json",[15,209,210,220,228,260,285,291,297,305,340],{"__ignoreMap":74},[78,211,212,216],{"class":80,"line":81},[78,213,215],{"class":214},"sZZnC","\"fields\"",[78,217,219],{"class":218},"sVt8B",": {\n",[78,221,222,226],{"class":80,"line":87},[78,223,225],{"class":224},"sj4cs","  \"input\"",[78,227,219],{"class":218},[78,229,231,234,237,240,243,246,249,252,254,257],{"class":80,"line":230},3,[78,232,233],{"class":224},"    \"a\"",[78,235,236],{"class":218},": { ",[78,238,239],{"class":224},"\"type\"",[78,241,242],{"class":218},": ",[78,244,245],{"class":214},"\"number\"",[78,247,248],{"class":218},", ",[78,250,251],{"class":224},"\"default\"",[78,253,242],{"class":218},[78,255,256],{"class":224},"0",[78,258,259],{"class":218}," },\n",[78,261,263,266,268,270,272,274,276,278,280,282],{"class":80,"line":262},4,[78,264,265],{"class":224},"    \"b\"",[78,267,236],{"class":218},[78,269,239],{"class":224},[78,271,242],{"class":218},[78,273,245],{"class":214},[78,275,248],{"class":218},[78,277,251],{"class":224},[78,279,242],{"class":218},[78,281,256],{"class":224},[78,283,284],{"class":218}," }\n",[78,286,288],{"class":80,"line":287},5,[78,289,290],{"class":218},"  }\n",[78,292,294],{"class":80,"line":293},6,[78,295,296],{"class":218},"},\n",[78,298,300,303],{"class":80,"line":299},7,[78,301,302],{"class":214},"\"settings\"",[78,304,219],{"class":218},[78,306,308,311,313,315,317,319,321,324,326,329,331,333,335,338],{"class":80,"line":307},8,[78,309,310],{"class":224},"  \"precision\"",[78,312,236],{"class":218},[78,314,239],{"class":224},[78,316,242],{"class":218},[78,318,245],{"class":214},[78,320,248],{"class":218},[78,322,323],{"class":224},"\"label\"",[78,325,242],{"class":218},[78,327,328],{"class":214},"\"Decimal places\"",[78,330,248],{"class":218},[78,332,251],{"class":224},[78,334,242],{"class":218},[78,336,337],{"class":224},"2",[78,339,284],{"class":218},[78,341,343],{"class":80,"line":342},9,[78,344,345],{"class":218},"}\n",[19,347,348,350],{},[15,349,31],{}," receives:",[69,352,354],{"className":71,"code":353,"language":73,"meta":74,"style":74},"input    == { \"a\": 3, \"b\": 4 }\nsettings == { \"precision\": 2 }\n",[15,355,356,361],{"__ignoreMap":74},[78,357,358],{"class":80,"line":81},[78,359,360],{},"input    == { \"a\": 3, \"b\": 4 }\n",[78,362,363],{"class":80,"line":87},[78,364,365],{},"settings == { \"precision\": 2 }\n",[61,367,369],{"id":368},"what-it-returns","What it returns",[19,371,372,373,376,377,383],{},"Return a ",[15,374,375],{},"dict"," whose keys match your ",[378,379,380],"strong",{},[15,381,382],{},"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.",[69,385,387],{"className":71,"code":386,"language":73,"meta":74,"style":74},"def process(input, settings):\n    total = input[\"a\"] + input[\"b\"]\n    return { \"sum\": round(total, settings[\"precision\"]) }\n",[15,388,389,394,399],{"__ignoreMap":74},[78,390,391],{"class":80,"line":81},[78,392,393],{},"def process(input, settings):\n",[78,395,396],{"class":80,"line":87},[78,397,398],{},"    total = input[\"a\"] + input[\"b\"]\n",[78,400,401],{"class":80,"line":230},[78,402,403],{},"    return { \"sum\": round(total, settings[\"precision\"]) }\n",[19,405,406,407,410],{},"Returning an empty ",[15,408,409],{},"{}"," is valid — it just produces empty outputs.",[19,412,413,414,416],{},"If ",[15,415,31],{}," 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.",[61,418,420],{"id":419},"logging","Logging",[19,422,423,424,427,428,431,432,435],{},"Anything you ",[15,425,426],{},"print()"," is captured and streamed to the run's log panel as a ",[15,429,430],{},"DEBUG"," line. For levelled logging, use the SDK's ",[15,433,434],{},"log"," object:",[69,437,439],{"className":71,"code":438,"language":73,"meta":74,"style":74},"from artifuncs.sandbox_sdk import log\n\ndef process(input, settings):\n    log.info(\"processing started\")\n    log.debug(f\"a={input['a']} b={input['b']}\")\n    return { \"sum\": input[\"a\"] + input[\"b\"] }\n",[15,440,441,446,452,456,461,466],{"__ignoreMap":74},[78,442,443],{"class":80,"line":81},[78,444,445],{},"from artifuncs.sandbox_sdk import log\n",[78,447,448],{"class":80,"line":87},[78,449,451],{"emptyLinePlaceholder":450},true,"\n",[78,453,454],{"class":80,"line":230},[78,455,393],{},[78,457,458],{"class":80,"line":262},[78,459,460],{},"    log.info(\"processing started\")\n",[78,462,463],{"class":80,"line":287},[78,464,465],{},"    log.debug(f\"a={input['a']} b={input['b']}\")\n",[78,467,468],{"class":80,"line":293},[78,469,470],{},"    return { \"sum\": input[\"a\"] + input[\"b\"] }\n",[19,472,473,475,476,248,479,248,482,485,486,489,490,494],{},[15,474,434],{}," exposes ",[15,477,478],{},"debug",[15,480,481],{},"info",[15,483,484],{},"warn",", and ",[15,487,488],{},"error",". See ",[53,491,493],{"href":492},"/docs/sandbox/running-tools","Running tools"," for how logs reach the UI.",[61,496,498],{"id":497},"working-with-files","Working with files",[19,500,501],{},"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:",[69,503,505],{"className":71,"code":504,"language":73,"meta":74,"style":74},"from artifuncs.sandbox_sdk import get_file_content, save_file_content, list_files\n\ndef process(input, settings):\n    data = get_file_content(input[\"upload\"])\n    save_file_content(\"result.txt\", b\"...\")\n    return { \"files\": list_files() }\n",[15,506,507,512,516,520,525,530],{"__ignoreMap":74},[78,508,509],{"class":80,"line":81},[78,510,511],{},"from artifuncs.sandbox_sdk import get_file_content, save_file_content, list_files\n",[78,513,514],{"class":80,"line":87},[78,515,451],{"emptyLinePlaceholder":450},[78,517,518],{"class":80,"line":230},[78,519,393],{},[78,521,522],{"class":80,"line":262},[78,523,524],{},"    data = get_file_content(input[\"upload\"])\n",[78,526,527],{"class":80,"line":287},[78,528,529],{},"    save_file_content(\"result.txt\", b\"...\")\n",[78,531,532],{"class":80,"line":293},[78,533,534],{},"    return { \"files\": list_files() }\n",[61,536,538],{"id":537},"dependencies","Dependencies",[19,540,541,542,545,546,548,549,551,552,555],{},"Third-party packages go in ",[15,543,544],{},"requirements.txt"," next to ",[15,547,17],{},"; the sandbox installs them when the tool is set up. Import them in ",[15,550,17],{}," as usual. See ",[53,553,538],{"href":554},"/docs/development/dependencies"," for the full story on both languages.",[61,557,559],{"id":558},"javascript-tools","JavaScript tools",[19,561,562,563,566,567,569,570,572],{},"JavaScript tools use ",[15,564,565],{},"index.js"," instead of ",[15,568,17],{},", and export the same ",[15,571,31],{}," contract. The function may be sync or async:",[69,574,578],{"className":575,"code":576,"language":577,"meta":74,"style":74},"language-js shiki shiki-themes github-light github-dark","export async function process(input, settings) {\n    return { sum: input.a + input.b }\n}\n","js",[15,579,580,608,622],{"__ignoreMap":74},[78,581,582,586,589,591,595,598,601,603,605],{"class":80,"line":81},[78,583,585],{"class":584},"szBVR","export",[78,587,588],{"class":584}," async",[78,590,67],{"class":584},[78,592,594],{"class":593},"sScJk"," process",[78,596,597],{"class":218},"(",[78,599,158],{"class":600},"s4XuR",[78,602,248],{"class":218},[78,604,177],{"class":600},[78,606,607],{"class":218},") {\n",[78,609,610,613,616,619],{"class":80,"line":87},[78,611,612],{"class":584},"    return",[78,614,615],{"class":218}," { sum: input.a ",[78,617,618],{"class":584},"+",[78,620,621],{"class":218}," input.b }\n",[78,623,624],{"class":80,"line":230},[78,625,345],{"class":218},[19,627,628,629,632,633,636,637,640],{},"Dependencies are declared in ",[15,630,631],{},"package.json",". Everything else on this page — the arguments, the return shape, logging via ",[15,634,635],{},"console.log","/",[15,638,639],{},"print",", and field-name mapping — works the same way.",[642,643,644],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":74,"searchDepth":87,"depth":87,"links":646},[647,649,650,651,652,653,654],{"id":63,"depth":87,"text":648},"The process function",{"id":122,"depth":87,"text":123},{"id":368,"depth":87,"text":369},{"id":419,"depth":87,"text":420},{"id":497,"depth":87,"text":498},{"id":537,"depth":87,"text":538},{"id":558,"depth":87,"text":559},"main.py and the process() function — the function the sandbox calls on every run, what it receives, and what it returns.","md",{},"/docs/development/entry-point",{"title":5,"description":655},"docs/development/entry-point","f_12Wo-WWQz6rL5optJoyWmU5iiyiT0JgwNrFqc3P1g",1781004913741]