xrtm-data 0.2.6__tar.gz → 0.3.0__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 (53) hide show
  1. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/PKG-INFO +20 -15
  2. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/README.md +19 -10
  3. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/pyproject.toml +2 -8
  4. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm/data/__init__.py +8 -1
  5. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm/data/core/schemas/__init__.py +8 -0
  6. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm/data/core/schemas/forecast.py +202 -37
  7. xrtm_data-0.3.0/src/xrtm/data/corpora/__init__.py +56 -0
  8. xrtm_data-0.3.0/src/xrtm/data/corpora/_builtin_corpora.py +61 -0
  9. {xrtm_data-0.2.6/src/xrtm/data/providers/local → xrtm_data-0.3.0/src/xrtm/data/providers}/__init__.py +1 -5
  10. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm/data/providers/online/__init__.py +3 -2
  11. xrtm_data-0.3.0/src/xrtm/data/providers/online/metaculus.py +155 -0
  12. xrtm_data-0.3.0/src/xrtm/data/providers/online/polymarket.py +161 -0
  13. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm/data/version.py +1 -1
  14. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm_data.egg-info/PKG-INFO +20 -15
  15. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm_data.egg-info/SOURCES.txt +1 -20
  16. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm_data.egg-info/requires.txt +0 -4
  17. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/tests/test_corpus_registry.py +0 -45
  18. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/tests/test_schemas.py +34 -10
  19. xrtm_data-0.2.6/src/xrtm/data/cli/__init__.py +0 -330
  20. xrtm_data-0.2.6/src/xrtm/data/corpora/__init__.py +0 -99
  21. xrtm_data-0.2.6/src/xrtm/data/corpora/_builtin_corpora.py +0 -185
  22. xrtm_data-0.2.6/src/xrtm/data/corpora/forecast_importer.py +0 -517
  23. xrtm_data-0.2.6/src/xrtm/data/corpora/importers.py +0 -296
  24. xrtm_data-0.2.6/src/xrtm/data/corpora/splits.py +0 -261
  25. xrtm_data-0.2.6/src/xrtm/data/kit/__init__.py +0 -33
  26. xrtm_data-0.2.6/src/xrtm/data/kit/processors/__init__.py +0 -212
  27. xrtm_data-0.2.6/src/xrtm/data/providers/__init__.py +0 -28
  28. xrtm_data-0.2.6/src/xrtm/data/providers/local/csv.py +0 -176
  29. xrtm_data-0.2.6/src/xrtm/data/providers/online/polymarket.py +0 -256
  30. xrtm_data-0.2.6/src/xrtm/data/providers/subgraph/__init__.py +0 -25
  31. xrtm_data-0.2.6/src/xrtm/data/providers/subgraph/polymarket.py +0 -357
  32. xrtm_data-0.2.6/src/xrtm_data.egg-info/entry_points.txt +0 -2
  33. xrtm_data-0.2.6/tests/test_beta_fitter.py +0 -147
  34. xrtm_data-0.2.6/tests/test_cli_loading.py +0 -42
  35. xrtm_data-0.2.6/tests/test_cli_ux.py +0 -41
  36. xrtm_data-0.2.6/tests/test_corpus_importers.py +0 -270
  37. xrtm_data-0.2.6/tests/test_corpus_splits.py +0 -278
  38. xrtm_data-0.2.6/tests/test_forecast_importer.py +0 -302
  39. xrtm_data-0.2.6/tests/test_local_datasource.py +0 -214
  40. xrtm_data-0.2.6/tests/test_polymarket_source.py +0 -150
  41. xrtm_data-0.2.6/tests/test_polymarket_subgraph.py +0 -286
  42. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/LICENSE +0 -0
  43. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/setup.cfg +0 -0
  44. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm/data/core/__init__.py +0 -0
  45. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm/data/core/interfaces.py +0 -0
  46. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm/data/core/schemas/prior.py +0 -0
  47. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm/data/core/schemas/trade.py +0 -0
  48. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm/data/corpora/real_binary.py +0 -0
  49. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm/data/corpora/registry.py +0 -0
  50. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm_data.egg-info/dependency_links.txt +0 -0
  51. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/src/xrtm_data.egg-info/top_level.txt +0 -0
  52. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/tests/test_prior_schemas.py +0 -0
  53. {xrtm_data-0.2.6 → xrtm_data-0.3.0}/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.3.0
4
4
  Summary: The Snapshot Vault for XRTM.
5
5
  Author-email: XRTM Team <moy@xrtm.org>
6
6
  License-Expression: Apache-2.0
@@ -8,11 +8,7 @@ Requires-Python: <3.13,>=3.11
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
10
  Requires-Dist: pydantic>=2.0.0
