mirror of
https://github.com/0xMassi/webclaw.git
synced 2026-07-24 07:31:01 +02:00
Merge pull request #92 from 0xMassi/feat/atlascloud-provider
feat(llm): add Atlas Cloud as an opt-in provider
This commit is contained in:
commit
63056a8968
3 changed files with 88 additions and 2 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,15 @@ 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_base_url.clone(),
|
||||||
|
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,7 +2258,7 @@ 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 {
|
||||||
|
|
|
||||||
76
crates/webclaw-llm/src/providers/atlascloud.rs
Normal file
76
crates/webclaw-llm/src/providers/atlascloud.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
/// 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>,
|
||||||
|
base_url: Option<String>,
|
||||||
|
model: Option<String>,
|
||||||
|
) -> Option<Self> {
|
||||||
|
let key = super::load_api_key(key_override, "ATLASCLOUD_API_KEY")?;
|
||||||
|
let base_url = base_url
|
||||||
|
.or_else(|| 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, None).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn explicit_key_constructs_with_atlas_defaults() {
|
||||||
|
let provider =
|
||||||
|
AtlasCloudProvider::new(Some("test-key".into()), None, 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("https://proxy.example.com/v1".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