jeffrey1963 commited on
Commit
5cc57aa
Β·
verified Β·
1 Parent(s): c599a4d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +200 -26
app.py CHANGED
@@ -250,6 +250,80 @@ def compute_housing_per_acre(cost: float, acres: float) -> float:
250
  # Housing = 0.5% of cost, allocated per acre
251
  return 0.005 * float(cost) / max(float(acres), 1e-9)
252
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
  # ---- Keyword intent + Regex fallback ----
255
  NUM=r"[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?"
@@ -545,13 +619,28 @@ with gr.Blocks(css="footer {visibility: hidden}") as demo:
545
  gr.Markdown("### Student HUD β€” what Jerry sees")
546
  hud_box = gr.Textbox(label="Final Data (persists this session)", lines=18, value=_pp({k: None for k in HUD_FIELDS}))
547
  changed_box = gr.Textbox(label="Fields updated by last prompt", lines=3, value="(none)")
548
-
 
 
 
 
549
  # Optional: teacher debug accordion if you kept the teacher JSON earlier
550
  # (If you added a teacher debug already, you can leave this out or keep your version.)
551
  with gr.Accordion("Teacher debug β€” Parser & JSON (optional)", open=False):
552
  dbg = gr.Textbox(label="LLM/Regex + Raw/Final JSON + Diagnostics", lines=22)
553
 
554
  def _ask_with_hud(user_text, hud):
 
 
 
 
 
 
 
 
 
 
 
555
  # ---- Parse (LLM first, then regex) ----
556
  raw_llm = llm_parse(user_text)
557
  if raw_llm:
@@ -596,69 +685,154 @@ with gr.Blocks(css="footer {visibility: hidden}") as demo:
596
  rate = new_hud.get("rate")
597
  if rate is not None:
598
  rate = float(rate)
599
- if rate > 1.0: rate = rate / 100.0 # accept 8 or 8% as 0.08
600
  else:
601
  rate = 0.08
602
 
603
- # ---- Readiness check for chosen cost ----
604
  if not which:
605
  msg = "Jerry β†’ Tell me which cost (e.g., fuel, repair, depreciation). Check the HUD to see what I have so far."
606
  teacher_dbg = _pp({"mode": mode, "raw_llm": raw_llm, "raw_regex": raw_regex, "final_data": new_hud})
607
- return msg, _pp(new_hud), ", ".join(changed) or "(none)", teacher_dbg, new_hud
608
 
 
609
  missing = missing_for(which, new_hud)
610
  if missing:
611
  msg = f"Jerry β†’ Not enough data for {which}. Missing: {', '.join(missing)}. Check the HUD and add what's missing."
612
  teacher_dbg = _pp({"mode": mode, "raw_llm": raw_llm, "raw_regex": raw_regex, "final_data": new_hud, "missing": missing})
613
- return msg, _pp(new_hud), ", ".join(changed) or "(none)", teacher_dbg, new_hud
614
-
615
- # ---- Compute (now that HUD is complete for this cost) ----
616
- salv_frac, salvage, salvage_class = salvage_parts(equip, cost, hp, acres, speed, life)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
617
  if which == "repair_per_acre_year":
618
  r = compute_repair_per_acre_year(equip, cost, life, acres, speed)
619
  ans = f"Jerry β†’ REPAIR per acre-year: fraction={r['fraction']:.3f}, value=${r['repair_per_acre_year']:.2f}/ac/yr (mode={mode})"
 
 
 
 
 
 
 
 
 
620
  elif which == "fuel_per_acre":
621
  f = compute_fuel_per_acre(hp, diesel, speed)
622
  ans = f"Jerry β†’ FUEL per acre=${f:.2f} (mode={mode})"
 
 
 
 
 
623
  elif which == "lube_per_acre":
624
  f = compute_fuel_per_acre(hp, diesel, speed)
625
  l = compute_lube_per_acre(f)
626
  ans = f"Jerry β†’ LUBE per acre=${l:.2f} (mode={mode})"
 
 
 
 
 
627
  elif which == "labor_per_acre":
628
  l = compute_labor_per_acre(wage, speed)
629
  ans = f"Jerry β†’ LABOR per acre=${l:.2f} (mode={mode})"
630
-
 
 
 
 
631
  elif which == "depreciation_per_acre_year":
632
  d = compute_depr_per_acre_year(cost, salvage, life, acres)
