//! Worker層の公開イベント型 //! //! 外部利用者に公開するためのイベント表現。 use serde::{Deserialize, Serialize}; // ============================================================================= // Core Event Types (from llm_client layer) // ============================================================================= /// LLMからのストリーミングイベント /// /// 各LLMプロバイダからのレスポンスは、この`Event`のストリームとして /// 統一的に処理されます。 /// /// # イベントの種類 /// /// - **メタイベント**: `Ping`, `Usage`, `Status`, `Error` /// - **ブロックイベント**: `BlockStart`, `BlockDelta`, `BlockStop`, `BlockAbort` /// /// # ブロックのライフサイクル /// /// テキストやツール呼び出しは、`BlockStart` → `BlockDelta`(複数) → `BlockStop` /// の順序でイベントが発生します。 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Event { /// ハートビート Ping(PingEvent), /// トークン使用量 Usage(UsageEvent), /// ストリームのステータス変化 Status(StatusEvent), /// エラー発生 Error(ErrorEvent), /// ブロック開始(テキスト、ツール使用等) BlockStart(BlockStart), /// ブロックの差分データ BlockDelta(BlockDelta), /// ブロック正常終了 BlockStop(BlockStop), /// ブロック中断 BlockAbort(BlockAbort), } // ============================================================================= // Meta Events // ============================================================================= /// Pingイベント(ハートビート) #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] pub struct PingEvent { pub timestamp: Option, } /// 使用量イベント #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] pub struct UsageEvent { /// 入力トークン数 pub input_tokens: Option, /// 出力トークン数 pub output_tokens: Option, /// 合計トークン数 pub total_tokens: Option, /// キャッシュ読み込みトークン数 pub cache_read_input_tokens: Option, /// キャッシュ作成トークン数 pub cache_creation_input_tokens: Option, } /// ステータスイベント #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct StatusEvent { pub status: ResponseStatus, } /// レスポンスステータス #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ResponseStatus { /// ストリーム開始 Started, /// 正常完了 Completed, /// キャンセルされた Cancelled, /// エラー発生 Failed, } /// エラーイベント #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ErrorEvent { pub code: Option, pub message: String, } // ============================================================================= // Block Types // ============================================================================= /// ブロックの種別 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum BlockType { /// テキスト生成 Text, /// 思考 (Claude Extended Thinking等) Thinking, /// ツール呼び出し ToolUse, /// ツール結果 ToolResult, } /// ブロック開始イベント #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct BlockStart { /// ブロックのインデックス pub index: usize, /// ブロックの種別 pub block_type: BlockType, /// ブロック固有のメタデータ pub metadata: BlockMetadata, } impl BlockStart { pub fn block_type(&self) -> BlockType { self.block_type } } /// ブロックのメタデータ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum BlockMetadata { Text, Thinking, ToolUse { id: String, name: String }, ToolResult { tool_use_id: String }, } /// ブロックデルタイベント #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct BlockDelta { /// ブロックのインデックス pub index: usize, /// デルタの内容 pub delta: DeltaContent, } /// デルタの内容 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum DeltaContent { /// テキストデルタ Text(String), /// 思考デルタ Thinking(String), /// ツール引数のJSON部分文字列 InputJson(String), } impl DeltaContent { /// デルタのブロック種別を取得 pub fn block_type(&self) -> BlockType { match self { DeltaContent::Text(_) => BlockType::Text, DeltaContent::Thinking(_) => BlockType::Thinking, DeltaContent::InputJson(_) => BlockType::ToolUse, } } } /// ブロック停止イベント #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct BlockStop { /// ブロックのインデックス pub index: usize, /// ブロックの種別 pub block_type: BlockType, /// 停止理由 pub stop_reason: Option, } impl BlockStop { pub fn block_type(&self) -> BlockType { self.block_type } } /// ブロック中断イベント #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct BlockAbort { /// ブロックのインデックス pub index: usize, /// ブロックの種別 pub block_type: BlockType, /// 中断理由 pub reason: String, } impl BlockAbort { pub fn block_type(&self) -> BlockType { self.block_type } } /// 停止理由 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum StopReason { /// 自然終了 EndTurn, /// 最大トークン数到達 MaxTokens, /// ストップシーケンス到達 StopSequence, /// ツール使用 ToolUse, } // ============================================================================= // Builder / Factory helpers // ============================================================================= impl Event { /// テキストブロック開始イベントを作成 pub fn text_block_start(index: usize) -> Self { Event::BlockStart(BlockStart { index, block_type: BlockType::Text, metadata: BlockMetadata::Text, }) } /// テキストデルタイベントを作成 pub fn text_delta(index: usize, text: impl Into) -> Self { Event::BlockDelta(BlockDelta { index, delta: DeltaContent::Text(text.into()), }) } /// テキストブロック停止イベントを作成 pub fn text_block_stop(index: usize, stop_reason: Option) -> Self { Event::BlockStop(BlockStop { index, block_type: BlockType::Text, stop_reason, }) } /// ツール使用ブロック開始イベントを作成 pub fn tool_use_start(index: usize, id: impl Into, name: impl Into) -> Self { Event::BlockStart(BlockStart { index, block_type: BlockType::ToolUse, metadata: BlockMetadata::ToolUse { id: id.into(), name: name.into(), }, }) } /// ツール引数デルタイベントを作成 pub fn tool_input_delta(index: usize, json: impl Into) -> Self { Event::BlockDelta(BlockDelta { index, delta: DeltaContent::InputJson(json.into()), }) } /// ツール使用ブロック停止イベントを作成 pub fn tool_use_stop(index: usize) -> Self { Event::BlockStop(BlockStop { index, block_type: BlockType::ToolUse, stop_reason: Some(StopReason::ToolUse), }) } /// 使用量イベントを作成 pub fn usage(input_tokens: u64, output_tokens: u64) -> Self { Event::Usage(UsageEvent { input_tokens: Some(input_tokens), output_tokens: Some(output_tokens), total_tokens: Some(input_tokens + output_tokens), cache_read_input_tokens: None, cache_creation_input_tokens: None, }) } /// Pingイベントを作成 pub fn ping() -> Self { Event::Ping(PingEvent { timestamp: None }) } } // ============================================================================= // Conversions: timeline::event -> worker::event // ============================================================================= impl From for ResponseStatus { fn from(value: crate::timeline::event::ResponseStatus) -> Self { match value { crate::timeline::event::ResponseStatus::Started => ResponseStatus::Started, crate::timeline::event::ResponseStatus::Completed => ResponseStatus::Completed, crate::timeline::event::ResponseStatus::Cancelled => ResponseStatus::Cancelled, crate::timeline::event::ResponseStatus::Failed => ResponseStatus::Failed, } } } impl From for BlockType { fn from(value: crate::timeline::event::BlockType) -> Self { match value { crate::timeline::event::BlockType::Text => BlockType::Text, crate::timeline::event::BlockType::Thinking => BlockType::Thinking, crate::timeline::event::BlockType::ToolUse => BlockType::ToolUse, crate::timeline::event::BlockType::ToolResult => BlockType::ToolResult, } } } impl From for BlockMetadata { fn from(value: crate::timeline::event::BlockMetadata) -> Self { match value { crate::timeline::event::BlockMetadata::Text => BlockMetadata::Text, crate::timeline::event::BlockMetadata::Thinking => BlockMetadata::Thinking, crate::timeline::event::BlockMetadata::ToolUse { id, name } => { BlockMetadata::ToolUse { id, name } } crate::timeline::event::BlockMetadata::ToolResult { tool_use_id } => { BlockMetadata::ToolResult { tool_use_id } } } } } impl From for DeltaContent { fn from(value: crate::timeline::event::DeltaContent) -> Self { match value { crate::timeline::event::DeltaContent::Text(text) => DeltaContent::Text(text), crate::timeline::event::DeltaContent::Thinking(text) => DeltaContent::Thinking(text), crate::timeline::event::DeltaContent::InputJson(json) => DeltaContent::InputJson(json), } } } impl From for StopReason { fn from(value: crate::timeline::event::StopReason) -> Self { match value { crate::timeline::event::StopReason::EndTurn => StopReason::EndTurn, crate::timeline::event::StopReason::MaxTokens => StopReason::MaxTokens, crate::timeline::event::StopReason::StopSequence => StopReason::StopSequence, crate::timeline::event::StopReason::ToolUse => StopReason::ToolUse, } } } impl From for PingEvent { fn from(value: crate::timeline::event::PingEvent) -> Self { PingEvent { timestamp: value.timestamp, } } } impl From for UsageEvent { fn from(value: crate::timeline::event::UsageEvent) -> Self { UsageEvent { input_tokens: value.input_tokens, output_tokens: value.output_tokens, total_tokens: value.total_tokens, cache_read_input_tokens: value.cache_read_input_tokens, cache_creation_input_tokens: value.cache_creation_input_tokens, } } } impl From for StatusEvent { fn from(value: crate::timeline::event::StatusEvent) -> Self { StatusEvent { status: value.status.into(), } } } impl From for ErrorEvent { fn from(value: crate::timeline::event::ErrorEvent) -> Self { ErrorEvent { code: value.code, message: value.message, } } } impl From for BlockStart { fn from(value: crate::timeline::event::BlockStart) -> Self { BlockStart { index: value.index, block_type: value.block_type.into(), metadata: value.metadata.into(), } } } impl From for BlockDelta { fn from(value: crate::timeline::event::BlockDelta) -> Self { BlockDelta { index: value.index, delta: value.delta.into(), } } } impl From for BlockStop { fn from(value: crate::timeline::event::BlockStop) -> Self { BlockStop { index: value.index, block_type: value.block_type.into(), stop_reason: value.stop_reason.map(Into::into), } } } impl From for BlockAbort { fn from(value: crate::timeline::event::BlockAbort) -> Self { BlockAbort { index: value.index, block_type: value.block_type.into(), reason: value.reason, } } } impl From for Event { fn from(value: crate::timeline::event::Event) -> Self { match value { crate::timeline::event::Event::Ping(p) => Event::Ping(p.into()), crate::timeline::event::Event::Usage(u) => Event::Usage(u.into()), crate::timeline::event::Event::Status(s) => Event::Status(s.into()), crate::timeline::event::Event::Error(e) => Event::Error(e.into()), crate::timeline::event::Event::BlockStart(s) => Event::BlockStart(s.into()), crate::timeline::event::Event::BlockDelta(d) => Event::BlockDelta(d.into()), crate::timeline::event::Event::BlockStop(s) => Event::BlockStop(s.into()), crate::timeline::event::Event::BlockAbort(a) => Event::BlockAbort(a.into()), } } }