fix(kernel): 使用 Kernel 配置的 model 而非 Agent 持久化的旧值
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
问题:在"模型与 API"页面切换模型后,对话仍使用旧模型 根因:Agent 配置从数据库恢复,其 model 字段优先于 Kernel 配置 修复: - kernel.rs: send_message/send_message_stream 始终使用 Kernel 的当前 model - openai.rs: 添加 User-Agent header 解决 Coding Plan API 405 错误 - kernel_commands.rs: 添加详细调试日志便于追踪配置传递 - troubleshooting.md: 记录此问题的排查过程和解决方案 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,8 +39,8 @@ pub struct CreateAgentRequest {
|
||||
pub temperature: f32,
|
||||
}
|
||||
|
||||
fn default_provider() -> String { "anthropic".to_string() }
|
||||
fn default_model() -> String { "claude-sonnet-4-20250514".to_string() }
|
||||
fn default_provider() -> String { "openai".to_string() }
|
||||
fn default_model() -> String { "gpt-4o-mini".to_string() }
|
||||
fn default_max_tokens() -> u32 { 4096 }
|
||||
fn default_temperature() -> f32 { 0.7 }
|
||||
|
||||
@@ -79,30 +79,120 @@ pub struct KernelStatusResponse {
|
||||
pub initialized: bool,
|
||||
pub agent_count: usize,
|
||||
pub database_url: Option<String>,
|
||||
pub default_provider: Option<String>,
|
||||
pub default_model: Option<String>,
|
||||
pub base_url: Option<String>,
|
||||
pub model: Option<String>,
|
||||
}
|
||||
|
||||
/// Kernel configuration request
|
||||
///
|
||||
/// Simple configuration: base_url + api_key + model
|
||||
/// Model ID is passed directly to the API without any transformation
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct KernelConfigRequest {
|
||||
/// LLM provider (for preset URLs): anthropic, openai, zhipu, kimi, qwen, deepseek, local, custom
|
||||
#[serde(default = "default_kernel_provider")]
|
||||
pub provider: String,
|
||||
/// Model identifier - passed directly to the API
|
||||
#[serde(default = "default_kernel_model")]
|
||||
pub model: String,
|
||||
/// API key
|
||||
pub api_key: Option<String>,
|
||||
/// Base URL (optional, uses provider default if not specified)
|
||||
pub base_url: Option<String>,
|
||||
/// API protocol: openai or anthropic
|
||||
#[serde(default = "default_api_protocol")]
|
||||
pub api_protocol: String,
|
||||
}
|
||||
|
||||
fn default_api_protocol() -> String { "openai".to_string() }
|
||||
fn default_kernel_provider() -> String { "openai".to_string() }
|
||||
fn default_kernel_model() -> String { "gpt-4o-mini".to_string() }
|
||||
|
||||
/// Initialize the internal ZCLAW Kernel
|
||||
///
|
||||
/// If kernel already exists with the same config, returns existing status.
|
||||
/// If config changed, reboots kernel with new config.
|
||||
#[tauri::command]
|
||||
pub async fn kernel_init(
|
||||
state: State<'_, KernelState>,
|
||||
config_request: Option<KernelConfigRequest>,
|
||||
) -> Result<KernelStatusResponse, String> {
|
||||
let mut kernel_lock = state.lock().await;
|
||||
|
||||
if kernel_lock.is_some() {
|
||||
let kernel = kernel_lock.as_ref().unwrap();
|
||||
return Ok(KernelStatusResponse {
|
||||
initialized: true,
|
||||
agent_count: kernel.list_agents().len(),
|
||||
database_url: None,
|
||||
default_provider: Some("anthropic".to_string()),
|
||||
default_model: Some("claude-sonnet-4-20250514".to_string()),
|
||||
});
|
||||
eprintln!("[kernel_init] Called with config_request: {:?}", config_request);
|
||||
|
||||
// Check if we need to reboot kernel with new config
|
||||
if let Some(kernel) = kernel_lock.as_ref() {
|
||||
// Get current config from kernel
|
||||
let current_config = kernel.config();
|
||||
eprintln!("[kernel_init] Current kernel config: model={}, base_url={}",
|
||||
current_config.llm.model, current_config.llm.base_url);
|
||||
|
||||
// Check if config changed
|
||||
let config_changed = if let Some(ref req) = config_request {
|
||||
let default_base_url = zclaw_kernel::config::KernelConfig::from_provider(
|
||||
&req.provider, "", &req.model, None, &req.api_protocol
|
||||
).llm.base_url;
|
||||
let request_base_url = req.base_url.clone().unwrap_or(default_base_url.clone());
|
||||
|
||||
eprintln!("[kernel_init] Request config: model={}, base_url={}", req.model, request_base_url);
|
||||
eprintln!("[kernel_init] Comparing: current.model={} vs req.model={}, current.base_url={} vs req.base_url={}",
|
||||
current_config.llm.model, req.model, current_config.llm.base_url, request_base_url);
|
||||
|
||||
let changed = current_config.llm.model != req.model ||
|
||||
current_config.llm.base_url != request_base_url;
|
||||
eprintln!("[kernel_init] Config changed: {}", changed);
|
||||
changed
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !config_changed {
|
||||
// Same config, return existing status
|
||||
eprintln!("[kernel_init] Config unchanged, reusing existing kernel");
|
||||
return Ok(KernelStatusResponse {
|
||||
initialized: true,
|
||||
agent_count: kernel.list_agents().len(),
|
||||
database_url: None,
|
||||
base_url: Some(current_config.llm.base_url.clone()),
|
||||
model: Some(current_config.llm.model.clone()),
|
||||
});
|
||||
}
|
||||
|
||||
// Config changed, need to reboot kernel
|
||||
eprintln!("[kernel_init] Config changed, rebooting kernel...");
|
||||
|
||||
// Shutdown old kernel
|
||||
if let Err(e) = kernel.shutdown().await {
|
||||
eprintln!("[kernel_init] Warning: Failed to shutdown old kernel: {}", e);
|
||||
}
|
||||
*kernel_lock = None;
|
||||
}
|
||||
|
||||
// Load configuration
|
||||
let config = zclaw_kernel::config::KernelConfig::default();
|
||||
// Build configuration from request
|
||||
let config = if let Some(req) = &config_request {
|
||||
let api_key = req.api_key.as_deref().unwrap_or("");
|
||||
let base_url = req.base_url.as_deref();
|
||||
|
||||
eprintln!("[kernel_init] Building config: provider={}, model={}, base_url={:?}, api_protocol={}",
|
||||
req.provider, req.model, base_url, req.api_protocol);
|
||||
|
||||
zclaw_kernel::config::KernelConfig::from_provider(
|
||||
&req.provider,
|
||||
api_key,
|
||||
&req.model,
|
||||
base_url,
|
||||
&req.api_protocol,
|
||||
)
|
||||
} else {
|
||||
zclaw_kernel::config::KernelConfig::default()
|
||||
};
|
||||
|
||||
let base_url = config.llm.base_url.clone();
|
||||
let model = config.llm.model.clone();
|
||||
|
||||
eprintln!("[kernel_init] Final config: model={}, base_url={}", model, base_url);
|
||||
|
||||
// Boot kernel
|
||||
let kernel = Kernel::boot(config.clone())
|
||||
@@ -113,12 +203,14 @@ pub async fn kernel_init(
|
||||
|
||||
*kernel_lock = Some(kernel);
|
||||
|
||||
eprintln!("[kernel_init] Kernel booted successfully with new config");
|
||||
|
||||
Ok(KernelStatusResponse {
|
||||
initialized: true,
|
||||
agent_count,
|
||||
database_url: Some(config.database_url),
|
||||
default_provider: Some(config.default_provider),
|
||||
default_model: Some(config.default_model),
|
||||
base_url: Some(base_url),
|
||||
model: Some(model),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -134,15 +226,15 @@ pub async fn kernel_status(
|
||||
initialized: true,
|
||||
agent_count: kernel.list_agents().len(),
|
||||
database_url: None,
|
||||
default_provider: Some("anthropic".to_string()),
|
||||
default_model: Some("claude-sonnet-4-20250514".to_string()),
|
||||
base_url: None,
|
||||
model: None,
|
||||
}),
|
||||
None => Ok(KernelStatusResponse {
|
||||
initialized: false,
|
||||
agent_count: 0,
|
||||
database_url: None,
|
||||
default_provider: None,
|
||||
default_model: None,
|
||||
base_url: None,
|
||||
model: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user