roadmodel 0.2.84__tar.gz → 0.2.85__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.
- {roadmodel-0.2.84 → roadmodel-0.2.85}/PKG-INFO +2 -2
- {roadmodel-0.2.84 → roadmodel-0.2.85}/README.md +1 -1
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/claude-commands/roadmap-refresh.md +9 -2
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/templates/prompt-phase-roadmap.md +8 -1
- {roadmodel-0.2.84 → roadmodel-0.2.85}/pyproject.toml +1 -1
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/prompt-phase-roadmap.md +8 -1
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/roadmap-refresh.md +9 -2
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/__init__.py +1 -1
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/cli.py +32 -2
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/scoring.py +168 -25
- {roadmodel-0.2.84 → roadmodel-0.2.85}/.github/README.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/.gitignore +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/LICENSE +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/NOTICE +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/benchmarks.json +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/catalog.json +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/claude-commands/roadmap-phase.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/claude-commands/roadmap-project.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/claude-commands/roadmap-step.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/claude-commands/roadmodel-upgrade.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/context-windows.json +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/effort-tokens.json +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/model-selector.txt +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/model-tier-cost-scale.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/settings-display.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/templates/phase-roadmap-template.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/templates/planning-kit-how-to-use.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/templates/project-roadmap-template.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/templates/prompt-project-roadmap.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/docs/user-context.example.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/hatch_build.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/infra/README.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/infra/gcp-killswitch/README.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/infra/supabase/README.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/benchmarks.json +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/catalog.json +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/context-windows.json +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/effort-tokens.json +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/model-selector.txt +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/model-tier-cost-scale.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/phase-roadmap-template.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/planning-kit-how-to-use.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/project-roadmap-template.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/prompt-project-roadmap.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/roadmap-phase.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/roadmap-project.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/roadmap-step.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/roadmodel-upgrade.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/settings-display.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/roadmodel/data/user-context.example.md +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/scripts/agent_context.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/scripts/agent_rules.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/scripts/update_projects.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/scripts/updater_lock.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/scripts/upgrade_fleet.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/scripts/worktree_memory.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/__main__.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/config.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/cost.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/errors.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/mcp_server.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/private_files.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/providers/__init__.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/providers/_safety.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/providers/anthropic.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/providers/google.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/providers/openai.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/providers/openai_compatible.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/providers/registry.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/recommend.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/usage.py +0 -0
- {roadmodel-0.2.84 → roadmodel-0.2.85}/src/roadmodel/user_context.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: roadmodel
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.85
|
|
4
4
|
Summary: BYO-key CLI that recommends the right AI model, platform, and settings for a prompt.
|
|
5
5
|
Project-URL: Homepage, https://roadmodel.ai
|
|
6
6
|
Project-URL: Repository, https://github.com/nathanramoscfa/roadmodel
|
|
@@ -607,7 +607,7 @@ server and the reasoning-effort default in parity automatically.
|
|
|
607
607
|
| `roadmodel recommend --model` | Override the specific model ID on the chosen provider. |
|
|
608
608
|
| `roadmodel recommend --user-context PATH` | Override the user-context.md location for this invocation. |
|
|
609
609
|
| `roadmodel score --category C --complexity X` | Rank (model, platform, effort) candidates in code for a classified task — no engine call. See [docs/scoring-model.md](docs/scoring-model.md). |
|
|
610
|
-
| `roadmodel backup --category C --complexity X --model M --effort E` | Name the cross-maker backup for a pick, by the rule /recommend uses — no engine call. Prints the roadmap Settings Backup cell. |
|
|
610
|
+
| `roadmodel backup --category C --complexity X --model M --effort E` | Name the cross-maker backup for a pick, by the rule /recommend uses — no engine call. Prints the roadmap Settings Backup cell, and first a `MODEL` cell when a newer version of the pick's model (same platform, no dearer) scores level or better at its effort. |
|
|
611
611
|
| `roadmodel catalog show` | Print the bundled `model-selector.txt` (use `--doc tier-cost-scale` for the price doc). |
|
|
612
612
|
| `roadmodel catalog path` | Print the on-disk path of the bundled catalog document (same `--doc` flag). |
|
|
613
613
|
| `roadmodel context show` | Print the resolved `user-context.md` file. |
|
|
@@ -356,7 +356,7 @@ server and the reasoning-effort default in parity automatically.
|
|
|
356
356
|
| `roadmodel recommend --model` | Override the specific model ID on the chosen provider. |
|
|
357
357
|
| `roadmodel recommend --user-context PATH` | Override the user-context.md location for this invocation. |
|
|
358
358
|
| `roadmodel score --category C --complexity X` | Rank (model, platform, effort) candidates in code for a classified task — no engine call. See [docs/scoring-model.md](docs/scoring-model.md). |
|
|
359
|
-
| `roadmodel backup --category C --complexity X --model M --effort E` | Name the cross-maker backup for a pick, by the rule /recommend uses — no engine call. Prints the roadmap Settings Backup cell. |
|
|
359
|
+
| `roadmodel backup --category C --complexity X --model M --effort E` | Name the cross-maker backup for a pick, by the rule /recommend uses — no engine call. Prints the roadmap Settings Backup cell, and first a `MODEL` cell when a newer version of the pick's model (same platform, no dearer) scores level or better at its effort. |
|
|
360
360
|
| `roadmodel catalog show` | Print the bundled `model-selector.txt` (use `--doc tier-cost-scale` for the price doc). |
|
|
361
361
|
| `roadmodel catalog path` | Print the on-disk path of the bundled catalog document (same `--doc` flag). |
|
|
362
362
|
| `roadmodel context show` | Print the resolved `user-context.md` file. |
|
|
@@ -186,8 +186,15 @@ exclusion in the selector, and include a backup model.
|
|
|
186
186
|
--effort <the step's effort>` prints for the step's classification —
|
|
187
187
|
the rule /recommend backs a pick up by, which takes the cheaper of two
|
|
188
188
|
substitutes the benchmarks cannot tell apart — and it outranks the
|
|
189
|
-
selector's BACKUP.
|
|
190
|
-
|
|
189
|
+
selector's BACKUP. When the command also prints a `MODEL` line, that
|
|
190
|
+
cell replaces the step's model — a newer version of the same model
|
|
191
|
+
(GPT-6 Sol → GPT-6.1 Sol), on the same platform, no dearer, that
|
|
192
|
+
scores level or better at the step's effort; the selector's letters
|
|
193
|
+
are measured at each model's top effort, and a step that runs lower
|
|
194
|
+
can reverse them. That is a difference: rewrite the table, its Model
|
|
195
|
+
rationale and its selection block with the `MODEL` cell, keep the
|
|
196
|
+
Backup cell printed with it, and add the `Settings updated` line.
|
|
197
|
+
A table written before roadmodel 0.2.41 has no such row: add it from the
|
|
191
198
|
backup the step already names (its Model rationale's closing
|
|
192
199
|
sentence, or its entry in the Model selection blocks) — a layout
|
|
193
200
|
fix, not a new pick, so it takes no `Settings updated` line.
|
|
@@ -109,7 +109,14 @@ the cell `roadmodel backup --category <c> --complexity <x> [--novel]
|
|
|
109
109
|
step's own classification: the rule /recommend backs a pick up by, in
|
|
110
110
|
code, which takes the cheaper of two substitutes the benchmarks cannot
|
|
111
111
|
tell apart. The selector's BACKUP stands only where it is not
|
|
112
|
-
installed.
|
|
112
|
+
installed. When the command also prints a `MODEL` line, that cell
|
|
113
|
+
replaces the step's model: a newer version of the same model (GPT-6 Sol
|
|
114
|
+
→ GPT-6.1 Sol), on the same platform, no dearer, that scores level or
|
|
115
|
+
better at the step's effort. The selector's letters are measured at each
|
|
116
|
+
model's top effort, and a step that runs lower can reverse them. Write
|
|
117
|
+
the `MODEL` cell into the Model row, the Model rationale and the step's
|
|
118
|
+
selection block, citing the newer model's figures at the step's effort,
|
|
119
|
+
and keep the Backup cell printed with it.
|
|
113
120
|
|
|
114
121
|
Honor the template's style rules: 80-column prose, zero `{{...}}`
|
|
115
122
|
tokens left, `**Status:** Not started` directly under the title and
|
|
@@ -109,7 +109,14 @@ the cell `roadmodel backup --category <c> --complexity <x> [--novel]
|
|
|
109
109
|
step's own classification: the rule /recommend backs a pick up by, in
|
|
110
110
|
code, which takes the cheaper of two substitutes the benchmarks cannot
|
|
111
111
|
tell apart. The selector's BACKUP stands only where it is not
|
|
112
|
-
installed.
|
|
112
|
+
installed. When the command also prints a `MODEL` line, that cell
|
|
113
|
+
replaces the step's model: a newer version of the same model (GPT-6 Sol
|
|
114
|
+
→ GPT-6.1 Sol), on the same platform, no dearer, that scores level or
|
|
115
|
+
better at the step's effort. The selector's letters are measured at each
|
|
116
|
+
model's top effort, and a step that runs lower can reverse them. Write
|
|
117
|
+
the `MODEL` cell into the Model row, the Model rationale and the step's
|
|
118
|
+
selection block, citing the newer model's figures at the step's effort,
|
|
119
|
+
and keep the Backup cell printed with it.
|
|
113
120
|
|
|
114
121
|
Honor the template's style rules: 80-column prose, zero `{{...}}`
|
|
115
122
|
tokens left, `**Status:** Not started` directly under the title and
|
|
@@ -186,8 +186,15 @@ exclusion in the selector, and include a backup model.
|
|
|
186
186
|
--effort <the step's effort>` prints for the step's classification —
|
|
187
187
|
the rule /recommend backs a pick up by, which takes the cheaper of two
|
|
188
188
|
substitutes the benchmarks cannot tell apart — and it outranks the
|
|
189
|
-
selector's BACKUP.
|
|
190
|
-
|
|
189
|
+
selector's BACKUP. When the command also prints a `MODEL` line, that
|
|
190
|
+
cell replaces the step's model — a newer version of the same model
|
|
191
|
+
(GPT-6 Sol → GPT-6.1 Sol), on the same platform, no dearer, that
|
|
192
|
+
scores level or better at the step's effort; the selector's letters
|
|
193
|
+
are measured at each model's top effort, and a step that runs lower
|
|
194
|
+
can reverse them. That is a difference: rewrite the table, its Model
|
|
195
|
+
rationale and its selection block with the `MODEL` cell, keep the
|
|
196
|
+
Backup cell printed with it, and add the `Settings updated` line.
|
|
197
|
+
A table written before roadmodel 0.2.41 has no such row: add it from the
|
|
191
198
|
backup the step already names (its Model rationale's closing
|
|
192
199
|
sentence, or its entry in the Model selection blocks) — a layout
|
|
193
200
|
fix, not a new pick, so it takes no `Settings updated` line.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# src/roadmodel/__init__.py
|
|
2
|
-
__version__ = "0.2.
|
|
2
|
+
__version__ = "0.2.85"
|
|
@@ -561,17 +561,41 @@ def backup_command(
|
|
|
561
561
|
The rule /recommend backs each pick up by: another maker's model that does
|
|
562
562
|
the job as well, for the least; where the benchmarks cannot tell two
|
|
563
563
|
substitutes apart, the cheaper. Prints the roadmap Settings Backup cell.
|
|
564
|
+
|
|
565
|
+
When a newer version of the pick's model (same maker and series, on the
|
|
566
|
+
same platform, no dearer) scores level or better at the pick's effort, it
|
|
567
|
+
first prints that model on a MODEL line: the step runs it instead, and the
|
|
568
|
+
backup printed is its backup.
|
|
564
569
|
"""
|
|
565
570
|
text = _scoring_user_context(user_context_path)
|
|
566
571
|
posture = (budget or scoring.declared_budget(text)).lower()
|
|
567
572
|
try:
|
|
568
573
|
task = scoring.Task(category.lower(), complexity.lower(), novel, posture)
|
|
569
|
-
|
|
574
|
+
successor = scoring.successor_for_pick(task, text, model, effort)
|
|
575
|
+
runs = successor.candidate.model_id if successor is not None else model
|
|
576
|
+
backup = scoring.backup_for_pick(task, text, runs, effort)
|
|
570
577
|
except ValueError as exc:
|
|
571
578
|
raise click.ClickException(str(exc)) from exc
|
|
572
579
|
cell = scoring.backup_cell(backup) if backup is not None else "None"
|
|
580
|
+
model_cell = scoring.backup_cell(successor) if successor is not None else None
|
|
573
581
|
if output.lower() == "json":
|
|
574
|
-
payload: dict[str, Any] = {
|
|
582
|
+
payload: dict[str, Any] = {
|
|
583
|
+
"model": None,
|
|
584
|
+
"model_cell": model_cell,
|
|
585
|
+
"backup": None,
|
|
586
|
+
"cell": cell,
|
|
587
|
+
}
|
|
588
|
+
if successor is not None:
|
|
589
|
+
s = successor.candidate
|
|
590
|
+
payload["model"] = {
|
|
591
|
+
"model_id": s.model_id,
|
|
592
|
+
"model": s.model_name,
|
|
593
|
+
"platform_id": s.platform_id,
|
|
594
|
+
"platform": s.platform_name,
|
|
595
|
+
"effort": successor.effort,
|
|
596
|
+
"native_effort": successor.native,
|
|
597
|
+
"replaces": successor.replaces,
|
|
598
|
+
}
|
|
575
599
|
if backup is not None:
|
|
576
600
|
c = backup.candidate
|
|
577
601
|
payload["backup"] = {
|
|
@@ -584,6 +608,12 @@ def backup_command(
|
|
|
584
608
|
}
|
|
585
609
|
click.echo(json.dumps(payload, indent=2, ensure_ascii=False))
|
|
586
610
|
return
|
|
611
|
+
if successor is not None:
|
|
612
|
+
click.echo(f"MODEL {model_cell}")
|
|
613
|
+
click.echo(
|
|
614
|
+
f" replaces {successor.replaces}: a newer version, no dearer, "
|
|
615
|
+
"level or better at this effort"
|
|
616
|
+
)
|
|
587
617
|
click.echo(f"BACKUP {cell}")
|
|
588
618
|
|
|
589
619
|
|
|
@@ -2027,22 +2027,49 @@ def _model_ref_id(ref: str, catalog: dict[str, Any]) -> str:
|
|
|
2027
2027
|
return ref
|
|
2028
2028
|
|
|
2029
2029
|
|
|
2030
|
-
|
|
2030
|
+
@dataclass(frozen=True)
|
|
2031
|
+
class _Pick:
|
|
2032
|
+
"""A pick made outside the ladder (a roadmap step's Settings), with the
|
|
2033
|
+
pool and scales it is read against (:func:`_pick`)."""
|
|
2034
|
+
|
|
2035
|
+
candidate: Candidate # the model, on the platform the pool reaches it best
|
|
2036
|
+
level: str # the scorer's level it runs at
|
|
2037
|
+
ranking: Ranking
|
|
2038
|
+
base: Task
|
|
2039
|
+
models: dict[str, dict[str, Any]]
|
|
2040
|
+
bench: dict[str, Any]
|
|
2041
|
+
composite: dict[str, Any]
|
|
2042
|
+
scales: tuple[EvidenceScale | None, EvidenceScale | None]
|
|
2043
|
+
catalog: dict[str, Any]
|
|
2044
|
+
|
|
2045
|
+
def at(self, c: Candidate, level: str | None) -> Candidate:
|
|
2046
|
+
"""``c`` read at ``level`` (None: its headline row; see :func:`_at_level`)."""
|
|
2047
|
+
return _at_level(
|
|
2048
|
+
c,
|
|
2049
|
+
level,
|
|
2050
|
+
self.models[c.model_id],
|
|
2051
|
+
self.base,
|
|
2052
|
+
self.composite,
|
|
2053
|
+
self.scales,
|
|
2054
|
+
self.ranking.lam,
|
|
2055
|
+
self.ranking.k_points_per_decade,
|
|
2056
|
+
)
|
|
2057
|
+
|
|
2058
|
+
def measured(self, model_id: str) -> bool:
|
|
2059
|
+
return self.level in measured_levels(self.bench, model_id)
|
|
2060
|
+
|
|
2061
|
+
|
|
2062
|
+
def _pick(
|
|
2031
2063
|
task: Task,
|
|
2032
2064
|
user_context_text: str,
|
|
2033
2065
|
model: str,
|
|
2034
2066
|
effort: str,
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
)
|
|
2040
|
-
|
|
2041
|
-
Settings): ``model`` (catalog id or name) run at ``effort`` (a scorer or
|
|
2042
|
-
native level), backed up by the rule a /recommend rung follows
|
|
2043
|
-
(:func:`_backup_for`), over the same pool, so the two never disagree.
|
|
2044
|
-
``task.budget`` sets the posture an unmeasured backup's effort follows.
|
|
2045
|
-
None when the pool holds no other maker's model. ValueError when the
|
|
2067
|
+
unavailable_models: list[str] | None,
|
|
2068
|
+
catalog: dict[str, Any] | None,
|
|
2069
|
+
benchmarks: dict[str, Any] | None,
|
|
2070
|
+
) -> _Pick:
|
|
2071
|
+
"""``model`` (catalog id or name) run at ``effort`` (a scorer or native
|
|
2072
|
+
level) for ``task``, over the pool /recommend ranks. ValueError when the
|
|
2046
2073
|
model is not one this user-context can reach."""
|
|
2047
2074
|
cat = catalog if catalog is not None else _cost._load_catalog()
|
|
2048
2075
|
bench = benchmarks if benchmarks is not None else _load_benchmarks()
|
|
@@ -2060,30 +2087,145 @@ def backup_for_pick(
|
|
|
2060
2087
|
models = {str(m.get("id", "")): m for m in cat.get("models", []) if isinstance(m, dict)}
|
|
2061
2088
|
if picked is None or model_id not in models:
|
|
2062
2089
|
raise ValueError(f"{model!r} is not a model this user-context can reach")
|
|
2063
|
-
funded = [c for c in ranking.candidates if c.funding != "unfunded"]
|
|
2064
|
-
pool = funded or ranking.candidates
|
|
2065
|
-
headroom = consumption_headroom(user_context_text or "")
|
|
2066
|
-
lam, k = ranking.lam, ranking.k_points_per_decade
|
|
2067
|
-
settings = effort_settings(pool, base, headroom, lam=lam, k=k, catalog=cat, benchmarks=bench)
|
|
2068
2090
|
composite = with_composites(bench)
|
|
2069
2091
|
scales = (
|
|
2070
2092
|
_evidence_scale(cat, composite, CATEGORY_EVIDENCE[base.category]),
|
|
2071
2093
|
_evidence_scale(cat, composite, CATEGORY_EVIDENCE["planning"]),
|
|
2072
2094
|
)
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2095
|
+
return _Pick(
|
|
2096
|
+
picked, _scorer_level(effort), ranking, base, models, bench, composite, scales, cat
|
|
2097
|
+
)
|
|
2098
|
+
|
|
2099
|
+
|
|
2100
|
+
def backup_for_pick(
|
|
2101
|
+
task: Task,
|
|
2102
|
+
user_context_text: str,
|
|
2103
|
+
model: str,
|
|
2104
|
+
effort: str,
|
|
2105
|
+
*,
|
|
2106
|
+
unavailable_models: list[str] | None = None,
|
|
2107
|
+
catalog: dict[str, Any] | None = None,
|
|
2108
|
+
benchmarks: dict[str, Any] | None = None,
|
|
2109
|
+
) -> Backup | None:
|
|
2110
|
+
"""The backup for a pick made outside the ladder (a roadmap step's
|
|
2111
|
+
Settings): ``model`` (catalog id or name) run at ``effort`` (a scorer or
|
|
2112
|
+
native level), backed up by the rule a /recommend rung follows
|
|
2113
|
+
(:func:`_backup_for`), over the same pool, so the two never disagree.
|
|
2114
|
+
``task.budget`` sets the posture an unmeasured backup's effort follows.
|
|
2115
|
+
None when the pool holds no other maker's model. ValueError when the
|
|
2116
|
+
model is not one this user-context can reach."""
|
|
2117
|
+
p = _pick(task, user_context_text, model, effort, unavailable_models, catalog, benchmarks)
|
|
2118
|
+
funded = [c for c in p.ranking.candidates if c.funding != "unfunded"]
|
|
2119
|
+
pool = funded or p.ranking.candidates
|
|
2120
|
+
headroom = consumption_headroom(user_context_text or "")
|
|
2121
|
+
settings = effort_settings(
|
|
2122
|
+
pool,
|
|
2123
|
+
p.base,
|
|
2124
|
+
headroom,
|
|
2125
|
+
lam=p.ranking.lam,
|
|
2126
|
+
k=p.ranking.k_points_per_decade,
|
|
2127
|
+
catalog=p.catalog,
|
|
2128
|
+
benchmarks=p.bench,
|
|
2077
2129
|
)
|
|
2130
|
+
model_id = p.candidate.model_id
|
|
2131
|
+
at = p.at(p.candidate, p.level if p.measured(model_id) else None)
|
|
2078
2132
|
point = FrontierPoint(
|
|
2079
|
-
at, _evidence(bench, model_id, FRONTIER_INDEX, at.evidence_level), frontier_price(at)
|
|
2133
|
+
at, _evidence(p.bench, model_id, FRONTIER_INDEX, at.evidence_level), frontier_price(at)
|
|
2080
2134
|
)
|
|
2081
2135
|
tier = next(t for t, b in TIER_BUDGET.items() if b == task.budget)
|
|
2082
2136
|
return _backup_for(
|
|
2083
|
-
Rung(tier, point, level),
|
|
2137
|
+
Rung(tier, point, p.level),
|
|
2138
|
+
settings,
|
|
2139
|
+
task,
|
|
2140
|
+
headroom,
|
|
2141
|
+
p.bench,
|
|
2142
|
+
p.catalog,
|
|
2143
|
+
_aa_band(p.bench, p.catalog),
|
|
2084
2144
|
)
|
|
2085
2145
|
|
|
2086
2146
|
|
|
2147
|
+
def _series(model_id: str) -> frozenset[str]:
|
|
2148
|
+
"""A model id's name words, its version numbers and the vendor prefix
|
|
2149
|
+
aside: `gpt-6-sol` and `gpt-6.1-sol` are one series, `gpt-6-sol` and
|
|
2150
|
+
`gpt-6-luna` two. Mirrors update/rating_guard.py same_series."""
|
|
2151
|
+
return frozenset(re.findall(r"[a-z]+", model_id.lower())) - {"claude"}
|
|
2152
|
+
|
|
2153
|
+
|
|
2154
|
+
def _version(model_id: str) -> tuple[int, ...]:
|
|
2155
|
+
return tuple(int(n) for n in re.findall(r"\d+", model_id))
|
|
2156
|
+
|
|
2157
|
+
|
|
2158
|
+
@dataclass(frozen=True)
|
|
2159
|
+
class Successor:
|
|
2160
|
+
"""A newer version of a pick's model that runs in its place
|
|
2161
|
+
(:func:`successor_for_pick`)."""
|
|
2162
|
+
|
|
2163
|
+
candidate: Candidate # the newer model, read at the pick's effort
|
|
2164
|
+
effort: str # the scorer's level
|
|
2165
|
+
native: str | None
|
|
2166
|
+
replaces: str # the pick's model name
|
|
2167
|
+
|
|
2168
|
+
@property
|
|
2169
|
+
def level(self) -> str:
|
|
2170
|
+
return self.native or self.effort
|
|
2171
|
+
|
|
2172
|
+
|
|
2173
|
+
def successor_for_pick(
|
|
2174
|
+
task: Task,
|
|
2175
|
+
user_context_text: str,
|
|
2176
|
+
model: str,
|
|
2177
|
+
effort: str,
|
|
2178
|
+
*,
|
|
2179
|
+
unavailable_models: list[str] | None = None,
|
|
2180
|
+
catalog: dict[str, Any] | None = None,
|
|
2181
|
+
benchmarks: dict[str, Any] | None = None,
|
|
2182
|
+
) -> Successor | None:
|
|
2183
|
+
"""The newer version of ``model`` a pick made outside the ladder should run
|
|
2184
|
+
instead: the same maker's next release in the same series (GPT-6 Sol →
|
|
2185
|
+
GPT-6.1 Sol), funded on the pick's platform, no dearer per token, and, run
|
|
2186
|
+
at ``effort``, level with it in the task's category (:func:`_level_with`)
|
|
2187
|
+
and at least as high on the AA Intelligence Index. The newest such version;
|
|
2188
|
+
None when there is none. ValueError when ``model`` is not one this
|
|
2189
|
+
user-context can reach.
|
|
2190
|
+
|
|
2191
|
+
The selector reads each model's letters at its top effort, and a step that
|
|
2192
|
+
runs lower can reverse them: GPT-6 Sol letters S in coding and GPT-6.1 Sol
|
|
2193
|
+
A on a 3-point SciCode gap at max, yet at medium 6.1 Sol leads it on
|
|
2194
|
+
Terminal-Bench 4.0 by 29 points at the same price. The two are compared
|
|
2195
|
+
at ``effort`` where AA has measured both there, else on their headline
|
|
2196
|
+
rows, so neither is credited with a run the other is not."""
|
|
2197
|
+
p = _pick(task, user_context_text, model, effort, unavailable_models, catalog, benchmarks)
|
|
2198
|
+
old = p.candidate
|
|
2199
|
+
series, version = _series(old.model_id), _version(old.model_id)
|
|
2200
|
+
best: Successor | None = None
|
|
2201
|
+
for c in p.ranking.candidates:
|
|
2202
|
+
if (
|
|
2203
|
+
c.platform_id != old.platform_id
|
|
2204
|
+
or c.provider != old.provider
|
|
2205
|
+
or _series(c.model_id) != series
|
|
2206
|
+
or _version(c.model_id) <= version
|
|
2207
|
+
or c.blended_price_usd > old.blended_price_usd
|
|
2208
|
+
):
|
|
2209
|
+
continue
|
|
2210
|
+
both = p.measured(old.model_id) and p.measured(c.model_id)
|
|
2211
|
+
level = p.level if both else None
|
|
2212
|
+
was, now = p.at(old, level), p.at(c, level)
|
|
2213
|
+
aa_was = _evidence(p.bench, old.model_id, FRONTIER_INDEX, level)
|
|
2214
|
+
aa_now = _evidence(p.bench, c.model_id, FRONTIER_INDEX, level)
|
|
2215
|
+
if not _level_with(now, was, task.category):
|
|
2216
|
+
continue
|
|
2217
|
+
if aa_now is None or (aa_was is not None and aa_now < aa_was):
|
|
2218
|
+
continue
|
|
2219
|
+
if best is not None and (
|
|
2220
|
+
_version(c.model_id),
|
|
2221
|
+
-c.blended_price_usd,
|
|
2222
|
+
) <= (_version(best.candidate.model_id), -best.candidate.blended_price_usd):
|
|
2223
|
+
continue
|
|
2224
|
+
native = native_level(c.platform_id, c.model_id, p.level, catalog=p.catalog)
|
|
2225
|
+
best = Successor(now, p.level, native, old.model_name)
|
|
2226
|
+
return best
|
|
2227
|
+
|
|
2228
|
+
|
|
2087
2229
|
# A platform's effort dial as a Settings table names it (docs/settings-display.md):
|
|
2088
2230
|
# Intelligence on OpenAI's reasoning surfaces, Effort elsewhere; Cursor has none.
|
|
2089
2231
|
_DIAL_NAMES: Final[dict[str, str | None]] = {
|
|
@@ -2103,9 +2245,10 @@ _LEVEL_WORDS: Final[dict[str, str]] = {
|
|
|
2103
2245
|
}
|
|
2104
2246
|
|
|
2105
2247
|
|
|
2106
|
-
def backup_cell(backup: Backup) -> str:
|
|
2248
|
+
def backup_cell(backup: Backup | Successor) -> str:
|
|
2107
2249
|
"""``backup`` as a roadmap Settings table's Backup cell:
|
|
2108
|
-
``GPT-6.1 Sol — Codex · Intelligence XHigh``.
|
|
2250
|
+
``GPT-6.1 Sol — Codex · Intelligence XHigh``. A :class:`Successor` reads
|
|
2251
|
+
the same way, as the model the step runs."""
|
|
2109
2252
|
c = backup.candidate
|
|
2110
2253
|
dial = _DIAL_NAMES.get(c.platform_id, "Effort")
|
|
2111
2254
|
level = _LEVEL_WORDS.get(normalize_level(backup.level), backup.level)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|