bazaar-compute-node 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. bazaar_compute_node/__init__.py +3 -0
  2. bazaar_compute_node/app/__init__.py +1 -0
  3. bazaar_compute_node/app/application.py +398 -0
  4. bazaar_compute_node/app/attachments.py +154 -0
  5. bazaar_compute_node/app/command.py +342 -0
  6. bazaar_compute_node/app/config.py +121 -0
  7. bazaar_compute_node/app/registry.py +120 -0
  8. bazaar_compute_node/app/transport.py +264 -0
  9. bazaar_compute_node/app/windows_pipe.py +463 -0
  10. bazaar_compute_node/app/wrapper.py +63 -0
  11. bazaar_compute_node/bcc.py +524 -0
  12. bazaar_compute_node/cli.py +382 -0
  13. bazaar_compute_node/contrib/__init__.py +1 -0
  14. bazaar_compute_node/contrib/codex_app_server/__init__.py +63 -0
  15. bazaar_compute_node/contrib/codex_app_server/approval.py +168 -0
  16. bazaar_compute_node/contrib/codex_app_server/client.py +408 -0
  17. bazaar_compute_node/contrib/codex_app_server/events.py +431 -0
  18. bazaar_compute_node/contrib/codex_app_server/plugin.py +15 -0
  19. bazaar_compute_node/contrib/codex_app_server/process.py +583 -0
  20. bazaar_compute_node/contrib/codex_app_server/protocol.py +103 -0
  21. bazaar_compute_node/contrib/codex_app_server/runtime.py +513 -0
  22. bazaar_compute_node/contrib/logging/__init__.py +5 -0
  23. bazaar_compute_node/contrib/logging/audit.py +61 -0
  24. bazaar_compute_node/contrib/logging/plugin.py +11 -0
  25. bazaar_compute_node/contrib/sqlite/__init__.py +14 -0
  26. bazaar_compute_node/contrib/sqlite/codec.py +768 -0
  27. bazaar_compute_node/contrib/sqlite/database.py +282 -0
  28. bazaar_compute_node/contrib/sqlite/migrations.py +646 -0
  29. bazaar_compute_node/contrib/sqlite/plugin.py +11 -0
  30. bazaar_compute_node/contrib/sqlite/repository.py +1059 -0
  31. bazaar_compute_node/contrib/wecom/__init__.py +1 -0
  32. bazaar_compute_node/contrib/wecom/channel.py +960 -0
  33. bazaar_compute_node/contrib/wecom/markdown.py +146 -0
  34. bazaar_compute_node/contrib/wecom/plugin.py +29 -0
  35. bazaar_compute_node/core/__init__.py +5 -0
  36. bazaar_compute_node/core/approval.py +51 -0
  37. bazaar_compute_node/core/audit.py +101 -0
  38. bazaar_compute_node/core/channel.py +121 -0
  39. bazaar_compute_node/core/client.py +30 -0
  40. bazaar_compute_node/core/command.py +85 -0
  41. bazaar_compute_node/core/concurrency.py +29 -0
  42. bazaar_compute_node/core/correlation.py +48 -0
  43. bazaar_compute_node/core/instruction.py +224 -0
  44. bazaar_compute_node/core/lifecycle.py +48 -0
  45. bazaar_compute_node/core/models/__init__.py +63 -0
  46. bazaar_compute_node/core/models/entities.py +514 -0
  47. bazaar_compute_node/core/models/states.py +369 -0
  48. bazaar_compute_node/core/observability.py +47 -0
  49. bazaar_compute_node/core/orchestration/__init__.py +5 -0
  50. bazaar_compute_node/core/orchestration/command.py +614 -0
  51. bazaar_compute_node/core/orchestration/services.py +135 -0
  52. bazaar_compute_node/core/orchestration/session.py +891 -0
  53. bazaar_compute_node/core/orchestration/turn.py +451 -0
  54. bazaar_compute_node/core/outcomes.py +51 -0
  55. bazaar_compute_node/core/paths.py +19 -0
  56. bazaar_compute_node/core/runtime.py +118 -0
  57. bazaar_compute_node/core/storage.py +167 -0
  58. bazaar_compute_node-0.1.3.dist-info/METADATA +178 -0
  59. bazaar_compute_node-0.1.3.dist-info/RECORD +62 -0
  60. bazaar_compute_node-0.1.3.dist-info/WHEEL +4 -0
  61. bazaar_compute_node-0.1.3.dist-info/entry_points.txt +15 -0
  62. bazaar_compute_node-0.1.3.dist-info/licenses/LICENSE +613 -0
