use std::env;
use vyre_driver::backend::{backend_dispatches, registered_backends_by_precedence_slice};
use vyre_driver::{BackendError, BackendRegistration};
use vyre_foundation::ir::Program;
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum Override<'a> {
FromEnv,
Explicit(&'a str),
None,
}
const OVERRIDE_ENV: &str = "VYRE_BACKEND";
#[derive(Debug, Clone)]
pub struct RouterDecision {
pub backend: &'static str,
pub reason: Reason,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Reason {
EnvOverride,
Precedence,
}
#[derive(Default)]
pub struct BackendRouter;
impl BackendRouter {
#[must_use]
pub fn new() -> Self {
Self
}
pub fn pick(&self, program: &Program) -> Result<RouterDecision, BackendError> {
self.pick_with_override(program, Override::FromEnv)
}
pub fn pick_with_override(
&self,
_program: &Program,
source: Override<'_>,
) -> Result<RouterDecision, BackendError> {
let registered = vyre_driver::backend::registered_backends();
let forced: Option<String> = match source {
Override::FromEnv => env::var(OVERRIDE_ENV).ok(),
Override::Explicit(s) => Some(s.to_owned()),
Override::None => None,
};
if let Some(forced) = forced {
let forced = forced.trim();
if !forced.is_empty() {
let hit = registered.iter().find(|r| {
r.id == forced && backend_dispatches(r.id) && !is_reference_oracle_backend(r.id)
});
return match hit {
Some(reg) => Ok(RouterDecision {
backend: reg.id,
reason: Reason::EnvOverride,
}),
None => Err(BackendError::new(format!(
"VYRE_BACKEND={forced} is not an executable registered GPU backend. Fix: link CUDA/WGPU or unset VYRE_BACKEND; cpu-ref/reference are explicit conformance oracles, not runtime router targets."
))),
};
}
}
for reg in registered_backends_by_precedence_slice() {
if registered.iter().any(|r| r.id == reg.id)
&& backend_dispatches(reg.id)
&& !is_reference_oracle_backend(reg.id)
{
return Ok(RouterDecision {
backend: reg.id,
reason: Reason::Precedence,
});
}
}
Err(BackendError::new(
"no executable GPU backend is registered. Fix: link vyre-driver-cuda or vyre-driver-wgpu into the binary and verify the GPU driver probe succeeds.",
))
}
#[must_use]
pub fn enumerate_by_precedence() -> Vec<&'static BackendRegistration> {
registered_backends_by_precedence_slice().to_vec()
}
}
fn is_reference_oracle_backend(id: &str) -> bool {
matches!(id, "cpu-ref" | "reference")
}
#[cfg(test)]
mod tests {
use super::*;
use vyre_driver::backend::backend_precedence;
fn noop_program() -> Program {
Program::wrapped(Vec::new(), [1, 1, 1], Vec::new())
}
#[test]
fn enumerate_by_precedence_puts_wgpu_before_reference() {
let wgpu_rank = backend_precedence("wgpu");
let ref_rank = backend_precedence("cpu-ref");
assert!(
wgpu_rank < ref_rank || ref_rank == u32::MAX,
"wgpu (rank {wgpu_rank}) must take precedence over the CPU reference oracle (rank {ref_rank})"
);
}
#[test]
fn enumerate_by_precedence_is_inventory_driven() {
let ranked = BackendRouter::enumerate_by_precedence();
let wgpu = ranked.iter().find(|r| r.id == "wgpu").expect(
"Fix: wgpu backend registered in this crate; restore this invariant before continuing.",
);
assert_eq!(backend_precedence(wgpu.id), 30);
}
#[test]
fn explicit_override_with_unknown_backend_surfaces_error() {
let router = BackendRouter::new();
let err = router
.pick_with_override(
&noop_program(),
Override::Explicit("does-not-exist-backend"),
)
.expect_err("unknown backend must error");
let msg = format!("{err}");
assert!(msg.contains("does-not-exist-backend"));
assert!(msg.contains("Fix:"));
}
#[test]
fn explicit_override_picks_the_named_backend_when_registered() {
let router = BackendRouter::new();
let decision = router
.pick_with_override(&noop_program(), Override::Explicit("wgpu"))
.expect("Fix: wgpu backend is registered in this crate");
assert_eq!(decision.backend, "wgpu");
assert_eq!(decision.reason, Reason::EnvOverride);
}
#[test]
fn precedence_picks_wgpu_when_registered() {
let router = BackendRouter::new();
let decision = router
.pick_with_override(&noop_program(), Override::None)
.expect("Fix: at least one backend must register");
assert_eq!(decision.reason, Reason::Precedence);
assert!(
backend_precedence(decision.backend) < u32::MAX,
"picked backend {} did not submit a BackendPrecedence inventory entry",
decision.backend
);
}
}