roadmodel 0.2.68__tar.gz → 0.2.69__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 (55) hide show
  1. {roadmodel-0.2.68 → roadmodel-0.2.69}/PKG-INFO +1 -1
  2. {roadmodel-0.2.68 → roadmodel-0.2.69}/pyproject.toml +1 -1
  3. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/__init__.py +1 -1
  4. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/scoring.py +27 -10
  5. {roadmodel-0.2.68 → roadmodel-0.2.69}/.gitignore +0 -0
  6. {roadmodel-0.2.68 → roadmodel-0.2.69}/LICENSE +0 -0
  7. {roadmodel-0.2.68 → roadmodel-0.2.69}/NOTICE +0 -0
  8. {roadmodel-0.2.68 → roadmodel-0.2.69}/README.md +0 -0
  9. {roadmodel-0.2.68 → roadmodel-0.2.69}/docs/benchmarks.json +0 -0
  10. {roadmodel-0.2.68 → roadmodel-0.2.69}/docs/catalog.json +0 -0
  11. {roadmodel-0.2.68 → roadmodel-0.2.69}/docs/model-selector.txt +0 -0
  12. {roadmodel-0.2.68 → roadmodel-0.2.69}/docs/model-tier-cost-scale.md +0 -0
  13. {roadmodel-0.2.68 → roadmodel-0.2.69}/docs/settings-display.md +0 -0
  14. {roadmodel-0.2.68 → roadmodel-0.2.69}/docs/templates/phase-roadmap-template.md +0 -0
  15. {roadmodel-0.2.68 → roadmodel-0.2.69}/docs/templates/planning-kit-how-to-use.md +0 -0
  16. {roadmodel-0.2.68 → roadmodel-0.2.69}/docs/templates/project-roadmap-template.md +0 -0
  17. {roadmodel-0.2.68 → roadmodel-0.2.69}/docs/templates/prompt-phase-roadmap.md +0 -0
  18. {roadmodel-0.2.68 → roadmodel-0.2.69}/docs/templates/prompt-project-roadmap.md +0 -0
  19. {roadmodel-0.2.68 → roadmodel-0.2.69}/docs/user-context.example.md +0 -0
  20. {roadmodel-0.2.68 → roadmodel-0.2.69}/hatch_build.py +0 -0
  21. {roadmodel-0.2.68 → roadmodel-0.2.69}/infra/README.md +0 -0
  22. {roadmodel-0.2.68 → roadmodel-0.2.69}/infra/gcp-killswitch/README.md +0 -0
  23. {roadmodel-0.2.68 → roadmodel-0.2.69}/infra/supabase/README.md +0 -0
  24. {roadmodel-0.2.68 → roadmodel-0.2.69}/roadmodel/data/benchmarks.json +0 -0
  25. {roadmodel-0.2.68 → roadmodel-0.2.69}/roadmodel/data/catalog.json +0 -0
  26. {roadmodel-0.2.68 → roadmodel-0.2.69}/roadmodel/data/model-selector.txt +0 -0
  27. {roadmodel-0.2.68 → roadmodel-0.2.69}/roadmodel/data/model-tier-cost-scale.md +0 -0
  28. {roadmodel-0.2.68 → roadmodel-0.2.69}/roadmodel/data/phase-roadmap-template.md +0 -0
  29. {roadmodel-0.2.68 → roadmodel-0.2.69}/roadmodel/data/planning-kit-how-to-use.md +0 -0
  30. {roadmodel-0.2.68 → roadmodel-0.2.69}/roadmodel/data/project-roadmap-template.md +0 -0
  31. {roadmodel-0.2.68 → roadmodel-0.2.69}/roadmodel/data/prompt-phase-roadmap.md +0 -0
  32. {roadmodel-0.2.68 → roadmodel-0.2.69}/roadmodel/data/prompt-project-roadmap.md +0 -0
  33. {roadmodel-0.2.68 → roadmodel-0.2.69}/roadmodel/data/settings-display.md +0 -0
  34. {roadmodel-0.2.68 → roadmodel-0.2.69}/roadmodel/data/user-context.example.md +0 -0
  35. {roadmodel-0.2.68 → roadmodel-0.2.69}/scripts/agent_context.py +0 -0
  36. {roadmodel-0.2.68 → roadmodel-0.2.69}/scripts/agent_rules.py +0 -0
  37. {roadmodel-0.2.68 → roadmodel-0.2.69}/scripts/update_projects.py +0 -0
  38. {roadmodel-0.2.68 → roadmodel-0.2.69}/scripts/updater_lock.py +0 -0
  39. {roadmodel-0.2.68 → roadmodel-0.2.69}/scripts/upgrade_fleet.py +0 -0
  40. {roadmodel-0.2.68 → roadmodel-0.2.69}/scripts/worktree_memory.py +0 -0
  41. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/__main__.py +0 -0
  42. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/cli.py +0 -0
  43. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/config.py +0 -0
  44. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/cost.py +0 -0
  45. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/errors.py +0 -0
  46. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/mcp_server.py +0 -0
  47. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/providers/__init__.py +0 -0
  48. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/providers/anthropic.py +0 -0
  49. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/providers/google.py +0 -0
  50. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/providers/openai.py +0 -0
  51. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/providers/openai_compatible.py +0 -0
  52. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/providers/registry.py +0 -0
  53. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/recommend.py +0 -0
  54. {roadmodel-0.2.68 → roadmodel-0.2.69}/src/roadmodel/usage.py +0 -0
  55. {roadmodel-0.2.68 → roadmodel-0.2.69}/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.68
