cn0303 commited on
Commit
de063cc
·
verified ·
1 Parent(s): 71ca3cd

Fix buy advice: Minimum and Comfortable are now distinct builds

Browse files
Files changed (3) hide show
  1. engine/real_advisor.py +25 -9
  2. static/app.js +4 -0
  3. static/style.css +2 -0
engine/real_advisor.py CHANGED
@@ -791,8 +791,16 @@ def min_specs(usecases, mode: str = "run") -> dict:
791
  return advise_real({"usecase": uc.key}, spec)
792
 
793
  def walk(tiers):
794
- minimum = comfortable = None
795
- for label, price, hw, spec_line, detail in tiers:
 
 
 
 
 
 
 
 
796
  spec = _spec_for_tier(hw)
797
  per_goal, all_meet, all_great = [], True, True
798
  for uc in ucs:
@@ -806,13 +814,21 @@ def min_specs(usecases, mode: str = "run") -> dict:
806
  "goals": per_goal,
807
  "runs": "; ".join(f"{g['goal']}: {g['model']}" for g in per_goal)
808
  if len(per_goal) > 1 else per_goal[0]["model"]}
809
- if minimum is None and all_meet:
810
- minimum = tier
811
- if comfortable is None and all_great:
812
- comfortable = tier
813
- if minimum and comfortable:
814
- break
815
- return minimum, comfortable
 
 
 
 
 
 
 
 
816
 
817
  platforms = []
818
  for p in _PLATFORMS:
 
791
  return advise_real({"usecase": uc.key}, spec)
792
 
793
  def walk(tiers):
794
+ # Score every tier once, then choose two DISTINCT builds:
795
+ # minimum = cheapest tier that genuinely works (great or tight).
796
+ # comfortable = the next step UP that adds real headroom (prefers a
797
+ # tier that runs everything great; falls back to the next
798
+ # one that at least works). It is always strictly above
799
+ # the minimum, so the two are never the same build -- the
800
+ # old logic collapsed them whenever the cheapest tier
801
+ # already ran everything great.
802
+ built = []
803
+ for i, (label, price, hw, spec_line, detail) in enumerate(tiers):
804
  spec = _spec_for_tier(hw)
805
  per_goal, all_meet, all_great = [], True, True
806
  for uc in ucs:
 
814
  "goals": per_goal,
815
  "runs": "; ".join(f"{g['goal']}: {g['model']}" for g in per_goal)
816
  if len(per_goal) > 1 else per_goal[0]["model"]}
817
+ built.append({"idx": i, "tier": tier, "all_meet": all_meet,
818
+ "all_great": all_great})
819
+
820
+ mn = next((b for b in built if b["all_meet"]), None)
821
+ if mn is None:
822
+ return None, None
823
+ # Comfortable: cheapest tier strictly above the minimum that runs all
824
+ # goals great; else the next one above that at least works.
825
+ cf = (next((b for b in built if b["idx"] > mn["idx"] and b["all_great"]), None)
826
+ or next((b for b in built if b["idx"] > mn["idx"] and b["all_meet"]), None))
827
+ if cf is None:
828
+ # Minimum is the top of this platform's ladder; say so honestly
829
+ # instead of repeating the same build as "comfortable".
830
+ return mn["tier"], {"ceiling": True, "label": mn["tier"]["label"]}
831
+ return mn["tier"], cf["tier"]
832
 
833
  platforms = []
834
  for p in _PLATFORMS:
static/app.js CHANGED
@@ -429,6 +429,10 @@ function renderBuy(d) {
429
  <div class="bc-head"><span class="bc-kind ${kind}">${kind === "min" ? "Minimum" : "Comfortable"}</span></div>
430
  <p class="bc-none">No build on this platform ${ft ? "trains" : "runs"} ${multi ? "all of these" : "this"}${ft ? "" : " well"}, it wants more than this ladder offers.</p>
431
  </div>`;
 
 
 
 
432
  const goalsHtml = multi
433
  ? `<ul class="bc-goals">${t.goals.map(g => `<li><span>${esc(g.goal)}</span><b>${esc(g.model)}</b>${g.verdict === "tight" ? " <i>(tight)</i>" : ""}</li>`).join("")}</ul>`
434
  : `<div class="bc-runs">${ft ? "Can train up to" : "Runs"} <b>${esc(t.runs)}</b>${t.goals[0] && t.goals[0].verdict === "tight" ? " <i>(tight)</i>" : ""}</div>`;
 
429
  <div class="bc-head"><span class="bc-kind ${kind}">${kind === "min" ? "Minimum" : "Comfortable"}</span></div>
430
  <p class="bc-none">No build on this platform ${ft ? "trains" : "runs"} ${multi ? "all of these" : "this"}${ft ? "" : " well"}, it wants more than this ladder offers.</p>
431
  </div>`;
432
+ if (t.ceiling) return `<div class="buy-card ceiling">
433
+ <div class="bc-head"><span class="bc-kind ${kind}">Comfortable</span><span class="ic" data-ic="check"></span></div>
434
+ <p class="bc-none">The minimum build is already the most capable ${ft ? "training " : ""}box on this platform for ${multi ? "these goals" : "this goal"}. Nothing bigger is needed here.</p>
435
+ </div>`;
436
  const goalsHtml = multi
437
  ? `<ul class="bc-goals">${t.goals.map(g => `<li><span>${esc(g.goal)}</span><b>${esc(g.model)}</b>${g.verdict === "tight" ? " <i>(tight)</i>" : ""}</li>`).join("")}</ul>`
438
  : `<div class="bc-runs">${ft ? "Can train up to" : "Runs"} <b>${esc(t.runs)}</b>${t.goals[0] && t.goals[0].verdict === "tight" ? " <i>(tight)</i>" : ""}</div>`;
static/style.css CHANGED
@@ -496,6 +496,8 @@ details.disc > summary:hover { color: var(--text-primary); }
496
  border-radius: var(--r-md); padding: var(--s-3) var(--s-4);
497
  }
498
  .buy-card.empty { opacity: .7; }
 
 
499
  .bc-head { display: flex; align-items: center; justify-content: space-between; }
500
  .bc-kind { font-family: var(--font-crt); font-size: 14px; font-weight: 400; text-transform: uppercase; letter-spacing: .08em; color: var(--text-muted); }
501
  .bc-kind.min { color: var(--warn); } .bc-kind.comfy { color: var(--ok); }
 
496
  border-radius: var(--r-md); padding: var(--s-3) var(--s-4);
497
  }
498
  .buy-card.empty { opacity: .7; }
499
+ .buy-card.ceiling { border-color: var(--accent-strong); }
500
+ .buy-card.ceiling .bc-head .ic { color: var(--accent); font-size: 16px; }
501
  .bc-head { display: flex; align-items: center; justify-content: space-between; }
502
  .bc-kind { font-family: var(--font-crt); font-size: 14px; font-weight: 400; text-transform: uppercase; letter-spacing: .08em; color: var(--text-muted); }
503
  .bc-kind.min { color: var(--warn); } .bc-kind.comfy { color: var(--ok); }