use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OpenClawConfig {
pub meta: Option<Value>,
pub wizard: Option<Value>,
pub gateway: Option<Value>,
pub agents: Option<Value>,
pub models: Option<Value>,
pub auth: Option<Value>,
pub channels: Option<Value>,
pub session: Option<Value>,
pub bindings: Option<Value>,
pub cron: Option<Value>,
pub tools: Option<Value>,
pub sandbox: Option<Value>,
pub skills: Option<Value>,
pub plugins: Option<Value>,
pub hooks: Option<Value>,
pub memory: Option<Value>,
pub messages: Option<Value>,
pub commands: Option<Value>,
}
impl OpenClawConfig {
pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
serde_json::from_str(json)
}
pub fn gateway_host(&self) -> String {
self.gateway
.as_ref()
.and_then(|g| g.get("bind"))
.and_then(|b| b.as_str())
.map(|s| {
if s == "loopback" {
"127.0.0.1"
} else {
"0.0.0.0"
}
})
.unwrap_or("127.0.0.1")
.to_string()
}
pub fn gateway_port(&self) -> u16 {
self.gateway
.as_ref()
.and_then(|g| g.get("port"))
.and_then(|p| p.as_u64())
.unwrap_or(8080) as u16
}
pub fn get_providers(&self) -> Vec<ProviderEntry> {
let mut providers = Vec::new();
if let Some(models) = &self.models {
if let Some(providers_obj) = models.get("providers") {
if let Some(map) = providers_obj.as_object() {
for (name, config) in map {
let provider_type = config
.get("api")
.and_then(|a| a.as_str())
.unwrap_or("openai");
let provider_type = if provider_type.starts_with("openai") {
"openai"
} else {
provider_type
};
let base_url = config
.get("baseUrl")
.and_then(|u| u.as_str())
.map(|s| s.to_string());
let api_key = config
.get("apiKey")
.and_then(|k| k.as_str())
.map(|s| s.to_string());
let mut models_list = Vec::new();
if let Some(models_arr) = config.get("models").and_then(|m| m.as_array()) {
for model in models_arr {
if let Some(id) = model.get("id").and_then(|i| i.as_str()) {
let max_tokens = model
.get("maxTokens")
.and_then(|t| t.as_u64())
.map(|t| t as u32);
models_list.push(ModelEntry {
name: id.to_string(),
max_tokens,
supports_functions: None,
supports_vision: None,
});
}
}
}
providers.push(ProviderEntry {
name: name.clone(),
provider_type: provider_type.to_string(),
api_key,
base_url,
models: models_list,
});
}
}
}
}
providers
}
pub fn get_agents(&self) -> Vec<AgentEntry> {
let mut agents = Vec::new();
if let Some(agents_config) = &self.agents {
if let Some(defaults) = agents_config.get("defaults") {
let model = defaults
.get("model")
.and_then(|m| m.get("primary"))
.and_then(|p| p.as_str())
.map(|s| s.to_string());
let _workspace = defaults
.get("workspace")
.and_then(|w| w.as_str())
.map(|s| s.to_string());
let max_concurrent = defaults
.get("maxConcurrent")
.and_then(|c| c.as_u64())
.map(|c| c as u32);
agents.push(AgentEntry {
name: "default".to_string(),
model,
system_prompt: None,
tools: None,
channels: None,
default: true,
max_tokens: None,
memory_limit_mb: max_concurrent.map(|c| c * 512),
});
}
}
agents
}
}
#[derive(Debug, Clone)]
pub struct ProviderEntry {
pub name: String,
pub provider_type: String,
pub api_key: Option<String>,
pub base_url: Option<String>,
pub models: Vec<ModelEntry>,
}
#[derive(Debug, Clone)]
pub struct ModelEntry {
pub name: String,
pub max_tokens: Option<u32>,
pub supports_functions: Option<bool>,
pub supports_vision: Option<bool>,
}
#[derive(Debug, Clone)]
pub struct AgentEntry {
pub name: String,
pub model: Option<String>,
pub system_prompt: Option<String>,
pub tools: Option<Vec<String>>,
pub channels: Option<Vec<String>>,
pub default: bool,
pub max_tokens: Option<u32>,
pub memory_limit_mb: Option<u32>,
}