xrtm-data 0.2.6__tar.gz → 0.2.7__tar.gz

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 (49) hide show
  1. {xrtm_data-0.2.6/src/xrtm_data.egg-info → xrtm_data-0.2.7}/PKG-INFO +20 -11
  2. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/README.md +19 -10
  3. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/pyproject.toml +1 -1
  4. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/__init__.py +8 -0
  5. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/core/schemas/__init__.py +8 -0
  6. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/core/schemas/forecast.py +201 -36
  7. {xrtm_data-0.2.6 → xrtm_data-0.2.7/src/xrtm_data.egg-info}/PKG-INFO +20 -11
  8. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_schemas.py +34 -10
  9. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/LICENSE +0 -0
  10. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/setup.cfg +0 -0
  11. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/cli/__init__.py +0 -0
  12. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/core/__init__.py +0 -0
  13. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/core/interfaces.py +0 -0
  14. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/core/schemas/prior.py +0 -0
  15. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/core/schemas/trade.py +0 -0
  16. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/corpora/__init__.py +0 -0
  17. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/corpora/_builtin_corpora.py +0 -0
  18. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/corpora/forecast_importer.py +0 -0
  19. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/corpora/importers.py +0 -0
  20. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/corpora/real_binary.py +0 -0
  21. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/corpora/registry.py +0 -0
  22. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/corpora/splits.py +0 -0
  23. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/kit/__init__.py +0 -0
  24. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/kit/processors/__init__.py +0 -0
  25. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/providers/__init__.py +0 -0
  26. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/providers/local/__init__.py +0 -0
  27. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/providers/local/csv.py +0 -0
  28. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/providers/online/__init__.py +0 -0
  29. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/providers/online/polymarket.py +0 -0
  30. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/providers/subgraph/__init__.py +0 -0
  31. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/providers/subgraph/polymarket.py +0 -0
  32. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm/data/version.py +0 -0
  33. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm_data.egg-info/SOURCES.txt +0 -0
  34. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm_data.egg-info/dependency_links.txt +0 -0
  35. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm_data.egg-info/entry_points.txt +0 -0
  36. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm_data.egg-info/requires.txt +0 -0
  37. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/src/xrtm_data.egg-info/top_level.txt +0 -0
  38. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_beta_fitter.py +0 -0
  39. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_cli_loading.py +0 -0
  40. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_cli_ux.py +0 -0
  41. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_corpus_importers.py +0 -0
  42. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_corpus_registry.py +0 -0
  43. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_corpus_splits.py +0 -0
  44. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_forecast_importer.py +0 -0
  45. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_local_datasource.py +0 -0
  46. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_polymarket_source.py +0 -0
  47. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_polymarket_subgraph.py +0 -0
  48. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_prior_schemas.py +0 -0
  49. {xrtm_data-0.2.6 → xrtm_data-0.2.7}/tests/test_real_binary_corpus.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xrtm-data
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: The Snapshot Vault for XRTM.
5
5
  Author-email: XRTM Team <moy@xrtm.org>
6
6
  License-Expression: Apache-2.0
@@ -51,19 +51,28 @@ pip install xrtm-data
51
51
  ## Core Primitives
52
52
 
53
53
  ### 1. The Forecast Object Standard
54
- Adhering to strict **Governance v1**, the `ForecastOutput` schema mandates that every prediction be accompanied by a structured causal graph (`logical_trace`) and a calibrated confidence interval.
54
+ Adhering to strict Governance, the canonical runtime schema is
55
+ `ForecastResult` (legacy alias: `ForecastOutput`). Every forecast result carries
56
+ `forecast_request_id`, `probability`, a `reasoning_trace`, and an optional
57
+ `execution_trace`. Legacy `question_id`, `reasoning`, `logical_trace`,
58
+ `logical_edges`, and `structural_trace` inputs remain accepted for compatibility.
55
59
 
