fix(pi): fail closed on unrepresentable compat spread

This commit is contained in:
SaladDay
2026-08-02 16:16:14 +00:00
parent bd3e2e54fc
commit d2ad7995a3
6 changed files with 511 additions and 34 deletions
+53
View File
@@ -466,6 +466,13 @@ fn collect_managed_reasons(config: &PiManagedProviderConfig) -> Vec<PiDiagnostic
}
fn managed_validation_reason(error: PiConfigError) -> PiDiagnosticReason {
if let PiConfigError::UnrepresentableCompat { json_pointer } = &error {
return diagnostic_reason(
PiDiagnosticLayer::Managed,
PiReasonCode::UnrepresentableCompat,
json_pointer,
);
}
let (code, pointer) = match error {
PiConfigError::ProviderHasNoModels => (PiReasonCode::MissingExplicitModels, "/models"),
PiConfigError::EmptyApiId => (PiReasonCode::ManagedTypeConversionFailed, "/api"),
@@ -481,6 +488,9 @@ fn managed_validation_reason(error: PiConfigError) -> PiDiagnosticReason {
(PiReasonCode::UnknownModelOverride, "/modelOverrides")
}
PiConfigError::InvalidCompat { .. } => (PiReasonCode::InvalidCompat, "/compat"),
PiConfigError::UnrepresentableCompat { .. } => {
unreachable!("handled before the exhaustive mapping")
}
PiConfigError::EmptyOptionalField { .. } => (PiReasonCode::EmptyOptionalField, ""),
PiConfigError::NonPositiveModelLimit { .. } => {
(PiReasonCode::NonPositiveModelLimit, "/models")
@@ -585,6 +595,9 @@ fn map_composer_reasons(composition: &PiNativeComposition) -> Vec<PiDiagnosticRe
PiComposerReasonCode::NonPositiveModelLimit => {
PiReasonCode::NonPositiveModelLimit
}
PiComposerReasonCode::UnrepresentableCompat => {
PiReasonCode::UnrepresentableCompat
}
PiComposerReasonCode::CompositionFailed => PiReasonCode::CompositionFailed,
},
&reason.json_pointer,
@@ -760,6 +773,46 @@ mod tests {
assert_eq!(malformed.gateway_status, PiGatewayStatus::Unknown);
}
#[test]
fn public_inspection_fails_closed_for_unrepresentable_compat_spread() {
let temp = tempfile::tempdir().expect("tempdir");
let path = temp.path().join("models.json");
fs::write(
&path,
r#"{"providers":{"surrogate":{
"api":"openai-responses",
"baseUrl":"https://compat.example/v1",
"apiKey":"literal",
"compat":{"chatTemplateKwargs":"😀"},
"models":[{"id":"m"}],
"modelOverrides":{"m":{"compat":{"chatTemplateKwargs":{"named":true}}}}
}}}"#,
)
.expect("write");
let diagnostic =
&PiNativeInspectionService::inspect_catalog(&path, &BTreeMap::new()).unwrap()[0];
assert_eq!(diagnostic.raw_validity, PiRawNativeValidity::Valid);
assert_eq!(
diagnostic.managed_assessment,
PiManagedAssessment::Unsupported
);
assert_eq!(diagnostic.composition_status, PiCompositionStatus::Unknown);
assert_eq!(diagnostic.gateway_status, PiGatewayStatus::Unknown);
assert!(has_reason(
diagnostic,
PiDiagnosticLayer::Managed,
PiReasonCode::UnrepresentableCompat,
"/modelOverrides/m/compat"
));
assert!(has_reason(
diagnostic,
PiDiagnosticLayer::Composition,
PiReasonCode::UnrepresentableCompat,
"/modelOverrides/m/compat"
));
}
#[test]
fn managed_rejection_does_not_control_raw_composition() {
let temp = tempfile::tempdir().expect("tempdir");