feat: Cleaning up examples
This commit is contained in:
parent
1e126c1698
commit
45c8457b71
|
|
@ -1,176 +0,0 @@
|
||||||
//! LLMクライアント + Timeline統合サンプル
|
|
||||||
//!
|
|
||||||
//! Anthropic Claude APIにリクエストを送信し、Timelineでイベントを処理するサンプル
|
|
||||||
//!
|
|
||||||
//! ## 使用方法
|
|
||||||
//!
|
|
||||||
//! ```bash
|
|
||||||
//! # .envファイルにAPIキーを設定
|
|
||||||
//! echo "ANTHROPIC_API_KEY=your-api-key" > .env
|
|
||||||
//!
|
|
||||||
//! # 実行
|
|
||||||
//! cargo run --example llm_client_anthropic
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use futures::StreamExt;
|
|
||||||
use worker::{
|
|
||||||
Handler, TextBlockEvent, TextBlockKind, Timeline, ToolUseBlockEvent, ToolUseBlockKind,
|
|
||||||
UsageEvent, UsageKind,
|
|
||||||
llm_client::{LlmClient, Request, providers::anthropic::AnthropicClient},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// テキスト出力をリアルタイムで表示するハンドラー
|
|
||||||
struct PrintHandler;
|
|
||||||
|
|
||||||
impl Handler<TextBlockKind> for PrintHandler {
|
|
||||||
type Scope = ();
|
|
||||||
|
|
||||||
fn on_event(&mut self, _scope: &mut (), event: &TextBlockEvent) {
|
|
||||||
match event {
|
|
||||||
TextBlockEvent::Start(_) => {
|
|
||||||
print!("\n🤖 Assistant: ");
|
|
||||||
}
|
|
||||||
TextBlockEvent::Delta(text) => {
|
|
||||||
print!("{}", text);
|
|
||||||
// 即時出力をフラッシュ
|
|
||||||
use std::io::Write;
|
|
||||||
std::io::stdout().flush().ok();
|
|
||||||
}
|
|
||||||
TextBlockEvent::Stop(_) => {
|
|
||||||
println!("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// テキストを蓄積するハンドラー
|
|
||||||
struct TextCollector {
|
|
||||||
texts: Arc<Mutex<Vec<String>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler<TextBlockKind> for TextCollector {
|
|
||||||
type Scope = String;
|
|
||||||
|
|
||||||
fn on_event(&mut self, buffer: &mut String, event: &TextBlockEvent) {
|
|
||||||
match event {
|
|
||||||
TextBlockEvent::Start(_) => {}
|
|
||||||
TextBlockEvent::Delta(text) => {
|
|
||||||
buffer.push_str(text);
|
|
||||||
}
|
|
||||||
TextBlockEvent::Stop(_) => {
|
|
||||||
let text = std::mem::take(buffer);
|
|
||||||
self.texts.lock().unwrap().push(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ツール使用を検出するハンドラー
|
|
||||||
struct ToolUseDetector;
|
|
||||||
|
|
||||||
impl Handler<ToolUseBlockKind> for ToolUseDetector {
|
|
||||||
type Scope = String; // JSON accumulator
|
|
||||||
|
|
||||||
fn on_event(&mut self, json_buffer: &mut String, event: &ToolUseBlockEvent) {
|
|
||||||
match event {
|
|
||||||
ToolUseBlockEvent::Start(start) => {
|
|
||||||
println!("\n🔧 Tool Call: {} (id: {})", start.name, start.id);
|
|
||||||
}
|
|
||||||
ToolUseBlockEvent::InputJsonDelta(json) => {
|
|
||||||
json_buffer.push_str(json);
|
|
||||||
}
|
|
||||||
ToolUseBlockEvent::Stop(stop) => {
|
|
||||||
println!(" Arguments: {}", json_buffer);
|
|
||||||
println!(" Tool {} completed\n", stop.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 使用量を追跡するハンドラー
|
|
||||||
struct UsageTracker {
|
|
||||||
total_input: Arc<Mutex<u64>>,
|
|
||||||
total_output: Arc<Mutex<u64>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler<UsageKind> for UsageTracker {
|
|
||||||
type Scope = ();
|
|
||||||
|
|
||||||
fn on_event(&mut self, _scope: &mut (), event: &UsageEvent) {
|
|
||||||
if let Some(input) = event.input_tokens {
|
|
||||||
*self.total_input.lock().unwrap() += input;
|
|
||||||
}
|
|
||||||
if let Some(output) = event.output_tokens {
|
|
||||||
*self.total_output.lock().unwrap() += output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
// APIキーを環境変数から取得
|
|
||||||
let api_key = std::env::var("ANTHROPIC_API_KEY")
|
|
||||||
.expect("ANTHROPIC_API_KEY environment variable must be set");
|
|
||||||
|
|
||||||
println!("=== LLM Client + Timeline Integration Example ===\n");
|
|
||||||
|
|
||||||
// クライアントを作成
|
|
||||||
let client = AnthropicClient::new(api_key, "claude-sonnet-4-20250514");
|
|
||||||
|
|
||||||
// 共有状態
|
|
||||||
let collected_texts = Arc::new(Mutex::new(Vec::new()));
|
|
||||||
let total_input = Arc::new(Mutex::new(0u64));
|
|
||||||
let total_output = Arc::new(Mutex::new(0u64));
|
|
||||||
|
|
||||||
// タイムラインを構築
|
|
||||||
let mut timeline = Timeline::new();
|
|
||||||
timeline
|
|
||||||
.on_text_block(PrintHandler)
|
|
||||||
.on_text_block(TextCollector {
|
|
||||||
texts: collected_texts.clone(),
|
|
||||||
})
|
|
||||||
.on_tool_use_block(ToolUseDetector)
|
|
||||||
.on_usage(UsageTracker {
|
|
||||||
total_input: total_input.clone(),
|
|
||||||
total_output: total_output.clone(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// リクエストを作成
|
|
||||||
let request = Request::new()
|
|
||||||
.system("You are a helpful assistant. Be concise.")
|
|
||||||
.user("What is the capital of Japan? Answer in one sentence.")
|
|
||||||
.max_tokens(100);
|
|
||||||
|
|
||||||
println!("📤 Sending request...\n");
|
|
||||||
|
|
||||||
// ストリーミングリクエストを送信
|
|
||||||
let mut stream = client.stream(request).await?;
|
|
||||||
|
|
||||||
// イベントを処理
|
|
||||||
while let Some(result) = stream.next().await {
|
|
||||||
match result {
|
|
||||||
Ok(event) => {
|
|
||||||
timeline.dispatch(&event);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error: {}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 結果を表示
|
|
||||||
println!("=== Summary ===");
|
|
||||||
println!(
|
|
||||||
"📊 Token Usage: {} input, {} output",
|
|
||||||
total_input.lock().unwrap(),
|
|
||||||
total_output.lock().unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let texts = collected_texts.lock().unwrap();
|
|
||||||
println!("📝 Collected {} text block(s)", texts.len());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
||||||
//! LLMクライアント + Timeline統合サンプル (Gemini)
|
|
||||||
//!
|
|
||||||
//! Google Gemini APIにリクエストを送信し、Timelineでイベントを処理するサンプル
|
|
||||||
//!
|
|
||||||
//! ## 使用方法
|
|
||||||
//!
|
|
||||||
//! ```bash
|
|
||||||
//! # .envファイルにAPIキーを設定
|
|
||||||
//! echo "GEMINI_API_KEY=your-api-key" > .env
|
|
||||||
//!
|
|
||||||
//! # 実行
|
|
||||||
//! cargo run --example llm_client_gemini
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use futures::StreamExt;
|
|
||||||
use worker::{
|
|
||||||
Handler, TextBlockEvent, TextBlockKind, Timeline, ToolUseBlockEvent, ToolUseBlockKind,
|
|
||||||
UsageEvent, UsageKind,
|
|
||||||
llm_client::{LlmClient, Request, providers::gemini::GeminiClient},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// テキスト出力をリアルタイムで表示するハンドラー
|
|
||||||
struct PrintHandler;
|
|
||||||
|
|
||||||
impl Handler<TextBlockKind> for PrintHandler {
|
|
||||||
type Scope = ();
|
|
||||||
|
|
||||||
fn on_event(&mut self, _scope: &mut (), event: &TextBlockEvent) {
|
|
||||||
match event {
|
|
||||||
TextBlockEvent::Start(_) => {
|
|
||||||
print!("\n🤖 Assistant: ");
|
|
||||||
}
|
|
||||||
TextBlockEvent::Delta(text) => {
|
|
||||||
print!("{}", text);
|
|
||||||
// 即時出力をフラッシュ
|
|
||||||
use std::io::Write;
|
|
||||||
std::io::stdout().flush().ok();
|
|
||||||
}
|
|
||||||
TextBlockEvent::Stop(_) => {
|
|
||||||
println!("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// テキストを蓄積するハンドラー
|
|
||||||
struct TextCollector {
|
|
||||||
texts: Arc<Mutex<Vec<String>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler<TextBlockKind> for TextCollector {
|
|
||||||
type Scope = String;
|
|
||||||
|
|
||||||
fn on_event(&mut self, buffer: &mut String, event: &TextBlockEvent) {
|
|
||||||
match event {
|
|
||||||
TextBlockEvent::Start(_) => {}
|
|
||||||
TextBlockEvent::Delta(text) => {
|
|
||||||
buffer.push_str(text);
|
|
||||||
}
|
|
||||||
TextBlockEvent::Stop(_) => {
|
|
||||||
let text = std::mem::take(buffer);
|
|
||||||
self.texts.lock().unwrap().push(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ツール使用を検出するハンドラー
|
|
||||||
struct ToolUseDetector;
|
|
||||||
|
|
||||||
impl Handler<ToolUseBlockKind> for ToolUseDetector {
|
|
||||||
type Scope = String; // JSON accumulator
|
|
||||||
|
|
||||||
fn on_event(&mut self, json_buffer: &mut String, event: &ToolUseBlockEvent) {
|
|
||||||
match event {
|
|
||||||
ToolUseBlockEvent::Start(start) => {
|
|
||||||
println!("\n🔧 Tool Call: {} (id: {})", start.name, start.id);
|
|
||||||
}
|
|
||||||
ToolUseBlockEvent::InputJsonDelta(json) => {
|
|
||||||
json_buffer.push_str(json);
|
|
||||||
}
|
|
||||||
ToolUseBlockEvent::Stop(stop) => {
|
|
||||||
println!(" Arguments: {}", json_buffer);
|
|
||||||
println!(" Tool {} completed\n", stop.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 使用量を追跡するハンドラー
|
|
||||||
struct UsageTracker {
|
|
||||||
total_input: Arc<Mutex<u64>>,
|
|
||||||
total_output: Arc<Mutex<u64>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler<UsageKind> for UsageTracker {
|
|
||||||
type Scope = ();
|
|
||||||
|
|
||||||
fn on_event(&mut self, _scope: &mut (), event: &UsageEvent) {
|
|
||||||
if let Some(input) = event.input_tokens {
|
|
||||||
*self.total_input.lock().unwrap() += input;
|
|
||||||
}
|
|
||||||
if let Some(output) = event.output_tokens {
|
|
||||||
*self.total_output.lock().unwrap() += output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
// APIキーを環境変数から取得
|
|
||||||
let api_key =
|
|
||||||
std::env::var("GEMINI_API_KEY").expect("GEMINI_API_KEY environment variable must be set");
|
|
||||||
|
|
||||||
println!("=== Gemini LLM Client + Timeline Integration Example ===\n");
|
|
||||||
|
|
||||||
// クライアントを作成
|
|
||||||
let client = GeminiClient::new(api_key, "gemini-2.0-flash");
|
|
||||||
|
|
||||||
// 共有状態
|
|
||||||
let collected_texts = Arc::new(Mutex::new(Vec::new()));
|
|
||||||
let total_input = Arc::new(Mutex::new(0u64));
|
|
||||||
let total_output = Arc::new(Mutex::new(0u64));
|
|
||||||
|
|
||||||
// タイムラインを構築
|
|
||||||
let mut timeline = Timeline::new();
|
|
||||||
timeline
|
|
||||||
.on_text_block(PrintHandler)
|
|
||||||
.on_text_block(TextCollector {
|
|
||||||
texts: collected_texts.clone(),
|
|
||||||
})
|
|
||||||
.on_tool_use_block(ToolUseDetector)
|
|
||||||
.on_usage(UsageTracker {
|
|
||||||
total_input: total_input.clone(),
|
|
||||||
total_output: total_output.clone(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// リクエストを作成
|
|
||||||
let request = Request::new()
|
|
||||||
.system("You are a helpful assistant. Be concise.")
|
|
||||||
.user("What is the capital of Japan? Answer in one sentence.")
|
|
||||||
.max_tokens(100);
|
|
||||||
|
|
||||||
println!("📤 Sending request...\n");
|
|
||||||
|
|
||||||
// ストリーミングリクエストを送信
|
|
||||||
let mut stream = client.stream(request).await?;
|
|
||||||
|
|
||||||
// イベントを処理
|
|
||||||
while let Some(result) = stream.next().await {
|
|
||||||
match result {
|
|
||||||
Ok(event) => {
|
|
||||||
timeline.dispatch(&event);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error: {}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 結果を表示
|
|
||||||
println!("=== Summary ===");
|
|
||||||
println!(
|
|
||||||
"📊 Token Usage: {} input, {} output",
|
|
||||||
total_input.lock().unwrap(),
|
|
||||||
total_output.lock().unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let texts = collected_texts.lock().unwrap();
|
|
||||||
println!("📝 Collected {} text block(s)", texts.len());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,134 +0,0 @@
|
||||||
//! Timeline使用例
|
|
||||||
//!
|
|
||||||
//! 設計ドキュメントに基づいたTimelineの使用パターンを示すサンプル
|
|
||||||
|
|
||||||
use worker::{
|
|
||||||
Event, Handler, TextBlockEvent, TextBlockKind, Timeline, ToolUseBlockEvent, ToolUseBlockKind,
|
|
||||||
UsageEvent, UsageKind,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// シミュレートされたイベントストリーム
|
|
||||||
let events = simulate_llm_response();
|
|
||||||
|
|
||||||
// Timelineを作成し、ハンドラーを登録
|
|
||||||
let mut timeline = Timeline::new();
|
|
||||||
|
|
||||||
// Usage収集ハンドラー
|
|
||||||
timeline.on_usage(UsageAccumulator::new());
|
|
||||||
|
|
||||||
// テキスト収集ハンドラー
|
|
||||||
timeline.on_text_block(TextCollector::new());
|
|
||||||
|
|
||||||
// ツール呼び出し収集ハンドラー
|
|
||||||
timeline.on_tool_use_block(ToolCallCollector::new());
|
|
||||||
|
|
||||||
// イベントをディスパッチ
|
|
||||||
for event in &events {
|
|
||||||
timeline.dispatch(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Timeline example completed!");
|
|
||||||
println!("Events processed: {}", events.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// LLMレスポンスをシミュレート
|
|
||||||
fn simulate_llm_response() -> Vec<Event> {
|
|
||||||
vec![
|
|
||||||
// テキストブロック
|
|
||||||
Event::text_block_start(0),
|
|
||||||
Event::text_delta(0, "Hello, "),
|
|
||||||
Event::text_delta(0, "I can help you with that."),
|
|
||||||
Event::text_block_stop(0, None),
|
|
||||||
// 使用量
|
|
||||||
Event::usage(100, 50),
|
|
||||||
// ツール呼び出し
|
|
||||||
Event::tool_use_start(1, "call_abc123", "get_weather"),
|
|
||||||
Event::tool_input_delta(1, r#"{"city":"#),
|
|
||||||
Event::tool_input_delta(1, r#""Tokyo"}"#),
|
|
||||||
Event::tool_use_stop(1),
|
|
||||||
// 最終的な使用量
|
|
||||||
Event::usage(100, 75),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// Example Handlers (defined in example, not in library)
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
/// 使用量を累積するハンドラー
|
|
||||||
struct UsageAccumulator {
|
|
||||||
total_tokens: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UsageAccumulator {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self { total_tokens: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler<UsageKind> for UsageAccumulator {
|
|
||||||
type Scope = ();
|
|
||||||
fn on_event(&mut self, _scope: &mut (), usage: &UsageEvent) {
|
|
||||||
self.total_tokens += usage.total_tokens.unwrap_or(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// テキストを収集するハンドラー
|
|
||||||
struct TextCollector {
|
|
||||||
results: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextCollector {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
results: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler<TextBlockKind> for TextCollector {
|
|
||||||
type Scope = String;
|
|
||||||
fn on_event(&mut self, buffer: &mut String, event: &TextBlockEvent) {
|
|
||||||
match event {
|
|
||||||
TextBlockEvent::Start(_) => {}
|
|
||||||
TextBlockEvent::Delta(s) => buffer.push_str(s),
|
|
||||||
TextBlockEvent::Stop(_) => {
|
|
||||||
self.results.push(std::mem::take(buffer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ツール呼び出しを収集するハンドラー
|
|
||||||
struct ToolCallCollector {
|
|
||||||
calls: Vec<(String, String)>, // (name, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToolCallCollector {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self { calls: Vec::new() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct ToolCallScope {
|
|
||||||
name: String,
|
|
||||||
args: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler<ToolUseBlockKind> for ToolCallCollector {
|
|
||||||
type Scope = ToolCallScope;
|
|
||||||
fn on_event(&mut self, scope: &mut ToolCallScope, event: &ToolUseBlockEvent) {
|
|
||||||
match event {
|
|
||||||
ToolUseBlockEvent::Start(s) => scope.name = s.name.clone(),
|
|
||||||
ToolUseBlockEvent::InputJsonDelta(json) => scope.args.push_str(json),
|
|
||||||
ToolUseBlockEvent::Stop(_) => {
|
|
||||||
self.calls.push((
|
|
||||||
std::mem::take(&mut scope.name),
|
|
||||||
std::mem::take(&mut scope.args),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -30,15 +30,18 @@
|
||||||
//! cargo run --example worker_cli -- --help
|
//! cargo run --example worker_cli -- --help
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
use clap::{Parser, ValueEnum};
|
use clap::{Parser, ValueEnum};
|
||||||
use worker::{
|
use worker::{
|
||||||
Handler, TextBlockEvent, TextBlockKind, ToolUseBlockEvent, ToolUseBlockKind, Worker,
|
ControlFlow, Handler, HookError, TextBlockEvent, TextBlockKind, ToolResult, ToolUseBlockEvent,
|
||||||
|
ToolUseBlockKind, Worker, WorkerHook,
|
||||||
llm_client::{
|
llm_client::{
|
||||||
LlmClient,
|
LlmClient,
|
||||||
providers::{
|
providers::{
|
||||||
|
|
@ -224,26 +227,83 @@ impl Handler<TextBlockKind> for StreamingPrinter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ツール呼び出しを表示するハンドラー
|
/// ツール呼び出しを表示するハンドラー
|
||||||
struct ToolCallPrinter;
|
struct ToolCallPrinter {
|
||||||
|
call_names: Arc<Mutex<HashMap<String, String>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToolCallPrinter {
|
||||||
|
fn new(call_names: Arc<Mutex<HashMap<String, String>>>) -> Self {
|
||||||
|
Self { call_names }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ToolCallPrinterScope {
|
||||||
|
input_json: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl Handler<ToolUseBlockKind> for ToolCallPrinter {
|
impl Handler<ToolUseBlockKind> for ToolCallPrinter {
|
||||||
type Scope = String;
|
type Scope = ToolCallPrinterScope;
|
||||||
|
|
||||||
fn on_event(&mut self, json_buffer: &mut String, event: &ToolUseBlockEvent) {
|
fn on_event(&mut self, scope: &mut Self::Scope, event: &ToolUseBlockEvent) {
|
||||||
match event {
|
match event {
|
||||||
ToolUseBlockEvent::Start(start) => {
|
ToolUseBlockEvent::Start(start) => {
|
||||||
|
scope.input_json.clear();
|
||||||
|
self.call_names
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(start.id.clone(), start.name.clone());
|
||||||
println!("\n🔧 Calling tool: {}", start.name);
|
println!("\n🔧 Calling tool: {}", start.name);
|
||||||
}
|
}
|
||||||
ToolUseBlockEvent::InputJsonDelta(json) => {
|
ToolUseBlockEvent::InputJsonDelta(json) => {
|
||||||
json_buffer.push_str(json);
|
scope.input_json.push_str(json);
|
||||||
}
|
}
|
||||||
ToolUseBlockEvent::Stop(_) => {
|
ToolUseBlockEvent::Stop(_) => {
|
||||||
println!(" Args: {}", json_buffer);
|
if scope.input_json.is_empty() {
|
||||||
|
println!(" Args: {{}}");
|
||||||
|
} else {
|
||||||
|
println!(" Args: {}", scope.input_json);
|
||||||
|
}
|
||||||
|
scope.input_json.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ツール実行結果を表示するHook
|
||||||
|
struct ToolResultPrinterHook {
|
||||||
|
call_names: Arc<Mutex<HashMap<String, String>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToolResultPrinterHook {
|
||||||
|
fn new(call_names: Arc<Mutex<HashMap<String, String>>>) -> Self {
|
||||||
|
Self { call_names }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl WorkerHook for ToolResultPrinterHook {
|
||||||
|
async fn after_tool_call(
|
||||||
|
&self,
|
||||||
|
tool_result: &mut ToolResult,
|
||||||
|
) -> Result<ControlFlow, HookError> {
|
||||||
|
let name = self
|
||||||
|
.call_names
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.remove(&tool_result.tool_use_id)
|
||||||
|
.unwrap_or_else(|| tool_result.tool_use_id.clone());
|
||||||
|
|
||||||
|
if tool_result.is_error {
|
||||||
|
println!(" Result ({}): ❌ {}", name, tool_result.content);
|
||||||
|
} else {
|
||||||
|
println!(" Result ({}): ✅ {}", name, tool_result.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ControlFlow::Continue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// クライアント作成
|
// クライアント作成
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
@ -371,6 +431,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// Worker作成
|
// Worker作成
|
||||||
let mut worker = Worker::new(client);
|
let mut worker = Worker::new(client);
|
||||||
|
|
||||||
|
let tool_call_names = Arc::new(Mutex::new(HashMap::new()));
|
||||||
|
|
||||||
// システムプロンプトを設定
|
// システムプロンプトを設定
|
||||||
if let Some(ref system_prompt) = args.system {
|
if let Some(ref system_prompt) = args.system {
|
||||||
worker.set_system_prompt(system_prompt);
|
worker.set_system_prompt(system_prompt);
|
||||||
|
|
@ -387,7 +449,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
worker
|
worker
|
||||||
.timeline_mut()
|
.timeline_mut()
|
||||||
.on_text_block(StreamingPrinter::new())
|
.on_text_block(StreamingPrinter::new())
|
||||||
.on_tool_use_block(ToolCallPrinter);
|
.on_tool_use_block(ToolCallPrinter::new(tool_call_names.clone()));
|
||||||
|
|
||||||
|
worker.add_hook(ToolResultPrinterHook::new(tool_call_names));
|
||||||
|
|
||||||
// 会話履歴
|
// 会話履歴
|
||||||
let mut history: Vec<Message> = Vec::new();
|
let mut history: Vec<Message> = Vec::new();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user