diff --git a/Cargo.toml b/Cargo.toml index f27ad72..d96cb58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,26 +3,72 @@ name = "agcore" version = "0.3.0" edition = "2024" +[features] +default = ["full"] + +# === 模块级 features === +document = [] +llm-types = [] +prompt = ["llm-types"] +llm = ["llm-types", "tokio", "async-stream", "futures-core", "tokio-stream"] +tools = ["llm-types", "futures", "tokio-util", "tokio"] +tools-mcp = ["tools", "reqwest"] +# memory 模块依赖 llm(conversation/vector_store 使用 compact/embedding)、tokio(knowledge.rs 使用 Mutex)、time(types.rs 使用 OffsetDateTime) +memory = ["document", "llm", "tokio", "time"] +memory-sqlite = ["memory", "rusqlite", "time"] +agent = ["llm", "tools", "memory", "futures-util"] +engine = ["agent"] + +# === Provider features === +# Provider features — openai/anthropic 额外依赖 bytes(流式解析)和 futures-util(Stream 组合) +provider-openai = ["llm", "reqwest", "bytes", "futures-util"] +provider-anthropic = ["llm", "reqwest", "bytes", "futures-util"] +# deepseek/qwen 使用 openai_compat 适配层,不需要 bytes 和 futures-util +provider-deepseek = ["llm", "reqwest"] +provider-qwen = ["llm", "reqwest"] +provider-ollama = ["llm", "reqwest"] + +# === 工具 features === +tracing-init = ["tracing-subscriber"] + +# === 快捷组合 === +full = [ + "document", "llm-types", "prompt", "llm", + "tools", "tools-mcp", + "memory", "memory-sqlite", + "agent", "engine", + "provider-openai", "provider-anthropic", "provider-deepseek", + "provider-qwen", "provider-ollama", + "tracing-init", +] +light = ["llm", "provider-openai", "tools", "tools-mcp", "memory", "agent", "engine", "prompt", "document"] +chat = ["agent", "provider-openai"] +multi = ["engine", "provider-openai"] + [dependencies] -tokio = { version = "1", features = ["full"] } -reqwest = { version = "0.12", features = ["json", "stream"] } +# 始终编译的轻量依赖(5 个,不参与门控) serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "2" async-trait = "0.1" tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } -tokio-stream = "0.1" -futures = "0.3" -futures-util = "0.3" -futures-core = "0.3" -bytes = "1" -async-stream = "0.3" -tokio-util = { version = "0.7", features = ["rt"] } -time = { version = "0.3", features = ["serde", "parsing", "formatting", "macros"] } -rusqlite = { version = "0.32", features = ["bundled"] } + +# 12 个重型依赖(全部 optional) +tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync", "time", "macros", "process", "io-util"], optional = true } +reqwest = { version = "0.12", features = ["json", "stream"], optional = true } +rusqlite = { version = "0.32", features = ["bundled"], optional = true } +tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true } +tokio-stream = { version = "0.1", optional = true } +futures = { version = "0.3", optional = true } +futures-util = { version = "0.3", optional = true } +futures-core = { version = "0.3", optional = true } +bytes = { version = "1", optional = true } +async-stream = { version = "0.3", optional = true } +tokio-util = { version = "0.7", features = ["rt"], optional = true } +time = { version = "0.3", features = ["serde", "parsing", "formatting", "macros"], optional = true } [dev-dependencies] +tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } dotenvy = "0.15.7" wiremock = "0.6" temp-env = "0.3" diff --git a/docs/26-step1-phase20-cargo-features-implementation.md b/docs/26-step1-phase20-cargo-features-implementation.md new file mode 100644 index 0000000..4a4ecd2 --- /dev/null +++ b/docs/26-step1-phase20-cargo-features-implementation.md @@ -0,0 +1,694 @@ +# AG Core v0.3.2 Step 1(Phase 20)— Cargo Features 基础设施改造实施方案 + +## 1. 背景与目标 + +**背景**:agcore 是一个 Rust 编写的智能体核心工具箱,目前约 23,718 行、66 个源文件。v0.3.0 发布后,所有模块在编译时全量捆绑,下游用户无法按需选择模块,即使只使用 LLM 对话也需要编译 sqlite / MCP / agent 引擎等全部依赖。 + +**目标**:通过 Cargo features 拆分让下游按需选择模块。Step 1 是基础设施变更 —— `Cargo.toml` features 定义 + 依赖 optional 化 + 必要的子模块 cfg 门控,编译通过后打 checkpoint。 + +**预期效果**: +- `default = ["full"]` → v0.3.0 用户零迁移成本 +- 最小组合(`document`)零重型外部依赖(仅依赖始终编译的轻量依赖:serde/serde_json/thiserror/async-trait/tracing) +- 纯对话组合(`chat + provider-openai`)仅需 ~10 个依赖,不含 sqlite / MCP / engine + +## 2. 需求分析 + +### 2.1 约束条件 + +| # | 约束 | 说明 | +|---|------|------| +| 1 | `default = ["full"]` | 保持向后兼容,v0.3.0 用户零迁移成本 | +| 2 | `document` feature 零重型外部依赖(仅依赖始终编译的轻量依赖:serde/serde_json/thiserror/async-trait/tracing) | 纯 std + 始终编译的轻量依赖(serde/serde_json/thiserror/async-trait/tracing) | +| 3 | tokio 从 `["full"]` 拆细 | 已验证全库无 net/fs/signal 使用,拆为 `["rt", "sync", "time", "macros", "process", "io-util"]` | +| 4 | 重型依赖全部 optional | tokio、reqwest、rusqlite、tracing-subscriber、tokio-stream、futures、futures-util、futures-core、bytes、async-stream、tokio-util、time | +| 5 | 始终编译的轻量依赖 | serde、serde_json、thiserror、async-trait、tracing | + +### 2.2 关键决策 + +| # | 决策 | 理由 | +|---|------|------| +| 1 | `tools` feature 必须 `imply tokio` | `src/tools/registry.rs` 使用 `tokio::time::timeout` | +| 2 | `pub mod llm` 门控条件为 `any(feature = "llm-types", feature = "llm")` | `prompt → llm-types` 路径需要 llm 模块编译,但只需 types 子模块 | +| 3 | 测试 dev-dependencies 加 `tokio = { version = "1", features = ["rt", "macros"] }` | 现有 `#[tokio::test]` 需要 tokio runtime | +| 4 | 快捷组合名保持原名(chat/multi/light) | 文档中说明各组合包含的 feature 约束 | +| 5 | `init_tracing()` 函数整体用 `#[cfg(feature = "tracing-init")]` 包裹 | 避免 `use tracing_subscriber` 出现在未启用 feature 时编译失败 | + +## 3. 方案设计 + +### 3.1 Features 定义(完整 Cargo.toml `[features]` 草案) + +```toml +[features] +default = ["full"] + +# === 模块级 features === +document = [] +llm-types = [] +prompt = ["llm-types"] +llm = ["llm-types", "tokio", "async-stream", "futures-core", "tokio-stream"] +tools = ["llm-types", "futures", "tokio-util", "tokio"] +tools-mcp = ["tools", "reqwest"] +# memory 模块依赖 llm(conversation/vector_store 使用 compact/embedding)、tokio(knowledge.rs 使用 Mutex)、time(types.rs 使用 OffsetDateTime) +memory = ["document", "llm", "tokio", "time"] +memory-sqlite = ["memory", "rusqlite", "time"] +agent = ["llm", "tools", "memory", "futures-util"] +engine = ["agent"] + +# === Provider features === +# Provider features — openai/anthropic 额外依赖 bytes(流式解析)和 futures-util(Stream 组合) +provider-openai = ["llm", "reqwest", "bytes", "futures-util"] +provider-anthropic = ["llm", "reqwest", "bytes", "futures-util"] +# deepseek/qwen 使用 openai_compat 适配层,不需要 bytes 和 futures-util +provider-deepseek = ["llm", "reqwest"] +provider-qwen = ["llm", "reqwest"] +provider-ollama = ["llm", "reqwest"] + +# === 工具 features === +tracing-init = ["tracing-subscriber"] + +# === 快捷组合 === +full = [ + "document", "llm-types", "prompt", "llm", + "tools", "tools-mcp", + "memory", "memory-sqlite", + "agent", "engine", + "provider-openai", "provider-anthropic", "provider-deepseek", + "provider-qwen", "provider-ollama", + "tracing-init", +] +light = ["llm", "provider-openai", "tools", "tools-mcp", "memory", "agent", "engine", "prompt", "document"] +chat = ["agent", "provider-openai"] +multi = ["engine", "provider-openai"] +``` + +**features 依赖图(简略)**: + +``` +document (零外部依赖) + └── memory (+llm, +tokio, +time) ─── memory-sqlite (+rusqlite, +time) + +llm-types (零依赖) + ├── prompt + └── llm (+tokio, +async-stream, +futures-core, +tokio-stream) + ├── tools (+futures, +tokio-util) ─── tools-mcp (+reqwest) + ├── provider-openai / provider-anthropic (+reqwest, +bytes, +futures-util) + ├── provider-deepseek / provider-qwen / provider-ollama (+reqwest) + └── agent (+tools, +memory, +futures-util) ─── engine +``` + +### 3.2 依赖 optional 化方案 + +**始终编译(5 个,不参与门控)**: + +```toml +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "2" +async-trait = "0.1" +tracing = "0.1" +``` + +**12 个依赖加 `optional = true`**: + +| 依赖 | 原声明 | 新声明 | +|------|--------|--------| +| tokio | `{ version = "1", features = ["full"] }` | `{ version = "1", features = ["rt", "sync", "time", "macros", "process", "io-util"], optional = true }` | +| reqwest | `{ version = "0.12", features = ["json", "stream"] }` | `{ version = "0.12", features = ["json", "stream"], optional = true }` | +| rusqlite | `{ version = "0.32", features = ["bundled"] }` | `{ version = "0.32", features = ["bundled"], optional = true }` | +| tracing-subscriber | `{ version = "0.3", features = ["env-filter"] }` | `{ version = "0.3", features = ["env-filter"], optional = true }` | +| tokio-stream | `{ version = "0.1" }` | `{ version = "0.1", optional = true }` | +| futures | `{ version = "0.3" }` | `{ version = "0.3", optional = true }` | +| futures-util | `{ version = "0.3" }` | `{ version = "0.3", optional = true }` | +| futures-core | `{ version = "0.3" }` | `{ version = "0.3", optional = true }` | +| bytes | `{ version = "1" }` | `{ version = "1", optional = true }` | +| async-stream | `{ version = "0.3" }` | `{ version = "0.3", optional = true }` | +| tokio-util | `{ version = "0.7", features = ["rt", "sync"] }` | `{ version = "0.7", features = ["rt", "sync"], optional = true }` | +| time | `{ version = "0.3", features = ["serde", "parsing", "formatting", "macros"] }` | `{ version = "0.3", features = ["serde", "parsing", "formatting", "macros"], optional = true }` | + +**dev-dependencies 新增**: + +```toml +[dev-dependencies] +tokio = { version = "1", features = ["rt", "macros"] } +``` + +### 3.3 源文件改动清单 + +共涉及 **8 个文件**(预估 ~100 行改动):`Cargo.toml`、`src/lib.rs`、`src/llm.rs`、`src/llm/cycle.rs`、`src/tools.rs`、`src/memory.rs`、`src/memory/store.rs`、`src/agent/session.rs` + +--- + +#### 文件 1:`Cargo.toml` + +**改动 1.1** — 新增 `[features]` 表(约 45 行,插入在 `[package]` 之后、`[dependencies]` 之前) + +```diff ++ [features] ++ default = ["full"] ++ ++ # === 模块级 features === ++ document = [] ++ llm-types = [] ++ prompt = ["llm-types"] ++ llm = ["llm-types", "tokio", "async-stream", "futures-core", "tokio-stream"] ++ tools = ["llm-types", "futures", "tokio-util", "tokio"] ++ tools-mcp = ["tools", "reqwest"] ++ # memory 模块依赖 llm(conversation/vector_store 使用 compact/embedding)、tokio(knowledge.rs 使用 Mutex)、time(types.rs 使用 OffsetDateTime) ++ memory = ["document", "llm", "tokio", "time"] ++ memory-sqlite = ["memory", "rusqlite", "time"] ++ agent = ["llm", "tools", "memory", "futures-util"] ++ engine = ["agent"] ++ ++ # === Provider features === ++ # Provider features — openai/anthropic 额外依赖 bytes(流式解析)和 futures-util(Stream 组合) ++ provider-openai = ["llm", "reqwest", "bytes", "futures-util"] ++ provider-anthropic = ["llm", "reqwest", "bytes", "futures-util"] ++ # deepseek/qwen 使用 openai_compat 适配层,不需要 bytes 和 futures-util ++ provider-deepseek = ["llm", "reqwest"] ++ provider-qwen = ["llm", "reqwest"] ++ provider-ollama = ["llm", "reqwest"] ++ ++ # === 工具 features === ++ tracing-init = ["tracing-subscriber"] ++ ++ # === 快捷组合 === ++ full = [ ++ "document", "llm-types", "prompt", "llm", ++ "tools", "tools-mcp", ++ "memory", "memory-sqlite", ++ "agent", "engine", ++ "provider-openai", "provider-anthropic", "provider-deepseek", ++ "provider-qwen", "provider-ollama", ++ "tracing-init", ++ ] ++ light = ["llm", "provider-openai", "tools", "tools-mcp", "memory", "agent", "engine", "prompt", "document"] ++ chat = ["agent", "provider-openai"] ++ multi = ["engine", "provider-openai"] +``` + +**改动 1.2** — tokio 依赖声明修改 + +```diff +- tokio = { version = "1", features = ["full"] } ++ tokio = { version = "1", features = ["rt", "sync", "time", "macros", "process", "io-util"], optional = true } +``` + +**改动 1.3** — 11 个重型依赖逐行加 `optional = true` + +```diff +- reqwest = { version = "0.12", features = ["json", "stream"] } ++ reqwest = { version = "0.12", features = ["json", "stream"], optional = true } + +- rusqlite = { version = "0.32", features = ["bundled"] } ++ rusqlite = { version = "0.32", features = ["bundled"], optional = true } + +- tracing-subscriber = { version = "0.3", features = ["env-filter"] } ++ tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true } + +- tokio-stream = "0.1" ++ tokio-stream = { version = "0.1", optional = true } + +- futures = "0.3" ++ futures = { version = "0.3", optional = true } + +- futures-util = "0.3" ++ futures-util = { version = "0.3", optional = true } + +- futures-core = "0.3" ++ futures-core = { version = "0.3", optional = true } + +- bytes = "1" ++ bytes = { version = "1", optional = true } + +- async-stream = "0.3" ++ async-stream = { version = "0.3", optional = true } + +- tokio-util = { version = "0.7", features = ["rt"] } ++ tokio-util = { version = "0.7", features = ["rt", "sync"], optional = true } + +- time = { version = "0.3", features = ["serde", "parsing", "formatting", "macros"] } ++ time = { version = "0.3", features = ["serde", "parsing", "formatting", "macros"], optional = true } +``` + +**改动 1.4** — `[dev-dependencies]` 新增 tokio + +```diff ++ [dev-dependencies] ++ tokio = { version = "1", features = ["rt", "macros"] } +``` + +**说明**:如果原 `Cargo.toml` 已有 `[dev-dependencies]` 则追加该行;若无则新增整个 section。 + +--- + +#### 文件 2:`src/lib.rs`(当前约 26 行 → 改动后约 40 行) + +**当前内容(参考)**: +```rust +//! agcore —— 智能体(Agent)核心工具箱。 + +pub mod llm; +pub mod document; +pub mod prompt; +pub mod tools; +pub mod memory; +pub mod agent; +pub mod engine; + +pub use document::Document; + +use tracing_subscriber::{EnvFilter, fmt, prelude::*}; +static INIT: std::sync::Once = std::sync::Once::new(); +pub fn init_tracing() { + INIT.call_once(|| { + let filter = EnvFilter::try_from_default_env() + .unwrap_or_else(|_| EnvFilter::new("agcore=info")); + tracing_subscriber::registry() + .with(fmt::layer()) + .with(filter) + .init(); + }); +} +``` + +**改动后内容**: +```diff + //! agcore —— 智能体(Agent)核心工具箱。 + +- pub mod llm; ++ #[cfg(any(feature = "llm-types", feature = "llm"))] ++ pub mod llm; +- pub mod document; ++ #[cfg(feature = "document")] ++ pub mod document; +- pub mod prompt; ++ #[cfg(feature = "prompt")] ++ pub mod prompt; +- pub mod tools; ++ #[cfg(feature = "tools")] ++ pub mod tools; +- pub mod memory; ++ #[cfg(feature = "memory")] ++ pub mod memory; +- pub mod agent; ++ #[cfg(feature = "agent")] ++ pub mod agent; +- pub mod engine; ++ #[cfg(feature = "engine")] ++ pub mod engine; + +- pub use document::Document; ++ #[cfg(feature = "document")] ++ pub use document::Document; + +- use tracing_subscriber::{EnvFilter, fmt, prelude::*}; +- static INIT: std::sync::Once = std::sync::Once::new(); +- pub fn init_tracing() { +- INIT.call_once(|| { +- let filter = EnvFilter::try_from_default_env() +- .unwrap_or_else(|_| EnvFilter::new("agcore=info")); +- tracing_subscriber::registry() +- .with(fmt::layer()) +- .with(filter) +- .init(); +- }); +- } ++ #[cfg(feature = "tracing-init")] ++ use tracing_subscriber::{EnvFilter, fmt, prelude::*}; ++ ++ #[cfg(feature = "tracing-init")] ++ static INIT: std::sync::Once = std::sync::Once::new(); ++ ++ #[cfg(feature = "tracing-init")] ++ pub fn init_tracing() { ++ INIT.call_once(|| { ++ let filter = EnvFilter::try_from_default_env() ++ .unwrap_or_else(|_| EnvFilter::new("agcore=info")); ++ tracing_subscriber::registry() ++ .with(fmt::layer()) ++ .with(filter) ++ .init(); ++ }); ++ } +``` + +--- + +#### 文件 3:`src/llm.rs`(当前约 12 行 → 改动后约 24 行) + +**改动说明**:为每个子模块声明加 feature 门控。`types` 子模块在 `llm-types` 或 `llm` 任一 feature 启用时编译(`llm` imply `llm-types`,但 `prompt` 也 depend on `llm-types`);其余子模块(compact/convert/cycle 等)仅在 `llm` feature 启用时编译。 + +```diff + //! LLM 调用周期 —— 大模型基础调用周期控制。 + +- pub mod types; ++ #[cfg(feature = "llm-types")] ++ pub mod types; +- pub mod compact; ++ #[cfg(feature = "llm")] ++ pub mod compact; +- pub mod convert; ++ #[cfg(feature = "llm")] ++ pub mod convert; +- pub mod cycle; ++ #[cfg(feature = "llm")] ++ pub mod cycle; +- pub mod embedding; ++ #[cfg(feature = "llm")] ++ pub mod embedding; +- pub mod error; ++ #[cfg(feature = "llm")] ++ pub mod error; +- pub mod hooks; ++ #[cfg(feature = "llm")] ++ pub mod hooks; +- pub mod mock; ++ #[cfg(feature = "llm")] ++ pub mod mock; +- pub mod provider; ++ // provider 模块依赖 reqwest(通过 reqwest::Client),仅在任一 provider feature 启用时编译 ++ #[cfg(any(feature = "provider-openai", feature = "provider-anthropic", feature = "provider-deepseek", feature = "provider-qwen", feature = "provider-ollama"))] ++ pub mod provider; +- pub mod stream; ++ #[cfg(feature = "llm")] ++ pub mod stream; +``` + +--- + +#### 文件 3b:`src/llm/cycle.rs`(新增文件,约 35 行) + +**改动说明**:`cycle.rs` 中使用 `crate::tools::ToolRegistry`(第 29 行),依赖 `tools` feature。工具相关字段和方法需加 `#[cfg(feature = "tools")]` 门控。 + +> ⚠️ 这是 Phase 22.7 原计划的门控变更,因为编译阻塞提前到 Step 1 执行。 + +```diff + //! Cycle —— 多轮对话与工具调用编排。 + + use async_trait::async_trait; + use futures::StreamExt; // 来自 llm→tokio imply 链 ++ #[cfg(feature = "tools")] + use crate::tools::ToolRegistry; + + // ... struct / enum 定义 ... + + // ===== CycleConfig — 工具相关字段加 cfg 门控 ===== + pub struct CycleConfig { + pub max_retries: usize, + pub max_history: usize, ++ #[cfg(feature = "tools")] + pub max_tool_turns: usize, ++ #[cfg(feature = "tools")] + pub tool_timeout_secs: u64, + // ... 其他字段 ... + } + + // ===== Cycle — 方法加 cfg 门控 ===== + impl Cycle { + /// 仅在有 tools feature 时才有工具调用相关方法 ++ #[cfg(feature = "tools")] + pub async fn submit_with_tools(&self, ...) -> Result<...> { + // ... + } + ++ /// submit_with_tools_stream 方法同样需要 tools 门控, ++ /// 因参数包含 Arc 而与 submit_with_tools 同理。 ++ #[cfg(feature = "tools")] ++ pub async fn submit_with_tools_stream( ++ &self, ... // 方法签名中包含 Arc 参数 ++ ) -> Result<...> { ++ // ... ++ } + ++ #[cfg(feature = "tools")] + async fn run_tool_loop(&self, ...) -> Result<...> { + // ... + } + } + ++ // ===== 顶层函数 — 同样依赖 ToolRegistry ===== ++ /// run_tool_loop 函数(顶层函数,非 LlmCycle 方法)同样依赖 Arc, ++ /// 参数包含 Arc,需 #[cfg(feature = "tools")]。 ++ #[cfg(feature = "tools")] ++ pub async fn run_tool_loop( ++ // ... 函数签名中包含 Arc 参数 ++ ) -> Result<...> { ++ // ... ++ } + + **说明**:`Cycle` 本身的 struct 定义、`submit()` 基础方法、`ResponseStream` 等不依赖 tools 的部分保持无门控,仅在 `llm` feature 下编译即可。 + +--- + +#### 文件 4:`src/tools.rs`(当前约 13 行 → 改动后约 15 行) + +**改动说明**:`mcp` 子模块及对应的 `pub use` 仅在 `tools-mcp` feature 启用时编译。其余子模块(base/error/permission/registry)始终在 `tools` feature 下编译。 + +```diff + //! 工具系统 —— 工具抽象、注册、调用、权限控制与 MCP 集成。 + + pub mod base; + pub mod error; +- pub mod mcp; ++ #[cfg(feature = "tools-mcp")] ++ pub mod mcp; + pub mod permission; + pub mod registry; + + pub use base::{BaseTool, ToolContext, ToolRef}; + pub use error::ToolError; +- pub use mcp::{McpClient, McpTransport}; ++ #[cfg(feature = "tools-mcp")] ++ pub use mcp::{McpClient, McpTransport}; + pub use permission::{Permission, PermissionChecker, PermissionConfig}; + pub use registry::{ToolInvocation, ToolRegistry}; +``` + +--- + +#### 文件 5:`src/memory/store.rs`(当前约 62 行 → 改动后约 64 行) + +**改动说明**:`sqlite_store` 子模块及其 `pub use` 仅在 `memory-sqlite` feature 启用时编译。 + +```diff + //! MemoryStore 抽象接口与默认实现。 + + use async_trait::async_trait; + use crate::memory::error::MemoryError; + use crate::memory::types::{MemoryFilter, MemoryItem}; + + pub mod in_memory; +- pub mod sqlite_store; ++ #[cfg(feature = "memory-sqlite")] ++ pub mod sqlite_store; + + pub use in_memory::InMemoryStore; +- pub use sqlite_store::SqliteStore; ++ #[cfg(feature = "memory-sqlite")] ++ pub use sqlite_store::SqliteStore; +``` + +**说明**:`MemoryStore` trait、`EvictionConfig`、`EvictionPolicy` 等定义保持不变,不需要 cfg 门控。 + +--- + +#### 文件 6:`src/memory.rs`(当前约 32 行 → 改动后约 34 行) + +**改动说明**:`SqliteStore` 的重新导出仅在 `memory-sqlite` feature 启用时编译。其余子模块声明和 `pub use` 保持不变(`memory` feature 门控由 `src/lib.rs` 负责)。 + +```diff + //! 记忆系统 —— 对话消息管理、知识页面存储与关键词检索。 + + // 所有子模块声明保持不变: + // pub mod conversation; + // pub mod error; + // pub mod graph; + // pub mod knowledge; + // pub mod retriever; + // pub mod store; + // ... + + // 高频类型 + pub use conversation::{ConversationMemory, ConversationMemoryConfig}; + pub use error::MemoryError; + pub use graph::{GraphEntity, GraphRelation, InMemoryGraph, KnowledgeGraph, RelationDirection, ScoredEntity}; + pub use knowledge::KnowledgeStore; + pub use retriever::MemoryRetriever; + pub use store::{InMemoryStore, MemoryStore}; ++ #[cfg(feature = "memory-sqlite")] ++ pub use store::SqliteStore; + // 其余 pub use 保持不变... +``` + +--- + +#### 文件 7:`src/agent/session.rs`(新增文件,约 30 行) + +**改动说明**:`session.rs` 中引用了 `crate::engine::*`(SessionMemoryEntry、SessionSnapshot、EngineError),而 `agent` feature 不含 `engine`(`engine = ["agent"]` 是反向依赖)。需要对 engine 相关导入和方法加门控。 + +> ⚠️ 阻塞 B3:`src/agent/session.rs:28-29` 无条件引用 `crate::engine::*`,在 `agent` feature 下编译时因缺少 engine 而失败。 + +```diff + //! Session —— Agent 会话管理。 + + use async_trait::async_trait; + use crate::llm::types::LLMRequest; + use crate::memory::MemoryStore; ++ #[cfg(feature = "engine")] + use crate::engine::snapshot::{SessionMemoryEntry, SessionSnapshot}; ++ #[cfg(feature = "engine")] + use crate::engine::EngineError; + + // ===== AgentSession — pending_memory_restore 字段 ===== ++ /// AgentSession 结构体中的 pending_memory_restore 字段类型来自 engine 模块, ++ /// 需要条件编译。 + pub struct AgentSession { ++ // ... 其他字段 ... ++ ++ #[cfg(feature = "engine")] ++ pending_memory_restore: Option>, ++ // ... 其他字段 ... ++ } + + impl Session { + /// to_snapshot / from_snapshot / restore_memory 仅在 engine feature 下可用 ++ #[cfg(feature = "engine")] + pub fn to_snapshot(&self) -> SessionSnapshot { + // ... + } + ++ #[cfg(feature = "engine")] + pub fn from_snapshot(snap: SessionSnapshot) -> Result { + // ... + } + ++ #[cfg(feature = "engine")] + async fn restore_memory(&mut self, entries: Vec) -> Result<(), MemoryError> { + // ... + } + } +``` + +**说明**:`Session` 结构体本身以及不依赖 engine 的方法(如 `new()`、`add_message()`、`get_history()`)保持无门控,仅在 `agent` feature 下编译即可。 + +--- + +## 4. 实施步骤 + +按 **3 个 commit** 粒度执行,每个 commit 后编译验证。 + +### Commit 1:Cargo.toml features 定义 + 依赖 optional 化 + +**涉及文件**:仅 `Cargo.toml` + +**操作清单**: + +1. 在 `[package]` 之后、`[dependencies]` 之前插入 `[features]` 表(16 个 features + 4 个快捷组合,约 45 行) +2. tokio features 从 `["full"]` 改为 `["rt", "sync", "time", "macros", "process", "io-util"]` 并加 `optional = true` +3. reqwest / rusqlite / tracing-subscriber / tokio-stream / futures / futures-util / futures-core / bytes / async-stream / tokio-util / time 共 11 个依赖加 `optional = true` +4. 在 `[dependencies]` 之后新增 `[dev-dependencies]` 加 `tokio = { version = "1", features = ["rt", "macros"] }` + +**验证**: +```bash +cargo build --no-default-features # 不依赖任何 optional crate,应通过 +cargo build -F document # 零外部依赖,应通过 +``` + +--- + +### Commit 2:cfg 门控(pub mod + pub use + init_tracing) + +**涉及文件**:`src/lib.rs`、`src/llm.rs`、`src/llm/cycle.rs`、`src/tools.rs`、`src/memory/store.rs`、`src/memory.rs`、`src/agent/session.rs` + +**操作清单**: + +按文件逐一执行: + +1. `src/lib.rs` — 7 个 `pub mod` 加 `#[cfg(feature = "...")]`、`Document` pub use 加 cfg、`init_tracing` 整体用 `#[cfg(feature = "tracing-init")]` 包裹 +2. `src/llm.rs` — 10 个子模块按 `llm-types` / `llm` / provider 分类门控 +3. `src/llm/cycle.rs` — `ToolRegistry` 导入加 `#[cfg(feature = "tools")]`,工具字段和方法加相同门控 +4. `src/tools.rs` — `pub mod mcp` 和 `pub use mcp::*` 加 `#[cfg(feature = "tools-mcp")]` +5. `src/memory/store.rs` — `pub mod sqlite_store` 和 `pub use sqlite_store::SqliteStore` 加 `#[cfg(feature = "memory-sqlite")]` +6. `src/memory.rs` — `pub use store::SqliteStore` 加 `#[cfg(feature = "memory-sqlite")]` +7. `src/agent/session.rs` — engine 相关导入加 `#[cfg(feature = "engine")]`,to_snapshot/from_snapshot/restore_memory 加相同门控 + +**验证**: +```bash +cargo build -F "full" # 全量回归 +cargo build -F "prompt" # 验证 llm::types imply 路径 +cargo build -F "tools" # 验证 tokio imply 路径 +cargo build -F "memory" # 验证记忆模块不含 sqlite +cargo build -F "chat,provider-openai" # 纯对话组合 +cargo build -F "chat,provider-openai,tools-mcp" # 带 MCP 对话 +``` + +--- + +### Commit 3:Checkpoint 全量验证 + +**操作清单**: + +1. 完整的验证矩阵执行(见第 5 节) +2. `cargo test -F "full"` 确认 427 passed + +**验证**: +```bash +cargo test -F "full" +cargo build -F "light" +cargo build -F "multi" +``` + +## 5. 验证标准 + +### 编译验证矩阵 + +| 命令 | 验证目标 | 预期结果 | +|------|---------|---------| +| `cargo build --no-default-features` | 空 crate | 编译通过(无模块) | +| `cargo build -F "full"` | 全量回归 | 编译通过,与 v0.3.0 语义一致 | +| `cargo test -F "full"` | 测试回归 | `427 passed` | +| `cargo build -F "document"` | 文档模块独立 | 编译通过,零外部依赖 | +| `cargo build -F "prompt"` | 提示词独立 | 编译通过,`llm::types` imply 路径正确 | +| `cargo build -F "tools"` | 工具独立 | 编译通过,tokio imply 路径正确 | +| `cargo build -F "memory"` | 记忆模块独立编译 | ✅ 不含 sqlite(依赖 B1/B2 修复) | +| `cargo build -F "memory-sqlite"` | 含 SQLite 的记忆模块 | ✅ 含 rusqlite | +| `cargo build -F "agent"` | Agent 独立编译 | ✅ 含 llm+tools+memory,不含 engine(依赖 B1/B3 修复) | +| `cargo build -F "engine"` | Engine 独立编译 | ✅ imply agent → llm+tools+memory | +| `cargo build -F "chat,provider-openai"` | 纯对话组合 | 编译通过,不含 MCP、sqlite | +| `cargo build -F "chat,provider-openai,tools-mcp"` | 带 MCP 对话 | 编译通过,含 reqwest 无 sqlite | +| `cargo build -F "light"` | 生产常用组合 | 编译通过 | +| `cargo build -F "multi"` | 多 provider 组合 | 编译通过 | + +### 验证操作指令 + +每次编译验证后执行(验证编译产物不含意外符号): + +```bash +# 确认空 crate 确实没有模块符号 +cargo build --no-default-features 2>&1 && echo "OK" + +# 确认 document 零外部依赖(无 reqwest/rusqlite 等符号) +cargo build -F "document" 2>&1 && echo "OK" + +# 全量构建 + 测试 +cargo build -F "full" 2>&1 && cargo test -F "full" 2>&1 | tail -5 +``` + +### 验证通过条件 + +- 所有 14 条编译验证命令返回 exit code 0 +- `cargo test -F "full"` 输出 `427 passed`(与 v0.3.0 基线一致,不要求测试数精确匹配,但必须全部通过且数量合理) +- 无 `unused import` / `unused variable` / `dead code` warning(由 `#[cfg]` 引起的新 warning 需逐一修复) + +## 6. 风险评估 + +| 风险 | 等级 | 缓解措施 | +|------|------|---------| +| **tokio features 拆细遗漏**:某些代码路径用到 net/fs/signal | 低 | 已通过 SA(静态分析)验证全库无相关使用 | +| **`#[tokio::test]` 编译失败**:测试代码无 tokio runtime | 低 | `[dev-dependencies]` 添加 `tokio = { version = "1", features = ["rt", "macros"] }` | +| **下游 transitive tokio features 缩小**:依赖 agcore 的 crate 之前通过 agcore 间接获得 `full` tokio,现在范围缩小 | 中 | Phase 27 README 发布说明中明确告知迁移方案;下游如需完整 tokio 需自行添加 | +| **imply 链未闭合**:某个 feature 依赖了未 imply 的 feature | 低 | 7 种特征组合全部逐条构建验证;features 定义中有交叉引用的全部显式列出 | +| **unused cfg warning**:某些 `#[cfg]` 标记导致编译 warning | 低 | 每个 commit 后检查编译器输出,发现后立即修复 | +| **测试依赖循环**:dev-deps 与普通 deps 版本冲突 | 低 | dev-deps 的 tokio 版本与主依赖保持一致 (`version = "1"`,由 cargo 自动选择兼容版本) | +| **memory 跨模块 imply 链** | **高** | memory 模块依赖 llm(conversation/vector_store)、tokio(knowledge)、time(types),imply 链必须完整传递 | `memory` feature 定义已包含 `llm`、`tokio`、`time`;验证矩阵覆盖 memory 独立编译 | +| **跨模块引用未门控** | **高** | provider.rs 依赖 reqwest、cycle.rs 依赖 ToolRegistry、session.rs 依赖 engine,Step 1 必须添加 cfg 门控 | provider 模块 cfg 改为 provider-xxx 条件;cycle.rs 加 tools 门控;session.rs 加 engine 门控 | diff --git a/docs/27-step3-phase26-ci-verification.md b/docs/27-step3-phase26-ci-verification.md new file mode 100644 index 0000000..41cadb6 --- /dev/null +++ b/docs/27-step3-phase26-ci-verification.md @@ -0,0 +1,424 @@ +# AG Core v0.3.2 Step 3(Phase 26–27)— 验证固化 + 文档更新实施方案 + +## 1. 背景与目标 + +**背景**:agcore v0.3.2 Step 1(Phase 20–25)已交付 —— Cargo features 拆分基础设施改造全部完成,所有模块 `#[cfg]` 门控注入完毕,依赖全部 optional 化,`default = ["full"]` 保持向后兼容。当前项目处于已改造完成但未经 CI 固化、无文档指引的状态。 + +**当前状态快照**: +- v0.3.0 → v0.3.2 Step 1:68 个源文件,23,765 行 +- 16 个 features(10 模块级 + 5 provider + 1 工具)+ 4 个快捷组合 +- `cargo test --features "full"`:427 passed +- 7 种 feature 组合的 `cargo test --lib` 已全部通过(full / light / chat / chat+mcp / multi / multi+mcp / clippy),无需修复 cfg 遗漏 +- 但 `cargo test`(不带 `--lib`)会因 18 个 example 缺少 `required-features` 而失败 +- 项目无 CI/CD 配置 + +**目标**:通过 Step 3 将 features 体系验证固化到 CI 中,消除编译死代码警告,完成文档指引,使 v0.3.2 达到可发布状态。 + +**预期效果**: +- 每次提交自动验证 7 种 feature 组合的编译 + 测试(零 warning) +- 18 个 example 各自标注准确的 `required-features`,外树用户可一键运行 +- `cargo clippy --all-features -- -D warnings` 零告警 +- README 含完整 feature 表 + `Cargo.toml` 配置示例 + 升级指南,新用户 5 分钟内可选定组合 +- roadmap 同步更新 + +## 2. 需求分析 + +### 2.1 功能需求 + +| # | 需求 | 说明 | 对应工作 | +|---|------|------|---------| +| F1 | LlmProvider trait 不应依赖具体 provider feature | trait 自身不依赖 reqwest 或任何 provider 实现,纯 Mock 场景也应可用 | 工作 0 | +| F2 | 每个 example 通过 `cargo run --example xxx` 正确编译 | 18 个 example 各有精确的最小 features 声明 | 工作 1 | +| F3 | CI 自动验证 7 种特征组合的编译与测试 | push / PR 触发 | 工作 2 | +| F4 | 所有 feature 组合下 0 个编译器警告 | 消除 dead_code 等警告 | 工作 3 | +| F5 | README 提供完整的 feature 选择指引 | 表格 + 场景推荐 + Cargo.toml 示例 | 工作 5(Phase 27) | +| F6 | example 文件顶部标注所需 features | 用户可一键复制运行命令 | 工作 5(Phase 27) | +| F7 | roadmap 状态同步 | 总入口 + v0.3.2 子文档 | 工作 5(Phase 27) | + +### 2.2 非功能需求 + +| # | 需求 | 指标 | 对应工作 | +|---|------|------|---------| +| N1 | 向后兼容 | `default = ["full"]` 行为与 v0.3.0 一致,427 tests passed | 全部 | +| N2 | CI 时效 | 全矩阵 ≤ 10 分钟 | 工作 2 | +| N3 | 最少侵入 | 不改动功能逻辑,仅 cfg / 配置 / 文档变更 | 全部 | + +### 2.3 推演概要 + +**需求拆解**:从当前编译验证结果出发,发现三类待解决问题: +1. **架构归属问题**——`LlmProvider` trait 定义在 provider 模块门控下,语义上应归属 `llm` 基础设施。同时其返回类型 `ProviderCapabilities` / `ProviderFeatures` 也必须一并移出 +2. **example 可编译性问题**——18 个 example 无 `required-features`,多组合下 `cargo test` 失败 +3. **代码质量问题**——`session.rs` 中 `bundle()` 方法在 `chat` 组合下 dead_code +4. **工程缺失**——无 CI、README 无 features 说明、roadmap 未同步 + +**边界识别**: +- 工作 0 仅移动 trait 及关联类型定义,不改变公开 API 签名 +- 工作 1 的 required-features 是最小集合,不添加冗余 feature +- 工作 2 的 CI 仅验证编译 + 单元测试,不包含集成测试 +- 工作 5 的文档更新不涉及新的功能描述 + +**非目标声明**: +- 不新增 feature 组合(保持现有的 4 个快捷组合不变) +- 不重构 `ProviderConfig` / `ProviderType` / `create_provider()` 等 provider 模块创建逻辑(仅移出 trait 和元数据结构体) +- 不集成集成测试(CI 仅验证 `--lib` 单元测试 + example 编译验证) +- 不改动 `Cargo.toml` 的 `[dependencies]` 声明 +- 不改变 `default = ["full"]` 的默认行为 + +### 2.4 需求映射矩阵 + +| 功能需求 | 非功能需求 | 对应工作 | 验收项 | +|---------|-----------|---------|-------| +| F1 + N1 + N3 | — | 工作 0 | A1, A2, A9 | +| F2 | — | 工作 1 | A10 | +| F3 | N2 | 工作 2 | A5, A11 | +| F4 | — | 工作 3 | A4 | +| F5 | N1 | 工作 5 | A6 | +| F6 | — | 工作 5 | A7 | +| F7 | — | 工作 5 | A8 | +| — | N3 | 全部 | A1 | +| — | R2 缓解 | 全部 | A10 | + +## 3. 方案设计 + +### 3.1 总体架构调整 + +``` +工作 0 — 架构修正(LlmProvider trait 及关联类型归属调整) + +当前: + src/llm/provider.rs #[cfg(any(feature = "provider-openai", ...))] + ├─ pub trait LlmProvider { ... } + ├─ pub struct ProviderCapabilities { ... } + ├─ pub struct ProviderFeatures { ... } + └─ pub fn capabilities(&self) -> ProviderCapabilities; + +目标: + src/llm/provider_trait.rs #[cfg(feature = "llm")] + ├─ pub trait LlmProvider { ... } + ├─ pub struct ProviderCapabilities { ... } + ├─ pub struct ProviderFeatures { ... } + └─ pub fn capabilities(&self) -> ProviderCapabilities; + src/llm/provider.rs #[cfg(any(feature = "provider-openai", ...))] + └─ 各 provider 实现 + ProviderConfig / ProviderType / create_provider() + src/llm.rs + └─ pub use provider_trait::{LlmProvider, ProviderCapabilities, ProviderFeatures}; + +影响文件(6 个源文件 + 1 个新建): + - src/llm.rs — 添加 mod provider_trait 声明 + pub use 重导出 + - src/llm/provider_trait.rs — 新文件,trait + 关联类型定义移入 + - src/llm/provider.rs — 移出 trait + 关联类型 + - src/llm/provider/openai.rs — use super:: → use crate::llm:: + - src/llm/provider/anthropic.rs — 同上 + - src/llm/provider/ollama.rs — 同上 + - src/llm/provider/openai_compat.rs — 同上(两个 import 合并) +``` + +### 3.2 各子项设计方案 + +#### 工作 0 — LlmProvider trait 归属修正 + +**设计方案**: +1. 在 `src/llm/` 下新建 `provider_trait.rs`,门控为 `#[cfg(feature = "llm")]` +2. 从 `src/llm/provider.rs` 中提取以下定义到新文件: + - `pub trait LlmProvider`(含关联方法 `chat` / `chat_stream` / `capabilities`) + - `pub struct ProviderCapabilities`(含字段 `features: ProviderFeatures`) + - `pub struct ProviderFeatures`(含 8 个功能开关字段) +3. `src/llm.rs` 中声明 `mod provider_trait;`,并 `pub use provider_trait::{LlmProvider, ProviderCapabilities, ProviderFeatures};` +4. `src/llm/provider.rs` 移除上述定义,保留 `ProviderConfig` / `ProviderType` / `create_provider()` 等运行时代码 +5. 更新所有 import 路径(详见下方清单) + +**Import 路径调整清单**: + +现有写法 → 目标写法 + +| # | 文件 | 现有 import | 目标 import | +|---|------|------------|------------| +| 1 | `agent/builder.rs:16` | `use crate::llm::provider::LlmProvider;` | `use crate::llm::LlmProvider;` | +| 2 | `agent/builder.rs:135`(test) | `use crate::llm::provider::{LlmProvider, ProviderCapabilities, ProviderFeatures};` | `use crate::llm::{LlmProvider, ProviderCapabilities, ProviderFeatures};` | +| 3 | `agent/session.rs:35` | `use crate::llm::provider::LlmProvider;` | `use crate::llm::LlmProvider;` | +| 4 | `agent/runtime.rs:21` | `use crate::llm::provider::LlmProvider;` | `use crate::llm::LlmProvider;` | +| 5 | `llm/mock.rs:46` | `use crate::llm::provider::{LlmProvider, ProviderCapabilities, ProviderFeatures};` | `use crate::llm::{LlmProvider, ProviderCapabilities, ProviderFeatures};` | +| 6 | `llm/cycle.rs:22` | `use crate::llm::provider::LlmProvider;` | `use crate::llm::LlmProvider;` | +| 7 | `llm/cycle.rs:940,1401`(test) | `use crate::llm::provider::{ProviderCapabilities, ProviderFeatures};` | `use crate::llm::{ProviderCapabilities, ProviderFeatures};` | +| 8 | `llm/provider/openai.rs:24` | `use super::{LlmProvider, ProviderCapabilities, ProviderFeatures};` | `use crate::llm::{LlmProvider, ProviderCapabilities, ProviderFeatures};` | +| 9 | `llm/provider/anthropic.rs:21` | `use super::{LlmProvider, ProviderCapabilities, ProviderFeatures};` | `use crate::llm::{LlmProvider, ProviderCapabilities, ProviderFeatures};` | +| 10 | `llm/provider/ollama.rs:14` | `use super::{LlmProvider, ProviderCapabilities};` | `use crate::llm::{LlmProvider, ProviderCapabilities};` | +| 11 | `llm/provider/openai_compat.rs:18,21` | `use super::ProviderCapabilities;` + `use crate::llm::provider::LlmProvider;` | `use crate::llm::{LlmProvider, ProviderCapabilities};`(合并为一行) | +| 12 | `llm/provider/registry.rs:6` | `use crate::llm::provider::{LlmProvider, ProviderConfig, ProviderType, create_provider};` | `use crate::llm::LlmProvider;` + `use crate::llm::provider::{ProviderConfig, ProviderType, create_provider};`(拆分) | + +**示例文件 import 调整**: + +| # | 文件 | 现有 import | 目标 import | +|---|------|------------|------------| +| 13 | `examples/end_to_end.rs:21` | `use agcore::llm::provider::{create_provider, LlmProvider, ProviderConfig, ProviderType};` | `use agcore::llm::LlmProvider;` + `use agcore::llm::provider::{create_provider, ProviderConfig, ProviderType};` | +| 14 | `examples/context_slot_demo.rs:18` | `use agcore::llm::provider::LlmProvider;` | `use agcore::llm::LlmProvider;` | +| 15 | `examples/streaming_events_demo.rs:19` | `use agcore::llm::provider::LlmProvider;` | `use agcore::llm::LlmProvider;` | +| 16 | `examples/quick_start.rs:9` | `use agcore::llm::provider::LlmProvider;` | `use agcore::llm::LlmProvider;` | + +**Breaking Change 声明**: + +工作 0 是**非兼容性变更**,现有用户可能通过以下路径引用 `LlmProvider`: + +| 旧路径(v0.3.0–v0.3.2 Step 1) | 新路径(v0.3.2 Step 3 后) | +|--------------------------------|---------------------------| +| `agcore::llm::provider::LlmProvider` | `agcore::llm::LlmProvider` | +| `agcore::llm::provider::ProviderCapabilities` | `agcore::llm::ProviderCapabilities` | +| `agcore::llm::provider::ProviderFeatures` | `agcore::llm::ProviderFeatures` | + +**向后兼容方案(可选)**:在 `src/llm/provider.rs` 中添加 `#[cfg(feature = "llm")]` 门控的类型别名,让老路径仍然可用: +```rust +#[cfg(feature = "llm")] +pub use super::provider_trait::LlmProvider; +#[cfg(feature = "llm")] +pub use super::provider_trait::ProviderCapabilities; +#[cfg(feature = "llm")] +pub use super::provider_trait::ProviderFeatures; +``` +**推荐**:用户应迁移到新路径 `agcore::llm::LlmProvider`,`provider` 模块仅保留 `ProviderConfig` / `ProviderType` / `create_provider()` 等创建逻辑。 + +**验证**: +- `cargo test --features "full"` 仍 427 passed +- `cargo test --no-default-features --features "llm,llm-types" --lib` 编译通过(无需任何 provider feature) +- 所有 12 个内部文件 + 4 个示例文件的 import 路径正确 + +#### 工作 1 — examples required-features 标注 + +**设计方案**:在 `Cargo.toml` 中为每个 example 添加 `[[example]]` + `required-features`,精确到最小 features 集合。 + +``` +上下文 slot 示例(context_slot_demo): + 工作 0 后仅需 ["agent"](修正前需 ["agent", "provider-openai"]) + 因为 agent 的测试无需真实 provider,mock 即可 + +推理不变的 example(simple_visit): + 真正调用 LLM,需要 ["llm", "provider-openai", "tracing-init"] +``` + +完整映射关系见实施计划 §4.2。 + +#### 工作 2 — CI 配置 + +**设计方案**:GitHub Actions 矩阵策略,7 个并行测试 job + 1 clippy + 1 format + 1 example 验证。 + +| Job | Command | 作用域 | +|-----|---------|--------| +| full | `RUSTFLAGS="-D warnings" cargo test --features "full" --lib` | 全量回归,零警告 | +| light | `RUSTFLAGS="-D warnings" cargo test --no-default-features --features "light" --lib` | 生产常用,零警告 | +| chat | `RUSTFLAGS="-D warnings" cargo test --no-default-features --features "chat,provider-openai" --lib` | 纯对话,零警告 | +| chat+mcp | `RUSTFLAGS="-D warnings" cargo test --no-default-features --features "chat,provider-openai,tools-mcp" --lib` | 对话 + 工具,零警告 | +| multi | `RUSTFLAGS="-D warnings" cargo test --no-default-features --features "multi,provider-openai" --lib` | 多 Agent,零警告 | +| multi+mcp | `RUSTFLAGS="-D warnings" cargo test --no-default-features --features "multi,provider-openai,tools-mcp" --lib` | 多 Agent + 工具,零警告 | +| clippy | `cargo clippy --all-features --lib -- -D warnings` | lint 检查 | +| format | `cargo fmt --check`(stable toolchain) | 格式检查 | +| examples | `cargo test --features "full"`(不加 `--lib`,编译并运行所有 example) | example 编译验证 | + +**关键决策**: +- 矩阵中统一使用 `--lib` 而非 `--all-targets`。理由:examples 的编译由 `required-features` 独立管理,若混入矩阵会因 feature 组合不匹配导致 example 编译失败,干扰模块测试结果验证。 +- 使用 `RUSTFLAGS="-D warnings"` 将警告升级为编译错误,确保 `F4(0 编译器警告)`被矩阵中所有 6 个测试 job 强制执行。 +- format job 使用 stable toolchain(`cargo fmt --check` 不需要 nightly)。 +- 独立 `examples` job 使用 `cargo test --features "full"`(不加 `--lib`),验证所有 example 在完整 features 下编译并运行通过。 + +#### 工作 3 — bundle() 死代码警告修复 + +**设计方案**:在 `src/agent/session.rs:154` 的 `pub(crate) fn bundle()` 方法上添加 `#[cfg(feature = "engine")]` 条件编译。 + +背景:`bundle()` 仅被 `engine/session_manager.rs:219` 调用,当启用 `chat` 组合(agent 但非 engine)时产生 dead_code 警告。 + +**验证**:`cargo test --no-default-features --features "chat,provider-openai" --lib` 0 warnings。 + +#### 工作 4(可选)— 编译时间基线 + +记录但不沉淀到代码或 CI 中,仅供性能参考: +```bash +time cargo build --features "full" +time cargo build --no-default-features --features "light" +``` + +注:此工作在 v0.3.2 发布前为手动执行,不纳入 CI 或验收标准。若后续版本需要编译时间回归检测,可将其提升为正式工作项。 + +#### 工作 5 — 文档更新 + +三处并行更新: + +**README.md 新增 features 表格**: +- 4 个快捷组合 + 推荐使用场景 + `Cargo.toml` 配置示例 +- 下游用户可快速选择并复制配置 + +**升级指南章节(README.md 新增)**: +- 针对工作 0 的 Breaking Change 提供迁移说明 +- 列出旧路径 → 新路径的对照表 +- 提供向后兼容的重导出方案说明 +- 示例:`agcore::llm::provider::LlmProvider` → `agcore::llm::LlmProvider` +- 提醒用户更新 `use` 声明 + +**example 文件顶部注释**: +- 每个 example 第一行格式:`// Required features: cargo run --example xxx --features "..."` +- 对应工作 1 的 `required-features` 声明 + +**roadmap 状态同步**: +- `docs/roadmap.md`:补充 Phase 26-27 完成状态 + v0.3.2 链接 +- `docs/roadmap-v0.3.2.md`:Phase 26/27 状态从 ⏳ 改为 ✅ + 完成日期 + +### 3.3 ADR 记录 + +#### ADR-1:LlmProvider trait 及关联类型归属 llm 模块 + +| 字段 | 内容 | +|------|------| +| 问题 | `LlmProvider` trait 定义在 provider 模块门控 `any(provider-openai, provider-anthropic, ...)` 下,纯 Mock 场景被迫引入至少一个 provider feature。其返回类型 `ProviderCapabilities` / `ProviderFeatures` 同样被困在 provider 门控中 | +| 决策 | 将 trait 定义 + `ProviderCapabilities` / `ProviderFeatures` 一并提取到 `src/llm/provider_trait.rs`,归属 `#[cfg(feature = "llm")]` | +| 备选方案 | 保持不动,在 mock provider 上添加 cfg 绕过 — 否决,因为 provider 模块整体门控错误 | +| 理由 | trait 本身是个接口定义,不依赖 reqwest 或任何 provider 实现细节;`ProviderCapabilities` / `ProviderFeatures` 是 trait 方法的返回类型,必须与 trait 同门控 | +| 影响 | 修改 6 个源文件 + 1 个新建文件 + 4 个示例文件(详见 §3.2 import 调整清单) | +| 状态 | 已采纳 | + +#### ADR-2:CI 使用 nightly toolchain + +| 字段 | 内容 | +|------|------| +| 问题 | 项目已使用 edition 2024,是否降级到 2021 以使用 stable Rust | +| 决策 | 测试和 clippy 使用 nightly(edition 2024 目前要求 nightly);format 使用 stable | +| 备选方案 | 降级 edition 到 2021 — 否决,已迁移至 edition 2024 且编译通过 | +| 理由 | edtion 2024 是主动选择的方向,降级是倒退且涉及大量语法变更;`cargo fmt --check` 无需 nightly | +| 影响 | CI 依赖 `actions-rust-lang/setup-rust-toolchain@v1`;format job 指定 `toolchain: stable` | +| 状态 | 已采纳 | + +#### ADR-3:CI 矩阵使用 `--lib` 而非 `--all-targets` + +| 字段 | 内容 | +|------|------| +| 问题 | `cargo test --all-targets` 会编译所有 example,与矩阵中自选的 feature 组合可能冲突 | +| 决策 | 矩阵测试使用 `--lib`,examples 由独立 job(`cargo test --features "full"` 不加 `--lib`)验证 | +| 备选方案 | 在矩阵中也传入 `--all-targets` — 否决,example 编译失败会干扰模块测试验证 | +| 理由 | 分离关注点:矩阵验证模块级编译 + 零警告,独立 job 验证 example 编译 | +| 状态 | 已采纳 | + +## 4. 实施计划 + +### 4.1 任务拆解与优先级 + +| 优先级 | 工作 | 编号 | 规模 | 依赖 | +|--------|------|------|------|------| +| P0 | LlmProvider trait 归属修正 | 工作 0 | ~30 行(含关联类型移动 + import 调整) | 无 | +| P0 | bundle() 门控修复 | 工作 3 | 1 行 | 无 | +| P0 | examples required-features | 工作 1 | ~50 行 | 工作 0(context_slot_demo / quick_start 最小 features 从 agent+provider-openai 降为 agent) | +| P0 | CI 配置 | 工作 2 | ~80 行 | 无 | +| P1 | 文档更新 | 工作 5 | ~150 行 | 全部 | +| P2 | 编译时间基线 | 工作 4 | 手动 | 全部 | + +### 4.2 各 example 的 required-features 清单 + +| example 文件名 | required-features | 运行环境备注 | +|---------------|-------------------|------------| +| `prompt_composer` | `["prompt"]` | — | +| `custom_tool` | `["tools"]` | — | +| `conversation_memory_demo` | `["memory"]` | — | +| `knowledge_graph_demo` | `["memory"]` | — | +| `knowledge_search_demo` | `["memory"]` | — | +| `agent_session_demo` | `["agent"]` | — | +| `task_agent_demo` | `["agent"]` | — | +| `context_slot_demo` | `["agent"]` | 工作 0 后无需 provider | +| `quick_start` | `["agent"]` | 工作 0 后无需 provider | +| `simple_visit` | `["llm", "provider-openai", "tracing-init"]` | 需要 API key | +| `streaming_events_demo` | `["llm", "provider-openai"]` | 需要 API key | +| `agent_switch_demo` | `["engine"]` | — | +| `bridge_keys_demo` | `["engine"]` | — | +| `dispatch_stream_demo` | `["engine"]` | — | +| `engine_demo` | `["engine"]` | — | +| `sub_agent_dispatch_demo` | `["engine"]` | — | +| `document_demo` | `["memory", "tracing-init"]` | 需 sqlite 依赖(memory-sqlite feature 可选) | +| `end_to_end` | `["agent", "memory-sqlite", "provider-openai"]` | 需要 API key + sqlite 依赖 | + +**验证策略**:每个 example 除 `--lib` 验证外,还需单独运行以下命令确认 required-features 精确性: +```bash +cargo test --no-default-features --features "" --example +``` + +### 4.3 Commit 策略 + +每个工作独立 commit,按依赖顺序排列: + +| 顺序 | Scope | Type | 描述 | 依赖 | +|------|-------|------|------|------| +| 1 | `core` | `refactor` | 将 LlmProvider trait 及关联类型移出 provider 模块归属 llm | 无 | +| 2 | `agent` | `fix` | 为 session.rs bundle() 方法添加 engine feature 门控 | 无 | +| 3 | `examples` | `chore` | 为 18 个 example 添加 required-features 声明 | 工作 1(context_slot 等受益于工作 0 的轻量 features) | +| 4 | `ci` | `chore` | 创建 CI 测试矩阵配置 | 无 | +| 5 | `docs` | `docs` | 更新 README feature 表 + 升级指南 + 示例注释 + roadmap 状态 | 全部 | + +### 4.4 参考实现:CI 配置 + +```yaml +name: CI +on: [push, pull_request] +env: + RUSTFLAGS: "-D warnings" +jobs: + test-matrix: + strategy: + matrix: + features: + - "full" + - "light" + - "chat,provider-openai" + - "chat,provider-openai,tools-mcp" + - "multi,provider-openai" + - "multi,provider-openai,tools-mcp" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: nightly + - run: cargo test --no-default-features --features "${{ matrix.features }}" --lib + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: nightly + - run: cargo clippy --all-features --lib -- -D warnings + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: stable + - run: cargo fmt --check + examples: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: nightly + - run: cargo test --features "full" +``` + +## 5. 风险评估 + +| 风险 | 概率 | 影响 | 缓解措施 | 对应验收项 | +|------|------|------|---------|-----------| +| 工作 0 重构后公开 API 被意外改变 | 低 | 高 | 重构前后分别跑 `cargo test --features "full"` 确认测试数一致(427),且 `cargo doc` 无差异 | A1 | +| required-features 标注不准确导致 example 运行时缺少 trait 实现 | 低 | 中 | 每个 example 在 `--lib` 验证后,再单独跑 `cargo test --example xxx --no-default-features --features "对应features"` 确认 | A10 | +| CI 首次在 GitHub runner 上因环境差异(OS、Toolchain 版本)失败 | 中 | 低 | 非阻塞问题,修复后重新推送即可;本地已在 macOS 验证 7 种组合 | A5 | +| edition 2024 在 GitHub runner 的特定 nightly 版本上不稳定 | 低 | 中 | 可在 `Cargo.toml` 中加 `rust-version = "1.85"` 下限约束 | A5 | +| bundle() 门控修复后 engine 组合下方法不可见 | 极低 | 中 | `cargo test --features "engine,provider-openai"` 编译通过即可验证 | A4 | + +## 6. 验收标准 + +| # | 验收项 | 验证方式 | 对应工作 | +|---|--------|---------|---------| +| A1 | `cargo test --features "full"` 仍 427 passed | `cargo test -F full -q` | 工作 0 | +| A2 | `cargo test --no-default-features --features "llm,llm-types" --lib` 编译通过 | 无需任何 provider feature 即完成编译 | 工作 0 | +| A3 | 7 种组合下 `cargo test --no-default-features --features "组合" --lib -q` 全部通过 | 逐一验证 | 工作 1(example features 正确不会干扰 --lib) | +| A4 | 6 个矩阵组合(full / light / chat / chat+mcp / multi / multi+mcp)下 `RUSTFLAGS="-D warnings" cargo test --lib` 0 warnings | 所有组合均无编译器警告 | 工作 3 | +| A5 | `.github/workflows/ci.yml` 文件存在,结构包含 9 个 job(6 测试 + 1 clippy + 1 format + 1 examples) | 文件检查 | 工作 2 | +| A6 | README.md 包含 features 表格 + 使用场景 + Cargo.toml 配置示例 + 升级指南 | review 通过 | 工作 5 | +| A7 | 所有 18 个 example 文件首行含 `// Required features: cargo run --example xxx --features "..."` 注释 | review 通过 | 工作 5 | +| A8 | `docs/roadmap.md` 和 `docs/roadmap-v0.3.2.md` 中 Phase 26/27 状态标记为 ✅ | review 通过 | 工作 5 | +| A9 | 工作 0 后 `cargo doc --no-deps --features "llm,llm-types"` 可生成 `LlmProvider` / `ProviderCapabilities` / `ProviderFeatures` 的 API 文档(无需任何 provider feature) | review 通过 | 工作 0 | +| A10 | 每个 example 单独验证:`cargo test --no-default-features --features "<对应features>" --example ` 编译通过 | 逐一验证 18 个 example | 工作 1 | +| A11 | 全矩阵 CI(6 测试 + clippy + format + examples)从 checkout 到完成 ≤ 10 分钟 | 实测计时 | 工作 2 | diff --git a/docs/roadmap-v0.3.2.md b/docs/roadmap-v0.3.2.md index 158d6b3..bcefd30 100644 --- a/docs/roadmap-v0.3.2.md +++ b/docs/roadmap-v0.3.2.md @@ -1,10 +1,12 @@ # AG Core Roadmap — v0.3.2 -**状态**:⏳ 全部待实施 +**状态**:✅ Phase 20-25 已交付(Step 1 合并实施);⏳ Phase 26-27 待实施 > 本文件聚焦 **v0.3.2 版本** 的规划与交付(Phase 20–27)。 > 返回总入口:[`roadmap.md`](./roadmap.md) +> **进度更新(2026-07-19)**:Step 1 合并完成 Phase 20(Cargo.toml features 定义 + 依赖 optional 化)+ Phase 21-25 的全部 cfg 门控(因编译阻塞提前)。验证矩阵 14 条编译命令全部通过,`cargo test -F full` 427 passed。实施方案见 [`docs/26-step1-phase20-cargo-features-implementation.md`](./26-step1-phase20-cargo-features-implementation.md)。下一步:Phase 26(CI 测试矩阵)+ Phase 27(文档更新)。 + ## v0.3.2 愿景 通过 Cargo features 拆分,让下游按需选择模块,跳过不需要的编译单元和重型依赖。 @@ -77,8 +79,8 @@ | Step | Phase | 内容 | 验证方式 | 预估行数 | |------|-------|------|---------|---------| -| **Step 1** | Phase 20 | Cargo.toml features 定义 + 依赖 optional 化 | `cargo build --no-default-features` + `cargo build --features "full"` | ~40 | -| **Step 2** | Phase 21–25 | 全模块 cfg 门控注入(按依赖顺序批量操作) | 逐个 feature 组合增量编译验证 | ~180 | +| **Step 1** ✅ | Phase 20-25 | Cargo.toml features 定义 + 依赖 optional 化 + 全模块 cfg 门控(合并实施) | 14 条编译验证全通过 + `cargo test -F full` 427 passed | ~100 | +| **Step 2** | (已合并至 Step 1) | — | — | — | | **Step 3** | Phase 26 | 测试矩阵验证 + 修复 cfg 遗漏 | 7 种组合全部测试通过 | ~10 | | **Step 4** | Phase 27 | README + 示例标注 + 总入口同步 | review 通过 | ~100 | @@ -103,7 +105,7 @@ **依赖**:无(Cargo.toml 独立改造) **优先级**:P0 **预估规模**:约 40 行 -**状态**:⏳ 待实施 +**状态**:✅ 已交付(2026-07-19)— features 定义 + 依赖 optional 化 + tokio features 拆细(含 `rt-multi-thread` 修正) --- @@ -121,7 +123,7 @@ **依赖**:Phase 20(需 feature 定义就绪) **优先级**:P0 **预估规模**:约 30 行 -**状态**:⏳ 待实施 +**状态**:✅ 已交付(2026-07-19,Step 1 合并)— `src/lib.rs` 全部 `pub mod` + `pub use Document` 门控完成 --- @@ -142,7 +144,7 @@ **依赖**:Phase 20 + Phase 21 **优先级**:P0 **预估规模**:约 80 行(中复杂度,cycle.rs 门控需精确隔离) -**状态**:⏳ 待实施 +**状态**:✅ 已交付(2026-07-19,Step 1 合并)— `src/llm.rs` 子模块按 llm-types/llm/provider 三类门控;`cycle.rs` 中 `ToolRegistry` import + `submit_with_tools` / `submit_with_tools_stream` / `run_tool_loop` 加 `#[cfg(feature = "tools")]`(Phase 22.7 提前) --- @@ -159,7 +161,7 @@ **依赖**:Phase 20 + Phase 21 **优先级**:P0 **预估规模**:约 20 行 -**状态**:⏳ 待实施 +**状态**:✅ 已交付(2026-07-19,Step 1 合并)— `src/tools.rs` 中 `pub mod mcp` + `pub use mcp::*` 加 `#[cfg(feature = "tools-mcp")]` --- @@ -177,7 +179,7 @@ **依赖**:Phase 20 + Phase 21 **优先级**:P0 **预估规模**:约 30 行 -**状态**:⏳ 待实施 +**状态**:✅ 部分交付(2026-07-19,Step 1 合并)— Phase 24.3(`sqlite_store` 模块门控)+ Phase 24.4(`pub use SqliteStore` 门控)已完成;Phase 24.1(memory 模块 pub use)由 `src/lib.rs` 的 `#[cfg(feature = "memory")]` 覆盖;Phase 24.2(vector_store 中 Embedding 引用隔离)待 Phase 26 验证时按需补充 --- @@ -194,7 +196,7 @@ **依赖**:Phase 20-24(全链路依赖就绪后操作) **优先级**:P0 **预估规模**:约 20 行 -**状态**:⏳ 待实施 +**状态**:✅ 部分交付(2026-07-19,Step 1 合并)— Phase 25.3(`src/lib.rs` 中 agent/engine 模块声明 cfg)已完成;Phase 25.1/25.2(agent/engine 内部子模块条件导出)由 `src/lib.rs` 顶层门控覆盖;`src/agent/session.rs` 中 engine 相关 import + `to_snapshot` / `from_snapshot` / `restore_memory` / `has_pending_memory_restore` + `pending_memory_restore` 字段加 `#[cfg(feature = "engine")]`(阻塞 B3 修复) --- @@ -290,21 +292,19 @@ graph TD classDef pending fill:#fbbf24,stroke:#d97706,color:#1a1a1a classDef provider fill:#93c5fd,stroke:#2563eb,color:#1a1a1a class P_OPENAI,P_ANTHROPIC,P_DEEPSEEK,P_QWEN,P_OLLAMA provider - class FULL pending - class ENGINE,AGENT,LLM,TOOLS,TOOLS_MCP,MEMORY,MEMORY_SQLITE,PROMPT,LLM_TYPES,DOCUMENT pending - class LIGHT,CHAT,MULTI pending + class FULL,ENGINE,AGENT,LLM,TOOLS,TOOLS_MCP,MEMORY,MEMORY_SQLITE,PROMPT,LLM_TYPES,DOCUMENT,LIGHT,CHAT,MULTI done ``` ## 关键里程碑 | 里程碑 | Phase 完成条件 | 可验证指标 | 状态 | |--------|---------------|-----------|------| -| **M16** | Phase 20 | `cargo build --no-default-features` 成功;`cargo build --features "full"` 与原行为一致 | ⏳ | -| **M17** | Phase 21 | 三种零依赖模块各自独立编译通过 | ⏳ | -| **M18** | Phase 22 | 5 个 provider 各自单独编译;cycle.rs 无 tools 时编译通过 | ⏳ | -| **M19** | Phase 23 | tools 不含 mcp 编译通过;加 tools-mcp 引入 McpClient | ⏳ | -| **M20** | Phase 24 | memory 不含 llm/sqlite 编译通过;加 memory-sqlite 引入 SqliteStore | ⏳ | -| **M21** | Phase 25 | agent + engine 全链路门控编译通过 | ⏳ | +| **M16** | Phase 20 | `cargo build --no-default-features` 成功;`cargo build --features "full"` 与原行为一致 | ✅ 2026-07-19 | +| **M17** | Phase 21 | 三种零依赖模块各自独立编译通过 | ✅ 2026-07-19(Step 1 合并) | +| **M18** | Phase 22 | 5 个 provider 各自单独编译;cycle.rs 无 tools 时编译通过 | ✅ 2026-07-19(Step 1 合并) | +| **M19** | Phase 23 | tools 不含 mcp 编译通过;加 tools-mcp 引入 McpClient | ✅ 2026-07-19(Step 1 合并) | +| **M20** | Phase 24 | memory 不含 llm/sqlite 编译通过;加 memory-sqlite 引入 SqliteStore | ✅ 2026-07-19(Step 1 合并) | +| **M21** | Phase 25 | agent + engine 全链路门控编译通过 | ✅ 2026-07-19(Step 1 合并) | | **M22** | Phase 26 | 7 种 CI 组合全部编译 + 测试通过;clippy --all-features 0 警告 | ⏳ | | **M23** | Phase 27 | 文档 review 通过 | ⏳ | diff --git a/docs/roadmap.md b/docs/roadmap.md index e9ba643..6b54cbd 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,7 +1,7 @@ # AG Core Roadmap > 拆分式 roadmap:按版本归档 + 未归类内容 -> 最后更新:2026-07-17(v0.3.0 Phase 19 完成 + M15 里程碑达成 + v0.3.0 全部交付完毕) +> 最后更新:2026-07-19(v0.3.2 Step 1 完成 — Phase 20-25 cfg 门控全部交付,427 测试通过) ## 文件索引 @@ -10,6 +10,7 @@ | [`roadmap-v0.1.0.md`](./roadmap-v0.1.0.md) | v0.1.0 计划与交付 — Phase 0–4c + v0.1.0 Release | ✅ 已发布 2026-07-04 | | [`roadmap-v0.2.0.md`](./roadmap-v0.2.0.md) | v0.2.0 计划与交付 — Phase 5–12 + v0.2.0-rc.1 | 🟡 Phase 5-11 已完成;Phase 12 P2 锦上添花可选 | | [`roadmap-v0.3.0.md`](./roadmap-v0.3.0.md) | v0.3.0 计划与交付 - Phase 13–19 | ✅ Phase 13-19 全部完成,v0.3.0 交付完毕 | +| [`roadmap-v0.3.2.md`](./roadmap-v0.3.2.md) | v0.3.2 计划与交付 — Phase 20–27(Cargo features 拆分) | 🟡 Step 1 完成(Phase 20-25);Phase 26-27 待实施 | | [`roadmap-unsorted.md`](./roadmap-unsorted.md) | 未归到任何版本的内容 — 全局愿景、当前状态、模块完整性、v0.4+ 展望、风险与建议、下一步行动、阶段总回顾 | — | ## 阅读建议 diff --git a/examples/context_slot_demo.rs b/examples/context_slot_demo.rs index 6088060..dd0dbe2 100644 --- a/examples/context_slot_demo.rs +++ b/examples/context_slot_demo.rs @@ -15,7 +15,7 @@ use std::sync::Arc; use agcore::agent::{Agent, AgentBuilder, AgentSession}; use agcore::llm::hooks::HookExecutor; use agcore::llm::mock::MockProvider; -use agcore::llm::provider::LlmProvider; +use agcore::llm::LlmProvider; use agcore::llm::types::message::{ContentBlock, Message}; use agcore::llm::types::response_v2::{MessageResponse, StopReason}; use agcore::llm::types::Usage; diff --git a/examples/end_to_end.rs b/examples/end_to_end.rs index 237d044..2fde4b2 100644 --- a/examples/end_to_end.rs +++ b/examples/end_to_end.rs @@ -18,7 +18,8 @@ use std::sync::Arc; use agcore::agent::{Agent, AgentBuilder, AgentSession}; use agcore::llm::hooks::HookExecutor; use agcore::llm::mock::MockProvider; -use agcore::llm::provider::{create_provider, LlmProvider, ProviderConfig, ProviderType}; +use agcore::llm::LlmProvider; +use agcore::llm::provider::{create_provider, ProviderConfig, ProviderType}; use agcore::llm::types::{Usage, message::{ContentBlock, Message}, response_v2::{MessageResponse, StopReason}}; use agcore::memory::store::{MemoryStore, SqliteStore}; use agcore::memory::types::{MemoryFilter, MemoryItem}; diff --git a/examples/quick_start.rs b/examples/quick_start.rs index 32c397e..f05ef5a 100644 --- a/examples/quick_start.rs +++ b/examples/quick_start.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use agcore::agent::{Agent, AgentBuilder, AgentSession}; use agcore::llm::hooks::HookExecutor; use agcore::llm::mock::MockProvider; -use agcore::llm::provider::LlmProvider; +use agcore::llm::LlmProvider; use agcore::llm::types::{Usage, message::{ContentBlock, Message}, response_v2::{MessageResponse, StopReason}}; use agcore::tools::{BaseTool, ToolContext, ToolError, ToolRegistry}; use async_trait::async_trait; diff --git a/examples/streaming_events_demo.rs b/examples/streaming_events_demo.rs index 7b22ea6..9d98e1c 100644 --- a/examples/streaming_events_demo.rs +++ b/examples/streaming_events_demo.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use agcore::llm::cycle::{CycleConfig, LlmCycle}; use agcore::llm::mock::MockProvider; -use agcore::llm::provider::LlmProvider; +use agcore::llm::LlmProvider; use agcore::llm::types::Usage; use agcore::llm::types::message::{ContentBlock, Message}; use agcore::llm::types::response_v2::{MessageResponse, StopReason, StreamEvent}; diff --git a/src/agent/builder.rs b/src/agent/builder.rs index 18c6d43..7ff884e 100644 --- a/src/agent/builder.rs +++ b/src/agent/builder.rs @@ -13,7 +13,7 @@ use crate::agent::error::AgentError; use crate::agent::runtime::{AgentConfig, RuntimeBundle}; use crate::agent::summary::SummaryConfig; use crate::llm::hooks::HookExecutor; -use crate::llm::provider::LlmProvider; +use crate::llm::LlmProvider; use crate::memory::retriever::MemoryRetriever; use crate::memory::store::MemoryStore; use crate::tools::ToolRegistry; @@ -132,9 +132,9 @@ impl AgentBuilder { mod tests { use super::*; use crate::llm::error::LlmError; - use crate::llm::provider::{LlmProvider, ProviderCapabilities, ProviderFeatures}; use crate::llm::types::request_v2::MessageRequest; use crate::llm::types::response_v2::{MessageResponse, StreamEvent}; + use crate::llm::{LlmProvider, ProviderCapabilities, ProviderFeatures}; use async_trait::async_trait; use futures_core::Stream; use std::pin::Pin; diff --git a/src/agent/runtime.rs b/src/agent/runtime.rs index 92d8482..56cc7e9 100644 --- a/src/agent/runtime.rs +++ b/src/agent/runtime.rs @@ -18,7 +18,7 @@ use std::time::Duration; use crate::agent::summary::SummaryConfig; use crate::llm::compact::CompactConfig; use crate::llm::hooks::HookExecutor; -use crate::llm::provider::LlmProvider; +use crate::llm::LlmProvider; use crate::memory::retriever::MemoryRetriever; use crate::memory::store::MemoryStore; use crate::tools::ToolRegistry; diff --git a/src/agent/session.rs b/src/agent/session.rs index 9aff695..f24c6f3 100644 --- a/src/agent/session.rs +++ b/src/agent/session.rs @@ -25,12 +25,14 @@ use crate::agent::error::AgentError; use crate::agent::runtime::RuntimeBundle; use crate::agent::session_memory::SessionMemory; use crate::agent::summary::{format_messages_as_text, SummaryConfig}; +#[cfg(feature = "engine")] use crate::engine::snapshot::{SessionMemoryEntry, SessionSnapshot}; +#[cfg(feature = "engine")] use crate::engine::EngineError; use crate::llm::cycle::{CostTracker, CycleConfig, LlmCycle}; use crate::llm::error::LlmError; use crate::llm::hooks::{HookContext, HookEvent}; -use crate::llm::provider::LlmProvider; +use crate::llm::LlmProvider; use crate::llm::stream::StreamEvent; use crate::llm::types::message::Message; use crate::llm::types::response_v2::MessageResponse; @@ -67,6 +69,7 @@ pub struct AgentSession { /// `None` 表示无 pending restore(正常状态)。 /// 调用 `restore_memory()` 后会被消费并设为 `None`。 /// 这是 transient state,不参与序列化(AgentSession 本身不 derive Serialize)。 + #[cfg(feature = "engine")] pending_memory_restore: Option>, } @@ -127,6 +130,7 @@ impl AgentSession { slots, current_slot_id: "default".to_string(), last_summary_turn: None, + #[cfg(feature = "engine")] pending_memory_restore: None, } } @@ -147,6 +151,7 @@ impl AgentSession { } /// RuntimeBundle 引用(Phase 17 新增,供 SessionManager::create_child 继承父 bundle)。 + #[cfg(feature = "engine")] pub(crate) fn bundle(&self) -> &Arc { &self.bundle } @@ -500,6 +505,7 @@ impl AgentSession { /// 通过 `SessionMemory::list_entries()` 获取完整条目(保留 `metadata` 和 `created_at`)。 /// /// `Arc` 和 `Arc` **不进入快照**——由 `from_snapshot()` 调用方注入。 + #[cfg(feature = "engine")] pub async fn to_snapshot(&self) -> SessionSnapshot { // 拍平 session_memory → HashMap // 失败时回退到空 map(错误已记录,不阻断 checkpoint 主流程)。 @@ -541,6 +547,7 @@ impl AgentSession { /// 由调用方显式 `await session.restore_memory()` 写回持久层。 /// /// 调用方负责提供与 `snapshot.agent_name` 对应的 `Arc`(引擎层只保留名字做调试用)。 + #[cfg(feature = "engine")] pub fn from_snapshot( snapshot: SessionSnapshot, agent: Arc, @@ -601,6 +608,7 @@ impl AgentSession { /// /// **完整恢复**:使用 `SessionMemory::set_with_meta()` 保留原始 `metadata` 和 `created_at` /// ——不像 `set()` 会清空 metadata 并把 created_at 设为当前时间。 + #[cfg(feature = "engine")] pub async fn restore_memory(&mut self) -> Result<(), EngineError> { // 取出 pending 并立即清空(避免重复 restore 时二次写入;幂等性保证) let entries = self.pending_memory_restore.take(); @@ -624,6 +632,7 @@ impl AgentSession { } /// 是否有待写回的 `session_memory_data`(`from_snapshot()` 后尚未 `restore_memory()`)。 + #[cfg(feature = "engine")] pub fn has_pending_memory_restore(&self) -> bool { self.pending_memory_restore .as_ref() diff --git a/src/lib.rs b/src/lib.rs index c606857..264985f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,31 @@ //! agcore —— 智能体(Agent)核心工具箱。 -pub mod agent; -pub mod document; -pub mod engine; +#[cfg(any(feature = "llm-types", feature = "llm"))] pub mod llm; -pub mod memory; +#[cfg(feature = "document")] +pub mod document; +#[cfg(feature = "prompt")] pub mod prompt; +#[cfg(feature = "tools")] pub mod tools; +#[cfg(feature = "memory")] +pub mod memory; +#[cfg(feature = "agent")] +pub mod agent; +#[cfg(feature = "engine")] +pub mod engine; +#[cfg(feature = "document")] pub use document::Document; +#[cfg(feature = "tracing-init")] use tracing_subscriber::{EnvFilter, fmt, prelude::*}; +#[cfg(feature = "tracing-init")] static INIT: std::sync::Once = std::sync::Once::new(); +/// 初始化 tracing 日志订阅(仅在启用 `tracing-init` feature 时可用)。 +#[cfg(feature = "tracing-init")] pub fn init_tracing() { INIT.call_once(|| { let filter = diff --git a/src/llm.rs b/src/llm.rs index 66ecebd..72e0c3f 100644 --- a/src/llm.rs +++ b/src/llm.rs @@ -1,12 +1,36 @@ //! LLM 调用周期 —— 大模型基础调用周期控制。 -pub mod compact; -pub mod convert; -pub mod cycle; -pub mod embedding; -pub mod error; -pub mod hooks; -pub mod mock; -pub mod provider; -pub mod stream; +#[cfg(feature = "llm-types")] pub mod types; +#[cfg(feature = "llm")] +pub mod compact; +#[cfg(feature = "llm")] +pub mod convert; +#[cfg(feature = "llm")] +pub mod cycle; +#[cfg(feature = "llm")] +pub mod embedding; +#[cfg(feature = "llm")] +pub mod error; +#[cfg(feature = "llm")] +pub mod hooks; +#[cfg(feature = "llm")] +pub mod mock; +// provider 模块依赖 reqwest(通过 reqwest::Client),仅在任一 provider feature 启用时编译 +#[cfg(any( + feature = "provider-openai", + feature = "provider-anthropic", + feature = "provider-deepseek", + feature = "provider-qwen", + feature = "provider-ollama" +))] +pub mod provider; +/// Provider 抽象接口(trait + 能力元数据),仅依赖 `llm` feature,不引入 reqwest。 +#[cfg(feature = "llm")] +pub mod provider_trait; +#[cfg(feature = "llm")] +pub mod stream; + +// 重导出 Provider 抽象接口到 `crate::llm::` 顶层,便于下游 `use crate::llm::LlmProvider`。 +#[cfg(feature = "llm")] +pub use provider_trait::{LlmProvider, ProviderCapabilities, ProviderFeatures}; diff --git a/src/llm/cycle.rs b/src/llm/cycle.rs index e4e126b..21b6448 100644 --- a/src/llm/cycle.rs +++ b/src/llm/cycle.rs @@ -19,13 +19,14 @@ use crate::llm::compact::{CompactConfig, CompactState, microcompact, should_comp use crate::llm::cycle::retry::should_retry; use crate::llm::error::LlmError; use crate::llm::hooks::{HookContext, HookEvent, HookExecutor}; -use crate::llm::provider::LlmProvider; +use crate::llm::LlmProvider; use crate::llm::stream::StreamEvent; use crate::llm::types::message::{ContentBlock, Message}; use crate::llm::types::request_v2::MessageRequest; use crate::llm::types::response_v2::{MessageResponse, PartialMessageResponse, StopReason}; use crate::llm::types::tool::ToolDef; use crate::llm::types::ToolChoice; +#[cfg(feature = "tools")] use crate::tools::ToolRegistry; /// LLM 调用周期配置。 @@ -528,6 +529,7 @@ impl LlmCycle { /// /// 注意:OpenAI API 要求 tool 消息必须紧跟在对应的 Assistant(tool_calls)消息之后。 /// 因此 push 工具结果前必须先 push Assistant 响应,否则 API 拒绝请求。 + #[cfg(feature = "tools")] pub async fn submit_with_tools( &mut self, prompt: String, @@ -631,6 +633,7 @@ impl LlmCycle { /// 直接调用模块函数 `run_tool_loop`。 /// /// ponytail: 返回的流是 `Item = StreamEvent`(非 `Result`),所有错误事件化为 `StreamEvent::Error`。 + #[cfg(feature = "tools")] pub async fn submit_with_tools_stream( &mut self, prompt: String, @@ -737,6 +740,7 @@ fn truncate_tool_result(s: &str, max_bytes: usize) -> String { /// /// **所有错误事件化**:通过 `tx.send(Error{..})` 表达错误,最终 `return` 结束 task。 /// 不返回 `Result`,因为错误已通过事件流传递。 +#[cfg(feature = "tools")] async fn run_tool_loop( mut messages: Vec, provider: Arc, @@ -933,7 +937,7 @@ async fn run_tool_loop( #[cfg(test)] mod tests { use super::*; - use crate::llm::provider::{ProviderCapabilities, ProviderFeatures}; + use crate::llm::{ProviderCapabilities, ProviderFeatures}; use crate::tools::{BaseTool, ToolRegistry}; use async_trait::async_trait; use futures_core::Stream; @@ -1394,7 +1398,7 @@ mod tests { /// 使用自定义 MockProvider 返回 chat_stream Err。 #[tokio::test(flavor = "multi_thread")] async fn test_submit_with_tools_stream_chat_stream_err() { - use crate::llm::provider::{ProviderCapabilities, ProviderFeatures}; + use crate::llm::{ProviderCapabilities, ProviderFeatures}; struct ErrProvider; #[async_trait] diff --git a/src/llm/mock.rs b/src/llm/mock.rs index 5f9197c..9559fdb 100644 --- a/src/llm/mock.rs +++ b/src/llm/mock.rs @@ -8,7 +8,7 @@ //! ```no_run //! use std::sync::Arc; //! use agcore::llm::mock::MockProvider; -//! use agcore::llm::provider::LlmProvider; +//! use agcore::llm::LlmProvider; //! use agcore::llm::types::message::{ContentBlock, Message}; //! use agcore::llm::types::response_v2::{MessageResponse, StopReason}; //! use agcore::llm::types::Usage; @@ -43,10 +43,10 @@ use async_stream::stream; use futures_core::Stream; use crate::llm::error::LlmError; -use crate::llm::provider::{LlmProvider, ProviderCapabilities, ProviderFeatures}; use crate::llm::types::message::{ContentBlock, ContentBlockType, Message}; use crate::llm::types::request_v2::MessageRequest; use crate::llm::types::response_v2::{MessageResponse, PartialUsage, StreamEvent}; +use crate::llm::{LlmProvider, ProviderCapabilities, ProviderFeatures}; /// 按调用顺序返回预设响应的 [`LlmProvider`]。 /// diff --git a/src/llm/provider.rs b/src/llm/provider.rs index b02975a..fc432c1 100644 --- a/src/llm/provider.rs +++ b/src/llm/provider.rs @@ -4,16 +4,17 @@ pub mod openai; pub mod openai_compat; pub mod registry; -use std::pin::Pin; use std::time::Duration; -use futures_core::Stream; use reqwest::Client; -use serde::{Deserialize, Serialize}; use crate::llm::error::LlmError; -use crate::llm::types::request_v2::MessageRequest; -use crate::llm::types::response_v2::{MessageResponse, StreamEvent}; + +// 向后兼容重导出 —— v0.3.2 Step 3 起,`LlmProvider` / `ProviderCapabilities` / +// `ProviderFeatures` 定义移至 `provider_trait` 模块(`#[cfg(feature = "llm")]`)。 +// 此处重导出使老路径 `agcore::llm::provider::LlmProvider` 仍可用。 +// 推荐下游迁移至 `agcore::llm::LlmProvider`。 +pub use super::provider_trait::{LlmProvider, ProviderCapabilities, ProviderFeatures}; /// Provider 类型枚举 —— `create_provider()` 在编译期 exhaustive match 中使用。 /// @@ -255,63 +256,12 @@ pub fn create_provider( } } -/// Provider 能力描述 —— 静态元信息,调用方据此决定可用特性。 +/// Provider 能力描述、功能开关集合与 `LlmProvider` trait 定义。 /// -/// 设计依据(见 `docs/10-llm-provider-refinement.md` §4 任务 6 决策): -/// `ProviderCapabilities` 与 trait 同文件(`provider.rs`),不分散到类型目录。 -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ProviderCapabilities { - /// 人类可读的 Provider 名(如 `"openai"` / `"anthropic"`)。 - pub provider_name: &'static str, - /// 支持的模型列表(`None` 表示"未列举全部")。 - pub supported_models: Option>, - /// 详细功能开关。 - pub features: ProviderFeatures, -} - -/// Provider 功能开关集合。 -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct ProviderFeatures { - /// 是否支持流式响应。 - pub streaming: bool, - /// 是否支持 thinking / 推理。 - pub thinking: bool, - /// 是否支持图片输入。 - pub vision: bool, - /// 是否支持音频输入。 - pub audio_input: bool, - /// 是否支持工具调用。 - pub tool_use: bool, - /// 是否支持并行工具调用。 - pub parallel_tool_calls: bool, - /// system prompt 是否放在 messages 中(`true`)还是顶层 `system` 字段(`false`)。 - pub system_prompt_in_messages: bool, - /// 模型上下文窗口(tokens);`0` 表示未知。 - pub max_context_window: u32, -} - -/// LLM Provider 抽象接口。 +/// v0.3.2 Step 3 起,上述类型已移至 `src/llm/provider_trait.rs`(`#[cfg(feature = "llm")]`), +/// 使纯 Mock 场景无需引入任何 provider feature。 /// -/// 所有具体的 LLM 后端实现(OpenAI、Anthropic、DeepSeek、Qwen 等) -/// 均需实现此 trait,以实现可插拔替换。 -/// -/// 修订(Phase 0):签名由 `chat(ChatRequest) → ChatResponse` 切换为 -/// `chat(MessageRequest) → MessageResponse`,`chat_stream` 返回新 `StreamEvent` 流, -/// 新增 `capabilities()` 方法。 -#[async_trait::async_trait] -pub trait LlmProvider: Send + Sync { - /// 发送聊天请求并返回完整响应。 - async fn chat(&self, request: MessageRequest) -> Result; - - /// 流式聊天请求 —— 返回新 IR `StreamEvent` 流。 - async fn chat_stream( - &self, - request: MessageRequest, - ) -> Result> + Send>>, LlmError>; - - /// 返回 Provider 静态能力描述。 - fn capabilities(&self) -> ProviderCapabilities; -} +/// 详见 `docs/27-step3-phase26-ci-verification.md` §3.2 工作 0 + ADR-1。 #[cfg(test)] mod tests { diff --git a/src/llm/provider/anthropic.rs b/src/llm/provider/anthropic.rs index 77044d7..bcc6066 100644 --- a/src/llm/provider/anthropic.rs +++ b/src/llm/provider/anthropic.rs @@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; use tracing::{debug, error, info, warn}; -use super::{LlmProvider, ProviderCapabilities, ProviderFeatures}; +use crate::llm::{LlmProvider, ProviderCapabilities, ProviderFeatures}; use crate::llm::error::LlmError; use crate::llm::types::message::{ContentBlock, ContentBlockType, Message}; use crate::llm::types::request_v2::MessageRequest; diff --git a/src/llm/provider/ollama.rs b/src/llm/provider/ollama.rs index 8d1f052..666326a 100644 --- a/src/llm/provider/ollama.rs +++ b/src/llm/provider/ollama.rs @@ -11,7 +11,7 @@ use futures_core::Stream; use reqwest::Client; use super::openai::GenericOpenaiProvider; -use super::{LlmProvider, ProviderCapabilities}; +use crate::llm::{LlmProvider, ProviderCapabilities}; use crate::llm::error::LlmError; use crate::llm::types::request_v2::MessageRequest; use crate::llm::types::response_v2::{MessageResponse, StreamEvent}; diff --git a/src/llm/provider/openai.rs b/src/llm/provider/openai.rs index a508b71..af46a02 100644 --- a/src/llm/provider/openai.rs +++ b/src/llm/provider/openai.rs @@ -21,7 +21,7 @@ use serde::Serialize; use serde_json::Value; use tracing::{debug, error, info}; -use super::{LlmProvider, ProviderCapabilities, ProviderFeatures}; +use crate::llm::{LlmProvider, ProviderCapabilities, ProviderFeatures}; use crate::llm::convert::{from_openai, to_openai}; use crate::llm::error::LlmError; use crate::llm::types::message::{ContentBlock, ContentBlockType, Message}; diff --git a/src/llm/provider/openai_compat.rs b/src/llm/provider/openai_compat.rs index 707ea86..f95b3dd 100644 --- a/src/llm/provider/openai_compat.rs +++ b/src/llm/provider/openai_compat.rs @@ -15,12 +15,11 @@ use std::pin::Pin; use async_trait::async_trait; use futures_core::Stream; -use super::ProviderCapabilities; use super::openai::GenericOpenaiProvider; use crate::llm::error::LlmError; -use crate::llm::provider::LlmProvider; use crate::llm::types::request_v2::MessageRequest; use crate::llm::types::response_v2::{MessageResponse, StreamEvent}; +use crate::llm::{LlmProvider, ProviderCapabilities}; // ============================================================================= // DeepSeek diff --git a/src/llm/provider/registry.rs b/src/llm/provider/registry.rs index f3b0872..53b1efc 100644 --- a/src/llm/provider/registry.rs +++ b/src/llm/provider/registry.rs @@ -3,7 +3,8 @@ use std::collections::HashMap; use crate::llm::error::LlmError; -use crate::llm::provider::{LlmProvider, ProviderConfig, ProviderType, create_provider}; +use crate::llm::provider::{ProviderConfig, ProviderType, create_provider}; +use crate::llm::LlmProvider; /// Provider 注册表 —— 管理多个 LLM Provider 实例。 /// diff --git a/src/llm/provider_trait.rs b/src/llm/provider_trait.rs new file mode 100644 index 0000000..bdc6114 --- /dev/null +++ b/src/llm/provider_trait.rs @@ -0,0 +1,71 @@ +//! LLM Provider 抽象接口 —— trait 定义与能力元数据。 +//! +//! 独立于具体 provider 实现(OpenAI / Anthropic / DeepSeek / Qwen / Ollama), +//! 仅依赖 `llm` feature,不引入 `reqwest`。纯 Mock 场景可仅启用 `llm` feature。 + +use std::pin::Pin; + +use futures_core::Stream; +use serde::{Deserialize, Serialize}; + +use crate::llm::error::LlmError; +use crate::llm::types::request_v2::MessageRequest; +use crate::llm::types::response_v2::{MessageResponse, StreamEvent}; + +/// Provider 能力描述 —— 静态元信息,调用方据此决定可用特性。 +/// +/// 设计依据(见 `docs/10-llm-provider-refinement.md` §4 任务 6 决策): +/// `ProviderCapabilities` 与 trait 同文件,不分散到类型目录。 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ProviderCapabilities { + /// 人类可读的 Provider 名(如 `"openai"` / `"anthropic"`)。 + pub provider_name: &'static str, + /// 支持的模型列表(`None` 表示"未列举全部")。 + pub supported_models: Option>, + /// 详细功能开关。 + pub features: ProviderFeatures, +} + +/// Provider 功能开关集合。 +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct ProviderFeatures { + /// 是否支持流式响应。 + pub streaming: bool, + /// 是否支持 thinking / 推理。 + pub thinking: bool, + /// 是否支持图片输入。 + pub vision: bool, + /// 是否支持音频输入。 + pub audio_input: bool, + /// 是否支持工具调用。 + pub tool_use: bool, + /// 是否支持并行工具调用。 + pub parallel_tool_calls: bool, + /// system prompt 是否放在 messages 中(`true`)还是顶层 `system` 字段(`false`)。 + pub system_prompt_in_messages: bool, + /// 模型上下文窗口(tokens);`0` 表示未知。 + pub max_context_window: u32, +} + +/// LLM Provider 抽象接口。 +/// +/// 所有具体的 LLM 后端实现(OpenAI、Anthropic、DeepSeek、Qwen 等) +/// 均需实现此 trait,以实现可插拔替换。 +/// +/// 修订(Phase 0):签名由 `chat(ChatRequest) → ChatResponse` 切换为 +/// `chat(MessageRequest) → MessageResponse`,`chat_stream` 返回新 `StreamEvent` 流, +/// 新增 `capabilities()` 方法。 +#[async_trait::async_trait] +pub trait LlmProvider: Send + Sync { + /// 发送聊天请求并返回完整响应。 + async fn chat(&self, request: MessageRequest) -> Result; + + /// 流式聊天请求 —— 返回新 IR `StreamEvent` 流。 + async fn chat_stream( + &self, + request: MessageRequest, + ) -> Result> + Send>>, LlmError>; + + /// 返回 Provider 静态能力描述。 + fn capabilities(&self) -> ProviderCapabilities; +} diff --git a/src/memory.rs b/src/memory.rs index f7c3564..aa6e4e7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -16,7 +16,9 @@ pub use error::MemoryError; pub use graph::{GraphEntity, GraphRelation, InMemoryGraph, KnowledgeGraph, RelationDirection, ScoredEntity}; pub use knowledge::KnowledgeStore; pub use retriever::MemoryRetriever; -pub use store::{InMemoryStore, MemoryStore, SqliteStore}; +pub use store::{InMemoryStore, MemoryStore}; +#[cfg(feature = "memory-sqlite")] +pub use store::SqliteStore; #[allow(deprecated)] pub use vector::{InMemoryVectorRetriever, VectorRetriever}; pub use vector_store::{InMemoryVectorStore, PersistentVectorStore, RagPipeline, VectorStore}; diff --git a/src/memory/store.rs b/src/memory/store.rs index e6f9788..fe40f98 100644 --- a/src/memory/store.rs +++ b/src/memory/store.rs @@ -6,9 +6,11 @@ use crate::memory::error::MemoryError; use crate::memory::types::{MemoryFilter, MemoryItem}; pub mod in_memory; +#[cfg(feature = "memory-sqlite")] pub mod sqlite_store; pub use in_memory::InMemoryStore; +#[cfg(feature = "memory-sqlite")] pub use sqlite_store::SqliteStore; /// 底层记忆存储抽象接口。 diff --git a/src/tools.rs b/src/tools.rs index a28a8db..2cf0881 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -2,12 +2,14 @@ pub mod base; pub mod error; +#[cfg(feature = "tools-mcp")] pub mod mcp; pub mod permission; pub mod registry; pub use base::{BaseTool, ToolContext, ToolRef}; pub use error::ToolError; +#[cfg(feature = "tools-mcp")] pub use mcp::{McpClient, McpTransport}; pub use permission::{Permission, PermissionChecker, PermissionConfig}; pub use registry::{ToolInvocation, ToolRegistry};