1pub fn now_ms() -> u64 {
2 std::time::SystemTime::now()
3 .duration_since(std::time::UNIX_EPOCH)
4 .unwrap_or_default()
5 .as_millis() as u64
6}
7
8use serde::Serialize;
9
10pub trait ProgressSink: Send + Sync + 'static {
11 fn emit(&self, event: ProgressEvent);
12}
13
14pub struct NullProgressSink;
15
16impl ProgressSink for NullProgressSink {
17 fn emit(&self, _event: ProgressEvent) {}
18}
19
20#[derive(Debug, Clone, Serialize)]
21pub enum ProgressEvent {
22 JobCreated {
23 job_id: String,
24 input_path: String,
25 output_path: String,
26 timestamp_ms: u64,
27 },
28 StageStarted {
29 stage: String,
30 timestamp_ms: u64,
31 },
32 StageFinished {
33 stage: String,
34 timestamp_ms: u64,
35 },
36 RuntimeConfigResolved {
37 profile: String,
38 provider_preset: Option<String>,
39 provider: String,
40 model: String,
41 concurrency: usize,
42 max_attempts: usize,
43 provider_max_attempts: usize,
44 validation_max_attempts: usize,
45 retry_after_policy: String,
46 max_backoff_seconds: u64,
47 timeout_seconds: u64,
48 batch_enabled: bool,
49 batch_target_tokens: usize,
50 batch_max_items: usize,
51 adaptive_batch_sizing: bool,
52 adaptive_concurrency: bool,
53 compact_prompts: bool,
54 thinking_disabled: bool,
55 json_mode: String,
56 model_context_tokens: Option<u32>,
57 max_output_tokens: Option<u32>,
58 batch_max_output_tokens: Option<u32>,
59 timestamp_ms: u64,
60 },
61 SegmentationFinished {
62 segment_count: usize,
63 timestamp_ms: u64,
64 },
65 CacheScanFinished {
66 hits: usize,
67 misses: usize,
68 timestamp_ms: u64,
69 },
70 BatchQueued {
71 batch_id: String,
72 item_count: usize,
73 timestamp_ms: u64,
74 },
75 BatchSplit {
76 batch_id: String,
77 left_items: usize,
78 right_items: usize,
79 timestamp_ms: u64,
80 },
81 BatchRepairStarted {
82 failed_item_count: usize,
83 timestamp_ms: u64,
84 },
85 BatchRepairFinished {
86 repaired_items: usize,
87 still_failed_items: usize,
88 timestamp_ms: u64,
89 },
90 RequestStarted {
91 request_id: String,
92 batch_id: Option<String>,
93 segment_id: Option<String>,
94 provider: Option<String>,
95 model: Option<String>,
96 prompt_template: Option<String>,
97 items: usize,
98 estimated_input_tokens: usize,
99 max_output_tokens: Option<u32>,
100 active_requests: usize,
101 target_concurrency: usize,
102 timestamp_ms: u64,
103 },
104 RequestFinished {
105 request_id: String,
106 batch_id: Option<String>,
107 segment_id: Option<String>,
108 status: String,
109 latency_ms: u64,
110 status_code: Option<u16>,
111 finish_reason: Option<String>,
112 retry_count: usize,
113 input_tokens: Option<u64>,
114 output_tokens: Option<u64>,
115 error_kind: Option<String>,
116 timestamp_ms: u64,
117 },
118 SegmentStarted {
119 segment_id: String,
120 ordinal: usize,
121 timestamp_ms: u64,
122 },
123 SegmentFinished {
124 segment_id: String,
125 status: String,
126 input_tokens: Option<u64>,
127 output_tokens: Option<u64>,
128 timestamp_ms: u64,
129 },
130 CheckpointQueued {
131 queued: usize,
132 timestamp_ms: u64,
133 },
134 CheckpointFlushed {
135 segment_id: Option<String>,
136 flushed_count: usize,
137 latency_ms: Option<u64>,
138 timestamp_ms: u64,
139 },
140 ConcurrencyChanged {
141 previous: usize,
142 current: usize,
143 reason: String,
144 timestamp_ms: u64,
145 },
146 BatchSizingChanged {
147 batch_id: Option<String>,
148 previous_target: usize,
149 new_target: usize,
150 previous_max_items: usize,
151 new_max_items: usize,
152 reason: String,
153 timestamp_ms: u64,
154 },
155 ArtifactWritten {
156 path: String,
157 timestamp_ms: u64,
158 },
159 Warning {
160 kind: String,
161 message: String,
162 timestamp_ms: u64,
163 },
164 Error {
165 kind: String,
166 message: String,
167 timestamp_ms: u64,
168 },
169 TranslationFinished {
170 succeeded: usize,
171 cached: usize,
172 needs_review: usize,
173 failed: usize,
174 input_tokens: u64,
175 output_tokens: u64,
176 elapsed_ms: u64,
177 timestamp_ms: u64,
178 },
179 DroppedEvents {
180 count: usize,
181 timestamp_ms: u64,
182 },
183}