Skip to content

feat(server): add OpenAI Chat stream projections and hooks - #2435

Merged
Pouyanpi merged 7 commits into
developfrom
pouyanpi/openai-chat-streaming-contract-2
Oct 13, 2026
Merged

Pouyanpi merged 7 commits into
developfrom
pouyanpi/openai-chat-streaming-contract-2

Conversation

@Pouyanpi

@Pouyanpi Pouyanpi commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Declares OpenAI Chat stream policy in typed Python models, derives classification metadata, and supplies handwritten completion hooks and native error framing.

What Changed

  • Adds typed payload and provider-error models, with explicit guarded-delta, metadata, and terminal-event classification rules.
  • Derives reviewed-field coverage and guarded text paths through reusable stream-policy helpers; replacement blockers on stream subjects are validated like buffered ones.
  • Keeps unknown choice and delta fields closed, accepts null or empty tool_calls, and requires the choice index to be exactly the integer 0.
  • Field helpers now accept ge, le, and strict alongside length and pattern, so exact integers need no custom validator. Export checks separately whether the contract can carry a constraint.
  • Gives data-less keepalives their own keepalive shape and drops unused classifier aliases.
  • Requires [DONE] for successful completion and rejects duplicate termination or later payloads. Data-less keepalives and provider-error endings remain supported.
  • Tests the Python declarations, derived classification metadata, event inventory, transport policy, and handwritten hooks without loading an authored YAML contract.

Review Notes

Review the typed policy, derived coverage and text paths, event inventory, strict JSON, and handwritten completion/error hooks. Endpoint/HTTP integration lands in the final PR.

With output inspection enabled, missing completion produces a native stream error and withholds pending text. Previously approved bytes already sent cannot be retracted. Provider errors can end the stream without [DONE]; the proxy preserves that failure without adding a missing-completion error.

AI Assistance

  • No AI tools were used.
  • AI tools were used (Codex and Claude Code assisted with implementation, regression tests, review probes, and PR drafting). Human review of the new diff is pending.

Checklist

  • I've read the CONTRIBUTING guidelines.
  • This is maintainer-directed work.
  • The PR title follows the project commit convention.
  • Applicable declarations and tests are included.
  • Generated changelog files were not edited manually.
  • Automated and human review comments are addressed or answered.
  • The responsible reviewer or team is mentioned.

Stack Position

Part 2 of 4.

Stack Context

Adds OpenAI Chat streaming above the merged buffered implementation on develop in four parts: provider-neutral execution, typed OpenAI stream declarations and handwritten hooks, injected HTTP lifecycle handling, and integration with the buffered request pipeline.

The open chain is develop → #2434 → #2435 → #2436 → #2437. The buffered foundation is merged; #2412 is closed.

Python declarations own field and event policy. Hooks are handwritten and created for each stream by the integration. The final PR exports the bound buffered and streaming policy for inspection; export never constructs runtime components. Concrete outbound HTTP, deployment configuration, and replacement execution land separately.

Review each PR against its listed base branch.

Order PR Branch Base
1 #2434 pouyanpi/transparent-proxy-streaming-kernel-1 develop
2 #2435 pouyanpi/openai-chat-streaming-contract-2 pouyanpi/transparent-proxy-streaming-kernel-1
3 #2436 pouyanpi/transparent-proxy-streaming-http-3 pouyanpi/openai-chat-streaming-contract-2
4 #2437 pouyanpi/openai-chat-streaming-integration-4 pouyanpi/transparent-proxy-streaming-http-3

Summary by CodeRabbit

  • New Features
    • Added streaming support for OpenAI Chat Completions, including guarded text chunks, provider error events, and stream termination handling.
    • Added validation for stream payload shapes and terminal events, with errors formatted as OpenAI-style events.
  • Documentation
    • Updated the streaming policy documentation to describe supported declarations and validation limits.

@github-actions github-actions Bot added size: L status: needs triage New issues that have not yet been reviewed or categorized. labels Oct 5, 2026
@Pouyanpi
Pouyanpi force-pushed the pouyanpi/openai-chat-streaming-contract-2 branch from 73f46d5 to 625e4cb Compare October 6, 2026 11:05
@Pouyanpi Pouyanpi added this to the v0.25.0 milestone Oct 6, 2026
@Pouyanpi Pouyanpi self-assigned this Oct 6, 2026
@Pouyanpi Pouyanpi removed the status: needs triage New issues that have not yet been reviewed or categorized. label Oct 6, 2026
@Pouyanpi
Pouyanpi marked this pull request as ready for review October 6, 2026 11:06
@Pouyanpi Pouyanpi added status: triaged Triaged by a maintainer; eligible for automated review (CodeRabbit/Greptile). and removed status: triaged Triaged by a maintainer; eligible for automated review (CodeRabbit/Greptile). labels Oct 6, 2026
@Pouyanpi
Pouyanpi force-pushed the pouyanpi/openai-chat-streaming-contract-2 branch from 625e4cb to 80703c1 Compare October 6, 2026 15:22
@Pouyanpi Pouyanpi added status: triaged Triaged by a maintainer; eligible for automated review (CodeRabbit/Greptile). status: stale and removed status: triaged Triaged by a maintainer; eligible for automated review (CodeRabbit/Greptile). status: stale labels Oct 6, 2026
@greptile-apps

