crprotocol 2.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 (153) hide show
  1. crp/__init__.py +126 -0
  2. crp/__main__.py +8 -0
  3. crp/_typing.py +27 -0
  4. crp/_version.py +5 -0
  5. crp/adapters.py +31 -0
  6. crp/advanced/__init__.py +40 -0
  7. crp/advanced/auto_ingest.py +400 -0
  8. crp/advanced/cqs.py +235 -0
  9. crp/advanced/cross_window.py +477 -0
  10. crp/advanced/curator.py +265 -0
  11. crp/advanced/feedback.py +146 -0
  12. crp/advanced/hierarchical.py +211 -0
  13. crp/advanced/meta_learning.py +401 -0
  14. crp/advanced/parallel.py +98 -0
  15. crp/advanced/review_cycle.py +329 -0
  16. crp/advanced/scale_mode.py +129 -0
  17. crp/advanced/source_grounding.py +207 -0
  18. crp/ckf/__init__.py +35 -0
  19. crp/ckf/community.py +377 -0
  20. crp/ckf/fabric.py +445 -0
  21. crp/ckf/gc.py +175 -0
  22. crp/ckf/graph_walk.py +87 -0
  23. crp/ckf/merge.py +133 -0
  24. crp/ckf/pattern_query.py +122 -0
  25. crp/ckf/pubsub.py +128 -0
  26. crp/ckf/semantic.py +207 -0
  27. crp/cli/__init__.py +7 -0
  28. crp/cli/main.py +329 -0
  29. crp/cli/sidecar.py +929 -0
  30. crp/cli/startup.py +272 -0
  31. crp/continuation/__init__.py +103 -0
  32. crp/continuation/completion.py +348 -0
  33. crp/continuation/degradation.py +157 -0
  34. crp/continuation/document_map.py +160 -0
  35. crp/continuation/flow.py +109 -0
  36. crp/continuation/gap.py +419 -0
  37. crp/continuation/manager.py +484 -0
  38. crp/continuation/quality_monitor.py +179 -0
  39. crp/continuation/stitch.py +419 -0
  40. crp/continuation/trigger.py +142 -0
  41. crp/continuation/voice.py +157 -0
  42. crp/core/__init__.py +69 -0
  43. crp/core/batch.py +77 -0
  44. crp/core/circuit_breaker.py +116 -0
  45. crp/core/config.py +377 -0
  46. crp/core/context_tools.py +540 -0
  47. crp/core/dispatch_router.py +3977 -0
  48. crp/core/errors.py +128 -0
  49. crp/core/extraction_facade.py +384 -0
  50. crp/core/facilitator.py +713 -0
  51. crp/core/idempotency.py +215 -0
  52. crp/core/orchestrator.py +1435 -0
  53. crp/core/relay_strategies.py +613 -0
  54. crp/core/security_manager.py +140 -0
  55. crp/core/session.py +134 -0
  56. crp/core/task_intent.py +36 -0
  57. crp/core/window.py +363 -0
  58. crp/envelope/__init__.py +30 -0
  59. crp/envelope/builder.py +288 -0
  60. crp/envelope/decomposer.py +236 -0
  61. crp/envelope/formatter.py +168 -0
  62. crp/envelope/packer.py +211 -0
  63. crp/envelope/reranker.py +209 -0
  64. crp/envelope/scoring.py +310 -0
  65. crp/extraction/__init__.py +45 -0
  66. crp/extraction/complexity.py +96 -0
  67. crp/extraction/contradiction.py +132 -0
  68. crp/extraction/pipeline.py +360 -0
  69. crp/extraction/quality_gate.py +237 -0
  70. crp/extraction/stage1_regex.py +173 -0
  71. crp/extraction/stage2_statistical.py +244 -0
  72. crp/extraction/stage3_gliner.py +210 -0
  73. crp/extraction/stage4_uie.py +183 -0
  74. crp/extraction/stage5_discourse.py +175 -0
  75. crp/extraction/stage6_llm.py +178 -0
  76. crp/extraction/structured_output.py +219 -0
  77. crp/extraction/types.py +299 -0
  78. crp/license_guard.py +722 -0
  79. crp/observability/__init__.py +30 -0
  80. crp/observability/audit.py +118 -0
  81. crp/observability/events.py +233 -0
  82. crp/observability/metrics.py +264 -0
  83. crp/observability/quality.py +135 -0
  84. crp/observability/structured_logging.py +81 -0
  85. crp/observability/telemetry.py +117 -0
  86. crp/provenance/__init__.py +314 -0
  87. crp/provenance/_embeddings.py +97 -0
  88. crp/provenance/_types.py +378 -0
  89. crp/provenance/attribution_scorer.py +252 -0
  90. crp/provenance/claim_detector.py +229 -0
  91. crp/provenance/contradiction_detector.py +243 -0
  92. crp/provenance/distortion_detector.py +397 -0
  93. crp/provenance/entailment_verifier.py +358 -0
  94. crp/provenance/fabrication_detector.py +203 -0
  95. crp/provenance/hallucination_scorer.py +320 -0
  96. crp/provenance/omission_analyzer.py +106 -0
  97. crp/provenance/provenance_chain.py +205 -0
  98. crp/provenance/report_generator.py +440 -0
  99. crp/providers/__init__.py +43 -0
  100. crp/providers/anthropic.py +270 -0
  101. crp/providers/base.py +135 -0
  102. crp/providers/custom.py +63 -0
  103. crp/providers/diagnostic.py +251 -0
  104. crp/providers/llamacpp.py +224 -0
  105. crp/providers/manager.py +139 -0
  106. crp/providers/ollama.py +243 -0
  107. crp/providers/openai.py +628 -0
  108. crp/providers/tokenizers.py +48 -0
  109. crp/py.typed +0 -0
  110. crp/resources/__init__.py +53 -0
  111. crp/resources/adaptive_allocator.py +525 -0
  112. crp/resources/cost_model.py +388 -0
  113. crp/resources/overhead_manager.py +217 -0
  114. crp/resources/resource_manager.py +262 -0
  115. crp/schemas/__init__.py +20 -0
  116. crp/schemas/cost-estimate.json +33 -0
  117. crp/schemas/crp-error.json +43 -0
  118. crp/schemas/envelope-preview.json +40 -0
  119. crp/schemas/persisted-state-header.json +27 -0
  120. crp/schemas/quality-report.json +94 -0
  121. crp/schemas/session-handle.json +33 -0
  122. crp/schemas/session-status.json +57 -0
  123. crp/schemas/stream-event.json +18 -0
  124. crp/schemas/task-intent.json +42 -0
  125. crp/security/__init__.py +93 -0
  126. crp/security/audit_trail.py +392 -0
  127. crp/security/binding.py +192 -0
  128. crp/security/compliance.py +813 -0
  129. crp/security/consent.py +593 -0
  130. crp/security/embedding_defense.py +161 -0
  131. crp/security/encryption.py +202 -0
  132. crp/security/injection.py +335 -0
  133. crp/security/integrity.py +267 -0
  134. crp/security/privacy.py +662 -0
  135. crp/security/quarantine.py +249 -0
  136. crp/security/rbac.py +221 -0
  137. crp/security/validation.py +164 -0
  138. crp/state/__init__.py +31 -0
  139. crp/state/cold_storage.py +258 -0
  140. crp/state/compaction.py +263 -0
  141. crp/state/critical_state.py +104 -0
  142. crp/state/event_log.py +313 -0
  143. crp/state/fact.py +189 -0
  144. crp/state/serialization.py +189 -0
  145. crp/state/session_cleanup.py +77 -0
  146. crp/state/snapshot.py +290 -0
  147. crp/state/warm_store.py +346 -0
  148. crprotocol-2.0.0.dist-info/METADATA +1295 -0
  149. crprotocol-2.0.0.dist-info/RECORD +153 -0
  150. crprotocol-2.0.0.dist-info/WHEEL +4 -0
  151. crprotocol-2.0.0.dist-info/entry_points.txt +2 -0
  152. crprotocol-2.0.0.dist-info/licenses/LICENSE.md +170 -0
  153. crprotocol-2.0.0.dist-info/licenses/NOTICE +18 -0
