llm_worker/
event.rs

1//! Worker層の公開イベント型
2//!
3//! 外部利用者に公開するためのイベント表現。
4
5use serde::{Deserialize, Serialize};
6
7// =============================================================================
8// Core Event Types (from llm_client layer)
9// =============================================================================
10
11/// LLMからのストリーミングイベント
12///
13/// 各LLMプロバイダからのレスポンスは、この`Event`のストリームとして
14/// 統一的に処理されます。
15///
16/// # イベントの種類
17///
18/// - **メタイベント**: `Ping`, `Usage`, `Status`, `Error`
19/// - **ブロックイベント**: `BlockStart`, `BlockDelta`, `BlockStop`, `BlockAbort`
20///
21/// # ブロックのライフサイクル
22///
23/// テキストやツール呼び出しは、`BlockStart` → `BlockDelta`(複数) → `BlockStop`
24/// の順序でイベントが発生します。
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26pub enum Event {
27    /// ハートビート
28    Ping(PingEvent),
29    /// トークン使用量
30    Usage(UsageEvent),
31    /// ストリームのステータス変化
32    Status(StatusEvent),
33    /// エラー発生
34    Error(ErrorEvent),
35
36    /// ブロック開始(テキスト、ツール使用等)
37    BlockStart(BlockStart),
38    /// ブロックの差分データ
39    BlockDelta(BlockDelta),
40    /// ブロック正常終了
41    BlockStop(BlockStop),
42    /// ブロック中断
43    BlockAbort(BlockAbort),
44}
45
46// =============================================================================
47// Meta Events
48// =============================================================================
49
50/// Pingイベント(ハートビート)
51#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
52pub struct PingEvent {
53    pub timestamp: Option<u64>,
54}
55
56/// 使用量イベント
57#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
58pub struct UsageEvent {
59    /// 入力トークン数
60    pub input_tokens: Option<u64>,
61    /// 出力トークン数
62    pub output_tokens: Option<u64>,
63    /// 合計トークン数
64    pub total_tokens: Option<u64>,
65    /// キャッシュ読み込みトークン数
66    pub cache_read_input_tokens: Option<u64>,
67    /// キャッシュ作成トークン数
68    pub cache_creation_input_tokens: Option<u64>,
69}
70
71/// ステータスイベント
72#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub struct StatusEvent {
74    pub status: ResponseStatus,
75}
76
77/// レスポンスステータス
78#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
79pub enum ResponseStatus {
80    /// ストリーム開始
81    Started,
82    /// 正常完了
83    Completed,
84    /// キャンセルされた
85    Cancelled,
86    /// エラー発生
87    Failed,
88}
89
90/// エラーイベント
91#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
92pub struct ErrorEvent {
93    pub code: Option<String>,
94    pub message: String,
95}
96
97// =============================================================================
98// Block Types
99// =============================================================================
100
101/// ブロックの種別
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
103pub enum BlockType {
104    /// テキスト生成
105    Text,
106    /// 思考 (Claude Extended Thinking等)
107    Thinking,
108    /// ツール呼び出し
109    ToolUse,
110    /// ツール結果
111    ToolResult,
112}
113
114/// ブロック開始イベント
115#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
116pub struct BlockStart {
117    /// ブロックのインデックス
118    pub index: usize,
119    /// ブロックの種別
120    pub block_type: BlockType,
121    /// ブロック固有のメタデータ
122    pub metadata: BlockMetadata,
123}
124
125impl BlockStart {
126    pub fn block_type(&self) -> BlockType {
127        self.block_type
128    }
129}
130
131/// ブロックのメタデータ
132#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
133pub enum BlockMetadata {
134    Text,
135    Thinking,
136    ToolUse { id: String, name: String },
137    ToolResult { tool_use_id: String },
138}
139
140/// ブロックデルタイベント
141#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
142pub struct BlockDelta {
143    /// ブロックのインデックス
144    pub index: usize,
145    /// デルタの内容
146    pub delta: DeltaContent,
147}
148
149/// デルタの内容
150#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
151pub enum DeltaContent {
152    /// テキストデルタ
153    Text(String),
154    /// 思考デルタ
155    Thinking(String),
156    /// ツール引数のJSON部分文字列
157    InputJson(String),
158}
159
160impl DeltaContent {
161    /// デルタのブロック種別を取得
162    pub fn block_type(&self) -> BlockType {
163        match self {
164            DeltaContent::Text(_) => BlockType::Text,
165            DeltaContent::Thinking(_) => BlockType::Thinking,
166            DeltaContent::InputJson(_) => BlockType::ToolUse,
167        }
168    }
169}
170
171/// ブロック停止イベント
172#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
173pub struct BlockStop {
174    /// ブロックのインデックス
175    pub index: usize,
176    /// ブロックの種別
177    pub block_type: BlockType,
178    /// 停止理由
179    pub stop_reason: Option<StopReason>,
180}
181
182impl BlockStop {
183    pub fn block_type(&self) -> BlockType {
184        self.block_type
185    }
186}
187
188/// ブロック中断イベント
189#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
190pub struct BlockAbort {
191    /// ブロックのインデックス
192    pub index: usize,
193    /// ブロックの種別
194    pub block_type: BlockType,
195    /// 中断理由
196    pub reason: String,
197}
198
199impl BlockAbort {
200    pub fn block_type(&self) -> BlockType {
201        self.block_type
202    }
203}
204
205/// 停止理由
206#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
207pub enum StopReason {
208    /// 自然終了
209    EndTurn,
210    /// 最大トークン数到達
211    MaxTokens,
212    /// ストップシーケンス到達
213    StopSequence,
214    /// ツール使用
215    ToolUse,
216}
217
218// =============================================================================
219// Builder / Factory helpers
220// =============================================================================
221
222impl Event {
223    /// テキストブロック開始イベントを作成
224    pub fn text_block_start(index: usize) -> Self {
225        Event::BlockStart(BlockStart {
226            index,
227            block_type: BlockType::Text,
228            metadata: BlockMetadata::Text,
229        })
230    }
231
232    /// テキストデルタイベントを作成
233    pub fn text_delta(index: usize, text: impl Into<String>) -> Self {
234        Event::BlockDelta(BlockDelta {
235            index,
236            delta: DeltaContent::Text(text.into()),
237        })
238    }
239
240    /// テキストブロック停止イベントを作成
241    pub fn text_block_stop(index: usize, stop_reason: Option<StopReason>) -> Self {
242        Event::BlockStop(BlockStop {
243            index,
244            block_type: BlockType::Text,
245            stop_reason,
246        })
247    }
248
249    /// ツール使用ブロック開始イベントを作成
250    pub fn tool_use_start(index: usize, id: impl Into<String>, name: impl Into<String>) -> Self {
251        Event::BlockStart(BlockStart {
252            index,
253            block_type: BlockType::ToolUse,
254            metadata: BlockMetadata::ToolUse {
255                id: id.into(),
256                name: name.into(),
257            },
258        })
259    }
260
261    /// ツール引数デルタイベントを作成
262    pub fn tool_input_delta(index: usize, json: impl Into<String>) -> Self {
263        Event::BlockDelta(BlockDelta {
264            index,
265            delta: DeltaContent::InputJson(json.into()),
266        })
267    }
268
269    /// ツール使用ブロック停止イベントを作成
270    pub fn tool_use_stop(index: usize) -> Self {
271        Event::BlockStop(BlockStop {
272            index,
273            block_type: BlockType::ToolUse,
274            stop_reason: Some(StopReason::ToolUse),
275        })
276    }
277
278    /// 使用量イベントを作成
279    pub fn usage(input_tokens: u64, output_tokens: u64) -> Self {
280        Event::Usage(UsageEvent {
281            input_tokens: Some(input_tokens),
282            output_tokens: Some(output_tokens),
283            total_tokens: Some(input_tokens + output_tokens),
284            cache_read_input_tokens: None,
285            cache_creation_input_tokens: None,
286        })
287    }
288
289    /// Pingイベントを作成
290    pub fn ping() -> Self {
291        Event::Ping(PingEvent { timestamp: None })
292    }
293}
294
295// =============================================================================
296// Conversions: timeline::event -> worker::event
297// =============================================================================
298
299impl From<crate::timeline::event::ResponseStatus> for ResponseStatus {
300    fn from(value: crate::timeline::event::ResponseStatus) -> Self {
301        match value {
302            crate::timeline::event::ResponseStatus::Started => ResponseStatus::Started,
303            crate::timeline::event::ResponseStatus::Completed => ResponseStatus::Completed,
304            crate::timeline::event::ResponseStatus::Cancelled => ResponseStatus::Cancelled,
305            crate::timeline::event::ResponseStatus::Failed => ResponseStatus::Failed,
306        }
307    }
308}
309
310impl From<crate::timeline::event::BlockType> for BlockType {
311    fn from(value: crate::timeline::event::BlockType) -> Self {
312        match value {
313            crate::timeline::event::BlockType::Text => BlockType::Text,
314            crate::timeline::event::BlockType::Thinking => BlockType::Thinking,
315            crate::timeline::event::BlockType::ToolUse => BlockType::ToolUse,
316            crate::timeline::event::BlockType::ToolResult => BlockType::ToolResult,
317        }
318    }
319}
320
321impl From<crate::timeline::event::BlockMetadata> for BlockMetadata {
322    fn from(value: crate::timeline::event::BlockMetadata) -> Self {
323        match value {
324            crate::timeline::event::BlockMetadata::Text => BlockMetadata::Text,
325            crate::timeline::event::BlockMetadata::Thinking => BlockMetadata::Thinking,
326            crate::timeline::event::BlockMetadata::ToolUse { id, name } => {
327                BlockMetadata::ToolUse { id, name }
328            }
329            crate::timeline::event::BlockMetadata::ToolResult { tool_use_id } => {
330                BlockMetadata::ToolResult { tool_use_id }
331            }
332        }
333    }
334}
335
336impl From<crate::timeline::event::DeltaContent> for DeltaContent {
337    fn from(value: crate::timeline::event::DeltaContent) -> Self {
338        match value {
339            crate::timeline::event::DeltaContent::Text(text) => DeltaContent::Text(text),
340            crate::timeline::event::DeltaContent::Thinking(text) => DeltaContent::Thinking(text),
341            crate::timeline::event::DeltaContent::InputJson(json) => DeltaContent::InputJson(json),
342        }
343    }
344}
345
346impl From<crate::timeline::event::StopReason> for StopReason {
347    fn from(value: crate::timeline::event::StopReason) -> Self {
348        match value {
349            crate::timeline::event::StopReason::EndTurn => StopReason::EndTurn,
350            crate::timeline::event::StopReason::MaxTokens => StopReason::MaxTokens,
351            crate::timeline::event::StopReason::StopSequence => StopReason::StopSequence,
352            crate::timeline::event::StopReason::ToolUse => StopReason::ToolUse,
353        }
354    }
355}
356
357impl From<crate::timeline::event::PingEvent> for PingEvent {
358    fn from(value: crate::timeline::event::PingEvent) -> Self {
359        PingEvent {
360            timestamp: value.timestamp,
361        }
362    }
363}
364
365impl From<crate::timeline::event::UsageEvent> for UsageEvent {
366    fn from(value: crate::timeline::event::UsageEvent) -> Self {
367        UsageEvent {
368            input_tokens: value.input_tokens,
369            output_tokens: value.output_tokens,
370            total_tokens: value.total_tokens,
371            cache_read_input_tokens: value.cache_read_input_tokens,
372            cache_creation_input_tokens: value.cache_creation_input_tokens,
373        }
374    }
375}
376
377impl From<crate::timeline::event::StatusEvent> for StatusEvent {
378    fn from(value: crate::timeline::event::StatusEvent) -> Self {
379        StatusEvent {
380            status: value.status.into(),
381        }
382    }
383}
384
385impl From<crate::timeline::event::ErrorEvent> for ErrorEvent {
386    fn from(value: crate::timeline::event::ErrorEvent) -> Self {
387        ErrorEvent {
388            code: value.code,
389            message: value.message,
390        }
391    }
392}
393
394impl From<crate::timeline::event::BlockStart> for BlockStart {
395    fn from(value: crate::timeline::event::BlockStart) -> Self {
396        BlockStart {
397            index: value.index,
398            block_type: value.block_type.into(),
399            metadata: value.metadata.into(),
400        }
401    }
402}
403
404impl From<crate::timeline::event::BlockDelta> for BlockDelta {
405    fn from(value: crate::timeline::event::BlockDelta) -> Self {
406        BlockDelta {
407            index: value.index,
408            delta: value.delta.into(),
409        }
410    }
411}
412
413impl From<crate::timeline::event::BlockStop> for BlockStop {
414    fn from(value: crate::timeline::event::BlockStop) -> Self {
415        BlockStop {
416            index: value.index,
417            block_type: value.block_type.into(),
418            stop_reason: value.stop_reason.map(Into::into),
419        }
420    }
421}
422
423impl From<crate::timeline::event::BlockAbort> for BlockAbort {
424    fn from(value: crate::timeline::event::BlockAbort) -> Self {
425        BlockAbort {
426            index: value.index,
427            block_type: value.block_type.into(),
428            reason: value.reason,
429        }
430    }
431}
432
433impl From<crate::timeline::event::Event> for Event {
434    fn from(value: crate::timeline::event::Event) -> Self {
435        match value {
436            crate::timeline::event::Event::Ping(p) => Event::Ping(p.into()),
437            crate::timeline::event::Event::Usage(u) => Event::Usage(u.into()),
438            crate::timeline::event::Event::Status(s) => Event::Status(s.into()),
439            crate::timeline::event::Event::Error(e) => Event::Error(e.into()),
440            crate::timeline::event::Event::BlockStart(s) => Event::BlockStart(s.into()),
441            crate::timeline::event::Event::BlockDelta(d) => Event::BlockDelta(d.into()),
442            crate::timeline::event::Event::BlockStop(s) => Event::BlockStop(s.into()),
443            crate::timeline::event::Event::BlockAbort(a) => Event::BlockAbort(a.into()),
444        }
445    }
446}