This guide describes the project's strategy for hardening cross-layer data exchange using canonical typed contracts. It defines what constitutes a "boundary surface," how to use the canonical contracts, and how to maintain compliance with the boundary type guardrails.
The Universal LLM Proxy uses a layered architecture where data flows between:
- Transport Layer (HTTP/FastAPI controllers, adapters)
- Core Services (Request processing, orchestration, accounting)
- Connectors (Provider-specific implementations)
To ensure stability, debuggability, and type safety, these cross-layer seams must exchange canonical typed contracts rather than ad-hoc dict[str, Any] payloads.
- Stable Contracts: Core services rely on explicit Pydantic models or dataclasses.
- Zero Dict Leaks:
dict[str, Any]is forbidden at boundary signatures. - JSON Safety: Extension fields crossing boundaries must be JSON-serializable (
JsonValue). - Enforcement: Automated tools prevent regression.
Use these contracts for data exchange across boundaries.
| Concept | Canonical Contract | Module |
|---|---|---|
| Request | CanonicalChatRequest |
src.core.domain.chat |
| Context | RequestContext |
src.core.domain.request_context |
| Connector Context | ConnectorRequestContext |
src.connectors.contracts |
| Target | BackendTarget |
src.core.domain.backend_target |
| Usage | UsageSummary |
src.core.domain.usage_summary |
| Response | ResponseEnvelope |
src.core.domain.responses |
| Stream | StreamingResponseEnvelope |
src.core.domain.responses |
| Stream Chunk | ProcessedResponse |
src.core.interfaces.response_processor_interface |
| Connector Input | ConnectorChatCompletionsRequest |
src.connectors.contracts |
The project maintains a clear distinction between approved extension mechanisms (for new code) and legacy mechanisms (allowed for backward compatibility only).
Use these mechanisms for new cross-layer extensibility. All extension values must be JsonValue (JSON-serializable):
RequestContext.extensions: dict[str, JsonValue]: For cross-layer context metadataConnectorRequestContext.extensions: dict[str, JsonValue]: For connector-facing context metadataUsageSummary.extensions: dict[str, JsonValue]: For provider-specific usage dataResponseEnvelope.metadata: dict[str, JsonValue] | None: For response metadata crossing seamsStreamingResponseEnvelope.metadata: dict[str, JsonValue] | None: For streaming response metadataProcessedResponse.metadata: dict[str, JsonValue]: For streaming processed-chunk metadata crossing core → transport seam
Key Requirements:
- All extension values must be
JsonValue(JSON-serializable) - This ensures deterministic logging, wire capture, and replay
- Type checkers can validate JSON-serializable constraint
The following mechanisms exist for backward compatibility only and should not be used for new features:
ChatRequest.extra_body: dict[str, Any] | None: Allowed for protocol compatibility, but prefer typed fields where possibleToolCall.extra_content: dict[str, Any] | None: Allowed for provider-specific tool artifactsStreamingChunk.payload.opaque_json_dict: dict[str, Any] | None: Allowed as an explicit "opaque" escape hatch for non-standard streaming payloads
Important: These legacy mechanisms are kept for compatibility but have a documented promotion path requirement. When a stable, recurring concept is carried via a legacy mechanism, it must be promoted into a typed field or approved JSON-safe extension mechanism on a time-bounded plan.
The following patterns are explicitly forbidden at boundary surfaces:
-
New
dict[str, Any]fields on boundary-carried contracts/envelopes:# FORBIDDEN: Adding new dict[str, Any] fields class ResponseEnvelope: metadata: dict[str, JsonValue] | None # ✅ Approved custom_data: dict[str, Any] # ❌ Forbidden
-
New
metadata: dict[str, Any]arguments on boundary Protocols/interfaces:# FORBIDDEN: Using dict[str, Any] for metadata def process_response( response: ResponseEnvelope, metadata: dict[str, Any] # ❌ Forbidden ) -> ProcessedResponse: ... # CORRECT: Using dict[str, JsonValue] def process_response( response: ResponseEnvelope, metadata: dict[str, JsonValue] | None # ✅ Approved ) -> ProcessedResponse: ...
-
New
**kwargs: Anyusage outside the connector invoker/legacy connector compatibility surface:# FORBIDDEN: Using **kwargs at boundary def process_request( request: CanonicalChatRequest, **kwargs: Any # ❌ Forbidden ) -> ResponseEnvelope: ... # CORRECT: Use explicit parameters or approved extension mechanism def process_request( request: CanonicalChatRequest, context: RequestContext # ✅ Uses approved extensions ) -> ResponseEnvelope: ...
-
Dynamic attribute assignment on boundary contracts:
# FORBIDDEN: Dynamic attribute assignment context.custom_field = value # ❌ Forbidden # CORRECT: Use approved extension mechanism context.extensions["custom_field"] = value # ✅ Approved (if JsonValue)
When extension keys become stable and frequently used, they should be promoted to first-class typed fields or moved to approved extension mechanisms.
An extension key should be promoted when:
- Stability: Field shape has been stable for multiple releases
- Frequency: Field is accessed in multiple places across layers
- Semantic Importance: Field affects core contract semantics or correctness
- Type Safety: Stronger typing would catch bugs or improve maintainability
Example 1: Stable Recurring Key → Typed Field
Before (using extension):
# Writers
context.extensions["backend"] = "openai"
# Readers
backend = context.extensions.get("backend")After (promoted to typed field):
# Contract definition
class RequestContext:
backend: str | None = None
extensions: dict[str, JsonValue] = field(default_factory=dict)
# Writers
context.backend = "openai"
# Readers
backend = context.backendMigration (support both during transition):
# Writers: use typed field
context.backend = "openai"
# Readers: check typed field first, fall back to extension for compatibility
backend = context.backend or context.extensions.get("backend")Example 2: Provider-Specific/Variable Key → Approved JSON-Safe Extension Mechanism
Scenario: You need to pass provider-specific configuration that varies by backend.
Correct Approach:
# Use approved extension mechanism
context.extensions["openai_service_tier"] = "priority" # ✅ JsonValue
context.extensions["gemini_generation_config"] = {"temperature": 0.7} # ✅ JsonValue
# Access via extension
tier = context.extensions.get("openai_service_tier")Incorrect Approach:
# FORBIDDEN: Adding new dict[str, Any] field
class RequestContext:
provider_config: dict[str, Any] # ❌ ForbiddenExample 3: "Opaque JSON Blob" → Explicitly Named Opaque Field
Scenario: You need to pass non-standard streaming payload data that doesn't fit standard types.
Legacy Approach (allowed for compatibility):
# Legacy mechanism (no new usage)
chunk.payload.opaque_json_dict = {"custom": "data"} # ⚠️ Legacy onlyPreferred Approach (for new code):
# Use approved extension mechanism if possible
processed_response.metadata["custom_payload"] = {"custom": "data"} # ✅ JsonValue
# Or promote to typed field if stable
class StreamingPayload:
custom_data: dict[str, JsonValue] | None = None # ✅ Typed fieldConnector options must follow a strict policy to ensure JSON-safety and type safety:
Canonical Connector API:
- The canonical connector API (
ICanonicalChatCompletionsBackend) takesoptions: dict[str, JsonValue]inConnectorChatCompletionsRequest - All connector options must be JSON-serializable (
JsonValue) - Options are explicitly tracked and validated
Legacy Connector Compatibility:
- Legacy
**kwargsexpansion happens only insideConnectorInvoker(the compatibility adapter) - This is a documented boundary exception with a deprecation note
- Legacy connectors may accept
**kwargs, but core orchestration always passes canonical contracts
Migration Path:
- Current State: Legacy connectors accept
**kwargsviaConnectorInvokercompatibility path - Migration: Update connector to implement
ICanonicalChatCompletionsBackendand userequest.options - Future: Legacy kwargs expansion will be deprecated and eventually removed
Example:
# Canonical connector API (preferred)
async def chat_completions(
self,
request: ConnectorChatCompletionsRequest,
) -> ResponseEnvelope:
# Access options via typed contract
api_key = request.options.get("api_key")
# ...
# Legacy connector (compatibility path)
async def chat_completions(
self,
request_data: CanonicalChatRequest,
**kwargs: Any # ⚠️ Legacy only, handled by ConnectorInvoker
) -> ResponseEnvelope:
# Options expanded from request.options by ConnectorInvoker
api_key = kwargs.get("api_key")
# ...The project maintains a clear policy on where Any is permitted (internal-only contexts) and where it is forbidden (boundary surfaces).
Any is permitted in the following contexts, which are not considered boundary surfaces:
-
Internal Implementation Details:
- Code within a single layer that doesn't cross boundaries
- Internal utilities and helper functions
- Example: Internal middleware processing raw provider responses before normalization
-
Test Utilities and Fixtures:
- Test code that doesn't test boundary contracts
- Note: Tests that verify boundary behavior should use canonical contracts
-
Internal Processing Boundaries:
- Middleware that processes raw provider responses before normalization
- Internal transformation pipelines that operate on untyped data before converting to contracts
- Example:
IResponseProcessor.process()acceptsAnyfor raw provider responses (internal processing)
-
Serialization/Deserialization Methods:
- Methods that convert between JSON/dict and typed contracts
- Example:
UsageSummary.from_dict()acceptsdict[str, Any]for JSON parsing - Example:
UsageSummary.to_dict()returnsdict[str, Any]for JSON serialization
-
Diagnostic/Logging Utilities:
- Utilities that accept arbitrary data for logging or debugging
- Example:
RequestContext.get_modification_summary()returnsdict[str, Any]for diagnostics
Key Principle: If code doesn't cross a layer boundary (Transport ↔ Core ↔ Connector), Any may be acceptable for internal implementation flexibility.
Any is forbidden in the following contexts, which are boundary surfaces:
-
Function Signatures in Boundary Modules (within enforcement scope):
- Function/method signatures in files defined in
dev/boundary_types_scope.json - Protocol definitions for cross-layer interfaces
- Example:
ICanonicalChatCompletionsBackend.chat_completions()must use typed contracts, notAny
- Function/method signatures in files defined in
-
Boundary-Carried Contract-Shaped Payloads:
- Data structures that cross layer boundaries
- Request/response payloads exchanged between Transport, Core, and Connectors
- Example:
ConnectorChatCompletionsRequestmust use typed contracts, notdict[str, Any]
-
Extension Containers:
- Extension fields must use
JsonValue, notAny - Example:
RequestContext.extensions: dict[str, JsonValue]✅, notdict[str, Any]❌
- Extension fields must use
-
Connector-Facing Contracts:
- All connector API contracts must use canonical typed contracts
- Example: Connectors receive
ConnectorChatCompletionsRequest, notdict[str, Any]
-
Transport Adapter Protocols:
- Adapter protocols that convert between transport and core layers
- Example:
IStreamingContentConvertermust useProcessedResponse, notAny
Key Principle: If code crosses a layer boundary, it must use canonical typed contracts or JsonValue for extensions.
When a boundary violation is strictly necessary, it must be documented via a time-bounded allowlist entry.
Add an allowlist entry only when:
- Legacy Compatibility: Temporary support for legacy interfaces during migration
- Time-Bounded Exception: Known violation with a clear plan to fix within the expiration period
- Internal Processing Boundary: Violation occurs at an internal processing boundary (not cross-layer contract exchange)
Do NOT add allowlist entries for:
- New code introducing violations (should use canonical contracts from the start)
- Violations without a documented promotion path or fix plan
- Violations that can be fixed immediately with minimal effort
Create an entry in dev/boundary_types_allowlist.json with the following structure:
{
"file": "src/connectors/base.py",
"symbol": "chat_completions",
"violation": "dict[str, Any]",
"reason": "Legacy connector API compatibility. Will be hardened in Phase 1 with canonical connector API.",
"expires_at": "2026-06-30T00:00:00Z",
"tracking": "typed-contracts-boundary-hardening Phase 1"
}Required Fields:
file: Exact file path (relative to project root)symbol: Optional function/class name (if applicable,nullif file-wide)violation: Violation type (Any-in-signatureordict[str, Any])reason: Clear rationale explaining why the violation is necessaryexpires_at: RFC3339 timestamp (typically 6-12 months in the future)tracking: Issue/spec reference (e.g., "typed-contracts-boundary-hardening Phase 1")
Important: Every allowlist entry must have a documented promotion path or fix plan. The reason field should explain how the violation will be resolved.
Remove an allowlist entry when:
- Violation is Fixed: The code has been updated to use canonical contracts
- Promotion Path Completed: The violation has been promoted to a typed field or approved extension mechanism
- Entry Expires: Expired entries cause check failures and must be either renewed or the violation fixed
Before Expiration:
- Review the allowlist entry and assess progress on the promotion path
- Either fix the violation or renew the entry with:
- Updated rationale (if circumstances changed)
- New expiration date (typically 6-12 months)
- Updated tracking reference (if issue/spec changed)
After Expiration:
- Expired entries cause the boundary type check to fail
- The violation must be fixed; expired entries cannot be renewed without addressing the underlying issue
- Update the code to use canonical contracts or remove the violation
Permitted Any Usage (Internal-Only):
# Internal middleware processing raw provider responses
class ResponseProcessor:
def process(self, raw_response: Any) -> ProcessedResponse: # ✅ Internal processing
# Normalize raw provider response to ProcessedResponse
...
# Serialization method for JSON compatibility
class UsageSummary:
@classmethod
def from_dict(cls, data: dict[str, Any]) -> UsageSummary: # ✅ JSON parsing
...Forbidden Any Usage (Boundary Surface):
# FORBIDDEN: Connector API accepting dict[str, Any]
async def chat_completions(
self,
request: dict[str, Any] # ❌ Forbidden at boundary
) -> ResponseEnvelope:
...
# CORRECT: Use canonical contract
async def chat_completions(
self,
request: ConnectorChatCompletionsRequest # ✅ Typed contract
) -> ResponseEnvelope:
...Allowlisted Violation (Time-Bounded Exception):
# Legacy connector compatibility (allowlisted)
async def chat_completions(
self,
request_data: CanonicalChatRequest,
**kwargs: Any # ⚠️ Allowlisted, expires 2026-06-30
) -> ResponseEnvelope:
# Entry in dev/boundary_types_allowlist.json with expiration
...Connectors must implement the canonical protocol to ensure type safety.
New connectors should implement the ICanonicalChatCompletionsBackend protocol (duck typing or explicit inheritance):
async def chat_completions(
self,
request: ConnectorChatCompletionsRequest,
) -> ResponseEnvelope | StreamingResponseEnvelope:
...The ConnectorChatCompletionsRequest bundles all necessary inputs:
request: The canonicalCanonicalChatRequest(never a dict).processed_messages: TypedSequence[ChatMessage].options: Adict[str, JsonValue]for provider options (API keys, URLs).
Do not use **kwargs for connector options in the canonical API. Instead, pass options via the options dictionary in the request object. This ensures all options are JSON-serializable and explicitly tracked.
A boundary surface is a cross-layer seam where data is exchanged between different architectural layers:
- Transport ↔ Core: HTTP/FastAPI controllers and adapters exchange data with core services
- Core ↔ Connector: Core orchestration invokes connector backends
- Core ↔ Capture/Replay: Core services interact with wire capture and replay systems
Boundary surfaces are the only places where cross-layer data exchange occurs. Internal implementation details within a single layer are not considered boundary surfaces.
The project defines an explicit boundary surface enforcement scope that determines which files are subject to boundary type checking. This scope is defined in dev/boundary_types_scope.json and serves as the source of truth for enforcement.
Scope Configuration Structure:
explicit_files: List of exact file paths that must be enforced (highest priority)include_globs: Glob patterns for files to include in enforcementexclude_globs: Glob patterns for files to exclude from enforcement
Precedence Rules:
explicit_filesoverrideexclude_globs- If a file matches both
include_globsandexclude_globs, it is excluded unless also present inexplicit_files
Phase 0 Scope (Current): Phase 0 enforcement uses signature-first enforcement focused on the highest-leverage boundary surfaces:
- Connector boundary API (
src/connectors/base.py) - Response processor interfaces (
src/core/interfaces/response_processor_interface.py) - Transport adapter protocols (
src/core/transport/fastapi/adapters/protocols.py) - Canonical contract carriers (response envelopes, request context, backend target, usage summary, streaming contracts)
Future Phases:
- Phase 1+: Expand enforcement to include connector implementations and additional core service interfaces as they become compliant
- Scope expansion is incremental and controlled to maintain actionable enforcement
Important: Phase 0 scope is enforced; violations in Phase 0 scope files will cause the boundary type check to fail. Areas outside Phase 0 scope are advisory until the scope is expanded in later phases.
The project uses a custom boundary type checker to enforce typed contract rules at boundary surfaces.
Canonical Command:
./.venv/Scripts/python.exe dev/scripts/check_boundary_types.pyThis command scans all files defined in the boundary surface enforcement scope (dev/boundary_types_scope.json) and reports violations.
What Violations Are Detected:
Anyin signature: Function signatures, method signatures, or Protocol definitions usingAnyordict[str, Any]for boundary-carried datadict[str, Any]: Explicit usage ofdict[str, Any]for contract-shaped payloads at boundary surfaces
Violation Reporting Format:
Violations are reported in file:line:column format with a human-readable message:
src/connectors/base.py:45:12: Function signature uses Any for boundary-carried request payload
Exit Codes:
- 0: Codebase is compliant (no violations or all violations are allowlisted and not expired)
- Non-zero: Violations detected that are not allowlisted or allowlist entries have expired
The scope configuration file (dev/boundary_types_scope.json) defines which files are subject to boundary type enforcement. This file is the source of truth for enforcement scope.
Current Scope (Phase 0):
explicit_files: Explicitly pinned files for signature-first enforcement:src/connectors/base.py(connector boundary API)src/core/interfaces/response_processor_interface.py(response processor seam)src/core/transport/fastapi/adapters/protocols.py(adapter protocol signatures)src/core/domain/responses.py(response envelopes)src/core/domain/request_context.py(context contract)src/core/domain/backend_target.py(routing contract)src/core/domain/usage_summary.py(usage contract)src/core/domain/streaming/contracts.py(typed chunk boundary model)
include_globs:src/connectors/contracts/**/*.py(connector-facing contracts)exclude_globs: Empty (no exclusions in Phase 0)
Future Scope Expansion:
- Phase 1+: Add narrowly targeted
include_globsas areas become compliant - Examples:
src/core/interfaces/**/*.py,src/core/transport/fastapi/adapters/**/*.py src/core/domain/**/*.pyremains excluded by default, except for explicit canonical contract carriers
When the boundary type check reports a violation, follow this workflow:
-
Fix the Violation (Preferred):
- Replace
Any/dict[str, Any]with a canonical contract orJsonValue - Use canonical contracts from the Canonical Contract Set
- Ensure boundary-carried data uses typed contracts
- Replace
-
Allowlist the Violation (Only if Strictly Necessary):
- Use allowlist only when the violation is required for legacy compatibility or represents a time-bounded exception
- Add an entry to
dev/boundary_types_allowlist.jsonwith: file: Exact file pathsymbol: Optional function/class name (if applicable)violation: Violation type (Any-in-signatureordict[str, Any])reason: Clear rationale explaining why the violation is necessaryexpires_at: RFC3339 timestamp (typically 6-12 months in the future)tracking: Issue/spec reference (e.g., "typed-contracts-boundary-hardening Phase 1")- Must have a documented promotion path to remove the violation
-
Verify Fix:
- Re-run the boundary type check to confirm the violation is resolved or properly allowlisted
- Ensure expired allowlist entries are renewed or violations are fixed
Important: Allowlist entries expire. Expired entries cause the boundary type check to fail, requiring either renewal (with updated rationale) or fixing the violation.
When Allowlist Entries Are Appropriate:
- Legacy compatibility: Temporary support for legacy interfaces during migration
- Time-bounded exceptions: Known violations with a clear plan to fix within the expiration period
- Internal processing boundaries: Violations that occur at internal processing boundaries (not cross-layer contract exchange)
When Allowlist Entries Are NOT Appropriate:
- New code introducing violations (should use canonical contracts from the start)
- Violations without a documented promotion path or fix plan
- Violations that can be fixed immediately with minimal effort
Allowlist Entry Structure:
{
"file": "src/connectors/base.py",
"symbol": "chat_completions",
"violation": "dict[str, Any]",
"reason": "Legacy connector API compatibility. Will be hardened in Phase 1 with canonical connector API.",
"expires_at": "2026-06-30T00:00:00Z",
"tracking": "typed-contracts-boundary-hardening Phase 1"
}Expiration Handling:
- Expired entries cause the boundary type check to fail
- Before expiration: Either fix the violation or renew the allowlist entry with updated rationale and expiration date
- After expiration: The violation must be fixed; expired entries cannot be renewed without addressing the underlying issue
The boundary type check is integrated into the project's required verification workflow:
- CI Integration: New violations introduced in pull requests will cause CI to fail unless properly allowlisted
- Pre-commit: Consider running the boundary type check before committing changes to boundary surfaces
- Enforcement Status: Phase 0 scope is enforced; violations in Phase 0 scope files will fail verification
Forbidden Patterns (will fail boundary type check):
- Adding
type: ignoreto bypass the checker without a documented reason and allowlist entry - Adding new
dict[str, Any]fields to boundary contracts - Passing internal Core objects (like
AppState) directly to Connectors - Using
Anyin boundary function signatures without allowlist entry
- Update the
chat_completionssignature to acceptConnectorChatCompletionsRequest. - Remove
**kwargsusage for options; userequest.optionsinstead. - Ensure
processed_messagesare treated asChatMessageobjects, not dicts. - Return
ResponseEnvelopeorStreamingResponseEnvelopewithUsageSummary.
If an extension key in metadata or extensions becomes stable and widely used:
- Propose adding it as a typed field to the relevant canonical contract.
- Update the conversion logic to populate the typed field.
- Deprecate the extension key usage.