feat(ollama): add local provider implementation and request timeout support
Introduce `OllamaLocalProvider` for communicating with a local Ollama daemon, including health checks, model listing, and stream generation. Export the provider in the Ollama module. Extend `OllamaClient` to accept an optional request timeout and apply it to the underlying HTTP client configuration.
This commit is contained in:
79
crates/owlen-providers/src/ollama/local.rs
Normal file
79
crates/owlen-providers/src/ollama/local.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use owlen_core::provider::{
|
||||||
|
GenerateRequest, GenerateStream, ModelInfo, ModelProvider, ProviderMetadata, ProviderStatus,
|
||||||
|
ProviderType,
|
||||||
|
};
|
||||||
|
use owlen_core::{Error as CoreError, Result as CoreResult};
|
||||||
|
use serde_json::Value;
|
||||||
|
use tokio::time::timeout;
|
||||||
|
|
||||||
|
use super::OllamaClient;
|
||||||
|
|
||||||
|
const DEFAULT_BASE_URL: &str = "http://localhost:11434";
|
||||||
|
const DEFAULT_HEALTH_TIMEOUT_SECS: u64 = 5;
|
||||||
|
|
||||||
|
/// ModelProvider implementation for a local Ollama daemon.
|
||||||
|
pub struct OllamaLocalProvider {
|
||||||
|
client: OllamaClient,
|
||||||
|
health_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OllamaLocalProvider {
|
||||||
|
/// Construct a new local provider using the shared [`OllamaClient`].
|
||||||
|
pub fn new(
|
||||||
|
base_url: Option<String>,
|
||||||
|
request_timeout: Option<Duration>,
|
||||||
|
health_timeout: Option<Duration>,
|
||||||
|
) -> CoreResult<Self> {
|
||||||
|
let base_url = base_url.unwrap_or_else(|| DEFAULT_BASE_URL.to_string());
|
||||||
|
let health_timeout =
|
||||||
|
health_timeout.unwrap_or_else(|| Duration::from_secs(DEFAULT_HEALTH_TIMEOUT_SECS));
|
||||||
|
|
||||||
|
let mut metadata =
|
||||||
|
ProviderMetadata::new("ollama_local", "Ollama (Local)", ProviderType::Local, false);
|
||||||
|
metadata
|
||||||
|
.metadata
|
||||||
|
.insert("base_url".into(), Value::String(base_url.clone()));
|
||||||
|
if let Some(timeout) = request_timeout {
|
||||||
|
metadata.metadata.insert(
|
||||||
|
"request_timeout_ms".into(),
|
||||||
|
Value::String(timeout.as_millis().to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = OllamaClient::new(&base_url, None, metadata, request_timeout)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
client,
|
||||||
|
health_timeout,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl ModelProvider for OllamaLocalProvider {
|
||||||
|
fn metadata(&self) -> &ProviderMetadata {
|
||||||
|
self.client.metadata()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn health_check(&self) -> CoreResult<ProviderStatus> {
|
||||||
|
match timeout(self.health_timeout, self.client.health_check()).await {
|
||||||
|
Ok(Ok(status)) => Ok(status),
|
||||||
|
Ok(Err(CoreError::Network(_))) | Ok(Err(CoreError::Timeout(_))) => {
|
||||||
|
Ok(ProviderStatus::Unavailable)
|
||||||
|
}
|
||||||
|
Ok(Err(err)) => Err(err),
|
||||||
|
Err(_) => Ok(ProviderStatus::Unavailable),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_models(&self) -> CoreResult<Vec<ModelInfo>> {
|
||||||
|
self.client.list_models().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn generate_stream(&self, request: GenerateRequest) -> CoreResult<GenerateStream> {
|
||||||
|
self.client.generate_stream(request).await
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
pub mod local;
|
||||||
pub mod shared;
|
pub mod shared;
|
||||||
|
|
||||||
|
pub use local::OllamaLocalProvider;
|
||||||
pub use shared::OllamaClient;
|
pub use shared::OllamaClient;
|
||||||
|
|||||||
@@ -29,12 +29,14 @@ impl OllamaClient {
|
|||||||
base_url: impl AsRef<str>,
|
base_url: impl AsRef<str>,
|
||||||
api_key: Option<String>,
|
api_key: Option<String>,
|
||||||
provider_metadata: ProviderMetadata,
|
provider_metadata: ProviderMetadata,
|
||||||
|
request_timeout: Option<Duration>,
|
||||||
) -> CoreResult<Self> {
|
) -> CoreResult<Self> {
|
||||||
let base_url = Url::parse(base_url.as_ref())
|
let base_url = Url::parse(base_url.as_ref())
|
||||||
.map_err(|err| CoreError::Config(format!("invalid base url: {}", err)))?;
|
.map_err(|err| CoreError::Config(format!("invalid base url: {}", err)))?;
|
||||||
|
|
||||||
|
let timeout = request_timeout.unwrap_or_else(|| Duration::from_secs(DEFAULT_TIMEOUT_SECS));
|
||||||
let http = Client::builder()
|
let http = Client::builder()
|
||||||
.timeout(Duration::from_secs(DEFAULT_TIMEOUT_SECS))
|
.timeout(timeout)
|
||||||
.build()
|
.build()
|
||||||
.map_err(map_reqwest_error)?;
|
.map_err(map_reqwest_error)?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user