From 6c43ac99694f1dcb87729dd866e50ec447f0bcee Mon Sep 17 00:00:00 2001 From: Hare Date: Fri, 16 Jan 2026 16:58:03 +0900 Subject: [PATCH] docs: Translate ja -> en --- AGENTS.md | 9 +- llm-worker-macros/src/lib.rs | 74 ++-- .../examples/record_test_fixtures/main.rs | 16 +- .../examples/record_test_fixtures/recorder.rs | 14 +- .../record_test_fixtures/scenarios.rs | 20 +- llm-worker/examples/worker_cancel_demo.rs | 16 +- llm-worker/examples/worker_cli.rs | 116 +++--- llm-worker/src/event.rs | 144 +++---- llm-worker/src/handler.rs | 56 +-- llm-worker/src/hook.rs | 62 +-- llm-worker/src/lib.rs | 26 +- llm-worker/src/message.rs | 54 +-- llm-worker/src/state.rs | 42 +-- llm-worker/src/subscriber.rs | 110 +++--- llm-worker/src/tool.rs | 66 ++-- llm-worker/src/worker.rs | 356 +++++++++--------- llm-worker/tests/anthropic_fixtures.rs | 2 +- llm-worker/tests/gemini_fixtures.rs | 2 +- llm-worker/tests/ollama_fixtures.rs | 2 +- llm-worker/tests/openai_fixtures.rs | 2 +- llm-worker/tests/parallel_execution_test.rs | 42 +-- llm-worker/tests/subscriber_test.rs | 52 +-- llm-worker/tests/tool_macro_test.rs | 40 +- llm-worker/tests/validation_test.rs | 16 +- llm-worker/tests/worker_fixtures.rs | 72 ++-- llm-worker/tests/worker_state_test.rs | 108 +++--- 26 files changed, 760 insertions(+), 759 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index a13e47b..9c39c50 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,7 @@ -# llm-worker-rs 開発instruction +# llm-worker-rs Development Instructions -## パッケージ管理ルール +## Package Management Rules -- クレートに依存関係を追加・更新する際は必ず - `cargo`コマンドを使い、`Cargo.toml`を直接手で書き換えず、必ずコマンド経由で管理すること。 +- When adding or updating crate dependencies, always use the `cargo` command. Do + not manually edit `Cargo.toml` directly; always manage dependencies via + commands. diff --git a/llm-worker-macros/src/lib.rs b/llm-worker-macros/src/lib.rs index b1a2e45..d1007db 100644 --- a/llm-worker-macros/src/lib.rs +++ b/llm-worker-macros/src/lib.rs @@ -1,7 +1,7 @@ -//! llm-worker-macros - Tool生成用手続きマクロ +//! llm-worker-macros - Procedural macros for Tool generation //! -//! `#[tool_registry]` と `#[tool]` マクロを提供し、 -//! ユーザー定義のメソッドから `Tool` トレイト実装を自動生成する。 +//! Provides `#[tool_registry]` and `#[tool]` macros to +//! automatically generate `Tool` trait implementations from user-defined methods. use proc_macro::TokenStream; use quote::{format_ident, quote}; @@ -9,22 +9,22 @@ use syn::{ Attribute, FnArg, ImplItem, ItemImpl, Lit, Meta, Pat, ReturnType, Type, parse_macro_input, }; -/// `impl` ブロックに付与し、内部の `#[tool]` 属性がついたメソッドからツールを生成するマクロ。 +/// Macro applied to an `impl` block that generates tools from methods marked with `#[tool]`. /// /// # Example /// ```ignore /// #[tool_registry] /// impl MyApp { -/// /// ユーザー情報を取得する -/// /// 指定されたIDのユーザーをDBから検索します。 +/// /// Get user information +/// /// Retrieves a user from the database by their ID. /// #[tool] /// async fn get_user(&self, user_id: String) -> Result { ... } /// } /// ``` /// -/// これにより以下が生成されます: -/// - `GetUserArgs` 構造体(引数用) -/// - `Tool_get_user` 構造体(Toolラッパー) +/// This generates: +/// - `GetUserArgs` struct (for arguments) +/// - `Tool_get_user` struct (Tool wrapper) /// - `impl Tool for Tool_get_user` /// - `impl MyApp { fn get_user_tool(&self) -> Tool_get_user }` #[proc_macro_attribute] @@ -36,14 +36,14 @@ pub fn tool_registry(_attr: TokenStream, item: TokenStream) -> TokenStream { for item in &mut impl_block.items { if let ImplItem::Fn(method) = item { - // #[tool] 属性を探す + // Look for #[tool] attribute let mut is_tool = false; - // 属性を走査してtoolがあるか確認し、削除する + // Iterate through attributes to check for tool and remove it method.attrs.retain(|attr| { if attr.path().is_ident("tool") { is_tool = true; - false // 属性を削除 + false // Remove the attribute } else { true } @@ -65,7 +65,7 @@ pub fn tool_registry(_attr: TokenStream, item: TokenStream) -> TokenStream { TokenStream::from(expanded) } -/// ドキュメントコメントから説明文を抽出 +/// Extract description from doc comments fn extract_doc_comment(attrs: &[Attribute]) -> String { let mut lines = Vec::new(); @@ -75,7 +75,7 @@ fn extract_doc_comment(attrs: &[Attribute]) -> String { if let syn::Expr::Lit(expr_lit) = &meta.value { if let Lit::Str(lit_str) = &expr_lit.lit { let line = lit_str.value(); - // 先頭の空白を1つだけ除去(/// の後のスペース) + // Remove only the leading space (after ///) let trimmed = line.strip_prefix(' ').unwrap_or(&line); lines.push(trimmed.to_string()); } @@ -87,7 +87,7 @@ fn extract_doc_comment(attrs: &[Attribute]) -> String { lines.join("\n") } -/// #[description = "..."] 属性から説明を抽出 +/// Extract description from #[description = "..."] attribute fn extract_description_attr(attrs: &[syn::Attribute]) -> Option { for attr in attrs { if attr.path().is_ident("description") { @@ -103,19 +103,19 @@ fn extract_description_attr(attrs: &[syn::Attribute]) -> Option { None } -/// メソッドからTool実装を生成 +/// Generate Tool implementation from a method fn generate_tool_impl(self_ty: &Type, method: &syn::ImplItemFn) -> proc_macro2::TokenStream { let sig = &method.sig; let method_name = &sig.ident; let tool_name = method_name.to_string(); - // 構造体名を生成(PascalCase変換) + // Generate struct names (convert to PascalCase) let pascal_name = to_pascal_case(&method_name.to_string()); let tool_struct_name = format_ident!("Tool{}", pascal_name); let args_struct_name = format_ident!("{}Args", pascal_name); let definition_name = format_ident!("{}_definition", method_name); - // ドキュメントコメントから説明を取得 + // Get description from doc comments let description = extract_doc_comment(&method.attrs); let description = if description.is_empty() { format!("Tool: {}", tool_name) @@ -123,7 +123,7 @@ fn generate_tool_impl(self_ty: &Type, method: &syn::ImplItemFn) -> proc_macro2:: description }; - // 引数を解析(selfを除く) + // Parse arguments (excluding self) let args: Vec<_> = sig .inputs .iter() @@ -131,12 +131,12 @@ fn generate_tool_impl(self_ty: &Type, method: &syn::ImplItemFn) -> proc_macro2:: if let FnArg::Typed(pat_type) = arg { Some(pat_type) } else { - None // selfを除外 + None // Exclude self } }) .collect(); - // 引数構造体のフィールドを生成 + // Generate argument struct fields let arg_fields: Vec<_> = args .iter() .map(|pat_type| { @@ -144,14 +144,14 @@ fn generate_tool_impl(self_ty: &Type, method: &syn::ImplItemFn) -> proc_macro2:: let ty = &pat_type.ty; let desc = extract_description_attr(&pat_type.attrs); - // パターンから識別子を抽出 + // Extract identifier from pattern let field_name = if let Pat::Ident(pat_ident) = pat.as_ref() { &pat_ident.ident } else { panic!("Only simple identifiers are supported for tool arguments"); }; - // #[description] があればschemarsのdocに変換 + // Convert #[description] to schemars doc if present if let Some(desc_str) = desc { quote! { #[schemars(description = #desc_str)] @@ -165,7 +165,7 @@ fn generate_tool_impl(self_ty: &Type, method: &syn::ImplItemFn) -> proc_macro2:: }) .collect(); - // execute内で引数を展開するコード + // Code to expand arguments in execute let arg_names: Vec<_> = args .iter() .map(|pat_type| { @@ -178,17 +178,17 @@ fn generate_tool_impl(self_ty: &Type, method: &syn::ImplItemFn) -> proc_macro2:: }) .collect(); - // メソッドが非同期かどうか + // Check if method is async let is_async = sig.asyncness.is_some(); - // 戻り値の型を解析してResult判定 + // Parse return type and determine if Result let awaiter = if is_async { quote! { .await } } else { quote! {} }; - // 戻り値がResultかどうかを判定 + // Determine if return type is Result let result_handling = if is_result_type(&sig.output) { quote! { match result { @@ -202,7 +202,7 @@ fn generate_tool_impl(self_ty: &Type, method: &syn::ImplItemFn) -> proc_macro2:: } }; - // 引数がない場合は空のArgs構造体を作成 + // Create empty Args struct if no arguments let args_struct_def = if arg_fields.is_empty() { quote! { #[derive(serde::Deserialize, schemars::JsonSchema)] @@ -217,10 +217,10 @@ fn generate_tool_impl(self_ty: &Type, method: &syn::ImplItemFn) -> proc_macro2:: } }; - // 引数がない場合のexecute処理 + // Execute body handling for no arguments case let execute_body = if args.is_empty() { quote! { - // 引数なしでも空のJSONオブジェクトを許容 + // Allow empty JSON object even with no arguments let _: #args_struct_name = serde_json::from_str(input_json) .unwrap_or(#args_struct_name {}); @@ -253,7 +253,7 @@ fn generate_tool_impl(self_ty: &Type, method: &syn::ImplItemFn) -> proc_macro2:: } impl #self_ty { - /// ToolDefinition を取得(Worker への登録用) + /// Get ToolDefinition (for registering with Worker) pub fn #definition_name(&self) -> ::llm_worker::tool::ToolDefinition { let ctx = self.clone(); ::std::sync::Arc::new(move || { @@ -270,12 +270,12 @@ fn generate_tool_impl(self_ty: &Type, method: &syn::ImplItemFn) -> proc_macro2:: } } -/// 戻り値の型がResultかどうかを判定 +/// Determine if return type is Result fn is_result_type(return_type: &ReturnType) -> bool { match return_type { ReturnType::Default => false, ReturnType::Type(_, ty) => { - // Type::Pathの場合、最後のセグメントが"Result"かチェック + // For Type::Path, check if last segment is "Result" if let Type::Path(type_path) = ty.as_ref() { if let Some(segment) = type_path.path.segments.last() { return segment.ident == "Result"; @@ -286,7 +286,7 @@ fn is_result_type(return_type: &ReturnType) -> bool { } } -/// snake_case を PascalCase に変換 +/// Convert snake_case to PascalCase fn to_pascal_case(s: &str) -> String { s.split('_') .map(|part| { @@ -299,20 +299,20 @@ fn to_pascal_case(s: &str) -> String { .collect() } -/// マーカー属性。`tool_registry` によって処理されるため、ここでは何もしない。 +/// Marker attribute. Does nothing here as it's processed by `tool_registry`. #[proc_macro_attribute] pub fn tool(_attr: TokenStream, item: TokenStream) -> TokenStream { item } -/// 引数属性用のマーカー。パース時に`tool_registry`で解釈される。 +/// Marker for argument attributes. Interpreted by `tool_registry` during parsing. /// /// # Example /// ```ignore /// #[tool] /// async fn get_user( /// &self, -/// #[description = "取得したいユーザーのID"] user_id: String +/// #[description = "The ID of the user to retrieve"] user_id: String /// ) -> Result { ... } /// ``` #[proc_macro_attribute] diff --git a/llm-worker/examples/record_test_fixtures/main.rs b/llm-worker/examples/record_test_fixtures/main.rs index bf0c0d8..1578c63 100644 --- a/llm-worker/examples/record_test_fixtures/main.rs +++ b/llm-worker/examples/record_test_fixtures/main.rs @@ -1,18 +1,18 @@ -//! テストフィクスチャ記録ツール +//! Test fixture recording tool //! -//! 定義されたシナリオのAPIレスポンスを記録する。 +//! Records API responses for defined scenarios. //! -//! ## 使用方法 +//! ## Usage //! //! ```bash -//! # 利用可能なシナリオを表示 +//! # Show available scenarios //! cargo run --example record_test_fixtures //! -//! # 特定のシナリオを記録 +//! # Record specific scenario //! ANTHROPIC_API_KEY=your-key cargo run --example record_test_fixtures -- simple_text //! ANTHROPIC_API_KEY=your-key cargo run --example record_test_fixtures -- tool_call //! -//! # 全シナリオを記録 +//! # Record all scenarios //! ANTHROPIC_API_KEY=your-key cargo run --example record_test_fixtures -- --all //! ``` @@ -193,8 +193,8 @@ async fn main() -> Result<(), Box> { ClientType::Ollama => "ollama", }; - // シナリオのフィルタリングは main.rs のロジックで実行済み - // ここでは単純なループで実行 + // Scenario filtering is already done in main.rs logic + // Here we just execute in a simple loop for scenario in scenarios_to_run { match args.client { ClientType::Anthropic => { diff --git a/llm-worker/examples/record_test_fixtures/recorder.rs b/llm-worker/examples/record_test_fixtures/recorder.rs index 355f589..93a9054 100644 --- a/llm-worker/examples/record_test_fixtures/recorder.rs +++ b/llm-worker/examples/record_test_fixtures/recorder.rs @@ -1,6 +1,6 @@ -//! テストフィクスチャ記録機構 +//! Test fixture recording mechanism //! -//! イベントをJSONLフォーマットでファイルに保存する +//! Saves events to files in JSONL format use std::fs::{self, File}; use std::io::{BufWriter, Write}; @@ -10,7 +10,7 @@ use std::time::{Instant, SystemTime, UNIX_EPOCH}; use futures::StreamExt; use llm_worker::llm_client::{LlmClient, Request}; -/// 記録されたイベント +/// Recorded event #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct RecordedEvent { pub elapsed_ms: u64, @@ -18,7 +18,7 @@ pub struct RecordedEvent { pub data: String, } -/// セッションメタデータ +/// Session metadata #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct SessionMetadata { pub timestamp: u64, @@ -26,7 +26,7 @@ pub struct SessionMetadata { pub description: String, } -/// イベントシーケンスをファイルに保存 +/// Save event sequence to file pub fn save_fixture( path: impl AsRef, metadata: &SessionMetadata, @@ -43,7 +43,7 @@ pub fn save_fixture( Ok(()) } -/// リクエストを送信してイベントを記録 +/// Send request and record events pub async fn record_request( client: &C, request: Request, @@ -78,7 +78,7 @@ pub async fn record_request( } } - // 保存 + // Save let fixtures_dir = Path::new("worker/tests/fixtures").join(subdir); fs::create_dir_all(&fixtures_dir)?; diff --git a/llm-worker/examples/record_test_fixtures/scenarios.rs b/llm-worker/examples/record_test_fixtures/scenarios.rs index 0fe5c45..6fd80af 100644 --- a/llm-worker/examples/record_test_fixtures/scenarios.rs +++ b/llm-worker/examples/record_test_fixtures/scenarios.rs @@ -1,20 +1,20 @@ -//! テストフィクスチャ用リクエスト定義 +//! Test fixture request definitions //! -//! 各シナリオのリクエストと出力ファイル名を定義 +//! Defines requests and output file names for each scenario use llm_worker::llm_client::{Request, ToolDefinition}; -/// テストシナリオ +/// Test scenario pub struct TestScenario { - /// シナリオ名(説明) + /// Scenario name (description) pub name: &'static str, - /// 出力ファイル名(拡張子なし) + /// Output file name (without extension) pub output_name: &'static str, - /// リクエスト + /// Request pub request: Request, } -/// 全てのテストシナリオを取得 +/// Get all test scenarios pub fn scenarios() -> Vec { vec![ simple_text_scenario(), @@ -23,7 +23,7 @@ pub fn scenarios() -> Vec { ] } -/// シンプルなテキストレスポンス +/// Simple text response fn simple_text_scenario() -> TestScenario { TestScenario { name: "Simple text response", @@ -35,7 +35,7 @@ fn simple_text_scenario() -> TestScenario { } } -/// ツール呼び出しを含むレスポンス +/// Response with tool call fn tool_call_scenario() -> TestScenario { let get_weather_tool = ToolDefinition::new("get_weather") .description("Get the current weather for a city") @@ -61,7 +61,7 @@ fn tool_call_scenario() -> TestScenario { } } -/// 長文生成シナリオ +/// Long text generation scenario fn long_text_scenario() -> TestScenario { TestScenario { name: "Long text response", diff --git a/llm-worker/examples/worker_cancel_demo.rs b/llm-worker/examples/worker_cancel_demo.rs index 9d6e6d2..ba8e993 100644 --- a/llm-worker/examples/worker_cancel_demo.rs +++ b/llm-worker/examples/worker_cancel_demo.rs @@ -1,6 +1,6 @@ -//! Worker のキャンセル機能のデモンストレーション +//! Worker cancellation demo //! -//! ストリーミング受信中に別スレッドからキャンセルする例 +//! Example of cancelling from another thread during streaming use llm_worker::llm_client::providers::anthropic::AnthropicClient; use llm_worker::{Worker, WorkerResult}; @@ -10,10 +10,10 @@ use tokio::sync::Mutex; #[tokio::main] async fn main() -> Result<(), Box> { - // .envファイルを読み込む + // Load .env file dotenv::dotenv().ok(); - // ロギング初期化 + // Initialize logging tracing_subscriber::fmt() .with_env_filter( tracing_subscriber::EnvFilter::try_from_default_env() @@ -30,13 +30,13 @@ async fn main() -> Result<(), Box> { println!("🚀 Starting Worker..."); println!("💡 Will cancel after 2 seconds\n"); - // キャンセルSenderを先に取得(ロックを保持しない) + // Get cancel sender first (without holding lock) let cancel_tx = { let w = worker.lock().await; w.cancel_sender() }; - // タスク1: Workerを実行 + // Task 1: Run Worker let worker_clone = worker.clone(); let task = tokio::spawn(async move { let mut w = worker_clone.lock().await; @@ -55,14 +55,14 @@ async fn main() -> Result<(), Box> { } }); - // タスク2: 2秒後にキャンセル + // Task 2: Cancel after 2 seconds tokio::spawn(async move { tokio::time::sleep(Duration::from_secs(2)).await; println!("\n🛑 Cancelling worker..."); let _ = cancel_tx.send(()).await; }); - // タスク完了を待つ + // Wait for task completion task.await?; println!("\n✨ Demo complete!"); diff --git a/llm-worker/examples/worker_cli.rs b/llm-worker/examples/worker_cli.rs index fbc03e4..1b3b6ea 100644 --- a/llm-worker/examples/worker_cli.rs +++ b/llm-worker/examples/worker_cli.rs @@ -1,17 +1,17 @@ -//! Worker を用いた対話型 CLI クライアント +//! Interactive CLI client using Worker //! -//! 複数のLLMプロバイダ(Anthropic, Gemini, OpenAI, Ollama)と対話するCLIアプリケーション。 -//! ツールの登録と実行、ストリーミングレスポンスの表示をデモする。 +//! A CLI application for interacting with multiple LLM providers (Anthropic, Gemini, OpenAI, Ollama). +//! Demonstrates tool registration and execution, and streaming response display. //! -//! ## 使用方法 +//! ## Usage //! //! ```bash -//! # .envファイルにAPIキーを設定 +//! # Set API keys in .env file //! echo "ANTHROPIC_API_KEY=your-api-key" > .env //! echo "GEMINI_API_KEY=your-api-key" >> .env //! echo "OPENAI_API_KEY=your-api-key" >> .env //! -//! # Anthropic (デフォルト) +//! # Anthropic (default) //! cargo run --example worker_cli //! //! # Gemini @@ -20,13 +20,13 @@ //! # OpenAI //! cargo run --example worker_cli -- --provider openai --model gpt-4o //! -//! # Ollama (ローカル) +//! # Ollama (local) //! cargo run --example worker_cli -- --provider ollama --model llama3.2 //! -//! # オプション指定 +//! # With options //! cargo run --example worker_cli -- --provider anthropic --model claude-3-haiku-20240307 --system "You are a helpful assistant." //! -//! # ヘルプ表示 +//! # Show help //! cargo run --example worker_cli -- --help //! ``` @@ -53,15 +53,15 @@ use llm_worker::{ }; use llm_worker_macros::tool_registry; -// 必要なマクロ展開用インポート +// Required imports for macro expansion use schemars; use serde; // ============================================================================= -// プロバイダ定義 +// Provider Definition // ============================================================================= -/// 利用可能なLLMプロバイダ +/// Available LLM providers #[derive(Debug, Clone, Copy, ValueEnum, Default)] enum Provider { /// Anthropic Claude @@ -71,12 +71,12 @@ enum Provider { Gemini, /// OpenAI GPT Openai, - /// Ollama (ローカル) + /// Ollama (local) Ollama, } impl Provider { - /// プロバイダのデフォルトモデル + /// Default model for the provider fn default_model(&self) -> &'static str { match self { Provider::Anthropic => "claude-sonnet-4-20250514", @@ -86,7 +86,7 @@ impl Provider { } } - /// プロバイダの表示名 + /// Display name for the provider fn display_name(&self) -> &'static str { match self { Provider::Anthropic => "Anthropic Claude", @@ -96,78 +96,78 @@ impl Provider { } } - /// APIキーの環境変数名 + /// Environment variable name for API key fn env_var_name(&self) -> Option<&'static str> { match self { Provider::Anthropic => Some("ANTHROPIC_API_KEY"), Provider::Gemini => Some("GEMINI_API_KEY"), Provider::Openai => Some("OPENAI_API_KEY"), - Provider::Ollama => None, // Ollamaはローカルなので不要 + Provider::Ollama => None, // Ollama is local, no key needed } } } // ============================================================================= -// CLI引数定義 +// CLI Argument Definition // ============================================================================= -/// 複数のLLMプロバイダに対応した対話型CLIクライアント +/// Interactive CLI client supporting multiple LLM providers #[derive(Parser, Debug)] #[command(name = "worker-cli")] #[command(about = "Interactive CLI client for multiple LLM providers using Worker")] #[command(version)] struct Args { - /// 使用するプロバイダ + /// Provider to use #[arg(long, value_enum, default_value_t = Provider::Anthropic)] provider: Provider, - /// 使用するモデル名(未指定時はプロバイダのデフォルト) + /// Model name to use (defaults to provider's default if not specified) #[arg(short, long)] model: Option, - /// システムプロンプト + /// System prompt #[arg(short, long)] system: Option, - /// ツールを無効化 + /// Disable tools #[arg(long, default_value = "false")] no_tools: bool, - /// 最初のメッセージ(指定するとそれを送信して終了) + /// Initial message (if specified, sends it and exits) #[arg(short = 'p', long)] prompt: Option, - /// APIキー(環境変数より優先) + /// API key (takes precedence over environment variable) #[arg(long)] api_key: Option, } // ============================================================================= -// ツール定義 +// Tool Definition // ============================================================================= -/// アプリケーションコンテキスト +/// Application context #[derive(Clone)] struct AppContext; #[tool_registry] impl AppContext { - /// 現在の日時を取得する + /// Get the current date and time /// - /// システムの現在の日付と時刻を返します。 + /// Returns the system's current date and time. #[tool] fn get_current_time(&self) -> String { let now = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_secs(); - // シンプルなUnixタイムスタンプからの変換 + // Simple conversion from Unix timestamp format!("Current Unix timestamp: {}", now) } - /// 簡単な計算を行う + /// Perform a simple calculation /// - /// 2つの数値の四則演算を実行します。 + /// Executes arithmetic operations on two numbers. #[tool] fn calculate(&self, a: f64, b: f64, operation: String) -> Result { let result = match operation.as_str() { @@ -187,10 +187,10 @@ impl AppContext { } // ============================================================================= -// ストリーミング表示用ハンドラー +// Streaming Display Handlers // ============================================================================= -/// テキストをリアルタイムで出力するハンドラー +/// Handler that outputs text in real-time struct StreamingPrinter { is_first_delta: Arc>, } @@ -226,7 +226,7 @@ impl Handler for StreamingPrinter { } } -/// ツール呼び出しを表示するハンドラー +/// Handler that displays tool calls struct ToolCallPrinter { call_names: Arc>>, } @@ -270,7 +270,7 @@ impl Handler for ToolCallPrinter { } } -/// ツール実行結果を表示するHook +/// Hook that displays tool execution results struct ToolResultPrinterHook { call_names: Arc>>, } @@ -302,17 +302,17 @@ impl Hook for ToolResultPrinterHook { } // ============================================================================= -// クライアント作成 +// Client Creation // ============================================================================= -/// プロバイダに応じたAPIキーを取得 +/// Get API key based on provider fn get_api_key(args: &Args) -> Result { - // CLI引数のAPIキーが優先 + // CLI argument API key takes precedence if let Some(ref key) = args.api_key { return Ok(key.clone()); } - // プロバイダに応じた環境変数を確認 + // Check environment variable based on provider if let Some(env_var) = args.provider.env_var_name() { std::env::var(env_var).map_err(|_| { format!( @@ -321,12 +321,12 @@ fn get_api_key(args: &Args) -> Result { ) }) } else { - // Ollamaなどはキー不要 + // Ollama etc. don't need a key Ok(String::new()) } } -/// プロバイダに応じたクライアントを作成 +/// Create client based on provider fn create_client(args: &Args) -> Result, String> { let model = args .model @@ -356,17 +356,17 @@ fn create_client(args: &Args) -> Result, String> { } // ============================================================================= -// メイン +// Main // ============================================================================= #[tokio::main] async fn main() -> Result<(), Box> { - // .envファイルを読み込む + // Load .env file dotenv::dotenv().ok(); - // ロギング初期化 - // RUST_LOG=debug cargo run --example worker_cli ... で詳細ログ表示 - // デフォルトは warn レベル、RUST_LOG 環境変数で上書き可能 + // Initialize logging + // Use RUST_LOG=debug cargo run --example worker_cli ... for detailed logs + // Default is warn level, can be overridden with RUST_LOG environment variable let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn")); tracing_subscriber::fmt() @@ -374,7 +374,7 @@ async fn main() -> Result<(), Box> { .with_target(true) .init(); - // CLI引数をパース + // Parse CLI arguments let args = Args::parse(); info!( @@ -383,10 +383,10 @@ async fn main() -> Result<(), Box> { "Starting worker CLI" ); - // 対話モードかワンショットモードか + // Interactive mode or one-shot mode let is_interactive = args.prompt.is_none(); - // モデル名(表示用) + // Model name (for display) let model_name = args .model .clone() @@ -416,7 +416,7 @@ async fn main() -> Result<(), Box> { println!("─────────────────────────────────────────────────"); } - // クライアント作成 + // Create client let client = match create_client(&args) { Ok(c) => c, Err(e) => { @@ -425,17 +425,17 @@ async fn main() -> Result<(), Box> { } }; - // Worker作成 + // Create Worker let mut worker = Worker::new(client); let tool_call_names = Arc::new(Mutex::new(HashMap::new())); - // システムプロンプトを設定 + // Set system prompt if let Some(ref system_prompt) = args.system { worker.set_system_prompt(system_prompt); } - // ツール登録(--no-tools でなければ) + // Register tools (unless --no-tools) if !args.no_tools { let app = AppContext; worker @@ -444,7 +444,7 @@ async fn main() -> Result<(), Box> { worker.register_tool(app.calculate_definition()).unwrap(); } - // ストリーミング表示用ハンドラーを登録 + // Register streaming display handlers worker .timeline_mut() .on_text_block(StreamingPrinter::new()) @@ -452,7 +452,7 @@ async fn main() -> Result<(), Box> { worker.add_post_tool_call_hook(ToolResultPrinterHook::new(tool_call_names)); - // ワンショットモード + // One-shot mode if let Some(prompt) = args.prompt { match worker.run(&prompt).await { Ok(_) => {} @@ -465,7 +465,7 @@ async fn main() -> Result<(), Box> { return Ok(()); } - // 対話ループ + // Interactive loop loop { print!("\n👤 You: "); io::stdout().flush()?; @@ -483,7 +483,7 @@ async fn main() -> Result<(), Box> { break; } - // Workerを実行(Workerが履歴を管理) + // Run Worker (Worker manages history) match worker.run(input).await { Ok(_) => {} Err(e) => { diff --git a/llm-worker/src/event.rs b/llm-worker/src/event.rs index a913c4a..c7c7ff9 100644 --- a/llm-worker/src/event.rs +++ b/llm-worker/src/event.rs @@ -1,6 +1,6 @@ -//! Worker層の公開イベント型 +//! Public event types for Worker layer //! -//! 外部利用者に公開するためのイベント表現。 +//! Event representation exposed to external users. use serde::{Deserialize, Serialize}; @@ -8,38 +8,38 @@ use serde::{Deserialize, Serialize}; // Core Event Types (from llm_client layer) // ============================================================================= -/// LLMからのストリーミングイベント +/// Streaming events from LLM /// -/// 各LLMプロバイダからのレスポンスは、この`Event`のストリームとして -/// 統一的に処理されます。 +/// Responses from each LLM provider are processed uniformly +/// as a stream of `Event`. /// -/// # イベントの種類 +/// # Event Types /// -/// - **メタイベント**: `Ping`, `Usage`, `Status`, `Error` -/// - **ブロックイベント**: `BlockStart`, `BlockDelta`, `BlockStop`, `BlockAbort` +/// - **Meta events**: `Ping`, `Usage`, `Status`, `Error` +/// - **Block events**: `BlockStart`, `BlockDelta`, `BlockStop`, `BlockAbort` /// -/// # ブロックのライフサイクル +/// # Block Lifecycle /// -/// テキストやツール呼び出しは、`BlockStart` → `BlockDelta`(複数) → `BlockStop` -/// の順序でイベントが発生します。 +/// Text and tool calls have events in the order of +/// `BlockStart` → `BlockDelta`(multiple) → `BlockStop`. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Event { - /// ハートビート + /// Heartbeat Ping(PingEvent), - /// トークン使用量 + /// Token usage Usage(UsageEvent), - /// ストリームのステータス変化 + /// Stream status change Status(StatusEvent), - /// エラー発生 + /// Error occurred Error(ErrorEvent), - /// ブロック開始(テキスト、ツール使用等) + /// Block start (text, tool use, etc.) BlockStart(BlockStart), - /// ブロックの差分データ + /// Block delta data BlockDelta(BlockDelta), - /// ブロック正常終了 + /// Block normal end BlockStop(BlockStop), - /// ブロック中断 + /// Block abort BlockAbort(BlockAbort), } @@ -47,47 +47,47 @@ pub enum Event { // Meta Events // ============================================================================= -/// Pingイベント(ハートビート) +/// Ping event (heartbeat) #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] pub struct PingEvent { pub timestamp: Option, } -/// 使用量イベント +/// Usage event #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] pub struct UsageEvent { - /// 入力トークン数 + /// Input token count pub input_tokens: Option, - /// 出力トークン数 + /// Output token count pub output_tokens: Option, - /// 合計トークン数 + /// Total token count pub total_tokens: Option, - /// キャッシュ読み込みトークン数 + /// Cache read token count pub cache_read_input_tokens: Option, - /// キャッシュ作成トークン数 + /// Cache creation token count pub cache_creation_input_tokens: Option, } -/// ステータスイベント +/// Status event #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct StatusEvent { pub status: ResponseStatus, } -/// レスポンスステータス +/// Response status #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ResponseStatus { - /// ストリーム開始 + /// Stream started Started, - /// 正常完了 + /// Completed normally Completed, - /// キャンセルされた + /// Cancelled Cancelled, - /// エラー発生 + /// Error occurred Failed, } -/// エラーイベント +/// Error event #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ErrorEvent { pub code: Option, @@ -98,27 +98,27 @@ pub struct ErrorEvent { // Block Types // ============================================================================= -/// ブロックの種別 +/// Block type #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum BlockType { - /// テキスト生成 + /// Text generation Text, - /// 思考 (Claude Extended Thinking等) + /// Thinking (Claude Extended Thinking, etc.) Thinking, - /// ツール呼び出し + /// Tool call ToolUse, - /// ツール結果 + /// Tool result ToolResult, } -/// ブロック開始イベント +/// Block start event #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct BlockStart { - /// ブロックのインデックス + /// Block index pub index: usize, - /// ブロックの種別 + /// Block type pub block_type: BlockType, - /// ブロック固有のメタデータ + /// Block-specific metadata pub metadata: BlockMetadata, } @@ -128,7 +128,7 @@ impl BlockStart { } } -/// ブロックのメタデータ +/// Block metadata #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum BlockMetadata { Text, @@ -137,28 +137,28 @@ pub enum BlockMetadata { ToolResult { tool_use_id: String }, } -/// ブロックデルタイベント +/// Block delta event #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct BlockDelta { - /// ブロックのインデックス + /// Block index pub index: usize, - /// デルタの内容 + /// Delta content pub delta: DeltaContent, } -/// デルタの内容 +/// Delta content #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum DeltaContent { - /// テキストデルタ + /// Text delta Text(String), - /// 思考デルタ + /// Thinking delta Thinking(String), - /// ツール引数のJSON部分文字列 + /// JSON substring of tool arguments InputJson(String), } impl DeltaContent { - /// デルタのブロック種別を取得 + /// Get block type of the delta pub fn block_type(&self) -> BlockType { match self { DeltaContent::Text(_) => BlockType::Text, @@ -168,14 +168,14 @@ impl DeltaContent { } } -/// ブロック停止イベント +/// Block stop event #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct BlockStop { - /// ブロックのインデックス + /// Block index pub index: usize, - /// ブロックの種別 + /// Block type pub block_type: BlockType, - /// 停止理由 + /// Stop reason pub stop_reason: Option, } @@ -185,14 +185,14 @@ impl BlockStop { } } -/// ブロック中断イベント +/// Block abort event #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct BlockAbort { - /// ブロックのインデックス + /// Block index pub index: usize, - /// ブロックの種別 + /// Block type pub block_type: BlockType, - /// 中断理由 + /// Abort reason pub reason: String, } @@ -202,16 +202,16 @@ impl BlockAbort { } } -/// 停止理由 +/// Stop reason #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum StopReason { - /// 自然終了 + /// Natural end EndTurn, - /// 最大トークン数到達 + /// Max tokens reached MaxTokens, - /// ストップシーケンス到達 + /// Stop sequence reached StopSequence, - /// ツール使用 + /// Tool use ToolUse, } @@ -220,7 +220,7 @@ pub enum StopReason { // ============================================================================= impl Event { - /// テキストブロック開始イベントを作成 + /// Create text block start event pub fn text_block_start(index: usize) -> Self { Event::BlockStart(BlockStart { index, @@ -229,7 +229,7 @@ impl Event { }) } - /// テキストデルタイベントを作成 + /// Create text delta event pub fn text_delta(index: usize, text: impl Into) -> Self { Event::BlockDelta(BlockDelta { index, @@ -237,7 +237,7 @@ impl Event { }) } - /// テキストブロック停止イベントを作成 + /// Create text block stop event pub fn text_block_stop(index: usize, stop_reason: Option) -> Self { Event::BlockStop(BlockStop { index, @@ -246,7 +246,7 @@ impl Event { }) } - /// ツール使用ブロック開始イベントを作成 + /// Create tool use block start event pub fn tool_use_start(index: usize, id: impl Into, name: impl Into) -> Self { Event::BlockStart(BlockStart { index, @@ -258,7 +258,7 @@ impl Event { }) } - /// ツール引数デルタイベントを作成 + /// Create tool input delta event pub fn tool_input_delta(index: usize, json: impl Into) -> Self { Event::BlockDelta(BlockDelta { index, @@ -266,7 +266,7 @@ impl Event { }) } - /// ツール使用ブロック停止イベントを作成 + /// Create tool use block stop event pub fn tool_use_stop(index: usize) -> Self { Event::BlockStop(BlockStop { index, @@ -275,7 +275,7 @@ impl Event { }) } - /// 使用量イベントを作成 + /// Create usage event pub fn usage(input_tokens: u64, output_tokens: u64) -> Self { Event::Usage(UsageEvent { input_tokens: Some(input_tokens), @@ -286,7 +286,7 @@ impl Event { }) } - /// Pingイベントを作成 + /// Create ping event pub fn ping() -> Self { Event::Ping(PingEvent { timestamp: None }) } diff --git a/llm-worker/src/handler.rs b/llm-worker/src/handler.rs index 1c39a5c..225c514 100644 --- a/llm-worker/src/handler.rs +++ b/llm-worker/src/handler.rs @@ -1,8 +1,8 @@ -//! Handler/Kind型 +//! Handler/Kind Types //! -//! Timeline層でイベントを処理するためのトレイト。 -//! カスタムハンドラを実装してTimelineに登録することで、 -//! ストリームイベントを受信できます。 +//! Traits for processing events in the Timeline layer. +//! By implementing custom handlers and registering them with Timeline, +//! you can receive stream events. use crate::timeline::event::*; @@ -10,13 +10,13 @@ use crate::timeline::event::*; // Kind Trait // ============================================================================= -/// イベント種別を定義するマーカートレイト +/// Marker trait defining event types /// -/// 各Kindは対応するイベント型を指定します。 -/// HandlerはこのKindに対して実装され、同じKindに対して -/// 異なるScope型を持つ複数のHandlerを登録できます。 +/// Each Kind specifies its corresponding event type. +/// Handlers are implemented for this Kind, and multiple Handlers +/// with different Scope types can be registered for the same Kind. pub trait Kind { - /// このKindに対応するイベント型 + /// Event type corresponding to this Kind type Event; } @@ -24,10 +24,10 @@ pub trait Kind { // Handler Trait // ============================================================================= -/// イベントを処理するハンドラトレイト +/// Handler trait for processing events /// -/// 特定の`Kind`に対するイベント処理を定義します。 -/// `Scope`はブロックのライフサイクル中に保持される状態です。 +/// Defines event processing for a specific `Kind`. +/// `Scope` is state held during the block's lifecycle. /// /// # Examples /// @@ -39,7 +39,7 @@ pub trait Kind { /// } /// /// impl Handler for TextCollector { -/// type Scope = String; // ブロックごとのバッファ +/// type Scope = String; // Buffer per block /// /// fn on_event(&mut self, buffer: &mut String, event: &TextBlockEvent) { /// match event { @@ -53,13 +53,13 @@ pub trait Kind { /// } /// ``` pub trait Handler { - /// Handler固有のスコープ型 + /// Handler-specific scope type /// - /// ブロック開始時に`Default::default()`で生成され、 - /// ブロック終了時に破棄されます。 + /// Generated with `Default::default()` at block start, + /// and destroyed at block end. type Scope: Default; - /// イベントを処理する + /// Process the event fn on_event(&mut self, scope: &mut Self::Scope, event: &K::Event); } @@ -67,25 +67,25 @@ pub trait Handler { // Meta Kind Definitions // ============================================================================= -/// Usage Kind - 使用量イベント用 +/// Usage Kind - for usage events pub struct UsageKind; impl Kind for UsageKind { type Event = UsageEvent; } -/// Ping Kind - Pingイベント用 +/// Ping Kind - for ping events pub struct PingKind; impl Kind for PingKind { type Event = PingEvent; } -/// Status Kind - ステータスイベント用 +/// Status Kind - for status events pub struct StatusKind; impl Kind for StatusKind { type Event = StatusEvent; } -/// Error Kind - エラーイベント用 +/// Error Kind - for error events pub struct ErrorKind; impl Kind for ErrorKind { type Event = ErrorEvent; @@ -95,13 +95,13 @@ impl Kind for ErrorKind { // Block Kind Definitions // ============================================================================= -/// TextBlock Kind - テキストブロック用 +/// TextBlock Kind - for text blocks pub struct TextBlockKind; impl Kind for TextBlockKind { type Event = TextBlockEvent; } -/// テキストブロックのイベント +/// Text block events #[derive(Debug, Clone, PartialEq)] pub enum TextBlockEvent { Start(TextBlockStart), @@ -120,13 +120,13 @@ pub struct TextBlockStop { pub stop_reason: Option, } -/// ThinkingBlock Kind - 思考ブロック用 +/// ThinkingBlock Kind - for thinking blocks pub struct ThinkingBlockKind; impl Kind for ThinkingBlockKind { type Event = ThinkingBlockEvent; } -/// 思考ブロックのイベント +/// Thinking block events #[derive(Debug, Clone, PartialEq)] pub enum ThinkingBlockEvent { Start(ThinkingBlockStart), @@ -144,17 +144,17 @@ pub struct ThinkingBlockStop { pub index: usize, } -/// ToolUseBlock Kind - ツール使用ブロック用 +/// ToolUseBlock Kind - for tool use blocks pub struct ToolUseBlockKind; impl Kind for ToolUseBlockKind { type Event = ToolUseBlockEvent; } -/// ツール使用ブロックのイベント +/// Tool use block events #[derive(Debug, Clone, PartialEq)] pub enum ToolUseBlockEvent { Start(ToolUseBlockStart), - /// ツール引数のJSON部分文字列 + /// JSON substring of tool arguments InputJsonDelta(String), Stop(ToolUseBlockStop), } diff --git a/llm-worker/src/hook.rs b/llm-worker/src/hook.rs index 9fed3af..16970f9 100644 --- a/llm-worker/src/hook.rs +++ b/llm-worker/src/hook.rs @@ -1,6 +1,6 @@ -//! Hook関連の型定義 +//! Hook-related type definitions //! -//! Worker層でのターン制御・介入に使用される型 +//! Types used for turn control and intervention in the Worker layer use async_trait::async_trait; use serde::{Deserialize, Serialize}; @@ -60,25 +60,25 @@ use std::sync::Arc; use crate::tool::{Tool, ToolMeta}; -/// PreToolCall の入力コンテキスト +/// Input context for PreToolCall pub struct ToolCallContext { - /// ツール呼び出し情報(改変可能) + /// Tool call information (modifiable) pub call: ToolCall, - /// ツールメタ情報(不変) + /// Tool meta information (immutable) pub meta: ToolMeta, - /// ツールインスタンス(状態アクセス用) + /// Tool instance (for state access) pub tool: Arc, } -/// PostToolCall の入力コンテキスト +/// Input context for PostToolCall pub struct PostToolCallContext { - /// ツール呼び出し情報 + /// Tool call information pub call: ToolCall, - /// ツール実行結果(改変可能) + /// Tool execution result (modifiable) pub result: ToolResult, - /// ツールメタ情報(不変) + /// Tool meta information (immutable) pub meta: ToolMeta, - /// ツールインスタンス(状態アクセス用) + /// Tool instance (for state access) pub tool: Arc, } @@ -116,35 +116,35 @@ impl HookEventKind for OnAbort { // Tool Call / Result Types // ============================================================================= -/// ツール呼び出し情報 +/// Tool call information /// -/// LLMからのToolUseブロックを表現し、Hook処理で改変可能 +/// Represents a ToolUse block from LLM, modifiable in Hook processing #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ToolCall { - /// ツール呼び出しID(レスポンスとの紐付けに使用) + /// Tool call ID (used for linking with response) pub id: String, - /// ツール名 + /// Tool name pub name: String, - /// 入力引数(JSON) + /// Input arguments (JSON) pub input: Value, } -/// ツール実行結果 +/// Tool execution result /// -/// ツール実行後の結果を表現し、Hook処理で改変可能 +/// Represents the result after tool execution, modifiable in Hook processing #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ToolResult { - /// 対応するツール呼び出しID + /// Corresponding tool call ID pub tool_use_id: String, - /// 結果コンテンツ + /// Result content pub content: String, - /// エラーかどうか + /// Whether this is an error #[serde(default)] pub is_error: bool, } impl ToolResult { - /// 成功結果を作成 + /// Create a success result pub fn success(tool_use_id: impl Into, content: impl Into) -> Self { Self { tool_use_id: tool_use_id.into(), @@ -153,7 +153,7 @@ impl ToolResult { } } - /// エラー結果を作成 + /// Create an error result pub fn error(tool_use_id: impl Into, content: impl Into) -> Self { Self { tool_use_id: tool_use_id.into(), @@ -167,13 +167,13 @@ impl ToolResult { // Hook Error // ============================================================================= -/// Hookエラー +/// Hook error #[derive(Debug, Error)] pub enum HookError { - /// 処理が中断された + /// Processing was aborted #[error("Aborted: {0}")] Aborted(String), - /// 内部エラー + /// Internal error #[error("Hook error: {0}")] Internal(String), } @@ -182,9 +182,9 @@ pub enum HookError { // Hook Trait // ============================================================================= -/// Hookイベントの処理を行うトレイト +/// Trait for handling Hook events /// -/// 各イベント種別は戻り値型が異なるため、`HookEventKind`を介して型を制約する。 +/// Each event type has a different return type, constrained via `HookEventKind`. #[async_trait] pub trait Hook: Send + Sync { async fn call(&self, input: &mut E::Input) -> Result; @@ -194,9 +194,9 @@ pub trait Hook: Send + Sync { // Hook Registry // ============================================================================= -/// 全 Hook を保持するレジストリ +/// Registry holding all Hooks /// -/// Worker 内部で使用され、各種 Hook を一括管理する。 +/// Used internally by Worker to manage all Hook types. pub struct HookRegistry { /// on_prompt_submit Hook pub(crate) on_prompt_submit: Vec>>, @@ -219,7 +219,7 @@ impl Default for HookRegistry { } impl HookRegistry { - /// 空の HookRegistry を作成 + /// Create an empty HookRegistry pub fn new() -> Self { Self { on_prompt_submit: Vec::new(), diff --git a/llm-worker/src/lib.rs b/llm-worker/src/lib.rs index 2a311ab..ee2fac0 100644 --- a/llm-worker/src/lib.rs +++ b/llm-worker/src/lib.rs @@ -1,34 +1,34 @@ -//! llm-worker - LLMワーカーライブラリ +//! llm-worker - LLM Worker Library //! -//! LLMとの対話を管理するコンポーネントを提供します。 +//! Provides components for managing interactions with LLMs. //! -//! # 主要なコンポーネント +//! # Main Components //! -//! - [`Worker`] - LLMとの対話を管理する中心コンポーネント -//! - [`tool::Tool`] - LLMから呼び出し可能なツール -//! - [`hook::Hook`] - ターン進行への介入 -//! - [`subscriber::WorkerSubscriber`] - ストリーミングイベントの購読 +//! - [`Worker`] - Central component for managing LLM interactions +//! - [`tool::Tool`] - Tools that can be invoked by the LLM +//! - [`hook::Hook`] - Hooks for intercepting turn progression +//! - [`subscriber::WorkerSubscriber`] - Subscribing to streaming events //! //! # Quick Start //! //! ```ignore //! use llm_worker::{Worker, Message}; //! -//! // Workerを作成 +//! // Create a Worker //! let mut worker = Worker::new(client) //! .system_prompt("You are a helpful assistant."); //! -//! // ツールを登録(オプション) +//! // Register tools (optional) //! // worker.register_tool(my_tool_definition)?; //! -//! // 対話を実行 +//! // Run the interaction //! let history = worker.run("Hello!").await?; //! ``` //! -//! # キャッシュ保護 +//! # Cache Protection //! -//! KVキャッシュのヒット率を最大化するには、[`Worker::lock()`]で -//! ロック状態に遷移してから実行してください。 +//! To maximize KV cache hit rate, transition to the locked state +//! with [`Worker::lock()`] before execution. //! //! ```ignore //! let mut locked = worker.lock(); diff --git a/llm-worker/src/message.rs b/llm-worker/src/message.rs index fbc3a47..a9cb2ae 100644 --- a/llm-worker/src/message.rs +++ b/llm-worker/src/message.rs @@ -1,71 +1,71 @@ -//! メッセージ型 +//! Message Types //! -//! LLMとの会話で使用されるメッセージ構造。 -//! [`Message::user`]や[`Message::assistant`]で簡単に作成できます。 +//! Message structure used in conversations with LLM. +//! Can be easily created using [`Message::user`] or [`Message::assistant`]. use serde::{Deserialize, Serialize}; -/// メッセージのロール +/// Message role #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum Role { - /// ユーザー + /// User User, - /// アシスタント + /// Assistant Assistant, } -/// 会話のメッセージ +/// Conversation message /// /// # Examples /// /// ```ignore /// use llm_worker::Message; /// -/// // ユーザーメッセージ +/// // User message /// let user_msg = Message::user("Hello!"); /// -/// // アシスタントメッセージ +/// // Assistant message /// let assistant_msg = Message::assistant("Hi there!"); /// ``` #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Message { - /// ロール + /// Role pub role: Role, - /// コンテンツ + /// Content pub content: MessageContent, } -/// メッセージコンテンツ +/// Message content #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum MessageContent { - /// テキストコンテンツ + /// Text content Text(String), - /// ツール結果 + /// Tool result ToolResult { tool_use_id: String, content: String, }, - /// 複合コンテンツ (テキスト + ツール使用等) + /// Composite content (text + tool use, etc.) Parts(Vec), } -/// コンテンツパーツ +/// Content part #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type")] pub enum ContentPart { - /// テキスト + /// Text #[serde(rename = "text")] Text { text: String }, - /// ツール使用 + /// Tool use #[serde(rename = "tool_use")] ToolUse { id: String, name: String, input: serde_json::Value, }, - /// ツール結果 + /// Tool result #[serde(rename = "tool_result")] ToolResult { tool_use_id: String, @@ -74,13 +74,13 @@ pub enum ContentPart { } impl Message { - /// ユーザーメッセージを作成 + /// Create a user message /// /// # Examples /// /// ```ignore /// use llm_worker::Message; - /// let msg = Message::user("こんにちは"); + /// let msg = Message::user("Hello"); /// ``` pub fn user(content: impl Into) -> Self { Self { @@ -89,10 +89,10 @@ impl Message { } } - /// アシスタントメッセージを作成 + /// Create an assistant message /// - /// 通常はWorker内部で自動生成されますが、 - /// 履歴の初期化などで手動作成も可能です。 + /// Usually auto-generated inside Worker, + /// but can be manually created for history initialization, etc. pub fn assistant(content: impl Into) -> Self { Self { role: Role::Assistant, @@ -100,10 +100,10 @@ impl Message { } } - /// ツール結果メッセージを作成 + /// Create a tool result message /// - /// Worker内部でツール実行後に自動生成されます。 - /// 通常は直接作成する必要はありません。 + /// Auto-generated inside Worker after tool execution. + /// Usually no need to create manually. pub fn tool_result(tool_use_id: impl Into, content: impl Into) -> Self { Self { role: Role::User, diff --git a/llm-worker/src/state.rs b/llm-worker/src/state.rs index ca04507..e1127b0 100644 --- a/llm-worker/src/state.rs +++ b/llm-worker/src/state.rs @@ -1,25 +1,25 @@ -//! Worker状態 +//! Worker State //! -//! Type-stateパターンによるキャッシュ保護のための状態マーカー型。 -//! Workerは`Mutable` → `CacheLocked`の状態遷移を持ちます。 +//! State marker types for cache protection using the Type-state pattern. +//! Worker has state transitions from `Mutable` → `CacheLocked`. -/// Worker状態を表すマーカートレイト +/// Marker trait representing Worker state /// -/// このトレイトはシールされており、外部から実装することはできません。 +/// This trait is sealed and cannot be implemented externally. pub trait WorkerState: private::Sealed + Send + Sync + 'static {} mod private { pub trait Sealed {} } -/// 編集可能状態 +/// Mutable state (editable) /// -/// この状態では以下の操作が可能です: -/// - システムプロンプトの設定・変更 -/// - メッセージ履歴の編集(追加、削除、クリア) -/// - ツール・Hookの登録 +/// In this state, the following operations are available: +/// - Setting/changing system prompt +/// - Editing message history (add, delete, clear) +/// - Registering tools and hooks /// -/// `Worker::lock()`により[`CacheLocked`]状態へ遷移できます。 +/// Can transition to [`CacheLocked`] state via `Worker::lock()`. /// /// # Examples /// @@ -29,11 +29,11 @@ mod private { /// let mut worker = Worker::new(client) /// .system_prompt("You are helpful."); /// -/// // 履歴を編集可能 +/// // History can be edited /// worker.push_message(Message::user("Hello")); /// worker.clear_history(); /// -/// // ロックして保護状態へ +/// // Lock to protected state /// let locked = worker.lock(); /// ``` #[derive(Debug, Clone, Copy, Default)] @@ -42,17 +42,17 @@ pub struct Mutable; impl private::Sealed for Mutable {} impl WorkerState for Mutable {} -/// キャッシュロック状態(キャッシュ保護) +/// Cache locked state (cache protected) /// -/// この状態では以下の制限があります: -/// - システムプロンプトの変更不可 -/// - 既存メッセージ履歴の変更不可(末尾への追記のみ) +/// In this state, the following restrictions apply: +/// - System prompt cannot be changed +/// - Existing message history cannot be modified (only appending to the end) /// -/// LLM APIのKVキャッシュヒットを保証するため、 -/// 実行時にはこの状態の使用が推奨されます。 +/// To ensure LLM API KV cache hits, +/// using this state during execution is recommended. /// -/// `Worker::unlock()`により[`Mutable`]状態へ戻せますが、 -/// キャッシュ保護が解除されることに注意してください。 +/// Can return to [`Mutable`] state via `Worker::unlock()`, +/// but note that cache protection will be released. #[derive(Debug, Clone, Copy, Default)] pub struct CacheLocked; diff --git a/llm-worker/src/subscriber.rs b/llm-worker/src/subscriber.rs index 0e7cdf6..3f38493 100644 --- a/llm-worker/src/subscriber.rs +++ b/llm-worker/src/subscriber.rs @@ -1,7 +1,7 @@ -//! イベント購読 +//! Event Subscription //! -//! LLMからのストリーミングイベントをリアルタイムで受信するためのトレイト。 -//! UIへのストリーム表示やプログレス表示に使用します。 +//! Trait for receiving streaming events from LLM in real-time. +//! Used for stream display to UI and progress display. use std::sync::{Arc, Mutex}; @@ -18,17 +18,17 @@ use crate::{ // WorkerSubscriber Trait // ============================================================================= -/// LLMからのストリーミングイベントを購読するトレイト +/// Trait for subscribing to streaming events from LLM /// -/// Workerに登録すると、テキスト生成やツール呼び出しのイベントを -/// リアルタイムで受信できます。UIへのストリーム表示に最適です。 +/// When registered with Worker, you can receive events from text generation +/// and tool calls in real-time. Ideal for stream display to UI. /// -/// # 受信できるイベント +/// # Available Events /// -/// - **ブロックイベント**: テキスト、ツール使用(スコープ付き) -/// - **メタイベント**: 使用量、ステータス、エラー -/// - **完了イベント**: テキスト完了、ツール呼び出し完了 -/// - **ターン制御**: ターン開始、ターン終了 +/// - **Block events**: Text, tool use (with scope) +/// - **Meta events**: Usage, status, error +/// - **Completion events**: Text complete, tool call complete +/// - **Turn control**: Turn start, turn end /// /// # Examples /// @@ -44,7 +44,7 @@ use crate::{ /// /// fn on_text_block(&mut self, _: &mut (), event: &TextBlockEvent) { /// if let TextBlockEvent::Delta(text) = event { -/// print!("{}", text); // リアルタイム出力 +/// print!("{}", text); // Real-time output /// } /// } /// @@ -53,37 +53,37 @@ use crate::{ /// } /// } /// -/// // Workerに登録 +/// // Register with Worker /// worker.subscribe(StreamPrinter); /// ``` pub trait WorkerSubscriber: Send { // ========================================================================= - // スコープ型(ブロックイベント用) + // Scope Types (for block events) // ========================================================================= - /// テキストブロック処理用のスコープ型 + /// Scope type for text block processing /// - /// ブロック開始時にDefault::default()で生成され、 - /// ブロック終了時に破棄される。 + /// Generated with Default::default() at block start, + /// destroyed at block end. type TextBlockScope: Default + Send + Sync; - /// ツール使用ブロック処理用のスコープ型 + /// Scope type for tool use block processing type ToolUseBlockScope: Default + Send + Sync; // ========================================================================= - // ブロックイベント(スコープ管理あり) + // Block Events (with scope management) // ========================================================================= - /// テキストブロックイベント + /// Text block event /// - /// Start/Delta/Stopのライフサイクルを持つ。 - /// scopeはブロック開始時に生成され、終了時に破棄される。 + /// Has Start/Delta/Stop lifecycle. + /// Scope is generated at block start and destroyed at end. #[allow(unused_variables)] fn on_text_block(&mut self, scope: &mut Self::TextBlockScope, event: &TextBlockEvent) {} - /// ツール使用ブロックイベント + /// Tool use block event /// - /// Start/InputJsonDelta/Stopのライフサイクルを持つ。 + /// Has Start/InputJsonDelta/Stop lifecycle. #[allow(unused_variables)] fn on_tool_use_block( &mut self, @@ -93,62 +93,62 @@ pub trait WorkerSubscriber: Send { } // ========================================================================= - // 単発イベント(スコープ不要) + // Single Events (no scope needed) // ========================================================================= - /// 使用量イベント + /// Usage event #[allow(unused_variables)] fn on_usage(&mut self, event: &UsageEvent) {} - /// ステータスイベント + /// Status event #[allow(unused_variables)] fn on_status(&mut self, event: &StatusEvent) {} - /// エラーイベント + /// Error event #[allow(unused_variables)] fn on_error(&mut self, event: &ErrorEvent) {} // ========================================================================= - // 累積イベント(Worker層で追加) + // Accumulated Events (added in Worker layer) // ========================================================================= - /// テキスト完了イベント + /// Text complete event /// - /// テキストブロックが完了した時点で、累積されたテキスト全体が渡される。 - /// ブロック処理後の最終結果を受け取るのに便利。 + /// When a text block completes, the entire accumulated text is passed. + /// Convenient for receiving the final result after block processing. #[allow(unused_variables)] fn on_text_complete(&mut self, text: &str) {} - /// ツール呼び出し完了イベント + /// Tool call complete event /// - /// ツール使用ブロックが完了した時点で、完全なToolCallが渡される。 + /// When a tool use block completes, the complete ToolCall is passed. #[allow(unused_variables)] fn on_tool_call_complete(&mut self, call: &ToolCall) {} // ========================================================================= - // ターン制御 + // Turn Control // ========================================================================= - /// ターン開始時 + /// On turn start /// - /// `turn`は0から始まるターン番号。 + /// `turn` is a 0-based turn number. #[allow(unused_variables)] fn on_turn_start(&mut self, turn: usize) {} - /// ターン終了時 + /// On turn end #[allow(unused_variables)] fn on_turn_end(&mut self, turn: usize) {} } // ============================================================================= -// SubscriberAdapter - WorkerSubscriberをTimelineハンドラにブリッジ +// SubscriberAdapter - Bridge WorkerSubscriber to Timeline handlers // ============================================================================= // ============================================================================= // TextBlock Handler Adapter // ============================================================================= -/// TextBlockKind用のSubscriberアダプター +/// Subscriber adapter for TextBlockKind pub(crate) struct TextBlockSubscriberAdapter { subscriber: Arc>, } @@ -167,10 +167,10 @@ impl Clone for TextBlockSubscriberAdapter { } } -/// TextBlockのスコープをラップ +/// Wrapper for TextBlock scope pub struct TextBlockScopeWrapper { inner: S::TextBlockScope, - buffer: String, // on_text_complete用のバッファ + buffer: String, // Buffer for on_text_complete } impl Default for TextBlockScopeWrapper { @@ -186,16 +186,16 @@ impl Handler for TextBlockSubscrib type Scope = TextBlockScopeWrapper; fn on_event(&mut self, scope: &mut Self::Scope, event: &TextBlockEvent) { - // Deltaの場合はバッファに蓄積 + // Accumulate deltas into buffer if let TextBlockEvent::Delta(text) = event { scope.buffer.push_str(text); } - // SubscriberのTextBlockイベントハンドラを呼び出し + // Call Subscriber's TextBlock event handler if let Ok(mut subscriber) = self.subscriber.lock() { subscriber.on_text_block(&mut scope.inner, event); - // Stopの場合はon_text_completeも呼び出し + // Also call on_text_complete on Stop if matches!(event, TextBlockEvent::Stop(_)) { subscriber.on_text_complete(&scope.buffer); } @@ -207,7 +207,7 @@ impl Handler for TextBlockSubscrib // ToolUseBlock Handler Adapter // ============================================================================= -/// ToolUseBlockKind用のSubscriberアダプター +/// Subscriber adapter for ToolUseBlockKind pub(crate) struct ToolUseBlockSubscriberAdapter { subscriber: Arc>, } @@ -226,12 +226,12 @@ impl Clone for ToolUseBlockSubscriberAdapter { } } -/// ToolUseBlockのスコープをラップ +/// Wrapper for ToolUseBlock scope pub struct ToolUseBlockScopeWrapper { inner: S::ToolUseBlockScope, id: String, name: String, - input_json: String, // JSON蓄積用 + input_json: String, // JSON accumulation } impl Default for ToolUseBlockScopeWrapper { @@ -249,22 +249,22 @@ impl Handler for ToolUseBlockSu type Scope = ToolUseBlockScopeWrapper; fn on_event(&mut self, scope: &mut Self::Scope, event: &ToolUseBlockEvent) { - // Start時にメタデータを保存 + // Save metadata on Start if let ToolUseBlockEvent::Start(start) = event { scope.id = start.id.clone(); scope.name = start.name.clone(); } - // InputJsonDeltaの場合はバッファに蓄積 + // Accumulate InputJsonDelta into buffer if let ToolUseBlockEvent::InputJsonDelta(json) = event { scope.input_json.push_str(json); } - // SubscriberのToolUseBlockイベントハンドラを呼び出し + // Call Subscriber's ToolUseBlock event handler if let Ok(mut subscriber) = self.subscriber.lock() { subscriber.on_tool_use_block(&mut scope.inner, event); - // Stopの場合はon_tool_call_completeも呼び出し + // Also call on_tool_call_complete on Stop if matches!(event, ToolUseBlockEvent::Stop(_)) { let input: serde_json::Value = serde_json::from_str(&scope.input_json).unwrap_or_default(); @@ -283,7 +283,7 @@ impl Handler for ToolUseBlockSu // Meta Event Handler Adapters // ============================================================================= -/// UsageKind用のSubscriberアダプター +/// Subscriber adapter for UsageKind pub(crate) struct UsageSubscriberAdapter { subscriber: Arc>, } @@ -312,7 +312,7 @@ impl Handler for UsageSubscriberAdapte } } -/// StatusKind用のSubscriberアダプター +/// Subscriber adapter for StatusKind pub(crate) struct StatusSubscriberAdapter { subscriber: Arc>, } @@ -341,7 +341,7 @@ impl Handler for StatusSubscriberAdap } } -/// ErrorKind用のSubscriberアダプター +/// Subscriber adapter for ErrorKind pub(crate) struct ErrorSubscriberAdapter { subscriber: Arc>, } diff --git a/llm-worker/src/tool.rs b/llm-worker/src/tool.rs index eabe57f..eeaeeca 100644 --- a/llm-worker/src/tool.rs +++ b/llm-worker/src/tool.rs @@ -1,7 +1,7 @@ -//! ツール定義 +//! Tool Definition //! -//! LLMから呼び出し可能なツールを定義するためのトレイト。 -//! 通常は`#[tool]`マクロを使用して自動実装します。 +//! Traits for defining tools callable by LLM. +//! Usually auto-implemented using the `#[tool]` macro. use std::sync::Arc; @@ -9,40 +9,40 @@ use async_trait::async_trait; use serde_json::Value; use thiserror::Error; -/// ツール実行時のエラー +/// Error during tool execution #[derive(Debug, Error)] pub enum ToolError { - /// 引数が不正 + /// Invalid argument #[error("Invalid argument: {0}")] InvalidArgument(String), - /// 実行に失敗 + /// Execution failed #[error("Execution failed: {0}")] ExecutionFailed(String), - /// 内部エラー + /// Internal error #[error("Internal error: {0}")] Internal(String), } // ============================================================================= -// ToolMeta - 不変のメタ情報 +// ToolMeta - Immutable Meta Information // ============================================================================= -/// ツールのメタ情報(登録時に固定、不変) +/// Tool meta information (fixed at registration, immutable) /// -/// `ToolDefinition` ファクトリから生成され、Worker に登録後は変更されません。 -/// LLM へのツール定義送信に使用されます。 +/// Generated from `ToolDefinition` factory and does not change after registration with Worker. +/// Used for sending tool definitions to LLM. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ToolMeta { - /// ツール名(LLMが識別に使用) + /// Tool name (used by LLM for identification) pub name: String, - /// ツールの説明(LLMへのプロンプトに含まれる) + /// Tool description (included in prompt to LLM) pub description: String, - /// 引数のJSON Schema + /// JSON Schema for arguments pub input_schema: Value, } impl ToolMeta { - /// 新しい ToolMeta を作成 + /// Create a new ToolMeta pub fn new(name: impl Into) -> Self { Self { name: name.into(), @@ -51,13 +51,13 @@ impl ToolMeta { } } - /// 説明を設定 + /// Set the description pub fn description(mut self, desc: impl Into) -> Self { self.description = desc.into(); self } - /// 引数スキーマを設定 + /// Set the argument schema pub fn input_schema(mut self, schema: Value) -> Self { self.input_schema = schema; self @@ -65,14 +65,14 @@ impl ToolMeta { } // ============================================================================= -// ToolDefinition - ファクトリ型 +// ToolDefinition - Factory Type // ============================================================================= -/// ツール定義ファクトリ +/// Tool definition factory /// -/// 呼び出すと `(ToolMeta, Arc)` を返します。 -/// Worker への登録時に一度だけ呼び出され、メタ情報とインスタンスが -/// セッションスコープでキャッシュされます。 +/// When called, returns `(ToolMeta, Arc)`. +/// Called once during Worker registration, and the meta information and instance +/// are cached at session scope. /// /// # Examples /// @@ -93,15 +93,15 @@ pub type ToolDefinition = Arc (ToolMeta, Arc) + Send + Syn // Tool trait // ============================================================================= -/// LLMから呼び出し可能なツールを定義するトレイト +/// Trait for defining tools callable by LLM /// -/// ツールはLLMが外部リソースにアクセスしたり、 -/// 計算を実行したりするために使用します。 -/// セッション中の状態を保持できます。 +/// Tools are used by LLM to access external resources +/// or execute computations. +/// Can maintain state during the session. /// -/// # 実装方法 +/// # How to Implement /// -/// 通常は`#[tool_registry]`マクロを使用して自動実装します: +/// Usually auto-implemented using the `#[tool_registry]` macro: /// /// ```ignore /// #[tool_registry] @@ -112,11 +112,11 @@ pub type ToolDefinition = Arc (ToolMeta, Arc) + Send + Syn /// } /// } /// -/// // 登録 +/// // Register /// worker.register_tool(app.search_definition())?; /// ``` /// -/// # 手動実装 +/// # Manual Implementation /// /// ```ignore /// use llm_worker::tool::{Tool, ToolError, ToolMeta, ToolDefinition}; @@ -143,12 +143,12 @@ pub type ToolDefinition = Arc (ToolMeta, Arc) + Send + Syn /// ``` #[async_trait] pub trait Tool: Send + Sync { - /// ツールを実行する + /// Execute the tool /// /// # Arguments - /// * `input_json` - LLMが生成したJSON形式の引数 + /// * `input_json` - JSON-formatted arguments generated by LLM /// /// # Returns - /// 実行結果の文字列。この内容がLLMに返されます。 + /// Result string from execution. This content is returned to LLM. async fn execute(&self, input_json: &str) -> Result; } diff --git a/llm-worker/src/worker.rs b/llm-worker/src/worker.rs index 18f4c34..035e5f5 100644 --- a/llm-worker/src/worker.rs +++ b/llm-worker/src/worker.rs @@ -30,33 +30,33 @@ use crate::{ // Worker Error // ============================================================================= -/// Workerエラー +/// Worker errors #[derive(Debug, thiserror::Error)] pub enum WorkerError { - /// クライアントエラー + /// Client error #[error("Client error: {0}")] Client(#[from] ClientError), - /// ツールエラー + /// Tool error #[error("Tool error: {0}")] Tool(#[from] ToolError), - /// Hookエラー + /// Hook error #[error("Hook error: {0}")] Hook(#[from] HookError), - /// 処理が中断された + /// Execution was aborted #[error("Aborted: {0}")] Aborted(String), - /// Cancellation Tokenによって中断された + /// Cancelled by CancellationToken #[error("Cancelled")] Cancelled, - /// 設定に関する警告(未サポートのオプション) + /// Config warnings (unsupported options) #[error("Config warnings: {}", .0.iter().map(|w| w.to_string()).collect::>().join(", "))] ConfigWarnings(Vec), } -/// ツール登録エラー +/// Tool registration error #[derive(Debug, thiserror::Error)] pub enum ToolRegistryError { - /// 同名のツールが既に登録されている + /// A tool with the same name is already registered #[error("Tool with name '{0}' already registered")] DuplicateName(String), } @@ -65,10 +65,10 @@ pub enum ToolRegistryError { // Worker Config // ============================================================================= -/// Worker設定 +/// Worker configuration #[derive(Debug, Clone, Default)] pub struct WorkerConfig { - // 将来の拡張用(現在は空) + // Reserved for future extensions (currently empty) _private: (), } @@ -76,26 +76,26 @@ pub struct WorkerConfig { // Worker Result Types // ============================================================================= -/// Workerの実行結果(ステータス) +/// Worker execution result (status) #[derive(Debug)] pub enum WorkerResult { - /// 完了(ユーザー入力待ち状態) + /// Completed (waiting for user input) Finished, - /// 一時停止(再開可能) + /// Paused (can be resumed) Paused, } -/// 内部用: ツール実行結果 +/// Internal: tool execution result enum ToolExecutionResult { Completed(Vec), Paused, } // ============================================================================= -// ターン制御用コールバック保持 +// Turn Control Callback Storage // ============================================================================= -/// ターンイベントを通知するためのコールバック (型消去) +/// Callback for notifying turn events (type-erased) trait TurnNotifier: Send + Sync { fn on_turn_start(&self, turn: usize); fn on_turn_end(&self, turn: usize); @@ -123,76 +123,76 @@ impl TurnNotifier for SubscriberTurnNotifier { // Worker // ============================================================================= -/// LLMとの対話を管理する中心コンポーネント +/// Central component for managing LLM interactions /// -/// ユーザーからの入力を受け取り、LLMにリクエストを送信し、 -/// ツール呼び出しがあれば自動的に実行してターンを進行させます。 +/// Receives input from the user, sends requests to the LLM, and +/// automatically executes tool calls if any, advancing the turn. /// -/// # 状態遷移(Type-state) +/// # State Transitions (Type-state) /// -/// - [`Mutable`]: 初期状態。システムプロンプトや履歴を自由に編集可能。 -/// - [`CacheLocked`]: キャッシュ保護状態。`lock()`で遷移。前方コンテキストは不変。 +/// - [`Mutable`]: Initial state. System prompt and history can be freely edited. +/// - [`CacheLocked`]: Cache-protected state. Transition via `lock()`. Prefix context is immutable. /// /// # Examples /// /// ```ignore /// use llm_worker::{Worker, Message}; /// -/// // Workerを作成してツールを登録 +/// // Create a Worker and register tools /// let mut worker = Worker::new(client) /// .system_prompt("You are a helpful assistant."); /// worker.register_tool(my_tool); /// -/// // 対話を実行 +/// // Run the interaction /// let history = worker.run("Hello!").await?; /// ``` /// -/// # キャッシュ保護が必要な場合 +/// # When Cache Protection is Needed /// /// ```ignore /// let mut worker = Worker::new(client) /// .system_prompt("..."); /// -/// // 履歴を設定後、ロックしてキャッシュを保護 +/// // After setting history, lock to protect cache /// let mut locked = worker.lock(); /// locked.run("user input").await?; /// ``` pub struct Worker { - /// LLMクライアント + /// LLM client client: C, - /// イベントタイムライン + /// Event timeline timeline: Timeline, - /// テキストブロックコレクター(Timeline用ハンドラ) + /// Text block collector (Timeline handler) text_block_collector: TextBlockCollector, - /// ツールコールコレクター(Timeline用ハンドラ) + /// Tool call collector (Timeline handler) tool_call_collector: ToolCallCollector, - /// 登録されたツール (meta, instance) + /// Registered tools (meta, instance) tools: HashMap)>, - /// Hook レジストリ + /// Hook registry hooks: HookRegistry, - /// システムプロンプト + /// System prompt system_prompt: Option, - /// メッセージ履歴(Workerが所有) + /// Message history (owned by Worker) history: Vec, - /// ロック時点での履歴長(CacheLocked状態でのみ意味を持つ) + /// History length at lock time (only meaningful in CacheLocked state) locked_prefix_len: usize, - /// ターンカウント + /// Turn count turn_count: usize, - /// ターン通知用のコールバック + /// Turn notification callbacks turn_notifiers: Vec>, - /// リクエスト設定(max_tokens, temperature等) + /// Request configuration (max_tokens, temperature, etc.) request_config: RequestConfig, - /// 前回の実行が中断されたかどうか + /// Whether the previous run was interrupted last_run_interrupted: bool, - /// キャンセル通知用チャネル(実行中断用) + /// Cancel notification channel (for interrupting execution) cancel_tx: mpsc::Sender<()>, cancel_rx: mpsc::Receiver<()>, - /// 状態マーカー + /// State marker _state: PhantomData, } // ============================================================================= -// 共通実装(全状態で利用可能) +// Common Implementation (available in all states) // ============================================================================= impl Worker { @@ -200,10 +200,10 @@ impl Worker { self.last_run_interrupted = false; } - /// ターンを実行 + /// Execute a turn /// - /// 新しいユーザーメッセージを履歴に追加し、LLMにリクエストを送信する。 - /// ツール呼び出しがある場合は自動的にループする。 + /// Adds a new user message to history and sends a request to the LLM. + /// Automatically loops if there are tool calls. pub async fn run( &mut self, user_input: impl Into, @@ -247,17 +247,17 @@ impl Worker { } } - /// イベント購読者を登録する + /// Register an event subscriber /// - /// 登録したSubscriberは、LLMからのストリーミングイベントを - /// リアルタイムで受信できます。UIへのストリーム表示などに利用します。 + /// Registered subscribers receive streaming events from the LLM + /// in real-time. Useful for streaming display to UI. /// - /// # 受信できるイベント + /// # Available Events /// - /// - **ブロックイベント**: `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` + /// - **Block events**: `on_text_block`, `on_tool_use_block` + /// - **Meta events**: `on_usage`, `on_status`, `on_error` + /// - **Completion events**: `on_text_complete`, `on_tool_call_complete` + /// - **Turn control**: `on_turn_start`, `on_turn_end` /// /// # Examples /// @@ -281,15 +281,15 @@ impl Worker { pub fn subscribe(&mut self, subscriber: Sub) { let subscriber = Arc::new(Mutex::new(subscriber)); - // TextBlock用ハンドラを登録 + // Register TextBlock handler self.timeline .on_text_block(TextBlockSubscriberAdapter::new(subscriber.clone())); - // ToolUseBlock用ハンドラを登録 + // Register ToolUseBlock handler self.timeline .on_tool_use_block(ToolUseBlockSubscriberAdapter::new(subscriber.clone())); - // Meta系ハンドラを登録 + // Register meta handlers self.timeline .on_usage(UsageSubscriberAdapter::new(subscriber.clone())); self.timeline @@ -297,15 +297,15 @@ impl Worker { self.timeline .on_error(ErrorSubscriberAdapter::new(subscriber.clone())); - // ターン制御用コールバックを登録 + // Register turn control callback self.turn_notifiers .push(Box::new(SubscriberTurnNotifier { subscriber })); } - /// ツールを登録する + /// Register a tool /// - /// 登録されたツールはLLMからの呼び出しで自動的に実行されます。 - /// 同名のツールを登録するとエラーになります。 + /// Registered tools are automatically executed when called by the LLM. + /// Registering a tool with the same name will result in an error. /// /// # Examples /// @@ -327,7 +327,7 @@ impl Worker { Ok(()) } - /// 複数のツールを登録 + /// Register multiple tools pub fn register_tools( &mut self, factories: impl IntoIterator, @@ -338,68 +338,68 @@ impl Worker { Ok(()) } - /// on_prompt_submit Hookを追加する + /// Add an on_prompt_submit Hook /// - /// `run()` でユーザーメッセージを受け取った直後に呼び出される。 + /// Called immediately after receiving a user message in `run()`. pub fn add_on_prompt_submit_hook(&mut self, hook: impl Hook + 'static) { self.hooks.on_prompt_submit.push(Box::new(hook)); } - /// pre_llm_request Hookを追加する + /// Add a pre_llm_request Hook /// - /// 各ターンのLLMリクエスト送信前に呼び出される。 + /// Called before sending an LLM request for each turn. pub fn add_pre_llm_request_hook(&mut self, hook: impl Hook + 'static) { self.hooks.pre_llm_request.push(Box::new(hook)); } - /// pre_tool_call Hookを追加する + /// Add a pre_tool_call Hook pub fn add_pre_tool_call_hook(&mut self, hook: impl Hook + 'static) { self.hooks.pre_tool_call.push(Box::new(hook)); } - /// post_tool_call Hookを追加する + /// Add a post_tool_call Hook pub fn add_post_tool_call_hook(&mut self, hook: impl Hook + 'static) { self.hooks.post_tool_call.push(Box::new(hook)); } - /// on_turn_end Hookを追加する + /// Add an on_turn_end Hook pub fn add_on_turn_end_hook(&mut self, hook: impl Hook + 'static) { self.hooks.on_turn_end.push(Box::new(hook)); } - /// on_abort Hookを追加する + /// Add an on_abort Hook pub fn add_on_abort_hook(&mut self, hook: impl Hook + 'static) { self.hooks.on_abort.push(Box::new(hook)); } - /// タイムラインへの可変参照を取得(追加ハンドラ登録用) + /// Get a mutable reference to the timeline (for additional handler registration) pub fn timeline_mut(&mut self) -> &mut Timeline { &mut self.timeline } - /// 履歴への参照を取得 + /// Get a reference to the history pub fn history(&self) -> &[Message] { &self.history } - /// システムプロンプトへの参照を取得 + /// Get a reference to the system prompt pub fn get_system_prompt(&self) -> Option<&str> { self.system_prompt.as_deref() } - /// 現在のターンカウントを取得 + /// Get the current turn count pub fn turn_count(&self) -> usize { self.turn_count } - /// 現在のリクエスト設定への参照を取得 + /// Get a reference to the current request configuration pub fn request_config(&self) -> &RequestConfig { &self.request_config } - /// 最大トークン数を設定 + /// Set maximum tokens /// - /// この設定はキャッシュロックとは独立しており、各リクエストに適用されます。 + /// This setting is independent of cache lock and applies to each request. /// /// # Examples /// @@ -410,10 +410,10 @@ impl Worker { self.request_config.max_tokens = Some(max_tokens); } - /// temperatureを設定 + /// Set temperature /// - /// 0.0から1.0(または2.0)の範囲で設定します。 - /// 低い値はより決定的な出力を、高い値はより多様な出力を生成します。 + /// Set in the range of 0.0 to 1.0 (or 2.0). + /// Lower values produce more deterministic output, higher values produce more diverse output. /// /// # Examples /// @@ -424,7 +424,7 @@ impl Worker { self.request_config.temperature = Some(temperature); } - /// top_pを設定(nucleus sampling) + /// Set top_p (nucleus sampling) /// /// # Examples /// @@ -435,9 +435,9 @@ impl Worker { self.request_config.top_p = Some(top_p); } - /// top_kを設定 + /// Set top_k /// - /// トークン選択時に考慮する上位k個のトークンを指定します。 + /// Specifies the top k tokens to consider when selecting tokens. /// /// # Examples /// @@ -448,7 +448,7 @@ impl Worker { self.request_config.top_k = Some(top_k); } - /// ストップシーケンスを追加 + /// Add a stop sequence /// /// # Examples /// @@ -459,25 +459,25 @@ impl Worker { self.request_config.stop_sequences.push(sequence.into()); } - /// ストップシーケンスをクリア + /// Clear stop sequences pub fn clear_stop_sequences(&mut self) { self.request_config.stop_sequences.clear(); } - /// キャンセル通知用Senderを取得する + /// Get the cancel notification sender pub fn cancel_sender(&self) -> mpsc::Sender<()> { self.cancel_tx.clone() } - /// リクエスト設定を一括で設定 + /// Set request configuration at once pub fn set_request_config(&mut self, config: RequestConfig) { self.request_config = config; } - /// 実行をキャンセルする + /// Cancel execution /// - /// 現在実行中のストリーミングやツール実行を中断します。 - /// 次のイベントループのチェックポイントでWorkerError::Cancelledが返されます。 + /// Interrupts currently running streaming or tool execution. + /// WorkerError::Cancelled is returned at the next event loop checkpoint. /// /// # Examples /// @@ -485,31 +485,31 @@ impl Worker { /// use std::sync::Arc; /// let worker = Arc::new(Mutex::new(Worker::new(client))); /// - /// // 別スレッドで実行 + /// // Run in another thread /// let worker_clone = worker.clone(); /// tokio::spawn(async move { /// let mut w = worker_clone.lock().unwrap(); /// w.run("Long task...").await /// }); /// - /// // キャンセル + /// // Cancel /// worker.lock().unwrap().cancel(); /// ``` pub fn cancel(&self) { let _ = self.cancel_tx.try_send(()); } - /// キャンセルされているかチェック + /// Check if cancelled pub fn is_cancelled(&mut self) -> bool { self.try_cancelled() } - /// 前回の実行が中断されたかどうか + /// Whether the previous run was interrupted pub fn last_run_interrupted(&self) -> bool { self.last_run_interrupted } - /// 登録されたツールからLLM用ToolDefinitionのリストを生成 + /// Generate list of ToolDefinitions for LLM from registered tools fn build_tool_definitions(&self) -> Vec { self.tools .values() @@ -521,34 +521,34 @@ impl Worker { .collect() } - /// テキストブロックとツール呼び出しからアシスタントメッセージを構築 + /// Build assistant message from text blocks and tool calls fn build_assistant_message( &self, text_blocks: &[String], tool_calls: &[ToolCall], ) -> Option { - // テキストもツール呼び出しもない場合はNone + // Return None if no text or tool calls if text_blocks.is_empty() && tool_calls.is_empty() { return None; } - // テキストのみの場合はシンプルなテキストメッセージ + // Simple text message if text only if tool_calls.is_empty() { let text = text_blocks.join(""); return Some(Message::assistant(text)); } - // ツール呼び出しがある場合は Parts として構築 + // Build as Parts if tool calls are present let mut parts = Vec::new(); - // テキストパーツを追加 + // Add text parts for text in text_blocks { if !text.is_empty() { parts.push(ContentPart::Text { text: text.clone() }); } } - // ツール呼び出しパーツを追加 + // Add tool call parts for call in tool_calls { parts.push(ContentPart::ToolUse { id: call.id.clone(), @@ -563,7 +563,7 @@ impl Worker { }) } - /// リクエストを構築 + /// Build a request fn build_request( &self, tool_definitions: &[LlmToolDefinition], @@ -571,14 +571,14 @@ impl Worker { ) -> Request { let mut request = Request::new(); - // システムプロンプトを設定 + // Set system prompt if let Some(ref system) = self.system_prompt { request = request.system(system); } - // メッセージを追加 + // Add messages for msg in context { - // Message から llm_client::Message への変換 + // Convert Message to llm_client::Message request = request.message(crate::llm_client::Message { role: match msg.role { Role::User => crate::llm_client::Role::User, @@ -621,12 +621,12 @@ impl Worker { }); } - // ツール定義を追加 + // Add tool definitions for tool_def in tool_definitions { request = request.tool(tool_def.clone()); } - // リクエスト設定を適用 + // Apply request configuration request = request.config(self.request_config.clone()); request @@ -634,7 +634,7 @@ impl Worker { /// Hooks: on_prompt_submit /// - /// `run()` でユーザーメッセージを受け取った直後に呼び出される(最初だけ)。 + /// Called immediately after receiving a user message in `run()` (first time only). async fn run_on_prompt_submit_hooks( &self, message: &mut Message, @@ -653,7 +653,7 @@ impl Worker { /// Hooks: pre_llm_request /// - /// 各ターンのLLMリクエスト送信前に呼び出される(毎ターン)。 + /// Called before sending an LLM request for each turn. async fn run_pre_llm_request_hooks( &self, ) -> Result<(PreLlmRequestResult, Vec), WorkerError> { @@ -717,7 +717,7 @@ impl Worker { } } - /// 未実行のツール呼び出しがあるかチェック(Pauseからの復帰用) + /// Check for pending tool calls (for resuming from Pause) fn get_pending_tool_calls(&self) -> Option> { let last_msg = self.history.last()?; if last_msg.role != Role::Assistant { @@ -740,26 +740,26 @@ impl Worker { if calls.is_empty() { None } else { Some(calls) } } - /// ツールを並列実行 + /// Execute tools in parallel /// - /// 全てのツールに対してpre_tool_callフックを実行後、 - /// 許可されたツールを並列に実行し、結果にpost_tool_callフックを適用する。 + /// After running pre_tool_call hooks on all tools, + /// executes approved tools in parallel and applies post_tool_call hooks to results. async fn execute_tools( &mut self, tool_calls: Vec, ) -> Result { use futures::future::join_all; - // ツール呼び出しIDから (ToolCall, Meta, Tool) へのマップ - // PostToolCallフックで必要になるため保持する + // Map from tool call ID to (ToolCall, Meta, Tool) + // Retained because it's needed for PostToolCall hooks let mut call_info_map = HashMap::new(); - // Phase 1: pre_tool_call フックを適用(スキップ/中断を判定) + // Phase 1: Apply pre_tool_call hooks (determine skip/abort) let mut approved_calls = Vec::new(); for mut tool_call in tool_calls { - // ツール定義を取得 + // Get tool definition if let Some((meta, tool)) = self.tools.get(&tool_call.name) { - // コンテキストを作成 + // Create context let mut context = ToolCallContext { call: tool_call.clone(), meta: meta.clone(), @@ -789,10 +789,10 @@ impl Worker { } } - // フックで変更された内容を反映 + // Reflect changes made by hooks tool_call = context.call; - // マップに保存(実行する場合のみ) + // Save to map (only if executing) if !skip { call_info_map.insert( tool_call.id.clone(), @@ -801,13 +801,13 @@ impl Worker { approved_calls.push(tool_call); } } else { - // 未知のツールはそのまま承認リストに入れる(実行時にエラーになる) - // Hookは適用しない(Metaがないため) + // Unknown tools go into approved list as-is (will error at execution) + // Hooks are not applied (no Meta available) approved_calls.push(tool_call); } } - // Phase 2: 許可されたツールを並列実行(キャンセル可能) + // Phase 2: Execute approved tools in parallel (cancellable) let futures: Vec<_> = approved_calls .into_iter() .map(|tool_call| { @@ -830,7 +830,7 @@ impl Worker { }) .collect(); - // ツール実行をキャンセル可能にする + // Make tool execution cancellable let mut results = tokio::select! { results = join_all(futures) => results, cancel = self.cancel_rx.recv() => { @@ -843,9 +843,9 @@ impl Worker { } }; - // Phase 3: post_tool_call フックを適用 + // Phase 3: Apply post_tool_call hooks for tool_result in &mut results { - // 保存しておいた情報を取得 + // Get saved information if let Some((tool_call, meta, tool)) = call_info_map.get(&tool_result.tool_use_id) { let mut context = PostToolCallContext { call: tool_call.clone(), @@ -867,7 +867,7 @@ impl Worker { } } } - // フックで変更された結果を反映 + // Reflect hook-modified results *tool_result = context.result; } } @@ -875,7 +875,7 @@ impl Worker { Ok(ToolExecutionResult::Completed(results)) } - /// 内部で使用するターン実行ロジック + /// Internal turn execution logic async fn run_turn_loop(&mut self) -> Result { self.reset_interruption_state(); self.drain_cancel_queue(); @@ -910,7 +910,7 @@ impl Worker { } loop { - // キャンセルチェック + // Check for cancellation if self.try_cancelled() { info!("Execution cancelled"); self.timeline.abort_current_block(); @@ -918,7 +918,7 @@ impl Worker { return Err(WorkerError::Cancelled); } - // ターン開始を通知 + // Notify turn start let current_turn = self.turn_count; debug!(turn = current_turn, "Turn start"); for notifier in &self.turn_notifiers { @@ -942,7 +942,7 @@ impl Worker { PreLlmRequestResult::Continue => {} } - // リクエスト構築 + // Build request let request = self.build_request(&tool_definitions, &request_context); debug!( message_count = request.messages.len(), @@ -951,11 +951,11 @@ impl Worker { "Sending request to LLM" ); - // ストリーム処理 + // Stream processing debug!("Starting stream..."); let mut event_count = 0; - // ストリームを取得(キャンセル可能) + // Get stream (cancellable) let mut stream = tokio::select! { stream_result = self.client.stream(request) => stream_result .inspect_err(|_| self.last_run_interrupted = true)?, @@ -971,7 +971,7 @@ impl Worker { loop { tokio::select! { - // ストリームからイベントを受信 + // Receive event from stream event_result = stream.next() => { match event_result { Some(result) => { @@ -989,10 +989,10 @@ impl Worker { let timeline_event: crate::timeline::event::Event = event.into(); self.timeline.dispatch(&timeline_event); } - None => break, // ストリーム終了 + None => break, // Stream ended } } - // キャンセル待機 + // Wait for cancellation cancel = self.cancel_rx.recv() => { if cancel.is_some() { info!("Stream cancelled"); @@ -1005,24 +1005,24 @@ impl Worker { } debug!(event_count = event_count, "Stream completed"); - // ターン終了を通知 + // Notify turn end for notifier in &self.turn_notifiers { notifier.on_turn_end(current_turn); } self.turn_count += 1; - // 収集結果を取得 + // Get collected results let text_blocks = self.text_block_collector.take_collected(); let tool_calls = self.tool_call_collector.take_collected(); - // アシスタントメッセージを履歴に追加 + // Add assistant message to history let assistant_message = self.build_assistant_message(&text_blocks, &tool_calls); if let Some(msg) = assistant_message { self.history.push(msg); } if tool_calls.is_empty() { - // ツール呼び出しなし → ターン終了判定 + // No tool calls → determine turn end let turn_result = self .run_on_turn_end_hooks() .await @@ -1043,7 +1043,7 @@ impl Worker { } } - // ツール実行 + // Execute tools match self.execute_tools(tool_calls).await { Ok(ToolExecutionResult::Paused) => { self.last_run_interrupted = true; @@ -1063,9 +1063,9 @@ impl Worker { } } - /// 実行を再開(Pause状態からの復帰) + /// Resume execution (from Paused state) /// - /// 新しいユーザーメッセージを履歴に追加せず、現在の状態からターン処理を再開する。 + /// Resumes turn processing from current state without adding a new user message to history. pub async fn resume(&mut self) -> Result { self.reset_interruption_state(); let result = self.run_turn_loop().await; @@ -1074,18 +1074,18 @@ impl Worker { } // ============================================================================= -// Mutable状態専用の実装 +// Mutable State-Specific Implementation // ============================================================================= impl Worker { - /// 新しいWorkerを作成(Mutable状態) + /// Create a new Worker (in Mutable state) pub fn new(client: C) -> Self { let text_block_collector = TextBlockCollector::new(); let tool_call_collector = ToolCallCollector::new(); let mut timeline = Timeline::new(); let (cancel_tx, cancel_rx) = mpsc::channel(1); - // コレクターをTimelineに登録 + // Register collectors with Timeline timeline.on_text_block(text_block_collector.clone()); timeline.on_tool_use_block(tool_call_collector.clone()); @@ -1109,18 +1109,18 @@ impl Worker { } } - /// システムプロンプトを設定(ビルダーパターン) + /// Set system prompt (builder pattern) pub fn system_prompt(mut self, prompt: impl Into) -> Self { self.system_prompt = Some(prompt.into()); self } - /// システムプロンプトを設定(可変参照版) + /// Set system prompt (mutable reference version) pub fn set_system_prompt(&mut self, prompt: impl Into) { self.system_prompt = Some(prompt.into()); } - /// 最大トークン数を設定(ビルダーパターン) + /// Set maximum tokens (builder pattern) /// /// # Examples /// @@ -1134,7 +1134,7 @@ impl Worker { self } - /// temperatureを設定(ビルダーパターン) + /// Set temperature (builder pattern) /// /// # Examples /// @@ -1147,25 +1147,25 @@ impl Worker { self } - /// top_pを設定(ビルダーパターン) + /// Set top_p (builder pattern) pub fn top_p(mut self, top_p: f32) -> Self { self.request_config.top_p = Some(top_p); self } - /// top_kを設定(ビルダーパターン) + /// Set top_k (builder pattern) pub fn top_k(mut self, top_k: u32) -> Self { self.request_config.top_k = Some(top_k); self } - /// ストップシーケンスを追加(ビルダーパターン) + /// Add stop sequence (builder pattern) pub fn stop_sequence(mut self, sequence: impl Into) -> Self { self.request_config.stop_sequences.push(sequence.into()); self } - /// リクエスト設定をまとめて設定(ビルダーパターン) + /// Set request configuration at once (builder pattern) /// /// # Examples /// @@ -1183,10 +1183,10 @@ impl Worker { self } - /// 現在の設定をプロバイダに対してバリデーションする + /// Validate current configuration against the provider /// - /// 未サポートの設定があればエラーを返す。 - /// チェーンの最後で呼び出すことで、設定の問題を早期に検出できる。 + /// Returns an error if there are unsupported settings. + /// Call at the end of the chain to detect configuration issues early. /// /// # Examples /// @@ -1194,12 +1194,12 @@ impl Worker { /// let worker = Worker::new(client) /// .temperature(0.7) /// .top_k(40) - /// .validate()?; // OpenAIならtop_kがサポートされないためエラー + /// .validate()?; // Error if using OpenAI since top_k is not supported /// ``` /// /// # Returns - /// * `Ok(Self)` - バリデーション成功 - /// * `Err(WorkerError::ConfigWarnings)` - 未サポートの設定がある + /// * `Ok(Self)` - Validation successful + /// * `Err(WorkerError::ConfigWarnings)` - Has unsupported settings pub fn validate(self) -> Result { let warnings = self.client.validate_config(&self.request_config); if warnings.is_empty() { @@ -1209,55 +1209,55 @@ impl Worker { } } - /// 履歴への可変参照を取得 + /// Get a mutable reference to history /// - /// Mutable状態でのみ利用可能。履歴を自由に編集できる。 + /// Available only in Mutable state. History can be freely edited. pub fn history_mut(&mut self) -> &mut Vec { &mut self.history } - /// 履歴を設定 + /// Set history pub fn set_history(&mut self, messages: Vec) { self.history = messages; } - /// 履歴にメッセージを追加(ビルダーパターン) + /// Add a message to history (builder pattern) pub fn with_message(mut self, message: Message) -> Self { self.history.push(message); self } - /// 履歴にメッセージを追加 + /// Add a message to history pub fn push_message(&mut self, message: Message) { self.history.push(message); } - /// 複数のメッセージを履歴に追加(ビルダーパターン) + /// Add multiple messages to history (builder pattern) pub fn with_messages(mut self, messages: impl IntoIterator) -> Self { self.history.extend(messages); self } - /// 複数のメッセージを履歴に追加 + /// Add multiple messages to history pub fn extend_history(&mut self, messages: impl IntoIterator) { self.history.extend(messages); } - /// 履歴をクリア + /// Clear history pub fn clear_history(&mut self) { self.history.clear(); } - /// 設定を適用(将来の拡張用) + /// Apply configuration (reserved for future extensions) #[allow(dead_code)] pub fn config(self, _config: WorkerConfig) -> Self { self } - /// ロックしてCacheLocked状態へ遷移 + /// Lock and transition to CacheLocked state /// - /// この操作により、現在のシステムプロンプトと履歴が「確定済みプレフィックス」として - /// 固定される。以降は履歴への追記のみが可能となり、キャッシュヒットが保証される。 + /// This operation fixes the current system prompt and history as a "committed prefix". + /// After this, only appending to history is allowed, ensuring cache hits. pub fn lock(self) -> Worker { let locked_prefix_len = self.history.len(); Worker { @@ -1283,19 +1283,19 @@ impl Worker { } // ============================================================================= -// CacheLocked状態専用の実装 +// CacheLocked State-Specific Implementation // ============================================================================= impl Worker { - /// ロック時点のプレフィックス長を取得 + /// Get the prefix length at lock time pub fn locked_prefix_len(&self) -> usize { self.locked_prefix_len } - /// ロックを解除してMutable状態へ戻す + /// Unlock and return to Mutable state /// - /// 注意: この操作を行うと、以降のリクエストでキャッシュがヒットしなくなる可能性がある。 - /// 履歴を編集する必要がある場合にのみ使用すること。 + /// Note: After this operation, subsequent requests may not hit the cache. + /// Use only when you need to edit history. pub fn unlock(self) -> Worker { Worker { client: self.client, @@ -1320,5 +1320,5 @@ impl Worker { #[cfg(test)] mod tests { - // 基本的なテストのみ。LlmClientを使ったテストは統合テストで行う。 + // Basic tests only. Tests using LlmClient are done in integration tests. } diff --git a/llm-worker/tests/anthropic_fixtures.rs b/llm-worker/tests/anthropic_fixtures.rs index 8383a43..e84e2bf 100644 --- a/llm-worker/tests/anthropic_fixtures.rs +++ b/llm-worker/tests/anthropic_fixtures.rs @@ -1,4 +1,4 @@ -//! Anthropic フィクスチャベースの統合テスト +//! Anthropic fixture-based integration tests mod common; diff --git a/llm-worker/tests/gemini_fixtures.rs b/llm-worker/tests/gemini_fixtures.rs index b23bdee..4a2c3bd 100644 --- a/llm-worker/tests/gemini_fixtures.rs +++ b/llm-worker/tests/gemini_fixtures.rs @@ -1,4 +1,4 @@ -//! Gemini フィクスチャベースの統合テスト +//! Gemini fixture-based integration tests mod common; diff --git a/llm-worker/tests/ollama_fixtures.rs b/llm-worker/tests/ollama_fixtures.rs index d145195..d418bfc 100644 --- a/llm-worker/tests/ollama_fixtures.rs +++ b/llm-worker/tests/ollama_fixtures.rs @@ -1,4 +1,4 @@ -//! Ollama フィクスチャベースの統合テスト +//! Ollama fixture-based integration tests mod common; diff --git a/llm-worker/tests/openai_fixtures.rs b/llm-worker/tests/openai_fixtures.rs index 42289b6..d233d00 100644 --- a/llm-worker/tests/openai_fixtures.rs +++ b/llm-worker/tests/openai_fixtures.rs @@ -1,4 +1,4 @@ -//! OpenAI フィクスチャベースの統合テスト +//! OpenAI fixture-based integration tests mod common; diff --git a/llm-worker/tests/parallel_execution_test.rs b/llm-worker/tests/parallel_execution_test.rs index f3c2769..ccb9872 100644 --- a/llm-worker/tests/parallel_execution_test.rs +++ b/llm-worker/tests/parallel_execution_test.rs @@ -1,6 +1,6 @@ -//! 並列ツール実行のテスト +//! Parallel tool execution tests //! -//! Workerが複数のツールを並列に実行することを確認する。 +//! Verify that Worker executes multiple tools in parallel. use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -22,7 +22,7 @@ use common::MockLlmClient; // Parallel Execution Test Tools // ============================================================================= -/// 一定時間待機してから応答するツール +/// Tool that waits for a specified time before responding #[derive(Clone)] struct SlowTool { name: String, @@ -43,7 +43,7 @@ impl SlowTool { self.call_count.load(Ordering::SeqCst) } - /// ToolDefinition を作成 + /// Create ToolDefinition fn definition(&self) -> ToolDefinition { let tool = self.clone(); Arc::new(move || { @@ -71,13 +71,13 @@ impl Tool for SlowTool { // Tests // ============================================================================= -/// 複数のツールが並列に実行されることを確認 +/// Verify that multiple tools are executed in parallel /// -/// 各ツールが100msかかる場合、逐次実行なら300ms以上かかるが、 -/// 並列実行なら100ms程度で完了するはず。 +/// If each tool takes 100ms, sequential execution would take 300ms+, +/// but parallel execution should complete in about 100ms. #[tokio::test] async fn test_parallel_tool_execution() { - // 3つのツール呼び出しを含むイベントシーケンス + // Event sequence containing 3 tool calls let events = vec![ Event::tool_use_start(0, "call_1", "slow_tool_1"), Event::tool_input_delta(0, r#"{}"#), @@ -96,7 +96,7 @@ async fn test_parallel_tool_execution() { let client = MockLlmClient::new(events); let mut worker = Worker::new(client); - // 各ツールは100ms待機 + // Each tool waits 100ms let tool1 = SlowTool::new("slow_tool_1", 100); let tool2 = SlowTool::new("slow_tool_2", 100); let tool3 = SlowTool::new("slow_tool_3", 100); @@ -113,13 +113,13 @@ async fn test_parallel_tool_execution() { let _result = worker.run("Run all tools").await; let elapsed = start.elapsed(); - // 全ツールが呼び出されたことを確認 + // Verify all tools were called assert_eq!(tool1_clone.call_count(), 1, "Tool 1 should be called once"); assert_eq!(tool2_clone.call_count(), 1, "Tool 2 should be called once"); assert_eq!(tool3_clone.call_count(), 1, "Tool 3 should be called once"); - // 並列実行なら200ms以下で完了するはず(逐次なら300ms以上) - // マージン込みで250msをしきい値とする + // Parallel execution should complete in under 200ms (sequential would be 300ms+) + // Using 250ms as threshold with margin assert!( elapsed < Duration::from_millis(250), "Parallel execution should complete in ~100ms, but took {:?}", @@ -129,7 +129,7 @@ async fn test_parallel_tool_execution() { println!("Parallel execution completed in {:?}", elapsed); } -/// Hook: pre_tool_call でスキップされたツールは実行されないことを確認 +/// Hook: pre_tool_call - verify that skipped tools are not executed #[tokio::test] async fn test_before_tool_call_skip() { let events = vec![ @@ -156,7 +156,7 @@ async fn test_before_tool_call_skip() { worker.register_tool(allowed_tool.definition()).unwrap(); worker.register_tool(blocked_tool.definition()).unwrap(); - // "blocked_tool" をスキップするHook + // Hook to skip "blocked_tool" struct BlockingHook; #[async_trait] @@ -174,7 +174,7 @@ async fn test_before_tool_call_skip() { let _result = worker.run("Test hook").await; - // allowed_tool は呼び出されるが、blocked_tool は呼び出されない + // allowed_tool is called, but blocked_tool is not assert_eq!( allowed_clone.call_count(), 1, @@ -187,12 +187,12 @@ async fn test_before_tool_call_skip() { ); } -/// Hook: post_tool_call で結果が改変されることを確認 +/// Hook: post_tool_call - verify that results can be modified #[tokio::test] async fn test_post_tool_call_modification() { - // 複数リクエストに対応するレスポンスを準備 + // Prepare responses for multiple requests let client = MockLlmClient::with_responses(vec![ - // 1回目のリクエスト: ツール呼び出し + // First request: tool call vec![ Event::tool_use_start(0, "call_1", "test_tool"), Event::tool_input_delta(0, r#"{}"#), @@ -201,7 +201,7 @@ async fn test_post_tool_call_modification() { status: ResponseStatus::Completed, }), ], - // 2回目のリクエスト: ツール結果を受けてテキストレスポンス + // Second request: text response after receiving tool result vec![ Event::text_block_start(0), Event::text_delta(0, "Done!"), @@ -235,7 +235,7 @@ async fn test_post_tool_call_modification() { worker.register_tool(simple_tool_definition()).unwrap(); - // 結果を改変するHook + // Hook to modify results struct ModifyingHook { modified_content: Arc>>, } @@ -261,7 +261,7 @@ async fn test_post_tool_call_modification() { assert!(result.is_ok(), "Worker should complete: {:?}", result); - // Hookが呼ばれて内容が改変されたことを確認 + // Verify hook was called and content was modified let content = modified_content.lock().unwrap().clone(); assert!(content.is_some(), "Hook should have been called"); assert!( diff --git a/llm-worker/tests/subscriber_test.rs b/llm-worker/tests/subscriber_test.rs index efb8ad2..552aa04 100644 --- a/llm-worker/tests/subscriber_test.rs +++ b/llm-worker/tests/subscriber_test.rs @@ -1,6 +1,6 @@ -//! WorkerSubscriberのテスト +//! WorkerSubscriber tests //! -//! WorkerSubscriberを使ってイベントを購読するテスト +//! Tests for subscribing to events using WorkerSubscriber mod common; @@ -18,9 +18,9 @@ use llm_worker::timeline::{TextBlockEvent, ToolUseBlockEvent}; // Test Subscriber // ============================================================================= -/// テスト用のシンプルなSubscriber実装 +/// Simple Subscriber implementation for testing struct TestSubscriber { - // 記録用のバッファ + // Recording buffers text_deltas: Arc>>, text_completes: Arc>>, tool_call_completes: Arc>>, @@ -60,7 +60,7 @@ impl WorkerSubscriber for TestSubscriber { } fn on_tool_use_block(&mut self, _scope: &mut (), _event: &ToolUseBlockEvent) { - // 必要に応じて処理 + // Process as needed } fn on_tool_call_complete(&mut self, call: &ToolCall) { @@ -76,7 +76,7 @@ impl WorkerSubscriber for TestSubscriber { } fn on_error(&mut self, _event: &ErrorEvent) { - // 必要に応じて処理 + // Process as needed } fn on_turn_start(&mut self, turn: usize) { @@ -92,10 +92,10 @@ impl WorkerSubscriber for TestSubscriber { // Tests // ============================================================================= -/// WorkerSubscriberがテキストブロックイベントを正しく受け取ることを確認 +/// Verify that WorkerSubscriber correctly receives text block events #[tokio::test] async fn test_subscriber_text_block_events() { - // テキストレスポンスを含むイベントシーケンス + // Event sequence containing text response let events = vec![ Event::text_block_start(0), Event::text_delta(0, "Hello, "), @@ -109,33 +109,33 @@ async fn test_subscriber_text_block_events() { let client = MockLlmClient::new(events); let mut worker = Worker::new(client); - // Subscriberを登録 + // Register Subscriber let subscriber = TestSubscriber::new(); let text_deltas = subscriber.text_deltas.clone(); let text_completes = subscriber.text_completes.clone(); worker.subscribe(subscriber); - // 実行 + // Execute let result = worker.run("Greet me").await; assert!(result.is_ok(), "Worker should complete: {:?}", result); - // デルタが収集されていることを確認 + // Verify deltas were collected let deltas = text_deltas.lock().unwrap(); assert_eq!(deltas.len(), 2); assert_eq!(deltas[0], "Hello, "); assert_eq!(deltas[1], "World!"); - // 完了テキストが収集されていることを確認 + // Verify complete text was collected let completes = text_completes.lock().unwrap(); assert_eq!(completes.len(), 1); assert_eq!(completes[0], "Hello, World!"); } -/// WorkerSubscriberがツール呼び出し完了イベントを正しく受け取ることを確認 +/// Verify that WorkerSubscriber correctly receives tool call complete events #[tokio::test] async fn test_subscriber_tool_call_complete() { - // ツール呼び出しを含むイベントシーケンス + // Event sequence containing tool call let events = vec![ Event::tool_use_start(0, "call_123", "get_weather"), Event::tool_input_delta(0, r#"{"city":"#), @@ -149,15 +149,15 @@ async fn test_subscriber_tool_call_complete() { let client = MockLlmClient::new(events); let mut worker = Worker::new(client); - // Subscriberを登録 + // Register Subscriber let subscriber = TestSubscriber::new(); let tool_call_completes = subscriber.tool_call_completes.clone(); worker.subscribe(subscriber); - // 実行 + // Execute let _ = worker.run("Weather please").await; - // ツール呼び出し完了が収集されていることを確認 + // Verify tool call complete was collected let completes = tool_call_completes.lock().unwrap(); assert_eq!(completes.len(), 1); assert_eq!(completes[0].name, "get_weather"); @@ -165,7 +165,7 @@ async fn test_subscriber_tool_call_complete() { assert_eq!(completes[0].input["city"], "Tokyo"); } -/// WorkerSubscriberがターンイベントを正しく受け取ることを確認 +/// Verify that WorkerSubscriber correctly receives turn events #[tokio::test] async fn test_subscriber_turn_events() { let events = vec![ @@ -180,29 +180,29 @@ async fn test_subscriber_turn_events() { let client = MockLlmClient::new(events); let mut worker = Worker::new(client); - // Subscriberを登録 + // Register Subscriber let subscriber = TestSubscriber::new(); let turn_starts = subscriber.turn_starts.clone(); let turn_ends = subscriber.turn_ends.clone(); worker.subscribe(subscriber); - // 実行 + // Execute let result = worker.run("Do something").await; assert!(result.is_ok()); - // ターンイベントが収集されていることを確認 + // Verify turn events were collected let starts = turn_starts.lock().unwrap(); let ends = turn_ends.lock().unwrap(); assert_eq!(starts.len(), 1); - assert_eq!(starts[0], 0); // 最初のターン + assert_eq!(starts[0], 0); // First turn assert_eq!(ends.len(), 1); assert_eq!(ends[0], 0); } -/// WorkerSubscriberがUsageイベントを正しく受け取ることを確認 +/// Verify that WorkerSubscriber correctly receives Usage events #[tokio::test] async fn test_subscriber_usage_events() { let events = vec![ @@ -218,15 +218,15 @@ async fn test_subscriber_usage_events() { let client = MockLlmClient::new(events); let mut worker = Worker::new(client); - // Subscriberを登録 + // Register Subscriber let subscriber = TestSubscriber::new(); let usage_events = subscriber.usage_events.clone(); worker.subscribe(subscriber); - // 実行 + // Execute let _ = worker.run("Hello").await; - // Usageイベントが収集されていることを確認 + // Verify Usage events were collected let usages = usage_events.lock().unwrap(); assert_eq!(usages.len(), 1); assert_eq!(usages[0].input_tokens, Some(100)); diff --git a/llm-worker/tests/tool_macro_test.rs b/llm-worker/tests/tool_macro_test.rs index 4676852..873d46c 100644 --- a/llm-worker/tests/tool_macro_test.rs +++ b/llm-worker/tests/tool_macro_test.rs @@ -1,11 +1,11 @@ -//! ツールマクロのテスト +//! Tool macro tests //! -//! `#[tool_registry]` と `#[tool]` マクロの動作を確認する。 +//! Verify the behavior of `#[tool_registry]` and `#[tool]` macros. use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; -// マクロ展開に必要なインポート +// Imports needed for macro expansion use schemars; use serde; @@ -15,7 +15,7 @@ use llm_worker_macros::tool_registry; // Test: Basic Tool Generation // ============================================================================= -/// シンプルなコンテキスト構造体 +/// Simple context struct #[derive(Clone)] struct SimpleContext { prefix: String, @@ -23,21 +23,21 @@ struct SimpleContext { #[tool_registry] impl SimpleContext { - /// メッセージに挨拶を追加する + /// Add greeting to message /// - /// 指定されたメッセージにプレフィックスを付けて返します。 + /// Returns the message with a prefix added. #[tool] async fn greet(&self, message: String) -> String { format!("{}: {}", self.prefix, message) } - /// 二つの数を足す + /// Add two numbers #[tool] async fn add(&self, a: i32, b: i32) -> i32 { a + b } - /// 引数なしのツール + /// Tool with no arguments #[tool] async fn get_prefix(&self) -> String { self.prefix.clone() @@ -50,16 +50,16 @@ async fn test_basic_tool_generation() { prefix: "Hello".to_string(), }; - // ファクトリメソッドでToolDefinitionを取得 + // Get ToolDefinition from factory method let greet_definition = ctx.greet_definition(); - // ファクトリを呼び出してMetaとToolを取得 + // Call factory to get Meta and Tool let (meta, tool) = greet_definition(); - // メタ情報の確認 + // Verify meta information assert_eq!(meta.name, "greet"); assert!( - meta.description.contains("メッセージに挨拶を追加する"), + meta.description.contains("Add greeting to message"), "Description should contain doc comment: {}", meta.description ); @@ -73,7 +73,7 @@ async fn test_basic_tool_generation() { serde_json::to_string_pretty(&meta.input_schema).unwrap() ); - // 実行テスト + // Execution test let result = tool.execute(r#"{"message": "World"}"#).await; assert!(result.is_ok(), "Should execute successfully"); let output = result.unwrap(); @@ -107,7 +107,7 @@ async fn test_no_arguments() { assert_eq!(meta.name, "get_prefix"); - // 空のJSONオブジェクトで呼び出し + // Call with empty JSON object let result = tool.execute(r#"{}"#).await; assert!(result.is_ok()); let output = result.unwrap(); @@ -126,7 +126,7 @@ async fn test_invalid_arguments() { let (_, tool) = ctx.greet_definition()(); - // 不正なJSON + // Invalid JSON let result = tool.execute(r#"{"wrong_field": "value"}"#).await; assert!(result.is_err(), "Should fail with invalid arguments"); } @@ -149,7 +149,7 @@ impl std::fmt::Display for MyError { #[tool_registry] impl FallibleContext { - /// 与えられた値を検証する + /// Validate the given value #[tool] async fn validate(&self, value: i32) -> Result { if value > 0 { @@ -198,7 +198,7 @@ struct SyncContext { #[tool_registry] impl SyncContext { - /// カウンターをインクリメントして返す (非async) + /// Increment counter and return (non-async) #[tool] fn increment(&self) -> usize { self.counter.fetch_add(1, Ordering::SeqCst) + 1 @@ -213,7 +213,7 @@ async fn test_sync_method() { let (_, tool) = ctx.increment_definition()(); - // 3回実行 + // Execute 3 times let result1 = tool.execute(r#"{}"#).await; let result2 = tool.execute(r#"{}"#).await; let result3 = tool.execute(r#"{}"#).await; @@ -222,7 +222,7 @@ async fn test_sync_method() { assert!(result2.is_ok()); assert!(result3.is_ok()); - // カウンターは3になっているはず + // Counter should be 3 assert_eq!(ctx.counter.load(Ordering::SeqCst), 3); } @@ -236,7 +236,7 @@ async fn test_tool_meta_immutability() { prefix: "Test".to_string(), }; - // 2回取得しても同じメタ情報が得られることを確認 + // Verify same meta info is returned on multiple calls let (meta1, _) = ctx.greet_definition()(); let (meta2, _) = ctx.greet_definition()(); diff --git a/llm-worker/tests/validation_test.rs b/llm-worker/tests/validation_test.rs index 71e08e7..671140b 100644 --- a/llm-worker/tests/validation_test.rs +++ b/llm-worker/tests/validation_test.rs @@ -3,16 +3,16 @@ use llm_worker::{Worker, WorkerError}; #[test] fn test_openai_top_k_warning() { - // ダミーキーでクライアント作成(validate_configは通信しないため安全) + // Create client with dummy key (validate_config doesn't make network calls, so safe) let client = OpenAIClient::new("dummy-key", "gpt-4o"); - // top_kを設定したWorkerを作成 - let worker = Worker::new(client).top_k(50); // OpenAIはtop_k非対応 + // Create Worker with top_k set (OpenAI doesn't support top_k) + let worker = Worker::new(client).top_k(50); - // validate()を実行 + // Run validate() let result = worker.validate(); - // エラーが返り、ConfigWarningsが含まれていることを確認 + // Verify error is returned and ConfigWarnings is included match result { Err(WorkerError::ConfigWarnings(warnings)) => { assert_eq!(warnings.len(), 1); @@ -28,12 +28,12 @@ fn test_openai_top_k_warning() { fn test_openai_valid_config() { let client = OpenAIClient::new("dummy-key", "gpt-4o"); - // validな設定(temperatureのみ) + // Valid configuration (temperature only) let worker = Worker::new(client).temperature(0.7); - // validate()を実行 + // Run validate() let result = worker.validate(); - // 成功を確認 + // Verify success assert!(result.is_ok()); } diff --git a/llm-worker/tests/worker_fixtures.rs b/llm-worker/tests/worker_fixtures.rs index ce777fa..6f28752 100644 --- a/llm-worker/tests/worker_fixtures.rs +++ b/llm-worker/tests/worker_fixtures.rs @@ -1,7 +1,7 @@ -//! Workerフィクスチャベースの統合テスト +//! Worker fixture-based integration tests //! -//! 記録されたAPIレスポンスを使ってWorkerの動作をテストする。 -//! APIキー不要でローカルで実行可能。 +//! Tests Worker behavior using recorded API responses. +//! Can run locally without API keys. mod common; @@ -14,12 +14,12 @@ use common::MockLlmClient; use llm_worker::Worker; use llm_worker::tool::{Tool, ToolDefinition, ToolError, ToolMeta}; -/// フィクスチャディレクトリのパス +/// Fixture directory path fn fixtures_dir() -> std::path::PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/anthropic") } -/// シンプルなテスト用ツール +/// Simple test tool #[derive(Clone)] struct MockWeatherTool { call_count: Arc, @@ -61,13 +61,13 @@ impl Tool for MockWeatherTool { async fn execute(&self, input_json: &str) -> Result { self.call_count.fetch_add(1, Ordering::SeqCst); - // 入力をパース + // Parse input let input: serde_json::Value = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(e.to_string()))?; let city = input["city"].as_str().unwrap_or("Unknown"); - // モックのレスポンスを返す + // Return mock response Ok(format!("Weather in {}: Sunny, 22°C", city)) } } @@ -76,12 +76,12 @@ impl Tool for MockWeatherTool { // Basic Fixture Tests // ============================================================================= -/// MockLlmClientがJSONLフィクスチャファイルから正しくイベントをロードできることを確認 +/// Verify that MockLlmClient can correctly load events from JSONL fixture files /// -/// 既存のanthropic_*.jsonlファイルを使用し、イベントがパース・ロードされることを検証する。 +/// Uses existing anthropic_*.jsonl files to verify events are parsed and loaded. #[test] fn test_mock_client_from_fixture() { - // 既存のフィクスチャをロード + // Load existing fixture let fixture_path = fixtures_dir().join("anthropic_1767624445.jsonl"); if !fixture_path.exists() { println!("Fixture not found, skipping test"); @@ -93,14 +93,14 @@ fn test_mock_client_from_fixture() { println!("Loaded {} events from fixture", client.event_count()); } -/// MockLlmClientが直接指定されたイベントリストで正しく動作することを確認 +/// Verify that MockLlmClient works correctly with directly specified event lists /// -/// fixtureファイルを使わず、プログラムでイベントを構築してクライアントを作成する。 +/// Creates a client with programmatically constructed events instead of using fixture files. #[test] fn test_mock_client_from_events() { use llm_worker::llm_client::event::Event; - // 直接イベントを指定 + // Specify events directly let events = vec![ Event::text_block_start(0), Event::text_delta(0, "Hello!"), @@ -115,10 +115,10 @@ fn test_mock_client_from_events() { // Worker Tests with Fixtures // ============================================================================= -/// Workerがシンプルなテキストレスポンスを正しく処理できることを確認 +/// Verify that Worker can correctly process simple text responses /// -/// simple_text.jsonlフィクスチャを使用し、ツール呼び出しなしのシナリオをテストする。 -/// フィクスチャがない場合はスキップされる。 +/// Uses simple_text.jsonl fixture to test scenarios without tool calls. +/// Skipped if fixture is not present. #[tokio::test] async fn test_worker_simple_text_response() { let fixture_path = fixtures_dir().join("simple_text.jsonl"); @@ -131,16 +131,16 @@ async fn test_worker_simple_text_response() { let client = MockLlmClient::from_fixture(&fixture_path).unwrap(); let mut worker = Worker::new(client); - // シンプルなメッセージを送信 + // Send a simple message let result = worker.run("Hello").await; assert!(result.is_ok(), "Worker should complete successfully"); } -/// Workerがツール呼び出しを含むレスポンスを正しく処理できることを確認 +/// Verify that Worker can correctly process responses containing tool calls /// -/// tool_call.jsonlフィクスチャを使用し、MockWeatherToolが呼び出されることをテストする。 -/// max_turns=1に設定し、ツール実行後のループを防止。 +/// Uses tool_call.jsonl fixture to test that MockWeatherTool is called. +/// Sets max_turns=1 to prevent loop after tool execution. #[tokio::test] async fn test_worker_tool_call() { let fixture_path = fixtures_dir().join("tool_call.jsonl"); @@ -153,32 +153,32 @@ async fn test_worker_tool_call() { let client = MockLlmClient::from_fixture(&fixture_path).unwrap(); let mut worker = Worker::new(client); - // ツールを登録 + // Register tool let weather_tool = MockWeatherTool::new(); let tool_for_check = weather_tool.clone(); worker.register_tool(weather_tool.definition()).unwrap(); - // メッセージを送信 + // Send message let _result = worker.run("What's the weather in Tokyo?").await; - // ツールが呼び出されたことを確認 - // Note: max_turns=1なのでツール結果後のリクエストは送信されない + // Verify tool was called + // Note: max_turns=1 so no request is sent after tool result let call_count = tool_for_check.get_call_count(); println!("Tool was called {} times", call_count); - // フィクスチャにToolUseが含まれていればツールが呼び出されるはず - // ただしmax_turns=1なので1回で終了 + // Tool should be called if fixture contains ToolUse + // But ends after 1 turn due to max_turns=1 } -/// fixtureファイルなしでWorkerが動作することを確認 +/// Verify that Worker works without fixture files /// -/// プログラムでイベントシーケンスを構築し、MockLlmClientに渡してテストする。 -/// テストの独立性を高め、外部ファイルへの依存を排除したい場合に有用。 +/// Constructs event sequence programmatically and passes to MockLlmClient. +/// Useful when test independence is needed and external file dependency should be eliminated. #[tokio::test] async fn test_worker_with_programmatic_events() { use llm_worker::llm_client::event::{Event, ResponseStatus, StatusEvent}; - // プログラムでイベントシーケンスを構築 + // Construct event sequence programmatically let events = vec![ Event::text_block_start(0), Event::text_delta(0, "Hello, "), @@ -197,16 +197,16 @@ async fn test_worker_with_programmatic_events() { assert!(result.is_ok(), "Worker should complete successfully"); } -/// ToolCallCollectorがToolUseブロックイベントから正しくToolCallを収集することを確認 +/// Verify that ToolCallCollector correctly collects ToolCall from ToolUse block events /// -/// Timelineにイベントをディスパッチし、ToolCallCollectorが -/// id, name, input(JSON)を正しく抽出できることを検証する。 +/// Dispatches events to Timeline and verifies ToolCallCollector +/// correctly extracts id, name, and input (JSON). #[tokio::test] async fn test_tool_call_collector_integration() { use llm_worker::llm_client::event::Event; use llm_worker::timeline::{Timeline, ToolCallCollector}; - // ToolUseブロックを含むイベントシーケンス + // Event sequence containing ToolUse block let events = vec![ Event::tool_use_start(0, "call_123", "get_weather"), Event::tool_input_delta(0, r#"{"city":"#), @@ -218,13 +218,13 @@ async fn test_tool_call_collector_integration() { let mut timeline = Timeline::new(); timeline.on_tool_use_block(collector.clone()); - // イベントをディスパッチ + // Dispatch events for event in &events { let timeline_event: llm_worker::timeline::event::Event = event.clone().into(); timeline.dispatch(&timeline_event); } - // 収集されたToolCallを確認 + // Verify collected ToolCall let calls = collector.take_collected(); assert_eq!(calls.len(), 1, "Should collect one tool call"); assert_eq!(calls[0].name, "get_weather"); diff --git a/llm-worker/tests/worker_state_test.rs b/llm-worker/tests/worker_state_test.rs index e98135c..0ab3b04 100644 --- a/llm-worker/tests/worker_state_test.rs +++ b/llm-worker/tests/worker_state_test.rs @@ -1,7 +1,7 @@ -//! Worker状態管理のテスト +//! Worker state management tests //! -//! Type-stateパターン(Mutable/CacheLocked)による状態遷移と -//! ターン間の状態保持をテストする。 +//! Tests for state transitions using the Type-state pattern (Mutable/CacheLocked) +//! and state preservation between turns. mod common; @@ -11,10 +11,10 @@ use llm_worker::llm_client::event::{Event, ResponseStatus, StatusEvent}; use llm_worker::{Message, MessageContent}; // ============================================================================= -// Mutable状態のテスト +// Mutable State Tests // ============================================================================= -/// Mutable状態でシステムプロンプトを設定できることを確認 +/// Verify that system prompt can be set in Mutable state #[test] fn test_mutable_set_system_prompt() { let client = MockLlmClient::new(vec![]); @@ -29,35 +29,35 @@ fn test_mutable_set_system_prompt() { ); } -/// Mutable状態で履歴を自由に編集できることを確認 +/// Verify that history can be freely edited in Mutable state #[test] fn test_mutable_history_manipulation() { let client = MockLlmClient::new(vec![]); let mut worker = Worker::new(client); - // 初期状態は空 + // Initial state is empty assert!(worker.history().is_empty()); - // 履歴を追加 + // Add to history worker.push_message(Message::user("Hello")); worker.push_message(Message::assistant("Hi there!")); assert_eq!(worker.history().len(), 2); - // 履歴への可変アクセス + // Mutable access to history worker.history_mut().push(Message::user("How are you?")); assert_eq!(worker.history().len(), 3); - // 履歴をクリア + // Clear history worker.clear_history(); assert!(worker.history().is_empty()); - // 履歴を設定 + // Set history let messages = vec![Message::user("Test"), Message::assistant("Response")]; worker.set_history(messages); assert_eq!(worker.history().len(), 2); } -/// ビルダーパターンでWorkerを構築できることを確認 +/// Verify that Worker can be constructed using builder pattern #[test] fn test_mutable_builder_pattern() { let client = MockLlmClient::new(vec![]); @@ -74,7 +74,7 @@ fn test_mutable_builder_pattern() { assert_eq!(worker.history().len(), 4); } -/// extend_historyで複数メッセージを追加できることを確認 +/// Verify that multiple messages can be added with extend_history #[test] fn test_mutable_extend_history() { let client = MockLlmClient::new(vec![]); @@ -92,10 +92,10 @@ fn test_mutable_extend_history() { } // ============================================================================= -// 状態遷移テスト +// State Transition Tests // ============================================================================= -/// lock()でMutable -> CacheLocked状態に遷移することを確認 +/// Verify that lock() transitions from Mutable -> CacheLocked state #[test] fn test_lock_transition() { let client = MockLlmClient::new(vec![]); @@ -105,16 +105,16 @@ fn test_lock_transition() { worker.push_message(Message::user("Hello")); worker.push_message(Message::assistant("Hi")); - // ロック + // Lock let locked_worker = worker.lock(); - // CacheLocked状態でも履歴とシステムプロンプトにアクセス可能 + // History and system prompt are still accessible in CacheLocked state assert_eq!(locked_worker.get_system_prompt(), Some("System")); assert_eq!(locked_worker.history().len(), 2); assert_eq!(locked_worker.locked_prefix_len(), 2); } -/// unlock()でCacheLocked -> Mutable状態に遷移することを確認 +/// Verify that unlock() transitions from CacheLocked -> Mutable state #[test] fn test_unlock_transition() { let client = MockLlmClient::new(vec![]); @@ -123,20 +123,20 @@ fn test_unlock_transition() { worker.push_message(Message::user("Hello")); let locked_worker = worker.lock(); - // アンロック + // Unlock let mut worker = locked_worker.unlock(); - // Mutable状態に戻ったので履歴操作が可能 + // History operations are available again in Mutable state worker.push_message(Message::assistant("Hi")); worker.clear_history(); assert!(worker.history().is_empty()); } // ============================================================================= -// ターン実行と状態保持のテスト +// Turn Execution and State Preservation Tests // ============================================================================= -/// Mutable状態でターンを実行し、履歴が正しく更新されることを確認 +/// Verify that history is correctly updated after running a turn in Mutable state #[tokio::test] async fn test_mutable_run_updates_history() { let events = vec![ @@ -151,33 +151,33 @@ async fn test_mutable_run_updates_history() { let client = MockLlmClient::new(events); let mut worker = Worker::new(client); - // 実行 + // Execute let result = worker.run("Hi there").await; assert!(result.is_ok()); - // 履歴が更新されている + // History is updated let history = worker.history(); assert_eq!(history.len(), 2); // user + assistant - // ユーザーメッセージ + // User message assert!(matches!( &history[0].content, MessageContent::Text(t) if t == "Hi there" )); - // アシスタントメッセージ + // Assistant message assert!(matches!( &history[1].content, MessageContent::Text(t) if t == "Hello, I'm an assistant!" )); } -/// CacheLocked状態で複数ターンを実行し、履歴が正しく累積することを確認 +/// Verify that history accumulates correctly over multiple turns in CacheLocked state #[tokio::test] async fn test_locked_multi_turn_history_accumulation() { - // 2回のリクエストに対応するレスポンスを準備 + // Prepare responses for 2 requests let client = MockLlmClient::with_responses(vec![ - // 1回目のレスポンス + // First response vec![ Event::text_block_start(0), Event::text_delta(0, "Nice to meet you!"), @@ -186,7 +186,7 @@ async fn test_locked_multi_turn_history_accumulation() { status: ResponseStatus::Completed, }), ], - // 2回目のレスポンス + // Second response vec![ Event::text_block_start(0), Event::text_delta(0, "I can help with that."), @@ -199,37 +199,37 @@ async fn test_locked_multi_turn_history_accumulation() { let worker = Worker::new(client).system_prompt("You are helpful."); - // ロック(システムプロンプト設定後) + // Lock (after setting system prompt) let mut locked_worker = worker.lock(); - assert_eq!(locked_worker.locked_prefix_len(), 0); // メッセージはまだない + assert_eq!(locked_worker.locked_prefix_len(), 0); // No messages yet - // 1ターン目 + // Turn 1 let result1 = locked_worker.run("Hello!").await; assert!(result1.is_ok()); assert_eq!(locked_worker.history().len(), 2); // user + assistant - // 2ターン目 + // Turn 2 let result2 = locked_worker.run("Can you help me?").await; assert!(result2.is_ok()); assert_eq!(locked_worker.history().len(), 4); // 2 * (user + assistant) - // 履歴の内容を確認 + // Verify history contents let history = locked_worker.history(); - // 1ターン目のユーザーメッセージ + // Turn 1 user message assert!(matches!(&history[0].content, MessageContent::Text(t) if t == "Hello!")); - // 1ターン目のアシスタントメッセージ + // Turn 1 assistant message assert!(matches!(&history[1].content, MessageContent::Text(t) if t == "Nice to meet you!")); - // 2ターン目のユーザーメッセージ + // Turn 2 user message assert!(matches!(&history[2].content, MessageContent::Text(t) if t == "Can you help me?")); - // 2ターン目のアシスタントメッセージ + // Turn 2 assistant message assert!(matches!(&history[3].content, MessageContent::Text(t) if t == "I can help with that.")); } -/// locked_prefix_lenがロック時点の履歴長を正しく記録することを確認 +/// Verify that locked_prefix_len correctly records history length at lock time #[tokio::test] async fn test_locked_prefix_len_tracking() { let client = MockLlmClient::with_responses(vec![ @@ -253,25 +253,25 @@ async fn test_locked_prefix_len_tracking() { let mut worker = Worker::new(client); - // 事前にメッセージを追加 + // Add messages beforehand worker.push_message(Message::user("Pre-existing message 1")); worker.push_message(Message::assistant("Pre-existing response 1")); assert_eq!(worker.history().len(), 2); - // ロック + // Lock let mut locked_worker = worker.lock(); - assert_eq!(locked_worker.locked_prefix_len(), 2); // ロック時点で2メッセージ + assert_eq!(locked_worker.locked_prefix_len(), 2); // 2 messages at lock time - // ターン実行 + // Execute turn locked_worker.run("New message").await.unwrap(); - // 履歴は増えるが、locked_prefix_lenは変わらない + // History grows but locked_prefix_len remains unchanged assert_eq!(locked_worker.history().len(), 4); // 2 + 2 - assert_eq!(locked_worker.locked_prefix_len(), 2); // 変わらない + assert_eq!(locked_worker.locked_prefix_len(), 2); // Unchanged } -/// ターンカウントが正しくインクリメントされることを確認 +/// Verify that turn count is correctly incremented #[tokio::test] async fn test_turn_count_increment() { let client = MockLlmClient::with_responses(vec![ @@ -304,7 +304,7 @@ async fn test_turn_count_increment() { assert_eq!(worker.turn_count(), 2); } -/// unlock後に履歴を編集し、再度lockできることを確認 +/// Verify that history can be edited after unlock and re-locked #[tokio::test] async fn test_unlock_edit_relock() { let client = MockLlmClient::with_responses(vec![vec![ @@ -320,27 +320,27 @@ async fn test_unlock_edit_relock() { .with_message(Message::user("Hello")) .with_message(Message::assistant("Hi")); - // ロック -> アンロック + // Lock -> Unlock let locked = worker.lock(); assert_eq!(locked.locked_prefix_len(), 2); let mut unlocked = locked.unlock(); - // 履歴を編集 + // Edit history unlocked.clear_history(); unlocked.push_message(Message::user("Fresh start")); - // 再ロック + // Re-lock let relocked = unlocked.lock(); assert_eq!(relocked.history().len(), 1); assert_eq!(relocked.locked_prefix_len(), 1); } // ============================================================================= -// システムプロンプト保持のテスト +// System Prompt Preservation Tests // ============================================================================= -/// CacheLocked状態でもシステムプロンプトが保持されることを確認 +/// Verify that system prompt is preserved in CacheLocked state #[test] fn test_system_prompt_preserved_in_locked_state() { let client = MockLlmClient::new(vec![]); @@ -356,7 +356,7 @@ fn test_system_prompt_preserved_in_locked_state() { ); } -/// unlock -> 再lock でシステムプロンプトを変更できることを確認 +/// Verify that system prompt can be changed after unlock -> re-lock #[test] fn test_system_prompt_change_after_unlock() { let client = MockLlmClient::new(vec![]);