From 0dc231a5e9ce28d05afc2b976828e2e7dfc5ec71 Mon Sep 17 00:00:00 2001 From: Kayshen-X Date: Sat, 16 May 2026 23:24:29 +0800 Subject: [PATCH] refactor: extract op-ai crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relocate the AI chat layer (chat_provider.rs, chat_models.rs, agent_settings_state.rs) out of openpencil-shell-core into a dedicated op-ai crate. These three modules are dependency-free transport-free data shapes — the ChatProvider trait, the ModelEntry catalog type, and the Cmd+, settings-modal state — so they form a clean wasm32-clean leaf crate. A fresh lib.rs declares all three as modules; chat_models' `crate::agent_settings_state::` path stays valid. openpencil-desktop's chat_*.rs transports + model_discovery.rs now import `op_ai::*`. Pure relocation, no behaviour change. --- crates/op-ai/Cargo.toml | 13 ++++++++++++ .../src/agent_settings_state.rs | 0 .../src/chat_models.rs | 0 .../src/chat_provider.rs | 0 crates/op-ai/src/lib.rs | 20 +++++++++++++++++++ crates/openpencil-desktop/Cargo.toml | 5 +++++ crates/openpencil-desktop/src/chat_claude.rs | 2 +- crates/openpencil-desktop/src/chat_copilot.rs | 2 +- .../src/chat_http_server.rs | 2 +- crates/openpencil-desktop/src/chat_runtime.rs | 4 ++-- crates/openpencil-desktop/src/chat_session.rs | 4 ++-- .../openpencil-desktop/src/chat_subprocess.rs | 2 +- .../openpencil-desktop/src/model_discovery.rs | 6 +++--- crates/openpencil-shell-core/src/lib.rs | 3 --- 14 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 crates/op-ai/Cargo.toml rename crates/{openpencil-shell-core => op-ai}/src/agent_settings_state.rs (100%) rename crates/{openpencil-shell-core => op-ai}/src/chat_models.rs (100%) rename crates/{openpencil-shell-core => op-ai}/src/chat_provider.rs (100%) create mode 100644 crates/op-ai/src/lib.rs diff --git a/crates/op-ai/Cargo.toml b/crates/op-ai/Cargo.toml new file mode 100644 index 000000000..ef58e8fe1 --- /dev/null +++ b/crates/op-ai/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "op-ai" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +description = "OpenPencil AI chat layer — transport-free ChatProvider trait + model catalog + agent-settings state" + +[lib] +name = "op_ai" +path = "src/lib.rs" + +[dependencies] diff --git a/crates/openpencil-shell-core/src/agent_settings_state.rs b/crates/op-ai/src/agent_settings_state.rs similarity index 100% rename from crates/openpencil-shell-core/src/agent_settings_state.rs rename to crates/op-ai/src/agent_settings_state.rs diff --git a/crates/openpencil-shell-core/src/chat_models.rs b/crates/op-ai/src/chat_models.rs similarity index 100% rename from crates/openpencil-shell-core/src/chat_models.rs rename to crates/op-ai/src/chat_models.rs diff --git a/crates/openpencil-shell-core/src/chat_provider.rs b/crates/op-ai/src/chat_provider.rs similarity index 100% rename from crates/openpencil-shell-core/src/chat_provider.rs rename to crates/op-ai/src/chat_provider.rs diff --git a/crates/op-ai/src/lib.rs b/crates/op-ai/src/lib.rs new file mode 100644 index 000000000..e8cbaebbe --- /dev/null +++ b/crates/op-ai/src/lib.rs @@ -0,0 +1,20 @@ +//! OpenPencil AI chat layer. +//! +//! This crate carries the transport-free data shapes for the editor's +//! chat / agent integration — extracted out of `openpencil-shell-core` +//! in the Phase 7 strangler reorg: +//! +//! - [`chat_provider`] — the `ChatProvider` trait + provider-category +//! types (`CliName`, `Provider`, …) and the `EchoProvider` test +//! double. Real transports (tokio / reqwest / process-spawn) live +//! desktop-side in `openpencil-desktop`. +//! - [`chat_models`] — the `ModelEntry` model-catalog type. +//! - [`agent_settings_state`] — state types for the Cmd+, settings +//! modal (`AgentSettingsTab`, `AgentProvider`, …). +//! +//! The crate is dependency-free and wasm32-clean so both the native +//! and web shells can build against it. + +pub mod agent_settings_state; +pub mod chat_models; +pub mod chat_provider; diff --git a/crates/openpencil-desktop/Cargo.toml b/crates/openpencil-desktop/Cargo.toml index 93bcb70ab..f1d984dc4 100644 --- a/crates/openpencil-desktop/Cargo.toml +++ b/crates/openpencil-desktop/Cargo.toml @@ -25,6 +25,11 @@ op-editor-core = { path = "../op-editor-core" } # (`mcp.rs` + `mcp/*`) was extracted out of openpencil-shell-core into the # op-mcp crate; `mcp_serve.rs` registers tools through `op_mcp::*`. op-mcp = { path = "../op-mcp" } +# Phase 7 strangler reorg: the transport-free AI chat data shapes +# (`ChatProvider` trait + model catalog + agent-settings state) were +# extracted into op-ai; the `src/chat_*.rs` real transports + the +# model-discovery path import them through `op_ai::*`. +op-ai = { path = "../op-ai" } # Canonical `.op` (PenDocument) → shell `Document` loader / adapter. # Extracted out of this binary into a shared library crate so library # crates (openpencil-shell-native) can reuse the conversion; diff --git a/crates/openpencil-desktop/src/chat_claude.rs b/crates/openpencil-desktop/src/chat_claude.rs index f21ecbb51..03b4f77da 100644 --- a/crates/openpencil-desktop/src/chat_claude.rs +++ b/crates/openpencil-desktop/src/chat_claude.rs @@ -21,7 +21,7 @@ use anthropic_agent_sdk::{ types::{ContentBlock, Message}, ClaudeAgentOptions, StreamExt, }; -use openpencil_shell_core::chat_provider::{ +use op_ai::chat_provider::{ ChatDelta, ChatProvider, ChatRequest, StopReason, }; use tokio::sync::mpsc; diff --git a/crates/openpencil-desktop/src/chat_copilot.rs b/crates/openpencil-desktop/src/chat_copilot.rs index 54586a0a3..a70394d9f 100644 --- a/crates/openpencil-desktop/src/chat_copilot.rs +++ b/crates/openpencil-desktop/src/chat_copilot.rs @@ -27,7 +27,7 @@ use github_copilot_sdk::handler::{ }; use github_copilot_sdk::types::{MessageOptions, SessionConfig, SessionEvent}; use github_copilot_sdk::{Client, ClientOptions}; -use openpencil_shell_core::chat_provider::{ +use op_ai::chat_provider::{ ChatDelta, ChatProvider, ChatRequest, StopReason, }; use tokio::sync::mpsc; diff --git a/crates/openpencil-desktop/src/chat_http_server.rs b/crates/openpencil-desktop/src/chat_http_server.rs index ab36cb5b4..c04a74bd0 100644 --- a/crates/openpencil-desktop/src/chat_http_server.rs +++ b/crates/openpencil-desktop/src/chat_http_server.rs @@ -39,7 +39,7 @@ use std::sync::Arc; use std::time::Duration; -use openpencil_shell_core::chat_provider::{ +use op_ai::chat_provider::{ ChatDelta, ChatProvider, ChatRequest, CliName, StopReason, }; use serde::Serialize; diff --git a/crates/openpencil-desktop/src/chat_runtime.rs b/crates/openpencil-desktop/src/chat_runtime.rs index ab04b91d4..c0a437e4f 100644 --- a/crates/openpencil-desktop/src/chat_runtime.rs +++ b/crates/openpencil-desktop/src/chat_runtime.rs @@ -1,5 +1,5 @@ //! Real `ChatProvider` impls — the desktop-side companion to the -//! abstraction in `openpencil_shell_core::chat_provider`. Shell-core +//! abstraction in `op_ai::chat_provider`. Shell-core //! intentionally stays wasm32-clean (no tokio / reqwest / process), so //! transports live here on the native binary. //! @@ -27,7 +27,7 @@ use agent::provider::Provider; use agent::query::QueryEngine; use agent::stream::Event; use futures::StreamExt; -use openpencil_shell_core::chat_provider::{ +use op_ai::chat_provider::{ ChatDelta, ChatProvider, ChatRequest, StopReason, }; use tokio::runtime::{Builder, Runtime}; diff --git a/crates/openpencil-desktop/src/chat_session.rs b/crates/openpencil-desktop/src/chat_session.rs index 91927732b..08f97e27e 100644 --- a/crates/openpencil-desktop/src/chat_session.rs +++ b/crates/openpencil-desktop/src/chat_session.rs @@ -10,7 +10,7 @@ use std::sync::mpsc::{self, Receiver, TryRecvError}; use std::thread; -use openpencil_shell_core::chat_provider::{ +use op_ai::chat_provider::{ ChatDelta, ChatProvider, ChatRequest, CliName, }; use openpencil_shell_native::WidgetHostNative; @@ -207,7 +207,7 @@ pub fn pump( #[cfg(test)] mod tests { use super::*; - use openpencil_shell_core::chat_provider::{EchoProvider, StopReason}; + use op_ai::chat_provider::{EchoProvider, StopReason}; #[test] fn session_streams_echo_provider_deltas_to_completion() { diff --git a/crates/openpencil-desktop/src/chat_subprocess.rs b/crates/openpencil-desktop/src/chat_subprocess.rs index 2ef5d87dd..90c6b3495 100644 --- a/crates/openpencil-desktop/src/chat_subprocess.rs +++ b/crates/openpencil-desktop/src/chat_subprocess.rs @@ -33,7 +33,7 @@ use std::path::PathBuf; use std::sync::Arc; -use openpencil_shell_core::chat_provider::{ +use op_ai::chat_provider::{ ChatDelta, ChatProvider, ChatRequest, CliName, StopReason, }; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; diff --git a/crates/openpencil-desktop/src/model_discovery.rs b/crates/openpencil-desktop/src/model_discovery.rs index 77b4d4004..6c8d834f8 100644 --- a/crates/openpencil-desktop/src/model_discovery.rs +++ b/crates/openpencil-desktop/src/model_discovery.rs @@ -23,8 +23,8 @@ use std::process::{Command, Stdio}; use std::sync::mpsc::{self, Receiver, TryRecvError}; use std::time::{Duration, Instant}; -use openpencil_shell_core::agent_settings_state::AgentProvider; -use openpencil_shell_core::chat_models::ModelEntry; +use op_ai::agent_settings_state::AgentProvider; +use op_ai::chat_models::ModelEntry; /// Background model-discovery probe. [`discover_models`] reads a /// cache file and spawns a subprocess (`opencode models`, ~1 s), @@ -79,7 +79,7 @@ impl ModelProbe { /// Translate a shell-core `ModelEntry` into op-editor-core's. fn model_entry_to_ec(m: ModelEntry) -> op_editor_core::ModelEntry { - use openpencil_shell_core::agent_settings_state::AgentProvider as ScP; + use op_ai::agent_settings_state::AgentProvider as ScP; let provider = match m.provider { ScP::ClaudeCode => op_editor_core::AgentProvider::ClaudeCode, ScP::CodexCli => op_editor_core::AgentProvider::CodexCli, diff --git a/crates/openpencil-shell-core/src/lib.rs b/crates/openpencil-shell-core/src/lib.rs index ca9ac56a3..150e0050a 100644 --- a/crates/openpencil-shell-core/src/lib.rs +++ b/crates/openpencil-shell-core/src/lib.rs @@ -16,9 +16,6 @@ //! differentiation lives at the canvas viewport / chrome layer //! (single-page + infinite canvas recommended, multi-page also supported). -pub mod agent_settings_state; -pub mod chat_models; -pub mod chat_provider; // Phase 3 strangler reorg: i18n extracted into the op-i18n crate. Re-exported // as `i18n` so `crate::i18n::translate` / `crate::i18n::Locale` paths still resolve. pub use op_i18n as i18n;