Files
zesdex/src/service/provider.rs
T
asepharyana 3dee2a1427 Refactor API integration and enhance command handling
- Removed unused modules and updated module paths for clarity.
- Added autocomplete functionality for command input in InputState.
- Updated AppStateRest to include a method for checking if a turn is in flight.
- Refactored subagent engine to use new API client structure.
- Changed default provider from "openrouter" to "zen" with updated API keys and models.
- Implemented tests for memory management and edit log functionalities.
- Enhanced error handling in API requests and improved response parsing.
- Updated UI components to reflect new API provider and status indicators.
2026-07-11 22:10:17 +07:00

95 lines
3.0 KiB
Rust

use std::time::Duration;
use anyhow::Result;
use crate::dto::chat::message::ChatMessage;
use crate::dto::provider::request::ToolDef;
const DEFAULT_BASE_URL: &str = "https://opencode.ai/zen/v1";
const DEFAULT_MODEL: &str = "deepseek-v4-flash-free";
const CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
const REQUEST_TIMEOUT: Duration = Duration::from_secs(60);
pub struct LlmClient {
pub client: reqwest::blocking::Client,
pub api_key: String,
pub base_url: String,
pub model: String,
}
impl LlmClient {
pub fn new(api_key: String, model: String) -> Self {
let model = if model.is_empty() {
DEFAULT_MODEL.to_string()
} else {
model
};
let client = reqwest::blocking::Client::builder()
.timeout(REQUEST_TIMEOUT)
.connect_timeout(CONNECT_TIMEOUT)
.build()
.unwrap_or_else(|_| reqwest::blocking::Client::new());
LlmClient {
client,
api_key,
base_url: DEFAULT_BASE_URL.to_string(),
model,
}
}
pub fn chat(&self, messages: &[ChatMessage]) -> Result<String> {
let response = self.chat_with_tools(messages, None)?;
Ok(response.content.unwrap_or_default())
}
pub fn chat_with_tools(
&self,
messages: &[ChatMessage],
tools: Option<Vec<ToolDef>>,
) -> Result<ChatMessage> {
let req = crate::dto::provider::request::ChatRequest {
model: self.model.clone(),
messages: messages.to_vec(),
max_tokens: Some(4096),
temperature: Some(0.7),
tools,
stream: Some(false),
top_p: None,
stop: None,
};
let url = format!("{}/chat/completions", self.base_url);
let mut http_req = self.client
.post(&url)
.header("Content-Type", "application/json");
if !self.api_key.is_empty() {
http_req = http_req.header("Authorization", format!("Bearer {}", self.api_key));
}
let resp = http_req.json(&req).send().map_err(|e| {
if e.is_timeout() {
anyhow::anyhow!("API request timed out after {:?}. Check your network or try again.", REQUEST_TIMEOUT)
} else if e.is_connect() {
anyhow::anyhow!("Could not connect to {}. Is the URL correct and is the service reachable?", self.base_url)
} else {
anyhow::anyhow!("API request failed: {}", e)
}
})?;
if !resp.status().is_success() {
let status = resp.status();
let body = resp.text().unwrap_or_default();
anyhow::bail!("API error {} from {}: {}", status, self.base_url, body);
}
let data: crate::dto::provider::response::ChatResponse = resp.json()?;
let message = data
.choices
.into_iter()
.next()
.map(|c| c.message)
.ok_or_else(|| anyhow::anyhow!("API response had no choices"))?;
Ok(message)
}
}