Skip to content

fix: OpenAI compliance audit — .stream() events, streamed usage, request-id, parse/raw-response#20

Merged
Abhinavexist merged 1 commit into
mainfrom
fix/stream-event-contract
Jul 22, 2026
Merged

fix: OpenAI compliance audit — .stream() events, streamed usage, request-id, parse/raw-response#20
Abhinavexist merged 1 commit into
mainfrom
fix/stream-event-contract

Conversation

@Abhinavexist

Copy link
Copy Markdown
Collaborator

Supersedes #19 (closed — its Issue-1 handling was documentation-only, which didn't resolve the contract collision). This does the proper fix and re-carries #19's other fixes.

Issue #1 (the proper fix): .stream() now yields OpenAI events

Our .stream() shadowed OpenAI's method name but yielded raw ChatCompletionChunks instead of event objects — so a migrator's for event in stream: if event.type == "content.delta" ran, then crashed. Now .stream() yields the real OpenAI ChatCompletionStreamEvents (content.delta, content.done, chunk, tool_calls.function.arguments.delta/done, …) via openai's ChatCompletionStreamState, with Interfaze's inline <think>/<precontext> side-channels filtered out of the content events (buffered across chunk boundaries, flushed before content.done). get_final_completion() still returns the extended InterfazeChatCompletion (reasoning/precontext/vcache/usage).

Live-verified against the API with reasoning_effort:"high":

event types: {'chunk': 9, 'content.delta': 8, 'content.done': 1}
tag leak in content events: False
content: "The sky is blue because of a phenomenon called **Rayleigh scattering**…"
final.reasoning present: True (314 chars) · final content has <think>: False
  • Added text_deltas() for plain-token consumers; create(stream=True) unchanged (raw chunks, OpenAI Way A).
  • BREAKING: iterating .stream() now yields events, not chunks (it was never OpenAI-compatible before). Deliberate — that's the whole point of the audit finding.

Also carried from #19 (unchanged, still needed)

Note

Depends on openai.lib.streaming.chat.ChatCompletionStreamState (openai's helper-lib surface, like our existing _thenUnwrap use). The event tests act as the guard — they'd fail if a future openai changed its accumulation. Worth a tighter openai pin if this contract must be durable.

Verify: ruff + mypy --strict clean; 36 tests pass (event-contract, no-leak-in-events, tool-call events, text_deltas, async parity, double-consume, streamed usage).

@Abhinavexist
Abhinavexist force-pushed the fix/stream-event-contract branch from 5659482 to 058c262 Compare July 21, 2026 00:02
@Abhinavexist Abhinavexist changed the title fix: OpenAI compliance audit — .stream() event contract + streamed usage + request-id fix: OpenAI compliance audit — .stream() events, streamed usage, request-id, parse/raw-response (#1/#2/#3/#6) Jul 21, 2026
@Abhinavexist

Copy link
Copy Markdown
Collaborator Author

Rebased onto current main (post #7#11). Also folded in audit issue #6: parse() / with_raw_response / with_streaming_response are now exposed on the wrapper (delegated to the OpenAI client) — no more AttributeError.

⚠️ Merge-order note: this PR and #16 both restructure src/interfaze/_stream.py (this one → event-based .stream(); #16 → fence-strip on the aggregated path). They're each clean against main independently, but whichever merges second will need a quick re-rebase to fold the other's _stream.py change in.

@Abhinavexist

Copy link
Copy Markdown
Collaborator Author

Also — beyond the #16 textual overlap: this PR's event-based .stream() semantically conflicts with #18, which hardens the chunk-based .stream() contract. Merging both breaks #18's stream tests. Live-verified: parse() (#6) returns clean unfenced json_schema output against the real API — no fence-strip needed (unlike json_object).

@Abhinavexist Abhinavexist changed the title fix: OpenAI compliance audit — .stream() events, streamed usage, request-id, parse/raw-response (#1/#2/#3/#6) fix: OpenAI compliance audit — .stream() events, streamed usage, request-id, parse/raw-response Jul 21, 2026
@Abhinavexist

Copy link
Copy Markdown
Collaborator Author

Confirming the breaking change is intentional: iterating .stream() now yields typed OpenAI ChatCompletionStreamEvents (content.delta/content.done, tool-call events) instead of raw chunks. Rationale: audit issue #1.stream() must be OpenAI-compatible so for event in stream is drop-in. create(stream=True) is unchanged and still yields raw ChatCompletionChunks for chunk consumers. Live-verified against the API.

…est-id, escape hatches

- #1 .stream() now yields OpenAI ChatCompletionStreamEvents (content.delta/done,
  tool-call events) via ChatCompletionStreamState; <think>/<precontext> stripped from
  content events; text_deltas() for plain tokens; create(stream=True) unchanged.
- #2 streamed usage + system_fingerprint captured on get_final_completion().
- #3 _request_id carried onto the returned completion.
- #6 parse()/with_raw_response/with_streaming_response exposed on the wrapper
  (no more AttributeError; parse delegates to the OpenAI client, model default + guard).

Rebased onto main (post #7#11).
@Abhinavexist
Abhinavexist force-pushed the fix/stream-event-contract branch from 058c262 to 2b127bd Compare July 22, 2026 19:56
@Abhinavexist
Abhinavexist merged commit d37dfe1 into main Jul 22, 2026
6 checks passed
@Abhinavexist
Abhinavexist deleted the fix/stream-event-contract branch July 22, 2026 19:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant