Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@ sage -r <session-id>
- **Efficient memory** - Low footprint, handles large codebases

### 🤖 Multi-LLM Support
- **Anthropic** - Claude Sonnet, Opus (with prompt caching)
- **OpenAI** - GPT-4, GPT-4 Turbo
- **Google** - Gemini Pro
- **Anthropic** - Claude Opus 4.7, Sonnet 4.6, Haiku 4.5
- **OpenAI** - GPT-5.4 family
- **Google** - Gemini 2.5 Pro / Flash
- **Z.AI** - GLM-5.1 and GLM-5
- **Moonshot AI** - Kimi K2.6 / K2.5
- **Ollama** - Llama, Mistral, CodeLlama (offline)
- **Azure OpenAI** - Enterprise deployments
- **OpenRouter** - Access 100+ models
Expand Down Expand Up @@ -206,10 +208,20 @@ Create `sage_config.json` or use environment variables:
"default_provider": "anthropic",
"model_providers": {
"anthropic": {
"model": "claude-sonnet-4-20250514",
"model": "claude-opus-4-7",
"api_key": "${ANTHROPIC_API_KEY}",
"enable_prompt_caching": true
},
"zai": {
"model": "glm-5.1",
"api_key": "${ZAI_API_KEY}",
"base_url": "https://api.z.ai/api/paas/v4"
},
"moonshot": {
"model": "kimi-k2.6",
"api_key": "${MOONSHOT_API_KEY}",
"base_url": "https://api.moonshot.ai/v1"
},
"ollama": {
"model": "codellama",
"base_url": "http://localhost:11434"
Expand All @@ -226,6 +238,8 @@ Create `sage_config.json` or use environment variables:
# API Keys
export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..."
export ZAI_API_KEY="..."
export MOONSHOT_API_KEY="sk-..."

# Configuration
export SAGE_DEFAULT_PROVIDER="anthropic"
Expand Down
22 changes: 18 additions & 4 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@ sage -r <session-id>
- **Efficient memory** - Low footprint, handles large codebases

### 🤖 Multi-LLM Support
- **Anthropic** - Claude Sonnet, Opus (with prompt caching)
- **OpenAI** - GPT-4, GPT-4 Turbo
- **Google** - Gemini Pro
- **Anthropic** - Claude Opus 4.7、Sonnet 4.6、Haiku 4.5
- **OpenAI** - GPT-5.4 系列
- **Google** - Gemini 2.5 Pro / Flash
- **Z.AI** - GLM-5.1 和 GLM-5
- **Moonshot AI** - Kimi K2.6 / K2.5
- **Ollama** - Llama, Mistral, CodeLlama (offline)
- **Azure OpenAI** - Enterprise deployments
- **OpenRouter** - Access 100+ models
Expand Down Expand Up @@ -206,10 +208,20 @@ Create `sage_config.json` or use environment variables:
"default_provider": "anthropic",
"model_providers": {
"anthropic": {
"model": "claude-sonnet-4-20250514",
"model": "claude-opus-4-7",
"api_key": "${ANTHROPIC_API_KEY}",
"enable_prompt_caching": true
},
"zai": {
"model": "glm-5.1",
"api_key": "${ZAI_API_KEY}",
"base_url": "https://api.z.ai/api/paas/v4"
},
"moonshot": {
"model": "kimi-k2.6",
"api_key": "${MOONSHOT_API_KEY}",
"base_url": "https://api.moonshot.ai/v1"
},
"ollama": {
"model": "codellama",
"base_url": "http://localhost:11434"
Expand All @@ -226,6 +238,8 @@ Create `sage_config.json` or use environment variables:
# API Keys
export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..."
export ZAI_API_KEY="..."
export MOONSHOT_API_KEY="sk-..."

# Configuration
export SAGE_DEFAULT_PROVIDER="anthropic"
Expand Down
57 changes: 54 additions & 3 deletions crates/sage-cli/src/commands/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ fn create_sample_config() -> Config {
model_providers.insert(
"openai".to_string(),
ModelParameters {
model: "gpt-4".to_string(),
model: "gpt-5.4".to_string(),
api_key: Some("your-openai-api-key-here".to_string()),
max_tokens: Some(4096),
temperature: Some(0.7),
Expand All @@ -235,7 +235,7 @@ fn create_sample_config() -> Config {
model_providers.insert(
"anthropic".to_string(),
ModelParameters {
model: "claude-3-sonnet-20240229".to_string(),
model: "claude-opus-4-7".to_string(),
api_key: Some("your-anthropic-api-key-here".to_string()),
max_tokens: Some(4096),
temperature: Some(0.7),
Expand All @@ -249,8 +249,59 @@ fn create_sample_config() -> Config {
},
);

model_providers.insert(
"google".to_string(),
ModelParameters {
model: "gemini-2.5-pro".to_string(),
api_key: Some("your-google-api-key-here".to_string()),
max_tokens: Some(4096),
temperature: Some(0.7),
top_p: Some(1.0),
top_k: None,
parallel_tool_calls: Some(true),
max_retries: Some(3),
base_url: None,
api_version: None,
stop_sequences: None,
},
);

model_providers.insert(
"zai".to_string(),
ModelParameters {
model: "glm-5.1".to_string(),
api_key: Some("your-zai-api-key-here".to_string()),
max_tokens: Some(4096),
temperature: Some(1.0),
top_p: Some(0.95),
top_k: None,
parallel_tool_calls: Some(true),
max_retries: Some(3),
base_url: Some("https://api.z.ai/api/paas/v4".to_string()),
api_version: None,
stop_sequences: None,
},
);

model_providers.insert(
"moonshot".to_string(),
ModelParameters {
model: "kimi-k2.6".to_string(),
api_key: Some("your-moonshot-api-key-here".to_string()),
max_tokens: Some(4096),
temperature: Some(1.0),
top_p: Some(0.95),
top_k: None,
parallel_tool_calls: Some(true),
max_retries: Some(3),
base_url: Some("https://api.moonshot.ai/v1".to_string()),
api_version: None,
stop_sequences: None,
},
);

Config {
default_provider: "openai".to_string(),
default_provider: "anthropic".to_string(),
max_steps: None, // None = unlimited
total_token_budget: None,
model_providers,
Expand Down
6 changes: 6 additions & 0 deletions crates/sage-cli/src/commands/interactive/onboarding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ fn get_provider_env_var(provider: &str) -> &'static str {
match provider {
"anthropic" => "ANTHROPIC_API_KEY",
"openai" => "OPENAI_API_KEY",
"zai" => "ZAI_API_KEY",
"google" => "GOOGLE_API_KEY",
"glm" | "zhipu" => "GLM_API_KEY",
"moonshot" | "kimi" => "MOONSHOT_API_KEY",
"deepseek" => "DEEPSEEK_API_KEY",
_ => "API_KEY",
}
Expand All @@ -31,7 +34,10 @@ fn get_provider_help_url(provider: &str) -> &'static str {
match provider {
"anthropic" => "https://console.anthropic.com/settings/keys",
"openai" => "https://platform.openai.com/api-keys",
"zai" => "https://docs.z.ai/api-reference/introduction",
"google" => "https://makersuite.google.com/app/apikey",
"glm" | "zhipu" => "https://open.bigmodel.cn/usercenter/apikeys",
"moonshot" | "kimi" => "https://platform.kimi.ai/docs/models",
"deepseek" => "https://platform.deepseek.com/api_keys",
_ => "https://docs.sage-agent.dev/configuration",
}
Expand Down
4 changes: 3 additions & 1 deletion crates/sage-cli/src/commands/unified/slash_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,10 @@ pub async fn handle_interactive_command_v2(
let env_var = match provider_name {
"anthropic" => "ANTHROPIC_API_KEY",
"openai" => "OPENAI_API_KEY",
"zai" => "ZAI_API_KEY",
"google" => "GOOGLE_API_KEY",
"glm" | "zhipu" => "GLM_API_KEY",
"moonshot" | "kimi" => "MOONSHOT_API_KEY",
_ => "",
};
if !env_var.is_empty() {
Expand All @@ -210,7 +212,7 @@ pub async fn handle_interactive_command_v2(
.unwrap_or_default(),
}
}
"openai" | "openrouter" => {
"openai" | "openrouter" | "zai" | "moonshot" | "kimi" => {
match client
.fetch_openai_models(&base_url, api_key.as_deref().unwrap_or(""))
.await
Expand Down
2 changes: 1 addition & 1 deletion crates/sage-core/src/checkpoints/storage/file_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ impl CheckpointStorage for FileCheckpointStorage {
}

// Sort by creation time (newest first)
summaries.sort_by(|a, b| b.created_at.cmp(&a.created_at));
summaries.sort_by_key(|summary| std::cmp::Reverse(summary.created_at));

Ok(summaries)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/sage-core/src/checkpoints/storage/memory_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl CheckpointStorage for MemoryCheckpointStorage {
async fn list(&self) -> SageResult<Vec<CheckpointSummary>> {
let checkpoints = self.checkpoints.read().await;
let mut summaries: Vec<_> = checkpoints.values().map(CheckpointSummary::from).collect();
summaries.sort_by(|a, b| b.created_at.cmp(&a.created_at));
summaries.sort_by_key(|summary| std::cmp::Reverse(summary.created_at));
Ok(summaries)
}

Expand Down
2 changes: 2 additions & 0 deletions crates/sage-core/src/config/api_key_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::config::provider::{ApiKeyInfo, ApiKeySource};
pub fn get_standard_env_vars_for_provider(provider: &str) -> Vec<String> {
match provider {
"openai" => vec!["OPENAI_API_KEY".to_string()],
"zai" => vec!["ZAI_API_KEY".to_string()],
"anthropic" => vec![
"ANTHROPIC_API_KEY".to_string(),
"CLAUDE_API_KEY".to_string(),
Expand All @@ -20,6 +21,7 @@ pub fn get_standard_env_vars_for_provider(provider: &str) -> Vec<String> {
"openrouter" => vec!["OPENROUTER_API_KEY".to_string()],
"doubao" => vec!["DOUBAO_API_KEY".to_string(), "ARK_API_KEY".to_string()],
"glm" | "zhipu" => vec!["GLM_API_KEY".to_string(), "ZHIPU_API_KEY".to_string()],
"moonshot" | "kimi" => vec!["MOONSHOT_API_KEY".to_string(), "KIMI_API_KEY".to_string()],
_ => {
// For custom or default providers, try <PROVIDER>_API_KEY
vec![format!("{}_API_KEY", provider.to_uppercase())]
Expand Down
17 changes: 13 additions & 4 deletions crates/sage-core/src/config/args_loader.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Command line arguments-based configuration loading

use crate::config::model::Config;
use crate::config::model::{Config, ModelParameters};
use crate::error::{SageError, SageResult};
use std::collections::HashMap;
use std::path::PathBuf;
Expand Down Expand Up @@ -38,7 +38,10 @@ pub fn load_from_args(args: &HashMap<String, String>) -> SageResult<Config> {
.model_providers
.get(&provider)
.cloned()
.unwrap_or_default();
.unwrap_or_else(|| ModelParameters {
model: String::new(),
..ModelParameters::default()
});
params.model = model.clone();
config.model_providers.insert(provider, params);
}
Expand All @@ -49,7 +52,10 @@ pub fn load_from_args(args: &HashMap<String, String>) -> SageResult<Config> {
.model_providers
.get(&provider)
.cloned()
.unwrap_or_default();
.unwrap_or_else(|| ModelParameters {
model: String::new(),
..ModelParameters::default()
});
params.api_key = Some(api_key.clone());
config.model_providers.insert(provider, params);
}
Expand All @@ -60,7 +66,10 @@ pub fn load_from_args(args: &HashMap<String, String>) -> SageResult<Config> {
.model_providers
.get(&provider)
.cloned()
.unwrap_or_default();
.unwrap_or_else(|| ModelParameters {
model: String::new(),
..ModelParameters::default()
});
params.base_url = Some(base_url.clone());
config.model_providers.insert(provider, params);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/sage-core/src/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ mod tests {
fn test_config_default_model_parameters() {
let config = Config::default();
let params = config.default_model_parameters().unwrap();
assert_eq!(params.model, "claude-sonnet-4-5-20250929");
assert_eq!(params.model, "claude-opus-4-7");
}

#[test]
Expand Down
3 changes: 3 additions & 0 deletions crates/sage-core/src/config/credential/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub fn default_providers() -> Vec<ProviderEnvConfig> {
ProviderEnvConfig::new("google", "GOOGLE_API_KEY"),
ProviderEnvConfig::new("glm", "GLM_API_KEY"),
ProviderEnvConfig::new("zhipu", "ZHIPU_API_KEY"),
ProviderEnvConfig::new("zai", "ZAI_API_KEY"),
ProviderEnvConfig::new("moonshot", "MOONSHOT_API_KEY"),
ProviderEnvConfig::new("kimi", "KIMI_API_KEY"),
ProviderEnvConfig::new("ollama", "OLLAMA_API_KEY"),
]
}
Expand Down
Loading
Loading