Repository navigation
Capture application stdout/stderr during mic run (0.6.0) - #34
Merged
Merged
Conversation
Application prints used to be interleaved into the live progress display and into stdout alongside the summary or --json manifest. mic run now captures Python-level writes to sys.stdout and sys.stderr for the duration of a run: hidden by default, recorded to output.jsonl (mic-output-v1) with --output, and shown above the status line with --show-output, prefixed by row, trial, and scorer. Writes are attributed through a context variable set around each trial and scorer, so concurrent trials never splice partial lines together. The callback pool now copies context into executor threads, which run_in_executor does not do by default. run()/arun() gain capture_output=False; library callers keep their streams unless they opt in. Bump the version to 0.6.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Piped stderr on Windows cannot encode the Unicode glyphs, so progress correctly falls back to ASCII; the test hardcoded the Unicode separators. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Executive summary
mic runnow captures what application code writes tosys.stdout/sys.stderrduring a run, so it no longer bleeds into the live progress stream or the stdout summary/--jsonmanifest:--output DIR: recorded toDIR/output.jsonl(newmic-output-v1format). Each record carries the evaluation, source, row, trial, and scorer that wrote it.--show-output: shown on stderr above the status line, each line prefixed with where it came from (row 2 · trial 1 · recall │ …). It can't be combined with-q.run()/arun()gaincapture_output: bool = False, so library callers keep their streams unless they opt in. Version bumped to 0.6.0.Why
The streaming TUI wrapped
sys.stdout/sys.stderronly so that prints wouldn't land on the status line. Application output still flooded the progress display, and prints to stdout could corruptmic run --json | jq. There was no way to keep the stream clean, and no way to keep the output for later debugging.Design and implementation
src/mic/_runtime/capture.py): aContextVar[Origin]is set around each trial in the engine worker and narrowed around each scorer inrun_case.asyncio.to_threadinherit the trial.CallbackPool.invokenow runs sync callbacks undercontextvars.copy_context().run_in_executordoesn't carry context the wayto_threaddoes; without this, all sync task output would be untagged.OutputCapturereplacessys.stdout/sys.stderrwith aTextIOBasestream.finally, including on cancellation.events.jsonlunchanged, and preserves output from trials that hang or time out (late writes keep their trial attribution).truncatedrecord.output.jsonlis closed beforerun.jsonis published.outputrun failure (exit 1) without losing results.echocallback from the live display.\r(progress bars), and replace control characters, so they can't move the cursor.ConfigurationError. Trials from a concurrent run that doesn't capture still write to the real streams, because their origin carries a differentrun_id.examples/chatty.pydemonstrates all three modes.Verification and confidence
Risks exercised:
to_thread; bare threads counted as run-level; a barrier that forces four trials to interleave partial lines.output.jsonlwrite gives exit 1 with results kept and no exception text; late output after a timeout is still attributed; echo errors never fail the run.isatty()is false,fileno()raisesUnsupportedOperation, bytes raiseTypeError, andclose()is a no-op.--jsonstdout and the expectedoutput.jsonl;--show-outputprefixes;-q --show-outputexits 2.mic.run()withoutcapture_outputstill writes to stdout.Results:
uv run python scripts/verify.py: tests, coverage gates, strict and authoring typing, lint, and format all pass.report_javascriptandreport_interactionscould not run locally because Node isn't installed. This PR doesn't touch the report; CI covers them.uv buildplusscripts/verify_packaging.py: pass, at 0.6.0.--show-outputshowed prefixed output above a correctly redrawn status line, with stdout holding only the summary.Compatibility and risks
mic runno longer appear unless you pass--show-output; with--show-output, both streams appear on stderr. The release notes should say this.capture_output=Falseonrun/arun). The internalprogress()now yields(observer, echo).mic-output-v1/output.jsonl.mic-run-v4andmic-event-v1are unchanged.output.jsonlholds application output word for word, with no redaction. It is documented in artifacts and reporting docs, and is never sent to sinks or included in HTML reports.fileno()is unsupported.