56
60
  ```python
57
- from xrtm.data import ForecastOutput, CausalNode
61
+ from xrtm.data import ForecastResult, CausalNode
58
62
 
59
- prediction = ForecastOutput(
60
- question_id="q_123",
63
+ forecast_result = ForecastResult(
64
+ forecast_request_id="q_123",
61
65
  probability=0.75,
62
- reasoning="Base rate analysis suggests...",
63
- logical_trace=[
64
- CausalNode(event="Inflation rises", probability=0.8),
65
- CausalNode(event="Fed cuts rates", probability=0.4)
66
- ]
66
+ reasoning_trace={
67
+ "narrative": "Base rate analysis suggests...",
68
+ "causal_graph": {
69
+ "nodes": [
70
+ CausalNode(event="Inflation rises", probability=0.8),
71
+ CausalNode(event="Fed cuts rates", probability=0.4),
72
+ ]
73
+ },
74
+ },
75
+ execution_trace=["ingestion", "forecast"],
67
76
  )
68
77
  ```
69
78
 
@@ -76,7 +85,7 @@ The `MetadataBase` enforces a strict `snapshot_time`. This timestamp represents
76
85
  src/xrtm/data/
77
86
  ├── core/ # Interfaces & Schemas (domain-agnostic)
78
87
  │ ├── interfaces.py # DataSource protocol
79
- │ └── schemas/ # ForecastQuestion, ForecastOutput, etc.
88
+ │ └── schemas/ # ForecastRequest, ForecastResult, etc.
80
89
  ├── kit/ # Composable utilities (processors)
81
90
  └── providers/ # External data source implementations
82
91
  ├── local/ # LocalDataSource (JSON files)
@@ -28,19 +28,28 @@ pip install xrtm-data
28
28
  ## Core Primitives
29
29
 
30
30
  ### 1. The Forecast Object Standard
31
- Adhering to strict **Governance v1**, the `ForecastOutput` schema mandates that every prediction be accompanied by a structured causal graph (`logical_trace`) and a calibrated confidence interval.
31
+ Adhering to strict Governance, the canonical runtime schema is
32
+ `ForecastResult` (legacy alias: `ForecastOutput`). Every forecast result carries
33
+ `forecast_request_id`, `probability`, a `reasoning_trace`, and an optional
34
+ `execution_trace`. Legacy `question_id`, `reasoning`, `logical_trace`,
35
+ `logical_edges`, and `structural_trace` inputs remain accepted for compatibility.
32
36
 
33
37
  ```python
34
- from xrtm.data import ForecastOutput, CausalNode
38
+ from xrtm.data import ForecastResult, CausalNode
35
39
 
36
- prediction = ForecastOutput(
37
- question_id="q_123",
40
+ forecast_result = ForecastResult(
41
+ forecast_request_id="q_123",
38
42
  probability=0.75,
39
- reasoning="Base rate analysis suggests...",
40
- logical_trace=[
41
- CausalNode(event="Inflation rises", probability=0.8),
42
- CausalNode(event="Fed cuts rates", probability=0.4)
43
- ]
43
+ reasoning_trace={
44
+ "narrative": "Base rate analysis suggests...",
45
+ "causal_graph": {
46
+ "nodes": [
47
+ CausalNode(event="Inflation rises", probability=0.8),
48
+ CausalNode(event="Fed cuts rates", probability=0.4),
49
+ ]
50
+ },
51
+ },
52
+ execution_trace=["ingestion", "forecast"],
44
53
  )
45
54
  ```
46
55
 
@@ -53,7 +62,7 @@ The `MetadataBase` enforces a strict `snapshot_time`. This timestamp represents
53
62
  src/xrtm/data/
54
63
  ├── core/ # Interfaces & Schemas (domain-agnostic)
55
64
  │ ├── interfaces.py # DataSource protocol
