spanforge 1.0.0__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 (174) hide show
  1. spanforge/__init__.py +815 -0
  2. spanforge/_ansi.py +93 -0
  3. spanforge/_batch_exporter.py +409 -0
  4. spanforge/_cli.py +2094 -0
  5. spanforge/_cli_audit.py +639 -0
  6. spanforge/_cli_compliance.py +711 -0
  7. spanforge/_cli_cost.py +243 -0
  8. spanforge/_cli_ops.py +791 -0
  9. spanforge/_cli_phase11.py +356 -0
  10. spanforge/_hooks.py +337 -0
  11. spanforge/_server.py +1708 -0
  12. spanforge/_span.py +1036 -0
  13. spanforge/_store.py +288 -0
  14. spanforge/_stream.py +664 -0
  15. spanforge/_trace.py +335 -0
  16. spanforge/_tracer.py +254 -0
  17. spanforge/actor.py +141 -0
  18. spanforge/alerts.py +469 -0
  19. spanforge/auto.py +464 -0
  20. spanforge/baseline.py +335 -0
  21. spanforge/cache.py +635 -0
  22. spanforge/compliance.py +325 -0
  23. spanforge/config.py +532 -0
  24. spanforge/consent.py +228 -0
  25. spanforge/consumer.py +377 -0
  26. spanforge/core/__init__.py +5 -0
  27. spanforge/core/compliance_mapping.py +1254 -0
  28. spanforge/cost.py +600 -0
  29. spanforge/debug.py +548 -0
  30. spanforge/deprecations.py +205 -0
  31. spanforge/drift.py +482 -0
  32. spanforge/egress.py +58 -0
  33. spanforge/eval.py +648 -0
  34. spanforge/event.py +1064 -0
  35. spanforge/exceptions.py +240 -0
  36. spanforge/explain.py +178 -0
  37. spanforge/export/__init__.py +69 -0
  38. spanforge/export/append_only.py +337 -0
  39. spanforge/export/cloud.py +357 -0
  40. spanforge/export/datadog.py +497 -0
  41. spanforge/export/grafana.py +320 -0
  42. spanforge/export/jsonl.py +195 -0
  43. spanforge/export/openinference.py +158 -0
  44. spanforge/export/otel_bridge.py +294 -0
  45. spanforge/export/otlp.py +811 -0
  46. spanforge/export/otlp_bridge.py +233 -0
  47. spanforge/export/redis_backend.py +282 -0
  48. spanforge/export/siem_schema.py +98 -0
  49. spanforge/export/siem_splunk.py +264 -0
  50. spanforge/export/siem_syslog.py +212 -0
  51. spanforge/export/webhook.py +299 -0
  52. spanforge/exporters/__init__.py +30 -0
  53. spanforge/exporters/console.py +271 -0
  54. spanforge/exporters/jsonl.py +144 -0
  55. spanforge/exporters/sqlite.py +142 -0
  56. spanforge/gate.py +1150 -0
  57. spanforge/governance.py +181 -0
  58. spanforge/hitl.py +295 -0
  59. spanforge/http.py +187 -0
  60. spanforge/inspect.py +427 -0
  61. spanforge/integrations/__init__.py +45 -0
  62. spanforge/integrations/_pricing.py +280 -0
  63. spanforge/integrations/anthropic.py +388 -0
  64. spanforge/integrations/azure_openai.py +133 -0
  65. spanforge/integrations/bedrock.py +292 -0
  66. spanforge/integrations/crewai.py +251 -0
  67. spanforge/integrations/gemini.py +351 -0
  68. spanforge/integrations/groq.py +442 -0
  69. spanforge/integrations/langchain.py +349 -0
  70. spanforge/integrations/langgraph.py +306 -0
  71. spanforge/integrations/llamaindex.py +373 -0
  72. spanforge/integrations/ollama.py +287 -0
  73. spanforge/integrations/openai.py +368 -0
  74. spanforge/integrations/together.py +483 -0
  75. spanforge/io.py +214 -0
  76. spanforge/lint.py +322 -0
  77. spanforge/metrics.py +417 -0
  78. spanforge/metrics_export.py +343 -0
  79. spanforge/migrate.py +402 -0
  80. spanforge/model_registry.py +278 -0
  81. spanforge/models.py +389 -0
  82. spanforge/namespaces/__init__.py +254 -0
  83. spanforge/namespaces/audit.py +256 -0
  84. spanforge/namespaces/cache.py +237 -0
  85. spanforge/namespaces/chain.py +77 -0
  86. spanforge/namespaces/confidence.py +72 -0
  87. spanforge/namespaces/consent.py +92 -0
  88. spanforge/namespaces/cost.py +179 -0
  89. spanforge/namespaces/decision.py +143 -0
  90. spanforge/namespaces/diff.py +157 -0
  91. spanforge/namespaces/drift.py +80 -0
  92. spanforge/namespaces/eval_.py +251 -0
  93. spanforge/namespaces/feedback.py +241 -0
  94. spanforge/namespaces/fence.py +193 -0
  95. spanforge/namespaces/guard.py +105 -0
  96. spanforge/namespaces/hitl.py +91 -0
  97. spanforge/namespaces/latency.py +72 -0
  98. spanforge/namespaces/prompt.py +190 -0
  99. spanforge/namespaces/redact.py +173 -0
  100. spanforge/namespaces/retrieval.py +379 -0
  101. spanforge/namespaces/runtime_governance.py +494 -0
  102. spanforge/namespaces/template.py +208 -0
  103. spanforge/namespaces/tool_call.py +77 -0
  104. spanforge/namespaces/trace.py +1029 -0
  105. spanforge/normalizer.py +171 -0
  106. spanforge/plugins.py +82 -0
  107. spanforge/presidio_backend.py +349 -0
  108. spanforge/processor.py +258 -0
  109. spanforge/prompt_registry.py +418 -0
  110. spanforge/py.typed +0 -0
  111. spanforge/redact.py +914 -0
  112. spanforge/regression.py +192 -0
  113. spanforge/runtime_policy.py +159 -0
  114. spanforge/sampling.py +511 -0
  115. spanforge/schema.py +183 -0
  116. spanforge/schemas/v1.0/schema.json +170 -0
  117. spanforge/schemas/v2.0/schema.json +536 -0
  118. spanforge/sdk/__init__.py +625 -0
  119. spanforge/sdk/_base.py +584 -0
  120. spanforge/sdk/_base.pyi +71 -0
  121. spanforge/sdk/_exceptions.py +1096 -0
  122. spanforge/sdk/_types.py +2184 -0
  123. spanforge/sdk/alert.py +1514 -0
  124. spanforge/sdk/alert.pyi +56 -0
  125. spanforge/sdk/audit.py +1196 -0
  126. spanforge/sdk/audit.pyi +67 -0
  127. spanforge/sdk/cec.py +1215 -0
  128. spanforge/sdk/cec.pyi +37 -0
  129. spanforge/sdk/config.py +641 -0
  130. spanforge/sdk/config.pyi +55 -0
  131. spanforge/sdk/enterprise.py +714 -0
  132. spanforge/sdk/enterprise.pyi +79 -0
  133. spanforge/sdk/explain.py +170 -0
  134. spanforge/sdk/fallback.py +432 -0
  135. spanforge/sdk/feedback.py +351 -0
  136. spanforge/sdk/gate.py +874 -0
  137. spanforge/sdk/gate.pyi +51 -0
  138. spanforge/sdk/identity.py +2114 -0
  139. spanforge/sdk/identity.pyi +47 -0
  140. spanforge/sdk/lineage.py +175 -0
  141. spanforge/sdk/observe.py +1065 -0
  142. spanforge/sdk/observe.pyi +50 -0
  143. spanforge/sdk/operator.py +338 -0
  144. spanforge/sdk/pii.py +1473 -0
  145. spanforge/sdk/pii.pyi +119 -0
  146. spanforge/sdk/pipelines.py +458 -0
  147. spanforge/sdk/pipelines.pyi +39 -0
  148. spanforge/sdk/policy.py +930 -0
  149. spanforge/sdk/rag.py +594 -0
  150. spanforge/sdk/rbac.py +280 -0
  151. spanforge/sdk/registry.py +430 -0
  152. spanforge/sdk/registry.pyi +46 -0
  153. spanforge/sdk/scope.py +279 -0
  154. spanforge/sdk/secrets.py +293 -0
  155. spanforge/sdk/secrets.pyi +25 -0
  156. spanforge/sdk/security.py +560 -0
  157. spanforge/sdk/security.pyi +57 -0
  158. spanforge/sdk/trust.py +472 -0
  159. spanforge/sdk/trust.pyi +41 -0
  160. spanforge/secrets.py +799 -0
  161. spanforge/signing.py +1179 -0
  162. spanforge/stats.py +100 -0
  163. spanforge/stream.py +560 -0
  164. spanforge/testing.py +378 -0
  165. spanforge/testing_mocks.py +1052 -0
  166. spanforge/trace.py +199 -0
  167. spanforge/types.py +696 -0
  168. spanforge/ulid.py +300 -0
  169. spanforge/validate.py +379 -0
  170. spanforge-1.0.0.dist-info/METADATA +1509 -0
  171. spanforge-1.0.0.dist-info/RECORD +174 -0
  172. spanforge-1.0.0.dist-info/WHEEL +4 -0
  173. spanforge-1.0.0.dist-info/entry_points.txt +5 -0
  174. spanforge-1.0.0.dist-info/licenses/LICENSE +128 -0
