use crate::LlmModel;
use crate::Result as LlmResult;
use std::future::Future;
use std::pin::Pin;
use tokio_stream::Stream;
use super::{Context, LlmResponse};
pub type LlmResponseStream = Pin<Box<dyn Stream<Item = LlmResult<LlmResponse>> + Send>>;
#[doc = include_str!("docs/provider_factory.md")]
pub trait ProviderFactory: Sized {
fn from_env() -> impl Future<Output = LlmResult<Self>> + Send;
fn with_model(self, model: &str) -> Self;
}
#[doc = include_str!("docs/streaming_model_provider.md")]
pub trait StreamingModelProvider: Send + Sync {
fn stream_response(&self, context: &Context) -> LlmResponseStream;
fn display_name(&self) -> String;
fn context_window(&self) -> Option<u32>;
fn model(&self) -> Option<LlmModel> {
None
}
}
pub fn get_context_window(provider: &str, model_id: &str) -> Option<u32> {
let key = format!("{provider}:{model_id}");
key.parse::<LlmModel>().ok().and_then(|m| m.context_window())
}
impl StreamingModelProvider for Box<dyn StreamingModelProvider> {
fn stream_response(&self, context: &Context) -> LlmResponseStream {
(**self).stream_response(context)
}
fn display_name(&self) -> String {
(**self).display_name()
}
fn context_window(&self) -> Option<u32> {
(**self).context_window()
}
fn model(&self) -> Option<LlmModel> {
(**self).model()
}
}
impl<T: StreamingModelProvider> StreamingModelProvider for std::sync::Arc<T> {
fn stream_response(&self, context: &Context) -> LlmResponseStream {
(**self).stream_response(context)
}
fn display_name(&self) -> String {
(**self).display_name()
}
fn context_window(&self) -> Option<u32> {
(**self).context_window()
}
fn model(&self) -> Option<LlmModel> {
(**self).model()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lookup_context_window_known_model() {
assert_eq!(get_context_window("anthropic", "claude-opus-4-6"), Some(1_000_000));
}
#[test]
fn lookup_context_window_openrouter_model() {
let result = get_context_window("openrouter", "anthropic/claude-opus-4");
assert_eq!(result, Some(200_000));
}
#[test]
fn lookup_context_window_unknown_model() {
assert_eq!(get_context_window("anthropic", "unknown-model-xyz"), None);
}
#[test]
fn lookup_context_window_unknown_provider() {
assert_eq!(get_context_window("unknown-provider", "some-model"), None);
}
}