Repository navigation
fix: keep a trailing reasoning signature with the block it signs - #26
Merged
sena-labs merged 1 commit intoOct 6, 2026
Conversation
_accumulate_reasoning started a new record whenever an incoming unindexed fragment carried a signature. A provider that streams its signature in a final delta — the usual shape for signed reasoning — therefore produced two records: the accumulated text, and a separate signature-only entry. Replayed into the next tool round, the signature no longer accompanied the text it signs, and providers that verify signed reasoning can reject the continuation. The seal is now read off the accumulated slot instead of the incoming fragment: a slot that already has a signature is complete, so the next fragment starts a new record, while a trailing signature-only delta merges into the block it closes. Keying on the slot also leaves multi-fragment encrypted payloads concatenating, which sealing on `data` would have split. The indexed path was already correct; this only affects streams that omit `index`. Addresses the unresolved review finding on PR #25 (openrouter_pipe.py:492). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rw7cYoHbaXFqdGBTXiRfeh
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
sena-labs
merged commit Oct 6, 2026
e59c4f9
into
feat/openrouter-api-alignment-phase1
7 checks passed
sena-labs
added a commit
that referenced
this pull request
Oct 11, 2026
…s (Phase 1) (#25) * feat: align chat, media and routing flows with current OpenRouter APIs Phase 1 of docs/PLAN-OPENROUTER-ALIGNMENT.md: - merge provider/reasoning controls without losing explicit settings; enforce ZDR/deny-data policy against user preferences - replay reasoning_details and assistant content across tool rounds; keep server tools alongside OWUI functions; run sync callables off-loop - cumulative cost accounting across tool rounds and TTS chunks (USD only) - variant resolver: routing tags (:online/:nitro/:floor/:exacto) stripped for catalog lookup; never fabricate media or free variants - free filter now excludes paid media models whose token prices are 0/0 - auth validation on /key with 60s cache; public discovery with personal keys; icon registry via /api/frontend/v1/all-providers - TTS: single-flight, Seed Audio limits (3000 chars, speed 0.5-2.0), cumulative usage aggregation, credit prefetch on cache hits - video: reject before submit under ZDR/deny-data; terminal states failed/cancelled/expired surfaced with job id - tests: test_alignment.py (39 regressions), updated test_pipe.py, CI - docs: audit report, alignment plan, README/TESTING/CONTRIBUTING/CHANGELOG - e2e_live.py: budget-guarded paid live checks (chat/tool loop, TTS, video) * fix: use keyed per-process fingerprints for credential cache identity CodeQL flagged plain SHA-256 over the bearer token where it is used as a cache/scope key (py/hashing-sensitive-data). Those digests identify cache entries, they are never stored or used for authentication, but a keyed construction is strictly better: HMAC-SHA256 with a per-process random secret makes the fingerprints non-comparable across restarts. - add Pipe._credential_fingerprint (HMAC, os.urandom(32) per-process key) - use it for: models-list cache key, /key validation cache, credit cache (read+write), TTS single-flight scope and TTS cache key - update test_pipe.py helper that primes the credit cache directly * fix: address review findings and CodeQL on credential fingerprints - credential fingerprint: keyed blake2b instead of HMAC-SHA256 - skip /key validation on custom OPENROUTER_BASE_URL - merge unindexed streamed reasoning fragments, split distinct records - e2e_live: import time, read nested /credits data, pre-call budget checkpoint * fix: replace credential hashing with opaque per-process tokens (CodeQL) * fix: keep a trailing reasoning signature with the block it signs (#26) Read the seal off the accumulated slot rather than the incoming fragment, so a trailing signature-only delta merges into the reasoning block it closes instead of opening a separate record. Dropping `data` from the seal test keeps multi-fragment encrypted payloads concatenating. Closes the last open review thread on #25.
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.
Summary
Closes the one review finding on #25 that was still open on the current head. Targets
feat/openrouter-api-alignment-phase1rather thanmain, so it lands inside #25 instead of racing it.A streamed reasoning block whose signature arrives in a final, unindexed delta was split into two records — the accumulated text, and a separate signature-only entry. Replayed into the next tool round the signature no longer accompanied the text it signs, which is exactly what #25 set out to fix.
Related Issue
Follows up #25 (review thread on
openrouter_pipe.py:492). No separate issue.Type of Change
Changes Made
_accumulate_reasoningreads the seal off the accumulated slot, not the incoming fragment. The condition wasdetail.get("signature") is not None or detail.get("data") is not None— an incoming fragment carrying a signature always opened a new record. It is nowslot.get("signature") is not None: a slot that already holds a signature is complete, so the next fragment starts a new record, while a trailing signature-only delta merges into the block it closes.datafrom the seal test. The merge loop concatenatesdatastrings, so sealing ondatawould have split a multi-fragment encrypted payload into one record per fragment — the same defect in a different coat. Keying on the slot's signature leaves those concatenating.test_alignment.py: a trailing signature-only delta stays with its text; a block following a signed one still starts a new record (the separation the old condition was reaching for is preserved); unindexed encrypted fragments concatenate.The indexed path was already correct —
indexkeys the slot, so the signature merged there. Only streams that omitindexwere affected.Before / after
Testing
python test_pipe.py) — 1058 asserts, 0 failedpython -m unittest -v test_maintenance test_alignment) — 60 tests OK (3 new)CHANGELOG.md— no new entry:[Unreleased]already carries "Replay signed/encrypted reasoning and assistant content through native tool rounds" from feat: align chat, media and routing flows with current OpenRouter APIs (Phase 1) #25, and this makes that line true in the unindexed case rather than adding a separate change. PerCONTRIBUTING.md,[Unreleased]holds pending entries only.Both commands above are exactly what the
Unit testsCI job runs.Screenshots / Demo
N/A — behavioral fix; the before/after above is the reproduction.
Checklist
printstatements beyond[OpenRouter Pipe]logging🤖 Generated with Claude Code
https://claude.ai/code/session_01Rw7cYoHbaXFqdGBTXiRfeh
Generated by Claude Code