greptile-apps Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium impact] The PR appears safe to merge in the reviewed stack, with numeric contract export support completed by PR #2437.

Summary

Adds typed OpenAI Chat stream models, derived field and event coverage, and handwritten completion hooks.

  • OpenAI Chat stream events have clear rules for text, metadata, and errors.
  • Typed stream bindings derive text paths and event coverage from their declarations.
  • Chat streams distinguish successful endings from provider errors.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Provider SSE event] --> B{Data present?}
  B -- No --> C[Keepalive]
  B -- Yes --> D{DONE marker?}
  D -- Yes --> E[Mark completion]
  D -- No --> F[Select and validate event model]
  F --> G{Event kind}
  G -- Text --> H[Extract original guarded text]
  G -- Metadata --> I[Keep reviewed metadata]
  G -- Error --> J[Mark provider failure]
  H --> K[Run output checks before release]
  C --> L[Check stream history]
  E --> L
  I --> L
  J --> L
  L --> M[Require DONE or provider failure at stream end]
Loading

Reviews (9) · Last reviewed commit: "test(server): cover typed stream policy ..." · Reviewed by Greptile

@codecov

codecov Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Pouyanpi
Pouyanpi added this pull request to stack #2445 October 9, 2026 13:16
@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: NVIDIA-NeMo/Guardrails/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Enterprise
  • Run ID: 3f8ad0e2-4192-4f5c-afb9-014efdad019f

📥 Commits

Reviewing files that changed from the base of the PR and between cb9fb48 and 969832f.


📒 Files selected for processing (8)
  • nemoguardrails/server/experimental/provider/stream_classifier.py
  • nemoguardrails/server/experimental/provider/stream_policy.py
  • nemoguardrails/server/experimental/providers/openai/chat_completions/README.md
  • nemoguardrails/server/experimental/providers/openai/chat_completions/stream_classifier.py
  • nemoguardrails/server/experimental/providers/openai/chat_completions/stream_hooks.py
  • nemoguardrails/server/experimental/providers/openai/chat_completions/stream_projection.py
  • tests/server/experimental/test_openai_chat_streaming_projections.py
  • tests/server/experimental/test_stream_policy.py

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 10 remain after this review.



📝 Walkthrough

Walkthrough

The changes add shared stream-policy helpers and an OpenAI Chat Completions streaming policy. The new policy defines payload and error projections, classifies stream events, and tracks terminal events and provider errors. Tests cover policy derivation, classification, and stream handling.

Changes

Chat Completions streaming

Layer / File(s) Summary
Shared stream policy
nemoguardrails/server/experimental/provider/stream_policy.py, tests/server/experimental/test_stream_policy.py
Adds guarded text-path derivation and event-shape coverage. Tests check supported and rejected schemas, role conflicts, and classifier contract consistency.
OpenAI stream projection and classification
nemoguardrails/server/experimental/providers/openai/chat_completions/stream_projection.py, nemoguardrails/server/experimental/providers/openai/chat_completions/stream_classifier.py, nemoguardrails/server/experimental/provider/stream_classifier.py, nemoguardrails/server/experimental/providers/openai/chat_completions/README.md, tests/server/experimental/test_openai_chat_streaming_projections.py
Adds OpenAI payload and error projection models, stream event rules, derived coverage, and a classifier. The README documents the declarations and their limits. Tests check schemas, event classification, and declaration metadata. The shared classifier module’s documentation now describes declarative rules.
Stream lifecycle handling
nemoguardrails/server/experimental/providers/openai/chat_completions/stream_hooks.py, tests/server/experimental/test_openai_chat_streaming_projections.py
Adds hooks that track [DONE] and provider errors, reject disallowed events after termination, validate stream completion, and encode provider errors. Tests cover hook state and guarded-stream handling.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature


Merge Risk: ⚪ Minimal · up to 96983

No identified streaming-policy defect remains to resolve before merging this PR.