633
- ans = (
634
- f"Jerry β†’ DEPRECIATION per acre-year=${d:.2f} "
635
- f"(salvage fraction={salv_frac:.2f}, salvage=${salvage:,.0f}; mode={mode})"
636
- )
 
 
637
  elif which == "occ_per_acre_year":
638
  o = compute_occ_per_acre_year(cost, salvage, rate, life, acres)
639
- ans = (
640
- f"Jerry β†’ OCC per acre-year=${o:.2f} "
641
- f"(salvage fraction={salv_frac:.2f}, salvage=${salvage:,.0f}; mode={mode})"
642
- )
 
 
643
  elif which == "tax":
644
- tval = compute_tax_per_acre(cost, acres)
645
- ans = f"Jerry β†’ TAX per acre=${tval:.2f} (mode={mode})"
 
 
 
 
 
646
  elif which == "insurance":
647
- ival = compute_insurance_per_acre(cost, acres)
648
- ans = f"Jerry β†’ INSURANCE per acre=${ival:.2f} (mode={mode})"
 
 
 
 
 
649
  elif which == "housing":
650
- hval = compute_housing_per_acre(cost, acres)
651
- ans = f"Jerry β†’ HOUSING per acre=${hval:.2f} (mode={mode})"
 
 
 
 
 
 
 
 
 
652
  teacher_dbg = _pp({
653
  "mode": mode,
654
  "raw_llm": raw_llm,
655
  "raw_regex": raw_regex,
656
  "final_data": new_hud
657
  })
658
- return ans, _pp(new_hud), ", ".join(changed) or "(none)", teacher_dbg, new_hud
659
-
 
660
  # Wire it up; we update 5 outputs: student answer, HUD JSON, changed fields, teacher debug, and the state
661
- ask.click(_ask_with_hud, [q, hud_state], [out, hud_box, changed_box, dbg, hud_state])
 
 
 
 
 
 
 
 
 
 
 
 
662
 
663
  if __name__=="__main__":
664
  demo.queue().launch(server_name="0.0.0.0",server_port=int(os.getenv("PORT","7860")))
 
250
  # Housing = 0.5% of cost, allocated per acre
251
  return 0.005 * float(cost) / max(float(acres), 1e-9)
252
 