56
- │ └── schemas/ # ForecastQuestion, ForecastOutput, etc.
65
+ │ └── schemas/ # ForecastRequest, ForecastResult, etc.
57
66
  ├── kit/ # Composable utilities (processors)
58
67
  └── providers/ # External data source implementations
59
68
  ├── local/ # LocalDataSource (JSON files)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "xrtm-data"
7
- version = "0.2.6"
7
+ version = "0.2.7"
8
8
  description = "The Snapshot Vault for XRTM."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11,<3.13"
@@ -36,11 +36,15 @@ from xrtm.data.core import DataSource, DataSourceError, SourceFetchError, Source
36
36
  # Core schemas (public API)
37
37
  from xrtm.data.core.schemas import (
38
38
  CausalEdge,
39
+ CausalGraph,
39
40
  CausalNode,
40
41
  ConfidenceInterval,
41
42
  ForecastOutput,
42
43
  ForecastQuestion,
44
+ ForecastRequest,
45
+ ForecastResult,
43
46
  MetadataBase,
47
+ ReasoningTrace,
44
48
  )
45
49
 
46
50
  __all__ = [
@@ -52,8 +56,12 @@ __all__ = [
52
56
  # Schemas
53
57
  "MetadataBase",
54
58
  "ForecastQuestion",
59
+ "ForecastRequest",
55
60
  "ForecastOutput",
61
+ "ForecastResult",
56
62
  "CausalNode",
57
63
  "CausalEdge",
64
+ "CausalGraph",
65
+ "ReasoningTrace",
58
66
  "ConfidenceInterval",
59
67
  ]
@@ -21,11 +21,15 @@ This module exports all Pydantic schemas used across the xrtm ecosystem.
21
21
 
22
22
  from xrtm.data.core.schemas.forecast import (
23
23
  CausalEdge,
24
+ CausalGraph,
24
25
  CausalNode,
25
26
  ConfidenceInterval,
26
27
  ForecastOutput,
27
28
  ForecastQuestion,
29
+ ForecastRequest,
30
+ ForecastResult,
28
31
  MetadataBase,
32
+ ReasoningTrace,
29
33
  )
30
34
  from xrtm.data.core.schemas.prior import BetaPrior, PriorState
31
35
  from xrtm.data.core.schemas.trade import TradeEvent, TradeWindow
@@ -34,9 +38,13 @@ __all__ = [
34
38
  # Forecast schemas
35
39
  "MetadataBase",
36
40
  "ForecastQuestion",
41
+ "ForecastRequest",
37
42
  "ForecastOutput",
43
+ "ForecastResult",
38
44
  "CausalNode",
39
45
  "CausalEdge",
46
+ "CausalGraph",
47
+ "ReasoningTrace",
40
48
  "ConfidenceInterval",
41
49
  # Prior schemas
42
50
  "BetaPrior",
@@ -25,8 +25,9 @@ Example:
25
25
  >>> q = ForecastQuestion(id="q1", title="Will it rain tomorrow?")
26
26
  """
27
27
 
28
+ from collections.abc import ItemsView, KeysView, ValuesView
28
29
  from datetime import datetime, timezone
29
- from typing import Any, Dict, List, Optional
30
+ from typing import Any, Dict, Generator, List, Optional
30
31
 
31
32
  from pydantic import AliasChoices, BaseModel, ConfigDict, Field, field_validator, model_validator
32
33
 
@@ -118,6 +119,9 @@ class ForecastQuestion(BaseModel):
118
119
  return self.description
119
120
 
120
121
 
122
+ ForecastRequest = ForecastQuestion
123
+
124
+
121
125
  class CausalNode(BaseModel):
122
126
  r"""
123
127
  Represents a single step in a logical reasoning chain.
@@ -170,6 +174,76 @@ class ConfidenceInterval(BaseModel):
170
174
  level: float = Field(0.9, ge=0, le=1, description="Confidence level")
171
175
 
172
176
 
177
+ class MappingCompatibleModel(BaseModel):
178
+ r"""Base model with lightweight dict-style compatibility helpers."""
179
+
180
+ def get(self, key: str, default: Any = None) -> Any:
181
+ r"""Provide dict-style access for compatibility shims."""
182
+ return getattr(self, key, default)
183
+
184
+ def __getitem__(self, key: str) -> Any:
185
+ return getattr(self, key)
186
+
187
+ def __contains__(self, key: object) -> bool:
188
+ return isinstance(key, str) and hasattr(self, key)
189
+
190
+ def items(self) -> ItemsView[str, Any]:
191
+ return self.model_dump(exclude_none=True).items()
192
+
193
+ def keys(self) -> KeysView[str]:
194
+ return self.model_dump(exclude_none=True).keys()
195
+
196
+ def values(self) -> ValuesView[Any]:
197
+ return self.model_dump(exclude_none=True).values()
198
+
199
+ def __iter__(self) -> Generator[tuple[str, Any], None, None]:
200
+ for item in self.model_dump(exclude_none=True).items():
201
+ yield item
202
+
203
+ def __len__(self) -> int:
204
+ return len(self.model_dump(exclude_none=True))
205
+
206
+ def __eq__(self, other: object) -> bool:
207
+ if isinstance(other, dict):
208
+ return self.model_dump(exclude_none=True) == other
209
+ return super().__eq__(other)
210
+
211
+
212
+ class CausalGraph(MappingCompatibleModel):
213
+ r"""Qualified causal graph embedded inside a reasoning trace."""
214
+
215
+ nodes: List[CausalNode] = Field(
216
+ default_factory=list,
217
+ description="Ordered forecast-path nodes inside the qualified causal graph",
218
+ )
219
+ edges: List[CausalEdge] = Field(
220
+ default_factory=list,
221
+ description="Qualified causal graph edges connecting forecast-path nodes",
222
+ )
223
+
224
+
225
+ class ReasoningTrace(MappingCompatibleModel):
226
+ r"""Canonical reasoning trace payload for a forecast result."""
227
+
228
+ narrative: str = Field("", description="Narrative reasoning for the forecast result")
229
+ causal_graph: CausalGraph = Field(
230
+ default_factory=CausalGraph,
231
+ description="Qualified causal graph for structured forecast-path reasoning",
232
+ )
233
+
234
+ @property
235
+ def forecast_path(self) -> List[CausalNode]:
236
+ r"""Canonical alias for ordered reasoning nodes."""
237
+ return self.causal_graph.nodes
238
+
239
+ @forecast_path.setter
240
+ def forecast_path(self, value: List[CausalNode] | List[Dict[str, Any]]) -> None:
241
+ r"""Backward-compatible setter for forecast-path nodes."""
242
+ self.causal_graph.nodes = [
243
+ node if isinstance(node, CausalNode) else CausalNode.model_validate(node) for node in value
244
+ ]
245
+
246
+
173
247
  class ForecastOutput(BaseModel):
174
248
  r"""
175
249
  The structured result of an agent's forecasting reasoning.
@@ -178,19 +252,23 @@ class ForecastOutput(BaseModel):
178
252
  complete reasoning chain that led to it, enabling audit and calibration.
179
253
 
180
254
  Attributes:
181
- question_id: Reference to the input question.
255
+ forecast_request_id: Reference to the input forecast request.
182
256
  probability: The assigned probability of the primary outcome.
183
257
  uncertainty: Optional measure of forecast uncertainty.
184
258
  confidence_interval: Range for calibration.
185
- reasoning: Narrative reasoning for the forecast.
186
- logical_trace: Bayesian-style sequence of assumptions.
187
- logical_edges: Causal dependencies between nodes.
188
- structural_trace: Order of graph nodes executed.
259
+ reasoning_trace: Narrative reasoning plus a qualified causal graph.
260
+ execution_trace: Ordered workflow stages executed to produce the result.
189
261
  calibration_metrics: Performance metrics.
190
262
  metadata: Associated temporal and source metadata.
191
263
  """
192
264
 
193
- question_id: str = Field(..., description="Reference to the input question")
265
+ model_config = ConfigDict(populate_by_name=True)
266
+
267
+ forecast_request_id: str = Field(
268
+ ...,
269
+ validation_alias=AliasChoices("forecast_request_id", "question_id"),
270
+ description="Reference to the input forecast request",
271
+ )
194
272
  probability: float = Field(
195
273
  ...,
196
274
  alias="confidence",
@@ -201,49 +279,76 @@ class ForecastOutput(BaseModel):
201
279
  )
202
280
  uncertainty: Optional[float] = Field(None, ge=0, le=1, description="Measure of forecast uncertainty")
203
281
  confidence_interval: Optional[ConfidenceInterval] = None
204
- reasoning: str = Field(..., description="Narrative reasoning for the forecast")
205
- logical_trace: List[CausalNode] = Field(
206
- default_factory=list, description="The Bayesian-style sequence of assumptions"
282
+ reasoning_trace: ReasoningTrace = Field(
283
+ ...,
284
+ description="Narrative reasoning trace with a qualified causal graph",
285
+ )
286
+ execution_trace: List[str] = Field(
287
+ default_factory=list,
288
+ validation_alias=AliasChoices("execution_trace", "structural_trace"),
289
+ description="Ordered workflow stages executed for this forecast result",
207
290
  )
208
- logical_edges: List[CausalEdge] = Field(default_factory=list, description="Causal dependencies between nodes")
209
- structural_trace: List[str] = Field(default_factory=list, description="Order of graph nodes executed")
210
291
  calibration_metrics: Dict[str, Any] = Field(default_factory=dict, description="Performance metrics")
211
292
  metadata: MetadataBase = Field(default_factory=MetadataBase) # type: ignore[arg-type]
212
293
 
213
294
  @model_validator(mode="before")
214
295
  @classmethod
215
- def _apply_reasoning_trace_alias(cls, data: Any) -> Any:
216
- r"""Accept governance ``reasoning_trace`` as an alias for runtime trace fields."""
217
- if not isinstance(data, dict) or "reasoning_trace" not in data:
296
+ def _normalize_runtime_aliases(cls, data: Any) -> Any:
297
+ r"""Normalize legacy runtime aliases into the canonical result vocabulary."""
298
+ if not isinstance(data, dict):
218
299
  return data
219
300
 
220
- trace = data["reasoning_trace"]
221
301
  updated = dict(data)
222
- if isinstance(trace, dict):
223
- if "reasoning" not in updated and isinstance(trace.get("narrative"), str):
224
- updated["reasoning"] = trace["narrative"]
302
+ if "question_id" in updated and "forecast_request_id" not in updated:
303
+ updated["forecast_request_id"] = updated["question_id"]
304
+ if "structural_trace" in updated and "execution_trace" not in updated:
305
+ updated["execution_trace"] = updated["structural_trace"]
306
+
307
+ trace = updated.get("reasoning_trace")
308
+ if isinstance(trace, list):
309
+ trace = {
310
+ "narrative": str(updated.get("reasoning", "")),
311
+ "causal_graph": {
312
+ "nodes": trace,
313
+ "edges": updated.get("logical_edges", []),
314
+ },
315
+ }
316
+ updated["reasoning_trace"] = trace
225
317
 
318
+ if isinstance(trace, dict):
319
+ trace_dict = dict(trace)
320
+ if "narrative" not in trace_dict and isinstance(updated.get("reasoning"), str):
321
+ trace_dict["narrative"] = updated["reasoning"]
226
322
  causal_graph = trace.get("causal_graph")
227
- if isinstance(causal_graph, dict):
228
- if "logical_trace" not in updated and "nodes" in causal_graph:
229
- updated["logical_trace"] = causal_graph["nodes"]
230
- if "logical_edges" not in updated and "edges" in causal_graph:
231
- updated["logical_edges"] = causal_graph["edges"]
232
- elif isinstance(trace, list) and "logical_trace" not in updated:
233
- updated["logical_trace"] = trace
323
+ if not isinstance(causal_graph, dict):
324
+ causal_graph = {}
325
+ causal_graph = dict(causal_graph)
326
+ if "nodes" not in causal_graph and "logical_trace" in updated:
327
+ causal_graph["nodes"] = updated["logical_trace"]
328
+ if "edges" not in causal_graph and "logical_edges" in updated:
329
+ causal_graph["edges"] = updated["logical_edges"]
330
+ trace_dict["causal_graph"] = causal_graph
331
+ updated["reasoning_trace"] = trace_dict
332
+ elif any(key in updated for key in ("reasoning", "logical_trace", "logical_edges")):
333
+ updated["reasoning_trace"] = {
334
+ "narrative": str(updated.get("reasoning", "")),
335
+ "causal_graph": {
336
+ "nodes": updated.get("logical_trace", []),
337
+ "edges": updated.get("logical_edges", []),
338
+ },
339
+ }
234
340
 
235
341
  return updated
236
342
 
237
343
  @property
238
- def reasoning_trace(self) -> Dict[str, Any]:
239
- r"""Governance-compatible alias for the narrative and causal graph trace."""
240
- return {
241
- "narrative": self.reasoning,
242
- "causal_graph": {
243
- "nodes": [node.model_dump(exclude_none=True) for node in self.logical_trace],
244
- "edges": [edge.model_dump(exclude_none=True) for edge in self.logical_edges],
245
- },
246
- }
344
+ def question_id(self) -> str:
345
+ r"""Backward compatibility alias for ``forecast_request_id``."""
346
+ return self.forecast_request_id
347
+
348
+ @question_id.setter
349
+ def question_id(self, value: str) -> None:
350
+ r"""Backward compatibility setter for ``forecast_request_id``."""
351
+ self.forecast_request_id = value
247
352
 
248
353
  @property
249
354
  def confidence(self) -> float:
@@ -255,9 +360,63 @@ class ForecastOutput(BaseModel):
255
360
  r"""Backward compatibility setter for probability."""
256
361
  self.probability = value
257
362
 
363
+ @property
364
+ def reasoning(self) -> str:
365
+ r"""Backward compatibility alias for ``reasoning_trace.narrative``."""
366
+ return self.reasoning_trace.narrative
367
+
368
+ @reasoning.setter
369
+ def reasoning(self, value: str) -> None:
370
+ r"""Backward compatibility setter for ``reasoning_trace.narrative``."""
371
+ self.reasoning_trace.narrative = value
372
+
373
+ @property
374
+ def logical_trace(self) -> List[CausalNode]:
375
+ r"""Backward compatibility alias for reasoning-trace nodes."""
376
+ return self.reasoning_trace.causal_graph.nodes
377
+
378
+ @logical_trace.setter
379
+ def logical_trace(self, value: List[CausalNode] | List[Dict[str, Any]]) -> None:
380
+ r"""Backward compatibility setter for reasoning-trace nodes."""
381
+ self.reasoning_trace.causal_graph.nodes = [
382
+ node if isinstance(node, CausalNode) else CausalNode.model_validate(node) for node in value
383
+ ]
384
+
385
+ @property
386
+ def logical_edges(self) -> List[CausalEdge]:
387
+ r"""Backward compatibility alias for reasoning-trace edges."""
388
+ return self.reasoning_trace.causal_graph.edges
389
+
390
+ @logical_edges.setter
391
+ def logical_edges(self, value: List[CausalEdge] | List[Dict[str, Any]]) -> None:
392
+ r"""Backward compatibility setter for reasoning-trace edges."""
393
+ self.reasoning_trace.causal_graph.edges = [
394
+ edge if isinstance(edge, CausalEdge) else CausalEdge.model_validate(edge) for edge in value
395
+ ]
396
+
397
+ @property
398
+ def forecast_path(self) -> List[CausalNode]:
399
+ r"""Canonical alias for the ordered reasoning path."""
400
+ return self.reasoning_trace.forecast_path
401
+
402
+ @forecast_path.setter
403
+ def forecast_path(self, value: List[CausalNode] | List[Dict[str, Any]]) -> None:
404
+ r"""Canonical setter for the ordered reasoning path."""
405
+ self.reasoning_trace.forecast_path = value
406
+
407
+ @property
408
+ def structural_trace(self) -> List[str]:
409
+ r"""Backward compatibility alias for ``execution_trace``."""
410
+ return self.execution_trace
411
+
412
+ @structural_trace.setter
413
+ def structural_trace(self, value: List[str]) -> None:
414
+ r"""Backward compatibility setter for ``execution_trace``."""
415
+ self.execution_trace = list(value)
416
+
258
417
  def to_networkx(self) -> Any:
259
418
  r"""
260
- Convert the logical trace to a NetworkX directed graph.
419
+ Convert the reasoning trace to a NetworkX directed graph.
261
420
 
262
421
  Returns:
263
422
  A NetworkX DiGraph representing the reasoning chain.
@@ -287,8 +446,14 @@ class ForecastOutput(BaseModel):
287
446
  __all__ = [
288
447
  "MetadataBase",
289
448
  "ForecastQuestion",
449
+ "ForecastRequest",
290
450
  "CausalNode",
291
451
  "CausalEdge",
452
+ "CausalGraph",
292
453
  "ConfidenceInterval",
454
+ "ReasoningTrace",
293
455
  "ForecastOutput",
456
+ "ForecastResult",
294
457
  ]
458
+
459
+ ForecastResult = ForecastOutput
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xrtm-data
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: The Snapshot Vault for XRTM.
5
5
  Author-email: XRTM Team <moy@xrtm.org>
6
6
  License-Expression: Apache-2.0
@@ -51,19 +51,28 @@ pip install xrtm-data
51
51
  ## Core Primitives
52
52
 
53
53
  ### 1. The Forecast Object Standard
54
- Adhering to strict **Governance v1**, the `ForecastOutput` schema mandates that every prediction be accompanied by a structured causal graph (`logical_trace`) and a calibrated confidence interval.
54
+ Adhering to strict Governance, the canonical runtime schema is
55
+ `ForecastResult` (legacy alias: `ForecastOutput`). Every forecast result carries
56
+ `forecast_request_id`, `probability`, a `reasoning_trace`, and an optional
57
+ `execution_trace`. Legacy `question_id`, `reasoning`, `logical_trace`,
58
+ `logical_edges`, and `structural_trace` inputs remain accepted for compatibility.
55
59
 
56
60
  ```python
57
- from xrtm.data import ForecastOutput, CausalNode
61
+ from xrtm.data import ForecastResult, CausalNode
58
62
 
59
- prediction = ForecastOutput(
60
- question_id="q_123",
63
+ forecast_result = ForecastResult(
64
+ forecast_request_id="q_123",
61
65
  probability=0.75,
62
- reasoning="Base rate analysis suggests...",
63
- logical_trace=[
64
- CausalNode(event="Inflation rises", probability=0.8),
65
- CausalNode(event="Fed cuts rates", probability=0.4)
66
- ]
66
+ reasoning_trace={
67
+ "narrative": "Base rate analysis suggests...",
68
+ "causal_graph": {
69
+ "nodes": [
70
+ CausalNode(event="Inflation rises", probability=0.8),
71
+ CausalNode(event="Fed cuts rates", probability=0.4),
72
+ ]
73
+ },
74
+ },
75
+ execution_trace=["ingestion", "forecast"],
67
76
  )
68
77
  ```
69
78
 
@@ -76,7 +85,7 @@ The `MetadataBase` enforces a strict `snapshot_time`. This timestamp represents
76
85
  src/xrtm/data/
77
86
  ├── core/ # Interfaces & Schemas (domain-agnostic)
78
87
  │ ├── interfaces.py # DataSource protocol
79
- │ └── schemas/ # ForecastQuestion, ForecastOutput, etc.
88
+ │ └── schemas/ # ForecastRequest, ForecastResult, etc.
80
89
  ├── kit/ # Composable utilities (processors)
81
90
  └── providers/ # External data source implementations
82
91
  ├── local/ # LocalDataSource (JSON files)
@@ -16,21 +16,38 @@ from datetime import datetime, timedelta, timezone
16
16
 
17
17
  import pytest
18
18
 
19
- from xrtm.data import CausalEdge, CausalNode, ForecastOutput, MetadataBase
19
+ from xrtm.data import CausalEdge, CausalNode, ForecastOutput, ForecastResult, MetadataBase
20
20
  from xrtm.data.core.schemas import TradeEvent, TradeWindow
21
21
 
22
22
 
23
- def test_forecast_output_initialization():
24
- """Verify that we can create a valid ForecastOutput object."""
25
- output = ForecastOutput(
26
- question_id="test_q_1",
23
+ def test_forecast_result_initialization():
24
+ """Verify that we can create a valid ForecastResult object."""
25
+ output = ForecastResult(
26
+ forecast_request_id="test_q_1",
27
27
  probability=0.8,
28
- reasoning="Test reasoning",
29
- logical_trace=[CausalNode(event="Event A", probability=0.9), CausalNode(event="Event B", probability=0.8)],
30
- logical_edges=[CausalEdge(source="node_1", target="node_2")],
28
+ reasoning_trace={
29
+ "narrative": "Test reasoning",
30
+ "causal_graph": {
31
+ "nodes": [
32
+ CausalNode(event="Event A", probability=0.9).model_dump(exclude_none=True),
33
+ CausalNode(event="Event B", probability=0.8).model_dump(exclude_none=True),
34
+ ],
35
+ "edges": [CausalEdge(source="node_1", target="node_2").model_dump(exclude_none=True)],
36
+ },
37
+ },
38
+ execution_trace=["ingestion", "forecast"],
31
39
  )
32
40
  assert output.probability == 0.8
33
- assert len(output.logical_trace) == 2
41
+ assert output.question_id == "test_q_1"
42
+ assert len(output.forecast_path) == 2
43
+ assert output.execution_trace == ["ingestion", "forecast"]
44
+
45
+ payload = output.model_dump(mode="json")
46
+ assert payload["forecast_request_id"] == "test_q_1"
47
+ assert payload["reasoning_trace"]["narrative"] == "Test reasoning"
48
+ assert payload["execution_trace"] == ["ingestion", "forecast"]
49
+ assert "question_id" not in payload
50
+ assert "structural_trace" not in payload
34
51
 
35
52
 
36
53
  def test_forecast_output_validation_range():
@@ -45,9 +62,16 @@ def test_forecast_output_validation_range():
45
62
 
46
63
  def test_backward_compatibility_aliases():
47
64
  """Verify legacy aliases work (confidence -> probability)."""
48
- output = ForecastOutput(question_id="test_q_3", confidence=0.7, reasoning="Alias test")
65
+ output = ForecastOutput(
66
+ question_id="test_q_3",
67
+ confidence=0.7,
68
+ reasoning="Alias test",
69
+ structural_trace=["legacy-stage"],
70
+ )
49
71
  assert output.probability == 0.7
50
72
  assert output.confidence == 0.7
73
+ assert output.forecast_request_id == "test_q_3"
74
+ assert output.execution_trace == ["legacy-stage"]
51
75
 
52
76
  output.confidence = 0.5
53
77
  assert output.probability == 0.5
File without changes
File without changes