@@ -0,0 +1,97 @@
1
+ # Copyright © 2025 Constantinos Vidiniotis. All rights reserved.
2
+ # Licensed under Elastic License 2.0 — see LICENSE.md for details.
3
+ """Shared semantic embedding module for DPE components (§7.14.3).
4
+
5
+ Provides lazy-loaded sentence-transformer embeddings used by:
6
+ - attribution_scorer (G-1): real semantic similarity vs hash-BOW
7
+ - entailment_verifier (P-1): ML-driven fallback when NLI unavailable
8
+ - distortion_detector (P-3): semantic drift detection
9
+
10
+ The model (all-MiniLM-L6-v2, ~80 MB, 384-dim, CPU-only) is loaded
11
+ once and shared across all components to avoid duplicate memory usage.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ import math
18
+ from typing import Any
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Module-level model cache (lazy singleton)
24
+ # ---------------------------------------------------------------------------
25
+
26
+ _model: Any = None
27
+ _model_load_failed: bool = False
28
+ _DEFAULT_MODEL = "all-MiniLM-L6-v2"
29
+
30
+
31
+ def get_embedder(*, model_name: str = _DEFAULT_MODEL) -> Any:
32
+ """Lazy-load the SentenceTransformer model (singleton).
33
+
34
+ Returns the model object or None if loading fails.
35
+ """
36
+ global _model, _model_load_failed # noqa: PLW0603
37
+
38
+ if _model_load_failed:
39
+ return None
40
+
41
+ if _model is not None:
42
+ return _model
43
+
44
+ try:
45
+ from sentence_transformers import SentenceTransformer # type: ignore[import-untyped]
46
+ logger.info("Loading embedding model: %s", model_name)
47
+ _model = SentenceTransformer(model_name)
48
+ return _model
49
+ except Exception as exc:
50
+ logger.warning(
51
+ "Embedding model unavailable (%s), semantic features degraded: %s",
52
+ model_name, exc,
53
+ )
54
+ _model_load_failed = True
55
+ return None
56
+
57
+
58
+ def encode_texts(
59
+ texts: list[str],
60
+ *,
61
+ _model_override: Any = None,
62
+ ) -> list[list[float]] | None:
63
+ """Encode texts into dense embeddings.
64
+
65
+ Args:
66
+ texts: List of strings to encode.
67
+ _model_override: Injected model for testing (skips singleton).
68
+
69
+ Returns:
70
+ List of embedding vectors (as lists of floats), or None if
71
+ the model is unavailable.
72
+ """
73
+ model = _model_override or get_embedder()
74
+ if model is None:
75
+ return None
76
+
77
+ try:
78
+ embeddings = model.encode(texts, convert_to_numpy=True)
79
+ return [emb.tolist() for emb in embeddings]
80
+ except Exception as exc:
81
+ logger.warning("Embedding encode failed: %s", exc)
82
+ return None
83
+
84
+
85
+ def cosine_similarity(a: list[float], b: list[float]) -> float:
86
+ """Compute cosine similarity between two vectors."""
87
+ dot = sum(x * y for x, y in zip(a, b))
88
+ norm_a = math.sqrt(sum(x * x for x in a)) or 1e-12
89
+ norm_b = math.sqrt(sum(x * x for x in b)) or 1e-12
90
+ return dot / (norm_a * norm_b)
91
+
92
+
93
+ def reset_cache() -> None:
94
+ """Reset the module-level model cache (for testing)."""
95
+ global _model, _model_load_failed # noqa: PLW0603
96
+ _model = None
97
+ _model_load_failed = False
@@ -0,0 +1,378 @@
1
+ # Copyright © 2025 Constantinos Vidiniotis. All rights reserved.
2
+ # Licensed under Elastic License 2.0 — see LICENSE.md for details.
3
+ """Decision Provenance Engine — data types (§7.14.3).
4
+
5
+ Dataclasses for claim attribution, fact scoring, provenance chains,
6
+ and provenance reports used by the DPE pipeline.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass, field
12
+ from enum import Enum
13
+ from typing import Any
14
+
15
+
16
+ # ---------------------------------------------------------------------------
17
+ # Claim classification
18
+ # ---------------------------------------------------------------------------
19
+
20
+
21
+ class ClaimType(str, Enum):
22
+ """Classification of individual sentences/claims in LLM output."""
23
+
24
+ FACTUAL_CLAIM = "FACTUAL_CLAIM" # Verifiable factual assertion
25
+ OPINION = "OPINION" # Subjective view or judgment
26
+ PROCEDURAL = "PROCEDURAL" # Action or instruction
27
+ HEDGE = "HEDGE" # Qualified/uncertain statement
28
+ CONNECTIVE = "CONNECTIVE" # Structural/transitional text
29
+
30
+
31
+ class AttributionType(str, Enum):
32
+ """How a claim maps to its knowledge source."""
33
+
34
+ CONTEXT_GROUNDED = "CONTEXT_GROUNDED" # Supported by envelope fact(s)
35
+ PARAMETRIC = "PARAMETRIC" # Likely from model training data
36
+ MIXED = "MIXED" # Partial context + parametric
37
+ UNCERTAIN = "UNCERTAIN" # Low-confidence attribution
38
+
39
+
40
+ # ---------------------------------------------------------------------------
41
+ # Fidelity verification — did the model FAITHFULLY represent its sources?
42
+ # ---------------------------------------------------------------------------
43
+
44
+
45
+ class DistortionType(str, Enum):
46
+ """How the model distorted a source fact in its output."""
47
+
48
+ NUMBER_CHANGED = "NUMBER_CHANGED" # 10% → 15%
49
+ NEGATION_FLIP = "NEGATION_FLIP" # "is safe" → "is not safe"
50
+ QUALIFIER_DROPPED = "QUALIFIER_DROPPED" # "approximately 10" → "10"
51
+ QUALIFIER_ADDED = "QUALIFIER_ADDED" # "10" → "always 10"
52
+ SCOPE_CHANGED = "SCOPE_CHANGED" # "in Q3" → "annually"
53
+ ENTITY_SUBSTITUTED = "ENTITY_SUBSTITUTED" # "Company A" → "Company B"
54
+ SEMANTIC_DRIFT = "SEMANTIC_DRIFT" # Meaning-level deviation despite lexical similarity
55
+
56
+
57
+ class FabricationType(str, Enum):
58
+ """Category of fabricated entity in a claim."""
59
+
60
+ NUMBER = "NUMBER" # Invented number not in any fact
61
+ PERCENTAGE = "PERCENTAGE" # Invented percentage
62
+ DATE = "DATE" # Invented date/year
63
+ PROPER_NOUN = "PROPER_NOUN" # Invented name/entity
64
+ CITATION = "CITATION" # Invented citation/reference
65
+
66
+
67
+ class OmissionSeverity(str, Enum):
68
+ """How important the omitted fact was."""
69
+
70
+ CRITICAL = "CRITICAL" # Top-quartile relevance, omitted entirely
71
+ HIGH = "HIGH" # High relevance, omitted
72
+ MEDIUM = "MEDIUM" # Medium relevance, omitted
73
+ LOW = "LOW" # Low relevance, omitted (expected)
74
+
75
+
76
+ # ---------------------------------------------------------------------------
77
+ # Scoring results
78
+ # ---------------------------------------------------------------------------
79
+
80
+
81
+ @dataclass
82
+ class FactScore:
83
+ """Attribution score between a single claim and a single envelope fact."""
84
+
85
+ fact_id: str = ""
86
+ fact_text_preview: str = "" # First 120 chars of fact text
87
+ semantic_similarity: float = 0.0 # Bag-of-words cosine similarity
88
+ lexical_overlap: float = 0.0 # N-gram token overlap ratio
89
+ composite_score: float = 0.0 # Weighted combination
90
+ fact_source_window: str = "" # Which window created this fact
91
+ fact_extraction_stage: int = 0 # Which pipeline stage (1-6)
92
+
93
+
94
+ @dataclass
95
+ class ClaimAttribution:
96
+ """Attribution result for a single claim in the LLM output."""
97
+
98
+ claim_text: str = ""
99
+ claim_index: int = 0 # Position in output (0-based)
100
+ claim_type: ClaimType = ClaimType.CONNECTIVE
101
+ attributed_facts: list[FactScore] = field(default_factory=list)
102
+ top_score: float = 0.0 # Highest composite score
103
+ attribution_type: AttributionType = AttributionType.UNCERTAIN
104
+ confidence: float = 0.0 # Overall attribution confidence
105
+
106
+
107
+ # ---------------------------------------------------------------------------
108
+ # Provenance chain
109
+ # ---------------------------------------------------------------------------
110
+
111
+
112
+ @dataclass
113
+ class ProvenanceLink:
114
+ """Single link in a provenance chain — traces from claim → source."""
115
+
116
+ level: str = "" # "claim", "fact", "window", "envelope", "task"
117
+ label: str = "" # Human-readable label for this link
118
+ detail: dict[str, Any] = field(default_factory=dict)
119
+
120
+
121
+ @dataclass
122
+ class ProvenanceChain:
123
+ """Full provenance chain for one claim — linked list from output to input."""
124
+
125
+ claim_text: str = ""
126
+ claim_index: int = 0
127
+ attribution_type: AttributionType = AttributionType.UNCERTAIN
128
+ links: list[ProvenanceLink] = field(default_factory=list)
129
+
130
+
131
+ # ---------------------------------------------------------------------------
132
+ # Provenance report
133
+ # ---------------------------------------------------------------------------
134
+
135
+
136
+ @dataclass
137
+ class ProvenanceReport:
138
+ """Complete provenance report for a single dispatch window."""
139
+
140
+ session_id: str = ""
141
+ window_id: str = ""
142
+ timestamp: float = 0.0
143
+
144
+ # Counts
145
+ total_claims: int = 0
146
+ factual_claims: int = 0
147
+ opinion_claims: int = 0
148
+ procedural_claims: int = 0
149
+ hedge_claims: int = 0
150
+ connective_claims: int = 0
151
+
152
+ # Attribution summary
153
+ context_grounded_count: int = 0
154
+ parametric_count: int = 0
155
+ mixed_count: int = 0
156
+ uncertain_count: int = 0
157
+ grounding_ratio: float = 0.0 # context_grounded / factual_claims
158
+
159
+ # Payload
160
+ attributions: list[ClaimAttribution] = field(default_factory=list)
161
+ chains: list[ProvenanceChain] = field(default_factory=list)
162
+
163
+ # Integrity
164
+ chain_verified: bool = False
165
+ output_token_count: int = 0
166
+ envelope_facts_count: int = 0
167
+
168
+ # Fidelity verification (None until fidelity checks run)
169
+ fidelity: FidelityReport | None = None
170
+
171
+ # Semantic entailment verification (None until entailment runs)
172
+ entailment_results: list[EntailmentResult] = field(default_factory=list)
173
+
174
+ # Hallucination risk assessment (None until risk scoring runs)
175
+ risk_report: HallucinationRiskReport | None = None
176
+
177
+
178
+ # ---------------------------------------------------------------------------
179
+ # Fidelity verification results
180
+ # ---------------------------------------------------------------------------
181
+
182
+
183
+ @dataclass
184
+ class DistortionResult:
185
+ """A specific distortion found between a grounded claim and its source fact."""
186
+
187
+ claim_index: int = 0
188
+ claim_text: str = ""
189
+ source_fact_id: str = ""
190
+ source_fact_preview: str = ""
191
+ distortion_type: DistortionType = DistortionType.NUMBER_CHANGED
192
+ severity: float = 0.0 # 0.0-1.0
193
+ detail: str = "" # Human-readable explanation
194
+ claim_value: str = "" # The value in the claim
195
+ fact_value: str = "" # The value in the source fact
196
+
197
+
198
+ @dataclass
199
+ class FabricationResult:
200
+ """A specific fabrication found in a claim — an entity not in any source fact."""
201
+
202
+ claim_index: int = 0
203
+ claim_text: str = ""
204
+ fabricated_entity: str = ""
205
+ entity_type: FabricationType = FabricationType.NUMBER
206
+ severity: float = 0.0 # 0.0-1.0
207
+ detail: str = ""
208
+
209
+
210
+ @dataclass
211
+ class OmissionResult:
212
+ """An envelope fact that the model ignored entirely."""
213
+
214
+ fact_id: str = ""
215
+ fact_text_preview: str = ""
216
+ fact_relevance_score: float = 0.0 # Original packing score
217
+ max_attribution_score: float = 0.0 # Highest score any claim gave this fact
218
+ severity: OmissionSeverity = OmissionSeverity.LOW
219
+
220
+
221
+ @dataclass
222
+ class ContradictionResult:
223
+ """Two claims that contradict each other."""
224
+
225
+ claim_a_index: int = 0
226
+ claim_a_text: str = ""
227
+ claim_b_index: int = 0
228
+ claim_b_text: str = ""
229
+ contradiction_type: str = "" # "NEGATION" | "NUMBER_CONFLICT" | "SEMANTIC"
230
+ severity: float = 0.0 # 0.0-1.0
231
+ detail: str = ""
232
+
233
+
234
+ @dataclass
235
+ class FidelityReport:
236
+ """Complete fidelity verification results for a dispatch window.
237
+
238
+ Answers: "Given source attribution, did the model faithfully represent
239
+ the sources, or did it distort, fabricate, omit, or contradict?"
240
+ """
241
+
242
+ distortions: list[DistortionResult] = field(default_factory=list)
243
+ fabrications: list[FabricationResult] = field(default_factory=list)
244
+ omissions: list[OmissionResult] = field(default_factory=list)
245
+ contradictions: list[ContradictionResult] = field(default_factory=list)
246
+
247
+ # Aggregate counts
248
+ distortion_count: int = 0
249
+ fabrication_count: int = 0
250
+ critical_omission_count: int = 0
251
+ contradiction_count: int = 0
252
+
253
+ # Composite fidelity score (1.0 = perfect fidelity, 0.0 = severe issues)
254
+ fidelity_score: float = 1.0
255
+
256
+
257
+ # ---------------------------------------------------------------------------
258
+ # Semantic Entailment — ML-powered claim↔fact verification
259
+ # ---------------------------------------------------------------------------
260
+
261
+
262
+ class EntailmentLabel(str, Enum):
263
+ """NLI classification of a claim against its source fact."""
264
+
265
+ ENTAILED = "ENTAILED" # Claim logically follows from fact
266
+ CONTRADICTION = "CONTRADICTION" # Claim contradicts the fact
267
+ NEUTRAL = "NEUTRAL" # Claim is unrelated to the fact
268
+
269
+
270
+ @dataclass
271
+ class EntailmentResult:
272
+ """Semantic entailment verdict for a single claim↔fact pair.
273
+
274
+ Uses Natural Language Inference (NLI) to determine whether a claim
275
+ is *semantically* supported by its attributed source fact — beyond
276
+ lexical overlap.
277
+ """
278
+
279
+ claim_index: int = 0
280
+ claim_text: str = ""
281
+ fact_id: str = ""
282
+ fact_text_preview: str = ""
283
+ label: EntailmentLabel = EntailmentLabel.NEUTRAL
284
+ confidence: float = 0.0 # P(predicted_label)
285
+ entailment_score: float = 0.0 # P(entailed)
286
+ contradiction_score: float = 0.0 # P(contradiction)
287
+ neutral_score: float = 0.0 # P(neutral)
288
+ used_model: bool = False # True if NLI model ran; False if heuristic
289
+
290
+
291
+ # ---------------------------------------------------------------------------
292
+ # Hallucination Risk — composite per-claim risk assessment
293
+ # ---------------------------------------------------------------------------
294
+
295
+
296
+ class HallucinationRisk(str, Enum):
297
+ """Per-claim hallucination risk level."""
298
+
299
+ LOW = "LOW" # Well-sourced, semantically faithful
300
+ MEDIUM = "MEDIUM" # Partially sourced or minor fidelity issues
301
+ HIGH = "HIGH" # Poorly sourced or semantic mismatch
302
+ CRITICAL = "CRITICAL" # Contradicts source or fabricated content
303
+
304
+
305
+ @dataclass
306
+ class ClaimRiskAssessment:
307
+ """Hallucination risk assessment for a single claim.
308
+
309
+ Aggregates four independent signals into one auditable risk score:
310
+ 1. Attribution signal — how well-sourced is this claim?
311
+ 2. Fidelity signal — did lexical checks find distortions?
312
+ 3. Entailment signal — does NLI confirm semantic support?
313
+ 4. Specificity signal — how specific (and thus risky) is the claim?
314
+ """
315
+
316
+ claim_index: int = 0
317
+ claim_text: str = ""
318
+ risk_level: HallucinationRisk = HallucinationRisk.LOW
319
+ risk_score: float = 0.0 # 0.0=safe, 1.0=critical
320
+ # Individual signals (all 0.0-1.0; higher = safer)
321
+ attribution_signal: float = 0.0
322
+ fidelity_signal: float = 1.0
323
+ entailment_signal: float = 0.0
324
+ specificity_signal: float = 0.0 # Higher = more specific = riskier
325
+ risk_factors: list[str] = field(default_factory=list)
326
+
327
+
328
+ @dataclass
329
+ class HallucinationRiskReport:
330
+ """Window-level hallucination risk report.
331
+
332
+ Answers: \"For each claim, how likely is it that the model hallucinated,
333
+ and what is the overall risk profile of this output?\"
334
+ """
335
+
336
+ assessments: list[ClaimRiskAssessment] = field(default_factory=list)
337
+ high_risk_count: int = 0
338
+ critical_risk_count: int = 0
339
+ mean_risk_score: float = 0.0
340
+ window_risk_level: HallucinationRisk = HallucinationRisk.LOW
341
+
342
+
343
+ # ---------------------------------------------------------------------------
344
+ # Configuration
345
+ # ---------------------------------------------------------------------------
346
+
347
+
348
+ @dataclass
349
+ class ProvenanceConfig:
350
+ """Configuration for the Decision Provenance Engine."""
351
+
352
+ enabled: bool = True
353
+
354
+ # Claim detection
355
+ min_claim_length: int = 10 # Skip very short fragments
356
+ max_claims_per_output: int = 50 # Safety limit
357
+
358
+ # Attribution scoring
359
+ similarity_threshold: float = 0.50 # Below this → PARAMETRIC
360
+ mixed_threshold: float = 0.35 # Between mixed and parametric
361
+ lexical_weight: float = 0.40 # Weight for lexical overlap
362
+ semantic_weight: float = 0.60 # Weight for semantic similarity
363
+
364
+ # Report generation
365
+ generate_report: bool = True
366
+ fact_preview_length: int = 120 # Chars of fact text in previews
367
+
368
+ # Semantic entailment
369
+ entailment_enabled: bool = True
370
+ entailment_model: str = "cross-encoder/nli-MiniLM2-L6-H768"
371
+ entailment_contradiction_threshold: float = 0.70 # P(contradiction) above this → flag
372
+
373
+ # Hallucination risk scoring
374
+ risk_scoring_enabled: bool = True
375
+ risk_weight_attribution: float = 0.30
376
+ risk_weight_fidelity: float = 0.25
377
+ risk_weight_entailment: float = 0.30
378
+ risk_weight_specificity: float = 0.15