重构: tool_registry 声明式注册基础设施(declare_tool! 宏 + list_projects 试点,零回归)

This commit is contained in:
lxy
2026-08-01 12:56:19 +08:00
parent 438f9692f2
commit c002f0b352
6 changed files with 239 additions and 18 deletions
+3
View File
@@ -44,6 +44,9 @@ pub mod skills;
pub mod stream_recv;
pub mod title;
pub mod tool_registry;
// 声明式工具注册集合(tool_registry 拆分第一步 · 试点容器)。
// 试点:list_projects 已迁声明式 declare_tool! 宏,其余 47 工具仍在 tool_registry.rs。
pub mod tools;
use agentic::conv_state::{ConvState, ConvStateStore};
+9 -18
View File
@@ -18,7 +18,10 @@ use crate::state::AllowedDirs;
/// CRUD list 工具的默认返回上限(防 LLM context 膨胀)
/// 用于 list_projects / list_tasks / list_ideas / list_trash
const MAX_LIST_RESULTS: usize = 50;
///
/// tool_registry 拆分第一步:list_projects 已迁声明式试点(tools/list_projects.rs),
/// 复用本常量保单真相源(避免字面量 50 漂移),故 pub(crate) 暴露给 tools 子模块。
pub(crate) const MAX_LIST_RESULTS: usize = 50;
/// run_command 默认超时(秒)。LLM 可在 args timeout_secs 覆盖此默认值。
/// 提取为常量便于在超时标注处引用同一来源(F-260616-04)。
@@ -522,23 +525,11 @@ fn register_data_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
/// 抽自 register_data_tools(SMELL-P0-2 续拆),【原样移入】,零行为变更。
/// 组内顺序保留原相对顺序(list/update/create/bind/delete/restore/purge/get_count)。
fn register_project_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
registry.register(
"list_projects", "列出所有项目,支持 offset/limit 分页。返回 items(项目列表)、total(总量)、has_more(是否有更多页)。默认 limit=50",
df_ai::ai_tools::object_schema(vec![("offset", "integer", false), ("limit", "integer", false)]), RiskLevel::Low,
{ let db = db.clone(); Box::new(move |args: serde_json::Value| {
let db = db.clone();
Box::pin(async move {
let repo = df_storage::crud::ProjectRepo::new(&db);
let items = repo.list_active().await?; // list_active 排除回收站(deleted_at),防 LLM 看到已软删项目
let total = items.len();
let offset = args["offset"].as_u64().unwrap_or(0) as usize;
let limit = args["limit"].as_u64().unwrap_or(MAX_LIST_RESULTS as u64).min(MAX_LIST_RESULTS as u64) as usize;
let page_items: Vec<_> = items.into_iter().skip(offset).take(limit).collect();
let has_more = (offset + page_items.len()) < total;
Ok(serde_json::json!({ "items": page_items, "total": total, "has_more": has_more }))
})
})},
);
// tool_registry 拆分第一步:list_projects 迁声明式试点(tools/list_projects.rs),
// 改调 declare_tool! 宏注册。其余 7 个项目工具仍手写在下方,零改动(共存)。
// 行为逐字等价(schema/risk/handler body 同源),基线测试 test_build_ai_tool_registry_baseline_tool_count
// 仍断言 48 总量。
super::tools::list_projects::register(registry, db);
registry.register(
"update_project", "更新项目的指定字段(name/status/description/path/stack),需要提供项目 ID、字段名和新值。绑定代码目录推荐改用 bind_directory",
df_ai::ai_tools::object_schema(vec![("id", "string", true), ("field", "string", true), ("value", "string", true)]),
@@ -0,0 +1,57 @@
//! 试点工具:`list_projects` 声明式注册(tool_registry 拆分第一步 · 迁移试点)
//!
//! 验证 `declare_tool!` 宏基础设施可行:从原 `register_project_tools` 第 1 个 register 调用
//! (tool_registry.rs:524-541)迁移到声明式,行为逐字等价(schema/risk/handler body 同源)。
//!
//! 迁移策略(并存零回归):
//! - 原 `register_project_tools` 内的 list_projects register 调用删除,改调本模块 `register`。
//! - 其余 7 个项目工具(update/create/bind/delete/restore/purge/get_count)仍手写在原处,
//! 不动(本步只迁 1 个试点,后续批次渐进)。
//! - `MAX_LIST_RESULTS` 由 tool_registry.rs 改 `pub(crate)` 暴露,试点复用同一常量(单真相源,
//! 避免字面量 50 漂移)。
//!
//! 等价性验证:基线测试 test_build_ai_tool_registry_baseline_tool_count 仍断言 48 总量;
//! execute("list_projects", args) 行为不变(list_active 分页 + has_more 语义)。
use std::sync::Arc;
use df_ai::ai_tools::{object_schema, AiToolRegistry, RiskLevel};
use df_ai::declare_tool;
use df_storage::db::Database;
use crate::commands::ai::tool_registry::MAX_LIST_RESULTS;
/// 用声明式宏注册 `list_projects` 到 `$registry`。
///
/// 与原手写 register(name, desc, schema, risk, handler) 语义 1:1:
/// - name/desc/schema 字符串与 JSON Schema 逐字照搬原定义
/// - risk = RiskLevel::Low(只读,无副作用)
/// - handler body 与原 async move 块逐字一致(list_active 排软删 + offset/limit 分页 +
/// has_more 语义 + 默认/上限对齐 MAX_LIST_RESULTS)
///
/// 唯一差异:闭包包装(`{ let db = db.clone(); Box::new(move |args| { ... Box::pin(async move {...}) }) }`)
/// 改由 `declare_tool!` 宏生成,handler body 直接写业务逻辑。
pub fn register(registry: &mut AiToolRegistry, db: &Arc<Database>) {
declare_tool!(
registry,
db: Arc<Database>,
"list_projects",
"列出所有项目,支持 offset/limit 分页。返回 items(项目列表)、total(总量)、has_more(是否有更多页)。默认 limit=50",
RiskLevel::Low,
schema: object_schema(vec![("offset", "integer", false), ("limit", "integer", false)]),
args => {
let repo = df_storage::crud::ProjectRepo::new(&db);
// list_active 排除回收站(deleted_at),防 LLM 看到已软删项目
let items = repo.list_active().await?;
let total = items.len();
let offset = args["offset"].as_u64().unwrap_or(0) as usize;
let limit = args["limit"]
.as_u64()
.unwrap_or(MAX_LIST_RESULTS as u64)
.min(MAX_LIST_RESULTS as u64) as usize;
let page_items: Vec<_> = items.into_iter().skip(offset).take(limit).collect();
let has_more = (offset + page_items.len()) < total;
Ok(serde_json::json!({ "items": page_items, "total": total, "has_more": has_more }))
}
);
}
+11
View File
@@ -0,0 +1,11 @@
//! 声明式注册工具集合(tool_registry 拆分第一步 · 试点容器)
//!
//! 容纳迁自 `tool_registry.rs` 手写 register 调用、改用 `declare_tool!` 宏的工具。
//! 本步(基础设施 + 1 试点)仅迁 `list_projects`,其余 47 个工具仍在 tool_registry.rs 手写,
//! 后续批次渐进迁移(每批 1-3 个,各自独立文件,互不影响)。
//!
//! 设计:每个工具一个独立文件 + `register(&mut registry, db)` 入口,tool_registry.rs
//! 对应 `register_*` 函数改调本模块(原手写 register 调用删除)。这样工具定义与实现同源、
//! 文件边界清晰,新增工具只加文件 + 在对应 register_* 接一行,不动 4224 行巨函数主体。
pub mod list_projects;