sable-sphynx 0.7.0.dev0__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. sable_sphynx-0.7.0.dev0.dist-info/METADATA +152 -0
  2. sable_sphynx-0.7.0.dev0.dist-info/RECORD +174 -0
  3. sable_sphynx-0.7.0.dev0.dist-info/WHEEL +5 -0
  4. sable_sphynx-0.7.0.dev0.dist-info/top_level.txt +1 -0
  5. sphynx/__init__.py +469 -0
  6. sphynx/__init__.pyi +376 -0
  7. sphynx/_analytical_client.py +77 -0
  8. sphynx/_analytical_values.py +252 -0
  9. sphynx/_audit_client.py +173 -0
  10. sphynx/_comparison_adapters.py +113 -0
  11. sphynx/_componentized_full_rag.py +34 -0
  12. sphynx/_composed_pipeline.py +109 -0
  13. sphynx/_full_rag_grading.py +218 -0
  14. sphynx/_full_rag_trace.py +221 -0
  15. sphynx/_local_eval.py +268 -0
  16. sphynx/_pipeline_core.py +243 -0
  17. sphynx/_pr_client.py +237 -0
  18. sphynx/_rag_diagnostics_calibration.py +285 -0
  19. sphynx/_rag_diagnostics_catalog.py +39 -0
  20. sphynx/_rag_diagnostics_common.py +26 -0
  21. sphynx/_rag_diagnostics_contracts.py +186 -0
  22. sphynx/_rag_diagnostics_engine.py +243 -0
  23. sphynx/_rag_diagnostics_importers.py +242 -0
  24. sphynx/_rag_diagnostics_ranking.py +81 -0
  25. sphynx/_rag_diagnostics_semantic.py +325 -0
  26. sphynx/_rag_ranking.py +149 -0
  27. sphynx/_speech_benchmark_scoring.py +140 -0
  28. sphynx/_speech_benchmark_sources.py +275 -0
  29. sphynx/_speech_complete_scoring.py +218 -0
  30. sphynx/_speech_complete_source.py +118 -0
  31. sphynx/_speech_kido.py +294 -0
  32. sphynx/_speech_scoring.py +77 -0
  33. sphynx/_track_capture.py +213 -0
  34. sphynx/analytical_views.py +515 -0
  35. sphynx/assessment_contracts.py +378 -0
  36. sphynx/assessment_selection.py +57 -0
  37. sphynx/audit_access.py +245 -0
  38. sphynx/audit_contracts.py +204 -0
  39. sphynx/audit_requests.py +180 -0
  40. sphynx/authoring.py +188 -0
  41. sphynx/benchmarks.py +568 -0
  42. sphynx/candidates.py +62 -0
  43. sphynx/catalog.py +272 -0
  44. sphynx/chunking.py +372 -0
  45. sphynx/clickhouse_reader.py +169 -0
  46. sphynx/client.py +486 -0
  47. sphynx/comparison_core.py +149 -0
  48. sphynx/comparison_views.py +223 -0
  49. sphynx/components.py +1661 -0
  50. sphynx/contracts.py +514 -0
  51. sphynx/customer_rerank.py +299 -0
  52. sphynx/customer_retrieval.py +146 -0
  53. sphynx/data/benchmark-releases.v1.json +164 -0
  54. sphynx/data/public-benchmark-release.v1.json +354 -0
  55. sphynx/data/rag-diagnostics-synthetic.v1.json +6982 -0
  56. sphynx/data/rag-paper-references.v1.json +176 -0
  57. sphynx/data/rag-paper-schemas.v1.json +1069 -0
  58. sphynx/data/rag-rerank-v1.json +507 -0
  59. sphynx/dataset_catalog_data.py +1214 -0
  60. sphynx/dataset_readiness.py +138 -0
  61. sphynx/dataset_v3.py +999 -0
  62. sphynx/demo.py +130 -0
  63. sphynx/demo_candidates/__init__.py +1 -0
  64. sphynx/demo_candidates/dense.py +11 -0
  65. sphynx/demo_candidates/lexical.py +21 -0
  66. sphynx/diagnostics.py +207 -0
  67. sphynx/documentation.py +737 -0
  68. sphynx/documentation_candidate.py +939 -0
  69. sphynx/evals.py +538 -0
  70. sphynx/evaluation_protocols.py +124 -0
  71. sphynx/evaluator_bindings.py +370 -0
  72. sphynx/evidence.py +352 -0
  73. sphynx/execution_capabilities.py +109 -0
  74. sphynx/fast_search.py +285 -0
  75. sphynx/full_rag.py +341 -0
  76. sphynx/hosted/__init__.py +18 -0
  77. sphynx/hosted/benchmark_intake.py +218 -0
  78. sphynx/hosted/contracts.py +361 -0
  79. sphynx/hosted/model_admission.py +130 -0
  80. sphynx/hosted/model_artifacts.py +298 -0
  81. sphynx/hosted/model_contracts.py +63 -0
  82. sphynx/hosted/model_gcs.py +181 -0
  83. sphynx/hosted/model_history.py +84 -0
  84. sphynx/hosted/model_session.py +233 -0
  85. sphynx/hosted/public_suite.py +74 -0
  86. sphynx/hosted/registry.py +97 -0
  87. sphynx/hosted/revisions.py +126 -0
  88. sphynx/hosted/sandbox.py +105 -0
  89. sphynx/hosted/submission.py +287 -0
  90. sphynx/hosted_workflow.py +451 -0
  91. sphynx/http_transport.py +86 -0
  92. sphynx/integrated_rag.py +352 -0
  93. sphynx/integrated_workflow.py +552 -0
  94. sphynx/integration_contracts.py +474 -0
  95. sphynx/invocation.py +791 -0
  96. sphynx/invocation_candidates.py +392 -0
  97. sphynx/invocation_catalog_data.py +40 -0
  98. sphynx/learned_catalog_data.py +30 -0
  99. sphynx/learned_rerank.py +267 -0
  100. sphynx/legacy_full_rag.py +67 -0
  101. sphynx/lifecycle.py +885 -0
  102. sphynx/lifecycle_workflow.py +490 -0
  103. sphynx/metric_views.py +474 -0
  104. sphynx/metrics.py +282 -0
  105. sphynx/model_adapters.py +438 -0
  106. sphynx/model_catalog_data.py +30 -0
  107. sphynx/official_benchmarks.py +385 -0
  108. sphynx/packs.py +258 -0
  109. sphynx/pipeline.py +313 -0
  110. sphynx/pipeline_workflow.py +551 -0
  111. sphynx/pr_evaluations.py +560 -0
  112. sphynx/progress.py +89 -0
  113. sphynx/public_bundle.py +173 -0
  114. sphynx/public_eval.py +62 -0
  115. sphynx/public_rerank.py +355 -0
  116. sphynx/py.typed +0 -0
  117. sphynx/pytest_plugin.py +62 -0
  118. sphynx/question_robustness.py +437 -0
  119. sphynx/rag.py +329 -0
  120. sphynx/rag_diagnostics.py +309 -0
  121. sphynx/rag_papers/__init__.py +29 -0
  122. sphynx/rag_papers/__main__.py +45 -0
  123. sphynx/rag_papers/candidates.py +19 -0
  124. sphynx/rag_papers/contracts.py +282 -0
  125. sphynx/rag_papers/datasets.py +291 -0
  126. sphynx/rag_papers/experiments.py +116 -0
  127. sphynx/rag_papers/judges.py +308 -0
  128. sphynx/rag_papers/registry.py +33 -0
  129. sphynx/rag_papers/runner.py +176 -0
  130. sphynx/rag_papers/sable.py +135 -0
  131. sphynx/rag_papers/schemas.py +12 -0
  132. sphynx/rag_papers/scoring.py +249 -0
  133. sphynx/rag_papers/semantic.py +288 -0
  134. sphynx/rag_papers/trec.py +80 -0
  135. sphynx/recorded_scores.py +352 -0
  136. sphynx/records.py +124 -0
  137. sphynx/reflex_traces.py +193 -0
  138. sphynx/report_rendering.py +71 -0
  139. sphynx/rerank_family_contracts.py +150 -0
  140. sphynx/rerank_workflow.py +424 -0
  141. sphynx/research_candidate.py +190 -0
  142. sphynx/results.py +852 -0
  143. sphynx/retrieval.py +750 -0
  144. sphynx/retrieval_candidates.py +295 -0
  145. sphynx/retrieval_catalog_data.py +19 -0
  146. sphynx/retrieval_recordings.py +125 -0
  147. sphynx/reviewer_audit.py +266 -0
  148. sphynx/routing_policies.py +375 -0
  149. sphynx/runner.py +544 -0
  150. sphynx/sable_rerank.py +489 -0
  151. sphynx/score_reliability.py +393 -0
  152. sphynx/speech_benchmarks.py +204 -0
  153. sphynx/speech_campaigns.py +378 -0
  154. sphynx/speech_complete.py +236 -0
  155. sphynx/speech_contracts.py +225 -0
  156. sphynx/speech_evidence.py +429 -0
  157. sphynx/speech_listening.py +115 -0
  158. sphynx/speech_multilingual.py +71 -0
  159. sphynx/speech_tts.py +479 -0
  160. sphynx/sql/001_sync_v1.sql +30 -0
  161. sphynx/stage_contracts.py +199 -0
  162. sphynx/statistical_catalog_data.py +53 -0
  163. sphynx/statistical_rerank.py +232 -0
  164. sphynx/sync.py +797 -0
  165. sphynx/workbench.py +378 -0
  166. sphynx/workbench_catalog_data.py +13 -0
  167. sphynx/workbench_tracks.py +78 -0
  168. sphynx/workflow_async.py +197 -0
  169. sphynx/workflow_contracts.py +594 -0
  170. sphynx/workflow_decisions.py +211 -0
  171. sphynx/workflow_defaults.py +16 -0
  172. sphynx/workflow_extensions.py +212 -0
  173. sphynx/workflow_registry.py +513 -0
  174. sphynx/workflows.py +792 -0
