Repository navigation
feat(server): integrate OpenAI Chat buffered proxy #2414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Pouyanpi
merged 27 commits into
develop
from
pouyanpi/openai-chat-buffered-integration-4
Oct 9, 2026
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
5d6f6b7
feat(server): integrate OpenAI Chat buffered proxy
Pouyanpi b577e1d
fix(server): construct buffered operation on Python 3.10
Pouyanpi 4ea116d
fix(server): preserve provider binding failures
Pouyanpi 1863bbd
fix(server): render native OpenAI proxy route rejections
Pouyanpi 54d0936
feat(server): export the handwritten buffered Chat contract
Pouyanpi fc11e1f
docs(server): document projection policy export boundaries
Pouyanpi 19a2b9c
docs(server): align buffered export with contract cleanup
Pouyanpi 852674e
refactor(server): share buffered guard contract export
Pouyanpi 8449706
fix(server): align buffered Chat integration with closed projections
Pouyanpi 57586c1
fix(server): relay buffered Chat responses that have no text
Pouyanpi 960dab5
fix(server): request identity-encoded responses for guarded Chat
Pouyanpi 9ff8685
docs(server): describe buffered Chat relay and encoding behavior
Pouyanpi e1da40f
refactor(server): derive contract identity from provider endpoints
Pouyanpi ec4133b
refactor(server): isolate guarded route declarations from HTTP execution
Pouyanpi 9e3ca2b
feat(server): export explicit object member policies
Pouyanpi a81c720
fix(server): request identity encoding only when output is inspected
Pouyanpi eee516f
test(llm): isolate LLM call info in exception context tests
Pouyanpi 9a034f5
fix(server): publish policy-aware request schemas
Pouyanpi 3c6f2b8
fix(server): reject duplicate API revision selectors
Pouyanpi 64366b3
docs(server): remove legacy guard contract wording
Pouyanpi 1a5aafb
fix(server): enforce object policy configuration invariants
Pouyanpi 32bd22c
fix(server): bind schema policies to model identity
Pouyanpi 0c0b271
refactor(server): derive text bindings from typed fields
Pouyanpi fdeb80b
fix(server): validate exported policy against runtime bindings
Pouyanpi 097220a
fix(server): honor endpoint rejection codes in buffered HTTP
Pouyanpi 5e30672
refactor(server): simplify buffered request forwarding
Pouyanpi 4722147
test(server): cover buffered integration error paths
Pouyanpi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """Compose provider declarations with the guarded buffered HTTP pipeline.""" | ||
|
|
||
| from dataclasses import dataclass | ||
|
|
||
| from pydantic import ValidationError | ||
|
|
||
| from nemoguardrails.server.experimental._guarded_operation import ( | ||
| BufferedGuardedOperation, | ||
| ContentInspectionNotApplicable, | ||
| InvalidGuardedPayload, | ||
| UnsupportedGuardedPayload, | ||
| UnsupportedGuardedRepresentation, | ||
| ) | ||
| from nemoguardrails.server.experimental._http_kernel import ( | ||
| BufferedHttpRequest, | ||
| BufferedHttpResponse, | ||
| GuardedHttpOperation, | ||
| GuardedOperationPath, | ||
| ) | ||
| from nemoguardrails.server.experimental._json_payload import InvalidJson, UnsupportedJsonShape, parse_json_object | ||
| from nemoguardrails.server.experimental.provider.endpoint import GuardedJsonEndpoint | ||
| from nemoguardrails.server.experimental.provider.errors import ProviderErrorMapping | ||
| from nemoguardrails.server.experimental.provider.payload import GuardedMessageTarget, guarded_schema_error | ||
| from nemoguardrails.server.experimental.provider.projection_policy import PolicyModel, policy_json_schema | ||
| from nemoguardrails.server.experimental.provider.transport import ProviderBindingViolation | ||
| from nemoguardrails.server.experimental.provider.types import GuardedMessage | ||
|
|
||
|
|
||
| @dataclass(slots=True) | ||
| class GuardableProviderRequest: | ||
| """Hold one original HTTP request and its validated exact guarded target.""" | ||
|
|
||
| request: BufferedHttpRequest | ||
| target: GuardedMessageTarget | ||
|
|
||
|
|
||
| def _header(headers: tuple[tuple[bytes, bytes], ...], name: bytes) -> str | None: | ||
| """Return the last value of a case-insensitive HTTP header.""" | ||
| values = [value.decode("latin-1") for key, value in headers if key.lower() == name] | ||
| return values[-1] if values else None | ||
|
|
||
|
|
||
| def _media_type(value: str | None) -> str | None: | ||
| """Return a normalized media type without parameters.""" | ||
| return value.split(";", 1)[0].strip().lower() if value is not None else None | ||
|
|
||
|
|
||
| def _require_json_representation(request: BufferedHttpRequest) -> None: | ||
| """Require an unencoded JSON representation for guarded request parsing.""" | ||
| if _media_type(_header(request.headers, b"content-type")) != "application/json": | ||
| raise UnsupportedGuardedRepresentation( | ||
| "Content-Type must be application/json.", | ||
| "unsupported_media_type", | ||
| ) | ||
| content_encoding = _header(request.headers, b"content-encoding") | ||
| if content_encoding is not None and content_encoding.strip().lower() not in {"", "identity"}: | ||
| raise UnsupportedGuardedRepresentation( | ||
| "Encoded request bodies are not supported by the guarded path.", | ||
| "unsupported_content_encoding", | ||
| ) | ||
|
|
||
|
|
||
| def _prepare_guardable_request( | ||
| request: BufferedHttpRequest, | ||
| endpoint: GuardedJsonEndpoint, | ||
| ) -> GuardableProviderRequest: | ||
| """Validate the request and locate its exact guarded text target.""" | ||
| if endpoint.api_revision is not None: | ||
| try: | ||
| endpoint.api_revision.validate(request) | ||
| except ProviderBindingViolation as error: | ||
| raise UnsupportedGuardedPayload(str(error), error.code) from error | ||
| _require_json_representation(request) | ||
| try: | ||
| payload = parse_json_object(request.body) | ||
| except InvalidJson as error: | ||
| raise InvalidGuardedPayload("The request body must be valid JSON.") from error | ||
| except UnsupportedJsonShape as error: | ||
| raise UnsupportedGuardedPayload(str(error), endpoint.unsupported_request_code) from error | ||
| try: | ||
| projection = endpoint.guarded_request_model.validate_payload(payload) | ||
| target = projection.locate_guarded_message(payload) | ||
| except ValidationError as error: | ||
| raise UnsupportedGuardedPayload( | ||
| guarded_schema_error(error, "request"), endpoint.unsupported_request_code | ||
| ) from error | ||
| if projection.streams_response: | ||
| raise UnsupportedGuardedPayload( | ||
| "The guarded endpoint does not support streaming responses yet.", endpoint.unsupported_request_code | ||
| ) | ||
| return GuardableProviderRequest( | ||
| request=request, | ||
| target=target, | ||
| ) | ||
|
|
||
|
|
||
| def create_buffered_guarded_http_operation( | ||
| endpoint: GuardedJsonEndpoint, | ||
| errors: ProviderErrorMapping, | ||
| ) -> GuardedHttpOperation: | ||
| """Create one buffered HTTP operation from the provider endpoint contract.""" | ||
|
|
||
| def prepare_request(request: BufferedHttpRequest) -> GuardableProviderRequest: | ||
| """Prepare one buffered HTTP request for guarded execution.""" | ||
| return _prepare_guardable_request(request, endpoint) | ||
|
|
||
| def project_request(payload: GuardableProviderRequest) -> GuardedMessage: | ||
| """Select guarded input text from a prepared provider request.""" | ||
| return payload.target.message | ||
|
|
||
| def forward_request(request: GuardableProviderRequest) -> BufferedHttpRequest: | ||
| """Forward the original HTTP request without reserializing its body.""" | ||
| return request.request | ||
|
|
||
| def project_response( | ||
| payload: BufferedHttpResponse, | ||
| ) -> GuardedMessage | ContentInspectionNotApplicable: | ||
| """Select guarded output text from a successful JSON response.""" | ||
| if not 200 <= payload.status_code < 300: | ||
| return ContentInspectionNotApplicable() | ||
| if _media_type(_header(payload.headers, b"content-type")) != "application/json": | ||
| raise UnsupportedGuardedPayload( | ||
| "The successful provider response must use application/json.", endpoint.unsupported_response_code | ||
| ) | ||
| content_encoding = _header(payload.headers, b"content-encoding") | ||
| if content_encoding is not None and content_encoding.strip().lower() not in {"", "identity"}: | ||
| raise UnsupportedGuardedPayload( | ||
| "Encoded successful provider responses are not supported.", endpoint.unsupported_response_code | ||
| ) | ||
| try: | ||
| document = parse_json_object(payload.body) | ||
| projection = endpoint.guarded_response_model.validate_payload(document) | ||
| target = projection.locate_guarded_message(document) | ||
| # Validation has already rejected content in any other reviewed | ||
| # field, so a response without text has nothing to inspect. | ||
| if not target.has_text: | ||
| return ContentInspectionNotApplicable() | ||
| return target.message | ||
| except ValidationError as error: | ||
| raise UnsupportedGuardedPayload( | ||
| guarded_schema_error(error, "successful response"), endpoint.unsupported_response_code | ||
| ) from error | ||
| except (InvalidJson, UnsupportedJsonShape) as error: | ||
| raise UnsupportedGuardedPayload(str(error), endpoint.unsupported_response_code) from error | ||
|
|
||
| request_model = endpoint.guarded_request_model | ||
| request_schema = ( | ||
| policy_json_schema(request_model) | ||
| if issubclass(request_model, PolicyModel) | ||
| else request_model.model_json_schema() | ||
| ) | ||
| openapi_extra = { | ||
| "requestBody": { | ||
| "required": True, | ||
| "content": { | ||
| "application/json": { | ||
| "schema": request_schema, | ||
| } | ||
| }, | ||
| } | ||
| } | ||
| if endpoint.api_revision is not None: | ||
| openapi_extra["parameters"] = [endpoint.api_revision.openapi_parameter()] | ||
|
|
||
| operation: BufferedGuardedOperation[GuardableProviderRequest, BufferedHttpResponse] = BufferedGuardedOperation( | ||
| name=endpoint.operation_name, | ||
| input_projection=project_request, | ||
| output_projection=project_response, | ||
| ) | ||
|
|
||
| return GuardedHttpOperation( | ||
| operation_path=GuardedOperationPath(endpoint.route_path, frozenset({endpoint.method})), | ||
| operation=operation, | ||
| prepare_request=prepare_request, | ||
| forward_request=forward_request, | ||
| documented_responses=errors.documented_responses, | ||
| guarded_operation_paths=endpoint.operation_paths, | ||
| openapi_extra=openapi_extra, | ||
| ) | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.