253
+ # Blackbord helpers
254
+ # ---------- Blackboard helpers ----------
255
+ def _fmt_money(x):
256
+ return f"${x:,.2f}"
257
+
258
+ def _fmt_pct(x):
259
+ return f"{x*100:.1f}%"
260
+
261
+ def explain_repair(equip, cost, life, acres, speed, frac, hours, per_ac_yr):
262
+ # Iowa method: AccumHours = life * (acres / speed) and Repair$/ac/yr = (Cost Γ— AccumRepairFraction) / (life Γ— acres)
263
+ # Sources: Repair method & per-year/per-acre basis in course notes.
264
+ src = "Iowa table (Accumulated Repair Fraction) β€” see class notes."
265
+ return (
266
+ "REPAIR β€” show your work\n"
267
+ f"1) Accumulated hours: hours = life Γ— (acres / speed) = {life:g} Γ— ({acres:g} / {speed:g}) = {hours:,.1f} hr.\n"
268
+ f"2) Table lookup: fraction = {frac:.4f} ({_fmt_pct(frac)}) ← {src}\n"
269
+ f"3) Total lifetime repair $: Cost Γ— fraction = {_fmt_money(cost)} Γ— {frac:.4f} = {_fmt_money(cost*frac)}.\n"
270
+ f"4) Per-acre-per-year: lifetime_repair Γ· (life Γ— acres) = {_fmt_money(cost*frac)} Γ· ({life:g} Γ— {acres:g}) = {_fmt_money(per_ac_yr)} per ac/yr."
271
+ )
272
+
273
+ def explain_fuel(hp, diesel, speed, per_ac):
274
+ # Fuel Cost per ac = (0.044 Γ— PTO HP Γ— Diesel $/gal) / speed_acph
275
+ # Reference: fuel formula in notes.
276
+ return (
277
+ "FUEL β€” show your work\n"
278
+ f"Fuel$/ac = (0.044 Γ— PTO_HP Γ— diesel_price) Γ· speed\n"
279
+ f" = (0.044 Γ— {hp:g} Γ— {_fmt_money(diesel)}/gal) Γ· {speed:g}\n"
280
+ f" = {_fmt_money(per_ac)} per ac."
281
+ )
282
+
283
+ def explain_lube(fuel_per_ac, lube_per_ac):
284
+ # Lube = 0.15 Γ— Fuel (Iowa)
285
+ return (
286
+ "LUBE β€” show your work\n"
287
+ f"Lube$/ac = 0.15 Γ— Fuel$/ac = 0.15 Γ— {_fmt_money(fuel_per_ac)} = {_fmt_money(lube_per_ac)} per ac."
288
+ )
289
+
290
+ def explain_labor(wage, speed, per_ac):
291
+ # Labor$/ac = wage / speed
292
+ return (
293
+ "LABOR β€” show your work\n"
294
+ f"Labor$/ac = wage Γ· speed = {_fmt_money(wage)} Γ· {speed:g} = {_fmt_money(per_ac)} per ac."
295
+ )
296
+
297
+ def explain_depr(cost, salvage_frac, salvage, life, acres, per_ac_yr):
298
+ # Dep$/ac/yr = (C βˆ’ S) / (life Γ— acres)
299
+ return (
300
+ "DEPRECIATION β€” show your work\n"
301
+ f"1) Salvage fraction from table = {salvage_frac:.3f} ({_fmt_pct(salvage_frac)}); Salvage S = {_fmt_money(salvage)}.\n"
302
+ f"2) Dep$/ac/yr = (C βˆ’ S) Γ· (life Γ— acres) = ({_fmt_money(cost)} βˆ’ {_fmt_money(salvage)}) Γ· ({life:g} Γ— {acres:g})\n"
303
+ f" = {_fmt_money(per_ac_yr)} per ac/yr."
304
+ )
305
+
306
+ def explain_occ(cost, salvage, rate, acres, per_ac_yr):
307
+ # OCC$/ac/yr = rate Γ— ((C + S)/2) Γ· acres (no division by life)
308
+ avg_inv = 0.5*(cost+salvage)
309
+ return (
310
+ "OPPORTUNITY COST OF CAPITAL β€” show your work\n"
311
+ f"Avg investment = (C + S)/2 = ({_fmt_money(cost)} + {_fmt_money(salvage)}) / 2 = {_fmt_money(avg_inv)}.\n"
312
+ f"OCC$/ac/yr = rate Γ— AvgInvestment Γ· acres = {rate:.3f} Γ— {_fmt_money(avg_inv)} Γ· {acres:g}\n"
313
+ f" = {_fmt_money(per_ac_yr)} per ac/yr."
314
+ )
315
+
316
+ def explain_tax_ins_housing(kind, cost, per_ac):
317
+ # Tax=1% of cost; Insurance=0.5%; Housing=0.5% (annual) then Γ· acres for per-acre basis
318
+ rates = {"tax":0.01, "insurance":0.005, "housing":0.005}
319
+ r = rates[kind]
320
+ label = {"tax":"TAX","insurance":"INSURANCE","housing":"HOUSING"}[kind]
321
+ return (
322
+ f"{label} β€” show your work\n"
323
+ f"{label}$/ac/yr = ({r:.3f} Γ— Cost) Γ· acres = ({r:.3f} Γ— {_fmt_money(cost)}) Γ· acres = {_fmt_money(per_ac)} per ac/yr."
324
+ )
325
+
326
+
327
 
328
  # ---- Keyword intent + Regex fallback ----
329
  NUM=r"[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?"
 
619
  gr.Markdown("### Student HUD β€” what Jerry sees")
620
  hud_box = gr.Textbox(label="Final Data (persists this session)", lines=18, value=_pp({k: None for k in HUD_FIELDS}))
621
  changed_box = gr.Textbox(label="Fields updated by last prompt", lines=3, value="(none)")
622
+
623
+ # NEW: a Blackboard textbox (under your teacher accordion or right below out)
624
+ #blackboard = gr.Textbox(label="Blackboard β€” step-by-step", lines=14)
625
+ steps_box = gr.Textbox(label="Blackboard β€” show the work", lines=16, value="(none)")
626
+
627
  # Optional: teacher debug accordion if you kept the teacher JSON earlier
628
  # (If you added a teacher debug already, you can leave this out or keep your version.)
629
  with gr.Accordion("Teacher debug β€” Parser & JSON (optional)", open=False):
630
  dbg = gr.Textbox(label="LLM/Regex + Raw/Final JSON + Diagnostics", lines=22)
631
 
632
  def _ask_with_hud(user_text, hud):
