mostlyright-data 0.9.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 (314) hide show
  1. mostlyright/data_harness/__init__.py +158 -0
  2. mostlyright/data_harness/acquisition/__init__.py +55 -0
  3. mostlyright/data_harness/acquisition/http.py +2773 -0
  4. mostlyright/data_harness/acquisition/parsing.py +809 -0
  5. mostlyright/data_harness/acquisition/ranges.py +495 -0
  6. mostlyright/data_harness/acquisition/result_download.py +360 -0
  7. mostlyright/data_harness/acquisition/retention_admission.py +248 -0
  8. mostlyright/data_harness/acquisition/sandbox.py +4888 -0
  9. mostlyright/data_harness/acquisition/url_policy.py +530 -0
  10. mostlyright/data_harness/agent_runtime.py +2743 -0
  11. mostlyright/data_harness/assets/logo-ink.svg +31 -0
  12. mostlyright/data_harness/backends/__init__.py +28 -0
  13. mostlyright/data_harness/backends/pandas_backend.py +350 -0
  14. mostlyright/data_harness/backends/polars_backend.py +366 -0
  15. mostlyright/data_harness/backends/protocol.py +124 -0
  16. mostlyright/data_harness/backends/reference.py +83 -0
  17. mostlyright/data_harness/backends/registry.py +55 -0
  18. mostlyright/data_harness/backends/restrictions.py +126 -0
  19. mostlyright/data_harness/canonical.py +333 -0
  20. mostlyright/data_harness/catalog_job.py +625 -0
  21. mostlyright/data_harness/cli.py +5398 -0
  22. mostlyright/data_harness/contracts.py +53 -0
  23. mostlyright/data_harness/coordinator.py +1307 -0
  24. mostlyright/data_harness/deploy.py +924 -0
  25. mostlyright/data_harness/deploy_target.py +312 -0
  26. mostlyright/data_harness/deployment_evidence.py +1067 -0
  27. mostlyright/data_harness/event_presentation.py +576 -0
  28. mostlyright/data_harness/events.py +2152 -0
  29. mostlyright/data_harness/fast_delimited.py +239 -0
  30. mostlyright/data_harness/fleet.py +237 -0
  31. mostlyright/data_harness/formats.py +236 -0
  32. mostlyright/data_harness/governors.py +1163 -0
  33. mostlyright/data_harness/hosted_bootstrap.py +972 -0
  34. mostlyright/data_harness/hosted_crawler.py +1115 -0
  35. mostlyright/data_harness/hosted_crawler_container_smoke.py +351 -0
  36. mostlyright/data_harness/hosted_crawler_fetch.py +423 -0
  37. mostlyright/data_harness/hosted_crawler_job.py +1277 -0
  38. mostlyright/data_harness/hosted_crawler_protocol.py +676 -0
  39. mostlyright/data_harness/hosted_dataset.py +1500 -0
  40. mostlyright/data_harness/hosted_deploy.py +3037 -0
  41. mostlyright/data_harness/hosted_handoff.py +62 -0
  42. mostlyright/data_harness/hosted_ingestion_contract.py +504 -0
  43. mostlyright/data_harness/hosted_ingestion_job.py +356 -0
  44. mostlyright/data_harness/hosted_ingestion_job_smoke.py +40 -0
  45. mostlyright/data_harness/hosted_session_container_smoke.py +194 -0
  46. mostlyright/data_harness/hosted_session_worker.py +3554 -0
  47. mostlyright/data_harness/hosted_session_worker_job_smoke.py +46 -0
  48. mostlyright/data_harness/hosted_worker.py +6784 -0
  49. mostlyright/data_harness/ingestion/__init__.py +56 -0
  50. mostlyright/data_harness/ingestion/contracts.py +461 -0
  51. mostlyright/data_harness/ingestion/faults.py +42 -0
  52. mostlyright/data_harness/ingestion/gcs_store.py +1162 -0
  53. mostlyright/data_harness/ingestion/spool.py +130 -0
  54. mostlyright/data_harness/ingestion/store.py +885 -0
  55. mostlyright/data_harness/key_seam.py +434 -0
  56. mostlyright/data_harness/linux_process_boundary.py +262 -0
  57. mostlyright/data_harness/local_contracts.py +2880 -0
  58. mostlyright/data_harness/local_search/__init__.py +5 -0
  59. mostlyright/data_harness/local_search/build_index.py +1087 -0
  60. mostlyright/data_harness/local_search/contracts.py +920 -0
  61. mostlyright/data_harness/local_search/query_trace.py +266 -0
  62. mostlyright/data_harness/local_search/retrieval.py +700 -0
  63. mostlyright/data_harness/local_search/sealed.py +474 -0
  64. mostlyright/data_harness/local_search/service.py +784 -0
  65. mostlyright/data_harness/nbrender/CONTRACT.md +212 -0
  66. mostlyright/data_harness/nbrender/__init__.py +12 -0
  67. mostlyright/data_harness/nbrender/chrome.py +359 -0
  68. mostlyright/data_harness/nbrender/code_body.py +266 -0
  69. mostlyright/data_harness/nbrender/document.py +407 -0
  70. mostlyright/data_harness/nbrender/frame.py +275 -0
  71. mostlyright/data_harness/nbrender/interactive.py +337 -0
  72. mostlyright/data_harness/nbrender/markdown_body.py +477 -0
  73. mostlyright/data_harness/nbrender/mr_components.py +134 -0
  74. mostlyright/data_harness/nbrender/outputs_data.py +595 -0
  75. mostlyright/data_harness/nbrender/outputs_rich.py +906 -0
  76. mostlyright/data_harness/nbrender/outputs_source.py +260 -0
  77. mostlyright/data_harness/nbrender/outputs_stage.py +176 -0
  78. mostlyright/data_harness/nbrender/outputs_text.py +400 -0
  79. mostlyright/data_harness/nbrender/parse.py +394 -0
  80. mostlyright/data_harness/nbrender/status.py +40 -0
  81. mostlyright/data_harness/nbrender/tokens.py +1295 -0
  82. mostlyright/data_harness/notebook.py +1710 -0
  83. mostlyright/data_harness/offline.py +2049 -0
  84. mostlyright/data_harness/operation_registry.py +1007 -0
  85. mostlyright/data_harness/operator_setup.py +239 -0
  86. mostlyright/data_harness/pipeline.py +6428 -0
  87. mostlyright/data_harness/plan_graph.py +2026 -0
  88. mostlyright/data_harness/preparation/__init__.py +104 -0
  89. mostlyright/data_harness/preparation/contracts.py +1017 -0
  90. mostlyright/data_harness/preparation/engine.py +221 -0
  91. mostlyright/data_harness/preparation/errors.py +14 -0
  92. mostlyright/data_harness/preparation/gates.py +751 -0
  93. mostlyright/data_harness/preparation/joins.py +574 -0
  94. mostlyright/data_harness/preparation/profile.py +384 -0
  95. mostlyright/data_harness/preparation/table.py +217 -0
  96. mostlyright/data_harness/preparation/transforms.py +568 -0
  97. mostlyright/data_harness/progress_events.py +534 -0
  98. mostlyright/data_harness/readers/__init__.py +46 -0
  99. mostlyright/data_harness/readers/containers.py +963 -0
  100. mostlyright/data_harness/readers/contracts.py +542 -0
  101. mostlyright/data_harness/readers/delimited.py +257 -0
  102. mostlyright/data_harness/readers/grib2/__init__.py +33 -0
  103. mostlyright/data_harness/readers/grib2/admission.py +722 -0
  104. mostlyright/data_harness/readers/grib2/decode.py +1009 -0
  105. mostlyright/data_harness/readers/grib2/geometry.py +1133 -0
  106. mostlyright/data_harness/readers/grib2/portable_math.py +501 -0
  107. mostlyright/data_harness/readers/json_tabular.py +485 -0
  108. mostlyright/data_harness/readers/registry.py +514 -0
  109. mostlyright/data_harness/readers/samples/README.md +110 -0
  110. mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/cities.csv.gz +0 -0
  111. mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/expected.json +24 -0
  112. mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/cities.csv.gz +0 -0
  113. mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/expected.json +24 -0
  114. mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/cities.tar +0 -0
  115. mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/expected.json +24 -0
  116. mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/cities.tar +0 -0
  117. mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/expected.json +24 -0
  118. mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/cities.zip +0 -0
  119. mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/expected.json +25 -0
  120. mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  121. mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/expected.json +25 -0
  122. mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  123. mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/expected.json +25 -0
  124. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/cities.csv +3 -0
  125. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/expected.json +23 -0
  126. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/cities.tsv +5 -0
  127. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/expected.json +25 -0
  128. mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/expected.json +30 -0
  129. mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/observations.csv +5 -0
  130. mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/expected.json +44 -0
  131. mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/stations.json +1 -0
  132. mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/expected.json +48 -0
  133. mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/observations.ndjson +4 -0
  134. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/cities.xlsx +0 -0
  135. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/expected.json +24 -0
  136. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/expected.json +27 -0
  137. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  138. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/expected.json +27 -0
  139. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  140. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/README.md +20 -0
  141. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/expected.json +55 -0
  142. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/gfs-2m-temperature.grib2 +0 -0
  143. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/expected.json +54 -0
  144. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  145. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/expected.json +54 -0
  146. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/hrrr-categorical-rain.grib2 +0 -0
  147. mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/expected.json +54 -0
  148. mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  149. mostlyright/data_harness/readers/samples.py +582 -0
  150. mostlyright/data_harness/readers/spreadsheet.py +803 -0
  151. mostlyright/data_harness/readers/tabular.py +510 -0
  152. mostlyright/data_harness/recipe.py +5321 -0
  153. mostlyright/data_harness/repair/__init__.py +78 -0
  154. mostlyright/data_harness/repair/adapters.py +274 -0
  155. mostlyright/data_harness/repair/contracts.py +872 -0
  156. mostlyright/data_harness/repair/coordinator.py +1099 -0
  157. mostlyright/data_harness/repair/errors.py +16 -0
  158. mostlyright/data_harness/review.py +2533 -0
  159. mostlyright/data_harness/rowset.py +283 -0
  160. mostlyright/data_harness/serving.py +1975 -0
  161. mostlyright/data_harness/serving_edge.py +590 -0
  162. mostlyright/data_harness/serving_http.py +1031 -0
  163. mostlyright/data_harness/session_probes.py +759 -0
  164. mostlyright/data_harness/signing.py +101 -0
  165. mostlyright/data_harness/source_discovery.py +898 -0
  166. mostlyright/data_harness/sources/__init__.py +209 -0
  167. mostlyright/data_harness/sources/_adapter_steps.py +213 -0
  168. mostlyright/data_harness/sources/adapters.py +1214 -0
  169. mostlyright/data_harness/sources/cadence.py +1428 -0
  170. mostlyright/data_harness/sources/cadence_emission.py +453 -0
  171. mostlyright/data_harness/sources/cadence_history.py +546 -0
  172. mostlyright/data_harness/sources/catalog/__init__.py +17 -0
  173. mostlyright/data_harness/sources/catalog/admission.py +477 -0
  174. mostlyright/data_harness/sources/catalog/authoring.py +1701 -0
  175. mostlyright/data_harness/sources/catalog/authoring_policy.py +701 -0
  176. mostlyright/data_harness/sources/catalog/authoring_shards.py +1217 -0
  177. mostlyright/data_harness/sources/catalog/bounded_io.py +231 -0
  178. mostlyright/data_harness/sources/catalog/channel.py +523 -0
  179. mostlyright/data_harness/sources/catalog/channel_client.py +296 -0
  180. mostlyright/data_harness/sources/catalog/contracts.py +825 -0
  181. mostlyright/data_harness/sources/catalog/coverage.py +137 -0
  182. mostlyright/data_harness/sources/catalog/delta.py +1340 -0
  183. mostlyright/data_harness/sources/catalog/embedding.py +532 -0
  184. mostlyright/data_harness/sources/catalog/entry_v2.py +1182 -0
  185. mostlyright/data_harness/sources/catalog/fill.py +3889 -0
  186. mostlyright/data_harness/sources/catalog/fill_partitions.py +459 -0
  187. mostlyright/data_harness/sources/catalog/fill_staging.py +1105 -0
  188. mostlyright/data_harness/sources/catalog/gating.py +374 -0
  189. mostlyright/data_harness/sources/catalog/generation_receipt.py +1607 -0
  190. mostlyright/data_harness/sources/catalog/harvest/__init__.py +7 -0
  191. mostlyright/data_harness/sources/catalog/harvest/ckan.py +384 -0
  192. mostlyright/data_harness/sources/catalog/harvest/datagov_v4.py +798 -0
  193. mostlyright/data_harness/sources/catalog/harvest/protocol.py +964 -0
  194. mostlyright/data_harness/sources/catalog/harvest/sdmx.py +445 -0
  195. mostlyright/data_harness/sources/catalog/harvest/stac.py +384 -0
  196. mostlyright/data_harness/sources/catalog/health.py +447 -0
  197. mostlyright/data_harness/sources/catalog/hosted_catalog.py +105 -0
  198. mostlyright/data_harness/sources/catalog/identity_history.py +1549 -0
  199. mostlyright/data_harness/sources/catalog/neural.py +1618 -0
  200. mostlyright/data_harness/sources/catalog/packed_catalog.py +2345 -0
  201. mostlyright/data_harness/sources/catalog/packed_retrieval.py +1517 -0
  202. mostlyright/data_harness/sources/catalog/packed_writer.py +2802 -0
  203. mostlyright/data_harness/sources/catalog/query_trace.py +1037 -0
  204. mostlyright/data_harness/sources/catalog/recommend.py +171 -0
  205. mostlyright/data_harness/sources/catalog/retrieval.py +230 -0
  206. mostlyright/data_harness/sources/catalog/retrieval_manifest.py +995 -0
  207. mostlyright/data_harness/sources/catalog/rights_decisions.py +254 -0
  208. mostlyright/data_harness/sources/catalog/sealed.py +560 -0
  209. mostlyright/data_harness/sources/catalog/search.py +230 -0
  210. mostlyright/data_harness/sources/catalog/streaming_delta.py +1097 -0
  211. mostlyright/data_harness/sources/catalog/update.py +891 -0
  212. mostlyright/data_harness/sources/collections.py +815 -0
  213. mostlyright/data_harness/sources/contracts.py +2223 -0
  214. mostlyright/data_harness/sources/deletion.py +761 -0
  215. mostlyright/data_harness/sources/fitness.py +162 -0
  216. mostlyright/data_harness/sources/governance.py +163 -0
  217. mostlyright/data_harness/sources/hosted.py +173 -0
  218. mostlyright/data_harness/sources/integration.py +218 -0
  219. mostlyright/data_harness/sources/range_reader.py +418 -0
  220. mostlyright/data_harness/sources/registry.py +514 -0
  221. mostlyright/data_harness/sources/rights_rule.py +59 -0
  222. mostlyright/data_harness/sources/source_cadence_vectors.v1.json +1 -0
  223. mostlyright/data_harness/sources/sports.py +521 -0
  224. mostlyright/data_harness/sources/stream.py +524 -0
  225. mostlyright/data_harness/sources/stream_connector.py +418 -0
  226. mostlyright/data_harness/sources/stream_recorder.py +1404 -0
  227. mostlyright/data_harness/studio_boundary.py +2019 -0
  228. mostlyright/data_harness/thin/__init__.py +37 -0
  229. mostlyright/data_harness/thin/acquire.py +1137 -0
  230. mostlyright/data_harness/thin/acquire_cancel.py +579 -0
  231. mostlyright/data_harness/thin/approvals.py +617 -0
  232. mostlyright/data_harness/thin/commands.py +406 -0
  233. mostlyright/data_harness/thin/download.py +194 -0
  234. mostlyright/data_harness/thin/narrative.py +589 -0
  235. mostlyright/data_harness/thin/parity.py +1070 -0
  236. mostlyright/data_harness/thin/propose.py +2759 -0
  237. mostlyright/data_harness/thin/research.py +1663 -0
  238. mostlyright/data_harness/thin/router.py +924 -0
  239. mostlyright/data_harness/thin/runs.py +519 -0
  240. mostlyright/data_harness/thin/session.py +281 -0
  241. mostlyright/data_harness/thin/stream.py +501 -0
  242. mostlyright/data_harness/thin/transport.py +187 -0
  243. mostlyright/data_harness/thin/vocabulary.py +368 -0
  244. mostlyright/data_harness/thin/workers.py +164 -0
  245. mostlyright/data_harness/ucum/TABLE-PIN.json +40 -0
  246. mostlyright/data_harness/ucum/ucum-subset.v1.json +632 -0
  247. mostlyright/data_harness/unit_flow.py +927 -0
  248. mostlyright/data_harness/units.py +572 -0
  249. mostlyright/data_harness/ux/__init__.py +9 -0
  250. mostlyright/data_harness/ux/approve.py +485 -0
  251. mostlyright/data_harness/ux/author_yaml.py +597 -0
  252. mostlyright/data_harness/ux/cloud_auth.py +447 -0
  253. mostlyright/data_harness/ux/commands/__init__.py +260 -0
  254. mostlyright/data_harness/ux/commands/approve.py +136 -0
  255. mostlyright/data_harness/ux/commands/auth.py +744 -0
  256. mostlyright/data_harness/ux/commands/author.py +79 -0
  257. mostlyright/data_harness/ux/commands/catalog_author.py +403 -0
  258. mostlyright/data_harness/ux/commands/catalog_fill.py +523 -0
  259. mostlyright/data_harness/ux/commands/catalog_harvest.py +545 -0
  260. mostlyright/data_harness/ux/commands/catalog_publish.py +1838 -0
  261. mostlyright/data_harness/ux/commands/catalog_search.py +71 -0
  262. mostlyright/data_harness/ux/commands/catalog_update.py +437 -0
  263. mostlyright/data_harness/ux/commands/deploy.py +134 -0
  264. mostlyright/data_harness/ux/commands/deploy_dataset.py +98 -0
  265. mostlyright/data_harness/ux/commands/deploy_plan.py +105 -0
  266. mostlyright/data_harness/ux/commands/deploy_status.py +104 -0
  267. mostlyright/data_harness/ux/commands/diff.py +74 -0
  268. mostlyright/data_harness/ux/commands/index.py +84 -0
  269. mostlyright/data_harness/ux/commands/inventory.py +47 -0
  270. mostlyright/data_harness/ux/commands/list_builds.py +143 -0
  271. mostlyright/data_harness/ux/commands/login.py +63 -0
  272. mostlyright/data_harness/ux/commands/peek.py +236 -0
  273. mostlyright/data_harness/ux/commands/plan_check.py +90 -0
  274. mostlyright/data_harness/ux/commands/preflight.py +97 -0
  275. mostlyright/data_harness/ux/commands/record.py +107 -0
  276. mostlyright/data_harness/ux/commands/review_setup.py +47 -0
  277. mostlyright/data_harness/ux/commands/search.py +440 -0
  278. mostlyright/data_harness/ux/commands/show.py +61 -0
  279. mostlyright/data_harness/ux/commands/whoami.py +37 -0
  280. mostlyright/data_harness/ux/credential_native.py +551 -0
  281. mostlyright/data_harness/ux/credential_store.py +1055 -0
  282. mostlyright/data_harness/ux/credentials.py +631 -0
  283. mostlyright/data_harness/ux/diffing.py +444 -0
  284. mostlyright/data_harness/ux/headline.py +671 -0
  285. mostlyright/data_harness/ux/hosted_acquisition.py +974 -0
  286. mostlyright/data_harness/ux/hosted_run_status.py +619 -0
  287. mostlyright/data_harness/ux/inventory.py +427 -0
  288. mostlyright/data_harness/ux/local_review.py +375 -0
  289. mostlyright/data_harness/ux/login.py +691 -0
  290. mostlyright/data_harness/ux/path_kind.py +147 -0
  291. mostlyright/data_harness/ux/peek.py +1000 -0
  292. mostlyright/data_harness/ux/plain_file.py +178 -0
  293. mostlyright/data_harness/ux/plan_check.py +311 -0
  294. mostlyright/data_harness/ux/preflight.py +918 -0
  295. mostlyright/data_harness/ux/readers.py +1124 -0
  296. mostlyright/data_harness/ux/remediation.py +2195 -0
  297. mostlyright/data_harness/ux/render.py +657 -0
  298. mostlyright/data_harness/ux/workload.py +1077 -0
  299. mostlyright/data_harness/viewer.py +3713 -0
  300. mostlyright/data_harness/visual_run/__init__.py +83 -0
  301. mostlyright/data_harness/visual_run/authoring.py +235 -0
  302. mostlyright/data_harness/visual_run/contracts.py +673 -0
  303. mostlyright/data_harness/visual_run/materialize.py +486 -0
  304. mostlyright/data_harness/visual_run/observations.py +874 -0
  305. mostlyright/data_harness/visual_run/query.py +259 -0
  306. mostlyright/data_harness/visual_run/reducer.py +280 -0
  307. mostlyright/data_harness/visual_run/sdk.py +892 -0
  308. mostlyright/data_harness/visual_run/store.py +584 -0
  309. mostlyright/data_harness/visual_run/transport.py +239 -0
  310. mostlyright/data_harness/watch.py +2999 -0
  311. mostlyright_data-0.9.0.dist-info/METADATA +607 -0
  312. mostlyright_data-0.9.0.dist-info/RECORD +314 -0
  313. mostlyright_data-0.9.0.dist-info/WHEEL +4 -0
  314. mostlyright_data-0.9.0.dist-info/entry_points.txt +12 -0
@@ -0,0 +1,171 @@
1
+ """The agent-facing call: a question in, ranked and gated source recommendations out.
2
+
3
+ This is the composition root, and the only module in the package that names both retrieval and
4
+ gating. What crosses the boundary is ``tuple[str, ...]``. No number reaches this module from
5
+ ``rank_entry_ids``, and none
6
+ could be handed onward to ``gate_entries`` even if one did, because neither ``CatalogEntry`` nor
7
+ ``CatalogRequirement`` has a field able to hold one. A composition root reaching both sides is
8
+ what a composition root is for; ``gating`` cannot reach ``retrieval``.
9
+
10
+ The order is a trust boundary and must not be rearranged: retrieval narrows the field, the facts
11
+ gate decides. The candidate list is never filtered by anything derived from retrieval except
12
+ ``limit``, which truncates.
13
+
14
+ ``to_dict`` is canonical-JSON-safe on purpose. One set of facts should be able to drive both a
15
+ sentence a person reads and a machine-readable payload, without a second code path inventing a
16
+ slightly different answer.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import re
22
+ from dataclasses import dataclass
23
+ from typing import Any
24
+
25
+ from mostlyright.data_harness.canonical import canonical_sha256
26
+ from mostlyright.data_harness.sources.catalog.gating import (
27
+ CATALOG_DISPOSITIONS,
28
+ CatalogGateVerdict,
29
+ CatalogRequirement,
30
+ gate_entries,
31
+ )
32
+ from mostlyright.data_harness.sources.catalog.retrieval import rank_entry_ids
33
+ from mostlyright.data_harness.sources.catalog.sealed import SealedEmbeddingIndex
34
+ from mostlyright.data_harness.sources.contracts import SourceContractError, _CanonicalContract
35
+
36
+ RECOMMENDATION_CONTRACT_VERSION = "harness-source-recommendation.v1"
37
+
38
+ # The shape the refusal below names. Checking the length alone made the message a description of
39
+ # a check that was not being run, and every other digest field in this package is checked here.
40
+ _SHA256 = re.compile(r"^[0-9a-f]{64}$")
41
+
42
+ # Which field on this contract answers for which verdict disposition. Written once and read by
43
+ # both the check below and `recommend_sources`, so the two can never sort into different buckets.
44
+ # `::test_every_gate_disposition_has_a_field_that_answers_for_it` fails if `gating` grows a fourth
45
+ # disposition this table does not name.
46
+ RECOMMENDATION_BUCKETS = (
47
+ ("admitted", "admitted"),
48
+ ("escalated", "human_escalation_required"),
49
+ ("refused", "refused"),
50
+ )
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class SourceRecommendation(_CanonicalContract):
55
+ """What the catalog can say about one question, with every decision cited.
56
+
57
+ ``question_digest`` binds the answer to the exact question without putting a stranger's free
58
+ text into a digest chain. ``requirement_digest`` binds it to what was asked *for*, which is the
59
+ other half of the input and the only thing that distinguishes two answers about the same
60
+ question. It is carried here as well as on each verdict because a question that retrieves
61
+ nothing is a legitimate answer with no verdicts in it, and that answer would otherwise cite no
62
+ requirement at all.
63
+ """
64
+
65
+ question_digest: str
66
+ requirement_digest: str
67
+ candidates: tuple[CatalogGateVerdict, ...]
68
+ admitted: tuple[str, ...]
69
+ escalated: tuple[str, ...]
70
+ refused: tuple[str, ...]
71
+ schema_version: str = RECOMMENDATION_CONTRACT_VERSION
72
+
73
+ def __post_init__(self) -> None:
74
+ if self.schema_version != RECOMMENDATION_CONTRACT_VERSION:
75
+ raise SourceContractError(
76
+ "VERSION",
77
+ "recommendation.schema_version",
78
+ f"must equal {RECOMMENDATION_CONTRACT_VERSION!r}",
79
+ )
80
+ for name in ("question_digest", "requirement_digest"):
81
+ value = getattr(self, name)
82
+ if not isinstance(value, str) or not _SHA256.fullmatch(value):
83
+ raise SourceContractError(
84
+ "SHA256",
85
+ f"recommendation.{name}",
86
+ "must be a lowercase 64-character SHA-256",
87
+ )
88
+ for name in ("candidates", "admitted", "escalated", "refused"):
89
+ if not isinstance(getattr(self, name), tuple):
90
+ raise SourceContractError(
91
+ "TYPE",
92
+ f"recommendation.{name}",
93
+ "must be an immutable tuple",
94
+ )
95
+ for position, verdict in enumerate(self.candidates):
96
+ if not isinstance(verdict, CatalogGateVerdict):
97
+ raise SourceContractError(
98
+ "TYPE",
99
+ f"recommendation.candidates[{position}]",
100
+ "must be a CatalogGateVerdict",
101
+ )
102
+ # One answer, one requirement. A candidate decided against a different requirement
103
+ # from the one this answer cites would make the citation a decoration.
104
+ if verdict.requirement_digest != self.requirement_digest:
105
+ raise SourceContractError(
106
+ "REQUIREMENT_BINDING",
107
+ f"recommendation.candidates[{position}].requirement_digest",
108
+ "a candidate was decided against a different requirement from the one this "
109
+ "recommendation cites",
110
+ )
111
+ # The three lists must be the verdicts *sorted into their own dispositions*, not merely a
112
+ # partition of the same coordinates. Checking only that the union matches let a
113
+ # recommendation name a source under `refused` whose verdict said `admitted` -- two
114
+ # conflicting answers about one source in the same payload, with nothing raised.
115
+ expected: dict[str, list[str]] = {name: [] for name in CATALOG_DISPOSITIONS}
116
+ for verdict in self.candidates:
117
+ expected[verdict.disposition].append(verdict.entry_coordinate)
118
+ for name, disposition in RECOMMENDATION_BUCKETS:
119
+ if sorted(getattr(self, name)) != sorted(expected[disposition]):
120
+ raise SourceContractError(
121
+ "RECOMMENDATION_PARTITION",
122
+ f"recommendation.{name}",
123
+ f"must name exactly the candidates whose verdict is {disposition!r}, each "
124
+ "once; the three lists are the candidates sorted by their own disposition, "
125
+ "not an independently supplied partition of them",
126
+ )
127
+
128
+ def to_dict(self) -> dict[str, Any]:
129
+ return {
130
+ "schema_version": self.schema_version,
131
+ "question_digest": self.question_digest,
132
+ "requirement_digest": self.requirement_digest,
133
+ "candidates": [item.to_dict() for item in self.candidates],
134
+ "admitted": list(self.admitted),
135
+ "escalated": list(self.escalated),
136
+ "refused": list(self.refused),
137
+ }
138
+
139
+
140
+ def recommend_sources(
141
+ *,
142
+ index: SealedEmbeddingIndex,
143
+ question: str,
144
+ requirement: CatalogRequirement,
145
+ limit: int,
146
+ ) -> SourceRecommendation:
147
+ """Retrieve candidates for this question, then decide each one on facts.
148
+
149
+ A question that retrieves nothing yields an empty recommendation rather than an error: "the
150
+ catalog holds nothing like this" is an answer, and an exception would make a caller unable to
151
+ tell it from a failure.
152
+ """
153
+
154
+ if not isinstance(index, SealedEmbeddingIndex):
155
+ raise SourceContractError("TYPE", "index", "must be a SealedEmbeddingIndex")
156
+ entry_ids = rank_entry_ids(index, question, limit=limit)
157
+ by_id = {entry.entry_id: entry for entry in index.entries}
158
+ entries = tuple(by_id[entry_id] for entry_id in entry_ids if entry_id in by_id)
159
+ verdicts = gate_entries(entries, requirement=requirement)
160
+ # Sorted through the same table the contract checks against, so the builder and the check
161
+ # cannot drift into disagreeing about which field answers for which disposition.
162
+ sorted_into = {
163
+ field: tuple(item.entry_coordinate for item in verdicts if item.disposition == disposition)
164
+ for field, disposition in RECOMMENDATION_BUCKETS
165
+ }
166
+ return SourceRecommendation(
167
+ question_digest=canonical_sha256({"question": question}),
168
+ requirement_digest=requirement.digest,
169
+ candidates=verdicts,
170
+ **sorted_into,
171
+ )
@@ -0,0 +1,230 @@
1
+ """Retrieval: find candidate sources. Decide nothing.
2
+
3
+ This module ranks catalog entries against a question and returns their identifiers. It returns a
4
+ ``tuple[str, ...]`` and nothing else, ever. The numbers it computes to produce that order are
5
+ module-private and die at the return statement, so no caller can consult one and no future edit
6
+ can accidentally route one into a decision.
7
+
8
+ That is a deliberate design choice over the obvious alternative. A ``RankedCandidate`` dataclass
9
+ carrying an unread score field would behave identically today and would quietly break the trust
10
+ boundary: the score would be *available* to the gate, and the separation would rest on
11
+ nobody ever reading it. Making the number impossible to pass is cheaper than policing who reads it.
12
+
13
+ There is no threshold parameter here, and there must never be one. A threshold that admits or
14
+ rejects is an admission decision made by similarity, which is exactly what the facts gate exists
15
+ to prevent. ``limit`` truncates a list; it cannot admit anything.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from mostlyright.data_harness.sources.catalog.contracts import EMBEDDING_LAYERS
21
+ from mostlyright.data_harness.sources.catalog.retrieval_manifest import (
22
+ LoadedRetrievalCatalog,
23
+ LoadedRetrievalRepresentation,
24
+ )
25
+ from mostlyright.data_harness.sources.catalog.sealed import SealedEmbeddingIndex
26
+ from mostlyright.data_harness.sources.contracts import SourceContractError
27
+
28
+ MAX_QUERY_CHARS = 4_096
29
+ MAX_RETRIEVAL_LIMIT = 100
30
+ RETRIEVAL_BACKENDS = frozenset({"lexical", "neural"})
31
+
32
+
33
+ def rank_entry_ids(index: SealedEmbeddingIndex, question: str, *, limit: int) -> tuple[str, ...]:
34
+ """Return at most ``limit`` catalog entry ids, best match first.
35
+
36
+ Ties break on ``entry_id`` ascending, so the order is total and reproducible -- an eval that
37
+ depends on a coin flip is an eval that flakes. A question with no lexical signal returns an
38
+ empty tuple rather than an arbitrary alphabetical list, so "no signal" stays visibly different
39
+ from "weak signal".
40
+ """
41
+
42
+ if not isinstance(index, SealedEmbeddingIndex):
43
+ raise SourceContractError("TYPE", "index", "must be a SealedEmbeddingIndex")
44
+ if type(limit) is not int or not 1 <= limit <= MAX_RETRIEVAL_LIMIT:
45
+ raise SourceContractError(
46
+ "RETRIEVAL_LIMIT",
47
+ "limit",
48
+ f"must be an integer in [1, {MAX_RETRIEVAL_LIMIT}]",
49
+ )
50
+ if not isinstance(question, str):
51
+ raise SourceContractError("TYPE", "question", "must be a string")
52
+ if len(question) > MAX_QUERY_CHARS:
53
+ raise SourceContractError(
54
+ "RETRIEVAL_QUERY_LIMIT",
55
+ "question",
56
+ f"a question may not exceed {MAX_QUERY_CHARS} characters",
57
+ )
58
+
59
+ # `SealedEmbeddingIndex` cannot hold a backend that is unsafe to run in-process: it refuses one
60
+ # at construction, not only on the `load` path. That is why the isinstance check above is
61
+ # sufficient here and this module needs no policy of its own -- the precondition is a property
62
+ # of the argument's type rather than a claim about where the caller got it from.
63
+ asked = index.backend.encode(question)
64
+ if not any(asked):
65
+ return ()
66
+
67
+ ordered: list[tuple[int, str]] = []
68
+ for entry in index.entries:
69
+ best = _best_layer_score(index, entry.entry_id, asked)
70
+ if best <= 0:
71
+ continue
72
+ ordered.append((-best, entry.entry_id))
73
+ ordered.sort()
74
+ return tuple(entry_id for _negated, entry_id in ordered[:limit])
75
+
76
+
77
+ def rank_retrieval_entry_ids(
78
+ catalog: LoadedRetrievalCatalog,
79
+ question: str,
80
+ *,
81
+ backend: str,
82
+ allow_lexical_fallback: bool,
83
+ limit: int,
84
+ ) -> tuple[str, ...]:
85
+ """Order verified v2 entry identities with one explicit representation choice.
86
+
87
+ Selecting ``neural`` never discovers a model. The caller must already have supplied an exact
88
+ backend to ``load_retrieval_catalog``. When it did not, fallback occurs only under the explicit
89
+ boolean; the v2 trace carries the loader's typed reason. Numerical ranking data stays here.
90
+ """
91
+
92
+ return _rank_retrieval(
93
+ catalog,
94
+ question,
95
+ backend=backend,
96
+ allow_lexical_fallback=allow_lexical_fallback,
97
+ limit=limit,
98
+ )[0]
99
+
100
+
101
+ def _rank_retrieval(
102
+ catalog: LoadedRetrievalCatalog,
103
+ question: str,
104
+ *,
105
+ backend: str,
106
+ allow_lexical_fallback: bool,
107
+ limit: int,
108
+ ) -> tuple[tuple[str, ...], str, str, str | None]:
109
+ if not isinstance(catalog, LoadedRetrievalCatalog):
110
+ raise SourceContractError(
111
+ "TYPE", "catalog", "must be a fully verified LoadedRetrievalCatalog"
112
+ )
113
+ if not isinstance(backend, str) or backend not in RETRIEVAL_BACKENDS:
114
+ raise SourceContractError(
115
+ "RETRIEVAL_BACKEND", "backend", "must be either lexical or neural"
116
+ )
117
+ if type(allow_lexical_fallback) is not bool:
118
+ raise SourceContractError("TYPE", "allow_lexical_fallback", "must be a boolean")
119
+ _validate_request(question, limit)
120
+
121
+ selected = catalog.lexical
122
+ representation = "lexical"
123
+ fallback_reason: str | None = None
124
+ if backend == "neural":
125
+ if catalog.neural is not None:
126
+ selected = catalog.neural
127
+ representation = "neural"
128
+ elif not allow_lexical_fallback:
129
+ raise SourceContractError(
130
+ "NEURAL_RETRIEVAL_UNAVAILABLE",
131
+ "backend",
132
+ catalog.fallback_reason or "the verified catalog has no neural representation",
133
+ )
134
+ else:
135
+ fallback_reason = catalog.fallback_reason or "NEURAL_RUNTIME_UNAVAILABLE"
136
+
137
+ entry_ids = _rank_loaded_entry_ids(selected, question, limit=limit)
138
+ return (
139
+ entry_ids,
140
+ representation,
141
+ selected.backend.descriptor.coordinate,
142
+ fallback_reason,
143
+ )
144
+
145
+
146
+ def _validate_request(question: str, limit: int) -> None:
147
+ if type(limit) is not int or not 1 <= limit <= MAX_RETRIEVAL_LIMIT:
148
+ raise SourceContractError(
149
+ "RETRIEVAL_LIMIT",
150
+ "limit",
151
+ f"must be an integer in [1, {MAX_RETRIEVAL_LIMIT}]",
152
+ )
153
+ if not isinstance(question, str):
154
+ raise SourceContractError("TYPE", "question", "must be a string")
155
+ if len(question) > MAX_QUERY_CHARS:
156
+ raise SourceContractError(
157
+ "RETRIEVAL_QUERY_LIMIT",
158
+ "question",
159
+ f"a question may not exceed {MAX_QUERY_CHARS} characters",
160
+ )
161
+
162
+
163
+ def _rank_loaded_entry_ids(
164
+ representation: LoadedRetrievalRepresentation,
165
+ question: str,
166
+ *,
167
+ limit: int,
168
+ ) -> tuple[str, ...]:
169
+ if not any(character.isalnum() for character in question):
170
+ return ()
171
+ asked = representation.backend.encode(question)
172
+ dimensions = representation.backend.descriptor.dimensions
173
+ if (
174
+ not isinstance(asked, tuple)
175
+ or len(asked) != dimensions
176
+ or any(type(value) is not int for value in asked)
177
+ ):
178
+ raise SourceContractError(
179
+ "RETRIEVAL_QUERY_VECTOR",
180
+ "question",
181
+ "the selected backend returned a vector outside its exact integer coordinate",
182
+ )
183
+ if not any(asked):
184
+ return ()
185
+
186
+ ordered: list[tuple[int, str]] = []
187
+ for entry in representation.entries:
188
+ best = _best_loaded_layer_score(representation, entry.entry_id, asked)
189
+ if best > 0:
190
+ ordered.append((-best, entry.entry_id))
191
+ ordered.sort()
192
+ return tuple(entry_id for _negated, entry_id in ordered[:limit])
193
+
194
+
195
+ def _best_layer_score(index: SealedEmbeddingIndex, entry_id: str, asked: tuple[int, ...]) -> int:
196
+ """The strongest of this entry's four layers against the question. Private on purpose.
197
+
198
+ An entry that matches only on its published column names still ranks by that layer, which is
199
+ what makes the four-layer split worth having.
200
+ """
201
+
202
+ best = 0
203
+ for layer in EMBEDDING_LAYERS:
204
+ sealed = index.vectors.get((entry_id, layer))
205
+ if sealed is None:
206
+ continue
207
+ total = 0
208
+ for left, right in zip(asked, sealed, strict=True):
209
+ total += left * right
210
+ best = max(best, total)
211
+ return best
212
+
213
+
214
+ def _best_loaded_layer_score(
215
+ representation: LoadedRetrievalRepresentation,
216
+ entry_id: str,
217
+ asked: tuple[int, ...],
218
+ ) -> int:
219
+ """Return one private v2 ranking number; callers can receive only identities."""
220
+
221
+ best = 0
222
+ for layer in EMBEDDING_LAYERS:
223
+ sealed = representation.vectors.get((entry_id, layer))
224
+ if sealed is None:
225
+ continue
226
+ total = 0
227
+ for left, right in zip(asked, sealed, strict=True):
228
+ total += left * right
229
+ best = max(best, total)
230
+ return best