loopmath 0.2.1__tar.gz → 0.2.3__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.
- {loopmath-0.2.1 → loopmath-0.2.3}/PKG-INFO +3 -1
- {loopmath-0.2.1 → loopmath-0.2.3}/README.md +2 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/pyproject.toml +1 -1
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/__init__.py +1 -1
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/design.py +19 -7
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/fit.py +40 -3
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/forest.py +1 -0
- loopmath-0.2.3/src/loopmath/belief/pricing.py +142 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/priors.py +46 -7
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/state.py +9 -3
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/builder/context.py +149 -20
- loopmath-0.2.3/src/loopmath/builder/predict.py +306 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/builder/server.py +15 -11
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/cli_registry.py +7 -2
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/onboard/commands.py +5 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/__init__.py +31 -8
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/benchmarks.py +6 -1
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/benchmarks.toml +472 -5
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/build.py +107 -32
- loopmath-0.2.3/src/loopmath/priors/bundle/README.md +7 -0
- loopmath-0.2.3/src/loopmath/priors/bundle/e0.jsonl.gz +0 -0
- loopmath-0.2.3/src/loopmath/priors/bundle/lanes.jsonl.gz +0 -0
- loopmath-0.2.3/src/loopmath/priors/bundle/manifest.json +357 -0
- loopmath-0.2.3/src/loopmath/priors/bundle/rq1.jsonl.gz +0 -0
- loopmath-0.2.3/src/loopmath/priors/bundle/sweep.jsonl.gz +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/commands.py +18 -12
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/e0.py +8 -3
- loopmath-0.2.3/src/loopmath/priors/lanes.py +281 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/ocpdoc.py +45 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/reduce.py +4 -2
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/registry.py +11 -6
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/rq1.py +13 -3
- loopmath-0.2.3/src/loopmath/priors/show.py +182 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/sweep.py +34 -7
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/recommend/commands.py +42 -18
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/recommend/engine.py +55 -17
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/recommend/message.py +50 -4
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/recommend/search.py +9 -6
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/recommend/storeread.py +8 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/skill/skills/loopmath-import-runs/SKILL.md +2 -2
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/skill/skills/loopmath-onboard/SKILL.md +3 -1
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/skill/skills/loopmath-update-fit/SKILL.md +2 -2
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/skill/skills/reference.md +3 -3
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/config.py +5 -2
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/types.py +9 -1
- loopmath-0.2.3/src/loopmath/views/assets/builder.css +307 -0
- loopmath-0.2.3/src/loopmath/views/assets/builder.html +70 -0
- loopmath-0.2.3/src/loopmath/views/assets/builder.js +1350 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/plans.css +1 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/plans.js +29 -4
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/results.js +12 -3
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/posterior.py +14 -1
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath.egg-info/PKG-INFO +3 -1
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath.egg-info/SOURCES.txt +6 -0
- loopmath-0.2.3/tests/test_starting_prior.py +233 -0
- loopmath-0.2.1/src/loopmath/builder/predict.py +0 -180
- loopmath-0.2.1/src/loopmath/priors/bundle/README.md +0 -1
- loopmath-0.2.1/src/loopmath/priors/bundle/e0.jsonl.gz +0 -0
- loopmath-0.2.1/src/loopmath/priors/bundle/manifest.json +0 -202
- loopmath-0.2.1/src/loopmath/priors/bundle/rq1.jsonl.gz +0 -0
- loopmath-0.2.1/src/loopmath/priors/bundle/sweep.jsonl.gz +0 -0
- loopmath-0.2.1/src/loopmath/priors/show.py +0 -72
- loopmath-0.2.1/src/loopmath/views/assets/builder.html +0 -109
- {loopmath-0.2.1 → loopmath-0.2.3}/LICENSE +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/setup.cfg +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/spec/ocp-v0.2.schema.json +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/spec/ocp-v0.schema.json +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/spec/ocp_conformance.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/__main__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapter_host.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/atrium.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/base.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/bb.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/bb_support.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/omp.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/omp_facts.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/omp_links.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/omp_parse.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/omp_types.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/opencode.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/opencode_store.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/orca.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/otel_genai.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/otel_genai_graph.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/otel_genai_parse.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/otel_genai_types.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/otel_genai_usage.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/paseo.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/pi.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/pi_support.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/adapters/registry.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/block.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/check_pymc.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/commands.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/compose.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/features.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/gaussian.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/lookahead.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/belief/outcome.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/builder/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/builder/cli.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/cli.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/cli_graph.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/cli_support.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/e0/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/e0/arms.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/e0/cli_e0.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/e0/estimate.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/e0/estimate_rework.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/e0/figures.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/e0/io.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/e0/verb.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/explain.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/fit.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/fit_assembly.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/fit_bayes.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/fit_masks.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/fit_predict.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/fit_pricing.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/fit_transfer.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/gaps.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/gitwalk.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/grade.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/grade_support.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/artifact_git.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/artifact_join.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/artifact_kinds.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/artifacts.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/bashparse.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/bashread_ops.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/bashwrite_ops.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/bashwrites.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/cache.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/codexio.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/codexio_parse.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/codexio_scan.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/dataset.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/dataset_build.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/dataset_core.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/dataset_edges.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/dataset_nodes.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/extract.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/html_common.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/html_costcurve.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/html_css.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/html_data.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/html_force.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/html_render.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/html_swimlanes.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_cli.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_common.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_parse.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_pricing.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_prompt.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_prompt_build.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_prompt_dispatch.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_prompt_evidence.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_prompt_node.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_prompt_records.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_prompt_result.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_prompt_session.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_prompt_tools.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_prompt_validation.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_prompt_workflow.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_report.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labeler_score.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/labels.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/launch.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/launch_origin.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/ocp.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/ocp_emit.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/ocp_support.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/render.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/runfile.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/runfile_export.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/runfile_finish.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/scan.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/schema.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/graph/token_completeness.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ingest/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ingest/base.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ingest/claude_code.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ingest/claude_code_support.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ingest/codex.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ingest/codex_support.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ingest/ocp.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ingest/ocp_analysis.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ingest/ocp_artifact.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ingest/ocp_common.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ingest/ocp_convert.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ingest/ocp_projection.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/logmatch/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/logmatch/artifacts.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/logmatch/clip.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/logmatch/costs.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/logmatch/match.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/logmatch/settle.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/logmatch/tariff.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ocp/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ocp/canonical.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ocp/commands.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ocp/conformance.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ocp/contractv3.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ocp/emit.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ocp/migrate.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ocp/schema/ocp-v0.2.schema.json +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ocp/schema/ocp-v0.3.schema.json +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ocp/schema/ocp-v0.schema.json +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/ocp/version.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/onboard/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/onboard/history.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/onboard/label.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/onboard/usual.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/output.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/pool.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/price.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/price_validation.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/prices.toml +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/history.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/priors/validate.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/receipts.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/recommend/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/recommend/backlog.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/recommend/curve.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/recommend/gain.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/recommend/receipts.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/recommend/stats.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/report/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/report/html.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/report/terminal.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/report/terminal_exclusions.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/report/terminal_format.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/report/terminal_read.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/research_defaults.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/research_paths.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/scoring.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/scoring_comparisons.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/scoring_predictive.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/share/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/share/commands.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/share/export.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/share/import_.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/skill/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/skill/commands.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/skill/doctor.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/skill/install.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/skill/skills/loopmath/SKILL.md +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/skill/skills/loopmath-plan-task/SKILL.md +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/skill/skills/loopmath-record-run/SKILL.md +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/budget.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/commands.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/finish.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/fitjob.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/home.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/ids.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/lock.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/record.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/report.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/runs.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/store/status.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/surface.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/surface_bootstrap.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/surface_estimation.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/surface_frame.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/surface_presentation.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/surface_support.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/taskmodel.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/README.md +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/base.css +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/common.js +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/estimates.css +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/estimates.js +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/graph.css +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/graph.js +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/results.css +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/runs.js +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/viz.css +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/assets/viz.js +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/common.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/plans.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/views/runs.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/__init__.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/candidates.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/catalog/best_of_n.toml +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/catalog/implement_review.toml +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/catalog/plan_implement.toml +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/catalog/plan_implement_review.toml +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/catalog/solo.toml +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/catalog/swarm.toml +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/commands.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/diff.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/format.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/graphview.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/ids.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/infer.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/models.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/ocp.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath/workflows/shapes.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath.egg-info/dependency_links.txt +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath.egg-info/entry_points.txt +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath.egg-info/requires.txt +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/src/loopmath.egg-info/top_level.txt +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_adapter_atrium.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_adapter_bb.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_adapter_omp.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_adapter_opencode.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_adapter_opencode_output_tokens.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_adapter_orca.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_adapter_otel_genai.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_adapter_paseo.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_adapter_services.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_adapters.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_adapters_pi.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_arms.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_cli_graph.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_cli_help_lists.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_cli_labeler.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_cli_main_module.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_cli_plan_hidden.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_cli_prices.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_cli_startup.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_color_proof.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_duplicate_guards.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_estimate.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_explain.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_fit.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_gaps.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_gitwalk.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_grade.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_artifact_recording.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_artifacts.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_bashwrites.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_cache.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_codexio.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_dataset.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_extract.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_glue.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_html.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_labeler.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_labeler_evidence_matrix.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_launch.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_ocp.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_render.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_runfile.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_graph_schema.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_home_guard.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_html.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_ingest.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_ingest_claude_code.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_ingest_codex.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_ingest_ocp.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_io.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_not_run_summary.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_ocp_conformance.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_ocp_golden.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_ocp_open_enums.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_ocp_versioning.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_output_html_path.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_packaging_metadata.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_packaging_smoke.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_pool.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_preview_script.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_price.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_receipts.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_release_paths.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_runfile_privacy_n1b.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_scoring.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_split_rebindings.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_surface.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_taskmodel_features.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_terminal.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_types_control.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_types_piece_prediction.py +0 -0
- {loopmath-0.2.1 → loopmath-0.2.3}/tests/test_v01_scaffold.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: loopmath
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Plan agent workflows, record how they went, and learn from your own Claude Code and Codex logs
|
|
5
5
|
Author: QKV Labs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -103,6 +103,8 @@ Reading the history uses up to 8 worker processes (one per CPU, at most 8). Set
|
|
|
103
103
|
|
|
104
104
|
loopmath ships with a prior built from our own sweeps and experiments, so `recommend` gives an answer before you have any history. Your own runs then move it.
|
|
105
105
|
|
|
106
|
+
The prior ships inside the package: our runs, in the shared form (no titles, paths, commands or free text), and the published benchmark results. They are the same files we fit with, and each release updates them. `onboard` and the results page say in one line which prior they start from, and `loopmath prior show` lists it source by source.
|
|
107
|
+
|
|
106
108
|
## The loop
|
|
107
109
|
|
|
108
110
|
The plan and record skills take an agent through the loop in three commands:
|
|
@@ -65,6 +65,8 @@ Reading the history uses up to 8 worker processes (one per CPU, at most 8). Set
|
|
|
65
65
|
|
|
66
66
|
loopmath ships with a prior built from our own sweeps and experiments, so `recommend` gives an answer before you have any history. Your own runs then move it.
|
|
67
67
|
|
|
68
|
+
The prior ships inside the package: our runs, in the shared form (no titles, paths, commands or free text), and the published benchmark results. They are the same files we fit with, and each release updates them. `onboard` and the results page say in one line which prior they start from, and `loopmath prior show` lists it source by source.
|
|
69
|
+
|
|
68
70
|
## The loop
|
|
69
71
|
|
|
70
72
|
The plan and record skills take an agent through the loop in three commands:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "loopmath"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "Plan agent workflows, record how they went, and learn from your own Claude Code and Codex logs"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -13,7 +13,7 @@ A term is `(node_id, parent_id, value)`. Rows:
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
15
|
import math
|
|
16
|
-
from collections.abc import Iterable
|
|
16
|
+
from collections.abc import Iterable, Mapping
|
|
17
17
|
from dataclasses import dataclass, field
|
|
18
18
|
from datetime import datetime
|
|
19
19
|
from functools import lru_cache
|
|
@@ -25,6 +25,7 @@ from ..types import (
|
|
|
25
25
|
Workflow,
|
|
26
26
|
)
|
|
27
27
|
from .forest import canonical_model_id, canonical_role, model_path
|
|
28
|
+
from .pricing import price_term
|
|
28
29
|
|
|
29
30
|
Term = tuple[str, "str | None", float]
|
|
30
31
|
|
|
@@ -324,12 +325,15 @@ def _effort_scaled(terms: tuple[Term, ...], weight: float) -> tuple[Term, ...]:
|
|
|
324
325
|
|
|
325
326
|
|
|
326
327
|
def cost_rest(st: Structure, piece: str, k: int, setting: Setting | None = None, *,
|
|
327
|
-
source: str | None = None, effort: float = 1.0
|
|
328
|
+
source: str | None = None, effort: float = 1.0,
|
|
329
|
+
prices: Mapping[str, float] | None = None) -> tuple[Term, ...]:
|
|
328
330
|
"""Cost and tokens rows without the task part: setting, role, topology, position, round, control,
|
|
329
331
|
and with a `source` the position x source node `psrc:<shape>#<pos>|<source>` and the family x source
|
|
330
332
|
node `fsrc:<family>|<source>` (spec 04 section 1). `effort` is the value of the terms a timebox caps, the
|
|
331
333
|
effort and shape (topology, position) terms (`effort_weight`: below 1 under a timebox); the role, psrc and
|
|
332
|
-
fsrc terms stay at 1.
|
|
334
|
+
fsrc terms stay at 1. `prices` is a fit's price offsets (`FitState.price_offsets`, spec 04 section 2): the
|
|
335
|
+
model's `price:offset` term goes last. The cost head has that node and the tokens head does not, so a
|
|
336
|
+
prediction's shared row prices only its cost."""
|
|
333
337
|
s = setting or st.settings[piece]
|
|
334
338
|
h, m, e = _s(s)
|
|
335
339
|
shape = st.shape or st.workflow_id
|
|
@@ -343,7 +347,8 @@ def cost_rest(st: Structure, piece: str, k: int, setting: Setting | None = None,
|
|
|
343
347
|
fam = model_path(m)[1]
|
|
344
348
|
terms += ((f"psrc:{position}|{source}", f"position:{position}", 1.0),
|
|
345
349
|
(f"fsrc:{fam}|{source}", f"family:{fam}", 1.0))
|
|
346
|
-
return terms + tuple(round_terms(k)) + tuple(_control_terms(st.k_max, st.widths[piece]))
|
|
350
|
+
return (terms + tuple(round_terms(k)) + tuple(_control_terms(st.k_max, st.widths[piece]))
|
|
351
|
+
+ price_term(prices, m))
|
|
347
352
|
|
|
348
353
|
|
|
349
354
|
def gate_rest(st: Structure, gate: GateInfo, k: int) -> tuple[Term, ...]:
|
|
@@ -421,6 +426,7 @@ class AttemptObs:
|
|
|
421
426
|
weight: float
|
|
422
427
|
setting: Setting
|
|
423
428
|
repriced: bool = True # False: the recorded dollars, not the current tariff
|
|
429
|
+
streams: tuple[float, ...] = () # input, cache read, cache write, output tokens (pricing.STREAMS order)
|
|
424
430
|
|
|
425
431
|
|
|
426
432
|
@dataclass
|
|
@@ -458,8 +464,12 @@ def source_label(doc: dict) -> str:
|
|
|
458
464
|
ext = run.get("ext") or {}
|
|
459
465
|
share = ext.get("dev.loopmath.share")
|
|
460
466
|
if isinstance(share, dict):
|
|
461
|
-
|
|
462
|
-
|
|
467
|
+
# the importer writes `source` and `task.org` as `shared:<org_hash>` already: never prefix twice. With no
|
|
468
|
+
# org at all the source is `shared`, the name `fit --without shared` takes (0.2.3, 22X review N1)
|
|
469
|
+
org = (share.get("source") or share.get("org") or share.get("org_hash") or (run.get("task") or {}).get("org")
|
|
470
|
+
or "")
|
|
471
|
+
name = str(org).removeprefix("shared:")
|
|
472
|
+
return f"shared:{name}" if name and name != "shared" else "shared"
|
|
463
473
|
src = (run.get("task") or {}).get("source")
|
|
464
474
|
kind = src.get("kind") if isinstance(src, dict) else src
|
|
465
475
|
kind = str(kind or "").strip().lower()
|
|
@@ -761,7 +771,9 @@ def parse_run(doc: dict, *, now: datetime | None = None, rule: AcceptanceRule |
|
|
|
761
771
|
if weight <= 0 or ((usd is None or usd <= 0) and not tokens):
|
|
762
772
|
dropped["attempt without usable cost"] = dropped.get("attempt without usable cost", 0) + 1
|
|
763
773
|
continue
|
|
764
|
-
|
|
774
|
+
streams = tuple(_ocp_fields(cost)[name] for name in _OCP_TOKEN_FIELDS)
|
|
775
|
+
attempts.append(AttemptObs(piece, k, usd if usd and usd > 0 else None, tokens, weight, setting, repriced,
|
|
776
|
+
streams))
|
|
765
777
|
|
|
766
778
|
gates = _gate_observations(doc, st, by_piece_round)
|
|
767
779
|
use_rule = rule or rule_from_doc(doc)
|
|
@@ -36,6 +36,7 @@ from .design import (DESIGN_VERSION, Term, Unusable, cost_row, gate_row, parse_r
|
|
|
36
36
|
from .features import FeatureTally, add_horizon, drop_features, horizon_nodes, horizon_specs
|
|
37
37
|
from .block import leaf_split
|
|
38
38
|
from .forest import Forest, default_scale, fixed_sd, scale_group
|
|
39
|
+
from .pricing import PRICE_NODE, PRICE_PRIOR_SD, STREAMS, fit_offsets, price_term
|
|
39
40
|
from .gaussian import N_DRAWS, Factors, GaussianHead, HeadFit, LogisticHead, Prior
|
|
40
41
|
from .state import SCORE_CLAMP, inverse_transform, transform # noqa: F401 (re-exported)
|
|
41
42
|
|
|
@@ -163,6 +164,7 @@ class HeadRows:
|
|
|
163
164
|
sources: list[str] = field(default_factory=list)
|
|
164
165
|
types: list[str] = field(default_factory=list)
|
|
165
166
|
not_repriced: int = 0 # cost rows on recorded dollars, not the current tariff
|
|
167
|
+
prices: dict = field(default_factory=dict) # cost head: the fit's price offsets (pricing.fit_offsets)
|
|
166
168
|
|
|
167
169
|
def add(self, terms, y, w, run, source, ttype):
|
|
168
170
|
self.rows.append(terms)
|
|
@@ -213,6 +215,8 @@ def collect_rows(docs: Iterable[dict | tuple[str | None, dict]], *, without: tup
|
|
|
213
215
|
stored: set[str] = set() # run ids of the user's store, whose copy wins
|
|
214
216
|
overlap: Counter = Counter()
|
|
215
217
|
labels: Counter = Counter()
|
|
218
|
+
model_runs: dict[str, set[str]] = defaultdict(set) # the price offset's inputs (spec 04 section 2)
|
|
219
|
+
model_streams: dict[str, list[float]] = defaultdict(lambda: [0.0] * len(STREAMS))
|
|
216
220
|
for item in docs:
|
|
217
221
|
origin, doc = item if isinstance(item, tuple) else (None, item)
|
|
218
222
|
if origin is not None and _excluded(origin, without):
|
|
@@ -258,6 +262,10 @@ def collect_rows(docs: Iterable[dict | tuple[str | None, dict]], *, without: tup
|
|
|
258
262
|
if a.usd:
|
|
259
263
|
heads["cost"].add(terms, math.log(a.usd), a.weight, pr.run_id, pr.source, ttype)
|
|
260
264
|
heads["cost"].not_repriced += not a.repriced
|
|
265
|
+
model_runs[a.setting.model].add(pr.run_id)
|
|
266
|
+
acc = model_streams[a.setting.model]
|
|
267
|
+
for i, v in enumerate(a.streams):
|
|
268
|
+
acc[i] += v
|
|
261
269
|
if a.tokens:
|
|
262
270
|
heads["tokens"].add(terms, math.log(a.tokens), a.weight, pr.run_id, pr.source, ttype)
|
|
263
271
|
for gi, k, passed in pr.gates:
|
|
@@ -289,10 +297,34 @@ def collect_rows(docs: Iterable[dict | tuple[str | None, dict]], *, without: tup
|
|
|
289
297
|
coding = tally.horizon_coding() # the horizon terms need every run's horizon first (spec 04 section 1)
|
|
290
298
|
for hr in heads.values():
|
|
291
299
|
hr.rows = add_horizon(drop_features(hr.rows, tally.nodes), hr.runs, tally.run_horizon, coding)
|
|
300
|
+
add_prices(heads["cost"], {m: len(r) for m, r in model_runs.items()}, model_streams)
|
|
292
301
|
return (heads, score_info, dict(dropped), dict(runs_by_source), {c: dict(n) for c, n in config_support.items()},
|
|
293
302
|
dict(checks), dict(overlap), dict(labels), tally)
|
|
294
303
|
|
|
295
304
|
|
|
305
|
+
def add_prices(hr: HeadRows, runs: dict[str, int], streams: dict[str, list[float]]) -> None:
|
|
306
|
+
"""The price offset (spec 04 section 2): the fit's offsets from its runs and token mix under the current price
|
|
307
|
+
table, and each cost row with its model's `price:offset` term (`pricing.price_term`, as at prediction)."""
|
|
308
|
+
hr.prices = fit_offsets(runs, streams, design_rows._price_table())
|
|
309
|
+
offsets = hr.prices["models"]
|
|
310
|
+
if not offsets:
|
|
311
|
+
return
|
|
312
|
+
rows = []
|
|
313
|
+
for row in hr.rows:
|
|
314
|
+
model = next((n[len("model:"):] for n, _, _ in row if n.startswith("model:")), None)
|
|
315
|
+
term = price_term(offsets, model) if model else ()
|
|
316
|
+
rows.append(row + list(term) if term else row)
|
|
317
|
+
hr.rows = rows
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def price_spec(hr: HeadRows) -> list:
|
|
321
|
+
"""The prior that holds `price:offset` at 1, on the cost head of a fit with offsets, --no-prior included."""
|
|
322
|
+
if not hr.prices.get("models"):
|
|
323
|
+
return []
|
|
324
|
+
return [prior_data.FactorSpec("cost", [(PRICE_NODE, None, 1.0)], 1.0, PRICE_PRIOR_SD ** 2,
|
|
325
|
+
f"{PRICE_NODE} held at 1, prior N(1, {PRICE_PRIOR_SD}^2)")]
|
|
326
|
+
|
|
327
|
+
|
|
296
328
|
# ---------------------------------------------------------------- matrices and priors
|
|
297
329
|
|
|
298
330
|
def build_matrix(rows: list[list[Term]], forest: Forest, extra_nodes: Iterable[tuple[str, str | None]] = ()):
|
|
@@ -494,7 +526,8 @@ def fit(home: Path, *, no_prior: bool = False, without: tuple[str, ...] = (), fu
|
|
|
494
526
|
with fit_lock(home, wait_s):
|
|
495
527
|
remove_partials(home)
|
|
496
528
|
config = _read_config(home)
|
|
497
|
-
|
|
529
|
+
raw_weight = config.get("benchmark_prior_weight") # unset: each benchmark's own weight (spec 04 section 5)
|
|
530
|
+
weight = None if raw_weight is None else float(raw_weight)
|
|
498
531
|
try:
|
|
499
532
|
features, features_error = FeatureSet.from_config(config.get("features")), None
|
|
500
533
|
except FeatureConfigError as exc: # a hand-edited config.toml: fit with the built-ins, say why
|
|
@@ -518,9 +551,10 @@ def fit(home: Path, *, no_prior: bool = False, without: tuple[str, ...] = (), fu
|
|
|
518
551
|
raise UnknownSource(unknown_source_message(unknown, known))
|
|
519
552
|
if not any(hr.rows for hr in heads_rows.values()):
|
|
520
553
|
raise NothingToFit(_nothing_to_fit(no_prior, without, dropped))
|
|
521
|
-
specs = []
|
|
554
|
+
specs, bench_weights = [], {}
|
|
522
555
|
if not no_prior and "benchmark" not in without:
|
|
523
556
|
specs = prior_data.benchmark_factors(benchmarks, weight=weight)
|
|
557
|
+
bench_weights = prior_data.benchmark_weights(benchmarks, weight=weight)
|
|
524
558
|
seed_key = input_key(heads_rows, specs, {"no_prior": no_prior, "without": sorted(without), "eb": eb,
|
|
525
559
|
"benchmark_prior_weight": weight})
|
|
526
560
|
forest = Forest()
|
|
@@ -532,6 +566,8 @@ def fit(home: Path, *, no_prior: bool = False, without: tuple[str, ...] = (), fu
|
|
|
532
566
|
nodes = horizon_nodes(hr.rows)
|
|
533
567
|
if nodes: # the horizon priors, --no-prior included (spec 04 section 1)
|
|
534
568
|
head_specs += horizon_specs(name, hr.kind, nodes)
|
|
569
|
+
if name == "cost": # the price offset (spec 04 section 2)
|
|
570
|
+
head_specs += price_spec(hr)
|
|
535
571
|
fitted[name] = fit_head(hr, forest, head_specs, eb=eb)
|
|
536
572
|
fit_id = new_fit_id(fits, now)
|
|
537
573
|
partial = fits / f"{fit_id}.partial"
|
|
@@ -570,7 +606,7 @@ def fit(home: Path, *, no_prior: bool = False, without: tuple[str, ...] = (), fu
|
|
|
570
606
|
"seed_key": seed_key, "timebox_effort": design_rows.TIMEBOX_EFFORT_COST,
|
|
571
607
|
"timebox_terms": list(design_rows.TIMEBOX_LEVELS),
|
|
572
608
|
"options": {"no_prior": no_prior, "without": list(without), "full": full, "eb": eb,
|
|
573
|
-
"benchmark_prior_weight": weight},
|
|
609
|
+
"benchmark_prior_weight": weight, "benchmark_weights": bench_weights},
|
|
574
610
|
"runs_by_source": runs_by_source,
|
|
575
611
|
"n_runs": {"prior": sum(v for k, v in runs_by_source.items() if k != "user"),
|
|
576
612
|
"user": runs_by_source.get("user", 0)},
|
|
@@ -592,6 +628,7 @@ def fit(home: Path, *, no_prior: bool = False, without: tuple[str, ...] = (), fu
|
|
|
592
628
|
"design_version": DESIGN_VERSION,
|
|
593
629
|
"features": {**tally.report, **({"error": features_error} if features_error else {})},
|
|
594
630
|
"horizons": tally.horizon_report(),
|
|
631
|
+
"price_offsets": heads_rows["cost"].prices,
|
|
595
632
|
}
|
|
596
633
|
if full:
|
|
597
634
|
meta["full"] = _full_check(fitted)
|
|
@@ -55,6 +55,7 @@ FIXED_SD: dict[str, tuple[float, float, float]] = {
|
|
|
55
55
|
"round": (2.0, 2.0, 2.0),
|
|
56
56
|
"control": (1.0, 1.0, 1.0),
|
|
57
57
|
"horizon": (10.0, 10.0, 10.0), # wide: the informative prior is a FactorSpec in fit() (spec 04 section 1)
|
|
58
|
+
"price": (10.0, 10.0, 10.0), # wide: fit() holds `price:offset` at 1 by a FactorSpec (spec 04 section 2)
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
HYPER_SD = 0.7 # sd of the log-normal hyperprior on each phi, around the defaults above
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""The price offset on the cost head (spec 04 section 2, 0.2.2, lane 22C).
|
|
2
|
+
|
|
3
|
+
A model's run cost moves with its list price. Every cost row of a model version `m` (not the tokens, gate,
|
|
4
|
+
success or score rows) carries the fixed term `price:offset` with the value
|
|
5
|
+
|
|
6
|
+
o_m = s_m * log(P_m / R_m), s_m = n0 / (n0 + n_m),
|
|
7
|
+
|
|
8
|
+
and the node's coefficient is held at 1 by a tight prior, so the term is a fixed offset on the log cost scale,
|
|
9
|
+
the same at fit and at prediction:
|
|
10
|
+
|
|
11
|
+
- `P_m` is m's blended list price ($/Mtok, the current price table) under the token mix (the shares of input,
|
|
12
|
+
cache read, cache write and output tokens) of the cost rows of m's family in the fit.
|
|
13
|
+
- `R_m` is the run-weighted geometric mean of the blended prices, under the same mix, of the family's models
|
|
14
|
+
with cost rows. A family with no cost rows anchors on its provider the same way (the provider's mix and
|
|
15
|
+
models). No anchor, or no price for m, gives no term.
|
|
16
|
+
- `n_m` is m's runs with a cost row in the fit; `PRICE_OFFSET_RUNS` is n0: the price counts like n0 of the
|
|
17
|
+
model's own runs, and its share of the prediction shrinks as they accumulate.
|
|
18
|
+
|
|
19
|
+
`fit_offsets` computes the offset of every model with cost rows and of every model in the price table once per
|
|
20
|
+
fit; `meta.json` `price_offsets` stores them, and prediction and the workflow search read them from there.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import math
|
|
26
|
+
from collections.abc import Iterable, Mapping
|
|
27
|
+
|
|
28
|
+
from .forest import canonical_model_id, model_path
|
|
29
|
+
|
|
30
|
+
PRICE_NODE = "price:offset"
|
|
31
|
+
PRICE_LEVEL = "price"
|
|
32
|
+
# n0: the list price counts like this many of the model's own runs (as one benchmark result counts like
|
|
33
|
+
# BENCHMARK_PRIOR_WEIGHT = 5 runs). A heuristic, to tune when stores have new versions with a few runs.
|
|
34
|
+
PRICE_OFFSET_RUNS = 5.0
|
|
35
|
+
PRICE_PRIOR_SD = 1e-3 # the coefficient's prior N(1, sd^2): a fixed offset, not a learned elasticity
|
|
36
|
+
STREAMS = ("input", "cache_read", "cache_write", "output") # price table rate keys, in the order of `streams`
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def mix_of(totals: Iterable[float]) -> tuple[float, ...] | None:
|
|
40
|
+
"""Token shares per stream (STREAMS order), or None without tokens."""
|
|
41
|
+
t = [max(0.0, float(x)) for x in totals]
|
|
42
|
+
s = sum(t)
|
|
43
|
+
return tuple(x / s for x in t) if s > 0 else None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def blended(rate: Mapping[str, float] | None, mix: tuple[float, ...] | None) -> float | None:
|
|
47
|
+
"""$/Mtok under `mix`, or None when the model has no rate, no mix, or a blended price of 0 (free)."""
|
|
48
|
+
if rate is None or mix is None:
|
|
49
|
+
return None
|
|
50
|
+
p = sum(share * float(rate.get(stream) or 0.0) for share, stream in zip(mix, STREAMS))
|
|
51
|
+
return p if p > 0 else None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def shrink(runs: float, n0: float = PRICE_OFFSET_RUNS) -> float:
|
|
55
|
+
return n0 / (n0 + max(0.0, float(runs)))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _add(acc: dict[str, list[float]], key: str, values: Iterable[float]) -> None:
|
|
59
|
+
cur = acc.setdefault(key, [0.0] * len(STREAMS))
|
|
60
|
+
for i, v in enumerate(values):
|
|
61
|
+
cur[i] += float(v)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def fit_offsets(runs: Mapping[str, int], streams: Mapping[str, Iterable[float]], table, *,
|
|
65
|
+
n0: float = PRICE_OFFSET_RUNS) -> dict:
|
|
66
|
+
"""The fit's `price_offsets`: {n0, models: {model: offset}, detail, anchors}.
|
|
67
|
+
|
|
68
|
+
`runs`: model (canonical id) to its runs with a cost row; `streams`: model to its token totals over those
|
|
69
|
+
rows (STREAMS order); `table`: a price.PriceTable, or None (no offsets). Offsets of 0 are left out of
|
|
70
|
+
`models`; `detail` says for each priced model which anchor it used, or why it has none."""
|
|
71
|
+
out: dict = {"n0": n0, "models": {}, "detail": {}, "anchors": {}}
|
|
72
|
+
if table is None:
|
|
73
|
+
return out
|
|
74
|
+
fam_tok: dict[str, list[float]] = {}
|
|
75
|
+
prov_tok: dict[str, list[float]] = {}
|
|
76
|
+
fam_models: dict[str, dict[str, int]] = {}
|
|
77
|
+
prov_models: dict[str, dict[str, int]] = {}
|
|
78
|
+
for model, n in sorted(runs.items()):
|
|
79
|
+
if n <= 0:
|
|
80
|
+
continue
|
|
81
|
+
prov, fam, _ = model_path(model)
|
|
82
|
+
fam_models.setdefault(fam, {})[model] = int(n)
|
|
83
|
+
prov_models.setdefault(prov, {})[model] = int(n)
|
|
84
|
+
values = list(streams.get(model) or [0.0] * len(STREAMS))
|
|
85
|
+
_add(fam_tok, fam, values)
|
|
86
|
+
_add(prov_tok, prov, values)
|
|
87
|
+
candidates = set(runs) | {canonical_model_id(k) for k in table.rates}
|
|
88
|
+
anchors: dict[str, dict | None] = {}
|
|
89
|
+
|
|
90
|
+
def anchor(key: str, members: dict[str, int], tokens: list[float] | None) -> dict | None:
|
|
91
|
+
if key in anchors:
|
|
92
|
+
return anchors[key]
|
|
93
|
+
mix = mix_of(tokens or [])
|
|
94
|
+
priced = {m: (n, blended(table.rate(m), mix)) for m, n in members.items()}
|
|
95
|
+
priced = {m: (n, p) for m, (n, p) in priced.items() if p is not None}
|
|
96
|
+
found = None
|
|
97
|
+
if mix is not None and priced:
|
|
98
|
+
total = sum(n for n, _ in priced.values())
|
|
99
|
+
log_ref = sum(n * math.log(p) for n, p in priced.values()) / total
|
|
100
|
+
found = {"mix": mix, "log_ref": log_ref,
|
|
101
|
+
"reference_usd_per_mtok": round(math.exp(log_ref), 6),
|
|
102
|
+
"models": {m: n for m, (n, _) in sorted(priced.items())},
|
|
103
|
+
"todo": sorted(m for m in priced if table.is_todo(m))}
|
|
104
|
+
anchors[key] = found
|
|
105
|
+
return found
|
|
106
|
+
|
|
107
|
+
for model in sorted(candidates):
|
|
108
|
+
prov, fam, _ = model_path(model)
|
|
109
|
+
a, key = None, None
|
|
110
|
+
if fam in fam_models:
|
|
111
|
+
key = f"family:{fam}"
|
|
112
|
+
a = anchor(key, fam_models[fam], fam_tok.get(fam))
|
|
113
|
+
if a is None and prov in prov_models and fam not in fam_models:
|
|
114
|
+
key = f"provider:{prov}"
|
|
115
|
+
a = anchor(key, prov_models[prov], prov_tok.get(prov))
|
|
116
|
+
if a is None:
|
|
117
|
+
out["detail"][model] = {"runs": int(runs.get(model, 0)), "note": "no anchor with runs and prices"}
|
|
118
|
+
continue
|
|
119
|
+
p = blended(table.rate(model), a["mix"])
|
|
120
|
+
if p is None:
|
|
121
|
+
out["detail"][model] = {"runs": int(runs.get(model, 0)), "anchor": key, "note": "no price"}
|
|
122
|
+
continue
|
|
123
|
+
n = int(runs.get(model, 0))
|
|
124
|
+
s = shrink(n, n0)
|
|
125
|
+
offset = s * (math.log(p) - a["log_ref"])
|
|
126
|
+
out["detail"][model] = {"runs": n, "anchor": key, "usd_per_mtok": round(p, 6), "shrink": round(s, 6),
|
|
127
|
+
"log_ratio": round(math.log(p) - a["log_ref"], 6), "offset": round(offset, 6),
|
|
128
|
+
**({"todo": True} if table.is_todo(model) else {})}
|
|
129
|
+
if offset != 0.0:
|
|
130
|
+
out["models"][model] = offset
|
|
131
|
+
out["anchors"] = {k: {**{kk: vv for kk, vv in v.items() if kk != "log_ref"}, "mix": [round(x, 6) for x in v["mix"]]}
|
|
132
|
+
for k, v in sorted(anchors.items()) if v}
|
|
133
|
+
return out
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def price_term(offsets: Mapping[str, float] | None, model: str) -> tuple[tuple[str, None, float], ...]:
|
|
137
|
+
"""The cost row's price term for `model` (a canonical id) under a fit's offsets: none for an offset of 0 or
|
|
138
|
+
a fit without offsets."""
|
|
139
|
+
if not offsets:
|
|
140
|
+
return ()
|
|
141
|
+
o = offsets.get(model)
|
|
142
|
+
return ((PRICE_NODE, None, float(o)),) if o else ()
|
|
@@ -10,8 +10,11 @@
|
|
|
10
10
|
version nodes: success head, the logit gap to the benchmark's reference model with variance
|
|
11
11
|
`1 / (w p (1 - p))`; cost head, the log cost ratio with variance `1 / w`; tokens head
|
|
12
12
|
(`kind = "tokens"`, published total tokens for the evaluation), the log token ratio
|
|
13
|
-
with variance `1 / w`. `w` is
|
|
14
|
-
|
|
13
|
+
with variance `1 / w`. `w` is one model's share of the benchmark's weight `w_b` (0.2.3,
|
|
14
|
+
lane 23K): the `weight` of its `[[benchmark]]` table (default 5, about 5 runs), shared by
|
|
15
|
+
the model's k results on that benchmark, so each gets `w_b / k` and a model with five
|
|
16
|
+
efforts pulls as hard in total as a model with one. Config `benchmark_prior_weight`, when
|
|
17
|
+
set, is one `w_b` for every benchmark; 0 turns the factors off.
|
|
15
18
|
"""
|
|
16
19
|
|
|
17
20
|
from __future__ import annotations
|
|
@@ -91,8 +94,30 @@ def _model_terms(model: str, effort: str | None, sign: float) -> list[tuple[str,
|
|
|
91
94
|
return terms
|
|
92
95
|
|
|
93
96
|
|
|
94
|
-
def
|
|
95
|
-
"""
|
|
97
|
+
def benchmark_weights(path: Path | None = None, *, weight: float | None = None) -> dict[str, float]:
|
|
98
|
+
"""Each benchmark's weight `w_b`: `weight` for every benchmark when it is given (config
|
|
99
|
+
`benchmark_prior_weight`), else the benchmark's own `weight` field, else 5. Empty for a missing file."""
|
|
100
|
+
path = path or benchmark_path()
|
|
101
|
+
if not path.is_file():
|
|
102
|
+
return {}
|
|
103
|
+
data = tomllib.loads(path.read_text(encoding="utf-8"))
|
|
104
|
+
return {str(b["id"]): _weight_of(b, weight) for b in data.get("benchmark") or [] if b.get("id")}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _weight_of(bench: dict, weight: float | None) -> float:
|
|
108
|
+
if weight is not None:
|
|
109
|
+
return float(weight)
|
|
110
|
+
own = bench.get("weight")
|
|
111
|
+
return float(own) if isinstance(own, (int, float)) and not isinstance(own, bool) else BENCHMARK_PRIOR_WEIGHT
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def benchmark_factors(path: Path | None = None, *, weight: float | None = None) -> list[FactorSpec]:
|
|
115
|
+
"""Prior factors from `benchmarks.toml`. A missing file gives no factors.
|
|
116
|
+
|
|
117
|
+
`weight` (config `benchmark_prior_weight`) overrides every benchmark's own weight; None reads
|
|
118
|
+
each benchmark's `weight`. A model's k results on one benchmark share its weight: each factor
|
|
119
|
+
has weight `w_b / k` (B1). A benchmark with weight 0 or less gives no factors.
|
|
120
|
+
"""
|
|
96
121
|
path = path or benchmark_path()
|
|
97
122
|
if not path.is_file():
|
|
98
123
|
return []
|
|
@@ -103,11 +128,15 @@ def benchmark_factors(path: Path | None = None, *, weight: float = BENCHMARK_PRI
|
|
|
103
128
|
for bid, bench in benches.items():
|
|
104
129
|
kind = str(bench.get("kind") or "success")
|
|
105
130
|
ref = canonical_model_id(str(bench.get("reference") or ""))
|
|
131
|
+
w_b = _weight_of(bench, weight)
|
|
132
|
+
if w_b <= 0:
|
|
133
|
+
continue
|
|
106
134
|
rows = [r for r in results if r["benchmark"] == bid]
|
|
107
135
|
ref_rows = [r for r in rows if canonical_model_id(str(r["model"])) == ref]
|
|
108
136
|
if not ref_rows:
|
|
109
137
|
continue
|
|
110
138
|
ref_value = float(ref_rows[0]["value"])
|
|
139
|
+
by_model: dict[str, list[tuple[str, list, float, dict]]] = {} # model -> (head, terms, mean, row) per result
|
|
111
140
|
for r in rows:
|
|
112
141
|
model = canonical_model_id(str(r["model"]))
|
|
113
142
|
if model == ref:
|
|
@@ -117,14 +146,24 @@ def benchmark_factors(path: Path | None = None, *, weight: float = BENCHMARK_PRI
|
|
|
117
146
|
terms = _cancel(terms)
|
|
118
147
|
if not terms:
|
|
119
148
|
continue
|
|
120
|
-
note = f"{bid}: {model} {value} against {ref} {ref_value} ({r.get('url', '')}, {r.get('date', '')})"
|
|
121
149
|
if kind == "success":
|
|
122
150
|
p = min(max(value, 0.01), 0.99)
|
|
123
151
|
p_ref = min(max(ref_value, 0.01), 0.99)
|
|
124
152
|
gap = math.log(p / (1 - p)) - math.log(p_ref / (1 - p_ref))
|
|
125
|
-
|
|
153
|
+
by_model.setdefault(model, []).append(("success", terms, gap, r))
|
|
126
154
|
elif kind in ("cost", "tokens") and value > 0 and ref_value > 0: # Tokens feed the tokens head only
|
|
127
|
-
|
|
155
|
+
by_model.setdefault(model, []).append((kind, terms, math.log(value / ref_value), r))
|
|
156
|
+
for model, items in by_model.items():
|
|
157
|
+
w = w_b / len(items) # one budget per model and benchmark (B1)
|
|
158
|
+
share = f"weight {w_b:g} over {len(items)}" if len(items) > 1 else f"weight {w_b:g}"
|
|
159
|
+
for head, terms, mean, r in items:
|
|
160
|
+
note = (f"{bid}: {model} {float(r['value'])} against {ref} {ref_value} ({share}; "
|
|
161
|
+
f"{r.get('url', '')}, {r.get('date', '')})")
|
|
162
|
+
if head == "success":
|
|
163
|
+
p = min(max(float(r["value"]), 0.01), 0.99)
|
|
164
|
+
out.append(FactorSpec("success", terms, mean, 1.0 / (w * p * (1 - p)), note))
|
|
165
|
+
else:
|
|
166
|
+
out.append(FactorSpec(head, terms, mean, 1.0 / w, note))
|
|
128
167
|
return out
|
|
129
168
|
|
|
130
169
|
|
|
@@ -67,7 +67,7 @@ MIN_SCORE_SWITCH = 5 # spec 04 section 2: g = p_reach once the score head has 5
|
|
|
67
67
|
BASE_HEADS = {"cost": ("gaussian", "cost"), "tokens": ("gaussian", "cost"), "gate": ("logistic", "logit"),
|
|
68
68
|
"success": ("logistic", "logit")}
|
|
69
69
|
TASK_CHAIN = ("task", "subtype", "repo", "type")
|
|
70
|
-
FIXED_LEVELS = ("fixed", "round", "control")
|
|
70
|
+
FIXED_LEVELS = ("fixed", "round", "control", "price")
|
|
71
71
|
SCORE_CLAMP = (0.005, 0.995)
|
|
72
72
|
_CACHE_LIMIT = 200_000
|
|
73
73
|
MEAN_NODES = 12 # Gauss-Hermite nodes over an unseen normal effect
|
|
@@ -339,6 +339,8 @@ class FitState:
|
|
|
339
339
|
self.sim_seed = _seed(self.seed_key, "simulate")
|
|
340
340
|
# spec 04 section 1: the value of the timebox-capped terms (effort, shape) in a timeboxed task's cost rows
|
|
341
341
|
self.timebox_effort = float(self.meta.get("timebox_effort", 1.0))
|
|
342
|
+
# spec 04 section 2: the fit's price offset per model (log scale); none in fits before 0.2.2
|
|
343
|
+
self.price_offsets: dict[str, float] = dict((self.meta.get("price_offsets") or {}).get("models") or {})
|
|
342
344
|
if heads is None:
|
|
343
345
|
heads = {}
|
|
344
346
|
for name, hmeta in (self.meta.get("heads") or {}).items():
|
|
@@ -418,7 +420,8 @@ class FitState:
|
|
|
418
420
|
for piece in st.pieces:
|
|
419
421
|
rounds = st.k_max if st.piece_loop.get(piece) is not None else 1
|
|
420
422
|
for k in range(1, rounds + 1):
|
|
421
|
-
cost[(piece, k)] = cost_rest(st, piece, k, source=PREDICT_SOURCE, effort=effort
|
|
423
|
+
cost[(piece, k)] = cost_rest(st, piece, k, source=PREDICT_SOURCE, effort=effort,
|
|
424
|
+
prices=self.price_offsets)
|
|
422
425
|
gate = {}
|
|
423
426
|
for gi, g in enumerate(st.gates):
|
|
424
427
|
for k in range(1, (st.k_max if st.gate_loop.get(gi) is not None else 1) + 1):
|
|
@@ -789,10 +792,13 @@ class FitState:
|
|
|
789
792
|
gate_pass=res[hg] if hg is not None else None, rounds=res[hr],
|
|
790
793
|
cost_per_round=Money(usd=res[hpr[0]], tokens=res[hpr[1]]) if hpr else None)
|
|
791
794
|
for piece, (hu, ht, hg, hr, hpr) in handles.items()}
|
|
795
|
+
# The typical run (0.2.3): the median of the same simulated runs, one per draw, the 80% range is read from
|
|
796
|
+
run_usd = dataclasses.replace(res[h_run[1]], median=float(np.median(rd.sim_usd)))
|
|
797
|
+
run_tokens = dataclasses.replace(res[h_run[2]], median=float(np.median(rd.sim_tokens)))
|
|
792
798
|
pred = Prediction(
|
|
793
799
|
config=cfg.id,
|
|
794
800
|
p_success=res[h_run[0]],
|
|
795
|
-
cost=Money(usd=
|
|
801
|
+
cost=Money(usd=run_usd, tokens=run_tokens),
|
|
796
802
|
ell=Money(usd=res[h_run[3]], tokens=res[h_run[4]]),
|
|
797
803
|
rounds=res[h_run[5]],
|
|
798
804
|
per_piece=per_piece,
|