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
summarizer_adapter.rs 和 saas-relay-client.ts 中的 fallback 模型名 (glm-4-flash / glm-4-flash-250414) 在 SaaS relay 中不存在,导致请求被拒绝。 改为未配置时明确报错(fail fast),不再静默使用错误模型。
136 lines
4.3 KiB
Rust
136 lines
4.3 KiB
Rust
//! Summarizer Adapter - Bridges zclaw_growth::SummaryLlmDriver with Tauri LLM Client
|
|
//!
|
|
//! Implements the SummaryLlmDriver trait using the local LlmClient,
|
|
//! enabling L0/L1 summary generation via the user's configured LLM.
|
|
|
|
use zclaw_growth::{MemoryEntry, SummaryLlmDriver, summarizer::{overview_prompt, abstract_prompt}};
|
|
|
|
/// Tauri-side implementation of SummaryLlmDriver using llm::LlmClient
|
|
pub struct TauriSummaryDriver {
|
|
endpoint: String,
|
|
api_key: String,
|
|
model: Option<String>,
|
|
}
|
|
|
|
impl TauriSummaryDriver {
|
|
/// Create a new Tauri summary driver
|
|
pub fn new(endpoint: String, api_key: String, model: Option<String>) -> Self {
|
|
Self {
|
|
endpoint,
|
|
api_key,
|
|
model,
|
|
}
|
|
}
|
|
|
|
/// Check if the driver is configured (has endpoint and api_key)
|
|
pub fn is_configured(&self) -> bool {
|
|
!self.endpoint.is_empty() && !self.api_key.is_empty()
|
|
}
|
|
|
|
/// Call the LLM API with a simple prompt
|
|
async fn call_llm(&self, prompt: String) -> Result<String, String> {
|
|
let client = reqwest::Client::new();
|
|
|
|
let model = self.model.clone().ok_or_else(|| {
|
|
"Summary driver model not configured — kernel_init must be called first".to_string()
|
|
})?;
|
|
|
|
let request = serde_json::json!({
|
|
"model": model,
|
|
"messages": [
|
|
{ "role": "user", "content": prompt }
|
|
],
|
|
"temperature": 0.3,
|
|
"max_tokens": 200,
|
|
});
|
|
|
|
let response = client
|
|
.post(format!("{}/chat/completions", self.endpoint))
|
|
.header("Authorization", format!("Bearer {}", self.api_key))
|
|
.header("Content-Type", "application/json")
|
|
.json(&request)
|
|
.send()
|
|
.await
|
|
.map_err(|e| format!("Summary LLM request failed: {}", e))?;
|
|
|
|
if !response.status().is_success() {
|
|
let status = response.status();
|
|
let body = response.text().await.unwrap_or_default();
|
|
return Err(format!("Summary LLM error {}: {}", status, body));
|
|
}
|
|
|
|
let json: serde_json::Value = response
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("Failed to parse summary response: {}", e))?;
|
|
|
|
json.get("choices")
|
|
.and_then(|c| c.get(0))
|
|
.and_then(|c| c.get("message"))
|
|
.and_then(|m| m.get("content"))
|
|
.and_then(|c| c.as_str())
|
|
.map(|s| s.to_string())
|
|
.ok_or_else(|| "Invalid summary LLM response format".to_string())
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl SummaryLlmDriver for TauriSummaryDriver {
|
|
async fn generate_overview(&self, entry: &MemoryEntry) -> Result<String, String> {
|
|
let prompt = overview_prompt(entry);
|
|
self.call_llm(prompt).await
|
|
}
|
|
|
|
async fn generate_abstract(&self, entry: &MemoryEntry) -> Result<String, String> {
|
|
let prompt = abstract_prompt(entry);
|
|
self.call_llm(prompt).await
|
|
}
|
|
}
|
|
|
|
/// Global summary driver instance (lazy-initialized)
|
|
static SUMMARY_DRIVER: tokio::sync::OnceCell<std::sync::Arc<TauriSummaryDriver>> =
|
|
tokio::sync::OnceCell::const_new();
|
|
|
|
/// Configure the global summary driver
|
|
pub fn configure_summary_driver(driver: TauriSummaryDriver) {
|
|
let _ = SUMMARY_DRIVER.set(std::sync::Arc::new(driver));
|
|
tracing::info!("[SummarizerAdapter] Summary driver configured");
|
|
}
|
|
|
|
/// Check if summary driver is available
|
|
pub fn is_summary_driver_configured() -> bool {
|
|
SUMMARY_DRIVER
|
|
.get()
|
|
.map(|d| d.is_configured())
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
/// Get the global summary driver
|
|
pub fn get_summary_driver() -> Option<std::sync::Arc<TauriSummaryDriver>> {
|
|
SUMMARY_DRIVER.get().cloned()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use zclaw_growth::MemoryType;
|
|
|
|
#[test]
|
|
fn test_summary_driver_not_configured_by_default() {
|
|
assert!(!is_summary_driver_configured());
|
|
}
|
|
|
|
#[test]
|
|
fn test_summary_driver_configure_and_check() {
|
|
let driver = TauriSummaryDriver::new(
|
|
"https://example.com/v1".to_string(),
|
|
"test-key".to_string(),
|
|
None,
|
|
);
|
|
assert!(driver.is_configured());
|
|
|
|
let empty_driver = TauriSummaryDriver::new(String::new(), String::new(), None);
|
|
assert!(!empty_driver.is_configured());
|
|
}
|
|
}
|