use crate::core::Provider;
use crate::error::LlmConnectorError;
use crate::types::{ChatRequest, ChatResponse, EmbedRequest, EmbedResponse};
use std::sync::Arc;
#[cfg(feature = "streaming")]
use crate::types::ChatStream;
pub struct LlmClient {
provider: Arc<dyn Provider>,
}
impl LlmClient {
pub fn builder() -> crate::builder::LlmClientBuilder {
crate::builder::LlmClientBuilder::new()
}
pub fn from_provider(provider: Arc<dyn Provider>) -> Self {
Self { provider }
}
pub fn openai(api_key: &str, base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::openai(api_key, base_url)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn azure_openai(
api_key: &str,
endpoint: &str,
api_version: &str,
) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::azure_openai(api_key, endpoint, api_version)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn aliyun(api_key: &str, base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::aliyun(api_key, base_url)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn anthropic(api_key: &str, base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::anthropic(api_key, base_url)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn zhipu(api_key: &str, base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::zhipu(api_key, base_url)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn zhipu_openai_compatible(
api_key: &str,
base_url: &str,
) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::zhipu_openai_compatible(api_key, base_url)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn ollama(base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::ollama(base_url)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn xinference(base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::xinference(base_url)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn xinference_with_api_key(
api_key: &str,
base_url: &str,
) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::xinference_with_api_key(api_key, base_url)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn openai_compatible(
api_key: &str,
base_url: &str,
service_name: &str,
) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::openai_compatible(api_key, base_url, service_name)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn longcat_anthropic(api_key: &str, base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::anthropic_compatible(api_key, base_url, "longcat")?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn longcat_anthropic_with_config(
api_key: &str,
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let protocol = crate::protocols::AnthropicProtocol::new(api_key);
let client = crate::core::HttpClient::with_config(base_url, timeout_secs, proxy)?
.with_header("Authorization".to_string(), format!("Bearer {}", api_key))
.with_header("anthropic-version".to_string(), "2023-06-01".to_string());
let provider = crate::core::GenericProvider::new(protocol, client);
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn volcengine(api_key: &str, base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::openai_compatible(api_key, base_url, "volcengine")?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn volcengine_with_config(
api_key: &str,
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let provider =
crate::providers::openai_with_config(api_key, base_url, timeout_secs, proxy)?;
Ok(Self::from_provider(Arc::new(provider)))
}
#[cfg(feature = "tencent")]
pub fn tencent(
secret_id: &str,
secret_key: &str,
base_url: &str,
) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::tencent(secret_id, secret_key, base_url)?;
Ok(Self::from_provider(Arc::new(provider)))
}
#[cfg(feature = "tencent")]
pub fn tencent_with_config(
secret_id: &str,
secret_key: &str,
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::tencent_with_config(
secret_id,
secret_key,
base_url,
timeout_secs,
proxy,
)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn moonshot(api_key: &str, base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::openai_compatible(api_key, base_url, "moonshot")?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn moonshot_with_config(
api_key: &str,
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let provider =
crate::providers::openai_with_config(api_key, base_url, timeout_secs, proxy)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn deepseek(api_key: &str, base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::openai_compatible(api_key, base_url, "deepseek")?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn deepseek_with_config(
api_key: &str,
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let provider =
crate::providers::openai_with_config(api_key, base_url, timeout_secs, proxy)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn xiaomi(api_key: &str, base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::openai_compatible(api_key, base_url, "xiaomi")?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn xiaomi_with_config(
api_key: &str,
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let provider =
crate::providers::openai_with_config(api_key, base_url, timeout_secs, proxy)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn openai_with_config(
api_key: &str,
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let provider =
crate::providers::openai_with_config(api_key, base_url, timeout_secs, proxy)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn aliyun_with_config(
api_key: &str,
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let provider =
crate::providers::aliyun_with_config(api_key, base_url, timeout_secs, proxy)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn aliyun_international(api_key: &str, region: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::aliyun_international(api_key, region)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn aliyun_private(api_key: &str, base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::aliyun_private(api_key, base_url)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn anthropic_with_config(
api_key: &str,
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let provider =
crate::providers::anthropic_with_config(api_key, base_url, timeout_secs, proxy)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn anthropic_vertex(
project_id: &str,
location: &str,
access_token: &str,
) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::anthropic_vertex(project_id, location, access_token)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn anthropic_bedrock(
region: &str,
access_key: &str,
secret_key: &str,
) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::anthropic_bedrock(region, access_key, secret_key)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn zhipu_with_config(
api_key: &str,
openai_compatible: bool,
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::zhipu_with_config(
api_key,
openai_compatible,
base_url,
timeout_secs,
proxy,
)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn zhipu_enterprise(api_key: &str, base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::zhipu_enterprise(api_key, base_url)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn ollama_with_config(
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::ollama_with_config(base_url, timeout_secs, proxy)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn xinference_with_config(
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::xinference_with_config(base_url, timeout_secs, proxy)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn google(api_key: &str, base_url: &str) -> Result<Self, LlmConnectorError> {
let provider = crate::providers::google(api_key, base_url)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn google_with_config(
api_key: &str,
base_url: &str,
timeout_secs: Option<u64>,
proxy: Option<&str>,
) -> Result<Self, LlmConnectorError> {
let provider =
crate::providers::google_with_config(api_key, base_url, timeout_secs, proxy)?;
Ok(Self::from_provider(Arc::new(provider)))
}
pub fn provider_name(&self) -> &str {
self.provider.name()
}
pub fn supported_providers() -> &'static [&'static str] {
&[
"openai",
"aliyun",
"anthropic",
"zhipu",
"ollama",
"xinference",
"volcengine",
"longcat_anthropic",
"azure_openai",
"openai_compatible",
"google",
"xiaomi",
#[cfg(feature = "tencent")]
"tencent",
]
}
pub async fn chat(&self, request: &ChatRequest) -> Result<ChatResponse, LlmConnectorError> {
self.provider.chat(request).await
}
#[cfg(feature = "streaming")]
pub async fn chat_stream(
&self,
request: &ChatRequest,
) -> Result<ChatStream, LlmConnectorError> {
self.provider.chat_stream(request).await
}
pub async fn embed(&self, request: &EmbedRequest) -> Result<EmbedResponse, LlmConnectorError> {
self.provider.embed(request).await
}
pub async fn models(&self) -> Result<Vec<String>, LlmConnectorError> {
self.provider.models().await
}
pub fn provider(&self) -> &dyn Provider {
self.provider.as_ref()
}
pub fn as_ollama(&self) -> Option<&crate::providers::OllamaProvider> {
self.provider
.as_any()
.downcast_ref::<crate::providers::OllamaProvider>()
}
pub fn as_openai(&self) -> Option<&crate::providers::OpenAIProvider> {
self.provider
.as_any()
.downcast_ref::<crate::providers::OpenAIProvider>()
}
pub fn as_aliyun(&self) -> Option<&crate::providers::AliyunProvider> {
self.provider
.as_any()
.downcast_ref::<crate::providers::AliyunProvider>()
}
pub fn as_anthropic(&self) -> Option<&crate::providers::AnthropicProvider> {
self.provider
.as_any()
.downcast_ref::<crate::providers::AnthropicProvider>()
}
pub fn as_zhipu(&self) -> Option<&crate::providers::ZhipuProvider> {
self.provider
.as_any()
.downcast_ref::<crate::providers::ZhipuProvider>()
}
pub fn mock(content: impl Into<String>) -> Self {
let provider = crate::providers::mock::MockProvider::new(content);
Self::from_provider(Arc::new(provider))
}
pub fn as_mock(&self) -> Option<&crate::providers::mock::MockProvider> {
self.provider
.as_any()
.downcast_ref::<crate::providers::mock::MockProvider>()
}
pub fn as_google(&self) -> Option<&crate::providers::GoogleProvider> {
self.provider
.as_any()
.downcast_ref::<crate::providers::GoogleProvider>()
}
}
impl Clone for LlmClient {
fn clone(&self) -> Self {
Self {
provider: Arc::clone(&self.provider),
}
}
}
impl std::fmt::Debug for LlmClient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LlmClient")
.field("provider", &self.provider.name())
.finish()
}
}