refactor: 统一项目名称从OpenFang到ZCLAW
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
重构所有代码和文档中的项目名称,将OpenFang统一更新为ZCLAW。包括: - 配置文件中的项目名称 - 代码注释和文档引用 - 环境变量和路径 - 类型定义和接口名称 - 测试用例和模拟数据 同时优化部分代码结构,移除未使用的模块,并更新相关依赖项。
This commit is contained in:
@@ -67,6 +67,13 @@ pub struct VikingAddResult {
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EmbeddingConfigResult {
|
||||
pub provider: String,
|
||||
pub configured: bool,
|
||||
}
|
||||
|
||||
// === Global Storage Instance ===
|
||||
|
||||
/// Global storage instance
|
||||
@@ -100,12 +107,20 @@ pub async fn init_storage() -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the storage instance (public for use by other modules)
|
||||
/// Get the storage instance, initializing on first access if needed
|
||||
pub async fn get_storage() -> Result<Arc<SqliteStorage>, String> {
|
||||
if let Some(storage) = STORAGE.get() {
|
||||
return Ok(storage.clone());
|
||||
}
|
||||
|
||||
// Attempt lazy initialization
|
||||
tracing::info!("[VikingCommands] Storage not yet initialized, attempting lazy init...");
|
||||
init_storage().await?;
|
||||
|
||||
STORAGE
|
||||
.get()
|
||||
.cloned()
|
||||
.ok_or_else(|| "Storage not initialized. Call init_storage() first.".to_string())
|
||||
.ok_or_else(|| "Storage initialization failed. Check logs for details.".to_string())
|
||||
}
|
||||
|
||||
/// Get storage directory for status
|
||||
@@ -217,12 +232,24 @@ pub async fn viking_find(
|
||||
Ok(entries
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, entry)| VikingFindResult {
|
||||
uri: entry.uri,
|
||||
score: 1.0 - (i as f64 * 0.1), // Simple scoring based on rank
|
||||
content: entry.content,
|
||||
level: "L1".to_string(),
|
||||
overview: None,
|
||||
.map(|(i, entry)| {
|
||||
// Use overview (L1) when available, full content otherwise (L2)
|
||||
let (content, level, overview) = if let Some(ref ov) = entry.overview {
|
||||
if !ov.is_empty() {
|
||||
(ov.clone(), "L1".to_string(), None)
|
||||
} else {
|
||||
(entry.content.clone(), "L2".to_string(), None)
|
||||
}
|
||||
} else {
|
||||
(entry.content.clone(), "L2".to_string(), None)
|
||||
};
|
||||
VikingFindResult {
|
||||
uri: entry.uri,
|
||||
score: 1.0 - (i as f64 * 0.1), // Simple scoring based on rank
|
||||
content,
|
||||
level,
|
||||
overview,
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
@@ -309,7 +336,7 @@ pub async fn viking_ls(path: String) -> Result<Vec<VikingResource>, String> {
|
||||
|
||||
/// Read memory content
|
||||
#[tauri::command]
|
||||
pub async fn viking_read(uri: String, _level: Option<String>) -> Result<String, String> {
|
||||
pub async fn viking_read(uri: String, level: Option<String>) -> Result<String, String> {
|
||||
let storage = get_storage().await?;
|
||||
|
||||
let entry = storage
|
||||
@@ -318,7 +345,34 @@ pub async fn viking_read(uri: String, _level: Option<String>) -> Result<String,
|
||||
.map_err(|e| format!("Failed to read memory: {}", e))?;
|
||||
|
||||
match entry {
|
||||
Some(e) => Ok(e.content),
|
||||
Some(e) => {
|
||||
// Support level-based content retrieval
|
||||
let content = match level.as_deref() {
|
||||
Some("L0") | Some("l0") => {
|
||||
// L0: abstract_summary (keywords)
|
||||
e.abstract_summary
|
||||
.filter(|s| !s.is_empty())
|
||||
.unwrap_or_else(|| {
|
||||
// Fallback: first 50 chars of overview
|
||||
e.overview
|
||||
.as_ref()
|
||||
.map(|ov| ov.chars().take(50).collect())
|
||||
.unwrap_or_else(|| e.content.chars().take(50).collect())
|
||||
})
|
||||
}
|
||||
Some("L1") | Some("l1") => {
|
||||
// L1: overview (1-2 sentence summary)
|
||||
e.overview
|
||||
.filter(|s| !s.is_empty())
|
||||
.unwrap_or_else(|| truncate_text(&e.content, 200))
|
||||
}
|
||||
_ => {
|
||||
// L2 or default: full content
|
||||
e.content
|
||||
}
|
||||
};
|
||||
Ok(content)
|
||||
}
|
||||
None => Err(format!("Memory not found: {}", uri)),
|
||||
}
|
||||
}
|
||||
@@ -442,6 +496,16 @@ pub async fn viking_inject_prompt(
|
||||
|
||||
// === Helper Functions ===
|
||||
|
||||
/// Truncate text to approximately max_chars characters
|
||||
fn truncate_text(text: &str, max_chars: usize) -> String {
|
||||
if text.chars().count() <= max_chars {
|
||||
text.to_string()
|
||||
} else {
|
||||
let truncated: String = text.chars().take(max_chars).collect();
|
||||
format!("{}...", truncated)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse URI to extract components
|
||||
fn parse_uri(uri: &str) -> Result<(String, MemoryType, String), String> {
|
||||
// Expected format: agent://{agent_id}/{type}/{category}
|
||||
@@ -462,6 +526,136 @@ fn parse_uri(uri: &str) -> Result<(String, MemoryType, String), String> {
|
||||
Ok((agent_id, memory_type, category))
|
||||
}
|
||||
|
||||
/// Configure embedding for semantic memory search
|
||||
/// Configures both SqliteStorage (VikingPanel) and PersistentMemoryStore (chat flow)
|
||||
#[tauri::command]
|
||||
pub async fn viking_configure_embedding(
|
||||
provider: String,
|
||||
api_key: String,
|
||||
model: Option<String>,
|
||||
endpoint: Option<String>,
|
||||
) -> Result<EmbeddingConfigResult, String> {
|
||||
let storage = get_storage().await?;
|
||||
|
||||
// 1. Configure SqliteStorage (VikingPanel / VikingCommands)
|
||||
let config_viking = crate::llm::EmbeddingConfig {
|
||||
provider: provider.clone(),
|
||||
api_key: api_key.clone(),
|
||||
endpoint: endpoint.clone(),
|
||||
model: model.clone(),
|
||||
};
|
||||
|
||||
let client_viking = crate::llm::EmbeddingClient::new(config_viking);
|
||||
let adapter = crate::embedding_adapter::TauriEmbeddingAdapter::new(client_viking);
|
||||
|
||||
storage
|
||||
.configure_embedding(std::sync::Arc::new(adapter))
|
||||
.await
|
||||
.map_err(|e| format!("Failed to configure embedding: {}", e))?;
|
||||
|
||||
// 2. Configure PersistentMemoryStore (chat flow)
|
||||
let config_memory = crate::llm::EmbeddingConfig {
|
||||
provider: provider.clone(),
|
||||
api_key,
|
||||
endpoint,
|
||||
model,
|
||||
};
|
||||
let client_memory = std::sync::Arc::new(crate::llm::EmbeddingClient::new(config_memory));
|
||||
|
||||
let embed_fn: crate::memory::EmbedFn = {
|
||||
let client_arc = client_memory.clone();
|
||||
std::sync::Arc::new(move |text: &str| {
|
||||
let client = client_arc.clone();
|
||||
let text = text.to_string();
|
||||
Box::pin(async move {
|
||||
let response = client.embed(&text).await?;
|
||||
Ok(response.embedding)
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
crate::memory::configure_embedding_client(embed_fn);
|
||||
|
||||
tracing::info!("[VikingCommands] Embedding configured with provider: {} (both storage systems)", provider);
|
||||
|
||||
Ok(EmbeddingConfigResult {
|
||||
provider,
|
||||
configured: true,
|
||||
})
|
||||
}
|
||||
|
||||
/// Configure summary driver for L0/L1 auto-generation
|
||||
#[tauri::command]
|
||||
pub async fn viking_configure_summary_driver(
|
||||
endpoint: String,
|
||||
api_key: String,
|
||||
model: Option<String>,
|
||||
) -> Result<bool, String> {
|
||||
let driver = crate::summarizer_adapter::TauriSummaryDriver::new(endpoint, api_key, model);
|
||||
crate::summarizer_adapter::configure_summary_driver(driver);
|
||||
|
||||
tracing::info!("[VikingCommands] Summary driver configured");
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Store a memory and optionally generate L0/L1 summaries in the background
|
||||
#[tauri::command]
|
||||
pub async fn viking_store_with_summaries(
|
||||
uri: String,
|
||||
content: String,
|
||||
) -> Result<VikingAddResult, String> {
|
||||
let storage = get_storage().await?;
|
||||
let (agent_id, memory_type, category) = parse_uri(&uri)?;
|
||||
|
||||
let entry = MemoryEntry::new(&agent_id, memory_type, &category, content);
|
||||
|
||||
// Store the entry immediately (L2 full content)
|
||||
storage
|
||||
.store(&entry)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to store memory: {}", e))?;
|
||||
|
||||
// Background: generate L0/L1 summaries if driver is configured
|
||||
if crate::summarizer_adapter::is_summary_driver_configured() {
|
||||
let entry_uri = entry.uri.clone();
|
||||
let storage_clone = storage.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Some(driver) = crate::summarizer_adapter::get_summary_driver() {
|
||||
let (overview, abstract_summary) =
|
||||
zclaw_growth::summarizer::generate_summaries(driver.as_ref(), &entry).await;
|
||||
|
||||
if overview.is_some() || abstract_summary.is_some() {
|
||||
// Update the entry with summaries
|
||||
let updated = MemoryEntry {
|
||||
overview,
|
||||
abstract_summary,
|
||||
..entry
|
||||
};
|
||||
|
||||
if let Err(e) = storage_clone.store(&updated).await {
|
||||
tracing::debug!(
|
||||
"[VikingCommands] Failed to update summaries for {}: {}",
|
||||
entry_uri,
|
||||
e
|
||||
);
|
||||
} else {
|
||||
tracing::debug!(
|
||||
"[VikingCommands] Updated L0/L1 summaries for {}",
|
||||
entry_uri
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(VikingAddResult {
|
||||
uri,
|
||||
status: "added".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
// === Tests ===
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user