use thiserror::Error;
use crate::Config;
use crate::connection::{
BindingId, BindingOrigin, ConnectionTargetError, ProviderBinding, RealmChain,
RealmConnectionSet, RealmId, ResolvedConnectionTarget, materialize_connection_target,
};
use crate::provider::Provider;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SelfHostedBindingServer {
Declared { server: String },
DerivedFromDefaultModel { server: String },
Conflicting {
declared: String,
default_model_server: String,
},
Undeclared,
}
impl SelfHostedBindingServer {
#[must_use]
pub fn server(&self) -> Option<&str> {
match self {
Self::Declared { server } | Self::DerivedFromDefaultModel { server } => {
Some(server.as_str())
}
Self::Conflicting { .. } | Self::Undeclared => None,
}
}
#[must_use]
pub fn matches(&self, server_id: &str) -> bool {
self.server() == Some(server_id)
}
#[must_use]
pub fn is_undeclared(&self) -> bool {
matches!(self, Self::Undeclared)
}
}
impl std::fmt::Display for SelfHostedBindingServer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Declared { server } => write!(f, "server '{server}' (declared)"),
Self::DerivedFromDefaultModel { server } => {
write!(f, "server '{server}' (from default_model)")
}
Self::Conflicting {
declared,
default_model_server,
} => write!(
f,
"conflicting servers (backend declares '{declared}', default_model serves '{default_model_server}')"
),
Self::Undeclared => f.write_str("no declared server"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SelfHostedBindingCandidate {
pub realm: RealmId,
pub binding: BindingId,
pub server: SelfHostedBindingServer,
}
impl std::fmt::Display for SelfHostedBindingCandidate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}:{} -> {}",
self.realm.as_str(),
self.binding.as_str(),
self.server
)
}
}
struct ConsideredList<'a>(&'a [SelfHostedBindingCandidate]);
impl std::fmt::Display for ConsideredList<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (idx, candidate) in self.0.iter().enumerate() {
if idx > 0 {
f.write_str("; ")?;
}
write!(f, "{candidate}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, Error, PartialEq, Eq)]
pub enum SelfHostedConnectionError {
#[error(
"self-hosted server '{server}' has no credential binding: no realm on the '{head}' chain \
declares a binding for provider 'self_hosted'. Declare a realm backend with \
provider = \"self_hosted\", server = \"{server}\" plus an auth profile and binding, \
or select one explicitly with an auth binding."
)]
NoSelfHostedBindings { server: String, head: String },
#[error(
"no credential binding for self-hosted server '{server}': considered {}. \
Add server = \"{server}\" to the backend profile of the binding that authenticates it, \
or select it explicitly with an auth binding.",
ConsideredList(.considered)
)]
NoBindingForServer {
server: String,
considered: Vec<SelfHostedBindingCandidate>,
},
#[error(
"cannot tell which credential binding authenticates self-hosted server '{server}': \
{} unconstrained bindings are reachable and none names a server: considered {}. \
Add server = \"{server}\" to the backend profile of the binding that authenticates it, \
or select it explicitly with an auth binding.",
.considered.iter().filter(|candidate| candidate.server.is_undeclared()).count(),
ConsideredList(.considered)
)]
AmbiguousServerBinding {
server: String,
considered: Vec<SelfHostedBindingCandidate>,
},
#[error(
"auth binding '{realm}:{binding}' declares self-hosted server '{declared}', but model \
'{model}' is served by '{server}'; the binding's credential does not authenticate that \
endpoint"
)]
ExplicitBindingServerMismatch {
realm: String,
binding: String,
declared: String,
server: String,
model: String,
},
#[error(transparent)]
ConnectionTarget(#[from] ConnectionTargetError),
}
#[must_use]
pub fn self_hosted_binding_server(
config: &Config,
realm: &RealmConnectionSet,
binding: &ProviderBinding,
) -> SelfHostedBindingServer {
let declared = realm
.backends
.get(&binding.backend_profile)
.and_then(|backend| backend.server.clone());
let from_default_model = binding
.default_model
.as_deref()
.and_then(|model| config.self_hosted.models.get(model))
.map(|model| model.server.clone())
.filter(|server| !server.is_empty());
match (declared, from_default_model) {
(Some(declared), Some(derived)) if declared != derived => {
SelfHostedBindingServer::Conflicting {
declared,
default_model_server: derived,
}
}
(Some(server), _) => SelfHostedBindingServer::Declared { server },
(None, Some(server)) => SelfHostedBindingServer::DerivedFromDefaultModel { server },
(None, None) => SelfHostedBindingServer::Undeclared,
}
}
fn ordered_self_hosted_bindings(realm: &RealmConnectionSet) -> Vec<&ProviderBinding> {
let mut bindings: Vec<&ProviderBinding> = realm
.bindings
.values()
.filter(|binding| {
let backend_is_self_hosted = realm
.backends
.get(&binding.backend_profile)
.is_some_and(|backend| backend.provider == Provider::SelfHosted);
let auth_is_self_hosted = realm
.auth_profiles
.get(&binding.auth_profile)
.is_some_and(|auth| auth.provider == Provider::SelfHosted);
backend_is_self_hosted && auth_is_self_hosted
})
.collect();
bindings.sort_by_key(|binding| {
if realm.default_binding.as_deref() == Some(binding.id.as_str()) {
0
} else if binding.provider_default {
1
} else {
2
}
});
bindings
}
fn collect_self_hosted_candidates(
config: &Config,
head: &RealmId,
) -> Result<Vec<(RealmConnectionSet, SelfHostedBindingCandidate)>, SelfHostedConnectionError> {
let chain = RealmChain::resolve(config, head).map_err(ConnectionTargetError::from)?;
let mut out = Vec::new();
for member in chain.realms() {
let Some(section) = config.realm.get(member.as_str()) else {
continue;
};
let Ok(realm) = RealmConnectionSet::from_config(member.as_str(), section) else {
continue;
};
for binding in ordered_self_hosted_bindings(&realm) {
let Ok(binding_id) = BindingId::parse(binding.id.clone()) else {
continue;
};
let server = self_hosted_binding_server(config, &realm, binding);
out.push((
realm.clone(),
SelfHostedBindingCandidate {
realm: realm.realm_id.clone(),
binding: binding_id,
server,
},
));
}
}
Ok(out)
}
pub fn resolve_self_hosted_binding_for_server(
config: &Config,
server_id: &str,
preferred_realm: Option<&RealmId>,
) -> Result<ResolvedConnectionTarget, SelfHostedConnectionError> {
let global = RealmId::global();
let head = preferred_realm.unwrap_or(&global);
let candidates = collect_self_hosted_candidates(config, head)?;
if let Some((realm, candidate)) = candidates
.iter()
.find(|(_, candidate)| candidate.server.matches(server_id))
{
return materialize(realm.clone(), candidate.binding.clone());
}
let considered: Vec<SelfHostedBindingCandidate> = candidates
.iter()
.map(|(_, candidate)| candidate.clone())
.collect();
if considered.is_empty() {
return Err(SelfHostedConnectionError::NoSelfHostedBindings {
server: server_id.to_string(),
head: head.as_str().to_string(),
});
}
let mut unconstrained = candidates
.iter()
.filter(|(_, candidate)| candidate.server.is_undeclared());
match (unconstrained.next(), unconstrained.next()) {
(Some((realm, candidate)), None) => materialize(realm.clone(), candidate.binding.clone()),
(Some(_), Some(_)) => Err(SelfHostedConnectionError::AmbiguousServerBinding {
server: server_id.to_string(),
considered,
}),
_ => Err(SelfHostedConnectionError::NoBindingForServer {
server: server_id.to_string(),
considered,
}),
}
}
fn materialize(
realm: RealmConnectionSet,
binding: BindingId,
) -> Result<ResolvedConnectionTarget, SelfHostedConnectionError> {
materialize_connection_target(
realm,
Some(Provider::SelfHosted),
binding,
None,
BindingOrigin::Configured,
)
.map_err(SelfHostedConnectionError::ConnectionTarget)
}
pub fn validate_explicit_self_hosted_target(
config: &Config,
target: &ResolvedConnectionTarget,
server_id: &str,
model: &str,
) -> Result<(), SelfHostedConnectionError> {
let identity = self_hosted_binding_server(config, &target.realm, &target.binding);
let declared = match &identity {
SelfHostedBindingServer::Declared { server } => server.clone(),
SelfHostedBindingServer::Conflicting { declared, .. } => declared.clone(),
SelfHostedBindingServer::DerivedFromDefaultModel { .. }
| SelfHostedBindingServer::Undeclared => return Ok(()),
};
if declared == server_id {
return Ok(());
}
Err(SelfHostedConnectionError::ExplicitBindingServerMismatch {
realm: target.auth_binding.realm.as_str().to_string(),
binding: target.auth_binding.binding.as_str().to_string(),
declared,
server: server_id.to_string(),
model: model.to_string(),
})
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use super::*;
use crate::config::{
SelfHostedApiStyle, SelfHostedModelConfig, SelfHostedServerConfig, SelfHostedTransport,
};
use crate::connection::{
AuthProfileConfig, BackendProfileConfig, CredentialSourceSpec, ProviderBindingConfig,
};
fn model(server: &str) -> SelfHostedModelConfig {
SelfHostedModelConfig {
server: server.to_string(),
remote_model: "remote".to_string(),
..Default::default()
}
}
fn base_config() -> Config {
let mut config = Config::default();
for (id, base_url) in [
("muse_vllm", "http://muse.invalid:8000"),
("cerebras", "https://api.cerebras.invalid/v1"),
] {
config.self_hosted.servers.insert(
id.to_string(),
SelfHostedServerConfig {
transport: SelfHostedTransport::OpenAiCompatible,
base_url: base_url.to_string(),
api_style: SelfHostedApiStyle::ChatCompletions,
},
);
}
config
.self_hosted
.models
.insert("muse-glimmer-30b".to_string(), model("muse_vllm"));
config
.self_hosted
.models
.insert("cerebras-gpt-oss-120b".to_string(), model("cerebras"));
config
}
fn add_binding(
config: &mut Config,
realm_id: &str,
binding_id: &str,
declared_server: Option<&str>,
default_model: Option<&str>,
realm_default: bool,
) {
let backend_id = format!("{binding_id}_backend");
let auth_id = format!("{binding_id}_auth");
let realm = config.realm.entry(realm_id.to_string()).or_default();
realm.backend.insert(
backend_id.clone(),
BackendProfileConfig {
provider: "self_hosted".to_string(),
backend_kind: "self_hosted".to_string(),
base_url: None,
options: serde_json::Value::Null,
server: declared_server.map(str::to_string),
},
);
realm.auth.insert(
auth_id.clone(),
AuthProfileConfig {
provider: "self_hosted".to_string(),
auth_method: "static_bearer".to_string(),
source: CredentialSourceSpec::InlineSecret {
secret: format!("{binding_id}-secret"),
},
constraints: Default::default(),
metadata_defaults: Default::default(),
},
);
realm.binding.insert(
binding_id.to_string(),
ProviderBindingConfig {
backend_profile: backend_id,
auth_profile: auth_id,
credential_account: None,
default_model: default_model.map(str::to_string),
policy: Default::default(),
provider_default: false,
},
);
if realm_default {
realm.default_binding = Some(binding_id.to_string());
}
}
fn realm(id: &str) -> RealmId {
RealmId::parse(id).unwrap()
}
#[test]
fn declared_server_outranks_realm_default_binding() {
let mut config = base_config();
add_binding(&mut config, "ws", "cerebras", Some("cerebras"), None, true);
add_binding(&mut config, "ws", "muse", Some("muse_vllm"), None, false);
let target =
resolve_self_hosted_binding_for_server(&config, "muse_vllm", Some(&realm("ws")))
.expect("the binding declaring the server must resolve");
assert_eq!(target.auth_binding.binding.as_str(), "muse");
assert_eq!(
target.auth_profile.source,
CredentialSourceSpec::InlineSecret {
secret: "muse-secret".to_string()
}
);
}
#[test]
fn declaration_outranks_default_model_inference_on_the_same_binding() {
let mut config = base_config();
add_binding(
&mut config,
"ws",
"mixed",
Some("muse_vllm"),
Some("cerebras-gpt-oss-120b"),
true,
);
let realm_set = RealmConnectionSet::from_config("ws", &config.realm["ws"]).unwrap();
let identity =
self_hosted_binding_server(&config, &realm_set, &realm_set.bindings["mixed"]);
assert_eq!(
identity,
SelfHostedBindingServer::Conflicting {
declared: "muse_vllm".to_string(),
default_model_server: "cerebras".to_string(),
}
);
assert!(!identity.matches("muse_vllm"));
assert!(!identity.matches("cerebras"));
let err = resolve_self_hosted_binding_for_server(&config, "muse_vllm", Some(&realm("ws")))
.expect_err("a contradicting binding must not authenticate either server");
let message = err.to_string();
assert!(
message.contains("muse_vllm") && message.contains("ws:mixed"),
"unexpected message: {message}"
);
}
#[test]
fn single_unconstrained_binding_resolves_any_server() {
let mut config = base_config();
add_binding(&mut config, "ws", "local", None, None, true);
for server in ["muse_vllm", "cerebras"] {
let target =
resolve_self_hosted_binding_for_server(&config, server, Some(&realm("ws")))
.expect("the single unconstrained binding stays usable");
assert_eq!(target.auth_binding.binding.as_str(), "local");
}
}
#[test]
fn several_unconstrained_bindings_fail_closed_naming_considered() {
let mut config = base_config();
add_binding(&mut config, "ws", "one", None, None, true);
add_binding(&mut config, "global", "two", None, None, false);
let err = resolve_self_hosted_binding_for_server(&config, "muse_vllm", Some(&realm("ws")))
.expect_err("guessing between two unconstrained bindings is the defect");
assert!(matches!(
err,
SelfHostedConnectionError::AmbiguousServerBinding { .. }
));
let message = err.to_string();
for expected in ["muse_vllm", "ws:one", "global:two"] {
assert!(
message.contains(expected),
"message must contain {expected}: {message}"
);
}
}
#[test]
fn no_self_hosted_binding_at_all_names_the_server() {
let config = base_config();
let err = resolve_self_hosted_binding_for_server(&config, "muse_vllm", Some(&realm("ws")))
.expect_err("no binding anywhere must fail closed");
assert!(matches!(
err,
SelfHostedConnectionError::NoSelfHostedBindings { .. }
));
assert!(err.to_string().contains("muse_vllm"), "{err}");
}
#[test]
fn absent_head_realm_still_inherits_the_chain() {
let mut config = base_config();
add_binding(
&mut config,
"global",
"muse",
Some("muse_vllm"),
None,
false,
);
let target = resolve_self_hosted_binding_for_server(
&config,
"muse_vllm",
Some(&realm("unmaterialized")),
)
.expect("an unmaterialized session realm still inherits global");
assert_eq!(target.auth_binding.realm.as_str(), "global");
}
#[test]
fn explicit_target_declaring_another_server_is_rejected() {
let mut config = base_config();
add_binding(&mut config, "ws", "cerebras", Some("cerebras"), None, true);
let target =
resolve_self_hosted_binding_for_server(&config, "cerebras", Some(&realm("ws")))
.expect("its own server resolves");
validate_explicit_self_hosted_target(&config, &target, "cerebras", "cerebras-gpt-oss-120b")
.expect("matching server passes");
let err =
validate_explicit_self_hosted_target(&config, &target, "muse_vllm", "muse-glimmer-30b")
.expect_err("a declared other server must be rejected");
let message = err.to_string();
for expected in ["muse_vllm", "cerebras", "muse-glimmer-30b"] {
assert!(
message.contains(expected),
"message must contain {expected}: {message}"
);
}
}
#[test]
fn explicit_target_without_a_declaration_is_accepted() {
let mut config = base_config();
add_binding(&mut config, "ws", "local", None, None, true);
let target =
resolve_self_hosted_binding_for_server(&config, "cerebras", Some(&realm("ws")))
.expect("unconstrained binding resolves");
validate_explicit_self_hosted_target(&config, &target, "muse_vllm", "muse-glimmer-30b")
.expect("an unconstrained binding remains the operator's escape hatch");
}
}