refactor(pi): certify native inspection boundary

This commit is contained in:
SaladDay
2026-08-02 10:12:00 +00:00
parent 2533db035a
commit 9b5a146323
6 changed files with 876 additions and 146 deletions
+99 -10
View File
@@ -9,7 +9,7 @@ use std::sync::LazyLock;
use syn::visit::{self, Visit};
use syn::{
Attribute, ExprLit, ImplItem, Item, ItemImpl, ItemStruct, ItemUse, Lit, Meta, Path as SynPath,
Type, UseTree,
Token, Type, UseTree,
};
static PROVIDER_DML: LazyLock<Regex> = LazyLock::new(|| {
@@ -30,19 +30,82 @@ struct Violation {
detail: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CfgTruth {
True,
False,
Unknown,
}
impl CfgTruth {
fn not(self) -> Self {
match self {
Self::True => Self::False,
Self::False => Self::True,
Self::Unknown => Self::Unknown,
}
}
fn and(self, other: Self) -> Self {
match (self, other) {
(Self::False, _) | (_, Self::False) => Self::False,
(Self::True, Self::True) => Self::True,
_ => Self::Unknown,
}
}
fn or(self, other: Self) -> Self {
match (self, other) {
(Self::True, _) | (_, Self::True) => Self::True,
(Self::False, Self::False) => Self::False,
_ => Self::Unknown,
}
}
}
/// Evaluate a cfg predicate for a production build where `test = false`.
/// Other target/features are deliberately unknown: the scanner may exclude an
/// item only when the predicate is definitely false in every production
/// configuration.
fn production_cfg_truth(meta: &Meta) -> CfgTruth {
match meta {
Meta::Path(path) if path.is_ident("test") => CfgTruth::False,
Meta::Path(_) | Meta::NameValue(_) => CfgTruth::Unknown,
Meta::List(list) if list.path.is_ident("not") => list
.parse_args::<Meta>()
.map(|inner| production_cfg_truth(&inner).not())
.unwrap_or(CfgTruth::Unknown),
Meta::List(list) if list.path.is_ident("all") || list.path.is_ident("any") => {
let Ok(items) = list
.parse_args_with(syn::punctuated::Punctuated::<Meta, Token![,]>::parse_terminated)
else {
return CfgTruth::Unknown;
};
if list.path.is_ident("all") {
items.iter().fold(CfgTruth::True, |truth, item| {
truth.and(production_cfg_truth(item))
})
} else {
items.iter().fold(CfgTruth::False, |truth, item| {
truth.or(production_cfg_truth(item))
})
}
}
Meta::List(_) => CfgTruth::Unknown,
}
}
fn is_cfg_test(attrs: &[Attribute]) -> bool {
attrs.iter().any(|attribute| {
if !attribute.path().is_ident("cfg") {
let Meta::List(list) = &attribute.meta else {
return false;
};
if !list.path.is_ident("cfg") {
return false;
}
match &attribute.meta {
Meta::List(list) => list
.tokens
.to_string()
.split(|character: char| !character.is_ascii_alphanumeric() && character != '_')
.any(|token| token == "test"),
_ => false,
}
list.parse_args::<Meta>()
.map(|predicate| production_cfg_truth(&predicate) == CfgTruth::False)
.unwrap_or(false)
})
}
@@ -559,4 +622,30 @@ fn architecture_scanner_negative_fixtures_prove_each_guard_fires() {
violations.is_empty(),
"#[cfg(test)] content must be excluded: {violations:?}"
);
let production_not_test_fixture = r#"
#[cfg(not(test))]
fn production_escape_attempt() {
save_provider();
}
"#;
let (violations, _) = scan_source("src/services/production.rs", production_not_test_fixture);
assert!(
violations
.iter()
.any(|violation| violation.kind == "forbidden_provider_symbol"),
"#[cfg(not(test))] production content must remain visible: {violations:?}"
);
let test_only_conjunction = r#"
#[cfg(all(test, unix))]
fn test_only() {
save_provider();
}
"#;
let (violations, _) = scan_source("src/services/test_only.rs", test_only_conjunction);
assert!(
violations.is_empty(),
"a predicate requiring test must stay excluded: {violations:?}"
);
}
+80 -20
View File
@@ -10,21 +10,46 @@ use super::composer::{PiComposedNativeModel, PiComposerStatus, PiNativeCompositi
use http::{HeaderMap, HeaderName, HeaderValue};
use url::Url;
const PROTECTED_HEADERS: &[&str] = &[
"authorization",
/// Headers owned by HTTP framing, the proxy hop, or gateway-generated request
/// identity. Configured providers may not override them.
const GATEWAY_OWNED_HEADERS: &[&str] = &[
"connection",
"content-length",
"forwarded",
"host",
"keep-alive",
"proxy-authenticate",
"proxy-authorization",
"te",
"trailer",
"transfer-encoding",
"upgrade",
"x-api-key",
"x-goog-api-key",
"x-forwarded-for",
"x-forwarded-host",
"x-forwarded-port",
"x-forwarded-proto",
"x-real-ip",
"cf-connecting-ip",
"cf-ipcountry",
"cf-ray",
"cf-visitor",
"true-client-ip",
"fastly-client-ip",
"x-azure-clientip",
"x-azure-fdid",
"x-azure-ref",
"akamai-origin-hop",
"x-akamai-config-log-detail",
"x-request-id",
"x-correlation-id",
"x-trace-id",
"x-amzn-trace-id",
"x-b3-traceid",
"x-b3-spanid",
"x-b3-parentspanid",
"x-b3-sampled",
"traceparent",
"tracestate",
];
const CANDIDATE_AUTH_HEADERS: &[&str] = &["authorization", "x-api-key", "x-goog-api-key"];
const PROTOCOL_HEADERS: &[&str] = &[
"anthropic-version",
"anthropic-beta",
@@ -32,6 +57,27 @@ const PROTOCOL_HEADERS: &[&str] = &[
"openai-version",
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ConfiguredHeaderClass {
GatewayOwned,
Protocol,
CandidateAuth,
CandidateLocal,
}
fn configured_header_class(name: &HeaderName) -> ConfiguredHeaderClass {
let name = name.as_str();
if name.starts_with("proxy-") || GATEWAY_OWNED_HEADERS.contains(&name) {
ConfiguredHeaderClass::GatewayOwned
} else if PROTOCOL_HEADERS.contains(&name) {
ConfiguredHeaderClass::Protocol
} else if CANDIDATE_AUTH_HEADERS.contains(&name) {
ConfiguredHeaderClass::CandidateAuth
} else {
ConfiguredHeaderClass::CandidateLocal
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(super) enum PiGatewayApiFamily {
AnthropicMessages,
@@ -185,12 +231,12 @@ impl CandidateHeaderPlan {
return Err(reasons);
}
};
let Some(credential) = model.api_key.as_ref() else {
let credential = model.api_key.as_ref();
if credential.is_none() {
reasons.push(PiGatewayReason {
code: PiGatewayReasonCode::MissingCredential,
json_pointer: "/apiKey".to_string(),
});
return Err(reasons);
};
let mut custom_headers = Vec::new();
@@ -208,7 +254,8 @@ impl CandidateHeaderPlan {
});
continue;
};
if PROTECTED_HEADERS.contains(&parsed_name.as_str()) {
let class = configured_header_class(&parsed_name);
if class == ConfiguredHeaderClass::GatewayOwned {
reasons.push(PiGatewayReason {
code: PiGatewayReasonCode::ProtectedHeader,
json_pointer: pointer,
@@ -227,7 +274,7 @@ impl CandidateHeaderPlan {
pointer,
DeferredHeaderValue::new(value.clone()),
);
if PROTOCOL_HEADERS.contains(&planned.0.as_str()) {
if class == ConfiguredHeaderClass::Protocol {
protocol_identity_predictable &= !value.starts_with('!');
protocol_headers.push(planned);
} else {
@@ -237,11 +284,19 @@ impl CandidateHeaderPlan {
if !reasons.is_empty() {
return Err(reasons);
}
let Some(credential) = credential.cloned() else {
// Missing credentials are accumulated above so configured-header
// diagnostics can be returned in the same assessment.
return Err(vec![PiGatewayReason {
code: PiGatewayReasonCode::MissingCredential,
json_pointer: "/apiKey".to_string(),
}]);
};
Ok(Self {
family,
endpoint,
credential: DeferredHeaderValue::new(credential.clone()),
credential: DeferredHeaderValue::new(credential),
auth_header: model.auth_header,
custom_headers,
protocol_headers,
@@ -282,6 +337,20 @@ impl CandidateHeaderPlan {
}
};
headers.insert(auth_name, auth_value);
// Pinned Anthropic/OpenAI SDKs merge explicit configured headers after
// their synthesized family auth, so an explicit candidate-auth value
// wins when authHeader is disabled.
for (name, pointer, value) in &self.custom_headers {
headers.insert(name.clone(), value.materialize(resolver, pointer)?);
}
for (name, pointer, value) in &self.protocol_headers {
let value = value.materialize(resolver, pointer)?;
protocol_headers.insert(name.clone(), value.clone());
headers.insert(name.clone(), value);
}
// Pi's provider composer applies authHeader after custom headers. It
// overwrites only Authorization and leaves family auth (for example
// x-api-key) at its already-materialized final value.
if self.auth_header {
let credential = bearer_credential.to_str().map_err(|_| PiGatewayReason {
code: PiGatewayReasonCode::InvalidHeaderValue,
@@ -295,15 +364,6 @@ impl CandidateHeaderPlan {
})?;
headers.insert(HeaderName::from_static("authorization"), bearer);
}
for (name, pointer, value) in &self.custom_headers {
headers.insert(name.clone(), value.materialize(resolver, pointer)?);
}
for (name, pointer, value) in &self.protocol_headers {
let value = value.materialize(resolver, pointer)?;
protocol_headers.insert(name.clone(), value.clone());
headers.insert(name.clone(), value);
}
let host = authority_header(&self.endpoint).ok_or_else(|| PiGatewayReason {
code: PiGatewayReasonCode::InvalidEndpoint,
json_pointer: "/baseUrl".to_string(),
+2
View File
@@ -9,4 +9,6 @@ mod document;
mod gateway;
pub(crate) mod model;
pub(crate) mod native;
#[cfg(test)]
mod native_inspection_certification;
mod raw_schema;
+139 -39
View File
@@ -14,6 +14,7 @@ use thiserror::Error;
use url::Url;
pub(crate) type PiHeaderMap = BTreeMap<String, String>;
pub(crate) type PiThinkingLevelMap = BTreeMap<String, Value>;
/// Pi uses JavaScript/TypeBox `Number`, not `Integer`, for model limits.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize)]
@@ -133,6 +134,8 @@ pub(crate) struct PiModelCostTier {
pub output: f64,
pub cache_read: f64,
pub cache_write: f64,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
@@ -142,6 +145,8 @@ pub(crate) struct PiModelCost {
pub rates: PiModelCostRates,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tiers: Vec<PiModelCostTier>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
@@ -157,6 +162,8 @@ pub(crate) struct PiModelCostOverride {
pub cache_write: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tiers: Option<Vec<PiModelCostTier>>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
@@ -166,8 +173,8 @@ pub(crate) struct PiManagedModelOverride {
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reasoning: Option<bool>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub thinking_level_map: BTreeMap<String, Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thinking_level_map: Option<PiThinkingLevelMap>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input: Option<Vec<PiModelInput>>,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -196,8 +203,8 @@ pub(crate) struct PiManagedModel {
pub api: Option<PiManagedApiId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reasoning: Option<bool>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub thinking_level_map: BTreeMap<String, Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thinking_level_map: Option<PiThinkingLevelMap>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input: Option<Vec<PiModelInput>>,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -252,7 +259,8 @@ pub(crate) struct PiEffectiveModel {
pub api_key: Option<String>,
pub auth_header: bool,
pub reasoning: bool,
pub thinking_level_map: BTreeMap<String, Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thinking_level_map: Option<PiThinkingLevelMap>,
pub input: Vec<PiModelInput>,
pub cost: PiModelCost,
pub context_window: PiNumber,
@@ -297,8 +305,8 @@ pub(crate) enum PiConfigError {
model_id: String,
field: &'static str,
},
#[error("Pi model '{model_id}' contains an unmanaged thinking level '{level}'")]
InvalidThinkingLevel { model_id: String, level: String },
#[error("Pi model '{model_id}' contains an invalid value for thinking level '{level}'")]
InvalidThinkingLevelValue { model_id: String, level: String },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
@@ -459,7 +467,7 @@ pub(crate) fn validate_pi_managed_provider(
validate_compat(model.compat.as_ref(), &format!("/models/{index}/compat"))?;
validate_model_limit(model, model.context_window, "contextWindow")?;
validate_model_limit(model, model.max_tokens, "maxTokens")?;
validate_thinking_levels(&model.id, model.thinking_level_map.keys())?;
validate_thinking_levels(&model.id, model.thinking_level_map.as_ref())?;
let _ = effective_pi_model_unchecked(provider, model)?;
}
@@ -470,7 +478,7 @@ pub(crate) fn validate_pi_managed_provider(
validate_optional_text(model_override.name.as_deref(), "model override name")?;
validate_optional_limit(model_id, model_override.context_window, "contextWindow")?;
validate_optional_limit(model_id, model_override.max_tokens, "maxTokens")?;
validate_thinking_levels(model_id, model_override.thinking_level_map.keys())?;
validate_thinking_levels(model_id, model_override.thinking_level_map.as_ref())?;
validate_compat(
model_override.compat.as_ref(),
&format!("/modelOverrides/{}/compat", escape_json_pointer(model_id)),
@@ -517,10 +525,10 @@ fn effective_pi_model_unchecked(
}
headers.extend(model.headers.clone());
let mut thinking_level_map = model.thinking_level_map.clone();
if let Some(model_override) = model_override {
thinking_level_map.extend(model_override.thinking_level_map.clone());
}
let thinking_level_map = merge_thinking_level_maps(
model.thinking_level_map.as_ref(),
model_override.and_then(|entry| entry.thinking_level_map.as_ref()),
);
let base_cost = model.cost.clone().unwrap_or_default();
let cost = model_override
@@ -571,21 +579,35 @@ fn effective_pi_model_unchecked(
})
}
fn apply_cost_override(base: PiModelCost, model_override: &PiModelCostOverride) -> PiModelCost {
PiModelCost {
rates: PiModelCostRates {
input: model_override.input.unwrap_or(base.rates.input),
output: model_override.output.unwrap_or(base.rates.output),
cache_read: model_override.cache_read.unwrap_or(base.rates.cache_read),
cache_write: model_override.cache_write.unwrap_or(base.rates.cache_write),
},
tiers: model_override
.tiers
.clone()
.unwrap_or_else(|| base.tiers.clone()),
fn merge_thinking_level_maps(
base: Option<&PiThinkingLevelMap>,
overlay: Option<&PiThinkingLevelMap>,
) -> Option<PiThinkingLevelMap> {
match (base, overlay) {
(None, None) => None,
(Some(value), None) | (None, Some(value)) => Some(value.clone()),
(Some(base), Some(overlay)) => {
let mut merged = base.clone();
merged.extend(overlay.clone());
Some(merged)
}
}
}
fn apply_cost_override(mut base: PiModelCost, model_override: &PiModelCostOverride) -> PiModelCost {
base.rates = PiModelCostRates {
input: model_override.input.unwrap_or(base.rates.input),
output: model_override.output.unwrap_or(base.rates.output),
cache_read: model_override.cache_read.unwrap_or(base.rates.cache_read),
cache_write: model_override.cache_write.unwrap_or(base.rates.cache_write),
};
if let Some(tiers) = &model_override.tiers {
base.tiers = tiers.clone();
}
base.extra.extend(model_override.extra.clone());
base
}
fn merge_compat(base: Option<Value>, overlay: Option<Value>) -> Option<Value> {
match (base, overlay) {
(None, None) => None,
@@ -679,13 +701,16 @@ fn escape_json_pointer(value: &str) -> String {
value.replace('~', "~0").replace('/', "~1")
}
fn validate_thinking_levels<'a>(
fn validate_thinking_levels(
model_id: &str,
levels: impl Iterator<Item = &'a String>,
levels: Option<&PiThinkingLevelMap>,
) -> Result<(), PiConfigError> {
for level in levels {
if !THINKING_LEVELS.contains(&level.as_str()) {
return Err(PiConfigError::InvalidThinkingLevel {
let Some(levels) = levels else {
return Ok(());
};
for (level, value) in levels {
if THINKING_LEVELS.contains(&level.as_str()) && !(value.is_string() || value.is_null()) {
return Err(PiConfigError::InvalidThinkingLevelValue {
model_id: model_id.to_string(),
level: level.clone(),
});
@@ -714,7 +739,7 @@ mod tests {
base_url: None,
api: None,
reasoning: None,
thinking_level_map: BTreeMap::new(),
thinking_level_map: None,
input: None,
cost: None,
context_window: None,
@@ -841,7 +866,7 @@ mod tests {
}
#[test]
fn managed_narrowing_rejects_duplicates_or_unknown_thinking_keys() {
fn managed_validation_rejects_duplicates_but_preserves_schema_valid_thinking_maps() {
let duplicate = provider(vec![model("same"), model("same")]);
assert_eq!(
validate_pi_managed_provider(&duplicate),
@@ -849,18 +874,93 @@ mod tests {
);
let mut future_thinking = model("thinking");
future_thinking
.thinking_level_map
.insert("future".into(), Some("opaque".into()));
future_thinking.thinking_level_map = Some(BTreeMap::from([
("high".into(), json!("native-high")),
("future".into(), json!({"opaque": true})),
]));
let future_config = provider(vec![future_thinking]);
validate_pi_managed_provider(&future_config)
.expect("pinned-schema additional keys remain manageable");
assert_eq!(
validate_pi_managed_provider(&provider(vec![future_thinking])),
Err(PiConfigError::InvalidThinkingLevel {
model_id: "thinking".into(),
level: "future".into()
serde_json::to_value(&future_config)
.expect("serialize")
.pointer("/models/0/thinkingLevelMap/future"),
Some(&json!({"opaque": true}))
);
let mut invalid_known = model("invalid-known");
invalid_known.thinking_level_map = Some(BTreeMap::from([("low".into(), json!(2))]));
assert_eq!(
validate_pi_managed_provider(&provider(vec![invalid_known])),
Err(PiConfigError::InvalidThinkingLevelValue {
model_id: "invalid-known".into(),
level: "low".into()
})
);
}
#[test]
fn future_cost_members_survive_model_override_and_effective_projection() {
let config: PiManagedProviderConfig = serde_json::from_value(json!({
"api": "anthropic-messages",
"baseUrl": "https://cost.example",
"models": [{
"id": "m",
"cost": {
"input": 1.0,
"output": 2.0,
"cacheRead": 0.5,
"cacheWrite": 0.25,
"futureRate": {"opaque": true},
"tiers": [{
"inputTokensAbove": 100.0,
"input": 1.0,
"output": 2.0,
"cacheRead": 0.5,
"cacheWrite": 0.25,
"futureTierField": ["preserved"]
}]
}
}],
"modelOverrides": {
"m": {
"cost": {
"output": 3.0,
"futureOverrideRate": "preserved"
}
}
}
}))
.expect("deserialize future cost members");
validate_pi_managed_provider(&config).expect("future cost members are manageable");
let round_trip = serde_json::to_value(&config).expect("serialize managed config");
assert_eq!(
round_trip.pointer("/models/0/cost/futureRate"),
Some(&json!({"opaque": true}))
);
assert_eq!(
round_trip.pointer("/models/0/cost/tiers/0/futureTierField"),
Some(&json!(["preserved"]))
);
let effective =
serde_json::to_value(effective_pi_model(&config, "m").expect("effective model"))
.expect("serialize effective model");
assert_eq!(effective.pointer("/cost/output"), Some(&json!(3.0)));
assert_eq!(
effective.pointer("/cost/futureRate"),
Some(&json!({"opaque": true}))
);
assert_eq!(
effective.pointer("/cost/futureOverrideRate"),
Some(&json!("preserved"))
);
assert_eq!(
effective.pointer("/cost/tiers/0/futureTierField"),
Some(&json!(["preserved"]))
);
}
#[test]
fn diagnostic_reason_serialization_is_structured() {
let reason = PiDiagnosticReason::new(
+28 -77
View File
@@ -310,7 +310,7 @@ fn assess_managed(
reasons: vec![diagnostic_reason(
PiDiagnosticLayer::Managed,
PiReasonCode::ManagedTypeConversionFailed,
&managed_conversion_pointer(value),
"",
)],
};
}
@@ -432,18 +432,6 @@ fn collect_managed_reasons(config: &PiManagedProviderConfig) -> Vec<PiDiagnostic
);
}
}
for level in model.thinking_level_map.keys() {
if !is_managed_thinking_level(level) {
add_reason(
&mut reasons,
diagnostic_reason(
PiDiagnosticLayer::Managed,
PiReasonCode::InvalidThinkingLevel,
&format!("{pointer}/thinkingLevelMap/{}", escape_json_pointer(level)),
),
);
}
}
}
for (model_id, model_override) in &config.model_overrides {
@@ -473,58 +461,10 @@ fn collect_managed_reasons(config: &PiManagedProviderConfig) -> Vec<PiDiagnostic
);
}
}
for level in model_override.thinking_level_map.keys() {
if !is_managed_thinking_level(level) {
add_reason(
&mut reasons,
diagnostic_reason(
PiDiagnosticLayer::Managed,
PiReasonCode::InvalidThinkingLevel,
&format!("{pointer}/thinkingLevelMap/{}", escape_json_pointer(level)),
),
);
}
}
}
reasons
}
fn managed_conversion_pointer(value: &Value) -> String {
if let Some(models) = value.get("models").and_then(Value::as_array) {
for (index, model) in models.iter().enumerate() {
if let Some(map) = model.get("thinkingLevelMap").and_then(Value::as_object) {
for (key, value) in map {
if !(value.is_string() || value.is_null()) {
return format!(
"/models/{index}/thinkingLevelMap/{}",
escape_json_pointer(key)
);
}
}
}
}
}
if let Some(overrides) = value.get("modelOverrides").and_then(Value::as_object) {
for (model_id, model_override) in overrides {
if let Some(map) = model_override
.get("thinkingLevelMap")
.and_then(Value::as_object)
{
for (key, value) in map {
if !(value.is_string() || value.is_null()) {
return format!(
"/modelOverrides/{}/thinkingLevelMap/{}",
escape_json_pointer(model_id),
escape_json_pointer(key)
);
}
}
}
}
}
String::new()
}
fn managed_validation_reason(error: PiConfigError) -> PiDiagnosticReason {
let (code, pointer) = match error {
PiConfigError::ProviderHasNoModels => (PiReasonCode::MissingExplicitModels, "/models"),
@@ -545,7 +485,7 @@ fn managed_validation_reason(error: PiConfigError) -> PiDiagnosticReason {
PiConfigError::NonPositiveModelLimit { .. } => {
(PiReasonCode::NonPositiveModelLimit, "/models")
}
PiConfigError::InvalidThinkingLevel { .. } => {
PiConfigError::InvalidThinkingLevelValue { .. } => {
(PiReasonCode::InvalidThinkingLevel, "/models")
}
};
@@ -710,13 +650,6 @@ fn valid_http_endpoint(value: &str) -> bool {
.is_some_and(|url| matches!(url.scheme(), "http" | "https") && url.host().is_some())
}
fn is_managed_thinking_level(value: &str) -> bool {
matches!(
value,
"off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max"
)
}
fn escape_json_pointer(value: &str) -> String {
value.replace('~', "~0").replace('/', "~1")
}
@@ -728,6 +661,7 @@ fn fingerprint(raw_source: &str) -> String {
#[cfg(test)]
mod tests {
use super::*;
use crate::pi_config::model::effective_pi_model;
use serde_json::json;
use std::fs;
@@ -862,7 +796,7 @@ mod tests {
}
#[test]
fn unknown_thinking_shape_is_lossless_for_composer_and_narrowed_separately() {
fn unknown_thinking_shape_is_lossless_through_managed_and_effective_boundaries() {
let temp = tempfile::tempdir().expect("tempdir");
let path = temp.path().join("models.json");
fs::write(
@@ -899,14 +833,31 @@ mod tests {
);
assert_eq!(
inspection.diagnostic.managed_assessment,
PiManagedAssessment::Unsupported
PiManagedAssessment::Manageable
);
let managed = serde_json::to_value(
inspection
.managed_config
.as_ref()
.expect("schema-valid managed config"),
)
.expect("serialize managed config");
assert_eq!(
managed.pointer("/models/0/thinkingLevelMap/future"),
Some(&json!({"opaque": true}))
);
let effective = serde_json::to_value(
effective_pi_model(
inspection.managed_config.as_ref().expect("managed config"),
"m",
)
.expect("effective model"),
)
.expect("serialize effective model");
assert_eq!(
effective.pointer("/thinkingLevelMap/future"),
Some(&json!({"opaque": true}))
);
assert!(has_reason(
&inspection.diagnostic,
PiDiagnosticLayer::Managed,
PiReasonCode::ManagedTypeConversionFailed,
"/models/0/thinkingLevelMap/future"
));
}
#[test]
@@ -0,0 +1,528 @@
#![cfg(test)]
//! 前置工程 C:只读 Native Inspection 认证测试套件
//!
//! ## 目标
//! **pinned Pi 决定什么是合法**。本仓库的 DTO 形状、网关支持范围、头部策略
//! 都不得成为"合法性"的来源:schema 接受的,managed 不得拒绝也不得丢值;
//! Pi 会发出的,网关不得降级;Pi 不接受的形态,我们也不假装支持。
//!
//! ## 规则
//! 实现方不得修改本文件(发现套件有错 → 上报裁决方);实现路径自选;全绿是
//! 盲审前置条件而非充分条件。既有测试不得弱化——以测试总数只增不减 + 盲审
//! 核对为准,不设扫描器。
//!
//! ## 三条裁决及其上游证据
//! C1【无损性】pinned schema 对 `thinkingLevelMap` 只约束 7 个标准键
//! (string|null;oracle 实证 `low: 2` 非法),额外键无约束(oracle 实证
//! `future: {nested:true}` 合法);`cost`/tier 同样接受未来键。managed 与
//! **effective 边界**(`effective_pi_model` 是 projection/routing/failover
//! 的共同入口)都必须无损,`{}` 与缺席必须保持可区分。
//! 据此取代两个既有测试中把收窄固化为断言的部分:
//! `managed_narrowing_rejects_duplicates_or_unknown_thinking_keys` 与
//! `unknown_thinking_shape_is_lossless_for_composer_and_narrowed_separately`
//! (授权改写、可改名;DuplicateModelId 与 composer 无损两个语义由本套件
//! 直接接管)。若 `InvalidThinkingLevel` 变体因此不再可构造,授权移除。
//! C2【认证头】authorization / x-api-key / x-goog-api-key 是候选认证头,
//! 不是 protected。取值次序据 pinned SDK 与 composer 源码:authHeader 未
//! 设时显式头优先于 apiKey 合成值(Anthropic/OpenAI SDK 按"合成 auth →
//! 显式 headers"合并,后项覆盖);authHeader:true 时合成 Bearer 反过来
//! 优先(pinned provider-composer 在自定义头之后写入,且只写
//! Authorization、不动 x-api-key)。Google 的 header-only 凭证不是 Pi 原生
//! 可请求形态(adapter 无条件要 apiKey),维持 MissingCredential 降级。
//! C3【传输层】放宽认证头不得连带放宽传输层:逐跳头完整覆盖并以 `proxy-`
//! **前缀**拒绝;契约 header 六分类中的 Gateway/HTTP owned(proxy trace /
//! CDN 客户端身份 / 分布式追踪)同样拒绝,清单与生产 forwarder 无条件
//! 剥离的集合对齐。
//!
//! ## 预期红绿
//! 应红 4:`certify_managed_losslessness_through_effective_boundary`、
//! `certify_auth_candidate_headers_are_not_protected`、
//! `certify_auth_header_bearer_overrides_explicit_authorization`、
//! `certify_transport_owned_headers_stay_protected`。
//! 应绿 3:夹具冻结、DuplicateModelId 保留、composer 无损。
//! 偏离(非清单红、应红变绿、编译失败)即上报。
//!
//! ## 残余
//! 显式优先只对 Anthropic/OpenAI 两族有 SDK 证据(Google 两值并存的优先级、
//! OpenAI-Completions、大小写变体未断言);transport oracle 只执行 resolver,
//! 不执行 adapter/SDK 头合并,主工程触碰数据面须先补 request-capture oracle;
//! 其余按盲审 finding 处理。
use super::composer::compose_explicit_custom_catalog;
use super::gateway::{assess_composition, PiGatewayCapability, PiGatewayReasonCode};
use super::model::{
effective_pi_model, validate_pi_managed_provider, PiConfigError, PiManagedAssessment,
PiManagedProviderConfig, PiManagementStatus, PiRawNativeValidity,
};
use super::native::inspect_pi_native_entry;
use super::raw_schema::evaluate_provider_value;
use serde_json::{json, Value};
use sha2::{Digest, Sha256};
use std::collections::BTreeMap;
use std::fs;
use std::path::{Path, PathBuf};
fn repo_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("workspace root")
.to_path_buf()
}
fn write_catalog(value: &Value) -> (tempfile::TempDir, PathBuf) {
let temp = tempfile::tempdir().expect("tempdir");
let path = temp.path().join("models.json");
fs::write(&path, serde_json::to_string_pretty(value).expect("encode")).expect("write");
(temp, path)
}
fn composed_catalog(value: Value) -> super::composer::PiNativeComposition {
let raw = evaluate_provider_value(&value);
compose_explicit_custom_catalog(
"candidate",
raw.valid_provider.as_ref().expect("raw-valid input"),
)
}
fn has_gateway_reason(
gateway: &super::gateway::PiGatewayAssessment,
code: PiGatewayReasonCode,
) -> bool {
gateway.reasons.iter().any(|reason| reason.code == code)
}
// ---------------------------------------------------------------------------
// 应绿:pinned 夹具冻结——oracle 是上游出处工件,不得为过测试再生成
// ---------------------------------------------------------------------------
const PINNED_FIXTURES: &[(&str, &str)] = &[
(
"tests/fixtures/pi/native-oracle/composer-oracle-v1.json",
"f7e54bb84e5fd6d50e5762dc304834410fa73ef608c2f9c42475c5983f8e0cf5",
),
(
"tests/fixtures/pi/native-oracle/field-coverage-v1.json",
"b8b85e611cf1dbef86c611df185ba8ac2d64160087d0c6e47747f838a0fafe42",
),
(
"tests/fixtures/pi/native-oracle/provenance-v1.json",
"6b2f9570ecc58d54ebe3da094530fee1c8c0d4a8265fa9c3199218582cb8dbcb",
),
(
"tests/fixtures/pi/native-oracle/provider-schema.snapshot.json",
"e498c9f1b344eee1bd3c3ba74d1b648dcb835378cfad92800ec80078b825745c",
),
(
"tests/fixtures/pi/native-oracle/raw-oracle-v1.json",
"5aaa37160f96a0fe50867d900ca38c73f13aba769e156a883324368d9dbeeb9a",
),
(
"tests/fixtures/pi/native-oracle/transport-oracle-v1.json",
"b2c816e53b60da5cd6352d2c23934939e9f6dd0077971488fe9dd36fa723e855",
),
(
"tests/fixtures/pi/module-boundaries-v1.json",
"a69ab84fc0db323d5eb8ddc63555a9c69613dda865962f077cd8691639951b4d",
),
];
#[test]
fn certify_pinned_fixtures_are_frozen() {
for (relative, expected) in PINNED_FIXTURES {
let bytes = fs::read(repo_root().join(relative))
.unwrap_or_else(|e| panic!("read fixture {relative}: {e}"));
assert_eq!(
&format!("{:x}", Sha256::digest(bytes)),
expected,
"pinned fixture '{relative}' drifted; fixtures are upstream provenance \
artifacts and may only change under adjudication"
);
}
}
// ---------------------------------------------------------------------------
// 应绿:被取代测试中必须保留的语义,由本套件直接接管
// ---------------------------------------------------------------------------
#[test]
fn certify_duplicate_model_id_rejection_is_preserved() {
let config: PiManagedProviderConfig = serde_json::from_value(json!({
"api": "anthropic-messages",
"baseUrl": "https://dup.example",
"apiKey": "literal",
"models": [{"id": "same"}, {"id": "same"}]
}))
.expect("deserialize managed provider");
assert_eq!(
validate_pi_managed_provider(&config),
Err(PiConfigError::DuplicateModelId("same".into())),
"duplicate model ids must keep being rejected"
);
}
#[test]
fn certify_composer_thinking_losslessness_guard() {
let odd_map = json!({"high": "h", "future": {"opaque": true}});
let composition = composed_catalog(json!({
"api": "anthropic-messages",
"baseUrl": "https://thinking.example",
"apiKey": "literal",
"models": [{"id": "m", "thinkingLevelMap": odd_map}]
}));
assert_eq!(
composition.models[0].thinking_level_map.as_ref(),
Some(&odd_map),
"composer keeps the raw thinkingLevelMap value verbatim"
);
}
// ---------------------------------------------------------------------------
// 应红(C1):schema 合法值必须无损直到 effective 边界
// ---------------------------------------------------------------------------
#[test]
fn certify_managed_losslessness_through_effective_boundary() {
// 标准键只取 schema 允许的 string|null;额外键覆盖全部 JSON 类型。
let model_map = json!({
"high": "native-high",
"medium": null,
"future-level": "textual",
"vendor": {"opaque": {"nested": true}},
"budget": 42,
"enabled": true
});
// 与 model_map 共有 "high",用于绑定 override 的覆盖方向。
let override_map = json!({
"high": "override-high",
"low": "override-low",
"another-future": [1, "two", null]
});
let cost = json!({
"input": 1.5,
"output": 2.5,
"cacheRead": 0.5,
"cacheWrite": 0.25,
"futureRate": 9.0,
"tiers": [{
"inputTokensAbove": 100.0,
"input": 1.0,
"output": 2.0,
"cacheRead": 0.5,
"cacheWrite": 0.25,
"futureTierField": "opaque"
}]
});
let catalog = json!({
"providers": {
"thinking": {
"api": "anthropic-messages",
"baseUrl": "https://thinking.example",
"apiKey": "literal",
"models": [
{"id": "m", "thinkingLevelMap": model_map.clone(), "cost": cost.clone()},
{"id": "empty-map", "thinkingLevelMap": {}},
{"id": "absent-map"}
],
"modelOverrides": {"m": {"thinkingLevelMap": override_map.clone()}}
}
}
});
let (_temp, path) = write_catalog(&catalog);
let inspection = inspect_pi_native_entry(&path, "thinking", &BTreeMap::new())
.expect("inspect")
.expect("entry present");
assert_eq!(
inspection.diagnostic.raw_validity,
PiRawNativeValidity::Valid,
"the pinned schema accepts additional thinkingLevelMap and cost members"
);
assert_eq!(
inspection.diagnostic.managed_assessment,
PiManagedAssessment::Manageable,
"managed must not reject what the executed pin accepts"
);
assert_eq!(
inspection.diagnostic.management_status,
PiManagementStatus::Importable
);
// 以序列化后的字符串码断言,便于 InvalidThinkingLevel 变体被整体移除。
let reasons = serde_json::to_value(&inspection.diagnostic.reasons).expect("serialize reasons");
assert!(
!reasons
.as_array()
.expect("reasons array")
.iter()
.any(|reason| reason["code"] == "invalid_thinking_level"),
"no invalid_thinking_level reason may fire for schema-valid input"
);
let managed = inspection.managed_config.expect("managed config");
let round_trip = serde_json::to_value(&managed).expect("serialize managed config");
assert_eq!(
round_trip.pointer("/models/0/thinkingLevelMap"),
Some(&model_map),
"model thinkingLevelMap must round-trip losslessly"
);
assert_eq!(
round_trip.pointer("/modelOverrides/m/thinkingLevelMap"),
Some(&override_map),
"override thinkingLevelMap must round-trip losslessly"
);
assert_eq!(
round_trip.pointer("/models/0/cost"),
Some(&cost),
"cost and tier members must round-trip losslessly, including future keys"
);
// 空对象与缺席是两种原生形态,序列化必须保持可区分。
assert_eq!(
round_trip.pointer("/models/1/thinkingLevelMap"),
Some(&json!({})),
"an explicitly empty thinkingLevelMap must survive as an empty object"
);
assert_eq!(
round_trip.pointer("/models/2/thinkingLevelMap"),
None,
"an absent thinkingLevelMap must stay absent"
);
// effective 是 projection / runtime / routing / failover 的共同入口:
// DTO 修好后在这里二次收窄同样是丢值。
let effective = effective_pi_model(&managed, "m").expect("effective model");
let effective_value = serde_json::to_value(&effective).expect("serialize effective model");
let mut merged = model_map.as_object().expect("model map").clone();
for (key, value) in override_map.as_object().expect("override map") {
merged.insert(key.clone(), value.clone());
}
assert_eq!(
effective_value.pointer("/thinkingLevelMap"),
Some(&Value::Object(merged)),
"the effective model must carry the merged map losslessly, with override \
entries winning on shared keys"
);
assert_eq!(
effective_value.pointer("/cost"),
Some(&cost),
"the effective model must not drop cost members either"
);
}
// ---------------------------------------------------------------------------
// 应红(C2):候选认证头不是 protected
// ---------------------------------------------------------------------------
#[test]
fn certify_auth_candidate_headers_are_not_protected() {
// (a) Anthropic:显式 x-api-key 不得被拒,取值优先于 apiKey 合成值。
let explicit = composed_catalog(json!({
"api": "anthropic-messages",
"baseUrl": "https://anthropic.example",
"apiKey": "synthesized-secret",
"headers": {"x-api-key": "explicit-secret"},
"models": [{"id": "m"}]
}));
let gateway = assess_composition(&explicit);
assert!(
!has_gateway_reason(&gateway, PiGatewayReasonCode::ProtectedHeader),
"x-api-key is candidate-auth, not protected"
);
assert_eq!(gateway.capability, PiGatewayCapability::Proxyable);
let materialized = gateway.plans[0]
.materialize(&|_: &str| None)
.expect("materialize literal candidate");
assert_eq!(
materialized.headers[&http::HeaderName::from_static("x-api-key")],
http::HeaderValue::from_static("explicit-secret"),
"explicit config header value takes precedence over synthesized family auth"
);
// 认证头永远不进 failover 协议身份。
if let Some((_, protocol_headers)) = materialized.failover_protocol_identity() {
assert!(
!protocol_headers.contains_key(&http::HeaderName::from_static("x-api-key")),
"auth headers must stay out of the failover protocol identity"
);
}
// (b) OpenAI-Responses:显式 authorization 同理。
let bearer = composed_catalog(json!({
"api": "openai-responses",
"baseUrl": "https://openai.example/v1",
"apiKey": "synthesized-secret",
"headers": {"authorization": "Bearer configured-token"},
"models": [{"id": "m"}]
}));
let gateway = assess_composition(&bearer);
assert!(
!has_gateway_reason(&gateway, PiGatewayReasonCode::ProtectedHeader),
"authorization is candidate-auth, not protected"
);
assert_eq!(gateway.capability, PiGatewayCapability::Proxyable);
assert_eq!(
gateway.plans[0]
.materialize(&|_: &str| None)
.expect("materialize")
.headers[&http::HeaderName::from_static("authorization")],
http::HeaderValue::from_static("Bearer configured-token")
);
// (c) Google:显式认证头与 apiKey 并存,不得拒绝、不得降级
// (取值优先级不断言——Google SDK 顺序无上游证据)。
let google = composed_catalog(json!({
"api": "google-generative-ai",
"baseUrl": "https://gemini.example",
"apiKey": "literal",
"headers": {"x-goog-api-key": "explicit-secret"},
"models": [{"id": "m"}]
}));
let gateway = assess_composition(&google);
assert!(
!has_gateway_reason(&gateway, PiGatewayReasonCode::ProtectedHeader),
"x-goog-api-key is candidate-auth, not protected"
);
assert_eq!(gateway.capability, PiGatewayCapability::Proxyable);
// (d) Google header-only 不是 Pi 原生可请求形态:维持降级,但认证头
// 依然不得被报为 ProtectedHeader。
let header_only = composed_catalog(json!({
"api": "google-generative-ai",
"baseUrl": "https://gemini.example",
"headers": {"x-goog-api-key": "header-secret"},
"models": [{"id": "m"}]
}));
let gateway = assess_composition(&header_only);
assert_eq!(
gateway.capability,
PiGatewayCapability::DirectOnly,
"header-only credentials stay DirectOnly, mirroring pinned Pi"
);
assert!(has_gateway_reason(
&gateway,
PiGatewayReasonCode::MissingCredential
));
assert!(
!has_gateway_reason(&gateway, PiGatewayReasonCode::ProtectedHeader),
"an auth-candidate header must not be reported as protected even when the \
credential is missing"
);
}
// ---------------------------------------------------------------------------
// 应红(C2):authHeader:true 时合成 Bearer 覆盖显式 Authorization
// ---------------------------------------------------------------------------
#[test]
fn certify_auth_header_bearer_overrides_explicit_authorization() {
let composition = composed_catalog(json!({
"api": "anthropic-messages",
"baseUrl": "https://anthropic.example",
"apiKey": "synthesized-secret",
"authHeader": true,
"headers": {"authorization": "Bearer explicit-token"},
"models": [{"id": "m"}]
}));
let gateway = assess_composition(&composition);
assert_eq!(
gateway.capability,
PiGatewayCapability::Proxyable,
"an explicit authorization header must not downgrade an authHeader model"
);
let materialized = gateway.plans[0]
.materialize(&|_: &str| None)
.expect("materialize literal candidate");
assert_eq!(
materialized.headers[&http::HeaderName::from_static("authorization")],
http::HeaderValue::from_static("Bearer synthesized-secret"),
"with authHeader:true the synthesized Bearer wins (pinned composer writes it \
after the explicit headers)"
);
assert_eq!(
materialized.headers[&http::HeaderName::from_static("x-api-key")],
http::HeaderValue::from_static("synthesized-secret"),
"the Bearer step only rewrites Authorization; family auth stays synthesized"
);
}
// ---------------------------------------------------------------------------
// 应红(C3):传输层与网关自有身份头
// ---------------------------------------------------------------------------
/// 逐跳/传输头。末四项是合成名字:精确枚举无法覆盖,必须按 `proxy-` 前缀拒绝。
const HOP_BY_HOP_HEADERS: &[&str] = &[
"host",
"connection",
"content-length",
"transfer-encoding",
"te",
"trailer",
"upgrade",
"keep-alive",
"proxy-authenticate",
"proxy-authorization",
"proxy-connection",
"proxy-future-extension",
"proxy-tenant-routing",
"proxy-x9",
];
/// Gateway/HTTP owned:proxy trace / CDN 客户端身份 / 分布式追踪。
/// 与生产 forwarder 无条件剥离的集合对齐,两侧同进退。
const GATEWAY_OWNED_HEADERS: &[&str] = &[
"forwarded",
"x-forwarded-for",
"x-forwarded-host",
"x-forwarded-port",
"x-forwarded-proto",
"x-real-ip",
"cf-connecting-ip",
"cf-ipcountry",
"cf-ray",
"cf-visitor",
"true-client-ip",
"fastly-client-ip",
"x-azure-clientip",
"x-azure-fdid",
"x-azure-ref",
"akamai-origin-hop",
"x-akamai-config-log-detail",
"x-request-id",
"x-correlation-id",
"x-trace-id",
"x-amzn-trace-id",
"x-b3-traceid",
"x-b3-spanid",
"x-b3-parentspanid",
"x-b3-sampled",
"traceparent",
"tracestate",
];
#[test]
fn certify_transport_owned_headers_stay_protected() {
let cases = HOP_BY_HOP_HEADERS
.iter()
.map(|header| ("hop-by-hop", *header))
.chain(
GATEWAY_OWNED_HEADERS
.iter()
.map(|header| ("gateway-owned", *header)),
);
for (class, header) in cases {
let composition = composed_catalog(json!({
"api": "openai-responses",
"baseUrl": "https://openai.example/v1",
"apiKey": "literal",
"headers": {header: "value"},
"models": [{"id": "m"}]
}));
let gateway = assess_composition(&composition);
assert!(
has_gateway_reason(&gateway, PiGatewayReasonCode::ProtectedHeader),
"{class} header '{header}' must be reported as ProtectedHeader"
);
assert_eq!(
gateway.capability,
PiGatewayCapability::DirectOnly,
"{class} header '{header}' must keep the model DirectOnly"
);
}
}