feat: #[tool_registry] and #[tool] macros
This commit is contained in:
+206
-1
@@ -228,4 +228,209 @@ pub enum TurnResult {
|
||||
* ただし、リアルタイム性を重視する場合(ストリーミング中にToolを実行開始等)は将来的な拡張とするが、現状は「結果が揃うのを待って」という要件に従い、収集フェーズと実行フェーズを分ける。
|
||||
|
||||
3. **worker-macros**:
|
||||
* `syn`, `quote` を用いて、関数定義から `Tool` トレイト実装と `InputInputSchema` (schemars利用) を生成。
|
||||
* `syn`, `quote` を用いて、関数定義から `Tool` トレイト実装と `InputSchema` (schemars利用) を生成。
|
||||
|
||||
## Worker Event API 設計
|
||||
|
||||
### 背景と目的
|
||||
|
||||
Workerは内部でイベントを処理し結果を返しますが、UIへのストリーミング表示やリアルタイムフィードバックには、イベントを外部に公開する仕組みが必要です。
|
||||
|
||||
**要件**:
|
||||
1. テキストデルタをリアルタイムでUIに表示
|
||||
2. ツール呼び出しの進行状況を表示
|
||||
3. ブロック完了時に累積結果を受け取る
|
||||
|
||||
### 設計思想
|
||||
|
||||
Worker APIは **Timeline層のHandler機構の薄いラッパー** として設計します。
|
||||
|
||||
| 層 | 目的 | 提供するもの |
|
||||
|---|------|-------------|
|
||||
| **Handler (Timeline層)** | 内部実装、役割分離 | スコープ管理 + Deltaイベント |
|
||||
| **Worker Event API** | ユーザー向け利便性 | Handler露出 + Completeイベント追加 |
|
||||
|
||||
Handlerのスコープ管理パターン(Start→Delta→End)をそのまま活かしつつ、累積済みのCompleteイベントも追加提供します。
|
||||
|
||||
### APIパターン
|
||||
|
||||
#### 1. 個別登録: `worker.on_*(handler)`
|
||||
|
||||
Timelineの`on_*`メソッドを直接露出。必要なイベントだけを個別に登録可能にする。
|
||||
|
||||
```rust
|
||||
// ブロックイベント(スコープ管理あり)
|
||||
worker.on_text_block(my_text_handler); // Handler<TextBlockKind>
|
||||
worker.on_tool_use_block(my_tool_handler); // Handler<ToolUseBlockKind>
|
||||
|
||||
// 単発イベント(スコープ = ())
|
||||
worker.on_usage(my_usage_handler); // Handler<UsageKind>
|
||||
worker.on_status(my_status_handler); // Handler<StatusKind>
|
||||
|
||||
// 累積イベント(Worker層で追加、スコープ = ())
|
||||
worker.on_text_complete(my_complete_handler); // Handler<TextCompleteKind>
|
||||
worker.on_tool_call_complete(my_tool_complete); // Handler<ToolCallCompleteKind>
|
||||
```
|
||||
|
||||
#### 2. 一括登録: `worker.subscribe(subscriber)`
|
||||
|
||||
`WorkerSubscriber`トレイトを実装し、全ハンドラをまとめて登録。
|
||||
|
||||
```rust
|
||||
/// 統合Subscriberトレイト
|
||||
pub trait WorkerSubscriber: Send {
|
||||
// スコープ型(ブロックイベント用)
|
||||
type TextBlockScope: Default + Send;
|
||||
type ToolUseBlockScope: Default + Send;
|
||||
|
||||
// === ブロックイベント(スコープ管理あり)===
|
||||
fn on_text_block(
|
||||
&mut self,
|
||||
_scope: &mut Self::TextBlockScope,
|
||||
_event: &TextBlockEvent,
|
||||
) {}
|
||||
|
||||
fn on_tool_use_block(
|
||||
&mut self,
|
||||
_scope: &mut Self::ToolUseBlockScope,
|
||||
_event: &ToolUseBlockEvent,
|
||||
) {}
|
||||
|
||||
// === 単発イベント ===
|
||||
fn on_usage(&mut self, _event: &UsageEvent) {}
|
||||
fn on_status(&mut self, _event: &StatusEvent) {}
|
||||
fn on_error(&mut self, _event: &ErrorEvent) {}
|
||||
|
||||
// === 累積イベント(Worker層で追加)===
|
||||
fn on_text_complete(&mut self, _text: &str) {}
|
||||
fn on_tool_call_complete(&mut self, _call: &ToolCall) {}
|
||||
|
||||
// === ターン制御 ===
|
||||
fn on_turn_start(&mut self, _turn: usize) {}
|
||||
fn on_turn_end(&mut self, _turn: usize) {}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用例: WorkerSubscriber
|
||||
|
||||
```rust
|
||||
struct MyUI {
|
||||
chat_view: ChatView,
|
||||
}
|
||||
|
||||
impl WorkerSubscriber for MyUI {
|
||||
type TextBlockScope = TextComponent;
|
||||
type ToolUseBlockScope = ToolComponent;
|
||||
|
||||
fn on_text_block(&mut self, comp: &mut TextComponent, event: &TextBlockEvent) {
|
||||
match event {
|
||||
TextBlockEvent::Start(_) => {
|
||||
// スコープ開始時にコンポーネント初期化(Defaultで自動生成)
|
||||
}
|
||||
TextBlockEvent::Delta(text) => {
|
||||
comp.append(text);
|
||||
self.chat_view.update(comp);
|
||||
}
|
||||
TextBlockEvent::Stop(_) => {
|
||||
comp.set_immutable();
|
||||
// スコープ終了後に自動破棄
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_text_complete(&mut self, text: &str) {
|
||||
// 累積済みテキストを履歴に保存
|
||||
self.chat_view.add_to_history(text);
|
||||
}
|
||||
|
||||
fn on_tool_use_block(&mut self, comp: &mut ToolComponent, event: &ToolUseBlockEvent) {
|
||||
match event {
|
||||
ToolUseBlockEvent::Start(start) => {
|
||||
comp.set_name(&start.name);
|
||||
self.chat_view.show_tool_indicator(comp);
|
||||
}
|
||||
ToolUseBlockEvent::InputJsonDelta(delta) => {
|
||||
comp.append_input(delta);
|
||||
}
|
||||
ToolUseBlockEvent::Stop(_) => {
|
||||
comp.finalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_tool_call_complete(&mut self, call: &ToolCall) {
|
||||
self.chat_view.update_tool_result(&call.name, &call.input);
|
||||
}
|
||||
}
|
||||
|
||||
// Worker に登録
|
||||
let mut worker = Worker::new(client);
|
||||
worker.subscribe(MyUI::new());
|
||||
|
||||
let result = worker.run(messages).await?;
|
||||
```
|
||||
|
||||
### 使用例: 個別登録
|
||||
|
||||
```rust
|
||||
// シンプルなクロージャベース(将来的な糖衣構文として検討)
|
||||
worker.on_text_complete(|text: &str| {
|
||||
println!("Complete: {}", text);
|
||||
});
|
||||
|
||||
// または Handler実装
|
||||
struct TextLogger;
|
||||
impl Handler<TextCompleteKind> for TextLogger {
|
||||
type Scope = ();
|
||||
fn on_event(&mut self, _: &mut (), text: &String) {
|
||||
println!("Complete: {}", text);
|
||||
}
|
||||
}
|
||||
worker.on_text_complete(TextLogger);
|
||||
```
|
||||
|
||||
### 累積イベント用Kind定義
|
||||
|
||||
```rust
|
||||
/// テキスト完了イベント用Kind
|
||||
pub struct TextCompleteKind;
|
||||
impl Kind for TextCompleteKind {
|
||||
type Event = String; // 累積済みテキスト
|
||||
}
|
||||
|
||||
/// ツール呼び出し完了イベント用Kind
|
||||
pub struct ToolCallCompleteKind;
|
||||
impl Kind for ToolCallCompleteKind {
|
||||
type Event = ToolCall; // 完全なToolCall
|
||||
}
|
||||
```
|
||||
|
||||
### 内部実装
|
||||
|
||||
WorkerはSubscriberを内部で分解し、各Kindに対応するHandlerとしてTimelineに登録します。
|
||||
累積イベント(TextComplete等)はWorker層で処理し、ブロック終了時に累積結果を渡します。
|
||||
|
||||
```rust
|
||||
impl<C: LlmClient> Worker<C> {
|
||||
pub fn subscribe<S: WorkerSubscriber + 'static>(&mut self, subscriber: S) {
|
||||
let subscriber = Arc::new(Mutex::new(subscriber));
|
||||
|
||||
// TextBlock用ハンドラを登録
|
||||
self.timeline.on_text_block(TextBlockAdapter {
|
||||
subscriber: subscriber.clone(),
|
||||
});
|
||||
|
||||
// 累積イベント用の内部ハンドラも登録
|
||||
// (TextBlockCollectorのStop時にon_text_completeを呼ぶ)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 設計上のポイント
|
||||
|
||||
1. **Handlerの再利用**: 既存のHandler traitをそのまま活用
|
||||
2. **スコープ管理の維持**: ブロックイベントはStart→Delta→Endのライフサイクルを保持
|
||||
3. **選択的購読**: on_*で必要なイベントだけ、またはSubscriberで一括
|
||||
4. **累積イベントの追加**: Worker層でComplete系イベントを追加提供
|
||||
5. **後方互換性**: 従来の`run()`も引き続き使用可能
|
||||
|
||||
|
||||
Reference in New Issue
Block a user