roadmodel 0.2.73__tar.gz → 0.2.74__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.
Files changed (57) hide show
  1. {roadmodel-0.2.73 → roadmodel-0.2.74}/PKG-INFO +1 -1
  2. {roadmodel-0.2.73/roadmodel/data → roadmodel-0.2.74/docs}/catalog.json +1 -1
  3. {roadmodel-0.2.73 → roadmodel-0.2.74}/pyproject.toml +1 -1
  4. {roadmodel-0.2.73/docs → roadmodel-0.2.74/roadmodel/data}/catalog.json +1 -1
  5. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/__init__.py +1 -1
  6. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/recommend.py +5 -1
  7. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/scoring.py +50 -7
  8. {roadmodel-0.2.73 → roadmodel-0.2.74}/.gitignore +0 -0
  9. {roadmodel-0.2.73 → roadmodel-0.2.74}/LICENSE +0 -0
  10. {roadmodel-0.2.73 → roadmodel-0.2.74}/NOTICE +0 -0
  11. {roadmodel-0.2.73 → roadmodel-0.2.74}/README.md +0 -0
  12. {roadmodel-0.2.73 → roadmodel-0.2.74}/docs/benchmarks.json +0 -0
  13. {roadmodel-0.2.73 → roadmodel-0.2.74}/docs/context-windows.json +0 -0
  14. {roadmodel-0.2.73 → roadmodel-0.2.74}/docs/model-selector.txt +0 -0
  15. {roadmodel-0.2.73 → roadmodel-0.2.74}/docs/model-tier-cost-scale.md +0 -0
  16. {roadmodel-0.2.73 → roadmodel-0.2.74}/docs/settings-display.md +0 -0
  17. {roadmodel-0.2.73 → roadmodel-0.2.74}/docs/templates/phase-roadmap-template.md +0 -0
  18. {roadmodel-0.2.73 → roadmodel-0.2.74}/docs/templates/planning-kit-how-to-use.md +0 -0
  19. {roadmodel-0.2.73 → roadmodel-0.2.74}/docs/templates/project-roadmap-template.md +0 -0
  20. {roadmodel-0.2.73 → roadmodel-0.2.74}/docs/templates/prompt-phase-roadmap.md +0 -0
  21. {roadmodel-0.2.73 → roadmodel-0.2.74}/docs/templates/prompt-project-roadmap.md +0 -0
  22. {roadmodel-0.2.73 → roadmodel-0.2.74}/docs/user-context.example.md +0 -0
  23. {roadmodel-0.2.73 → roadmodel-0.2.74}/hatch_build.py +0 -0
  24. {roadmodel-0.2.73 → roadmodel-0.2.74}/infra/README.md +0 -0
  25. {roadmodel-0.2.73 → roadmodel-0.2.74}/infra/gcp-killswitch/README.md +0 -0
  26. {roadmodel-0.2.73 → roadmodel-0.2.74}/infra/supabase/README.md +0 -0
  27. {roadmodel-0.2.73 → roadmodel-0.2.74}/roadmodel/data/benchmarks.json +0 -0
  28. {roadmodel-0.2.73 → roadmodel-0.2.74}/roadmodel/data/context-windows.json +0 -0
  29. {roadmodel-0.2.73 → roadmodel-0.2.74}/roadmodel/data/model-selector.txt +0 -0
  30. {roadmodel-0.2.73 → roadmodel-0.2.74}/roadmodel/data/model-tier-cost-scale.md +0 -0
  31. {roadmodel-0.2.73 → roadmodel-0.2.74}/roadmodel/data/phase-roadmap-template.md +0 -0
  32. {roadmodel-0.2.73 → roadmodel-0.2.74}/roadmodel/data/planning-kit-how-to-use.md +0 -0
  33. {roadmodel-0.2.73 → roadmodel-0.2.74}/roadmodel/data/project-roadmap-template.md +0 -0
  34. {roadmodel-0.2.73 → roadmodel-0.2.74}/roadmodel/data/prompt-phase-roadmap.md +0 -0
  35. {roadmodel-0.2.73 → roadmodel-0.2.74}/roadmodel/data/prompt-project-roadmap.md +0 -0
  36. {roadmodel-0.2.73 → roadmodel-0.2.74}/roadmodel/data/settings-display.md +0 -0
  37. {roadmodel-0.2.73 → roadmodel-0.2.74}/roadmodel/data/user-context.example.md +0 -0
  38. {roadmodel-0.2.73 → roadmodel-0.2.74}/scripts/agent_context.py +0 -0
  39. {roadmodel-0.2.73 → roadmodel-0.2.74}/scripts/agent_rules.py +0 -0
  40. {roadmodel-0.2.73 → roadmodel-0.2.74}/scripts/update_projects.py +0 -0
  41. {roadmodel-0.2.73 → roadmodel-0.2.74}/scripts/updater_lock.py +0 -0
  42. {roadmodel-0.2.73 → roadmodel-0.2.74}/scripts/upgrade_fleet.py +0 -0
  43. {roadmodel-0.2.73 → roadmodel-0.2.74}/scripts/worktree_memory.py +0 -0
  44. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/__main__.py +0 -0
  45. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/cli.py +0 -0
  46. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/config.py +0 -0
  47. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/cost.py +0 -0
  48. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/errors.py +0 -0
  49. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/mcp_server.py +0 -0
  50. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/providers/__init__.py +0 -0
  51. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/providers/anthropic.py +0 -0
  52. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/providers/google.py +0 -0
  53. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/providers/openai.py +0 -0
  54. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/providers/openai_compatible.py +0 -0
  55. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/providers/registry.py +0 -0
  56. {roadmodel-0.2.73 → roadmodel-0.2.74}/src/roadmodel/usage.py +0 -0
  57. {roadmodel-0.2.73 → roadmodel-0.2.74}/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.73
