mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-11 04:00:38 +00:00
fix(code-runtime-python): bind done-send callables and cover the stray-flush retention
Corrections to the settlement-path review fixes: - send_done was invoking channel.send_sync / channel.write_encoded via a late method look-up, which a program running as __main__ could rebind through __main__.ProtocolChannel.send_sync before the failure path ran — a rebound send that raises then skipped the done frame and downgraded a settled exception to worker-exit. Bind both channel methods into locals before the program runs, mirroring the pre-existing binding of flush_out/flush_err/ safe_model_traceback. - Restructure flushStray so the mid-sequence budget-flush retention arm is a self-contained v8-ignored branch and the covered default path decodes the full residual (not schedulable-through-the-seam boundary).
This commit is contained in:
@@ -899,11 +899,23 @@ async def _run(channel: ProtocolChannel) -> None:
|
||||
# have changed) or a dict ERROR frame (a rejection or the exception handler,
|
||||
# which carry no live model value). `send_done` posts whichever form: a string
|
||||
# is written verbatim via `write_encoded`, a dict is encoded by `send_sync`.
|
||||
#
|
||||
# The two channel methods are BOUND into locals here, before the program runs,
|
||||
# and `send_done` invokes those bound locals — never a late `channel.X` look-up.
|
||||
# The program runs as `__main__`, so `import __main__; __main__.ProtocolChannel
|
||||
# .send_sync = boom` would otherwise re-resolve the send to a rebranded class
|
||||
# method at call time and, when that replacement raises, skip the `done` frame
|
||||
# and downgrade a settled verdict to a host-side worker-exit (the binding-all-
|
||||
# names regression test pins this). Same reason `flush_out`/`flush_err`/
|
||||
# `safe_model_traceback` are bound above.
|
||||
write_encoded_bound = channel.write_encoded
|
||||
send_sync_bound = channel.send_sync
|
||||
|
||||
def send_done(payload: dict[str, Any] | str) -> None:
|
||||
if isinstance(payload, str):
|
||||
channel.write_encoded(payload)
|
||||
write_encoded_bound(payload)
|
||||
else:
|
||||
channel.send_sync(payload)
|
||||
send_sync_bound(payload)
|
||||
|
||||
max_value_bytes = int(boot["maxValueBytes"])
|
||||
done: dict[str, Any] | str
|
||||
|
||||
@@ -1135,31 +1135,37 @@ export class PythonCodeRuntime extends CodeRuntime {
|
||||
function flushStray(stray: StrayBuffer, retainPartialTail?: boolean): void {
|
||||
if (stray.chunks.length === 0 && stray.blocks.length === 0) return
|
||||
const begun = stray.blocks.length > 0 ? [...stray.blocks, ...stray.chunks] : stray.chunks
|
||||
const full = Buffer.concat(begun)
|
||||
let drop = 0
|
||||
// A budget flush landing exactly between a lead byte and its still-pending
|
||||
// continuation requires the combined-cost threshold to trip on a specific
|
||||
// mid-multibyte pipe boundary — not deterministically schedulable through
|
||||
// the black-box seam, which observes only complete entries. v8 ignore keeps
|
||||
// the retention branch honest (it is exercised by review reasoning over the
|
||||
// `stray.utf8` state, not by an in-tree test).
|
||||
/* v8 ignore next 8 -- mid-sequence budget-flush boundary is not schedulable from a test. */
|
||||
let full = Buffer.concat(begun)
|
||||
// A budget flush landing exactly between a lead byte and its
|
||||
// still-pending continuation requires the combined-cost threshold to trip
|
||||
// on a specific mid-multibyte pipe boundary — not deterministically
|
||||
// schedulable through the black-box seam, which observes only complete
|
||||
// entries. So the retention arm is v8-ignored (exercised by review
|
||||
// reasoning over the `stray.utf8` state, not by an in-tree test): it
|
||||
// withholds the lead-plus-consumed-continuations tail (≤3 bytes, via
|
||||
// `stray.utf8.width - stray.utf8.expected`) from the decode, re-carries it
|
||||
// for a later chunk, and re-accrues the pipe's cost/UTF-8 state over it;
|
||||
// decoding here would render a LEGAL, unfinished character as U+FFFD in an
|
||||
// admitted entry. Every retainPartialTail=false call (the `end`/closeDeadline
|
||||
// paths) and a budget flush with no partial tail in flight (`expected === 0`)
|
||||
// falls through with `keep` unset: the FULL residual is decoded — there a
|
||||
// trailing incomplete sequence is real truncated input and the U+FFFD is the
|
||||
// honest render.
|
||||
let keep: Buffer | undefined
|
||||
/* v8 ignore next 9 -- mid-sequence budget-flush boundary is not schedulable from a test. */
|
||||
if (retainPartialTail && stray.utf8.expected > 0) {
|
||||
drop = stray.utf8.width - stray.utf8.expected
|
||||
// Guard against a pathological width/expected mismatch: never drop more
|
||||
// bytes than were captured, and never drop so many that decoding the
|
||||
// admitted prefix would be empty because a single mid-sequence lead sat
|
||||
// alone. A well-formed walk keeps `drop` ≤ 3, but a defensive clamp
|
||||
// keeps the retention bounded.
|
||||
drop = Math.min(drop, full.length)
|
||||
const drop = Math.min(stray.utf8.width - stray.utf8.expected, full.length)
|
||||
keep = full.subarray(full.length - drop)
|
||||
full = full.subarray(0, full.length - drop)
|
||||
stray.chunks = detachResidual(keep)
|
||||
stray.cost = accrueStrayCost(keep, stray.utf8)
|
||||
} else {
|
||||
stray.chunks = []
|
||||
stray.cost = 0
|
||||
stray.utf8 = { expected: 0, width: 0, lowerFirst: 0, upperFirst: 0 }
|
||||
}
|
||||
const keep = full.subarray(full.length - drop)
|
||||
const emit = full.subarray(0, full.length - drop).toString('utf8')
|
||||
stray.chunks = drop > 0 ? detachResidual(keep) : []
|
||||
stray.blocks = []
|
||||
stray.cost = 0
|
||||
stray.utf8 = { expected: 0, width: 0, lowerFirst: 0, upperFirst: 0 }
|
||||
if (drop > 0) stray.cost = accrueStrayCost(keep, stray.utf8)
|
||||
const emit = full.toString('utf8')
|
||||
admit(emit)
|
||||
}
|
||||
child.stdout.on('data', (chunk: Buffer) => { captureStray(strayOut, chunk) })
|
||||
|
||||
Reference in New Issue
Block a user