feat: Implement HookEventKind

This commit is contained in:
2026-01-09 19:18:20 +09:00
parent 33f1c218f2
commit 5691b09fc8
15 changed files with 916 additions and 416 deletions
+71
View File
@@ -0,0 +1,71 @@
//! Worker のキャンセル機能のデモンストレーション
//!
//! ストリーミング受信中に別スレッドからキャンセルする例
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
use llm_worker::{Worker, WorkerResult};
use llm_worker::llm_client::providers::anthropic::AnthropicClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// .envファイルを読み込む
dotenv::dotenv().ok();
// ロギング初期化
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
let api_key = std::env::var("ANTHROPIC_API_KEY")
.expect("ANTHROPIC_API_KEY environment variable not set");
let client = AnthropicClient::new(&api_key, "claude-sonnet-4-20250514");
let worker = Arc::new(Mutex::new(Worker::new(client)));
println!("🚀 Starting Worker...");
println!("💡 Will cancel after 2 seconds\n");
// キャンセルトークンを先に取得(ロックを保持しない)
let cancel_token = {
let w = worker.lock().await;
w.cancellation_token().clone()
};
// タスク1: Workerを実行
let worker_clone = worker.clone();
let task = tokio::spawn(async move {
let mut w = worker_clone.lock().await;
println!("📡 Sending request to LLM...");
match w.run("Tell me a very long story about a brave knight. Make it as detailed as possible with many paragraphs.").await {
Ok(WorkerResult::Finished(_)) => {
println!("✅ Task completed normally");
}
Ok(WorkerResult::Paused(_)) => {
println!("⏸️ Task paused");
}
Err(e) => {
println!("❌ Task error: {}", e);
}
}
});
// タスク2: 2秒後にキャンセル
tokio::spawn(async move {
tokio::time::sleep(Duration::from_secs(2)).await;
println!("\n🛑 Cancelling worker...");
cancel_token.cancel();
});
// タスク完了を待つ
task.await?;
println!("\n✨ Demo complete!");
Ok(())
}
+6 -6
View File
@@ -41,7 +41,7 @@ use tracing_subscriber::EnvFilter;
use clap::{Parser, ValueEnum};
use llm_worker::{
Worker,
hook::{ControlFlow, HookError, ToolResult, WorkerHook},
hook::{AfterToolCall, AfterToolCallResult, Hook, HookError, ToolResult},
llm_client::{
LlmClient,
providers::{
@@ -282,11 +282,11 @@ impl ToolResultPrinterHook {
}
#[async_trait]
impl WorkerHook for ToolResultPrinterHook {
async fn after_tool_call(
impl Hook<AfterToolCall> for ToolResultPrinterHook {
async fn call(
&self,
tool_result: &mut ToolResult,
) -> Result<ControlFlow, HookError> {
) -> Result<AfterToolCallResult, HookError> {
let name = self
.call_names
.lock()
@@ -300,7 +300,7 @@ impl WorkerHook for ToolResultPrinterHook {
println!(" Result ({}): ✅ {}", name, tool_result.content);
}
Ok(ControlFlow::Continue)
Ok(AfterToolCallResult::Continue)
}
}
@@ -451,7 +451,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.on_text_block(StreamingPrinter::new())
.on_tool_use_block(ToolCallPrinter::new(tool_call_names.clone()));
worker.add_hook(ToolResultPrinterHook::new(tool_call_names));
worker.add_after_tool_call_hook(ToolResultPrinterHook::new(tool_call_names));
// ワンショットモード
if let Some(prompt) = args.prompt {