kevinkyi commited on
Commit
e41099f
·
verified ·
1 Parent(s): 3b7ecfe

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +53 -12
app.py CHANGED
@@ -20,7 +20,7 @@ def projectile_calc(v0_mps: float, theta_deg: float, y0_m: float, g: float, weig
20
  if not (0 < v0_mps <= 500): errors.append("Initial speed must be in (0, 500] m/s.")
21
  if not (-10 <= theta_deg <= 90): errors.append("Launch angle must be between -10° and 90°.")
22
  if not (0 <= y0_m <= 1000): errors.append("Initial height must be in [0, 1000] m.")
23
- if not (1 <= g <= 50): errors.append("Gravity must be in [1, 50] m/s².")
24
  if not (0.05 <= weight_kg <= 50): errors.append("Ball weight must be in [0.05, 50] kg.")
25
  if errors:
26
  return {"ok": False, "errors": errors}
@@ -73,6 +73,7 @@ def llm_one_sentence(structured: Dict[str, Any]) -> str:
73
  i = structured["inputs"]
74
  d = structured["derived"]
75
  o = structured["outputs"]
 
76
  payload = {
77
  "inputs": {
78
  "v0_mps": round(i["v0_mps"], 3),
@@ -91,14 +92,54 @@ def llm_one_sentence(structured: Dict[str, Any]) -> str:
91
  "impact_speed_mps": round(o["impact_speed_mps"], 3),
92
  }
93
  }
94
- instruction = dedent(""" Produce EXACTLY one sentence of this form using only the JSON values:
95
- "If you threw a ball that weighed {weight_kg} kg at v0={v0_mps} m/s, θ={theta_deg}°, from y0={y0_m} m under g={g_mps2} m/s²,
96
- it would stay in the air for {time_of_flight_s} s, reach {max_height_m} m, travel {range_m} m, and impact at {impact_speed_mps} m/s."
97
- """)
98
- user_prompt = f"JSON:\n{json.dumps(payload, indent=2)}\n\nFollow the instruction exactly."
99
- prompt = f"<|system|>\n{SYSTEM_PROMPT}\n<|user|>\n{instruction}\n\n{user_prompt}\n<|assistant|>"
100
- out = llm(prompt, max_new_tokens=120, do_sample=False, temperature=0.0, return_full_text=False)
101
- return out[0]["generated_text"].strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
 
103
  def run_once(v0_mps, theta_deg, y0_m, g_mps2, weight_kg):
104
  rec = projectile_calc(float(v0_mps), float(theta_deg), float(y0_m), float(g_mps2), float(weight_kg))
@@ -111,7 +152,7 @@ def run_once(v0_mps, theta_deg, y0_m, g_mps2, weight_kg):
111
  "v0_mps [m/s]": round(i["v0_mps"], 3),
112
  "theta_deg [deg]": round(i["theta_deg"], 3),
113
  "y0_m [m]": round(i["y0_m"], 3),
114
- "g_mps2 [m/s²]": round(i["g_mps2"], 3),
115
  "weight_kg [kg]": round(i["weight_kg"], 3),
116
  "t_apex_s [s]": round(d["t_apex_s"], 4),
117
  "max_height_m [m]": round(o["max_height_m"], 3),
@@ -130,14 +171,14 @@ with gr.Blocks(title="Projectile Motion — Deterministic + One-Sentence LLM") a
130
  v0 = gr.Slider(1, 200, value=30.0, step=0.5, label="Initial speed v₀ [m/s]")
131
  theta = gr.Slider(-10, 90, value=45.0, step=0.5, label="Launch angle θ [deg]")
132
  y0 = gr.Slider(0, 20, value=1.5, step=0.1, label="Initial height y₀ [m]")
133
- g = gr.Slider(5, 20, value=9.81, step=0.01, label="Gravity g [m/s²]")
134
  w = gr.Slider(0.05, 10.0, value=0.43, step=0.01, label="Ball weight [kg]")
135
 
136
  run_btn = gr.Button("Compute")
137
 
138
  results_df = gr.Dataframe(
139
  headers=[
140
- "v0_mps [m/s]", "theta_deg [deg]", "y0_m [m]", "g_mps2 [m/s²]", "weight_kg [kg]",
141
  "t_apex_s [s]", "max_height_m [m]", "time_of_flight_s [s]", "range_m [m]", "impact_speed_mps [m/s]"
142
  ],
143
  label="All values (inputs + derived)",
 
20
  if not (0 < v0_mps <= 500): errors.append("Initial speed must be in (0, 500] m/s.")
21
  if not (-10 <= theta_deg <= 90): errors.append("Launch angle must be between -10° and 90°.")
22
  if not (0 <= y0_m <= 1000): errors.append("Initial height must be in [0, 1000] m.")
23
+ if not (1 <= g <= 50): errors.append("Gravity must be in [1, 50] m/s^2.")
24
  if not (0.05 <= weight_kg <= 50): errors.append("Ball weight must be in [0.05, 50] kg.")
25
  if errors:
26
  return {"ok": False, "errors": errors}
 
73
  i = structured["inputs"]
74
  d = structured["derived"]
75
  o = structured["outputs"]
76
+
77
  payload = {
78
  "inputs": {
79
  "v0_mps": round(i["v0_mps"], 3),
 
92
  "impact_speed_mps": round(o["impact_speed_mps"], 3),
93
  }
94
  }
95
+
96
+ system_msg = (
97
+ "You are a careful technical writer. "
98
+ "Write EXACTLY ONE sentence using ONLY numbers provided in the JSON. "
99
+ "Do not invent or rename fields; keep units as given; use ~2–3 sig figs."
100
+ )
101
+
102
+ # Show the exact sentence shape we want.
103
+ instruction = '''Use only these keys: inputs, derived, outputs.
104
+ Return exactly one sentence in this form (fill in the braces with the JSON values):
105
+
106
+ If you threw a ball that weighed {weight_kg} kg at v0={v0_mps} m/s, θ={theta_deg}, from y0={y0_m} m under g={g_mps2} m/s^2,
107
+ it would stay in the air for {time_of_flight_s} s, reach {max_height_m} m, travel {range_m} m, and impact at {impact_speed_mps} m/s.
108
+ '''
109
+
110
+ user_msg = f"JSON:
111
+ {json.dumps(payload, indent=2)}
112
+ Produce the single sentence as specified."
113
+
114
+ # ✅ Use the tokenizer's chat template
115
+ messages = [
116
+ {"role": "system", "content": system_msg},
117
+ {"role": "user", "content": instruction},
118
+ {"role": "user", "content": user_msg},
119
+ ]
120
+ prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
121
+
122
+ out = llm(
123
+ prompt,
124
+ max_new_tokens=96,
125
+ do_sample=False,
126
+ temperature=0.0,
127
+ return_full_text=False,
128
+ )
129
+ text = out[0]["generated_text"].strip()
130
+
131
+ # Minimal fallback: if the model didn't produce a sensible one-liner, synthesize it.
132
+ if ("If you threw a ball" not in text) or (len(text.split()) < 6):
133
+ p = payload
134
+ text = (
135
+ f"If you threw a ball that weighed {p['inputs']['weight_kg']} kg at v0={p['inputs']['v0_mps']} m/s, "
136
+ f"θ={p['inputs']['theta_deg']}, from y0={p['inputs']['y0_m']} m under g={p['inputs']['g_mps2']} m/s^2, "
137
+ f"it would stay in the air for {p['outputs']['time_of_flight_s']} s, "
138
+ f"reach {p['outputs']['max_height_m']} m, travel {p['outputs']['range_m']} m, "
139
+ f"and impact at {p['outputs']['impact_speed_mps']} m/s."
140
+ )
141
+
142
+ return text
143
 
144
  def run_once(v0_mps, theta_deg, y0_m, g_mps2, weight_kg):
145
  rec = projectile_calc(float(v0_mps), float(theta_deg), float(y0_m), float(g_mps2), float(weight_kg))
 
152
  "v0_mps [m/s]": round(i["v0_mps"], 3),
153
  "theta_deg [deg]": round(i["theta_deg"], 3),
154
  "y0_m [m]": round(i["y0_m"], 3),
155
+ "g_mps2 [m/s^2]": round(i["g_mps2"], 3),
156
  "weight_kg [kg]": round(i["weight_kg"], 3),
157
  "t_apex_s [s]": round(d["t_apex_s"], 4),
158
  "max_height_m [m]": round(o["max_height_m"], 3),
 
171
  v0 = gr.Slider(1, 200, value=30.0, step=0.5, label="Initial speed v₀ [m/s]")
172
  theta = gr.Slider(-10, 90, value=45.0, step=0.5, label="Launch angle θ [deg]")
173
  y0 = gr.Slider(0, 20, value=1.5, step=0.1, label="Initial height y₀ [m]")
174
+ g = gr.Slider(5, 20, value=9.81, step=0.01, label="Gravity g [m/s^2]")
175
  w = gr.Slider(0.05, 10.0, value=0.43, step=0.01, label="Ball weight [kg]")
176
 
177
  run_btn = gr.Button("Compute")
178
 
179
  results_df = gr.Dataframe(
180
  headers=[
181
+ "v0_mps [m/s]", "theta_deg [deg]", "y0_m [m]", "g_mps2 [m/s^2]", "weight_kg [kg]",
182
  "t_apex_s [s]", "max_height_m [m]", "time_of_flight_s [s]", "range_m [m]", "impact_speed_mps [m/s]"
183
  ],
184
  label="All values (inputs + derived)",