omniference 0.1.1

A multi-protocol inference engine with provider adapters
Documentation
use crate::{stream::StreamEvent, types::ChatRequestIR, types::DiscoveredModel};
use async_trait::async_trait;
use futures_util::Stream;

#[async_trait]
pub trait ChatAdapter: Send + Sync {
    fn provider_kind(&self) -> crate::types::ProviderKind;

    async fn execute_chat(
        &self,
        ir: ChatRequestIR,
        cancel: tokio_util::sync::CancellationToken,
    ) -> Result<Box<dyn Stream<Item = StreamEvent> + Send + Unpin>, AdapterError>;

    async fn discover_models(
        &self,
        _provider_name: &str,
        _endpoint: &crate::types::ProviderEndpoint,
    ) -> Result<Vec<DiscoveredModel>, AdapterError> {
        Ok(Vec::new())
    }

    fn resolve_adapter_model_id(&self, model_id: &str, provider_name: &str) -> String {
        if model_id.starts_with(provider_name.to_lowercase().as_str()) {
            model_id.split_once('/').unwrap().1.to_string()
        } else {
            model_id.to_string()
        }
    }
}

#[derive(thiserror::Error, Debug)]
pub enum AdapterError {
    #[error("http error: {0}")]
    Http(String),
    #[error("provider error: {code} {message}")]
    Provider { code: String, message: String },
    #[error("invalid request: {0}")]
    Invalid(String),
    #[error("timeout")]
    Timeout,
    #[error("internal: {0}")]
    Internal(String),
}

impl AdapterError {
    pub fn http<S: Into<String>>(msg: S) -> Self {
        AdapterError::Http(msg.into())
    }

    pub fn provider<S: Into<String>>(code: S, message: S) -> Self {
        AdapterError::Provider {
            code: code.into(),
            message: message.into(),
        }
    }

    pub fn invalid<S: Into<String>>(msg: S) -> Self {
        AdapterError::Invalid(msg.into())
    }

    pub fn timeout() -> Self {
        AdapterError::Timeout
    }

    pub fn internal<S: Into<String>>(msg: S) -> Self {
        AdapterError::Internal(msg.into())
    }
}