11
- Requires-Dist: aiohttp>=3.9.0
12
11
  Requires-Dist: scipy>=1.11.0
13
- Requires-Dist: click>=8.0.0
14
- Requires-Dist: rich>=13.0.0
15
- Requires-Dist: pyarrow>=14.0.0
16
12
  Provides-Extra: dev
17
13
  Requires-Dist: pytest>=7.0.0; extra == "dev"
18
14
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -51,19 +47,28 @@ pip install xrtm-data
51
47
  ## Core Primitives
52
48
 
53
49
  ### 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.
50
+ Adhering to strict Governance, the canonical runtime schema is
51
+ `ForecastResult` (legacy alias: `ForecastOutput`). Every forecast result carries
52
+ `forecast_request_id`, `probability`, a `reasoning_trace`, and an optional
53
+ `execution_trace`. Legacy `question_id`, `reasoning`, `logical_trace`,
54
+ `logical_edges`, and `structural_trace` inputs remain accepted for compatibility.
55
55
 
56
56
  ```python
57
- from xrtm.data import ForecastOutput, CausalNode
57
+ from xrtm.data import ForecastResult, CausalNode
58
58
 
59
- prediction = ForecastOutput(
60
- question_id="q_123",
59
+ forecast_result = ForecastResult(
60
+ forecast_request_id="q_123",
61
61
  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
- ]
62
+ reasoning_trace={
63
+ "narrative": "Base rate analysis suggests...",
64
+ "causal_graph": {
65
+ "nodes": [
66
+ CausalNode(event="Inflation rises", probability=0.8),
67
+ CausalNode(event="Fed cuts rates", probability=0.4),
68
+ ]
69
+ },
70
+ },
71
+ execution_trace=["ingestion", "forecast"],
67
72
  )
68
73
  ```
69
74
 
@@ -76,7 +81,7 @@ The `MetadataBase` enforces a strict `snapshot_time`. This timestamp represents
76
81
  src/xrtm/data/
77
82
  ├── core/ # Interfaces & Schemas (domain-agnostic)
78
83
  │ ├── interfaces.py # DataSource protocol
79
- │ └── schemas/ # ForecastQuestion, ForecastOutput, etc.
84
+ │ └── schemas/ # ForecastRequest, ForecastResult, etc.
80
85
  ├── kit/ # Composable utilities (processors)
81
86
  └── providers/ # External data source implementations
82
87
  ├── 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.3.0"
8
8
  description = "The Snapshot Vault for XRTM."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11,<3.13"
@@ -14,11 +14,7 @@ authors = [
14
14
  ]
15
15
  dependencies = [
16
16
  "pydantic>=2.0.0",
17
- "aiohttp>=3.9.0",
18
17
  "scipy>=1.11.0",
19
- "click>=8.0.0",
20
- "rich>=13.0.0",
21
- "pyarrow>=14.0.0",
22
18
  ]
23
19
 
24
20
  [project.optional-dependencies]
@@ -30,9 +26,6 @@ dev = [
30
26
  "mypy>=1.0.0",
31
27
  ]
32
28
 
33
- [project.scripts]
34
- xrtm-data = "xrtm.data.cli:main"
35
-
36
29
  [tool.setuptools]
37
30
  package-dir = {"" = "src"}
38
31
  packages = {find = {where = ["src"], include = ["xrtm*"], namespaces = true}}
@@ -55,3 +48,4 @@ python_version = "3.11"
55
48
  ignore_missing_imports = true
56
49
  strict = false
57
50
  explicit_package_bases = true
51
+ exclude = ["build/", "dist/"]
@@ -27,7 +27,6 @@ Structure:
27
27
 
28
28
  Example:
29
29
  >>> from xrtm.data import ForecastQuestion, DataSource