spanforge/models.py ADDED
@@ -0,0 +1,389 @@
1
+ """Pydantic v2 model layer for spanforge events.
2
+
3
+ This module provides Pydantic v2 models that mirror the :class:`~spanforge.event.Event`
4
+ envelope with strict field-level validation and bidirectional conversion.
5
+
6
+ The model layer is **optional** — it requires ``pydantic>=2.7`` which is not a
7
+ core dependency. Install it with::
8
+
9
+ pip install "spanforge[pydantic]"
10
+
11
+ Design goals
12
+ ------------
13
+ * All field validation is equivalent to :meth:`~spanforge.event.Event.validate`,
14
+ giving callers a familiar API while leveraging Pydantic's declarative style.
15
+ * :class:`EventModel` is immutable (``frozen=True``).
16
+ * :meth:`EventModel.from_event` and :meth:`EventModel.to_event` provide lossless
17
+ round-trips.
18
+ * :meth:`EventModel.model_json_schema` exports a full JSON Schema (for Phase 5
19
+ schema publication).
20
+
21
+ Example::
22
+
23
+ from spanforge import Event, EventType
24
+ from spanforge.models import EventModel
25
+
26
+ event = Event(
27
+ event_type=EventType.TRACE_SPAN_COMPLETED,
28
+ source="llm-trace@0.3.1",
29
+ payload={"status": "ok"},
30
+ )
31
+ model = EventModel.from_event(event)
32
+ print(model.model_json_schema())
33
+ restored = model.to_event()
34
+ assert restored.event_id == event.event_id
35
+ """
36
+
37
+ from __future__ import annotations
38
+
39
+ import re
40
+ from typing import Any
41
+
42
+ try:
43
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
44
+ from pydantic import ValidationError as _PydanticValidationError
45
+ except ImportError as _import_err: # pragma: no cover
46
+ raise ImportError(
47
+ "pydantic>=2.7 is required for spanforge.models. "
48
+ 'Install it: pip install "spanforge[pydantic]"'
49
+ ) from _import_err
50
+
51
+ from spanforge.event import SCHEMA_VERSION, Event, Tags
52
+ from spanforge.types import EVENT_TYPE_PATTERN
53
+ from spanforge.ulid import validate as _validate_ulid
54
+
55
+ __all__ = ["EventModel", "TagsModel"]
56
+
57
+ # ---------------------------------------------------------------------------
58
+ # Validation patterns (must stay in sync with spanforge/event.py)
59
+ # ---------------------------------------------------------------------------
60
+
61
+ _SEMVER_RE: re.Pattern[str] = re.compile(r"^\d+\.\d+(?:\.\d+)?(?:[.-][a-zA-Z0-9.]+)?$")
62
+ _SOURCE_RE: re.Pattern[str] = re.compile(r"^[a-z][a-z0-9\-]*@\d+\.\d+\.\d+$")
63
+ _TIMESTAMP_RE: re.Pattern[str] = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$")
64
+ _TRACE_ID_RE: re.Pattern[str] = re.compile(r"^[0-9a-f]{32}$")
65
+ _SPAN_ID_RE: re.Pattern[str] = re.compile(r"^[0-9a-f]{16}$")
66
+ _EVENT_TYPE_RE: re.Pattern[str] = re.compile(EVENT_TYPE_PATTERN)
67
+
68
+
69
+ # ---------------------------------------------------------------------------
70
+ # TagsModel
71
+ # ---------------------------------------------------------------------------
72
+
73
+
74
+ class TagsModel(BaseModel):
75
+ """Pydantic model for event tags.
76
+
77
+ Allows arbitrary ``str → str`` key-value pairs as extra fields. All
78
+ values must be strings; non-string values are rejected by Pydantic.
79
+
80
+ Example::
81
+
82
+ tags = TagsModel(env="production", model="gpt-4o")
83
+ tags.model_dump() # {"env": "production", "model": "gpt-4o"}
84
+ """
85
+
86
+ model_config = ConfigDict(frozen=True, extra="allow")
87
+
88
+ @classmethod
89
+ def from_tags(cls, tags: Tags) -> TagsModel:
90
+ """Construct from a :class:`~spanforge.event.Tags` instance.
91
+
92
+ Args:
93
+ tags: A :class:`~spanforge.event.Tags` instance.
94
+
95
+ Returns:
96
+ A corresponding :class:`TagsModel`.
97
+ """
98
+ return cls(**dict(tags))
99
+
100
+ def to_tags(self) -> Tags:
101
+ """Convert back to a :class:`~spanforge.event.Tags` instance.
102
+
103
+ Returns:
104
+ A new :class:`~spanforge.event.Tags` with the same key-value pairs.
105
+ """
106
+ return Tags(**self.model_dump())
107
+
108
+
109
+ # ---------------------------------------------------------------------------
110
+ # EventModel
111
+ # ---------------------------------------------------------------------------
112
+
113
+
114
+ class EventModel(BaseModel):
115
+ """Pydantic v2 model for the spanforge event envelope.
116
+
117
+ Each field carries a Pydantic ``Field`` description and is validated by a
118
+ ``@field_validator``. The model is frozen (immutable after construction).
119
+
120
+ Validation rules are equivalent to those enforced by
121
+ :meth:`~spanforge.event.Event.validate`, so ``EventModel.from_event(event)``
122
+ succeeds for any event that passes :meth:`~spanforge.event.Event.validate`.
123
+
124
+ Args:
125
+ schema_version: Schema version string (e.g. ``"1.0"``).
126
+ event_id: 26-character ULID.
127
+ event_type: Namespaced event type (e.g. ``"llm.trace.span.completed"``).
128
+ timestamp: UTC ISO-8601 timestamp (e.g. ``"2026-03-01T12:00:00.000000Z"``).
129
+ source: Tool name + version (e.g. ``"llm-trace@0.3.1"``).
130
+ payload: Non-empty dict of event-type-specific data.
131
+ trace_id: Optional 32-char hex OpenTelemetry trace ID.
132
+ span_id: Optional 16-char hex OpenTelemetry span ID.
133
+ parent_span_id: Optional 16-char hex parent span ID.
134
+ org_id: Optional organisation identifier.
135
+ team_id: Optional team identifier.
136
+ actor_id: Optional user/service identifier.
137
+ session_id: Optional session/conversation identifier.
138
+ tags: Optional :class:`TagsModel` with arbitrary metadata.
139
+ checksum: Optional SHA-256 payload checksum.
140
+ signature: Optional HMAC-SHA256 audit chain signature.
141
+ prev_id: Optional ULID of preceding event in audit chain.
142
+
143
+ Example::
144
+
145
+ from spanforge.models import EventModel
146
+
147
+ model = EventModel(
148
+ event_id="01ARYZ3NDEKTSV4RRFFQ69G5FAV",
149
+ event_type="llm.trace.span.completed",
150
+ timestamp="2026-03-01T12:00:00.000000Z",
151
+ source="llm-trace@0.3.1",
152
+ payload={"status": "ok"},
153
+ )
154
+ """
155
+
156
+ model_config = ConfigDict(frozen=True, populate_by_name=True)
157
+
158
+ schema_version: str = Field(
159
+ default=SCHEMA_VERSION,
160
+ description="Schema version string, e.g. '1.0'.",
161
+ )
162
+ event_id: str = Field(
163
+ description="26-character Crockford Base32 ULID event identifier.",
164
+ )
165
+ event_type: str = Field(
166
+ description="Namespaced event type, e.g. 'llm.trace.span.completed'.",
167
+ )
168
+ timestamp: str = Field(
169
+ description="UTC ISO-8601 timestamp, e.g. '2026-03-01T12:00:00.000000Z'.",
170
+ )
171
+ source: str = Field(
172
+ description="Source tool and version, e.g. 'llm-trace@0.3.1'.",
173
+ )
174
+ payload: dict[str, Any] = Field(
175
+ description="Non-empty dict of event-type-specific data.",
176
+ )
177
+ trace_id: str | None = Field(
178
+ default=None,
179
+ description="OpenTelemetry trace ID — 32 lowercase hex characters.",
180
+ )
181
+ span_id: str | None = Field(
182
+ default=None,
183
+ description="OpenTelemetry span ID — 16 lowercase hex characters.",
184
+ )
185
+ parent_span_id: str | None = Field(
186
+ default=None,
187
+ description="Parent span ID — 16 lowercase hex characters.",
188
+ )
189
+ org_id: str | None = Field(
190
+ default=None,
191
+ description="Organisation identifier (non-empty string).",
192
+ )
193
+ team_id: str | None = Field(
194
+ default=None,
195
+ description="Team identifier within the organisation (non-empty string).",
196
+ )
197
+ actor_id: str | None = Field(
198
+ default=None,
199
+ description="User or service actor identifier (non-empty string).",
200
+ )
201
+ session_id: str | None = Field(
202
+ default=None,
203
+ description="Session or conversation identifier (non-empty string).",
204
+ )
205
+ tags: TagsModel | None = Field(
206
+ default=None,
207
+ description="Arbitrary string key-value metadata tags.",
208
+ )
209
+ checksum: str | None = Field(
210
+ default=None,
211
+ description="SHA-256 payload checksum (prefixed 'sha256:').",
212
+ )
213
+ signature: str | None = Field(
214
+ default=None,
215
+ description="HMAC-SHA256 audit chain signature (set by spanforge.signing).",
216
+ )
217
+ prev_id: str | None = Field(
218
+ default=None,
219
+ description="ULID of the preceding event in the tamper-evident audit chain.",
220
+ )
221
+
222
+ # ------------------------------------------------------------------
223
+ # Field validators
224
+ # ------------------------------------------------------------------
225
+
226
+ @field_validator("schema_version")
227
+ @classmethod
228
+ def _check_schema_version(cls, v: str) -> str:
229
+ if not _SEMVER_RE.match(v):
230
+ raise ValueError(f"schema_version must match SemVer pattern (e.g. '1.0'), got {v!r}")
231
+ return v
232
+
233
+ @field_validator("event_id")
234
+ @classmethod
235
+ def _check_event_id(cls, v: str) -> str:
236
+ if not _validate_ulid(v):
237
+ raise ValueError("event_id must be a valid 26-character ULID (Crockford Base32)")
238
+ return v
239
+
240
+ @field_validator("event_type")
241
+ @classmethod
242
+ def _check_event_type(cls, v: str) -> str:
243
+ if not _EVENT_TYPE_RE.match(v):
244
+ raise ValueError(
245
+ "event_type must follow 'llm.<namespace>.<entity>.<action>' "
246
+ "or 'x.<company>.<…>' pattern"
247
+ )
248
+ return v
249
+
250
+ @field_validator("timestamp")
251
+ @classmethod
252
+ def _check_timestamp(cls, v: str) -> str:
253
+ if not _TIMESTAMP_RE.match(v):
254
+ raise ValueError(
255
+ "timestamp must be a UTC ISO-8601 string ending in 'Z', "
256
+ f"e.g. '2026-03-01T12:00:00.000000Z', got {v!r}"
257
+ )
258
+ return v
259
+
260
+ @field_validator("source")
261
+ @classmethod
262
+ def _check_source(cls, v: str) -> str:
263
+ if not _SOURCE_RE.match(v):
264
+ raise ValueError(
265
+ "source must match 'tool-name@semver' pattern (full 3-part semver), "
266
+ f"e.g. 'llm-trace@0.3.1', got {v!r}"
267
+ )
268
+ return v
269
+
270
+ @field_validator("payload")
271
+ @classmethod
272
+ def _check_payload(cls, v: dict[str, Any]) -> dict[str, Any]:
273
+ if not v:
274
+ raise ValueError("payload must be a non-empty dict")
275
+ return v
276
+
277
+ @field_validator("trace_id")
278
+ @classmethod
279
+ def _check_trace_id(cls, v: str | None) -> str | None:
280
+ if v is not None and not _TRACE_ID_RE.match(v):
281
+ raise ValueError("trace_id must be exactly 32 lowercase hex characters")
282
+ return v
283
+
284
+ @field_validator("span_id", "parent_span_id")
285
+ @classmethod
286
+ def _check_span_id(cls, v: str | None) -> str | None:
287
+ if v is not None and not _SPAN_ID_RE.match(v):
288
+ raise ValueError("span_id / parent_span_id must be exactly 16 lowercase hex characters")
289
+ return v
290
+
291
+ @field_validator("org_id", "team_id", "actor_id", "session_id")
292
+ @classmethod
293
+ def _check_string_id(cls, v: str | None) -> str | None:
294
+ if v is not None and not v.strip():
295
+ raise ValueError("org_id / team_id / actor_id / session_id must be non-empty")
296
+ return v
297
+
298
+ @field_validator("prev_id")
299
+ @classmethod
300
+ def _check_prev_id(cls, v: str | None) -> str | None:
301
+ if v is not None and not _validate_ulid(v):
302
+ raise ValueError("prev_id must be a valid 26-character ULID (Crockford Base32)")
303
+ return v
304
+
305
+ # ------------------------------------------------------------------
306
+ # Conversion helpers
307
+ # ------------------------------------------------------------------
308
+
309
+ @classmethod
310
+ def from_event(cls, event: Event) -> EventModel:
311
+ """Construct an :class:`EventModel` from an :class:`~spanforge.event.Event`.
312
+
313
+ Args:
314
+ event: A validated or unvalidated :class:`~spanforge.event.Event`.
315
+
316
+ Returns:
317
+ A new :class:`EventModel` with all fields populated.
318
+
319
+ Raises:
320
+ pydantic.ValidationError: If the event contains invalid field values.
321
+
322
+ Example::
323
+
324
+ model = EventModel.from_event(event)
325
+ """
326
+ tags_model: TagsModel | None = (
327
+ TagsModel.from_tags(event.tags) if event.tags is not None else None
328
+ )
329
+ return cls(
330
+ schema_version=event.schema_version,
331
+ event_id=event.event_id,
332
+ event_type=event.event_type,
333
+ timestamp=event.timestamp,
334
+ source=event.source,
335
+ payload=dict(event.payload),
336
+ trace_id=event.trace_id,
337
+ span_id=event.span_id,
338
+ parent_span_id=event.parent_span_id,
339
+ org_id=event.org_id,
340
+ team_id=event.team_id,
341
+ actor_id=event.actor_id,
342
+ session_id=event.session_id,
343
+ tags=tags_model,
344
+ checksum=event.checksum,
345
+ signature=event.signature,
346
+ prev_id=event.prev_id,
347
+ )
348
+
349
+ def to_event(self) -> Event:
350
+ """Convert this model back to an :class:`~spanforge.event.Event`.
351
+
352
+ The returned event has the same field values as this model. Call
353
+ :meth:`~spanforge.event.Event.validate` if you want to re-run all
354
+ built-in validators (they are equivalent to those already applied by
355
+ Pydantic during construction of this model).
356
+
357
+ Returns:
358
+ A new :class:`~spanforge.event.Event` instance.
359
+
360
+ Example::
361
+
362
+ event = model.to_event()
363
+ assert event.event_id == model.event_id
364
+ """
365
+ tags: Tags | None = self.tags.to_tags() if self.tags is not None else None
366
+ kwargs: dict[str, Any] = {
367
+ k: v
368
+ for k, v in {
369
+ "schema_version": self.schema_version,
370
+ "event_id": self.event_id,
371
+ "event_type": self.event_type,
372
+ "timestamp": self.timestamp,
373
+ "source": self.source,
374
+ "payload": dict(self.payload),
375
+ "trace_id": self.trace_id,
376
+ "span_id": self.span_id,
377
+ "parent_span_id": self.parent_span_id,
378
+ "org_id": self.org_id,
379
+ "team_id": self.team_id,
380
+ "actor_id": self.actor_id,
381
+ "session_id": self.session_id,
382
+ "tags": tags,
383
+ "checksum": self.checksum,
384
+ "signature": self.signature,
385
+ "prev_id": self.prev_id,
386
+ }.items()
387
+ if v is not None
388
+ }
389
+ return Event(**kwargs)
@@ -0,0 +1,254 @@
1
+ """spanforge.namespaces — Namespace-specific payload dataclasses (v2.0).
2
+
3
+ Each sub-module provides dataclasses that model the ``payload`` field of
4
+ :class:`~spanforge.event.Event` for a given namespace.
5
+
6
+ All payload classes share the same contract:
7
+
8
+ * ``to_dict() -> dict`` — serialise to a plain dict for ``Event.payload``.
9
+ * ``from_dict(data) -> cls`` — reconstruct from a plain dict.
10
+ * ``__post_init__`` — validates every field at construction time.
11
+
12
+ Sub-modules
13
+ -----------
14
+ audit
15
+ :class:`AuditKeyRotatedPayload`, :class:`AuditChainVerifiedPayload`,
16
+ :class:`AuditChainTamperedPayload`, :class:`AuditChainPayload`
17
+ cache
18
+ :class:`CacheHitPayload`, :class:`CacheMissPayload`,
19
+ :class:`CacheEvictedPayload`, :class:`CacheWrittenPayload`
20
+ chain (RFC-0001 SPANFORGE)
21
+ :class:`ChainPayload`
22
+ confidence (RFC-0001 SPANFORGE)
23
+ :class:`ConfidencePayload`
24
+ consent (RFC-0001 SPANFORGE)
25
+ :class:`ConsentPayload`
26
+ hitl (RFC-0001 SPANFORGE)
27
+ :class:`HITLPayload`
28
+ playbook (RFC-0001 SPANFORGE)
29
+ *removed*
30
+ cost
31
+ :class:`CostTokenRecordedPayload`, :class:`CostSessionRecordedPayload`,
32
+ :class:`CostAttributedPayload`
33
+ decision (RFC-0001 SPANFORGE)
34
+ :class:`DecisionDriver`, :class:`DecisionPayload`
35
+ diff
36
+ :class:`DiffComputedPayload`, :class:`DiffRegressionFlaggedPayload`
37
+ drift (RFC-0001 SPANFORGE)
38
+ :class:`DriftPayload`
39
+ eval_
40
+ :class:`EvalScoreRecordedPayload`, :class:`EvalRegressionDetectedPayload`,
41
+ :class:`EvalScenarioStartedPayload`, :class:`EvalScenarioCompletedPayload`
42
+ fence
43
+ :class:`FenceValidatedPayload`, :class:`FenceRetryTriggeredPayload`,
44
+ :class:`FenceMaxRetriesExceededPayload`
45
+ guard
46
+ :class:`GuardPayload`
47
+ latency (RFC-0001 SPANFORGE)
48
+ :class:`LatencyPayload`
49
+ playbook (RFC-0001 SPANFORGE)
50
+ *removed*
51
+ prompt
52
+ :class:`PromptRenderedPayload`, :class:`PromptTemplateLoadedPayload`,
53
+ :class:`PromptVersionChangedPayload`
54
+ redact
55
+ :class:`RedactPiiDetectedPayload`, :class:`RedactPhiDetectedPayload`,
56
+ :class:`RedactAppliedPayload`
57
+ template
58
+ :class:`TemplateRegisteredPayload`, :class:`TemplateVariableBoundPayload`,
59
+ :class:`TemplateValidationFailedPayload`
60
+ tool_call (RFC-0001 SPANFORGE)
61
+ :class:`ToolCallPayload`
62
+ trace
63
+ :class:`GenAISystem`, :class:`GenAIOperationName`, :class:`SpanKind`,
64
+ :class:`TokenUsage`, :class:`ModelInfo`, :class:`CostBreakdown`,
65
+ :class:`PricingTier`, :class:`ToolCall`, :class:`ReasoningStep`,
66
+ :class:`DecisionPoint`, :class:`SpanPayload`, :class:`AgentStepPayload`,
67
+ :class:`AgentRunPayload`
68
+ """
69
+
70
+ from spanforge.namespaces.audit import (
71
+ AuditChainPayload,
72
+ AuditChainTamperedPayload,
73
+ AuditChainVerifiedPayload,
74
+ AuditKeyRotatedPayload,
75
+ )
76
+ from spanforge.namespaces.cache import (
77
+ CacheEvictedPayload,
78
+ CacheHitPayload,
79
+ CacheMissPayload,
80
+ CacheWrittenPayload,
81
+ )
82
+ from spanforge.namespaces.chain import ChainPayload
83
+ from spanforge.namespaces.confidence import ConfidencePayload
84
+ from spanforge.namespaces.consent import ConsentPayload
85
+ from spanforge.namespaces.cost import (
86
+ CostAttributedPayload,
87
+ CostSessionRecordedPayload,
88
+ CostTokenRecordedPayload,
89
+ )
90
+ from spanforge.namespaces.decision import DecisionDriver, DecisionPayload
91
+ from spanforge.namespaces.diff import (
92
+ DiffComputedPayload,
93
+ DiffRegressionFlaggedPayload,
94
+ )
95
+ from spanforge.namespaces.drift import DriftPayload
96
+ from spanforge.namespaces.eval_ import (
97
+ EvalRegressionDetectedPayload,
98
+ EvalScenarioCompletedPayload,
99
+ EvalScenarioStartedPayload,
100
+ EvalScoreRecordedPayload,
101
+ )
102
+ from spanforge.namespaces.feedback import (
103
+ FeedbackRating,
104
+ FeedbackSubmittedPayload,
105
+ FeedbackSummaryPayload,
106
+ )
107
+ from spanforge.namespaces.fence import (
108
+ FenceMaxRetriesExceededPayload,
109
+ FenceRetryTriggeredPayload,
110
+ FenceValidatedPayload,
111
+ )
112
+ from spanforge.namespaces.guard import GuardPayload
113
+ from spanforge.namespaces.hitl import HITLPayload
114
+ from spanforge.namespaces.latency import LatencyPayload
115
+ from spanforge.namespaces.prompt import (
116
+ PromptRenderedPayload,
117
+ PromptTemplateLoadedPayload,
118
+ PromptVersionChangedPayload,
119
+ )
120
+ from spanforge.namespaces.redact import (
121
+ RedactAppliedPayload,
122
+ RedactPhiDetectedPayload,
123
+ RedactPiiDetectedPayload,
124
+ )
125
+ from spanforge.namespaces.retrieval import (
126
+ RAGSessionPayload,
127
+ RAGSpanPayload,
128
+ RetrievalQueryPayload,
129
+ RetrievalResultPayload,
130
+ RetrievedChunk,
131
+ )
132
+ from spanforge.namespaces.runtime_governance import (
133
+ ExplanationFactor,
134
+ ExplanationPayload,
135
+ GroundingClaim,
136
+ GroundingPayload,
137
+ LineagePayload,
138
+ RBACDecisionPayload,
139
+ ScopeDecisionPayload,
140
+ )
141
+ from spanforge.namespaces.template import (
142
+ TemplateRegisteredPayload,
143
+ TemplateValidationFailedPayload,
144
+ TemplateVariableBoundPayload,
145
+ )
146
+ from spanforge.namespaces.tool_call import ToolCallPayload
147
+ from spanforge.namespaces.trace import (
148
+ AgentRunPayload,
149
+ AgentStepPayload,
150
+ CostBreakdown,
151
+ DecisionPoint,
152
+ GenAIOperationName,
153
+ GenAISystem,
154
+ ModelInfo,
155
+ PricingTier,
156
+ ReasoningStep,
157
+ SpanKind,
158
+ SpanPayload,
159
+ TokenUsage,
160
+ ToolCall,
161
+ )
162
+
163
+ __all__: list[str] = [
164
+ "AgentRunPayload",
165
+ "AgentStepPayload",
166
+ # audit (legacy + RFC-0001 SPANFORGE)
167
+ "AuditChainPayload",
168
+ "AuditChainTamperedPayload",
169
+ "AuditChainVerifiedPayload",
170
+ "AuditKeyRotatedPayload",
171
+ # cache
172
+ "CacheEvictedPayload",
173
+ "CacheHitPayload",
174
+ "CacheMissPayload",
175
+ "CacheWrittenPayload",
176
+ # chain (RFC-0001 SPANFORGE)
177
+ "ChainPayload",
178
+ # confidence (RFC-0001 SPANFORGE)
179
+ "ConfidencePayload",
180
+ # consent (RFC-0001 SPANFORGE)
181
+ "ConsentPayload",
182
+ # cost
183
+ "CostAttributedPayload",
184
+ "CostBreakdown",
185
+ "CostSessionRecordedPayload",
186
+ "CostTokenRecordedPayload",
187
+ # decision (RFC-0001 SPANFORGE)
188
+ "DecisionDriver",
189
+ "DecisionPayload",
190
+ # Backward-compat trace value object
191
+ "DecisionPoint",
192
+ # diff
193
+ "DiffComputedPayload",
194
+ "DiffRegressionFlaggedPayload",
195
+ # drift (RFC-0001 SPANFORGE)
196
+ "DriftPayload",
197
+ # eval
198
+ "EvalRegressionDetectedPayload",
199
+ "EvalScenarioCompletedPayload",
200
+ "EvalScenarioStartedPayload",
201
+ "EvalScoreRecordedPayload",
202
+ # fence
203
+ "FenceMaxRetriesExceededPayload",
204
+ "FenceRetryTriggeredPayload",
205
+ "FenceValidatedPayload",
206
+ # trace — value objects and payloads
207
+ "GenAIOperationName",
208
+ "GenAISystem",
209
+ # guard
210
+ "GuardPayload",
211
+ # hitl (RFC-0001 SPANFORGE)
212
+ "HITLPayload",
213
+ # latency (RFC-0001 SPANFORGE)
214
+ "LatencyPayload",
215
+ "ModelInfo",
216
+ "PricingTier",
217
+ # prompt
218
+ "PromptRenderedPayload",
219
+ "PromptTemplateLoadedPayload",
220
+ "PromptVersionChangedPayload",
221
+ "ReasoningStep",
222
+ # redact
223
+ "RedactAppliedPayload",
224
+ "RedactPhiDetectedPayload",
225
+ "RedactPiiDetectedPayload",
226
+ "SpanKind",
227
+ "SpanPayload",
228
+ # template
229
+ "TemplateRegisteredPayload",
230
+ "TemplateValidationFailedPayload",
231
+ "TemplateVariableBoundPayload",
232
+ "TokenUsage",
233
+ "ToolCall",
234
+ # tool_call (RFC-0001 SPANFORGE)
235
+ "ToolCallPayload",
236
+ # retrieval / RAG
237
+ "RAGSessionPayload",
238
+ "RAGSpanPayload",
239
+ "RetrievalQueryPayload",
240
+ "RetrievalResultPayload",
241
+ "RetrievedChunk",
242
+ # runtime governance GA payloads
243
+ "ExplanationFactor",
244
+ "ExplanationPayload",
245
+ "GroundingClaim",
246
+ "GroundingPayload",
247
+ "LineagePayload",
248
+ "ScopeDecisionPayload",
249
+ "RBACDecisionPayload",
250
+ # feedback
251
+ "FeedbackRating",
252
+ "FeedbackSubmittedPayload",
253
+ "FeedbackSummaryPayload",
254
+ ]