633
+ """
634
+ Returns SIX values in this order:
635
+ 1) student answer (string)
636
+ 2) HUD JSON (string)
637
+ 3) changed fields (string)
638
+ 4) teacher debug JSON (string)
639
+ 5) new HUD state (dict)
640
+ 6) blackboard/steps text (string)
641
+ """
642
+ steps_text = "(none)" # Blackboard default
643
+
644
  # ---- Parse (LLM first, then regex) ----
645
  raw_llm = llm_parse(user_text)
646
  if raw_llm:
 
685
  rate = new_hud.get("rate")
686
  if rate is not None:
687
  rate = float(rate)
688
+ if rate > 1.0: rate = rate / 100.0
689
  else:
690
  rate = 0.08
691
 
692
+ # ---- If no which_cost, early return (SIX outputs) ----
693
  if not which:
694
  msg = "Jerry β†’ Tell me which cost (e.g., fuel, repair, depreciation). Check the HUD to see what I have so far."
695
  teacher_dbg = _pp({"mode": mode, "raw_llm": raw_llm, "raw_regex": raw_regex, "final_data": new_hud})
696
+ return msg, _pp(new_hud), ", ".join(changed) or "(none)", teacher_dbg, new_hud, steps_text
697
 
698
+ # ---- Check readiness for chosen cost; early return if missing (SIX outputs) ----
699
  missing = missing_for(which, new_hud)
700
  if missing:
701
  msg = f"Jerry β†’ Not enough data for {which}. Missing: {', '.join(missing)}. Check the HUD and add what's missing."
702
  teacher_dbg = _pp({"mode": mode, "raw_llm": raw_llm, "raw_regex": raw_regex, "final_data": new_hud, "missing": missing})
703
+ return msg, _pp(new_hud), ", ".join(changed) or "(none)", teacher_dbg, new_hud, steps_text
704
+
705
+ # ---- Derived diagnostics for Blackboard (safe) ----
706
+ # salvage parts (works whether salvage_parts exists or not)
707
+ def _safe_salvage_parts(equip, cost, hp, acres, speed, life):
708
+ try:
709
+ # try the convenience helper if present in your file
710
+ return salvage_parts(equip, cost, hp, acres, speed, life) # (frac, salvage, class)
711
+ except Exception:
712
+ # fallback to the earlier direct computation
713
+ try:
714
+ machine_class = classify_machine_for_salvage(equip, hp)
715
+ except Exception:
716
+ machine_class = None
717
+ try:
718
+ annual_hours = float(acres)/max(float(speed), 1e-9)
719
+ except Exception:
720
+ annual_hours = 0.0
721
+ try:
722
+ salv_frac = get_salvage_fraction(machine_class, annual_hours, float(life)) if machine_class else None
723
+ except Exception:
724
+ salv_frac = None
725
+ if salv_frac is None:
726
+ salv_frac = 0.20
727
+ return float(salv_frac), float(cost)*float(salv_frac), machine_class or equip
728
+
729
+ salv_frac, salvage, salvage_class = _safe_salvage_parts(equip, cost, hp, acres, speed, life)
730
+
731
+ # ---- Compute chosen cost + Blackboard text ----
732
  if which == "repair_per_acre_year":
733
  r = compute_repair_per_acre_year(equip, cost, life, acres, speed)
734
  ans = f"Jerry β†’ REPAIR per acre-year: fraction={r['fraction']:.3f}, value=${r['repair_per_acre_year']:.2f}/ac/yr (mode={mode})"
735
+ # Blackboard
736
+ try:
737
+ steps_text = explain_repair(
738
+ equip=equip, cost=cost, life=life, acres=acres, speed=speed,
739
+ frac=r["fraction"], hours=r["hours"], per_ac_yr=r["repair_per_acre_year"]
740
+ )
741
+ except Exception:
742
+ steps_text = "(repair: explanation unavailable)"
743
+
744
  elif which == "fuel_per_acre":
745
  f = compute_fuel_per_acre(hp, diesel, speed)
746
  ans = f"Jerry β†’ FUEL per acre=${f:.2f} (mode={mode})"
747
+ try:
748
+ steps_text = explain_fuel(hp=hp, diesel=diesel, speed=speed, per_ac=f)
749
+ except Exception:
750
+ steps_text = "(fuel: explanation unavailable)"
751
+
752
  elif which == "lube_per_acre":
753
  f = compute_fuel_per_acre(hp, diesel, speed)