@@ -0,0 +1,352 @@
1
+ """Versioned router/rewrite/retrieval/context/answer execution.
2
+
3
+ This boundary executes retrieval. It does not reinterpret the native supplied-
4
+ context v1 runner. Component calls are trusted local execution, not OS isolation.
5
+ Lifecycle journals retain stage artifacts, including partial work on failure.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from copy import deepcopy
10
+ from dataclasses import dataclass
11
+ import hashlib
12
+ import json
13
+ import time
14
+
15
+ from .assessment_contracts import CaseView, _thaw
16
+ from .components import (
17
+ AdapterDescriptor, EvaluationTarget, OPERATIONS_BY_KIND, PIPELINE_COMPONENT_ORDER,
18
+ adapter_component_identity_sha256,
19
+ )
20
+ from .integration_contracts import value_digest
21
+ from .lifecycle import (
22
+ AttemptOutput, DriverDescriptor, DriverFailure, RunPolicy, execute_lifecycle,
23
+ )
24
+ from .pipeline import validate_pipeline_response
25
+ from .pipeline_workflow import (
26
+ PipelineBackend, PipelineCandidate, _stage_definition, validate_stage_input,
27
+ validate_stage_output,
28
+ )
29
+ from ._composed_pipeline import compose_pipeline, validate_chunks
30
+ from .workflow_contracts import PayloadSchema, PlanBinding, WorkflowContractError, _text, contract_digest
31
+
32
+ INTEGRATED_SCHEMA = "sphynx.rag.integrated.response.v1"
33
+ INTEGRATED_STAGE_SCHEMA = "sphynx.rag.integrated.stage.v1"
34
+ INTEGRATED_STAGES = ("router", "rewriter", *PIPELINE_COMPONENT_ORDER, "context", "generator")
35
+ _FULL_TARGET = EvaluationTarget("level3", "full_rag")
36
+
37
+
38
+ def _require(value, message):
39
+ if not value:
40
+ raise WorkflowContractError(message)
41
+
42
+
43
+ def json_bytes(value):
44
+ return json.dumps(_thaw(value), ensure_ascii=False, allow_nan=False,
45
+ sort_keys=True, separators=(",", ":")).encode("utf-8")
46
+
47
+
48
+ def text_digest(value):
49
+ return "sha256:" + hashlib.sha256(value.encode("utf-8")).hexdigest()
50
+
51
+
52
+ def validate_extra_output(kind, value):
53
+ _require(type(value) is dict, "integrated stage output must be an object")
54
+ if kind == "router":
55
+ _require(set(value) <= {"should_call_rag", "latency_ms"} and type(value.get("should_call_rag")) is bool,
56
+ "router must return a boolean should_call_rag")
57
+ elif kind == "rewriter":
58
+ _require("rewritten_query" in value and set(value) <= {"query", "rewritten_query", "latency_ms"},
59
+ "rewriter output fields differ")
60
+ _text(value["rewritten_query"], "rewritten query")
61
+ elif kind == "generator":
62
+ _require({"answer", "abstained"} <= set(value)
63
+ and set(value) <= {"answer", "abstained", "citations", "token_counts", "latency_ms"},
64
+ "generator output fields differ")
65
+ _require(type(value["answer"]) is str and type(value["abstained"]) is bool,
66
+ "generator answer/action differs")
67
+ _require(type(value.get("citations", [])) is list, "generator citations must be an array")
68
+ if "token_counts" in value:
69
+ counts = value["token_counts"]
70
+ _require(type(counts) is dict and set(counts) == {"prompt", "completion"}
71
+ and all(type(n) is int and n >= 0 for n in counts.values()), "token count fields differ")
72
+ elif kind == "context":
73
+ _require(type(value.get("rows")) is list and type(value.get("text")) is str
74
+ and type(value.get("bytes")) is int, "context output fields differ")
75
+ else:
76
+ raise WorkflowContractError("unknown integrated stage")
77
+
78
+
79
+ def build_context(response, documents, max_context_bytes):
80
+ """Select whole source-bound chunks in final-rank order under a UTF-8 budget."""
81
+ _require(type(max_context_bytes) is int and max_context_bytes > 0, "context byte limit must be positive")
82
+ chunks = validate_chunks(response["construction"]["chunks"], documents)
83
+ source = {row["document_id"]: row for row in documents}
84
+ selected = sorted((row for row in response["candidates"] if row["final_rank"] is not None),
85
+ key=lambda row: row["final_rank"])
86
+ rows, texts, count = [], [], 0
87
+ for selected_row in selected:
88
+ chunk = chunks[selected_row["candidate_id"]]
89
+ text = chunk["input"]
90
+ extra = len(text.encode("utf-8")) + (2 if texts else 0)
91
+ if count + extra > max_context_bytes:
92
+ break
93
+ document = source[chunk["document_id"]]
94
+ _require(document.get("content_sha256") == text_digest(document["text"]).removeprefix("sha256:"),
95
+ "context source document digest differs")
96
+ rows.append({"candidate_id": chunk["candidate_id"], "document_id": chunk["document_id"],
97
+ "start": chunk["start"], "end": chunk["end"], "input": text,
98
+ "text_sha256": text_digest(text), "source_document_sha256": text_digest(document["text"])})
99
+ texts.append(text)
100
+ count += extra
101
+ text = "\n\n".join(texts)
102
+ return {"rows": rows, "text": text, "bytes": count, "text_sha256": text_digest(text),
103
+ "selection_policy": "whole_chunks_in_final_rank_order_utf8_bytes.v1"}
104
+
105
+
106
+ def validate_answer(output, context):
107
+ validate_extra_output("generator", output)
108
+ allowed = {row["candidate_id"] for row in context["rows"]}
109
+ ids = set()
110
+ for citation in output.get("citations", []):
111
+ _require(type(citation) is dict and set(citation) == {"citation_id", "evidence_id", "start", "end"},
112
+ "integrated citation fields differ")
113
+ _text(citation["citation_id"], "citation_id")
114
+ _require(citation["citation_id"] not in ids and citation["evidence_id"] in allowed,
115
+ "citation leaves the exact generator context")
116
+ ids.add(citation["citation_id"])
117
+ _require(type(citation["start"]) is int and type(citation["end"]) is int
118
+ and 0 <= citation["start"] < citation["end"] <= len(output["answer"]),
119
+ "citation offsets leave the answer")
120
+
121
+
122
+ @dataclass(frozen=True)
123
+ class IntegratedCandidate:
124
+ pipeline: PipelineCandidate
125
+ router_id: str
126
+ rewriter_id: str
127
+ generator_id: str
128
+ revision: str
129
+
130
+ def __post_init__(self):
131
+ _require(isinstance(self.pipeline, PipelineCandidate), "integrated candidate needs PipelineCandidate")
132
+ for name in ("router_id", "rewriter_id", "generator_id", "revision"):
133
+ _text(getattr(self, name), name)
134
+ _require(self.revision == self.pipeline.revision, "component/subsystem/system candidate revisions differ")
135
+
136
+
137
+ class IntegratedDriver:
138
+ """Cooperative lifecycle driver over explicitly registered native components."""
139
+
140
+ def __init__(self, pipeline: PipelineBackend, *, router_id, rewriter_id, generator_id,
141
+ max_context_bytes=16000):
142
+ _require(isinstance(pipeline, PipelineBackend), "integrated driver requires PipelineBackend")
143
+ _require(type(max_context_bytes) is int and max_context_bytes > 0, "context byte limit must be positive")
144
+ self.pipeline = pipeline
145
+ self.router_id, self.rewriter_id, self.generator_id = router_id, rewriter_id, generator_id
146
+ self.max_context_bytes = max_context_bytes
147
+ self._configuration = {"pipeline_binding": pipeline.describe(pipeline.suite_id).definition_digest,
148
+ "router_id": router_id, "rewriter_id": rewriter_id,
149
+ "generator_id": generator_id, "max_context_bytes": max_context_bytes}
150
+ self.descriptor = DriverDescriptor(
151
+ "sphynx.rag.integrated.driver", "sphynx.rag.integrated.driver.v1",
152
+ ("sphynx.lifecycle.deadline.cooperative.v1",))
153
+ self._descriptor_digest = contract_digest(self.descriptor.to_dict())
154
+ for kind, identifier in (("router", router_id), ("rewriter", rewriter_id), ("generator", generator_id)):
155
+ self._component(kind, identifier, resolve=False)
156
+
157
+ def candidate(self, pipeline_candidate):
158
+ return IntegratedCandidate(pipeline_candidate, self.router_id, self.rewriter_id,
159
+ self.generator_id, pipeline_candidate.revision)
160
+
161
+ def _component(self, kind, identifier, *, resolve):
162
+ component = self.pipeline.components.by_id.get(identifier)
163
+ _require(component is not None and component.kind == kind, "integrated component kind/identity differs")
164
+ _require(_FULL_TARGET in component.capabilities, "integrated component lacks its native capability")
165
+ _require(component.model is None or not component.model.training_required, "integrated model requires forbidden training")
166
+ if resolve:
167
+ _require(component.implementation.kind in self.pipeline.allowed_implementation_kinds,
168
+ "integrated component implementation is not authorized")
169
+ adapter = self.pipeline.adapters.resolve(identifier, _FULL_TARGET, OPERATIONS_BY_KIND[kind][0])
170
+ _require(adapter.descriptor == AdapterDescriptor.from_component(component), "integrated adapter identity drift")
171
+ return component
172
+
173
+ def candidate_identity(self, candidate):
174
+ _require(isinstance(candidate, IntegratedCandidate), "candidate must be IntegratedCandidate")
175
+ self.pipeline.descriptor
176
+ _require(contract_digest(self.descriptor.to_dict()) == self._descriptor_digest,
177
+ "integrated driver descriptor drift")
178
+ _require(self._configuration == {
179
+ "pipeline_binding": self.pipeline.describe(self.pipeline.suite_id).definition_digest,
180
+ "router_id": self.router_id, "rewriter_id": self.rewriter_id,
181
+ "generator_id": self.generator_id, "max_context_bytes": self.max_context_bytes},
182
+ "integrated driver configuration drift")
183
+ native = self.pipeline._candidate(candidate.pipeline)
184
+ extra = {}
185
+ for kind in ("router", "rewriter", "generator"):
186
+ component = self._component(kind, getattr(candidate, kind + "_id"), resolve=False)
187
+ extra[kind] = {"component_id": component.component_id, "component_version": component.version,
188
+ "descriptor_digest": "sha256:" + adapter_component_identity_sha256(
189
+ AdapterDescriptor.from_component(component))}
190
+ return {"schema_version": "sphynx.rag.integrated.candidate.v1",
191
+ "revision": candidate.revision, "pipeline": native.to_dict(), "components": extra,
192
+ "context_policy": {"max_bytes": self.max_context_bytes,
193
+ "selection": "whole_chunks_in_final_rank_order_utf8_bytes.v1"},
194
+ "candidate_source_bytes_verified": False}
195
+
196
+ def admit(self, candidate):
197
+ self.candidate_identity(candidate)
198
+ plan = PlanBinding(self.pipeline.describe(self.pipeline.suite_id), candidate.pipeline, "subsystem", "local")
199
+ admitted = self.pipeline.admit(plan, self.pipeline.resolve(plan))
200
+ for kind in ("router", "rewriter", "generator"):
201
+ self._component(kind, getattr(candidate, kind + "_id"), resolve=True)
202
+ return admitted
203
+
204
+ def cases(self):
205
+ return self.pipeline.cases(self.pipeline.describe(self.pipeline.suite_id))
206
+
207
+ def prepare(self, candidate, context):
208
+ admitted = self.admit(candidate)
209
+ return {"source_digest": self.pipeline.source.sha256,
210
+ "pipeline_admission_digest": contract_digest(admitted.to_dict()),
211
+ "case_digests": {case.case_id: contract_digest(case.to_dict()) for case in self.cases()},
212
+ "context_policy": self.candidate_identity(candidate)["context_policy"]}
213
+
214
+ def execute(self, candidate, case: CaseView, context):
215
+ _require(case.case_id == context.case_id
216
+ and context.prepared.get("case_digests", {}).get(case.case_id) == contract_digest(case.to_dict()),
217
+ "integrated case differs from the prepared source")
218
+ identity = self.candidate_identity(candidate)
219
+ stages, events = {}, []
220
+ result = {"schema_version": INTEGRATED_SCHEMA, "case_id": case.case_id,
221
+ "trial": context.trial, "attempt": context.attempt, "candidate_revision": candidate.revision,
222
+ "input_digest": contract_digest(case.to_dict()), "candidate_identity_digest": value_digest(identity),
223
+ "stages": stages, "events": events, "retrieval": None, "context": None, "answer": None,
224
+ "citations": [], "abstained": None, "actual_rag_call_count": None,
225
+ "token_counts": None, "failure_stage": None, "skipped_stages": []}
226
+
227
+ def partial(failure, *, acknowledged=False):
228
+ return AttemptOutput(outputs=result, failure=failure,
229
+ metadata={"boundary": "integrated_retrieval_context_answer",
230
+ "timing_authority": "parent_observed_local_stage_wall",
231
+ "cancellation_acknowledged": acknowledged})
232
+
233
+ def checkpoint():
234
+ if context.cancellation.requested:
235
+ raise DriverFailure("cancelled", partial=partial("cancelled", acknowledged=True))
236
+ if context.deadline is not None and time.monotonic() >= context.deadline:
237
+ raise DriverFailure("timeout", partial=partial("timeout"))
238
+
239
+ def retain(kind, phase, record):
240
+ raw = json_bytes(record)
241
+ binding = context.emit("integrated." + kind + "." + phase, raw)
242
+ events.append({"sequence": len(events), "stage_id": kind, "occurrence": 1, "phase": phase,
243
+ "artifact": dict(binding)})
244
+ if binding["size_bytes"] != len(raw):
245
+ raise DriverFailure("output_limit", partial=partial("output_limit"))
246
+ return dict(binding)
247
+
248
+ def call(kind, operation, payload, *, internal=None):
249
+ checkpoint()
250
+ record = {"schema_version": INTEGRATED_STAGE_SCHEMA, "run_id": context.run_id,
251
+ "case_id": case.case_id, "trial": context.trial, "attempt": context.attempt,
252
+ "stage_id": kind, "occurrence": 1, "operation": operation,
253
+ "input_digest": value_digest(payload), "input": deepcopy(payload),
254
+ "output": None, "output_digest": None, "status": "started"}
255
+ if kind in PIPELINE_COMPONENT_ORDER:
256
+ ref = candidate.pipeline.configuration.by_kind[kind]
257
+ component = self.pipeline.components.by_id[ref.component_id]
258
+ validate_stage_input(kind, payload)
259
+ definition = _stage_definition(component).to_dict()
260
+ target = EvaluationTarget("level2", "pipeline")
261
+ elif kind != "context":
262
+ component = self._component(kind, getattr(candidate, kind + "_id"), resolve=True)
263
+ definition = {"component_id": component.component_id,
264
+ "descriptor_digest": "sha256:" + adapter_component_identity_sha256(
265
+ AdapterDescriptor.from_component(component))}
266
+ target = _FULL_TARGET
267
+ else:
268
+ definition = {"implementation_id": "sphynx.integrated.context-builder.v1",
269
+ "configuration": identity["context_policy"],
270
+ "authority": "package_declared_not_code_attestation"}
271
+ record["implementation"] = definition
272
+ retain(kind, "started", record)
273
+ begin = time.perf_counter()
274
+ try:
275
+ output = internal() if internal is not None else self.pipeline.adapters.resolve(
276
+ component.component_id, target, operation).invoke(operation, deepcopy(payload))
277
+ def validator(value):
278
+ if kind in PIPELINE_COMPONENT_ORDER:
279
+ validate_stage_output(kind, value)
280
+ else:
281
+ validate_extra_output(kind, value)
282
+ if kind == "generator":
283
+ validate_answer(value, result["context"])
284
+ schema = PayloadSchema("sphynx.integrated." + kind + ".output.v1", "v1",
285
+ "sphynx.integrated.stage-validator.v1", validator)
286
+ output = _thaw(schema.validate(output))
287
+ record.update(status="completed", output=output, output_digest=value_digest(output),
288
+ parent_elapsed_seconds=time.perf_counter() - begin)
289
+ binding = retain(kind, "completed", record)
290
+ stages[kind] = {**deepcopy(record), "artifact": binding}
291
+ checkpoint()
292
+ return output
293
+ except DriverFailure:
294
+ raise
295
+ except Exception as exc:
296
+ record.update(status="failed", error_type=type(exc).__name__,
297
+ parent_elapsed_seconds=time.perf_counter() - begin)
298
+ binding = retain(kind, "failed", record)
299
+ stages[kind] = {**deepcopy(record), "artifact": binding}
300
+ result["failure_stage"] = kind
301
+ raise DriverFailure("candidate_error", partial=partial("candidate_error")) from None
302
+
303
+ query = case.inputs["query"]
304
+ routing = call("router", "route", {"task_id": case.case_id, "query": query,
305
+ "conversation_context": _thaw(case.inputs["conversation_context"])})
306
+ result["routing_decision"] = routing["should_call_rag"]
307
+ result["actual_rag_call_count"] = 0
308
+ if not routing["should_call_rag"]:
309
+ result.update(abstained=True, skipped_stages=list(INTEGRATED_STAGES[1:]))
310
+ return AttemptOutput(outputs=result)
311
+ rewriting = call("rewriter", "rewrite", {"task_id": case.case_id, "query": query})
312
+ rewritten = rewriting["rewritten_query"]
313
+ result["rewritten_query"] = rewritten
314
+ native = self.pipeline._candidate(candidate.pipeline)
315
+ request = {"schema_version": "sphynx.rag.integrated.retrieval-input.v1", "case_id": case.case_id,
316
+ "query": rewritten, "documents": _thaw(case.inputs["documents"]),
317
+ "policy": {"boundary": "composed_source_documents",
318
+ "controlled_config": _thaw(native.parameters["controlled_config"])}}
319
+ request["input_digest"] = value_digest(request)
320
+ result["actual_rag_call_count"] = 1
321
+ try:
322
+ response = compose_pipeline(request, call)
323
+ validate_pipeline_response(response, request)
324
+ except DriverFailure:
325
+ raise
326
+ except Exception:
327
+ result["failure_stage"] = "retrieval_verification"
328
+ raise DriverFailure("candidate_error", partial=partial("candidate_error")) from None
329
+ result["retrieval"] = {"request": request, "response": response}
330
+ context_output = call("context", "construct_context",
331
+ {"retrieval_input_digest": request["input_digest"], "max_context_bytes": self.max_context_bytes},
332
+ internal=lambda: build_context(response, request["documents"], self.max_context_bytes))
333
+ result["context"] = context_output
334
+ generated = call("generator", "generate", {"task_id": case.case_id, "query": rewritten,
335
+ "contexts": deepcopy(context_output["rows"])})
336
+ result.update(answer=generated["answer"], abstained=generated["abstained"],
337
+ citations=generated.get("citations", []), token_counts=generated.get("token_counts"))
338
+ usage = {} if result["token_counts"] is None else {"tokens": sum(result["token_counts"].values())}
339
+ return AttemptOutput(outputs=result, usage=usage,
340
+ metadata={"boundary": "integrated_retrieval_context_answer",
341
+ "timing_authority": "parent_observed_local_stage_wall"})
342
+
343
+
344
+ def run_integrated(driver: IntegratedDriver, candidate: IntegratedCandidate, output_dir, *,
345
+ policy: RunPolicy | None = None, cancellation=None, resume=None):
346
+ """Execute the integrated boundary through the versioned lifecycle driver."""
347
+ driver.admit(candidate)
348
+ cases = driver.cases()
349
+ return execute_lifecycle(driver, candidate, cases, output_dir,
350
+ identity={"suite_binding": driver.pipeline.describe(driver.pipeline.suite_id).definition_digest,
351
+ "boundary": "sphynx.rag.integrated.v1", "source": driver.pipeline.source.to_dict()},
352
+ policy=policy, cancellation=cancellation, resume=resume)