diff --git a/.agent/workflows/documentation.md b/.agent/workflows/documentation.md new file mode 100644 index 0000000..f65148e --- /dev/null +++ b/.agent/workflows/documentation.md @@ -0,0 +1,109 @@ +--- +description: ドキュメントコメントの書き方ガイドライン +--- + +# ドキュメントコメント スタイルガイド + +## 基本原則 + +1. **利用者視点で書く**: 「何をするものか」「どう使うか」を先に、「なぜそう実装したか」は後に +2. **型パラメータはバッククォートで囲む**: `Handler` ✓ / Handler ✗ +3. **Examplesは`worker::`パスで書く**: re-export先のパスを使用 + +## 構造テンプレート + +```rust +/// [1行目: 何をするものか - 利用者が最初に知りたいこと] +/// +/// [詳細説明: いつ使うか、なぜ使うか、注意点など] +/// +/// # Examples +/// +/// ``` +/// use worker::SomeType; +/// +/// let instance = SomeType::new(); +/// instance.do_something(); +/// ``` +/// +/// # Notes (オプション) +/// +/// 実装上の注意事項や制限があれば記載 +pub struct SomeType { ... } +``` + +## 良い例・悪い例 + +### 構造体/Trait + +```rust +// ❌ 悪い例(実装視点) +/// HandlerからErasedHandlerへのラッパー +/// 各Handlerは独自のScope型を持つため、Timelineで保持するには型消去が必要 + +// ✅ 良い例(利用者視点) +/// `Handler`を`ErasedHandler`として扱うためのラッパー +/// +/// 通常は直接使用せず、`Timeline::on_text_block()`などのメソッド経由で +/// 自動的にラップされます。 +``` + +### メソッド + +```rust +// ❌ 悪い例(処理内容の説明のみ) +/// ツールを登録する + +// ✅ 良い例(何が起きるか、どう使うか) +/// ツールを登録する +/// +/// 登録されたツールはLLMからの呼び出しで自動的に実行されます。 +/// 同名のツールを登録した場合、後から登録したものが優先されます。 +/// +/// # Examples +/// +/// ``` +/// use worker::{Worker, Tool}; +/// +/// worker.register_tool(MyTool::new()); +/// ``` +``` + +### 型パラメータ + +```rust +// ❌ HTMLタグとして解釈されてしまう +/// Handlerを保持するフィールド + +// ✅ バッククォートで囲む +/// `Handler`を保持するフィールド +``` + +## ドキュメントの配置 + +| 項目 | 配置場所 | +|-----|---------| +| 型/trait/関数のdoc | 定義元のクレート(worker-types等) | +| モジュールdoc (`//!`) | 各クレートのlib.rsに書く | +| 実装詳細 | 実装コメント (`//`) を使用 | +| 利用者向けでない内部型 | `#[doc(hidden)]`または`pub(crate)` | + +## Examplesのuseパス + +re-exportされる型のExamplesでは、最終的な公開パスを使用: + +```rust +// worker-types/src/tool.rs でも +/// # Examples +/// ``` +/// use worker::Tool; // ✓ worker_types::Tool ではなく +/// ``` +``` + +## チェックリスト + +- [ ] 1行目は「何をするものか」を利用者視点で説明しているか +- [ ] 型パラメータ (``, `` 等) はバッククォートで囲んでいるか +- [ ] 主要なpub APIにはExamplesがあるか +- [ ] Examplesの`use`パスは`worker::`になっているか +- [ ] `cargo doc --no-deps`で警告が出ないか diff --git a/worker-types/src/event.rs b/worker-types/src/event.rs index 043fd2a..5321400 100644 --- a/worker-types/src/event.rs +++ b/worker-types/src/event.rs @@ -1,6 +1,7 @@ -//! イベント型定義 +//! イベント型 //! -//! llm_client層が出力するフラットなイベント列挙と関連型 +//! LLMからのストリーミングレスポンスを表現するイベント型。 +//! Timeline層がこのイベントを受信し、ハンドラにディスパッチします。 use serde::{Deserialize, Serialize}; @@ -8,21 +9,38 @@ use serde::{Deserialize, Serialize}; // Core Event Types (from llm_client layer) // ============================================================================= -/// llm_client層が出力するフラットなイベント列挙 +/// LLMからのストリーミングイベント /// -/// Timeline層がこのイベントストリームを受け取り、ブロック構造化を行う +/// 各LLMプロバイダからのレスポンスは、この`Event`のストリームとして +/// 統一的に処理されます。 +/// +/// # イベントの種類 +/// +/// - **メタイベント**: `Ping`, `Usage`, `Status`, `Error` +/// - **ブロックイベント**: `BlockStart`, `BlockDelta`, `BlockStop`, `BlockAbort` +/// +/// # ブロックのライフサイクル +/// +/// テキストやツール呼び出しは、`BlockStart` → `BlockDelta`(複数) → `BlockStop` +/// の順序でイベントが発生します。 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Event { - // Meta events (not tied to a block) + /// ハートビート Ping(PingEvent), + /// トークン使用量 Usage(UsageEvent), + /// ストリームのステータス変化 Status(StatusEvent), + /// エラー発生 Error(ErrorEvent), - // Block lifecycle events + /// ブロック開始(テキスト、ツール使用等) BlockStart(BlockStart), + /// ブロックの差分データ BlockDelta(BlockDelta), + /// ブロック正常終了 BlockStop(BlockStop), + /// ブロック中断 BlockAbort(BlockAbort), } diff --git a/worker-types/src/handler.rs b/worker-types/src/handler.rs index d47dcc4..783cf06 100644 --- a/worker-types/src/handler.rs +++ b/worker-types/src/handler.rs @@ -1,6 +1,8 @@ -//! Handler/Kind関連の型定義 +//! Handler/Kind型 //! -//! Timeline層でのイベント処理に使用するトレイトとKind定義 +//! Timeline層でイベントを処理するためのトレイト。 +//! カスタムハンドラを実装してTimelineに登録することで、 +//! ストリームイベントを受信できます。 use crate::event::*; @@ -8,10 +10,11 @@ use crate::event::*; // Kind Trait // ============================================================================= -/// Kindはイベント型のみを定義する +/// イベント種別を定義するマーカートレイト /// -/// スコープはHandler側で定義するため、同じKindに対して -/// 異なるスコープを持つHandlerを登録できる +/// 各Kindは対応するイベント型を指定します。 +/// HandlerはこのKindに対して実装され、同じKindに対して +/// 異なるScope型を持つ複数のHandlerを登録できます。 pub trait Kind { /// このKindに対応するイベント型 type Event; @@ -21,9 +24,39 @@ pub trait Kind { // Handler Trait // ============================================================================= -/// Kindに対する処理を定義し、自身のスコープ型も決定する +/// イベントを処理するハンドラトレイト +/// +/// 特定の`Kind`に対するイベント処理を定義します。 +/// `Scope`はブロックのライフサイクル中に保持される状態です。 +/// +/// # Examples +/// +/// ```ignore +/// use worker::{Handler, TextBlockKind, TextBlockEvent}; +/// +/// struct TextCollector { +/// texts: Vec, +/// } +/// +/// impl Handler for TextCollector { +/// type Scope = String; // ブロックごとのバッファ +/// +/// fn on_event(&mut self, buffer: &mut String, event: &TextBlockEvent) { +/// match event { +/// TextBlockEvent::Delta(text) => buffer.push_str(text), +/// TextBlockEvent::Stop(_) => { +/// self.texts.push(std::mem::take(buffer)); +/// } +/// _ => {} +/// } +/// } +/// } +/// ``` pub trait Handler { /// Handler固有のスコープ型 + /// + /// ブロック開始時に`Default::default()`で生成され、 + /// ブロック終了時に破棄されます。 type Scope: Default; /// イベントを処理する diff --git a/worker-types/src/hook.rs b/worker-types/src/hook.rs index d658cca..2bf6004 100644 --- a/worker-types/src/hook.rs +++ b/worker-types/src/hook.rs @@ -101,15 +101,50 @@ pub enum HookError { // WorkerHook Trait // ============================================================================= -/// Worker Hook trait +/// ターンの進行・ツール実行に介入するためのトレイト /// -/// ターンの進行・メッセージ・ツール実行に対して介入するためのトレイト。 -/// デフォルト実装では何も行わずContinueを返す。 +/// Hookを使うと、メッセージ送信前、ツール実行前後、ターン終了時に +/// 処理を挟んだり、実行をキャンセルしたりできます。 +/// +/// # Examples +/// +/// ```ignore +/// use worker::{WorkerHook, ControlFlow, HookError, ToolCall, TurnResult, Message}; +/// +/// struct ValidationHook; +/// +/// #[async_trait::async_trait] +/// impl WorkerHook for ValidationHook { +/// async fn before_tool_call(&self, call: &mut ToolCall) -> Result { +/// // 危険なツールをブロック +/// if call.name == "delete_all" { +/// return Ok(ControlFlow::Skip); +/// } +/// Ok(ControlFlow::Continue) +/// } +/// +/// async fn on_turn_end(&self, messages: &[Message]) -> Result { +/// // 条件を満たさなければ追加メッセージで継続 +/// if messages.len() < 3 { +/// return Ok(TurnResult::ContinueWithMessages(vec![ +/// Message::user("Please elaborate.") +/// ])); +/// } +/// Ok(TurnResult::Finish) +/// } +/// } +/// ``` +/// +/// # デフォルト実装 +/// +/// すべてのメソッドにはデフォルト実装があり、何も行わず`Continue`を返します。 +/// 必要なメソッドのみオーバーライドしてください。 #[async_trait] pub trait WorkerHook: Send + Sync { - /// メッセージ送信前 + /// メッセージ送信前に呼ばれる /// - /// リクエストに含まれるメッセージリストを改変できる。 + /// リクエストに含まれるメッセージリストを参照・改変できます。 + /// `ControlFlow::Abort`を返すとターンが中断されます。 async fn on_message_send( &self, _context: &mut Vec, @@ -117,16 +152,17 @@ pub trait WorkerHook: Send + Sync { Ok(ControlFlow::Continue) } - /// ツール実行前 + /// ツール実行前に呼ばれる /// - /// 実行をキャンセルしたり、引数を書き換えることができる。 + /// ツール呼び出しの引数を書き換えたり、実行をスキップしたりできます。 + /// `ControlFlow::Skip`を返すとこのツールの実行がスキップされます。 async fn before_tool_call(&self, _tool_call: &mut ToolCall) -> Result { Ok(ControlFlow::Continue) } - /// ツール実行後 + /// ツール実行後に呼ばれる /// - /// 結果を書き換えたり、隠蔽したりできる。 + /// ツールの実行結果を書き換えたり、隠蔽したりできます。 async fn after_tool_call( &self, _tool_result: &mut ToolResult, @@ -134,9 +170,11 @@ pub trait WorkerHook: Send + Sync { Ok(ControlFlow::Continue) } - /// ターン終了時 + /// ターン終了時に呼ばれる /// - /// 生成されたメッセージを検査し、必要ならリトライを指示できる。 + /// 生成されたメッセージを検査し、必要なら追加メッセージで継続を指示できます。 + /// `TurnResult::ContinueWithMessages`を返すと、指定したメッセージを追加して + /// 次のターンに進みます。 async fn on_turn_end(&self, _messages: &[crate::Message]) -> Result { Ok(TurnResult::Finish) } diff --git a/worker-types/src/lib.rs b/worker-types/src/lib.rs index 2a69777..bec2c26 100644 --- a/worker-types/src/lib.rs +++ b/worker-types/src/lib.rs @@ -1,12 +1,11 @@ -//! worker-types - LLMワーカーで使用される型定義 +//! worker-types - LLMワーカーの型定義 //! -//! このクレートは以下を提供します: -//! - Event: llm_client層からのフラットなイベント列挙 -//! - Kind/Handler: タイムライン層でのイベント処理トレイト -//! - Tool: ツール定義トレイト -//! - Hook: Worker層での介入用トレイト -//! - Message: メッセージ型 -//! - 各種イベント構造体 +//! このクレートは`worker`クレートで使用される型を提供します。 +//! 通常は直接使用せず、`worker`クレート経由で利用してください。 +//! +//! ```ignore +//! use worker::{Event, Message, Tool, WorkerHook}; +//! ``` mod event; mod handler; diff --git a/worker-types/src/message.rs b/worker-types/src/message.rs index dc66909..8822315 100644 --- a/worker-types/src/message.rs +++ b/worker-types/src/message.rs @@ -1,6 +1,7 @@ -//! メッセージ型定義 +//! メッセージ型 //! -//! LLM会話で使用されるメッセージ構造 +//! LLMとの会話で使用されるメッセージ構造。 +//! [`Message::user`]や[`Message::assistant`]で簡単に作成できます。 use serde::{Deserialize, Serialize}; @@ -14,7 +15,19 @@ pub enum Role { Assistant, } -/// メッセージ +/// 会話のメッセージ +/// +/// # Examples +/// +/// ```ignore +/// use worker::Message; +/// +/// // ユーザーメッセージ +/// let user_msg = Message::user("Hello!"); +/// +/// // アシスタントメッセージ +/// let assistant_msg = Message::assistant("Hi there!"); +/// ``` #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Message { /// ロール @@ -62,6 +75,13 @@ pub enum ContentPart { impl Message { /// ユーザーメッセージを作成 + /// + /// # Examples + /// + /// ```ignore + /// use worker::Message; + /// let msg = Message::user("こんにちは"); + /// ``` pub fn user(content: impl Into) -> Self { Self { role: Role::User, @@ -70,6 +90,9 @@ impl Message { } /// アシスタントメッセージを作成 + /// + /// 通常はWorker内部で自動生成されますが、 + /// 履歴の初期化などで手動作成も可能です。 pub fn assistant(content: impl Into) -> Self { Self { role: Role::Assistant, @@ -78,6 +101,9 @@ impl Message { } /// ツール結果メッセージを作成 + /// + /// Worker内部でツール実行後に自動生成されます。 + /// 通常は直接作成する必要はありません。 pub fn tool_result(tool_use_id: impl Into, content: impl Into) -> Self { Self { role: Role::User, diff --git a/worker-types/src/state.rs b/worker-types/src/state.rs index eecdd73..14728b8 100644 --- a/worker-types/src/state.rs +++ b/worker-types/src/state.rs @@ -1,24 +1,41 @@ -//! Worker状態マーカー型 +//! Worker状態 //! -//! Type-stateパターンによるキャッシュ保護のための状態定義 +//! Type-stateパターンによるキャッシュ保護のための状態マーカー型。 +//! Workerは`Mutable` → `Locked`の状態遷移を持ちます。 /// Worker状態を表すマーカートレイト /// -/// このトレイトはシールされており、外部から実装することはできない。 +/// このトレイトはシールされており、外部から実装することはできません。 pub trait WorkerState: private::Sealed + Send + Sync + 'static {} mod private { pub trait Sealed {} } -/// 変更可能状態 +/// 編集可能状態 /// -/// この状態では以下の操作が可能: +/// この状態では以下の操作が可能です: /// - システムプロンプトの設定・変更 /// - メッセージ履歴の編集(追加、削除、クリア) /// - ツール・Hookの登録 /// -/// `lock()` によって `Locked` 状態へ遷移できる。 +/// `Worker::lock()`により[`Locked`]状態へ遷移できます。 +/// +/// # Examples +/// +/// ```ignore +/// use worker::Worker; +/// +/// let mut worker = Worker::new(client) +/// .system_prompt("You are helpful."); +/// +/// // 履歴を編集可能 +/// worker.push_message(Message::user("Hello")); +/// worker.clear_history(); +/// +/// // ロックして保護状態へ +/// let locked = worker.lock(); +/// ``` #[derive(Debug, Clone, Copy, Default)] pub struct Mutable; @@ -27,12 +44,15 @@ impl WorkerState for Mutable {} /// ロック状態(キャッシュ保護) /// -/// この状態では以下の制限がある: +/// この状態では以下の制限があります: /// - システムプロンプトの変更不可 /// - 既存メッセージ履歴の変更不可(末尾への追記のみ) /// -/// 実行(`run`)はこの状態で行うことが推奨される。 -/// キャッシュヒットを保証するため、前方のコンテキストは不変となる。 +/// LLM APIのKVキャッシュヒットを保証するため、 +/// 実行時にはこの状態の使用が推奨されます。 +/// +/// `Worker::unlock()`により[`Mutable`]状態へ戻せますが、 +/// キャッシュ保護が解除されることに注意してください。 #[derive(Debug, Clone, Copy, Default)] pub struct Locked; diff --git a/worker-types/src/subscriber.rs b/worker-types/src/subscriber.rs index ac62ef1..8276f9e 100644 --- a/worker-types/src/subscriber.rs +++ b/worker-types/src/subscriber.rs @@ -1,7 +1,7 @@ -//! WorkerSubscriber - Worker層のイベント購読トレイト +//! イベント購読 //! -//! Timeline層のHandler機構の薄いラッパーとして設計され、 -//! UIへのストリーミング表示やリアルタイムフィードバックを可能にする。 +//! LLMからのストリーミングイベントをリアルタイムで受信するためのトレイト。 +//! UIへのストリーム表示やプログレス表示に使用します。 use crate::{ErrorEvent, StatusEvent, TextBlockEvent, ToolCall, ToolUseBlockEvent, UsageEvent}; @@ -9,39 +9,42 @@ use crate::{ErrorEvent, StatusEvent, TextBlockEvent, ToolCall, ToolUseBlockEvent // WorkerSubscriber Trait // ============================================================================= -/// Worker層の統合Subscriberトレイト +/// LLMからのストリーミングイベントを購読するトレイト /// -/// Timeline層のHandler機構をラップし、以下のイベントを一括で購読できる: -/// - ブロックイベント(スコープ管理あり): Text, ToolUse -/// - 単発イベント: Usage, Status, Error -/// - 累積イベント(Worker層で追加): TextComplete, ToolCallComplete -/// - ターン制御: TurnStart, TurnEnd +/// Workerに登録すると、テキスト生成やツール呼び出しのイベントを +/// リアルタイムで受信できます。UIへのストリーム表示に最適です。 /// -/// # 使用例 +/// # 受信できるイベント +/// +/// - **ブロックイベント**: テキスト、ツール使用(スコープ付き) +/// - **メタイベント**: 使用量、ステータス、エラー +/// - **完了イベント**: テキスト完了、ツール呼び出し完了 +/// - **ターン制御**: ターン開始、ターン終了 +/// +/// # Examples /// /// ```ignore -/// struct MyUI { -/// chat_view: ChatView, -/// } +/// use worker::{WorkerSubscriber, TextBlockEvent}; /// -/// impl WorkerSubscriber for MyUI { -/// type TextBlockScope = String; -/// type ToolUseBlockScope = ToolComponent; +/// struct StreamPrinter; /// -/// fn on_text_block(&mut self, buffer: &mut String, event: &TextBlockEvent) { -/// match event { -/// TextBlockEvent::Delta(text) => { -/// buffer.push_str(text); -/// self.chat_view.update(buffer); -/// } -/// _ => {} +/// impl WorkerSubscriber for StreamPrinter { +/// type TextBlockScope = (); +/// type ToolUseBlockScope = (); +/// +/// fn on_text_block(&mut self, _: &mut (), event: &TextBlockEvent) { +/// if let TextBlockEvent::Delta(text) = event { +/// print!("{}", text); // リアルタイム出力 /// } /// } /// /// fn on_text_complete(&mut self, text: &str) { -/// self.chat_view.add_to_history(text); +/// println!("\n--- Complete: {} chars ---", text.len()); /// } /// } +/// +/// // Workerに登録 +/// worker.subscribe(StreamPrinter); /// ``` pub trait WorkerSubscriber: Send { // ========================================================================= diff --git a/worker-types/src/tool.rs b/worker-types/src/tool.rs index 9ac4cdd..aa4b472 100644 --- a/worker-types/src/tool.rs +++ b/worker-types/src/tool.rs @@ -1,33 +1,90 @@ +//! ツール定義 +//! +//! LLMから呼び出し可能なツールを定義するためのトレイト。 +//! 通常は`#[tool]`マクロを使用して自動実装します。 + use async_trait::async_trait; use serde_json::Value; use thiserror::Error; +/// ツール実行時のエラー #[derive(Debug, Error)] pub enum ToolError { + /// 引数が不正 #[error("Invalid argument: {0}")] InvalidArgument(String), + /// 実行に失敗 #[error("Execution failed: {0}")] ExecutionFailed(String), + /// 内部エラー #[error("Internal error: {0}")] Internal(String), } -/// ツール定義トレイト +/// LLMから呼び出し可能なツールを定義するトレイト /// -/// ユーザー定義のツールはこれを実装し、Workerに登録される。 -/// 通常は `#[tool]` マクロによって自動生成される。 +/// ツールはLLMが外部リソースにアクセスしたり、 +/// 計算を実行したりするために使用します。 +/// +/// # 実装方法 +/// +/// 通常は`#[tool]`マクロを使用して自動実装します: +/// +/// ```ignore +/// use worker::tool; +/// +/// #[tool(description = "Search the web for information")] +/// async fn search(query: String) -> String { +/// // 検索処理 +/// format!("Results for: {}", query) +/// } +/// ``` +/// +/// # 手動実装 +/// +/// ```ignore +/// use worker::{Tool, ToolError}; +/// use serde_json::{json, Value}; +/// +/// struct MyTool; +/// +/// #[async_trait::async_trait] +/// impl Tool for MyTool { +/// fn name(&self) -> &str { "my_tool" } +/// fn description(&self) -> &str { "My custom tool" } +/// fn input_schema(&self) -> Value { +/// json!({ +/// "type": "object", +/// "properties": { +/// "query": { "type": "string" } +/// }, +/// "required": ["query"] +/// }) +/// } +/// async fn execute(&self, input: &str) -> Result { +/// Ok("result".to_string()) +/// } +/// } +/// ``` #[async_trait] pub trait Tool: Send + Sync { - /// ツール名 (LLMが識別に使用) + /// ツール名(LLMが識別に使用) fn name(&self) -> &str; - /// ツールの説明 (LLMへのプロンプトに含まれる) + /// ツールの説明(LLMへのプロンプトに含まれる) fn description(&self) -> &str; /// 引数のJSON Schema + /// + /// LLMはこのスキーマに従って引数を生成します。 fn input_schema(&self) -> Value; - /// 実行関数 - /// JSON文字列を受け取り、デシリアライズして元のメソッドを実行し、結果を返す + /// ツールを実行する + /// + /// # Arguments + /// * `input_json` - LLMが生成したJSON形式の引数 + /// + /// # Returns + /// 実行結果の文字列。この内容がLLMに返されます。 async fn execute(&self, input_json: &str) -> Result; } diff --git a/worker/src/lib.rs b/worker/src/lib.rs index 6401401..72b5bc3 100644 --- a/worker/src/lib.rs +++ b/worker/src/lib.rs @@ -1,10 +1,39 @@ -//! worker - LLMワーカーのメイン実装 +//! worker - LLMワーカーライブラリ //! -//! このクレートは以下を提供します: -//! - Worker: ターン制御を行う高レベルコンポーネント -//! - Timeline: イベントストリームの状態管理とハンドラーへのディスパッチ -//! - LlmClient: LLMプロバイダとの通信 -//! - 型消去されたHandler実装 +//! LLMとの対話を管理するコンポーネントを提供します。 +//! +//! # 主要なコンポーネント +//! +//! - [`Worker`] - LLMとの対話を管理する中心コンポーネント +//! - [`Tool`] - LLMから呼び出し可能なツール +//! - [`WorkerHook`] - ターン進行への介入 +//! - [`WorkerSubscriber`] - ストリーミングイベントの購読 +//! +//! # Quick Start +//! +//! ```ignore +//! use worker::{Worker, Message}; +//! +//! // Workerを作成 +//! let mut worker = Worker::new(client) +//! .system_prompt("You are a helpful assistant."); +//! +//! // ツールを登録(オプション) +//! worker.register_tool(my_tool); +//! +//! // 対話を実行 +//! let history = worker.run("Hello!").await?; +//! ``` +//! +//! # キャッシュ保護 +//! +//! KVキャッシュのヒット率を最大化するには、[`Worker::lock()`]で +//! ロック状態に遷移してから実行してください。 +//! +//! ```ignore +//! let mut locked = worker.lock(); +//! locked.run("user input").await?; +//! ``` pub mod llm_client; mod subscriber_adapter; diff --git a/worker/src/llm_client/mod.rs b/worker/src/llm_client/mod.rs index 871ee96..7e01614 100644 --- a/worker/src/llm_client/mod.rs +++ b/worker/src/llm_client/mod.rs @@ -1,12 +1,19 @@ //! LLMクライアント層 //! -//! LLMプロバイダと通信し、統一された`Event`ストリームを出力する。 +//! 各LLMプロバイダと通信し、統一された[`Event`](crate::Event)ストリームを出力します。 +//! +//! # サポートするプロバイダ +//! +//! - Anthropic (Claude) +//! - OpenAI (GPT-4, etc.) +//! - Google (Gemini) +//! - Ollama (ローカルLLM) //! //! # アーキテクチャ //! -//! - **client**: `LlmClient` trait定義 -//! - **scheme**: APIスキーマ(リクエスト/レスポンス変換) -//! - **providers**: プロバイダ固有のHTTPクライアント実装 +//! - [`LlmClient`] - プロバイダ共通のtrait +//! - `providers`: プロバイダ固有のクライアント実装 +//! - `scheme`: APIスキーマ(リクエスト/レスポンス変換) pub mod client; pub mod error; diff --git a/worker/src/timeline.rs b/worker/src/timeline.rs index 72afa07..7d85f85 100644 --- a/worker/src/timeline.rs +++ b/worker/src/timeline.rs @@ -1,6 +1,7 @@ -//! Timeline層の実装 +//! Timeline層 //! -//! イベントストリームを受信し、登録されたHandlerへディスパッチする +//! LLMからのイベントストリームを受信し、登録されたHandlerにディスパッチします。 +//! 通常はWorker経由で使用しますが、直接使用することも可能です。 use std::marker::PhantomData; @@ -10,9 +11,11 @@ use worker_types::*; // Type-erased Handler // ============================================================================= -/// 型消去されたHandler trait +/// 型消去された`Handler` trait /// -/// 各Handlerは独自のScope型を持つため、Timelineで保持するには型消去が必要 +/// 各Handlerは独自のScope型を持つため、Timelineで保持するには型消去が必要です。 +/// 通常は直接使用せず、`Timeline::on_text_block()`などのメソッド経由で +/// 自動的にラップされます。 pub trait ErasedHandler: Send { /// イベントをディスパッチ fn dispatch(&mut self, event: &K::Event); @@ -22,7 +25,7 @@ pub trait ErasedHandler: Send { fn end_scope(&mut self); } -/// HandlerからErasedHandlerへのラッパー +/// `Handler`を`ErasedHandler`として扱うためのラッパー pub struct HandlerWrapper where H: Handler, @@ -316,13 +319,34 @@ where // Timeline // ============================================================================= -/// Timeline - イベントストリームの状態管理とディスパッチ +/// イベントストリームの管理とハンドラへのディスパッチ /// -/// # 責務 -/// 1. Eventストリームを受信 -/// 2. Block系イベントをBlockKindごとのライフサイクルイベントに変換 -/// 3. 各Handlerごとのスコープの生成・管理 -/// 4. 登録されたHandlerへの登録順ディスパッチ +/// LLMからのイベントを受信し、登録されたハンドラに振り分けます。 +/// ブロック系イベントはスコープ管理付きで処理されます。 +/// +/// # Examples +/// +/// ```ignore +/// use worker::{Timeline, Handler, TextBlockKind, TextBlockEvent}; +/// +/// struct MyHandler; +/// impl Handler for MyHandler { +/// type Scope = String; +/// fn on_event(&mut self, buffer: &mut String, event: &TextBlockEvent) { +/// if let TextBlockEvent::Delta(text) = event { +/// buffer.push_str(text); +/// } +/// } +/// } +/// +/// let mut timeline = Timeline::new(); +/// timeline.on_text_block(MyHandler); +/// ``` +/// +/// # サポートするイベント種別 +/// +/// - **メタ系**: Usage, Ping, Status, Error +/// - **ブロック系**: TextBlock, ThinkingBlock, ToolUseBlock pub struct Timeline { // Meta系ハンドラー usage_handlers: Vec>>, diff --git a/worker/src/worker.rs b/worker/src/worker.rs index c10bc3e..1dca9ca 100644 --- a/worker/src/worker.rs +++ b/worker/src/worker.rs @@ -82,20 +82,40 @@ impl TurnNotifier for SubscriberTurnNotifier { // Worker // ============================================================================= -/// Worker - ターン制御コンポーネント +/// LLMとの対話を管理する中心コンポーネント /// -/// Type-stateパターンによりキャッシュ保護を実現する。 +/// ユーザーからの入力を受け取り、LLMにリクエストを送信し、 +/// ツール呼び出しがあれば自動的に実行してターンを進行させます。 /// -/// # 状態 -/// - `Mutable`: 初期状態。システムプロンプトや履歴を自由に編集可能。 -/// - `Locked`: キャッシュ保護状態。前方コンテキストは不変となり、追記のみ可能。 +/// # 状態遷移(Type-state) /// -/// # 責務 -/// - LLMへのリクエスト送信とレスポンス処理 -/// - ツール呼び出しの収集と実行 -/// - Hookによる介入の提供 -/// - ターンループの制御 -/// - 履歴の所有と管理 +/// - [`Mutable`]: 初期状態。システムプロンプトや履歴を自由に編集可能。 +/// - [`Locked`]: キャッシュ保護状態。`lock()`で遷移。前方コンテキストは不変。 +/// +/// # Examples +/// +/// ```ignore +/// use worker::{Worker, Message}; +/// +/// // Workerを作成してツールを登録 +/// let mut worker = Worker::new(client) +/// .system_prompt("You are a helpful assistant."); +/// worker.register_tool(my_tool); +/// +/// // 対話を実行 +/// let history = worker.run("Hello!").await?; +/// ``` +/// +/// # キャッシュ保護が必要な場合 +/// +/// ```ignore +/// let mut worker = Worker::new(client) +/// .system_prompt("..."); +/// +/// // 履歴を設定後、ロックしてキャッシュを保護 +/// let mut locked = worker.lock(); +/// locked.run("user input").await?; +/// ``` pub struct Worker { /// LLMクライアント client: C, @@ -128,13 +148,37 @@ pub struct Worker { // ============================================================================= impl Worker { - /// WorkerSubscriberを登録 + /// イベント購読者を登録する /// - /// Subscriberは以下のイベントを受け取ることができる: - /// - ブロックイベント: on_text_block, on_tool_use_block - /// - 単発イベント: on_usage, on_status, on_error - /// - 累積イベント: on_text_complete, on_tool_call_complete - /// - ターン制御: on_turn_start, on_turn_end + /// 登録したSubscriberは、LLMからのストリーミングイベントを + /// リアルタイムで受信できます。UIへのストリーム表示などに利用します。 + /// + /// # 受信できるイベント + /// + /// - **ブロックイベント**: `on_text_block`, `on_tool_use_block` + /// - **メタイベント**: `on_usage`, `on_status`, `on_error` + /// - **完了イベント**: `on_text_complete`, `on_tool_call_complete` + /// - **ターン制御**: `on_turn_start`, `on_turn_end` + /// + /// # Examples + /// + /// ```ignore + /// use worker::{Worker, WorkerSubscriber, TextBlockEvent}; + /// + /// struct MyPrinter; + /// impl WorkerSubscriber for MyPrinter { + /// type TextBlockScope = (); + /// type ToolUseBlockScope = (); + /// + /// fn on_text_block(&mut self, _: &mut (), event: &TextBlockEvent) { + /// if let TextBlockEvent::Delta(text) = event { + /// print!("{}", text); + /// } + /// } + /// } + /// + /// worker.subscribe(MyPrinter); + /// ``` pub fn subscribe(&mut self, subscriber: Sub) { let subscriber = Arc::new(Mutex::new(subscriber)); @@ -159,7 +203,19 @@ impl Worker { .push(Box::new(SubscriberTurnNotifier { subscriber })); } - /// ツールを登録 + /// ツールを登録する + /// + /// 登録されたツールはLLMからの呼び出しで自動的に実行されます。 + /// 同名のツールを登録した場合、後から登録したものが優先されます。 + /// + /// # Examples + /// + /// ```ignore + /// use worker::Worker; + /// use my_tools::SearchTool; + /// + /// worker.register_tool(SearchTool::new()); + /// ``` pub fn register_tool(&mut self, tool: impl Tool + 'static) { let name = tool.name().to_string(); self.tools.insert(name, Arc::new(tool)); @@ -172,7 +228,28 @@ impl Worker { } } - /// Hookを追加 + /// Hookを追加する + /// + /// Hookはターンの進行・ツール実行に介入できます。 + /// 複数のHookを登録した場合、登録順に実行されます。 + /// + /// # Examples + /// + /// ```ignore + /// use worker::{Worker, WorkerHook, ControlFlow, ToolCall}; + /// + /// struct LoggingHook; + /// + /// #[async_trait::async_trait] + /// impl WorkerHook for LoggingHook { + /// async fn before_tool_call(&self, call: &mut ToolCall) -> Result { + /// println!("Calling tool: {}", call.name); + /// Ok(ControlFlow::Continue) + /// } + /// } + /// + /// worker.add_hook(LoggingHook); + /// ``` pub fn add_hook(&mut self, hook: impl WorkerHook + 'static) { self.hooks.push(Box::new(hook)); }