mirror of
https://github.com/0xMassi/webclaw.git
synced 2026-07-25 07:41:01 +02:00
Add Atlas Cloud LLM provider
This commit is contained in:
parent
4072861677
commit
0124ab9df3
4 changed files with 92 additions and 7 deletions
|
|
@ -338,7 +338,7 @@ struct Cli {
|
||||||
#[arg(long, num_args = 0..=1, default_missing_value = "3")]
|
#[arg(long, num_args = 0..=1, default_missing_value = "3")]
|
||||||
summarize: Option<usize>,
|
summarize: Option<usize>,
|
||||||
|
|
||||||
/// Force a specific LLM provider (ollama, openai, anthropic)
|
/// Force a specific LLM provider (ollama, openai, atlascloud, anthropic)
|
||||||
#[arg(long, env = "WEBCLAW_LLM_PROVIDER")]
|
#[arg(long, env = "WEBCLAW_LLM_PROVIDER")]
|
||||||
llm_provider: Option<String>,
|
llm_provider: Option<String>,
|
||||||
|
|
||||||
|
|
@ -2239,6 +2239,14 @@ async fn build_llm_provider(cli: &Cli) -> Result<Box<dyn LlmProvider>, String> {
|
||||||
.ok_or("OPENAI_API_KEY not set")?;
|
.ok_or("OPENAI_API_KEY not set")?;
|
||||||
Ok(Box::new(provider))
|
Ok(Box::new(provider))
|
||||||
}
|
}
|
||||||
|
"atlascloud" => {
|
||||||
|
let provider = webclaw_llm::providers::atlascloud::AtlasCloudProvider::new(
|
||||||
|
None,
|
||||||
|
cli.llm_model.clone(),
|
||||||
|
)
|
||||||
|
.ok_or("ATLASCLOUD_API_KEY not set")?;
|
||||||
|
Ok(Box::new(provider))
|
||||||
|
}
|
||||||
"anthropic" => {
|
"anthropic" => {
|
||||||
let provider = webclaw_llm::providers::anthropic::AnthropicProvider::with_base_url(
|
let provider = webclaw_llm::providers::anthropic::AnthropicProvider::with_base_url(
|
||||||
None,
|
None,
|
||||||
|
|
@ -2249,14 +2257,14 @@ async fn build_llm_provider(cli: &Cli) -> Result<Box<dyn LlmProvider>, String> {
|
||||||
Ok(Box::new(provider))
|
Ok(Box::new(provider))
|
||||||
}
|
}
|
||||||
other => Err(format!(
|
other => Err(format!(
|
||||||
"unknown LLM provider: {other} (use ollama, openai, or anthropic)"
|
"unknown LLM provider: {other} (use ollama, openai, atlascloud, or anthropic)"
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let chain = webclaw_llm::ProviderChain::default().await;
|
let chain = webclaw_llm::ProviderChain::default().await;
|
||||||
if chain.is_empty() {
|
if chain.is_empty() {
|
||||||
return Err(
|
return Err(
|
||||||
"no LLM providers available -- start Ollama or set OPENAI_API_KEY / ANTHROPIC_API_KEY"
|
"no LLM providers available -- start Ollama or set OPENAI_API_KEY / ATLASCLOUD_API_KEY / ANTHROPIC_API_KEY"
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/// Provider chain — tries providers in order until one succeeds.
|
/// Provider chain — tries providers in order until one succeeds.
|
||||||
/// Default order: Ollama (local, free) -> OpenAI -> Gemini -> Anthropic.
|
/// Default order: Ollama (local, free) -> OpenAI -> Atlas Cloud -> Gemini -> Anthropic.
|
||||||
/// Only includes providers that are actually configured/available.
|
/// Only includes providers that are actually configured/available.
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
@ -7,8 +7,8 @@ use tracing::{debug, warn};
|
||||||
use crate::error::LlmError;
|
use crate::error::LlmError;
|
||||||
use crate::provider::{CompletionRequest, LlmProvider};
|
use crate::provider::{CompletionRequest, LlmProvider};
|
||||||
use crate::providers::{
|
use crate::providers::{
|
||||||
anthropic::AnthropicProvider, gemini::GeminiProvider, ollama::OllamaProvider,
|
anthropic::AnthropicProvider, atlascloud::AtlasCloudProvider, gemini::GeminiProvider,
|
||||||
openai::OpenAiProvider,
|
ollama::OllamaProvider, openai::OpenAiProvider,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ProviderChain {
|
pub struct ProviderChain {
|
||||||
|
|
@ -16,7 +16,7 @@ pub struct ProviderChain {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProviderChain {
|
impl ProviderChain {
|
||||||
/// Build the default chain: Ollama -> OpenAI -> Gemini -> Anthropic.
|
/// Build the default chain: Ollama -> OpenAI -> Atlas Cloud -> Gemini -> Anthropic.
|
||||||
/// Ollama is always added (availability checked at call time).
|
/// Ollama is always added (availability checked at call time).
|
||||||
/// Cloud providers are only added if their API keys are configured.
|
/// Cloud providers are only added if their API keys are configured.
|
||||||
/// Gemini sits ahead of Anthropic so Google Cloud credits are preferred,
|
/// Gemini sits ahead of Anthropic so Google Cloud credits are preferred,
|
||||||
|
|
@ -37,6 +37,11 @@ impl ProviderChain {
|
||||||
providers.push(Box::new(openai));
|
providers.push(Box::new(openai));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(atlascloud) = AtlasCloudProvider::new(None, None) {
|
||||||
|
debug!("atlascloud configured, adding to chain");
|
||||||
|
providers.push(Box::new(atlascloud));
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(gemini) = GeminiProvider::new(None, None, None) {
|
if let Some(gemini) = GeminiProvider::new(None, None, None) {
|
||||||
debug!("gemini configured, adding to chain");
|
debug!("gemini configured, adding to chain");
|
||||||
providers.push(Box::new(gemini));
|
providers.push(Box::new(gemini));
|
||||||
|
|
|
||||||
71
crates/webclaw-llm/src/providers/atlascloud.rs
Normal file
71
crates/webclaw-llm/src/providers/atlascloud.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
/// Atlas Cloud provider — OpenAI-compatible chat completions with Atlas defaults.
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use crate::error::LlmError;
|
||||||
|
use crate::provider::{CompletionRequest, LlmProvider};
|
||||||
|
|
||||||
|
use super::openai::OpenAiProvider;
|
||||||
|
|
||||||
|
pub struct AtlasCloudProvider {
|
||||||
|
inner: OpenAiProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AtlasCloudProvider {
|
||||||
|
/// Returns `None` if no Atlas Cloud API key is available (param or env).
|
||||||
|
pub fn new(key_override: Option<String>, model: Option<String>) -> Option<Self> {
|
||||||
|
let key = super::load_api_key(key_override, "ATLASCLOUD_API_KEY")?;
|
||||||
|
let base_url = std::env::var("ATLASCLOUD_BASE_URL")
|
||||||
|
.ok()
|
||||||
|
.unwrap_or_else(|| "https://api.atlascloud.ai/v1".into());
|
||||||
|
let model = model.unwrap_or_else(|| "qwen/qwen3.5-flash".into());
|
||||||
|
let inner = OpenAiProvider::new(Some(key), Some(base_url), Some(model))?;
|
||||||
|
Some(Self { inner })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_model(&self) -> &str {
|
||||||
|
self.inner.default_model()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LlmProvider for AtlasCloudProvider {
|
||||||
|
async fn complete(&self, request: &CompletionRequest) -> Result<String, LlmError> {
|
||||||
|
self.inner.complete(request).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_available(&self) -> bool {
|
||||||
|
self.inner.is_available().await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"atlascloud"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_key_returns_none() {
|
||||||
|
assert!(AtlasCloudProvider::new(Some(String::new()), None).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn explicit_key_constructs_with_atlas_defaults() {
|
||||||
|
let provider = AtlasCloudProvider::new(Some("test-key".into()), None)
|
||||||
|
.expect("should construct");
|
||||||
|
assert_eq!(provider.name(), "atlascloud");
|
||||||
|
assert_eq!(provider.default_model(), "qwen/qwen3.5-flash");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn explicit_model_override() {
|
||||||
|
let provider = AtlasCloudProvider::new(
|
||||||
|
Some("test-key".into()),
|
||||||
|
Some("deepseek-ai/deepseek-v4-pro".into()),
|
||||||
|
)
|
||||||
|
.expect("should construct");
|
||||||
|
assert_eq!(provider.default_model(), "deepseek-ai/deepseek-v4-pro");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod anthropic;
|
pub mod anthropic;
|
||||||
|
pub mod atlascloud;
|
||||||
pub mod gemini;
|
pub mod gemini;
|
||||||
pub mod ollama;
|
pub mod ollama;
|
||||||
pub mod openai;
|
pub mod openai;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue