use crate::middleware::{Middleware, MiddlewareError, Params};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PrivacyTier {
Confidential,
Frontier,
}
impl PrivacyTier {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
PrivacyTier::Confidential => "Confidential",
PrivacyTier::Frontier => "Frontier",
}
}
#[must_use]
fn downgrades_to(self, other: PrivacyTier) -> bool {
matches!(
(self, other),
(PrivacyTier::Confidential, PrivacyTier::Frontier)
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ModelSpec {
pub id: String,
pub tier: PrivacyTier,
}
impl ModelSpec {
#[must_use]
pub fn new(id: impl Into<String>, tier: PrivacyTier) -> Self {
Self {
id: id.into(),
tier,
}
}
}
#[derive(Debug, Clone)]
pub struct TierPolicy {
id: String,
}
impl TierPolicy {
#[must_use]
pub fn new(id: impl Into<String>) -> Self {
Self { id: id.into() }
}
}
impl Default for TierPolicy {
fn default() -> Self {
Self::new("native:tier-policy")
}
}
impl Middleware for TierPolicy {
fn id(&self) -> &str {
&self.id
}
fn transform_params(&self, params: Params) -> Result<Params, MiddlewareError> {
let Some(primary) = params.primary.as_ref() else {
return Ok(params);
};
if params.consent_cross_tier {
return Ok(params);
}
let candidates = std::iter::once(primary)
.chain(params.fallback.iter())
.chain(params.fusion.iter());
for candidate in candidates {
if primary.tier.downgrades_to(candidate.tier) {
return Err(downgrade_error(primary.tier, candidate));
}
}
Ok(params)
}
}
fn downgrade_error(from: PrivacyTier, to: &ModelSpec) -> MiddlewareError {
MiddlewareError::Blocked {
detail: format!(
"cross-tier downgrade without consent: {} request would route to \
{} model \"{}\" — set consent_cross_tier to allow",
from.as_str(),
to.tier.as_str(),
to.id
),
}
}
#[cfg(test)]
mod tests {
#![allow(
clippy::unwrap_used,
clippy::expect_used,
reason = "tests assert on known-good values"
)]
use crate::middleware::{MiddlewareChain, Model};
use super::*;
struct Echo;
impl Model for Echo {
fn generate(&self, params: &Params) -> Result<String, MiddlewareError> {
Ok(params.prompt.clone())
}
}
fn confidential(id: &str) -> ModelSpec {
ModelSpec::new(id, PrivacyTier::Confidential)
}
fn frontier(id: &str) -> ModelSpec {
ModelSpec::new(id, PrivacyTier::Frontier)
}
fn run(params: Params) -> Result<String, MiddlewareError> {
let policy = TierPolicy::default();
let chain = MiddlewareChain::new(vec![&policy]);
chain.generate(params, &Echo)
}
#[test]
fn same_tier_fallback_passes() {
let params = Params::new("hi")
.with_primary(confidential("tee-a"))
.with_fallback(vec![confidential("tee-b")]);
assert!(run(params).is_ok());
}
#[test]
fn confidential_to_frontier_fallback_blocks_without_consent() {
let params = Params::new("hi")
.with_primary(confidential("tee-a"))
.with_fallback(vec![frontier("gpt-x")]);
let err = run(params).unwrap_err();
let MiddlewareError::Blocked { detail } = err;
assert!(
detail.contains("Confidential"),
"names source tier: {detail}"
);
assert!(detail.contains("Frontier"), "names target tier: {detail}");
assert!(detail.contains("gpt-x"), "names offending model: {detail}");
}
#[test]
fn confidential_to_frontier_fallback_passes_with_consent() {
let params = Params::new("hi")
.with_primary(confidential("tee-a"))
.with_fallback(vec![frontier("gpt-x")])
.with_cross_tier_consent(true);
assert!(run(params).is_ok());
}
#[test]
fn fusion_set_spanning_tiers_blocks_without_consent() {
let params = Params::new("hi")
.with_primary(confidential("tee-a"))
.with_fusion(vec![confidential("tee-b"), frontier("claude-x")]);
let err = run(params).unwrap_err();
let MiddlewareError::Blocked { detail } = err;
assert!(detail.contains("Confidential") && detail.contains("Frontier"));
assert!(detail.contains("claude-x"));
}
#[test]
fn all_confidential_fusion_passes() {
let params = Params::new("hi")
.with_primary(confidential("tee-a"))
.with_fusion(vec![confidential("tee-b"), confidential("tee-c")]);
assert!(run(params).is_ok());
}
#[test]
fn frontier_to_confidential_upgrade_passes() {
let params = Params::new("hi")
.with_primary(frontier("gpt-x"))
.with_fallback(vec![confidential("tee-a")]);
assert!(run(params).is_ok());
}
#[test]
fn frontier_only_passes() {
let params = Params::new("hi")
.with_primary(frontier("gpt-x"))
.with_fallback(vec![frontier("claude-x")]);
assert!(run(params).is_ok());
}
#[test]
fn no_primary_is_pass_through() {
assert!(run(Params::new("hi")).is_ok());
}
}