@@ -0,0 +1,514 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from dataclasses import dataclass, field, replace
5
+ from typing import Self
6
+
7
+ from .states import (
8
+ FRESH_CHECK_TRANSITIONS,
9
+ OUTBOUND_DELIVERY_TRANSITIONS,
10
+ RUNTIME_TURN_TRANSITIONS,
11
+ ApprovalDecision,
12
+ ChannelTargetKind,
13
+ FreshCheckState,
14
+ OutboundDeliveryState,
15
+ RuntimeEventState,
16
+ RuntimeTurnState,
17
+ StreamEventKind,
18
+ ensure_transition,
19
+ )
20
+
21
+ Metadata = Mapping[str, object]
22
+
23
+
24
+ def _validate_text(value: str, field_name: str) -> None:
25
+ if not isinstance(value, str) or not value:
26
+ raise ValueError(f"{field_name} must be a non-empty string")
27
+
28
+
29
+ def _validate_non_negative(value: int, field_name: str) -> None:
30
+ if not isinstance(value, int) or value < 0:
31
+ raise ValueError(f"{field_name} must be a non-negative integer")
32
+
33
+
34
+ @dataclass(frozen=True, slots=True)
35
+ class ChannelSession:
36
+ id: str
37
+ channel: str
38
+ provider_thread_id: str
39
+ created_at_ms: int
40
+ updated_at_ms: int
41
+ target_kind: ChannelTargetKind = ChannelTargetKind.DM
42
+ following: bool = True
43
+ last_inbound_at_ms: int | None = None
44
+ last_outbound_at_ms: int | None = None
45
+ metadata: Metadata = field(default_factory=dict)
46
+
47
+ def __post_init__(self) -> None:
48
+ _validate_text(self.id, "id")
49
+ _validate_text(self.channel, "channel")
50
+ _validate_text(self.provider_thread_id, "provider_thread_id")
51
+ _validate_non_negative(self.created_at_ms, "created_at_ms")
52
+ _validate_non_negative(self.updated_at_ms, "updated_at_ms")
53
+ if not isinstance(self.target_kind, ChannelTargetKind):
54
+ raise TypeError("target_kind must be a ChannelTargetKind")
55
+ if self.last_inbound_at_ms is not None:
56
+ _validate_non_negative(self.last_inbound_at_ms, "last_inbound_at_ms")
57
+ if self.last_outbound_at_ms is not None:
58
+ _validate_non_negative(self.last_outbound_at_ms, "last_outbound_at_ms")
59
+
60
+
61
+ @dataclass(frozen=True, slots=True)
62
+ class BcnSession:
63
+ id: str
64
+ channel_session_id: str
65
+ workspace_id: str
66
+ created_at_ms: int
67
+ updated_at_ms: int
68
+ last_activity_at_ms: int | None = None
69
+ metadata: Metadata = field(default_factory=dict)
70
+
71
+ def __post_init__(self) -> None:
72
+ _validate_text(self.id, "id")
73
+ _validate_text(self.channel_session_id, "channel_session_id")
74
+ _validate_text(self.workspace_id, "workspace_id")
75
+ _validate_non_negative(self.created_at_ms, "created_at_ms")
76
+ _validate_non_negative(self.updated_at_ms, "updated_at_ms")
77
+ if self.last_activity_at_ms is not None:
78
+ _validate_non_negative(self.last_activity_at_ms, "last_activity_at_ms")
79
+
80
+
81
+ @dataclass(frozen=True, slots=True)
82
+ class RuntimeSession:
83
+ id: str
84
+ bcn_session_id: str
85
+ channel_session_id: str
86
+ runtime: str
87
+ workspace_id: str
88
+ created_at_ms: int
89
+ updated_at_ms: int
90
+ provider_thread_id: str | None = None
91
+ metadata: Metadata = field(default_factory=dict)
92
+
93
+ def __post_init__(self) -> None:
94
+ _validate_text(self.id, "id")
95
+ _validate_text(self.bcn_session_id, "bcn_session_id")
96
+ _validate_text(self.channel_session_id, "channel_session_id")
97
+ _validate_text(self.runtime, "runtime")
98
+ _validate_text(self.workspace_id, "workspace_id")
99
+ _validate_non_negative(self.created_at_ms, "created_at_ms")
100
+ _validate_non_negative(self.updated_at_ms, "updated_at_ms")
101
+ if self.provider_thread_id is not None:
102
+ _validate_text(self.provider_thread_id, "provider_thread_id")
103
+
104
+
105
+ @dataclass(frozen=True, slots=True)
106
+ class RuntimeAttempt:
107
+ turn_id: str
108
+ session_id: str
109
+ client_user_message_id: str
110
+ started_at_ms: int
111
+
112
+ def __post_init__(self) -> None:
113
+ _validate_text(self.turn_id, "turn_id")
114
+ _validate_text(self.session_id, "session_id")
115
+ _validate_text(self.client_user_message_id, "client_user_message_id")
116
+ _validate_non_negative(self.started_at_ms, "started_at_ms")
117
+
118
+
119
+ @dataclass(frozen=True, slots=True)
120
+ class RuntimeTurn:
121
+ turn_id: str
122
+ session_id: str
123
+ state: RuntimeTurnState
124
+ started_at_ms: int
125
+ provider_turn_id: str | None = None
126
+ client_user_message_id: str | None = None
127
+ completed_at_ms: int | None = None
128
+ latest_event_name: str | None = None
129
+ error_kind: str | None = None
130
+ error_message: str | None = None
131
+ metadata: Metadata = field(default_factory=dict)
132
+
133
+ def __post_init__(self) -> None:
134
+ _validate_text(self.turn_id, "turn_id")
135
+ _validate_text(self.session_id, "session_id")
136
+ _validate_non_negative(self.started_at_ms, "started_at_ms")
137
+ if self.provider_turn_id is not None:
138
+ _validate_text(self.provider_turn_id, "provider_turn_id")
139
+ if self.client_user_message_id is not None:
140
+ _validate_text(self.client_user_message_id, "client_user_message_id")
141
+ if self.completed_at_ms is not None:
142
+ _validate_non_negative(self.completed_at_ms, "completed_at_ms")
143
+
144
+ def transition_to(
145
+ self,
146
+ state: RuntimeTurnState,
147
+ *,
148
+ at_ms: int,
149
+ error_kind: str | None = None,
150
+ error_message: str | None = None,
151
+ latest_event_name: str | None = None,
152
+ ) -> Self:
153
+ _validate_non_negative(at_ms, "at_ms")
154
+ ensure_transition("runtime_turn", self.state, state, RUNTIME_TURN_TRANSITIONS)
155
+ if state is self.state:
156
+ return self
157
+ completed_at_ms = (
158
+ at_ms
159
+ if state
160
+ in {
161
+ RuntimeTurnState.COMPLETED,
162
+ RuntimeTurnState.FAILED,
163
+ RuntimeTurnState.CANCELLED,
164
+ RuntimeTurnState.UNKNOWN,
165
+ }
166
+ else self.completed_at_ms
167
+ )
168
+ return replace(
169
+ self,
170
+ state=state,
171
+ completed_at_ms=completed_at_ms,
172
+ latest_event_name=latest_event_name or self.latest_event_name,
173
+ error_kind=error_kind or self.error_kind,
174
+ error_message=error_message or self.error_message,
175
+ )
176
+
177
+
178
+ @dataclass(frozen=True, slots=True)
179
+ class InboundAttachment:
180
+ attachment_id: str
181
+ name: str
182
+ kind: str
183
+ state: str
184
+ media_type: str | None = None
185
+ relative_path: str | None = None
186
+ size_bytes: int | None = None
187
+ error: str | None = None
188
+
189
+ def __post_init__(self) -> None:
190
+ for value, field_name in (
191
+ (self.attachment_id, "attachment_id"),
192
+ (self.name, "name"),
193
+ (self.kind, "kind"),
194
+ (self.state, "state"),
195
+ ):
196
+ _validate_text(value, field_name)
197
+ if self.state not in {"ready", "failed"}:
198
+ raise ValueError("attachment state must be ready or failed")
199
+ if self.state == "ready" and self.relative_path is None:
200
+ raise ValueError("ready attachment must have a relative path")
201
+ if self.state == "failed" and self.relative_path is not None:
202
+ raise ValueError("failed attachment cannot have a relative path")
203
+ if self.size_bytes is not None:
204
+ _validate_non_negative(self.size_bytes, "size_bytes")
205
+
206
+
207
+ @dataclass(frozen=True, slots=True)
208
+ class InboundMessage:
209
+ seq: int
210
+ message_id: str
211
+ session_id: str
212
+ channel_session_id: str
213
+ channel: str
214
+ provider_thread_id: str
215
+ provider_message_id: str
216
+ received_at_ms: int
217
+ sender: str | None
218
+ message_type: str
219
+ canonical_target: str
220
+ body: str
221
+ target_kind: ChannelTargetKind = ChannelTargetKind.DM
222
+ mentions_agent: bool = False
223
+ notifies_runtime: bool = True
224
+ attachments: tuple[InboundAttachment, ...] = ()
225
+ provider_time_ms: int | None = None
226
+ reply_to_message_id: str | None = None
227
+ provider_payload_ref: str | None = None
228
+ metadata: Metadata = field(default_factory=dict)
229
+
230
+ def __post_init__(self) -> None:
231
+ _validate_non_negative(self.seq, "seq")
232
+ for value, field_name in (
233
+ (self.message_id, "message_id"),
234
+ (self.session_id, "session_id"),
235
+ (self.channel_session_id, "channel_session_id"),
236
+ (self.channel, "channel"),
237
+ (self.provider_thread_id, "provider_thread_id"),
238
+ (self.provider_message_id, "provider_message_id"),
239
+ (self.message_type, "message_type"),
240
+ (self.canonical_target, "canonical_target"),
241
+ ):
242
+ _validate_text(value, field_name)
243
+ _validate_non_negative(self.received_at_ms, "received_at_ms")
244
+ if self.sender is not None:
245
+ _validate_text(self.sender, "sender")
246
+ if self.reply_to_message_id is not None:
247
+ _validate_text(self.reply_to_message_id, "reply_to_message_id")
248
+ if not isinstance(self.target_kind, ChannelTargetKind):
249
+ raise TypeError("target_kind must be a ChannelTargetKind")
250
+ if self.provider_time_ms is not None:
251
+ _validate_non_negative(self.provider_time_ms, "provider_time_ms")
252
+
253
+
254
+ @dataclass(frozen=True, slots=True)
255
+ class OutboundMessage:
256
+ outbound_message_id: str
257
+ command_id: str
258
+ session_id: str
259
+ channel_session_id: str
260
+ target: str
261
+ body: str
262
+ state: OutboundDeliveryState
263
+ fresh_check_state: FreshCheckState
264
+ created_at_ms: int
265
+ reply_to_message_id: str | None = None
266
+ snapshot_seq: int | None = None
267
+ current_inbound_seq: int | None = None
268
+ provider_message_id: str | None = None
269
+ provider_receipt_ref: str | None = None
270
+ provider_attempted_at_ms: int | None = None
271
+ completed_at_ms: int | None = None
272
+ draft_saved_at_ms: int | None = None
273
+ error_kind: str | None = None
274
+ error_message: str | None = None
275
+ next_action: str | None = None
276
+ metadata: Metadata = field(default_factory=dict)
277
+
278
+ def __post_init__(self) -> None:
279
+ for value, field_name in (
280
+ (self.outbound_message_id, "outbound_message_id"),
281
+ (self.command_id, "command_id"),
282
+ (self.session_id, "session_id"),
283
+ (self.channel_session_id, "channel_session_id"),
284
+ (self.target, "target"),
285
+ ):
286
+ _validate_text(value, field_name)
287
+ _validate_non_negative(self.created_at_ms, "created_at_ms")
288
+ for value, field_name in ((self.reply_to_message_id, "reply_to_message_id"),):
289
+ if value is not None:
290
+ _validate_text(value, field_name)
291
+ for value, field_name in (
292
+ (self.snapshot_seq, "snapshot_seq"),
293
+ (self.current_inbound_seq, "current_inbound_seq"),
294
+ (self.provider_attempted_at_ms, "provider_attempted_at_ms"),
295
+ (self.completed_at_ms, "completed_at_ms"),
296
+ (self.draft_saved_at_ms, "draft_saved_at_ms"),
297
+ ):
298
+ if value is not None:
299
+ _validate_non_negative(value, field_name)
300
+
301
+ def record_fresh_check(
302
+ self,
303
+ state: FreshCheckState,
304
+ *,
305
+ snapshot_seq: int | None,
306
+ current_inbound_seq: int | None,
307
+ ) -> Self:
308
+ if snapshot_seq is not None:
309
+ _validate_non_negative(snapshot_seq, "snapshot_seq")
310
+ if current_inbound_seq is not None:
311
+ _validate_non_negative(current_inbound_seq, "current_inbound_seq")
312
+ if state is FreshCheckState.PASSED:
313
+ if snapshot_seq is None or current_inbound_seq is None:
314
+ raise ValueError(
315
+ "a passed fresh check requires both sequence boundaries"
316
+ )
317
+ if current_inbound_seq > snapshot_seq:
318
+ raise ValueError(
319
+ "a passed fresh check cannot observe a newer inbound sequence"
320
+ )
321
+ ensure_transition(
322
+ "fresh_check",
323
+ self.fresh_check_state,
324
+ state,
325
+ FRESH_CHECK_TRANSITIONS,
326
+ )
327
+ return replace(
328
+ self,
329
+ fresh_check_state=state,
330
+ snapshot_seq=snapshot_seq,
331
+ current_inbound_seq=current_inbound_seq,
332
+ )
333
+
334
+ def transition_to(
335
+ self,
336
+ state: OutboundDeliveryState,
337
+ *,
338
+ at_ms: int,
339
+ save_draft: bool = True,
340
+ provider_message_id: str | None = None,
341
+ provider_receipt_ref: str | None = None,
342
+ error_kind: str | None = None,
343
+ error_message: str | None = None,
344
+ next_action: str | None = None,
345
+ ) -> Self:
346
+ _validate_non_negative(at_ms, "at_ms")
347
+ ensure_transition(
348
+ "outbound_delivery", self.state, state, OUTBOUND_DELIVERY_TRANSITIONS
349
+ )
350
+ if state is self.state:
351
+ return self
352
+ if (
353
+ state
354
+ in {
355
+ OutboundDeliveryState.PENDING,
356
+ OutboundDeliveryState.QUEUED,
357
+ OutboundDeliveryState.SENT,
358
+ OutboundDeliveryState.PARTIAL,
359
+ }
360
+ and self.fresh_check_state is not FreshCheckState.PASSED
361
+ ):
362
+ raise ValueError("outbound delivery requires a passed fresh check")
363
+ completed_at_ms = (
364
+ at_ms
365
+ if state
366
+ in {
367
+ OutboundDeliveryState.SENT,
368
+ OutboundDeliveryState.PARTIAL,
369
+ OutboundDeliveryState.FAILED,
370
+ OutboundDeliveryState.UNKNOWN,
371
+ OutboundDeliveryState.REJECTED,
372
+ }
373
+ else self.completed_at_ms
374
+ )
375
+ draft_saved_at_ms = (
376
+ at_ms
377
+ if state is OutboundDeliveryState.REJECTED and save_draft
378
+ else self.draft_saved_at_ms
379
+ )
380
+ return replace(
381
+ self,
382
+ state=state,
383
+ provider_message_id=provider_message_id or self.provider_message_id,
384
+ provider_receipt_ref=provider_receipt_ref or self.provider_receipt_ref,
385
+ completed_at_ms=completed_at_ms,
386
+ draft_saved_at_ms=draft_saved_at_ms,
387
+ error_kind=error_kind or self.error_kind,
388
+ error_message=error_message or self.error_message,
389
+ next_action=next_action or self.next_action,
390
+ )
391
+
392
+
393
+ @dataclass(frozen=True, slots=True)
394
+ class ConsumerCursor:
395
+ session_id: str
396
+ delivered_through_seq: int = 0
397
+ inbox_snapshot_seq: int | None = None
398
+ inbox_snapshot_source: str | None = None
399
+ inbox_snapshot_at_ms: int | None = None
400
+ last_check_at_ms: int | None = None
401
+ last_read_at_ms: int | None = None
402
+ updated_at_ms: int = 0
403
+
404
+ def __post_init__(self) -> None:
405
+ _validate_text(self.session_id, "session_id")
406
+ _validate_non_negative(self.delivered_through_seq, "delivered_through_seq")
407
+ _validate_non_negative(self.updated_at_ms, "updated_at_ms")
408
+ for value, field_name in (
409
+ (self.inbox_snapshot_seq, "inbox_snapshot_seq"),
410
+ (self.inbox_snapshot_at_ms, "inbox_snapshot_at_ms"),
411
+ (self.last_check_at_ms, "last_check_at_ms"),
412
+ (self.last_read_at_ms, "last_read_at_ms"),
413
+ ):
414
+ if value is not None:
415
+ _validate_non_negative(value, field_name)
416
+ if (
417
+ self.inbox_snapshot_seq is not None
418
+ and self.inbox_snapshot_seq < self.delivered_through_seq
419
+ ):
420
+ raise ValueError("inbox_snapshot_seq cannot precede delivered_through_seq")
421
+
422
+
423
+ @dataclass(frozen=True, slots=True)
424
+ class ApprovalRequest:
425
+ request_id: str
426
+ session_id: str
427
+ runtime_session_id: str
428
+ action: str
429
+ created_at_ms: int
430
+ turn_id: str | None = None
431
+ metadata: Metadata = field(default_factory=dict)
432
+
433
+ def __post_init__(self) -> None:
434
+ for value, field_name in (
435
+ (self.request_id, "request_id"),
436
+ (self.session_id, "session_id"),
437
+ (self.runtime_session_id, "runtime_session_id"),
438
+ (self.action, "action"),
439
+ ):
440
+ _validate_text(value, field_name)
441
+ _validate_non_negative(self.created_at_ms, "created_at_ms")
442
+ if self.turn_id is not None:
443
+ _validate_text(self.turn_id, "turn_id")
444
+
445
+
446
+ @dataclass(frozen=True, slots=True)
447
+ class ApprovalResult:
448
+ request_id: str
449
+ decision: ApprovalDecision
450
+ decided_at_ms: int
451
+ reason: str | None = None
452
+
453
+ def __post_init__(self) -> None:
454
+ _validate_text(self.request_id, "request_id")
455
+ _validate_non_negative(self.decided_at_ms, "decided_at_ms")
456
+
457
+
458
+ @dataclass(frozen=True, slots=True)
459
+ class StreamEvent:
460
+ kind: StreamEventKind
461
+ created_at_ms: int
462
+ session_id: str
463
+ stream_id: str | None = None
464
+ content: str | None = None
465
+
466
+ def __post_init__(self) -> None:
467
+ if not isinstance(self.kind, StreamEventKind):
468
+ raise TypeError("kind must be a StreamEventKind")
469
+ _validate_non_negative(self.created_at_ms, "created_at_ms")
470
+ _validate_text(self.session_id, "session_id")
471
+ if self.stream_id is not None:
472
+ _validate_text(self.stream_id, "stream_id")
473
+ if self.content is not None and not isinstance(self.content, str):
474
+ raise TypeError("content must be a string when present")
475
+
476
+
477
+ @dataclass(frozen=True, slots=True)
478
+ class RuntimeEvent:
479
+ event_seq: int
480
+ event_id: str
481
+ created_at_ms: int
482
+ level: str
483
+ event_name: str
484
+ state: RuntimeEventState
485
+ duration_ms: int | None = None
486
+ node_id: str | None = None
487
+ channel: str | None = None
488
+ channel_session_id: str | None = None
489
+ bcn_session_id: str | None = None
490
+ runtime_session_id: str | None = None
491
+ turn_id: str | None = None
492
+ request_id: str | None = None
493
+ command_id: str | None = None
494
+ inbound_seq: int | None = None
495
+ outbound_message_id: str | None = None
496
+ error_kind: str | None = None
497
+ error_type: str | None = None
498
+ error_message: str | None = None
499
+ traceback_ref: str | None = None
500
+ runtime: str | None = None
501
+ metadata: Metadata = field(default_factory=dict)
502
+
503
+ def __post_init__(self) -> None:
504
+ _validate_non_negative(self.event_seq, "event_seq")
505
+ _validate_text(self.event_id, "event_id")
506
+ _validate_non_negative(self.created_at_ms, "created_at_ms")
507
+ _validate_text(self.level, "level")
508
+ _validate_text(self.event_name, "event_name")
509
+ for value, field_name in (
510
+ (self.duration_ms, "duration_ms"),
511
+ (self.inbound_seq, "inbound_seq"),
512
+ ):
513
+ if value is not None:
514
+ _validate_non_negative(value, field_name)