Pre-merge checks | Passed 5 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 18.37% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 49 functions across 7 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Test Results For Major Changes Passed The PR contains major changes, including new stream policy, projection, classifier, and hook code. The description includes specific testing information: it lists coverage for declarations, derived me…
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main changes: OpenAI Chat stream projections and hooks.

Full details: Docstring Coverage

Explanation

Docstring coverage is 18.37% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 49 functions across 7 files. (1 skipped: 1 unsupported.)


  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@Pouyanpi
Pouyanpi force-pushed the pouyanpi/openai-chat-streaming-contract-2 branch 3 times, most recently from 2c347bb to 021670c Compare October 12, 2026 13:21

@tgasser-nv tgasser-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Can you add some more test coverage for the missing lines before merging?

@Pouyanpi
Pouyanpi force-pushed the pouyanpi/openai-chat-streaming-contract-2 branch from 021670c to 3a89b42 Compare October 13, 2026 09:33
Base automatically changed from pouyanpi/transparent-proxy-streaming-kernel-1 to develop October 13, 2026 10:02
Add provider-neutral helpers that derive stream event metadata from the
typed projection declarations instead of a second authored policy.

stream_text_path follows guarded fields directly, without schema
generation, so unrelated field types cannot affect the binding. It
follows guarded models, nullable model containers, and lists bounded to
at most one item. Unlike buffered extraction, empty arrays, null
containers, and null text are valid and become missing text for the
event rule to classify. Union containers, unbounded arrays, non-string
subjects, recursion, and ambiguous subjects are rejected.

stream_shape_coverage collects event shapes by role from the declared
rules, missing-text fallbacks, sentinels, and non-data shapes.

Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Declare the Chat single_text_delta.v1 stream boundary with typed
projections and ObjectPolicy. The root, choice, and delta reject
unreviewed members; reviewed root metadata passes through unchanged. A
chunk carries zero or one choice whose index is exactly the integer 0
and whose logprobs are null, as in buffered responses. Missing or null
content is metadata, empty string content is guarded text, and provider
error messages are constrained, not guarded output. The error envelope
is closed, and its error object is configurable but closed by default.

Bind the projections to explicit stream event rules for content,
provider errors, and the [DONE] sentinel, with field and shape coverage
derived from the declarations. The OpenAI pin comes from
providers/openai/source.py.

Keep lifecycle checks in handwritten hooks: require an actual [DONE]
for successful completion, distinguish it from keepalives, reject
payloads after termination or a provider error, and frame native
errors.

Tests cover classification, unchecked sibling content, completion and
termination, raw preservation, isolated hook state, rejected log
probabilities and unreviewed members, and member-policy agreement
between the export and the projections. JSON Schema accepts index 0.0
where the runtime does not, a known value-level gap.

Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
The pinned OpenAI schema types ChatCompletionStreamResponseDelta.tool_calls
as an array with no minimum length, so an empty list is a valid value
that carries no tool content. The stream delta declared it null-only and
rejected such chunks, unlike buffered responses.

Constrain delta tool_calls to null or an empty list, matching the
buffered message. Any tool call chunk is still rejected.

Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Stream subjects now share the buffered check that a replacement blocker
names a declared or reviewed opaque sibling. Streaming replacement stays
unsupported, but a misspelled blocker is still a policy error.

Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
…bulary

Field helpers rejected any Pydantic constraint the guard contract could not
express, which pushed runtime declarations toward custom validators: the
stream choice index used a BeforeValidator to require the integer zero.
Helpers now accept the constraints declarations use (length, pattern, ge, le
and strict), so the index is declared as a strict integer bounded to zero.
Whether the export format can carry a constraint is checked at export time.
Strictness has no JSON Schema keyword and is enforced at runtime only.

The Chat stream classifier also drops unused aliases kept for an older
compiler, derives field coverage directly, and names data-less keepalives
"keepalive" instead of reusing the "[DONE]" sentinel's name. Runtime
behavior is unchanged.

Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Adds cases for annotated array items, containers that do not lead to a
model, untyped event models, text events without one assistant subject,
and non-canonical or repeated sentinels. No runtime behavior changes.

Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
@Pouyanpi
Pouyanpi force-pushed the pouyanpi/openai-chat-streaming-contract-2 branch from 3a89b42 to 21cc830 Compare October 13, 2026 10:02
@Pouyanpi
Pouyanpi merged commit 62ba813 into develop Oct 13, 2026
17 checks passed
@Pouyanpi
Pouyanpi deleted the pouyanpi/openai-chat-streaming-contract-2 branch October 13, 2026 10:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: L status: triaged Triaged by a maintainer; eligible for automated review (CodeRabbit/Greptile).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants