Generalize OpenAI-compatible provider adapters#204
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR generalizes Calciforge’s model-gateway “Helicone-only” path into a shared OpenAI-compatible HTTP core, with small engine/policy overlays keyed by backend_type. It expands root/provider backend parsing + validation to include named OpenAI-compatible gateways (LiteLLM/Portkey/TensorZero/Future-AGI/OpenRouter), and updates tests/scripts/docs to match the new architecture.
Changes:
- Introduces new
GatewayTypevariants + shared OpenAI-compatible header overlay logic, and routes Helicone/LiteLLM/etc through the same HTTP backend/gateway core. - Adds SSE parsing support to the HTTP backend using a generalized OpenAI-compatible streaming parser; updates routing + doctor reporting accordingly.
- Splits oversized validator tests into multiple modules, updates boundary scripts + scenario definitions, and refreshes docs/ADRs around the provider-adapter boundary.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/scenarios/high-risk-scenarios.json | Renames the streaming-tools high-risk scenario to reflect generalized OpenAI-compatible streaming coverage. |
| tests/boundaries/integration-surfaces.json | Updates boundary surface inventory and automation commands to use openai_streaming tests and new validator test modules. |
| scripts/model-gateway-litellm-smoke.py | Switches LiteLLM smoke config to backend_type = "litellm" to exercise named engine parsing/metadata. |
| scripts/boundary-explore-long.sh | Updates long boundary exploration to run proxy::openai_streaming::tests. |
| scripts/boundary-aggression.sh | Updates boundary aggression suite to run proxy::openai_streaming::tests. |
| docs/roadmap/litellm-gateway-owned-provider.md | Updates LiteLLM recipe to use the named litellm engine overlay and shared core framing. |
| docs/model-gateway.md | Reframes gateway engines as OpenAI-compatible overlays, expands supported backends, and documents gateway-vs-observability separation. |
| docs/adr/0002-provider-adapter-boundary.md | Notes the shared OpenAI-compatible HTTP core and engine overlays as an architectural decision. |
| docs/adr/0001-model-gateway-and-agent-boundaries.md | Updates diagrams/text to reflect a generalized OpenAI-compatible engine set and shared core. |
| crates/calciforge/src/proxy/routing.rs | Generalizes provider gateway construction by parsing GatewayType and applying engine-specific header overlays. |
| crates/calciforge/src/proxy/openai_streaming.rs | Renames/repurposes streaming parsing and error messages to be OpenAI-compatible rather than Helicone-specific. |
| crates/calciforge/src/proxy/mod.rs | Exposes gateway module internally, adds openai_streaming, and switches root backend allowlist to GatewayType constants. |
| crates/calciforge/src/proxy/helicone_router.rs | Deleted: removes the dedicated Helicone router implementation in favor of shared HTTP core. |
| crates/calciforge/src/proxy/helicone_router_tests.rs | Deleted: removes Helicone-router-specific tests (replaced by shared-core tests). |
| crates/calciforge/src/proxy/gateway.rs | Expands GatewayType, adds OpenAI-compatible header overlays, and routes Helicone/etc through the shared HTTP gateway implementation. |
| crates/calciforge/src/proxy/gateway_tests.rs | New consolidated gateway tests covering parsing, overlays, retry behavior, and streaming parsing via shared core. |
| crates/calciforge/src/proxy/backend.rs | Removes Helicone backend type and adds SSE parsing support to the HTTP backend. |
| crates/calciforge/src/doctor.rs | Updates provider-boundary reporting to recognize named OpenAI-compatible provider adapters and improves messaging. |
| crates/calciforge/src/config/validator.rs | Expands proxy/provider backend validation to use GatewayType parsing and splits test modules out of this file. |
| crates/calciforge/src/config/validator_tests_1.rs | New: first chunk of validator behavioral tests (moved from validator.rs). |
| crates/calciforge/src/config/validator_tests_2.rs | New: second chunk of validator behavioral tests (moved from validator.rs). |
| crates/calciforge/src/config/validator_tests_3.rs | New: third chunk of validator behavioral tests (moved from validator.rs). |
| crates/calciforge/src/config/validator_test_support.rs | New shared fixture/helpers for validator behavioral tests. |
| crates/calciforge/src/config.rs | Updates config docs/comments to reflect the expanded root/provider backend types and shared OpenAI-compatible core. |
| crates/calciforge/Cargo.toml | Removes default helicone feature and updates retry dependency comment to be engine-agnostic. |
Comments suppressed due to low confidence (1)
crates/calciforge/src/proxy/backend.rs:381
- HttpBackend applies
headerstwice (asClientBuilder::default_headersinnew()and again per-request insend_chat_completion_request). This can duplicate headers and also allows user-configured headers (e.g.,Authorization) to override/append after the API-key header, which can break auth and is risky. Prefer applying configured headers only once and ensure the computed Authorization header cannot be overridden by user headers (e.g., strip/ignoreauthorizationfrom configured headers or apply Authorization last with replacement semantics).
// Add default headers if provided
if let Some(headers) = &headers {
let mut header_map = reqwest::header::HeaderMap::new();
for (key, value) in headers {
if let Ok(header_name) = reqwest::header::HeaderName::from_bytes(key.as_bytes())
&& let Ok(header_value) = reqwest::header::HeaderValue::from_str(value)
{
header_map.insert(header_name, header_value);
}
}
client_builder = client_builder.default_headers(header_map);
}
let client = client_builder.build().expect("Failed to build HTTP client");
Self {
client,
base_url,
api_key,
timeout_seconds,
headers: headers.unwrap_or_default(),
}
}
async fn send_chat_completion_request(
&self,
request: crate::proxy::openai::ChatCompletionRequest,
) -> Result<ChatCompletionResponse, BackendError> {
let url = format!("{}/chat/completions", self.base_url);
let model = request.model.clone();
let mut request_body = serde_json::to_value(&request).map_err(|e| {
BackendError::InvalidResponse(format!("Failed to serialize request: {e}"))
})?;
apply_kimi_compat(&self.base_url, &model, &mut request_body);
let mut request_builder = self
.client
.post(&url)
.header("Content-Type", "application/json");
if !self.api_key.is_empty() {
request_builder =
request_builder.header("Authorization", format!("Bearer {}", self.api_key));
}
for (key, value) in &self.headers {
request_builder = request_builder.header(key, value);
}
Comment on lines
391
to
400
| if !response.status().is_success() { | ||
| let status = response.status(); | ||
| let error_text = response | ||
| .text() | ||
| .await | ||
| .unwrap_or_else(|_| "Unknown error".to_string()); | ||
| return Err(BackendError::http_status_error( | ||
| status, | ||
| format!("API error {}: {}", status, error_text), | ||
| )); |
Comment on lines
+592
to
600
| let root_gateway_type = proxy | ||
| .backend_type | ||
| .parse::<crate::proxy::gateway::GatewayType>() | ||
| .ok(); | ||
|
|
||
| if root_gateway_type.is_some_and(|gateway_type| gateway_type.requires_backend_url()) | ||
| && proxy.backend_url.trim().is_empty() | ||
| { | ||
| result.add_error(format!( |
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
litellm,portkey,tensorzero,future-agi, andopenrouteralongsidehelicone,http, andmockVerification
cargo test -p calciforge proxy::gateway_testscargo test -p calciforge proxy::routing::testspython3 scripts/check-boundary-surfaces.py && python3 scripts/check-scenarios.py && ruby scripts/check-architecture-ratchets.rb && git diff --checkpython3 scripts/model-gateway-helicone-smoke.py && python3 scripts/model-gateway-litellm-smoke.pycargo test -p calciforgeAdversarial Review Notes
GatewayTypepolicy overlay on the shared OpenAI-compatible HTTP core. This avoids the next adapter copying Helicone plumbing.