Refuse what a node cannot publish, and stop timing out work that is fine
Four things the python SDK turned up, each fixed where every client sees it. A key no port declares is now an error rather than a silent drop, on the return, the yield and the emit alike — the contract the docs already stated. The SDK reads literal yields at sync time, so a typo fails before anything runs, and an emission of one fails the call rather than being logged where nobody looks. NaN and infinity are refused at the port. JSON cannot spell either, so one that travelled came back as a 500, a socket frame that stopped the canvas, or a metric batch the database dropped whole. An artifact input takes `@run:<id>.<output>` or a bare digest, resolved on the engine — so the CLI, the run dialog and a python caller mean the same thing, and a sweep can pass one at all. Node timeouts are off by default. The clock measured silence, which a training node is full of, and remote workers had already stopped enforcing it — their heartbeat reset it. Now a heartbeat proves the agent rather than the node, ninety seconds of nothing fails the call either way, and the engine touches work it is still running so a long node is not redelivered at sixty seconds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019V5bsYGNxcgPs4xXmTPx69
This commit is contained in:
@@ -425,18 +425,33 @@ def main() -> None:
|
||||
}
|
||||
response["logs"] = captured.getvalue()
|
||||
try:
|
||||
reply = json.dumps(response)
|
||||
# allow_nan=False: a bare NaN is what json.dumps would write, and
|
||||
# nothing downstream can read it back — the ports refuse one too,
|
||||
# but this is the crossing, so refuse it where it is still the
|
||||
# node's own reply rather than a row somebody queries later.
|
||||
reply = json.dumps(response, allow_nan=False)
|
||||
except (TypeError, ValueError, RecursionError):
|
||||
# Encoding the reply is also the check that the node returned
|
||||
# something the typed-message contract can carry — the result is
|
||||
# the only part of this dict a node controls, so it is the only
|
||||
# part that can fail. Doing it once is why there is no separate
|
||||
# dumps of the result above.
|
||||
kind = type(response.pop("result", None)).__name__
|
||||
trouble = (
|
||||
f"returned {kind}, which cannot be sent back as JSON — "
|
||||
"return numbers, strings, booleans, lists or dicts."
|
||||
)
|
||||
result = response.pop("result", None)
|
||||
try:
|
||||
json.dumps(result)
|
||||
except (TypeError, ValueError, RecursionError):
|
||||
trouble = (
|
||||
f"returned {type(result).__name__}, which cannot be sent "
|
||||
"back as JSON — return numbers, strings, booleans, lists "
|
||||
"or dicts."
|
||||
)
|
||||
else:
|
||||
# It encodes with NaN allowed, so that is what is wrong with it.
|
||||
trouble = (
|
||||
"returned a NaN or an infinity, which JSON cannot carry — "
|
||||
"publish None, or a number that says the measurement had "
|
||||
"nothing in it."
|
||||
)
|
||||
response["ok"] = False
|
||||
response["error"] = {
|
||||
"type": "ValueError",
|
||||
|
||||
Reference in New Issue
Block a user