30
- >>> from xrtm.data.providers import LocalDataSource
31
30
  """
32
31
 
33
32
  # Core interfaces
@@ -36,11 +35,15 @@ from xrtm.data.core import DataSource, DataSourceError, SourceFetchError, Source
36
35
  # Core schemas (public API)
37
36
  from xrtm.data.core.schemas import (
38
37
  CausalEdge,
38
+ CausalGraph,
39
39
  CausalNode,
40
40
  ConfidenceInterval,
41
41
  ForecastOutput,
42
42
  ForecastQuestion,
43
+ ForecastRequest,
44
+ ForecastResult,
43
45
  MetadataBase,
46
+ ReasoningTrace,
44
47
  )
45
48
 
46
49
  __all__ = [
@@ -52,8 +55,12 @@ __all__ = [
52
55
  # Schemas
53
56
  "MetadataBase",
54
57
  "ForecastQuestion",
58
+ "ForecastRequest",
55
59
  "ForecastOutput",
60
+ "ForecastResult",
56
61
  "CausalNode",
57
62
  "CausalEdge",
63
+ "CausalGraph",
64
+ "ReasoningTrace",
58
65
  "ConfidenceInterval",
59
66
  ]
@@ -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.
@@ -151,7 +155,7 @@ class CausalEdge(BaseModel):
151
155
 
152
156
  source: str = Field(..., description="ID of the source node")
153
157
  target: str = Field(..., description="ID of the target node")
154
- weight: float = Field(default=1.0, ge=0, le=1, description="Strength of causal relationship")
158
+ weight: float = Field(default=1.0, ge=-1, le=1, description="Strength of causal relationship (negative = inhibitory)")
155
159
  description: Optional[str] = Field(None, description="Context for this causal link")
156
160
 
157
161
 
@@ -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
@@ -0,0 +1,56 @@
1
+ # coding=utf-8
2
+ # Copyright 2026 XRTM Team. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ r"""Public entry points for XRTM corpora."""
17
+
18
+ from xrtm.data.corpora.real_binary import (
19
+ REAL_BINARY_CORPUS_ID,
20
+ RealBinaryCorpusSource,
21
+ RealBinaryQuestionRecord,
22
+ load_real_binary_corpus,
23
+ load_real_binary_questions,
24
+ load_real_binary_resolved_outcomes,
25
+ validate_real_binary_corpus,
26
+ )
27
+ from xrtm.data.corpora.registry import (
28
+ CorpusManifest,
29
+ CorpusMetadata,
30
+ CorpusRegistry,
31
+ CorpusSplit,
32
+ CorpusTier,
33
+ LicenseType,
34
+ get_corpus,
35
+ get_corpus_metadata,
36
+ list_available_corpora,
37
+ )
38
+
39
+ __all__ = [
40
+ "REAL_BINARY_CORPUS_ID",
41
+ "RealBinaryQuestionRecord",
42
+ "RealBinaryCorpusSource",
43
+ "load_real_binary_corpus",
44
+ "load_real_binary_questions",
45
+ "load_real_binary_resolved_outcomes",
46
+ "validate_real_binary_corpus",
47
+ "CorpusRegistry",
48
+ "CorpusMetadata",
49
+ "CorpusManifest",
50
+ "CorpusTier",
51
+ "LicenseType",
52
+ "CorpusSplit",
53
+ "get_corpus",
54
+ "get_corpus_metadata",
55
+ "list_available_corpora",
56
+ ]
@@ -0,0 +1,61 @@
1
+ # coding=utf-8
2
+ # Copyright 2026 XRTM Team. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ r"""Concrete built-in corpus registrations."""
17
+
18
+ from __future__ import annotations
19
+
20
+ from typing import List
21
+
22
+ from xrtm.data.corpora.real_binary import REAL_BINARY_CORPUS_ID, RealBinaryCorpusSource
23
+ from xrtm.data.corpora.registry import (
24
+ CorpusManifest,
25
+ CorpusMetadata,
26
+ CorpusSplit,
27
+ CorpusTier,
28
+ LicenseType,
29
+ )
30
+
31
+
32
+ def build_builtin_manifests() -> List[CorpusManifest]:
33
+ """Build manifests for the corpora shipped with the registry bootstrap."""
34
+ return [
35
+ _build_real_binary_manifest(),
36
+ ]
37
+
38
+
39
+ def _build_real_binary_manifest() -> CorpusManifest:
40
+ real_binary_metadata = CorpusMetadata(
41
+ corpus_id=REAL_BINARY_CORPUS_ID,
42
+ name="XRTM Real Binary v1",
43
+ tier=CorpusTier.TIER_1,
44
+ license_type=LicenseType.APACHE_2_0,
45
+ description="Minimal deterministic real-world binary question corpus for CI smoke tests",
46
+ version="1.0",
47
+ release_gate_approved=True,
48
+ bundled=True,
49
+ size_estimate=25,
50
+ tags=["binary", "deterministic", "embedded", "seed-corpus"],
51
+ provenance_url="https://github.com/xrtm/xrtm",
52
+ license_url="https://www.apache.org/licenses/LICENSE-2.0",
53
+ )
54
+
55
+ return CorpusManifest(
56
+ corpus_id=REAL_BINARY_CORPUS_ID,
57
+ metadata=real_binary_metadata,
58
+ loader_fn=lambda: RealBinaryCorpusSource(),
59
+ available_splits=[CorpusSplit.FULL],
60
+ default_split=CorpusSplit.FULL,
61
+ )
@@ -13,8 +13,4 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- r"""Local file-based data providers."""
17
-
18
- from xrtm.data.providers.local.csv import LocalDataSource
19
-
20
- __all__ = ["LocalDataSource"]
16
+ r"""External data providers implementing the DataSource ABC."""