feat(keyring): 集成系统密钥环安全存储 API key

This commit is contained in:
2026-03-12 17:42:41 +08:00
parent c66d782eab
commit da85fc94b1
17 changed files with 990 additions and 1024 deletions

View File

@@ -57,48 +57,50 @@ impl Default for LlmClientConfig {
}
impl LlmClient {
/// Create LLM client from configuration
pub async fn from_config(config: &crate::config::LlmConfig) -> Result<Self> {
/// Create LLM client from configuration manager
pub async fn from_config(manager: &crate::config::manager::ConfigManager) -> Result<Self> {
let config = manager.config();
let client_config = LlmClientConfig {
max_tokens: config.max_tokens,
temperature: config.temperature,
timeout: Duration::from_secs(config.timeout),
max_tokens: config.llm.max_tokens,
temperature: config.llm.temperature,
timeout: Duration::from_secs(config.llm.timeout),
};
let provider: Box<dyn LlmProvider> = match config.provider.as_str() {
let provider = config.llm.provider.as_str();
let model = config.llm.model.as_str();
let base_url = manager.llm_base_url();
let api_key = manager.get_api_key();
let provider: Box<dyn LlmProvider> = match provider {
"ollama" => {
Box::new(OllamaClient::new(&config.ollama.url, &config.ollama.model))
Box::new(OllamaClient::new(&base_url, model))
}
"openai" => {
let api_key = config.openai.api_key.as_ref()
let key = api_key.as_ref()
.ok_or_else(|| anyhow::anyhow!("OpenAI API key not configured"))?;
Box::new(OpenAiClient::new(
&config.openai.base_url,
api_key,
&config.openai.model,
)?)
Box::new(OpenAiClient::new(&base_url, key, model)?)
}
"anthropic" => {
let api_key = config.anthropic.api_key.as_ref()
let key = api_key.as_ref()
.ok_or_else(|| anyhow::anyhow!("Anthropic API key not configured"))?;
Box::new(AnthropicClient::new(api_key, &config.anthropic.model)?)
Box::new(AnthropicClient::new(key, model)?)
}
"kimi" => {
let api_key = config.kimi.api_key.as_ref()
let key = api_key.as_ref()
.ok_or_else(|| anyhow::anyhow!("Kimi API key not configured"))?;
Box::new(KimiClient::with_base_url(api_key, &config.kimi.model, &config.kimi.base_url)?)
Box::new(KimiClient::with_base_url(key, model, &base_url)?)
}
"deepseek" => {
let api_key = config.deepseek.api_key.as_ref()
let key = api_key.as_ref()
.ok_or_else(|| anyhow::anyhow!("DeepSeek API key not configured"))?;
Box::new(DeepSeekClient::with_base_url(api_key, &config.deepseek.model, &config.deepseek.base_url)?)
Box::new(DeepSeekClient::with_base_url(key, model, &base_url)?)
}
"openrouter" => {
let api_key = config.openrouter.api_key.as_ref()
let key = api_key.as_ref()
.ok_or_else(|| anyhow::anyhow!("OpenRouter API key not configured"))?;
Box::new(OpenRouterClient::with_base_url(api_key, &config.openrouter.model, &config.openrouter.base_url)?)
Box::new(OpenRouterClient::with_base_url(key, model, &base_url)?)
}
_ => bail!("Unknown LLM provider: {}", config.provider),
_ => bail!("Unknown LLM provider: {}", provider),
};
Ok(Self {
@@ -1012,3 +1014,10 @@ Gruppieren Sie Commits nach:
Formatieren Sie in Markdown mit geeigneten Überschriften und Aufzählungspunkten.
"#;
/// Test LLM connection
pub async fn test_connection(manager: &crate::config::manager::ConfigManager) -> Result<String> {
let client = LlmClient::from_config(manager).await?;
let response = client.provider.generate("Say 'Hello, World!'").await?;
Ok(response)
}