Update app.py
Browse files
app.py
CHANGED
|
@@ -113,7 +113,7 @@ def optimize_supply_tool(forecast_json: str) -> str:
|
|
| 113 |
c[i_sell(p)] -= price[p]
|
| 114 |
c[i_einv(p)] += 0.0
|
| 115 |
bounds[i_prod(p)] = (0, None)
|
| 116 |
-
bounds[i_sell(p)] = (0, demand[p])
|
| 117 |
bounds[i_einv(p)] = (0, None)
|
| 118 |
for r in RMs:
|
| 119 |
c[i_pur(r)] += rm_cost[r]
|
|
@@ -221,26 +221,35 @@ SYSTEM_PLAN = (
|
|
| 221 |
"Return: {'forecast': <json>, 'plan': <json>, 'md61': <json>}"
|
| 222 |
)
|
| 223 |
|
| 224 |
-
def run_agentic(
|
| 225 |
agent = make_agent()
|
| 226 |
if file_obj is not None:
|
| 227 |
path = file_obj.name
|
| 228 |
prompt = (f"{SYSTEM_PLAN}\n"
|
| 229 |
-
f"Use forecast_tool(horizon_months={int(
|
| 230 |
f"Then run the other two steps as specified. Return only the final JSON.")
|
| 231 |
else:
|
| 232 |
prompt = (f"{SYSTEM_PLAN}\n"
|
| 233 |
-
f"Use forecast_tool(horizon_months={int(
|
| 234 |
f"Then run the other two steps as specified. Return only the final JSON.")
|
| 235 |
return agent.run(prompt)
|
| 236 |
|
| 237 |
|
| 238 |
-
# ==== UI Helpers (pretty
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
def parse_forecast(json_str):
|
| 240 |
df = pd.DataFrame(json.loads(json_str))
|
| 241 |
-
|
| 242 |
"product_id":"Product","period_start":"Period Start","forecast_qty":"Forecast Qty"
|
| 243 |
})
|
|
|
|
| 244 |
|
| 245 |
def parse_plan(json_str):
|
| 246 |
d = json.loads(json_str)
|
|
@@ -250,11 +259,11 @@ def parse_plan(json_str):
|
|
| 250 |
prod = pd.DataFrame(d["products"])
|
| 251 |
raw = pd.DataFrame(d["raw_materials"])
|
| 252 |
res = pd.DataFrame(d["resources"])
|
| 253 |
-
return d["status"], kpis, prod, raw, res
|
| 254 |
|
| 255 |
def parse_md61(json_str):
|
| 256 |
d = json.loads(json_str)
|
| 257 |
-
prev = pd.DataFrame(d.get("preview", []))
|
| 258 |
path = d.get("csv_path", "")
|
| 259 |
return d.get("status",""), prev, path
|
| 260 |
|
|
@@ -345,14 +354,31 @@ with gr.Blocks(title="Forecast → Optimize → SAP MD61") as demo:
|
|
| 345 |
a_md61_prev = gr.Dataframe(label="MD61 Preview", interactive=False)
|
| 346 |
a_md61_file = gr.File(label="Download MD61 CSV", interactive=False)
|
| 347 |
|
|
|
|
| 348 |
def do_agent(h, p, demo_flag, f):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 349 |
try:
|
| 350 |
-
res = run_agentic(h, p, demo_flag, f)
|
| 351 |
-
out =
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
except Exception as e:
|
| 357 |
return (f"Agent error: {e}", pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(),
|
| 358 |
pd.DataFrame(), pd.DataFrame(), None)
|
|
@@ -361,4 +387,7 @@ with gr.Blocks(title="Forecast → Optimize → SAP MD61") as demo:
|
|
| 361 |
outputs=[out_json, a_forecast_tbl, a_plan_kpis, a_plan_prod, a_plan_raw, a_plan_res, a_md61_prev, a_md61_file])
|
| 362 |
|
| 363 |
if __name__ == "__main__":
|
|
|
|
|
|
|
|
|
|
| 364 |
demo.launch()
|
|
|
|
| 113 |
c[i_sell(p)] -= price[p]
|
| 114 |
c[i_einv(p)] += 0.0
|
| 115 |
bounds[i_prod(p)] = (0, None)
|
| 116 |
+
bounds[i_sell(p)] = (0, demand[p]) # demand as an upper bound (no backorders)
|
| 117 |
bounds[i_einv(p)] = (0, None)
|
| 118 |
for r in RMs:
|
| 119 |
c[i_pur(r)] += rm_cost[r]
|
|
|
|
| 221 |
"Return: {'forecast': <json>, 'plan': <json>, 'md61': <json>}"
|
| 222 |
)
|
| 223 |
|
| 224 |
+
def run_agentic(h, plant, demo_flag, file_obj):
|
| 225 |
agent = make_agent()
|
| 226 |
if file_obj is not None:
|
| 227 |
path = file_obj.name
|
| 228 |
prompt = (f"{SYSTEM_PLAN}\n"
|
| 229 |
+
f"Use forecast_tool(horizon_months={int(h)}, use_demo=False, history_csv_path='{path}'). "
|
| 230 |
f"Then run the other two steps as specified. Return only the final JSON.")
|
| 231 |
else:
|
| 232 |
prompt = (f"{SYSTEM_PLAN}\n"
|
| 233 |
+
f"Use forecast_tool(horizon_months={int(h)}, use_demo={bool(demo_flag)}). "
|
| 234 |
f"Then run the other two steps as specified. Return only the final JSON.")
|
| 235 |
return agent.run(prompt)
|
| 236 |
|
| 237 |
|
| 238 |
+
# ==== UI Helpers (rounding + pretty) =========================================
|
| 239 |
+
def _round_df(df: pd.DataFrame, places: int = 2) -> pd.DataFrame:
|
| 240 |
+
if df is None or df.empty:
|
| 241 |
+
return df
|
| 242 |
+
out = df.copy()
|
| 243 |
+
num_cols = out.select_dtypes(include=["number"]).columns
|
| 244 |
+
out[num_cols] = out[num_cols].astype(float).round(places)
|
| 245 |
+
return out
|
| 246 |
+
|
| 247 |
def parse_forecast(json_str):
|
| 248 |
df = pd.DataFrame(json.loads(json_str))
|
| 249 |
+
df = df[["product_id","period_start","forecast_qty"]].rename(columns={
|
| 250 |
"product_id":"Product","period_start":"Period Start","forecast_qty":"Forecast Qty"
|
| 251 |
})
|
| 252 |
+
return _round_df(df)
|
| 253 |
|
| 254 |
def parse_plan(json_str):
|
| 255 |
d = json.loads(json_str)
|
|
|
|
| 259 |
prod = pd.DataFrame(d["products"])
|
| 260 |
raw = pd.DataFrame(d["raw_materials"])
|
| 261 |
res = pd.DataFrame(d["resources"])
|
| 262 |
+
return d["status"], _round_df(kpis), _round_df(prod), _round_df(raw), _round_df(res)
|
| 263 |
|
| 264 |
def parse_md61(json_str):
|
| 265 |
d = json.loads(json_str)
|
| 266 |
+
prev = _round_df(pd.DataFrame(d.get("preview", [])))
|
| 267 |
path = d.get("csv_path", "")
|
| 268 |
return d.get("status",""), prev, path
|
| 269 |
|
|
|
|
| 354 |
a_md61_prev = gr.Dataframe(label="MD61 Preview", interactive=False)
|
| 355 |
a_md61_file = gr.File(label="Download MD61 CSV", interactive=False)
|
| 356 |
|
| 357 |
+
# Robust agent handler: accepts dict OR str and rounds outputs
|
| 358 |
def do_agent(h, p, demo_flag, f):
|
| 359 |
+
def to_obj(x):
|
| 360 |
+
return x if isinstance(x, (dict, list)) else json.loads(x)
|
| 361 |
+
def to_str(x):
|
| 362 |
+
return x if isinstance(x, str) else json.dumps(x)
|
| 363 |
+
|
| 364 |
try:
|
| 365 |
+
res = run_agentic(h, p, demo_flag, f) # may be dict or str
|
| 366 |
+
out = to_obj(res)
|
| 367 |
+
|
| 368 |
+
forecast_json = to_str(out["forecast"])
|
| 369 |
+
plan_json = to_str(out["plan"])
|
| 370 |
+
md61_json = to_str(out["md61"])
|
| 371 |
+
|
| 372 |
+
f_df = parse_forecast(forecast_json)
|
| 373 |
+
_, kpis, prod, raw, res_tbl = parse_plan(plan_json)
|
| 374 |
+
_, prev, csv_path = parse_md61(md61_json)
|
| 375 |
+
|
| 376 |
+
pretty = {
|
| 377 |
+
"forecast": json.loads(forecast_json),
|
| 378 |
+
"plan": json.loads(plan_json),
|
| 379 |
+
"md61": json.loads(md61_json),
|
| 380 |
+
}
|
| 381 |
+
return (json.dumps(pretty, indent=2), f_df, kpis, prod, raw, res_tbl, prev, csv_path)
|
| 382 |
except Exception as e:
|
| 383 |
return (f"Agent error: {e}", pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(),
|
| 384 |
pd.DataFrame(), pd.DataFrame(), None)
|
|
|
|
| 387 |
outputs=[out_json, a_forecast_tbl, a_plan_kpis, a_plan_prod, a_plan_raw, a_plan_res, a_md61_prev, a_md61_file])
|
| 388 |
|
| 389 |
if __name__ == "__main__":
|
| 390 |
+
# Needs OPENAI_API_KEY in env for agent tab; manual tab works without it.
|
| 391 |
+
if not os.environ.get("OPENAI_API_KEY"):
|
| 392 |
+
print("⚠️ Set OPENAI_API_KEY (Space secret) to use Agentic tab.")
|
| 393 |
demo.launch()
|