3
+ Version: 0.2.69
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "roadmodel"
7
- version = "0.2.68"
7
+ version = "0.2.69"
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,2 +1,2 @@
1
1
  # src/roadmodel/__init__.py
2
- __version__ = "0.2.68"
2
+ __version__ = "0.2.69"
@@ -913,6 +913,10 @@ def rank(
913
913
  pj = str(method.get("provider_jurisdiction", "us")).lower()
914
914
  if pj != "local" and pj not in juris:
915
915
  continue # `local` runs on the operator's hardware: passes every list
916
+ if task.category == "speed" and str(method.get("billing", "")) == "local":
917
+ # Speed evidence is the hosted providers' measured throughput;
918
+ # the operator's own hardware runs at its own, unmeasured pace.
919
+ continue
916
920
  funding, state, scarcity, note = _funding_for(
917
921
  _cost._with_model(method, model_id),
918
922
  cat,
@@ -1210,9 +1214,12 @@ def ladder(
1210
1214
  category stands alone.
1211
1215
  - QUALITY is the adequate point that scores highest in the category (then
1212
1216
  the higher AA Index, then the cheaper). COST is the cheapest adequate
1213
- point. BALANCED is the point strictly between them with the best score
1214
- at the balanced posture; with none between, the COST or QUALITY model at
1215
- the balanced posture's effort, whichever differs from both other rungs.
1217
+ point the operator has already paid for (a subscription with headroom,
1218
+ or local weights); a per-token point takes COST only when no prepaid
1219
+ point is adequate. BALANCED is the point strictly between them, stronger
1220
+ than COST in the task's category, with the best score at the balanced
1221
+ posture; with none, the COST or QUALITY model at the balanced posture's
1222
+ effort, whichever differs from both other rungs.
1216
1223
  - Each rung runs at :func:`effort_for` its posture (cheap / balanced /
1217
1224
  best), set on its platform's own dial (:func:`native_level`) where the
1218
1225
  catalog documents one. A lower rung on the same model and platform as
@@ -1265,24 +1272,34 @@ def ladder(
1265
1272
  def same(a: Rung, b: Rung) -> bool:
1266
1273
  return a.candidate.model_id == b.candidate.model_id and a.level == b.level
1267
1274
 
1268
- cost_rung = rung("cost", span[0])
1275
+ # COST is the cheapest adequate point already paid for: a subscription pool
1276
+ # with headroom or local weights (scarcity below list price) costs the
1277
+ # operator nothing new, while a per-token call is fresh spend.
1278
+ cost_point = next((p for p in span if p.candidate.scarcity < 1.0), span[0])
1279
+ lo = span.index(cost_point)
1280
+ cost_rung = rung("cost", cost_point)
1269
1281
  quality_rung = (
1270
1282
  replace(rung("quality", specialist), specialist=True)
1271
1283
  if specialist is not None
1272
1284
  else rung("quality", top)
1273
1285
  )
1274
1286
  # With a specialist on QUALITY, BALANCED spans every adequate frontier point
1275
- # above COST, the frontier's strongest included.
1276
- between = span[1:] if specialist is not None else span[1:-1]
1287
+ # above COST, the frontier's strongest included. A BALANCED point earns its
1288
+ # higher price only by beating COST in the task's own category: the
1289
+ # frontier is drawn on the AA Index, and a point above COST on that axis
1290
+ # can still trail it in the category the task needs.
1291
+ between = span[lo + 1 :] if specialist is not None else span[lo + 1 : -1]
1292
+ between = [p for p in between if p.candidate.quality > cost_point.candidate.quality]
1277
1293
  if between:
1278
1294
  balanced_rung = rung(
1279
1295
  "balanced", max(between, key=lambda p: (p.candidate.score, -p.price_usd))
1280
1296
  )
1281
1297
  else:
1282
- # Two adequate points or one: BALANCED runs one of them at the balanced
1283
- # posture, whichever differs from both other rungs; it converges only
1284
- # when neither does. Beside a specialist it stays on the frontier.
1285
- options = [rung("balanced", span[0])]
1298
+ # No point between COST and QUALITY beats COST in the category:
1299
+ # BALANCED runs one of those two at the balanced posture, whichever
1300
+ # differs from both other rungs; it converges only when neither does.
1301
+ # Beside a specialist it stays on the frontier.
1302
+ options = [rung("balanced", cost_point)]
1286
1303
  if specialist is None:
1287
1304
  options.append(rung("balanced", top))
1288
1305
  balanced_rung = next(
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes