重构: String→newtype 强类型 + 工程清理
- df-types: ExecutionId/ToolCallType 从 type 别名改为 newtype 结构体 - df-ai-core: 新增 ToolType newtype / MessageStatus 枚举, ChatMessage.status 从 Option<String> 改为 Option<MessageStatus> - provider: is_active() 改用 MessageStatus::Active 模式匹配 - context/chat/conversation: 构造站点更新为枚举变体 - .gitignore: 添加分析脚本排除项,清理根目录临时文件 - Batch.md: 更新最新提交与新增 Batch 37 记录
This commit is contained in:
@@ -15,7 +15,7 @@ use std::sync::atomic::Ordering;
|
||||
use serde::Serialize;
|
||||
use tauri::{AppHandle, Emitter, Manager, State};
|
||||
|
||||
use df_ai::provider::{ChatMessage, ContentPart};
|
||||
use df_ai::provider::{ChatMessage, ContentPart, MessageStatus};
|
||||
use df_storage::models::AiProviderRecord;
|
||||
use df_types::augmentation::{MentionRef, MentionSpanDto};
|
||||
use df_types::types::new_id;
|
||||
@@ -1147,7 +1147,7 @@ pub async fn ai_chat_clear_context(
|
||||
// 对保护区外 active 消息标 archived_segment(messages_mut 直接改 status)。
|
||||
for t in conv.messages.messages_mut()[..protect_start].iter_mut() {
|
||||
if t.message.is_active() {
|
||||
t.message.status = Some("archived_segment".to_string());
|
||||
t.message.status = Some(MessageStatus::ArchivedSegment);
|
||||
}
|
||||
}
|
||||
drop(session);
|
||||
|
||||
@@ -15,7 +15,7 @@ use std::sync::atomic::Ordering;
|
||||
|
||||
use tauri::{AppHandle, State};
|
||||
|
||||
use df_ai::provider::{ChatMessage, MessageRole};
|
||||
use df_ai::provider::{ChatMessage, MessageRole, MessageStatus};
|
||||
use df_storage::models::AiMessageRecord;
|
||||
use df_types::types::new_id;
|
||||
|
||||
@@ -85,10 +85,10 @@ pub fn record_to_message(rec: &AiMessageRecord) -> ChatMessage {
|
||||
.filter(|s| !s.is_empty())
|
||||
.and_then(|s| serde_json::from_str(s).ok());
|
||||
// status "active" → None(归一化,对齐 ChatMessage 默认语义 None=正常可见)
|
||||
let status = if rec.status == "active" {
|
||||
let status = if rec.status == "active" || rec.status.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(rec.status.clone())
|
||||
MessageStatus::from_db_str(&rec.status)
|
||||
};
|
||||
ChatMessage {
|
||||
id: Some(rec.id.clone()),
|
||||
@@ -127,7 +127,10 @@ pub fn message_to_record(
|
||||
.tool_calls
|
||||
.as_ref()
|
||||
.map(|t| serde_json::to_string(t).unwrap_or_default());
|
||||
let status = msg.status.clone().unwrap_or_else(|| "active".to_string());
|
||||
let status = msg.status
|
||||
.as_ref()
|
||||
.map(|s| s.as_db_str().to_string())
|
||||
.unwrap_or_else(|| "active".to_string());
|
||||
AiMessageRecord {
|
||||
id,
|
||||
conversation_id: conversation_id.to_string(),
|
||||
@@ -634,7 +637,7 @@ pub async fn ai_conversation_export(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use df_ai::provider::{ChatMessage, ContentPart, MessageRole, ToolCall, ToolCallFunction};
|
||||
use df_ai::provider::{ChatMessage, ContentPart, MessageRole, MessageStatus, ToolCall, ToolCallFunction, ToolType};
|
||||
|
||||
fn base_msg() -> ChatMessage {
|
||||
ChatMessage {
|
||||
@@ -746,20 +749,22 @@ mod tests {
|
||||
assert_eq!(back.tool_call_id.as_deref(), Some("call_abc"));
|
||||
}
|
||||
|
||||
/// status 各值("truncated"/"compressed")round-trip(非 active 原样保留)
|
||||
/// status 各值(Truncated/Compressed)round-trip
|
||||
#[test]
|
||||
fn roundtrip_status_non_active_preserved() {
|
||||
let mut msg = base_msg();
|
||||
msg.status = Some("truncated".into());
|
||||
msg.status = Some(MessageStatus::Truncated);
|
||||
let rec = message_to_record(&msg, "c", 0, "ts");
|
||||
assert_eq!(rec.status, "truncated");
|
||||
let back = record_to_message(&rec);
|
||||
assert_eq!(back.status.as_deref(), Some("truncated"));
|
||||
assert_eq!(back.status, Some(MessageStatus::Truncated));
|
||||
|
||||
// compressed
|
||||
msg.status = Some("compressed".into());
|
||||
msg.status = Some(MessageStatus::Compressed);
|
||||
let rec = message_to_record(&msg, "c", 0, "ts");
|
||||
assert_eq!(rec.status, "compressed");
|
||||
let back = record_to_message(&rec);
|
||||
assert_eq!(back.status, Some(MessageStatus::Compressed));
|
||||
}
|
||||
|
||||
/// id=None 兜底:`msg_{conv}_{seq}`
|
||||
@@ -802,7 +807,7 @@ mod tests {
|
||||
},
|
||||
}]);
|
||||
msg.model = Some("m".into());
|
||||
msg.status = Some("compressed".into());
|
||||
msg.status = Some(MessageStatus::Compressed);
|
||||
msg.reasoning_content = Some("r".into());
|
||||
msg.timestamp = Some(123);
|
||||
let rec = message_to_record(&msg, "conv_full", 7, "ts_full");
|
||||
|
||||
@@ -102,10 +102,10 @@ pub async fn run_workflow_inner(
|
||||
let runtime_dag = state.registry.build_dag(&dag).map_err(err_str)?;
|
||||
|
||||
// 2. 写入执行记录(status=running)
|
||||
let execution_id = new_id();
|
||||
let execution_id: ExecutionId = new_id().into();
|
||||
let dag_json = serde_json::to_string(&dag).map_err(err_str)?;
|
||||
let record = WorkflowRecord {
|
||||
id: execution_id.clone(),
|
||||
id: execution_id.to_string(),
|
||||
name,
|
||||
dag_json,
|
||||
status: "running".to_string(),
|
||||
@@ -149,13 +149,15 @@ pub async fn run_workflow_inner(
|
||||
&event,
|
||||
WorkflowEvent::WorkflowCompleted { execution_id, .. }
|
||||
| WorkflowEvent::WorkflowFailed { execution_id, .. }
|
||||
// execution_id: WorkflowEvent 中反序列化的 ExecutionId(#[serde(default)]),
|
||||
// forward_exec_id: 本 forward 循环所属的 ExecutionId,同类型直接比较
|
||||
if execution_id == &forward_exec_id
|
||||
);
|
||||
let payload = WorkflowEventPayload {
|
||||
execution_id: forward_exec_id.clone(),
|
||||
event,
|
||||
};
|
||||
if let Err(e) = forward_app.emit("workflow-event", &payload) {
|
||||
if let Err(e) = forward_app.emit("workflow-event", &payload) {
|
||||
tracing::warn!("工作流事件转发失败: {}", e);
|
||||
}
|
||||
if finished {
|
||||
@@ -186,21 +188,21 @@ pub async fn run_workflow_inner(
|
||||
match workflows.get_by_id(&forward_exec_id).await {
|
||||
Ok(Some(record)) => {
|
||||
let terminal = match record.status.as_str() {
|
||||
"completed" => Some(WorkflowEvent::WorkflowCompleted {
|
||||
execution_id: forward_exec_id.clone(),
|
||||
total_duration_ms: 0,
|
||||
}),
|
||||
"failed" => Some(WorkflowEvent::WorkflowFailed {
|
||||
execution_id: forward_exec_id.clone(),
|
||||
error: "工作流执行失败(Lagged 兜底补发,详情见 DB)"
|
||||
.to_string(),
|
||||
failed_node: String::new(),
|
||||
}),
|
||||
"cancelled" => Some(WorkflowEvent::WorkflowFailed {
|
||||
execution_id: forward_exec_id.clone(),
|
||||
error: "工作流被取消(Lagged 兜底补发)".to_string(),
|
||||
failed_node: String::new(),
|
||||
}),
|
||||
"completed" => Some(WorkflowEvent::WorkflowCompleted {
|
||||
execution_id: forward_exec_id.clone(),
|
||||
total_duration_ms: 0,
|
||||
}),
|
||||
"failed" => Some(WorkflowEvent::WorkflowFailed {
|
||||
execution_id: forward_exec_id.clone(),
|
||||
error: "工作流执行失败(Lagged 兜底补发,详情见 DB)"
|
||||
.to_string(),
|
||||
failed_node: String::new(),
|
||||
}),
|
||||
"cancelled" => Some(WorkflowEvent::WorkflowFailed {
|
||||
execution_id: forward_exec_id.clone(),
|
||||
error: "工作流被取消(Lagged 兜底补发)".to_string(),
|
||||
failed_node: String::new(),
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(synth_event) = terminal {
|
||||
@@ -351,7 +353,7 @@ pub async fn run_workflow_inner(
|
||||
}
|
||||
});
|
||||
|
||||
Ok(execution_id)
|
||||
Ok(execution_id.to_string())
|
||||
}
|
||||
|
||||
/// 列出全部工作流执行记录
|
||||
|
||||
@@ -48,6 +48,7 @@ use df_workflow::state::StateMachine;
|
||||
|
||||
use crate::commands::ai::augmentation::ResolverRegistry;
|
||||
use crate::commands::ai::AiSession;
|
||||
use df_types::types::ExecutionId;
|
||||
|
||||
/// 应用全局状态 — 通过 `app.manage()` 注入,command 中以 `State<'_, AppState>` 取用
|
||||
pub struct AppState {
|
||||
@@ -152,7 +153,7 @@ pub struct AppState {
|
||||
/// clone 共享底层 HashMap,与下沉到 NodeContext.node_status 的是同一份);
|
||||
/// cancel_workflow_node IPC 经 execution_id 取出后 set_cancelled,
|
||||
/// 直达运行中 HumanNode 的 is_cancelled 轮询。执行完成(成功/失败)后移除条目。
|
||||
pub workflow_state_registry: Arc<Mutex<HashMap<String, StateMachine>>>,
|
||||
pub workflow_state_registry: Arc<Mutex<HashMap<ExecutionId, StateMachine>>>,
|
||||
// ── F-260619-03 Phase A: AI 工具文件访问授权目录白名单 ──
|
||||
/// AI 文件工具(read/write/list/patch/...)可访问的授权目录池。
|
||||
/// Phase A 仅持久化白名单(Settings KV `allowed_dirs` 加载),
|
||||
|
||||
Reference in New Issue
Block a user