3
+ Version: 0.2.74
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": "4",
3
3
  "output_contract_version": 2,
4
- "generated_at_utc": "2026-10-07T19:13:30Z",
4
+ "generated_at_utc": "2026-10-07T20:14:28Z",
5
5
  "source_doc_sha256": {
6
6
  "model-selector.txt": "6a75e2265459fa7302cb80d0faf5ec2aad575034fc29866238cd1579c001cc2d",
7
7
  "model-tier-cost-scale.md": "2b3b0eb82237eb5cb5bf468a3fe971bcaf0160ad7d0bc700698d2c16640bc41d"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "roadmodel"
7
- version = "0.2.73"
7
+ version = "0.2.74"
8
8
  description = "BYO-key CLI that recommends the right AI model, platform, and settings for a prompt."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": "4",
3
3
  "output_contract_version": 2,
4
- "generated_at_utc": "2026-10-07T19:13:30Z",
4
+ "generated_at_utc": "2026-10-07T20:14:28Z",
5
5
  "source_doc_sha256": {
6
6
  "model-selector.txt": "6a75e2265459fa7302cb80d0faf5ec2aad575034fc29866238cd1579c001cc2d",
7
7
  "model-tier-cost-scale.md": "2b3b0eb82237eb5cb5bf468a3fe971bcaf0160ad7d0bc700698d2c16640bc41d"
@@ -1,2 +1,2 @@
1
1
  # src/roadmodel/__init__.py
2
- __version__ = "0.2.73"
2
+ __version__ = "0.2.74"
@@ -479,7 +479,11 @@ _SAAS_LADDER_TABLE_HEADER: Final = (
479
479
  "1. One line `CLASSIFICATION: <category> / <complexity> / <novel|routine>`. "
480
480
  "<category> is the task's PRIMARY category (coding, planning, agentic, multimodal, "
481
481
  "long-context, knowledge or speed) per Step 1 of the selection algorithm; "
482
- "<complexity> is low, medium or high per its Step 2. `novel` marks "
482
+ "<complexity> is low, medium or high per its Step 2. High-volume routine work "
483
+ "(classifying, tagging, extracting or summarizing many similar items) is `speed` "
484
+ "whatever accuracy it needs, and its complexity is how hard ONE item is. Building an "
485
+ "agent or an app (writing its code) is `coding`; running one, where the model itself "
486
+ "acts through tools in a loop, is `agentic`. `novel` marks "
483
487
  "research-grade work only: an open problem, a new algorithm, or a proof of a "
484
488
  "result with no known proof. Writing out a known result (a textbook proof, a "
485
489
  "standard derivation) is `routine` however many steps it takes, and its "
@@ -961,10 +961,20 @@ def rank(
961
961
  catalog: dict[str, Any] | None = None,
962
962
  benchmarks: dict[str, Any] | None = None,
963
963
  top: int | None = None,
964
+ effort_aware: bool = True,
964
965
  ) -> Ranking:
965
- """Rank every reachable (model, platform) pair for ``task``."""
966
+ """Rank every reachable (model, platform) pair for ``task``.
967
+
968
+ With ``effort_aware`` (the default), a model AA has measured at several
969
+ efforts stands at the one that scores best for the task's posture, among
970
+ those it may run (:func:`effort_settings`), its quality, requirement
971
+ penalty (in the category and in general capability) and cost read there;
972
+ a model measured only at efforts the task does not run is left out. Off,
973
+ every model stands on its headline row at its posture's effort: the pool
974
+ :func:`ladder` expands itself."""
966
975
  cat = catalog if catalog is not None else _cost._load_catalog()
967
- bench = with_composites(benchmarks if benchmarks is not None else _load_benchmarks())
976
+ raw_bench = benchmarks if benchmarks is not None else _load_benchmarks()
977
+ bench = with_composites(raw_bench)
968
978
  text = user_context_text or ""
969
979
  headroom = consumption_headroom(text)
970
980
  pools = pool_states(text)
@@ -1100,6 +1110,28 @@ def rank(
1100
1110
  continue
1101
1111
  candidates.append(best_for_model)
1102
1112
 
1113
+ if effort_aware and candidates:
1114
+ settings = effort_settings(
1115
+ candidates, task, headroom, lam=lam, k=k, catalog=cat, benchmarks=raw_bench
1116
+ )
1117
+ best: dict[str, Candidate] = {}
1118
+ for c in settings:
1119
+ held = best.get(c.model_id)
1120
+ if held is None or (c.score, -c.effective_cost_usd) > (
1121
+ held.score,
1122
+ -held.effective_cost_usd,
1123
+ ):
1124
+ best[c.model_id] = c
1125
+ for c in candidates:
1126
+ if c.model_id not in best:
1127
+ excluded.append(
1128
+ {
1129
+ "model": c.model_id,
1130
+ "reason": "measured only at efforts this task does not run",
1131
+ }
1132
+ )
1133
+ candidates = [best[c.model_id] for c in candidates if c.model_id in best]
1134
+
1103
1135
  # Funded first, then score, then cheaper, then id — deterministic.
1104
1136
  candidates.sort(
1105
1137
  key=lambda c: (c.funding == "unfunded", -c.score, c.effective_cost_usd, c.model_id)
@@ -1404,7 +1436,8 @@ def _at_level(
1404
1436
  task: Task,
1405
1437
  bench: dict[str, Any],
1406
1438
  scales: tuple[EvidenceScale | None, EvidenceScale | None],
1407
- ranking: Ranking,
1439
+ lam: float,
1440
+ k: float,
1408
1441
  ) -> Candidate:
1409
1442
  """``c`` run at ``level`` (None: its headline row, at the posture's
1410
1443
  effort): quality from AA's row at that effort, and the requirement
@@ -1434,7 +1467,7 @@ def _at_level(
1434
1467
  mult = EFFORT_TOKEN_MULTIPLIER[level]
1435
1468
  eff_cost = c.blended_price_usd * c.scarcity * mult
1436
1469
  decades = math.log10(max(eff_cost, COST_FLOOR_USD) / COST_FLOOR_USD)
1437
- cost_pen = ranking.lam * ranking.k_points_per_decade * decades
1470
+ cost_pen = lam * k * decades
1438
1471
  nudge = 0.5 if task.category in AGENT_CATEGORIES and c.platform_id in AGENT_SURFACES else 0.0
1439
1472
  return replace(
1440
1473
  c,
@@ -1457,8 +1490,9 @@ def effort_settings(
1457
1490
  pool: list[Candidate],
1458
1491
  task: Task,
1459
1492
  headroom: str,
1460
- ranking: Ranking,
1461
1493
  *,
1494
+ lam: float,
1495
+ k: float,
1462
1496
  catalog: dict[str, Any],
1463
1497
  benchmarks: dict[str, Any],
1464
1498
  ) -> list[Candidate]:
@@ -1493,7 +1527,7 @@ def effort_settings(
1493
1527
  if headroom == "uncapped" and c.scarcity == 0:
1494
1528
  levels = levels[-1:] # a free path runs at its top effort
1495
1529
  out.extend(
1496
- _at_level(c, lv, model, task, bench, scales, ranking) for lv in (levels or [None])
1530
+ _at_level(c, lv, model, task, bench, scales, lam, k) for lv in (levels or [None])
1497
1531
  )
1498
1532
  return out
1499
1533
 
@@ -1558,6 +1592,7 @@ def ladder(
1558
1592
  unavailable_models=unavailable_models,
1559
1593
  catalog=cat,
1560
1594
  benchmarks=bench,
1595
+ effort_aware=False,
1561
1596
  )
1562
1597
  funded = [c for c in ranking.candidates if c.funding != "unfunded"]
1563
1598
  pool = funded or ranking.candidates
@@ -1567,7 +1602,15 @@ def ladder(
1567
1602
  headroom = consumption_headroom(user_context_text or "")
1568
1603
  # The picks are read off the same frontier drawn over each model at every
1569
1604
  # effort AA measured it at, so an effort is part of the pick it prices.
1570
- settings = effort_settings(pool, base, headroom, ranking, catalog=cat, benchmarks=bench)
1605
+ settings = effort_settings(
1606
+ pool,
1607
+ base,
1608
+ headroom,
1609
+ lam=ranking.lam,
1610
+ k=ranking.k_points_per_decade,
1611
+ catalog=cat,
1612
+ benchmarks=bench,
1613
+ )
1571
1614
  # Every model AA measured only at efforts this task does not run leaves
1572
1615
  # none: the picks then fall back to the models' frontier.
1573
1616
  points = frontier(settings, bench) or front
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes