mostlyright-data 0.31.11__tar.gz → 0.31.13__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.
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/PKG-INFO +8 -1
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/README.md +7 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/preparing-table-search.md +47 -3
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/router.py +2 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/runs.py +2 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/session.py +3 -1
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/stream.py +2 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/table_search_contract.json +44 -1
- mostlyright_data-0.31.13/src/mostlyright/data_harness/thin/user_agent.py +234 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4.py +2 -2
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_public.py +3 -1
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_query.py +2 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_runs.py +10 -1
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_search_contract.py +54 -3
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/attendance.py +11 -6
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/cloud_auth.py +2 -2
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/commands/clarify.py +10 -5
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/login.py +7 -2
- mostlyright_data-0.31.11/src/mostlyright/data_harness/thin/user_agent.py +0 -76
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/.gitignore +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/pyproject.toml +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/scripts/hatch_build.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/SKILL.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/agents/openai.yaml +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/1-open-the-page-and-the-link-to-it-in-the-first-message.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/2-brief-two-to-four-questions-each-with-a-recommended-answer.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/3-probe-read-a-source-before-committing-to-it.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/4-decide-say-what-you-chose-what-you-refused-and-ask-one-question.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/5-draft-one-recipe-document-one-call.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/6-build-one-run-sized-to-acquire-every-measured-source-whole.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/7-interrogate-ask-the-run-what-it-actually-delivered.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/8-fix-revise-the-document-and-register-it-again.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/9-present-only-what-survived-inspection-with-caveats.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/agent-protocol.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/autonomous-delivery.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/before-the-first-tool-call.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/boundaries.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/cloud-authentication-preflight.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/commands.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/cross-repository-protocol-reference.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/enriching-with-small-models.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/installation-parity.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/live-run.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/making-a-table-incremental.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/narrating-the-run.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/not-hosted-yet.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/one-install.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/patterns.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/prediction-labels.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/promote.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/readers.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/receipts.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/recipe-test.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/recording-a-stream-venue.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/recovering-an-import-failure.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/reference-pages.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/required-protocol.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/source-credentials.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/sources.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/the-one-thing-to-say-about-the-skill-itself.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/transforms.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/user-communication-contract.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/references/writing-a-decision-record.md +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/skills/mr-data-build/scripts/write_research_notebook.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/__init__.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/agent_protocol.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/canonical.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/formats.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/key_seam.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/page_coverage.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/part_check_evidence.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/session_probes.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/skill_assets.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/table_manifest.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/__init__.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/acquire.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/activity.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/approvals.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/backoff.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/categories.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/commands.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/dataset-categories-v1.json +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/delivery.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/description_shape.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/download.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/enrichment.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/fast_features.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/matching_benchmark.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/narrative.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/parity.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/probe.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/progress_rate.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/progress_vocabulary.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/propose.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/recipe.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/recipe_brief.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/recipe_explain.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/recipe_forecast.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/recipe_lint.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/recipe_test.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/refresh_rehearsal.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/release_check.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/research.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/run_diagnose.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/run_prefix.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/stream_venue.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/studio_capabilities.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/support.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/table_view.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/transport.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_artifacts.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_catalog.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_connected.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_connections.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_dataset_covers.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_datasets.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_handoff.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_matching.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_narrative.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_reader.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_search.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_secrets.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_stream.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_tables.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/vocabulary.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/__init__.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/clarification.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/credential_store.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/credentials.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/remediation.py +0 -0
- {mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/ux/render.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mostlyright-data
|
|
3
|
-
Version: 0.31.
|
|
3
|
+
Version: 0.31.13
|
|
4
4
|
Summary: Mostly Right hosted CLI for reviewed datasets
|
|
5
5
|
Project-URL: Homepage, https://mostlyright.md/
|
|
6
6
|
Project-URL: Documentation, https://mostlyright.md/docs/guides/cli/
|
|
@@ -46,6 +46,13 @@ Once a day, after a command has printed its answer, `mr-data` asks the Python pa
|
|
|
46
46
|
a newer release is published and says so in one line on stderr. `MOSTLYRIGHT_RELEASE_CHECK=0`
|
|
47
47
|
switches that off.
|
|
48
48
|
|
|
49
|
+
Every call to Mostly Right names the coding agent running `mr-data` in `X-Mostlyright-Agent`
|
|
50
|
+
(`claude-code`, `codex`, `gemini-cli`, `cursor`, `ci` or `none`, read from the variables those
|
|
51
|
+
hosts set), and a program that imports this package sends it too. An `mr-data` command also sends
|
|
52
|
+
one random id in `X-Mostlyright-Invocation`, so the requests of one command can be grouped; a
|
|
53
|
+
library call sends none. Neither is sent to storage hosts. `MOSTLYRIGHT_NO_AGENT_HEADERS=1`
|
|
54
|
+
switches both off.
|
|
55
|
+
|
|
49
56
|
## Sign in
|
|
50
57
|
|
|
51
58
|
```bash
|
|
@@ -31,6 +31,13 @@ Once a day, after a command has printed its answer, `mr-data` asks the Python pa
|
|
|
31
31
|
a newer release is published and says so in one line on stderr. `MOSTLYRIGHT_RELEASE_CHECK=0`
|
|
32
32
|
switches that off.
|
|
33
33
|
|
|
34
|
+
Every call to Mostly Right names the coding agent running `mr-data` in `X-Mostlyright-Agent`
|
|
35
|
+
(`claude-code`, `codex`, `gemini-cli`, `cursor`, `ci` or `none`, read from the variables those
|
|
36
|
+
hosts set), and a program that imports this package sends it too. An `mr-data` command also sends
|
|
37
|
+
one random id in `X-Mostlyright-Invocation`, so the requests of one command can be grouped; a
|
|
38
|
+
library call sends none. Neither is sent to storage hosts. `MOSTLYRIGHT_NO_AGENT_HEADERS=1`
|
|
39
|
+
switches both off.
|
|
40
|
+
|
|
34
41
|
## Sign in
|
|
35
42
|
|
|
36
43
|
```bash
|
|
@@ -194,7 +194,46 @@ column names, the descriptions and the planner. Never bind a word that two colum
|
|
|
194
194
|
such as "growth", "change" or "best", unless the brief names the metric. "Highest growth"
|
|
195
195
|
pointed at a revenue column turns "highest growth last 30 days" into a revenue sort.
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
### Declare a floor for every rate
|
|
198
|
+
|
|
199
|
+
A growth, change, ratio or per-unit column ranks nonsense first when its base is tiny. Revenue
|
|
200
|
+
that grew from $5,000 to $80 million is 1,600,000% growth, and a film with three votes has a
|
|
201
|
+
perfect rating. Sorting by such a column shows shells and placeholders, not the rows the reader
|
|
202
|
+
asked for. For each sortable rate column, declare the column it is a rate of, and the smallest
|
|
203
|
+
base that makes the rate meaningful, in `order_floors` (contract 4.54.0, up to 32 entries):
|
|
204
|
+
|
|
205
|
+
```json
|
|
206
|
+
"order_floors": [
|
|
207
|
+
{"column": "revenue_growth_pct", "base_column": "revenue_prior_year_usd", "min": 10000000},
|
|
208
|
+
{"column": "revenue_cagr_5y_pct", "base_column": "revenue_5y_ago_usd", "min": 10000000},
|
|
209
|
+
{"column": "vote_average", "base_column": "vote_count", "min": 100}
|
|
210
|
+
]
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- `column` is in `sortable_columns` or is a sort alias's column. `base_column` is another numeric
|
|
214
|
+
column in `filterable_columns`. List each base early in `filterable_columns`: a package keeps 64
|
|
215
|
+
filters, role and alias columns first and the rest in spec order, and a base past that cut
|
|
216
|
+
loses its floor. `search validate TABLE --spec` names such a floor in `warnings` under
|
|
217
|
+
`/order_floors/`; treat that warning as a floor that will not apply.
|
|
218
|
+
- `min` is a whole number in the base column's own unit, written without a decimal point or
|
|
219
|
+
exponent: `10000000` on a column in dollars, `10` on a column in millions.
|
|
220
|
+
- Floors are declared on a search revision with `mr-data search configure --spec`, not in the
|
|
221
|
+
recipe's `table.search`, which refuses the member. A revision configured from the recipe's own
|
|
222
|
+
spec carries none, and a new version of a private table keeps them only when you configure the
|
|
223
|
+
revision again. A plan made without the planner, such as a keystroke search, applies no floor.
|
|
224
|
+
- A Studio older than contract 4.54.0 refuses the member as unknown. Configure without it there.
|
|
225
|
+
- When a plan orders by `column`, in either direction, Studio adds `base_column >= min` as a
|
|
226
|
+
filter. The reader sees it in the search summary and can remove it. A query that already filters
|
|
227
|
+
`base_column` keeps its own filter.
|
|
228
|
+
- Prefer the true base (the earlier period's revenue, the vote count) over a size proxy such as
|
|
229
|
+
market cap. When the table has no base column, add one in the transform: it is cheaper than a
|
|
230
|
+
wrong ranking.
|
|
231
|
+
- Choose `min` from the built table with `mr-data query`: the smallest base at which the rate stops
|
|
232
|
+
being dominated by placeholders. State the floor at the start of the base column's
|
|
233
|
+
description, since the planner reads only the first 160 characters of a description.
|
|
234
|
+
|
|
235
|
+
A recipe's spec takes 128 filter aliases and 64 sort aliases, and a search revision 512 and 256.
|
|
236
|
+
`search validate` refuses more.
|
|
198
237
|
Rank the phrases and keep the top of the list. Put first the words that name the kind of row, the
|
|
199
238
|
facets users type most, the popularity sorts and the product's own vocabulary. A phrase you leave
|
|
200
239
|
out maps to nothing. The planner may still recover it from the column names and values, and
|
|
@@ -225,8 +264,13 @@ roles. It never sees rows.
|
|
|
225
264
|
`planner_detail.reason`. `external_context_not_allowed` means the recipe withholds consent.
|
|
226
265
|
`private_source` means a source is not public.
|
|
227
266
|
|
|
228
|
-
Write each column description as if the planner reads it
|
|
229
|
-
|
|
267
|
+
Write each column description as if the planner reads it, because it does, and the page shows it
|
|
268
|
+
to the reader beside results ordered by that column. Say what the column holds, the period it
|
|
269
|
+
covers ("trailing twelve months", "latest fiscal year against the year before", "over the last
|
|
270
|
+
five fiscal years"), its unit, and the words people use for it, abbreviations included
|
|
271
|
+
("compound annual growth rate, CAGR"). The planner picks the column a query asks about, named
|
|
272
|
+
bare ("revenue") or in a question ("what is the P/E ratio?"), from these descriptions. It reads
|
|
273
|
+
the first 160 characters of each, so lead with what the column measures and its period.
|
|
230
274
|
|
|
231
275
|
### Name and describe columns for search
|
|
232
276
|
|
{mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/router.py
RENAMED
|
@@ -52,6 +52,7 @@ from collections.abc import Callable, Mapping, Sequence
|
|
|
52
52
|
from typing import Any
|
|
53
53
|
|
|
54
54
|
from mostlyright.data_harness import skill_assets
|
|
55
|
+
from mostlyright.data_harness.thin import user_agent
|
|
55
56
|
from mostlyright.data_harness.thin.transport import ThinLaneError
|
|
56
57
|
from mostlyright.data_harness.thin.vocabulary import (
|
|
57
58
|
CLASSIFICATION,
|
|
@@ -781,6 +782,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
781
782
|
# not write, it never raises, and `MOSTLYRIGHT_SKILL_AUTOINSTALL=0` turns it off. A skill that
|
|
782
783
|
# arrived only when somebody knew to ask for it helped exactly the people who did not need it.
|
|
783
784
|
skill_assets.ensure_installed()
|
|
785
|
+
user_agent.begin_invocation()
|
|
784
786
|
|
|
785
787
|
arguments = list(sys.argv[1:] if argv is None else argv)
|
|
786
788
|
hosted_requested = _hosted_flag_present(arguments)
|
{mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/runs.py
RENAMED
|
@@ -46,6 +46,7 @@ from mostlyright.data_harness.thin.transport import (
|
|
|
46
46
|
UrlLibTransport,
|
|
47
47
|
signed_transfer_url,
|
|
48
48
|
)
|
|
49
|
+
from mostlyright.data_harness.thin.user_agent import agent_headers
|
|
49
50
|
|
|
50
51
|
#: The contract version every payload below is written against. ``studio_boundary`` pins the
|
|
51
52
|
#: generated client to the same 3.0.0 and to one OpenAPI source digest; this constant is the thin
|
|
@@ -279,6 +280,7 @@ class StudioApiClient:
|
|
|
279
280
|
headers = {
|
|
280
281
|
"Accept": "application/json",
|
|
281
282
|
CLIENT_HEADER: CLIENT_NAME,
|
|
283
|
+
**agent_headers(),
|
|
282
284
|
}
|
|
283
285
|
if extra_headers:
|
|
284
286
|
headers.update(extra_headers)
|
{mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/session.py
RENAMED
|
@@ -42,6 +42,7 @@ from mostlyright.data_harness.thin.transport import (
|
|
|
42
42
|
UrlLibTransport,
|
|
43
43
|
service_origin,
|
|
44
44
|
)
|
|
45
|
+
from mostlyright.data_harness.thin.user_agent import agent_headers
|
|
45
46
|
from mostlyright.data_harness.ux.credentials import ResolvedCloudCredentials
|
|
46
47
|
|
|
47
48
|
#: Cloud's token-exchange route. Held identical to ``hosted_deploy.CLOUD_TOKEN_PATH`` by a test.
|
|
@@ -396,7 +397,7 @@ def open_studio_session(
|
|
|
396
397
|
status, raw, headers = selected.request(
|
|
397
398
|
"POST",
|
|
398
399
|
f"{cloud_url}{CLOUD_TOKEN_PATH}",
|
|
399
|
-
{"Accept": "application/json", "x-api-key": credentials.raw_key},
|
|
400
|
+
{"Accept": "application/json", "x-api-key": credentials.raw_key, **agent_headers()},
|
|
400
401
|
b"",
|
|
401
402
|
MAX_TOKEN_RESPONSE_BYTES,
|
|
402
403
|
)
|
|
@@ -953,6 +954,7 @@ class CloudClient:
|
|
|
953
954
|
headers = {
|
|
954
955
|
"Accept": "application/json",
|
|
955
956
|
"Authorization": f"Bearer {self._credentials.raw_key}",
|
|
957
|
+
**agent_headers(),
|
|
956
958
|
}
|
|
957
959
|
encoded: bytes | None = None
|
|
958
960
|
if body is not None:
|
{mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/stream.py
RENAMED
|
@@ -49,6 +49,7 @@ from mostlyright.data_harness.thin.session import (
|
|
|
49
49
|
stated_sentence,
|
|
50
50
|
)
|
|
51
51
|
from mostlyright.data_harness.thin.transport import ThinLaneError, Transport, UrlLibTransport
|
|
52
|
+
from mostlyright.data_harness.thin.user_agent import agent_headers
|
|
52
53
|
|
|
53
54
|
#: Upstream's frame names. The first two are the tick/proof partition; ``end`` is its
|
|
54
55
|
#: end-of-stream notice.
|
|
@@ -593,6 +594,7 @@ class RunEventStream:
|
|
|
593
594
|
headers = {
|
|
594
595
|
"Accept": "text/event-stream",
|
|
595
596
|
CLIENT_HEADER: CLIENT_NAME,
|
|
597
|
+
**agent_headers(),
|
|
596
598
|
**session.authorization(),
|
|
597
599
|
}
|
|
598
600
|
# Resume in Studio's own cursor space when we have one. `from_seq` is only ever the
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "mostlyright-thin-table-search-contract.v1",
|
|
3
|
-
"source_commit": "
|
|
3
|
+
"source_commit": "5109da1f92eaaf4fc41ed35a03e5d010268d7c7b",
|
|
4
4
|
"recipe_search_definition": "recipe_search",
|
|
5
5
|
"$defs": {
|
|
6
|
+
"common.safe_integer": {
|
|
7
|
+
"description": "An integer exactly representable by both Python JSON integers and TypeScript numbers.",
|
|
8
|
+
"type": "integer",
|
|
9
|
+
"minimum": -9007199254740991,
|
|
10
|
+
"maximum": 9007199254740991
|
|
11
|
+
},
|
|
6
12
|
"evaluation_case": {
|
|
7
13
|
"type": "object",
|
|
8
14
|
"additionalProperties": false,
|
|
@@ -119,6 +125,14 @@
|
|
|
119
125
|
]
|
|
120
126
|
},
|
|
121
127
|
"description": "Keel's recipe reader does not admit long_text_columns yet (4.42.0): a search revision declares it."
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"not": {
|
|
131
|
+
"required": [
|
|
132
|
+
"order_floors"
|
|
133
|
+
]
|
|
134
|
+
},
|
|
135
|
+
"description": "The recipe readers do not admit order_floors (4.54.0): a search revision declares it."
|
|
122
136
|
}
|
|
123
137
|
]
|
|
124
138
|
},
|
|
@@ -252,6 +266,35 @@
|
|
|
252
266
|
"direction"
|
|
253
267
|
]
|
|
254
268
|
},
|
|
269
|
+
"order_floors": {
|
|
270
|
+
"description": "Order floors (4.54.0): for a sortable column whose order means little on rows with a tiny or missing base (a growth rate on almost no revenue, a rating from a handful of votes), the base column and the least value of it at which a row answers that order. When a plan orders by `column`, either direction, and states no predicate on `base_column`, the planner adds `base_column >= min` (origin `parser`), which the search node runs and reports as any predicate. `column` is declared sortable (in `sortable_columns` or a sort alias's, even where a v3 package sorts numbers by default), `base_column` is another declared numeric column in `filterable_columns`, and each `column` has at most one floor. `min` is an integer (a request body carries no fractional or exponent number), in the base column's own unit. Admitted on a search revision; a recipe's `table.search` may not name it yet.",
|
|
271
|
+
"type": "array",
|
|
272
|
+
"maxItems": 32,
|
|
273
|
+
"items": {
|
|
274
|
+
"type": "object",
|
|
275
|
+
"additionalProperties": false,
|
|
276
|
+
"properties": {
|
|
277
|
+
"column": {
|
|
278
|
+
"type": "string",
|
|
279
|
+
"minLength": 1,
|
|
280
|
+
"maxLength": 128
|
|
281
|
+
},
|
|
282
|
+
"base_column": {
|
|
283
|
+
"type": "string",
|
|
284
|
+
"minLength": 1,
|
|
285
|
+
"maxLength": 128
|
|
286
|
+
},
|
|
287
|
+
"min": {
|
|
288
|
+
"$ref": "#/$defs/common.safe_integer"
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
"required": [
|
|
292
|
+
"column",
|
|
293
|
+
"base_column",
|
|
294
|
+
"min"
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
},
|
|
255
298
|
"planner": {
|
|
256
299
|
"type": "object",
|
|
257
300
|
"additionalProperties": false,
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""The identity every first-party request to Mostly Right states.
|
|
2
|
+
|
|
3
|
+
Without it the standard library sends ``Python-urllib/3.x``, which is what every unattended
|
|
4
|
+
script on the internet sends: the CLI's own traffic could not be told apart from anything else in
|
|
5
|
+
a server log, so nothing downstream could measure it, rate-limit it separately, or exempt it.
|
|
6
|
+
|
|
7
|
+
The name follows the convention the two crawler lanes already use -- a product token, a version,
|
|
8
|
+
and the contact segment that says where to reach the people running it -- so an origin and our own
|
|
9
|
+
logs see one family of identities from this deployment rather than one per code path.
|
|
10
|
+
|
|
11
|
+
Two more headers ride beside it on first-party calls only (:func:`agent_headers`): which coding
|
|
12
|
+
agent, if any, is driving this process, and, under the ``mr-data`` command, one id shared by every
|
|
13
|
+
request that command makes. They go where ``X-Mostlyright-Client`` goes and on Cloud's own
|
|
14
|
+
routes, never through the transport, for the reason ``thin.runs.CLIENT_HEADER`` gives: the
|
|
15
|
+
transport also carries signed transfers to storage nobody here owns.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
import secrets
|
|
22
|
+
from collections.abc import Mapping
|
|
23
|
+
from functools import cache
|
|
24
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
25
|
+
from typing import NamedTuple
|
|
26
|
+
|
|
27
|
+
#: The contact segment ``hosted_worker.CRAWLER_CONTACT_SEGMENT`` states for the keyed-source
|
|
28
|
+
#: agents and ``hosted_crawler_fetch.PUBLIC_HTTPS_USER_AGENT`` carries for public sources, with
|
|
29
|
+
#: the measurement that makes it load-bearing recorded at the first of those.
|
|
30
|
+
#:
|
|
31
|
+
#: It is spelled again here rather than imported because both of those modules import ``pyarrow``
|
|
32
|
+
#: at module scope and this package installs with no compiled extension at all -- and they are
|
|
33
|
+
#: Studio's now, so importing them is not a choice this repository has. The spellings are held
|
|
34
|
+
#: against each other by ``tests/test_thin_user_agent.py`` under
|
|
35
|
+
#: ``make verify-cross-repo STUDIO_ROOT=...``, which reads the other one out of Studio's source.
|
|
36
|
+
CRAWLER_CONTACT_SEGMENT = "(+https://mostlyright.md/crawler; crawler@mostlyright.md)"
|
|
37
|
+
|
|
38
|
+
#: The distribution this client is installed as, and the product token it therefore reports.
|
|
39
|
+
DISTRIBUTION_NAME = "mostlyright-data"
|
|
40
|
+
|
|
41
|
+
#: The version reported from a source tree, where no distribution metadata exists to read. A
|
|
42
|
+
#: number rather than a word, so every value this header can carry parses the same way.
|
|
43
|
+
UNINSTALLED_VERSION = "0.0.0"
|
|
44
|
+
|
|
45
|
+
_USER_AGENT_HEADER = "User-Agent"
|
|
46
|
+
|
|
47
|
+
#: The header naming the agent driving this process, one of :data:`AGENTS`.
|
|
48
|
+
AGENT_HEADER = "X-Mostlyright-Agent"
|
|
49
|
+
|
|
50
|
+
#: The header carrying :func:`invocation_id`, sent only after :func:`begin_invocation`.
|
|
51
|
+
INVOCATION_HEADER = "X-Mostlyright-Invocation"
|
|
52
|
+
|
|
53
|
+
#: Set to anything but ``0`` or empty, and neither header above is sent.
|
|
54
|
+
NO_AGENT_HEADERS_ENV = "MOSTLYRIGHT_NO_AGENT_HEADERS"
|
|
55
|
+
|
|
56
|
+
AGENT_CLAUDE_CODE = "claude-code"
|
|
57
|
+
AGENT_CODEX = "codex"
|
|
58
|
+
AGENT_GEMINI_CLI = "gemini-cli"
|
|
59
|
+
AGENT_CURSOR = "cursor"
|
|
60
|
+
AGENT_CI = "ci"
|
|
61
|
+
AGENT_NONE = "none"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class AgentMarker(NamedTuple):
|
|
65
|
+
"""One variable an agent host exports into the commands it runs.
|
|
66
|
+
|
|
67
|
+
``conversation`` says whether the attendance check (``ux.attendance``) reads the variable as
|
|
68
|
+
a conversation existing. Only a marker every headless run of that host lacks may say so:
|
|
69
|
+
``CODEX_SANDBOX`` is set by ``codex exec`` on a cron job too, so it names Codex here and is
|
|
70
|
+
not read as a person being there.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
agent: str
|
|
74
|
+
variable: str
|
|
75
|
+
conversation: bool
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
#: Every marker, grouped by agent in precedence order.
|
|
79
|
+
#:
|
|
80
|
+
#: Presence of a non-empty value is the signal and the value is never read: these are the hosts'
|
|
81
|
+
#: variables, and their values are theirs to change. A coding agent outranks ``ci``, so
|
|
82
|
+
#: ``claude -p`` inside a GitHub Action reports ``claude-code``. Nothing that every terminal of an
|
|
83
|
+
#: editor exports is listed, because a person typing there would read as the agent.
|
|
84
|
+
AGENT_MARKERS: tuple[AgentMarker, ...] = (
|
|
85
|
+
AgentMarker(AGENT_CLAUDE_CODE, "CLAUDECODE", True),
|
|
86
|
+
AgentMarker(AGENT_CLAUDE_CODE, "CLAUDE_CODE_SESSION", True),
|
|
87
|
+
AgentMarker(AGENT_CODEX, "CODEX_SESSION", True),
|
|
88
|
+
AgentMarker(AGENT_CODEX, "CODEX_SANDBOX", False),
|
|
89
|
+
AgentMarker(AGENT_CODEX, "CODEX_SANDBOX_NETWORK_DISABLED", False),
|
|
90
|
+
AgentMarker(AGENT_GEMINI_CLI, "GEMINI_CLI", False),
|
|
91
|
+
AgentMarker(AGENT_CURSOR, "CURSOR_AGENT", False),
|
|
92
|
+
AgentMarker(AGENT_CI, "CI", False),
|
|
93
|
+
AgentMarker(AGENT_CI, "GITHUB_ACTIONS", False),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
#: Every value :data:`AGENT_HEADER` can carry.
|
|
97
|
+
AGENTS: tuple[str, ...] = (*dict.fromkeys(marker.agent for marker in AGENT_MARKERS), AGENT_NONE)
|
|
98
|
+
|
|
99
|
+
#: What :func:`invocation_id` returns: 16 random bytes as 32 lowercase hex digits.
|
|
100
|
+
INVOCATION_ID_PATTERN = r"^[0-9a-f]{32}$"
|
|
101
|
+
|
|
102
|
+
_invocation: str | None = None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@cache
|
|
106
|
+
def client_user_agent() -> str:
|
|
107
|
+
"""Return the ``User-Agent`` this client presents to Mostly Right.
|
|
108
|
+
|
|
109
|
+
Cached because the answer is a property of the installed environment, and reading distribution
|
|
110
|
+
metadata walks ``sys.path``; a command that makes twenty calls would otherwise pay for it
|
|
111
|
+
twenty times.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
installed = version(DISTRIBUTION_NAME)
|
|
116
|
+
except PackageNotFoundError:
|
|
117
|
+
installed = UNINSTALLED_VERSION
|
|
118
|
+
return f"{DISTRIBUTION_NAME}/{installed} {CRAWLER_CONTACT_SEGMENT}"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def with_user_agent(headers: Mapping[str, str]) -> dict[str, str]:
|
|
122
|
+
"""Return ``headers`` as a new dict carrying this client's identity.
|
|
123
|
+
|
|
124
|
+
A caller that stated its own ``User-Agent`` keeps it, under whatever spelling it used: HTTP
|
|
125
|
+
header names are case-insensitive, so adding a second one under a different capitalization
|
|
126
|
+
would send two identities rather than override one.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
resolved = dict(headers)
|
|
130
|
+
if any(name.lower() == "user-agent" for name in resolved):
|
|
131
|
+
return resolved
|
|
132
|
+
resolved[_USER_AGENT_HEADER] = client_user_agent()
|
|
133
|
+
return resolved
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def agent_env(agent: str) -> tuple[str, ...]:
|
|
137
|
+
"""Return the variables that name ``agent``; empty for ``none`` and for a name not listed."""
|
|
138
|
+
|
|
139
|
+
return tuple(marker.variable for marker in AGENT_MARKERS if marker.agent == agent)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def conversation_env() -> tuple[str, ...]:
|
|
143
|
+
"""Return the markers the attendance check reads as a conversation existing."""
|
|
144
|
+
|
|
145
|
+
return tuple(marker.variable for marker in AGENT_MARKERS if marker.conversation)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def first_set(names: tuple[str, ...], environ: Mapping[str, str] | None = None) -> str | None:
|
|
149
|
+
"""Return the first of ``names`` set to a non-empty value, or ``None``."""
|
|
150
|
+
|
|
151
|
+
table = os.environ if environ is None else environ
|
|
152
|
+
return next((name for name in names if (table.get(name) or "").strip()), None)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def detect_agent(environ: Mapping[str, str] | None = None) -> str:
|
|
156
|
+
"""Return the agent driving this process, one of :data:`AGENTS`."""
|
|
157
|
+
|
|
158
|
+
table = os.environ if environ is None else environ
|
|
159
|
+
for marker in AGENT_MARKERS:
|
|
160
|
+
if (table.get(marker.variable) or "").strip():
|
|
161
|
+
return marker.agent
|
|
162
|
+
return AGENT_NONE
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def begin_invocation() -> str:
|
|
166
|
+
"""Draw a fresh invocation id for the command starting now, and return it.
|
|
167
|
+
|
|
168
|
+
Called by the ``mr-data`` entry point once per command. A program importing this package
|
|
169
|
+
never calls it, so its requests carry no invocation id rather than one that lasts as long as
|
|
170
|
+
the process does.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
global _invocation
|
|
174
|
+
_invocation = secrets.token_hex(16)
|
|
175
|
+
return _invocation
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def end_invocation() -> None:
|
|
179
|
+
"""Forget the current invocation id, so later requests carry none."""
|
|
180
|
+
|
|
181
|
+
global _invocation
|
|
182
|
+
_invocation = None
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def invocation_id() -> str | None:
|
|
186
|
+
"""Return the current command's id (see :data:`INVOCATION_ID_PATTERN`), or ``None``."""
|
|
187
|
+
|
|
188
|
+
return _invocation
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def agent_headers(environ: Mapping[str, str] | None = None) -> dict[str, str]:
|
|
192
|
+
"""Return the agent and invocation headers, or none when :data:`NO_AGENT_HEADERS_ENV` is set.
|
|
193
|
+
|
|
194
|
+
The invocation header is present only inside a command :func:`begin_invocation` started. A
|
|
195
|
+
first-party call adds these itself; the transport never does.
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
table = os.environ if environ is None else environ
|
|
199
|
+
if (table.get(NO_AGENT_HEADERS_ENV) or "").strip() not in ("", "0"):
|
|
200
|
+
return {}
|
|
201
|
+
headers = {AGENT_HEADER: detect_agent(table)}
|
|
202
|
+
if _invocation is not None:
|
|
203
|
+
headers[INVOCATION_HEADER] = _invocation
|
|
204
|
+
return headers
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
__all__ = [
|
|
208
|
+
"AGENTS",
|
|
209
|
+
"AGENT_CI",
|
|
210
|
+
"AGENT_CLAUDE_CODE",
|
|
211
|
+
"AGENT_CODEX",
|
|
212
|
+
"AGENT_CURSOR",
|
|
213
|
+
"AGENT_GEMINI_CLI",
|
|
214
|
+
"AGENT_HEADER",
|
|
215
|
+
"AGENT_MARKERS",
|
|
216
|
+
"AGENT_NONE",
|
|
217
|
+
"CRAWLER_CONTACT_SEGMENT",
|
|
218
|
+
"DISTRIBUTION_NAME",
|
|
219
|
+
"INVOCATION_HEADER",
|
|
220
|
+
"INVOCATION_ID_PATTERN",
|
|
221
|
+
"NO_AGENT_HEADERS_ENV",
|
|
222
|
+
"UNINSTALLED_VERSION",
|
|
223
|
+
"AgentMarker",
|
|
224
|
+
"agent_env",
|
|
225
|
+
"agent_headers",
|
|
226
|
+
"begin_invocation",
|
|
227
|
+
"client_user_agent",
|
|
228
|
+
"conversation_env",
|
|
229
|
+
"detect_agent",
|
|
230
|
+
"end_invocation",
|
|
231
|
+
"first_set",
|
|
232
|
+
"invocation_id",
|
|
233
|
+
"with_user_agent",
|
|
234
|
+
]
|
{mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4.py
RENAMED
|
@@ -149,8 +149,8 @@ from mostlyright.data_harness.thin.v4_matching import CONTRACT_PATHS as MATCHING
|
|
|
149
149
|
#: ``JOB_INVALID`` outright, which in production was every refresh a collection epoch was offered
|
|
150
150
|
#: for rather than only the collection runs. This package must not reach a Studio older than the
|
|
151
151
|
#: commit it pins; ``docs/V4-WORKER-PROTOCOL.md`` states it beside the layout's own ordering rule.
|
|
152
|
-
PINNED_V4_OPENAPI_SOURCE_SHA256 = "
|
|
153
|
-
PINNED_V4_CONTRACT_VERSION = "4.
|
|
152
|
+
PINNED_V4_OPENAPI_SOURCE_SHA256 = "e7bd4e994b32d62ae00b4d7fc2f8936e4964cd4160b7aa30ae010528ff57b1a8"
|
|
153
|
+
PINNED_V4_CONTRACT_VERSION = "4.54.0"
|
|
154
154
|
|
|
155
155
|
#: The ``schema_version`` every command body that predates 4.14.0 stamps: ``dataset activity``,
|
|
156
156
|
#: ``table rename``, ``table archive`` and ``dataset archive``. It is the number the 0.25 line
|
{mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_public.py
RENAMED
|
@@ -52,7 +52,7 @@ from mostlyright.data_harness.thin.transport import (
|
|
|
52
52
|
UrlLibTransport,
|
|
53
53
|
service_origin,
|
|
54
54
|
)
|
|
55
|
-
from mostlyright.data_harness.thin.user_agent import with_user_agent
|
|
55
|
+
from mostlyright.data_harness.thin.user_agent import agent_headers, with_user_agent
|
|
56
56
|
from mostlyright.data_harness.ux.credentials import (
|
|
57
57
|
CLOUD_URL_ENV,
|
|
58
58
|
DEFAULT_CLOUD_URL,
|
|
@@ -245,6 +245,7 @@ class PublicCatalogClient:
|
|
|
245
245
|
{
|
|
246
246
|
"Accept": accept,
|
|
247
247
|
CLIENT_HEADER: CLIENT_NAME,
|
|
248
|
+
**agent_headers(),
|
|
248
249
|
}
|
|
249
250
|
)
|
|
250
251
|
|
|
@@ -960,6 +961,7 @@ class PublicQueryClient(CloudClient):
|
|
|
960
961
|
headers = {
|
|
961
962
|
"Accept": "application/json",
|
|
962
963
|
"Authorization": f"Bearer {self._credentials.raw_key}",
|
|
964
|
+
**agent_headers(),
|
|
963
965
|
}
|
|
964
966
|
status, raw, response_headers = self._transport.request(
|
|
965
967
|
"GET",
|
{mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_query.py
RENAMED
|
@@ -75,6 +75,7 @@ from mostlyright.data_harness.thin.transport import (
|
|
|
75
75
|
Transport,
|
|
76
76
|
UrlLibTransport,
|
|
77
77
|
)
|
|
78
|
+
from mostlyright.data_harness.thin.user_agent import agent_headers
|
|
78
79
|
from mostlyright.data_harness.thin.v4 import (
|
|
79
80
|
GET_RUN_PATH,
|
|
80
81
|
GET_RUN_QUERY_PATH,
|
|
@@ -329,6 +330,7 @@ class CloudRunQueryClient(CloudClient):
|
|
|
329
330
|
headers = {
|
|
330
331
|
"Accept": "application/json",
|
|
331
332
|
"Authorization": f"Bearer {self._credentials.raw_key}",
|
|
333
|
+
**agent_headers(),
|
|
332
334
|
}
|
|
333
335
|
encoded: bytes | None = None
|
|
334
336
|
if body is not None:
|
{mostlyright_data-0.31.11 → mostlyright_data-0.31.13}/src/mostlyright/data_harness/thin/v4_runs.py
RENAMED
|
@@ -1494,7 +1494,16 @@ def _retry_refusal_step(run: Mapping[str, Any]) -> str:
|
|
|
1494
1494
|
#: A source that kept refusing. Studio admits a person's named retry of these once its wait has
|
|
1495
1495
|
#: passed and answers ``RUN_RETRY_TOO_SOON`` with a Retry-After before then (planning #1234), so
|
|
1496
1496
|
#: this command sends them and lets Studio say when.
|
|
1497
|
-
PERSON_RETRY_CODES = frozenset(
|
|
1497
|
+
PERSON_RETRY_CODES = frozenset(
|
|
1498
|
+
{
|
|
1499
|
+
"SOURCE_RATE_LIMITED",
|
|
1500
|
+
"SOURCE_HTTP_5XX",
|
|
1501
|
+
"SOURCE_PROXY_BUSY",
|
|
1502
|
+
"TRANSPORT_FAILURE",
|
|
1503
|
+
"TOTAL_TIMEOUT",
|
|
1504
|
+
"DNS_TEMPORARY_FAILURE",
|
|
1505
|
+
}
|
|
1506
|
+
)
|
|
1498
1507
|
|
|
1499
1508
|
#: Failure codes a named retry may take without a recipe change. A hand copy of Studio's
|
|
1500
1509
|
#: ``agent_recovery`` retry and platform sets. The server refuses ``retry_of_run_id`` for anything
|
|
@@ -54,10 +54,57 @@ def _recipe_search_definition(recipe: dict[str, Any]) -> dict[str, Any]:
|
|
|
54
54
|
return localize(recipe["$defs"]["table_plan"]["properties"]["search"])
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
#: A reference into the common schema is carried into the bundle as a local definition named
|
|
58
|
+
#: with this prefix, so the bundle stays one self-contained document.
|
|
59
|
+
COMMON_REF = "common.schema.json#/$defs/"
|
|
60
|
+
COMMON_PREFIX = "common."
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
|
|
63
|
+
def _localize_common(value: Any) -> Any:
|
|
64
|
+
"""``value`` with every common-schema reference pointing at its bundled local copy."""
|
|
65
|
+
|
|
66
|
+
if isinstance(value, list):
|
|
67
|
+
return [_localize_common(item) for item in value]
|
|
68
|
+
if not isinstance(value, dict):
|
|
69
|
+
return value
|
|
70
|
+
result = {}
|
|
71
|
+
for key, item in value.items():
|
|
72
|
+
if key == "$ref" and isinstance(item, str) and item.startswith(COMMON_REF):
|
|
73
|
+
result[key] = "#/$defs/" + COMMON_PREFIX + item[len(COMMON_REF) :]
|
|
74
|
+
else:
|
|
75
|
+
result[key] = _localize_common(item)
|
|
76
|
+
return result
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def build_bundle(
|
|
80
|
+
table_search: dict[str, Any],
|
|
81
|
+
recipe: dict[str, Any],
|
|
82
|
+
pin: dict[str, Any],
|
|
83
|
+
common: dict[str, Any] | None = None,
|
|
84
|
+
) -> dict:
|
|
85
|
+
"""The closure of the definitions the client checks, from the vendored documents. A
|
|
86
|
+
definition the search schema takes from the common schema is copied in under
|
|
87
|
+
:data:`COMMON_PREFIX`."""
|
|
88
|
+
|
|
89
|
+
def within_common(value: Any) -> Any:
|
|
90
|
+
"""A common definition with its own local references pointing at their copies."""
|
|
91
|
+
|
|
92
|
+
if isinstance(value, list):
|
|
93
|
+
return [within_common(item) for item in value]
|
|
94
|
+
if not isinstance(value, dict):
|
|
95
|
+
return value
|
|
96
|
+
return {
|
|
97
|
+
key: (
|
|
98
|
+
"#/$defs/" + COMMON_PREFIX + _ref_name(item)
|
|
99
|
+
if key == "$ref" and isinstance(item, str) and item.startswith("#/$defs/")
|
|
100
|
+
else within_common(item)
|
|
101
|
+
)
|
|
102
|
+
for key, item in value.items()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
definitions = dict(table_search["$defs"])
|
|
106
|
+
for name, definition in ((common or {}).get("$defs") or {}).items():
|
|
107
|
+
definitions[COMMON_PREFIX + name] = within_common(definition)
|
|
61
108
|
recipe_member = "recipe_search"
|
|
62
109
|
wanted = [SPEC, recipe_member, CASES]
|
|
63
110
|
selected: dict[str, Any] = {recipe_member: _recipe_search_definition(recipe)}
|
|
@@ -66,6 +113,8 @@ def build_bundle(table_search: dict[str, Any], recipe: dict[str, Any], pin: dict
|
|
|
66
113
|
if isinstance(value, dict):
|
|
67
114
|
ref = value.get("$ref")
|
|
68
115
|
if isinstance(ref, str):
|
|
116
|
+
if ref.startswith(COMMON_REF):
|
|
117
|
+
ref = "#/$defs/" + COMMON_PREFIX + ref[len(COMMON_REF) :]
|
|
69
118
|
name = _ref_name(ref)
|
|
70
119
|
if name not in selected:
|
|
71
120
|
selected[name] = definitions[name]
|
|
@@ -81,6 +130,7 @@ def build_bundle(table_search: dict[str, Any], recipe: dict[str, Any], pin: dict
|
|
|
81
130
|
if name not in selected:
|
|
82
131
|
selected[name] = definitions[name]
|
|
83
132
|
collect(definitions[name])
|
|
133
|
+
selected = {name: _localize_common(definition) for name, definition in selected.items()}
|
|
84
134
|
return {
|
|
85
135
|
"schema_version": BUNDLE_SCHEMA_VERSION,
|
|
86
136
|
"source_commit": pin["source_commit"],
|
|
@@ -95,6 +145,7 @@ def bundle_from_vendor(vendor: Path = VENDOR) -> dict[str, Any]:
|
|
|
95
145
|
json.loads((schemas / "table-search.schema.json").read_text(encoding="utf-8")),
|
|
96
146
|
json.loads((schemas / "recipe.schema.json").read_text(encoding="utf-8")),
|
|
97
147
|
json.loads((vendor / "client-v4-pin.json").read_text(encoding="utf-8")),
|
|
148
|
+
json.loads((schemas / "common.schema.json").read_text(encoding="utf-8")),
|
|
98
149
|
)
|
|
99
150
|
|
|
100
151
|
|