Define stdout as one versioned JSON document
A successful Python process can print text that is useless to a downstream application. Define the output protocol before writing the calculation: one JSON object on stdout, a schema version, explicit units and bounded fields. Keep human-readable progress messages on stderr. The consumer should require the entire stdout to parse as that one document, rather than scraping the last line or extracting a JSON-looking substring.
This recipe totals three synthetic prices in integer cents. The task accepts at most one hundred bounded integers and returns a count and total. It demonstrates protocol design independently of business data. cpuOS captures stdout and stderr after execution; the pilot does not provide a live stream, file-download result or persistent notebook.
Scroll horizontally to see every column.
| Field | Contract | Reason |
|---|---|---|
| schema_version | Integer 1 | Make future incompatible changes explicit. |
| count | Integer 1–100 | Describe the number of validated amounts. |
| total_cents | Nonnegative safe integer, bounded by count × 100000 | Keep units and maximum range visible. |
Print the result only after complete validation
import jsonimport sysamounts = [250, 500, 1000] # Synthetic integer cents.try: if not amounts or len(amounts) > 100: raise ValueError("Expected 1 to 100 amounts.") if any(type(value) is not int or not 0 <= value <= 100_000 for value in amounts): raise ValueError("Amounts must be bounded integer cents.") result = {"schema_version": 1, "count": len(amounts), "total_cents": sum(amounts)} encoded = json.dumps(result, allow_nan=False, sort_keys=True, separators=(",", ":")) if len(encoded.encode("utf-8")) > 4096: raise ValueError("Result exceeds this recipe's 4 KiB budget.")except ValueError as error: print(str(error), file=sys.stderr) sys.exit(1)print("Validated 3 synthetic rows.", file=sys.stderr)print(encoded){ "schema_version": 1, "count": 3, "total_cents": 1750}Run python3 structured-result.py locally before submitting it. stdout contains one compact JSON document; stderr contains a fixed synthetic diagnostic. The Python sys documentation distinguishes the output streams. On a validation failure, the script exits nonzero and prints no partial result.
type(value) is int deliberately rejects booleans. An unrestricted Python integer check could otherwise admit True as a numeric input. Adapt the exact count and price limits to your task, then test both boundary values and wrong types. Input parsing, authorization and business rules remain your application's responsibility.
Choose JSON serialization rules that consumers can enforce
The producer uses allow_nan=False so non-finite numbers cannot silently become NaN or Infinity output. Compact separators keep the document small. Python's JSON encoder documents those options and explains that repeatedly dumping independent objects does not produce one framed JSON document.
A schema version is not a schema validator. Specify required keys, permitted extra fields, value types and application invariants. This version permits only three fields and represents money in integer cents. If a later task requires decimals, several currencies or optional values, define a different contract rather than overloading an existing field's meaning.
Keep emitted values within the consumer's numeric range. The example's maximum total is ten million cents, comfortably within JavaScript's safe-integer range. JSON key ordering is irrelevant after parsing. Compare semantic values instead of requiring a particular whitespace layout or dictionary order.
Validate the execution result before validating its JSON
Use the Python jobs client or reusable Node client to submit the published source with template: "python". After polling reaches a terminal state, inspect the job envelope first. A failed or cancelled attempt, nonzero exit, execution error or truncated capture must not become successful model evidence.
import assert from "node:assert/strict"function validateJobResult(job) { if (job.status !== "completed" || job.exitCode !== 0 || job.error || job.outputTruncated !== false || typeof job.stdout !== "string") { throw new Error("No complete successful job result.") } if (Buffer.byteLength(job.stdout, "utf8") > 4096) { throw new Error("Result exceeds this application's 4 KiB budget.") } let result try { result = JSON.parse(job.stdout) } catch { throw new Error("Result is not one JSON document.") } const fields = ["schema_version", "count", "total_cents"] if (result === null || typeof result !== "object" || Array.isArray(result) || Object.keys(result).length !== fields.length || fields.some((field) => !Object.hasOwn(result, field))) { throw new Error("Result fields do not match schema version 1.") } if (result.schema_version !== 1 || !Number.isInteger(result.count) || result.count < 1 || result.count > 100 || !Number.isSafeInteger(result.total_cents) || result.total_cents < 0 || result.total_cents > result.count * 100_000) { throw new Error("Result values violate schema version 1.") } return result}// Local API-result fixture; replace this with the GET /v1/jobs/:id object.const expected = {"schema_version":1,"count":3,"total_cents":1750}const fixture = { status: "completed", exitCode: 0, error: null, outputTruncated: false, stdout: JSON.stringify(expected), stderr: "Validated 3 synthetic rows.\n",}assert.deepStrictEqual(validateJobResult(fixture), expected)assert.throws(() => validateJobResult({ ...fixture, outputTruncated: true }))assert.throws(() => validateJobResult({ ...fixture, stdout: "debug\n" + fixture.stdout }))assert.throws(() => validateJobResult({ ...fixture, stdout: '{"schema_version":1,"count":true,"total_cents":1750}' }))console.log("Structured result fixture validated.")Run node validate-result.mjs locally. It checks a known successful fixture and rejects truncation, a debug prefix and a boolean count. Replace the synthetic fixture with the returned GET /v1/jobs/:id object in your application. This validator requires the current explicit outputTruncated: false field rather than assuming an absent flag means complete output.
Schema validity and calculation correctness are separate checks
A correctly typed total could still be arithmetically wrong. The local fixture requires count three and total 1750 through complete object equality. A production consumer should retain expected input counts, identifiers or independently checkable invariants for its own task. Do not accept an unknown total merely because it falls inside a broad numeric range.
The hand-written validator is a small application contract, not a general JSON Schema implementation. It uses Node's strict assertions for fixture verification and language-level checks for the result. Avoid coercing strings to numbers or interpreting an error-shaped object as a successful result. The JSON transformation guide expands input validation for records.
Reserve output space for evidence rather than verbose logs
cpuOS bounds each captured stdout and stderr stream at 64 KiB and reports outputTruncated when capture exceeds its limit. This recipe imposes a smaller four-KiB business result budget. Reject truncation even if a captured prefix happens to be parseable JSON: a complete protocol needs evidence that output was fully retained.
A large stderr diagnostic can also make the capture incomplete. Log a concise rule or record index, avoiding credentials and unnecessary customer values. The hosted control plane sees submitted source and outputs, including inline inputs and logs. An API key belongs in the client environment, never inside the Python job.
For larger datasets, emit aggregates and stable identifiers or divide the input into bounded jobs. Additional packages, network downloads and file-transfer endpoints are outside the current pilot. Keep each source under its separate 64-KiB UTF-8 code limit.
Return the checked object through an approved agent tool
An agent can consume the validated object with an explicit success outcome, while your application keeps raw diagnostics for inspection. On failure, return a failure outcome and reason instead of guessing missing fields or asking the model to repair truncated text. gpuOS tool calling covers the model interaction; asynchronous Python jobs cover lifecycle recovery before that handoff.