Repository navigation
fix: read and write skills, benchmark data and run files as UTF-8 - #349
Open
Tanishq (tanishqbaweja) wants to merge 1 commit into
Open
Tanishq (tanishqbaweja) wants to merge 1 commit into
Tanishq (tanishqbaweja) wants to merge 1 commit into
Conversation
The train and eval pipeline left the codec of 95 open() calls to the locale, which on Windows is the ANSI code page (cp1252, GBK, ...): - eval_only.py and train.py crashed reading five of the six released ckpt skills, which hold U+201D (byte 0x9D is undefined in cp1252). - SearchQA scored a correct answer 0 when the context, skill or reply held a character outside the code page: the prompt and trajectory writes before scoring failed inside a broad except. A reply with such a character then crashed the run writing results.jsonl. - Reflection crashed reading UTF-8 trajectories, and an optimizer edit with an arrow crashed the step when its patch was saved. - The SpreadsheetBench seed skill reached every prompt as mojibake, and raw LiveMathematicianBench files failed to load (microsoft#117). Pass encoding="utf-8" throughout. Files an earlier release wrote with the locale codec (skills, history, runtime state, patches, rollout records, trajectories, prompts and markers) are read through skillopt/utils/text_io.py, which falls back to that codec as skillopt_sleep/state.py does, so those runs still resume. results.jsonl is decoded line by line because a resumed rollout appends UTF-8 records to it.
Author
|
@microsoft-github-policy-service agree |
lucatreiber2007
added a commit
to lucatreiber2007/SkillOpt
that referenced
this pull request
Nov 1, 2026
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
Follow-up to #117. Item 2 of the reporter's follow-up comment there (LiveMathematicianBench
_load_jsonfails on Windows, and otheropen(...)reads and writes without an explicitencoding=, such as theconversation.jsonwriters, are likely affected) was not part of #135, and it still reproduces onmain.Problem
The train/eval pipeline opens skills, benchmark data and run files without an
encoding, so they use the locale codec: the ANSI code page on Windows (cp1252, GBK, ...). The DocVQA and OfficeQA rollouts,skillopt/datasets/base.py, the config loader (#124), the prompt loader and the Codex and Claude backends already pin UTF-8; the modules changed here do not. Reproduced on Windows 11, Python 3.12, cp1252, with a local fake OpenAI-compatible server standing in for the models:ckpt/README.mddocumentspython scripts/eval_only.py --skill ckpt/<bench>/gpt5.5_skill.md. Five of the six skills (all but DocVQA) contain U+201D, whose UTF-8 bytes end in 0x9D, a byte cp1252 does not define:scripts/train.py --skill_init ckpt/searchqa/gpt5.5_skill.mdstops the same way while reading the initial skill.process_onewrites the prompts and the trajectory before scoring, inside a broadexcept. WithΩin a retrieved passage,<answer>Paris</answer>getshard=0and afail_reasonoferror: 'charmap' codec can't encode character ....agent_okis already true, so the systemic-failure check never fires. When the reply itself holds such a character (The passage names the city → <answer>Paris</answer>), the baseline logshard=0and the run then crashes writingresults.jsonl.→crashes the training step withUnicodeEncodeErrorwhenreflect.pysaves the analyst's patch. The trainer writes the same text to the merged patch, the ranked edits, the edit-apply report and the candidate skill afterwards.conversation.jsonas UTF-8. A closing curly quote (U+201D) in one makesfmt_minibatch_trajectoriesraiseUnicodeDecodeError, and that read sits before the analyst'stry, so the reflect stage fails.skills/initial.mdis read as cp1252, so its—and→arrive as—and→._load_json, 0.2.0 wheel: missing prompt .md data files + Windows temp-dir cleanup crash (claude backend) #117): a question containing∏raisesUnicodeDecodeError(byte 0x8f).Change
open()calls (63 writes, 32 reads) inscripts/eval_only.py,skillopt/engine/trainer.py,skillopt/gradient/reflect.py,skillopt/optimizer/slow_update.pyand the SearchQA, LiveMathematicianBench, SpreadsheetBench and ALFWorld adapter, rollout and loader modules now passencoding="utf-8", as the DocVQA and OfficeQA rollouts already do.skillopt/utils/text_io.py: skills (includingskill_initand--skill, which may point at a previous run'sbest_skill.md),history.json,runtime_state.json, minibatch patches,results.jsonl, trajectories, target prompts, and the slow-update and meta-skill markers. It tries UTF-8 and falls back to the locale codec, the same migrationskillopt_sleep/state.pydoes since fix(sleep): read and write state.json as UTF-8 #302, so a run started before upgrading still resumes. Reading such a file as UTF-8 only fails, loudly for most of these reads and silently for some:_load_runtime_stateswallows the error, so the resume falls back tohistory.json, or starts over when no step has finished yet.results.jsonlis decoded line by line, because a resumed rollout appends UTF-8 records to a file the old release started; a whole-file fallback fails on such a file. A line valid in neither codec, such as a record cut off mid-write, is decoded with replacement characters instead of failing the whole read; a cut-off record then fails to parse like any other malformed line, and its item runs again.The benchmarks change together on purpose: fixing only the trainer's skill read would make SpreadsheetBench worse on Windows. Given the seed skill with a real
→, the codegen rollout's own prompt write raises inside its broadexcept(unexpected: UnicodeEncodeError ...) and the item scores 0 before the model is called.Not changed: the ALFWorld rollout's
conversation.jsonwrite, which #348 changes on the same line (reflection reads either encoding), and the config read in the vendoredalfworld_envs.py.Tests
New
tests/test_run_file_encoding.py, followingtests/test_sleep_state.py. A UTF-8 runner cannot show the defect by behaviour alone, so the pipeline tests run in a subprocess under-X warn_default_encodingwithEncodingWarningraised as an error for SkillOpt's own modules (a dependency's warning cannot fail them):run_batchwithΩin the context and→in the skill and the reply:hard == 1, the prompts, trajectory andresults.jsonlare UTF-8, and a second call resumes without a model call.eval_only.main()withckpt/searchqa/gpt5.5_skill.md(target stubbed):hard == 1.0, and the skill reaches the target prompt intact.skill_initand an optimizer edit containing→:skill_v0000.mdequals the released skill, and the saved patch and the candidate skill hold the edit.fmt_minibatch_trajectoriesover UTF-8 files with curly quotes,→andΩ.load_items, SearchQAload_raw_itemsand SpreadsheetBenchload_itemson a question containing∏.process_one_codegenwith the seed skill reaches the LLM phase (empty-code-block), and its system prompt keeps—and→.Two in-process tests force the locale codec to cp1252, so the fallback runs on any OS: cp1252
runtime_state.json,history.jsonand a skill load intact, andrun_batchreturns both records of aresults.jsonlholding one cp1252 line and one UTF-8 line without a model call; and a record cut off mid-character is rolled out again instead of failing the resume.Results:
main(source reverted, tests kept): 8 failed. The six subprocess tests stop withEncodingWarning: 'encoding' argument not specifiedat theopen()under test (for SpreadsheetBench as itsfail_reason); the two resume tests cannot import the helper._load_runtime_statereturnsNone. With a whole-file fallback instead of per-line decoding, it fails on the mixedresults.jsonl. With strict per-line decoding, the cut-off record test fails withUnicodeDecodeError.python -m pytest -qon Windows: 80 failed and 1835 passed onmain; 80 failed and 1843 passed on this branch. The outcome of every existing test is the same, and the 8 new tests pass. The 80 failures come from this machine's setup (Windows, a CRLF checkout on an exFAT drive) and fail identically onmain.ruff checkon the changed files reports nothing new apart from one moreE402ineval_only.py, where every import followsforce_utf8_stdout_stderr()by design.Manual check (Windows 11, Python 3.12, cp1252, local fake OpenAI-compatible server, SearchQA with 2/2/2 synthetic items):
maineval_only.py --skill ckpt/searchqa/gpt5.5_skill.mdUnicodeDecodeError, no model callhard=1.0train.py --skill_init ckpt/searchqa/gpt5.5_skill.mdUnicodeDecodeError, no model calltrain.py, target repliesThe passage names the city → <answer>Paris</answer>hard=0, thenUnicodeEncodeErrorwritingresults.jsonlhard=1.0train.py, optimizer edit contains→UnicodeEncodeErrorsaving the patchA one-epoch run started on
mainwithcaféin its output path leaves a cp1252runtime_state.json; this branch resumes it with--num_epochs 2from step 2.