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

重构所有代码和文档中的项目名称,将OpenFang统一更新为ZCLAW。包括:
- 配置文件中的项目名称
- 代码注释和文档引用
- 环境变量和路径
- 类型定义和接口名称
- 测试用例和模拟数据

同时优化部分代码结构,移除未使用的模块,并更新相关依赖项。
This commit is contained in:
iven
2026-03-27 07:36:03 +08:00
parent 4b08804aa9
commit 0d4fa96b82
226 changed files with 7288 additions and 5788 deletions

View File

@@ -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)]