754
  l = compute_lube_per_acre(f)
755
  ans = f"Jerry β†’ LUBE per acre=${l:.2f} (mode={mode})"
756
+ try:
757
+ steps_text = explain_lube(fuel_per_ac=f, lube_per_ac=l)
758
+ except Exception:
759
+ steps_text = "(lube: explanation unavailable)"
760
+
761
  elif which == "labor_per_acre":
762
  l = compute_labor_per_acre(wage, speed)
763
  ans = f"Jerry β†’ LABOR per acre=${l:.2f} (mode={mode})"
764
+ try:
765
+ steps_text = explain_labor(wage=wage, speed=speed, per_ac=l)
766
+ except Exception:
767
+ steps_text = "(labor: explanation unavailable)"
768
+
769
  elif which == "depreciation_per_acre_year":
770
  d = compute_depr_per_acre_year(cost, salvage, life, acres)
771
+ ans = f"Jerry β†’ DEPRECIATION per acre-year=${d:.2f} (salvage fraction={salv_frac:.2f}, salvage=${salvage:,.0f}; mode={mode})"
772
+ try:
773
+ steps_text = explain_depr(cost=cost, salvage_frac=salv_frac, salvage=salvage, life=life, acres=acres, per_ac_yr=d)
774
+ except Exception:
775
+ steps_text = "(depreciation: explanation unavailable)"
776
+
777
  elif which == "occ_per_acre_year":
778
  o = compute_occ_per_acre_year(cost, salvage, rate, life, acres)
779
+ ans = f"Jerry β†’ OCC per acre-year=${o:.2f} (salvage fraction={salv_frac:.2f}, salvage=${salvage:,.0f}; mode={mode})"
780
+ try:
781
+ steps_text = explain_occ(cost=cost, salvage=salvage, rate=rate, acres=acres, per_ac_yr=o)
782
+ except Exception:
783
+ steps_text = "(OCC: explanation unavailable)"
784
+
785
  elif which == "tax":
786
+ t = compute_tax_per_acre(cost, acres)
787
+ ans = f"Jerry β†’ TAX per acre-year=${t:.2f} (mode={mode})"
788
+ try:
789
+ steps_text = explain_tax_ins_housing("tax", cost, t)
790
+ except Exception:
791
+ steps_text = "(tax: explanation unavailable)"
792
+
793
  elif which == "insurance":
794
+ ins = compute_insurance_per_acre(cost, acres)
795
+ ans = f"Jerry β†’ INSURANCE per acre-year=${ins:.2f} (mode={mode})"
796
+ try:
797
+ steps_text = explain_tax_ins_housing("insurance", cost, ins)
798
+ except Exception:
799
+ steps_text = "(insurance: explanation unavailable)"
800
+
801
  elif which == "housing":
802
+ h = compute_housing_per_acre(cost, acres)
803
+ ans = f"Jerry β†’ HOUSING per acre-year=${h:.2f} (mode={mode})"
804
+ try:
805
+ steps_text = explain_tax_ins_housing("housing", cost, h)
806
+ except Exception:
807
+ steps_text = "(housing: explanation unavailable)"
808
+
809
+ else:
810
+ ans = f"Jerry β†’ Unknown cost. (mode={mode})"
811
+ steps_text = "(none)"
812
+
813
  teacher_dbg = _pp({
814
  "mode": mode,
815
  "raw_llm": raw_llm,
816
  "raw_regex": raw_regex,
817
  "final_data": new_hud
818
  })
819
+ return ans, _pp(new_hud), ", ".join(changed) or "(none)", teacher_dbg, new_hud, steps_text
820
+
821
+
822
  # Wire it up; we update 5 outputs: student answer, HUD JSON, changed fields, teacher debug, and the state
823
+ #ask.click(_ask_with_hud, [q, hud_state], [out, hud_box, changed_box, dbg, hud_state])
824
+ ask.click(
825
+ _ask_with_hud,
826
+ [q, hud_state], # <-- 2 inputs ONLY
827
+ [out, hud_box, changed_box, dbg, hud_state, steps_box] # <-- 6 outputs
828
+ )
829
+
830
+
831
+ # add this after `changed_box` definition
832
+ # --- Blackboard (always on) ---
833
+
834
+
835
+
836
 
837
  if __name__=="__main__":
838
  demo.queue().launch(server_name="0.0.0.0",server_port=int(os.getenv("PORT","7860")))