Spaces:
Runtime error
Runtime error
| import numpy as np | |
| from collections import defaultdict | |
| def grade_easy(logs): | |
| cost = sum(l['late_fee'] + 0.5 * l['interest'] for l in logs) | |
| penalty = min(1.0, cost / 500.0) | |
| score = 1.0 - penalty | |
| return max(0.0, min(1.0, score)) | |
| def grade_medium(logs, cash_hist): | |
| cost = sum(l['late_fee'] + l['interest'] for l in logs) | |
| penalty = min(1.0, cost / 1000.0) | |
| if cash_hist: | |
| liquidity = np.mean(cash_hist) | |
| else: | |
| liquidity = 0.0 | |
| bonus = min(0.1, liquidity / 40000.0) | |
| score = 1.0 - penalty + bonus | |
| return max(0.0, min(1.0, score)) | |
| def grade_hard(logs, cash_hist): | |
| cost = sum(l['late_fee'] + l['interest'] for l in logs) | |
| penalty = min(1.0, cost / 3000.0) | |
| if cash_hist: | |
| liquidity = np.mean(cash_hist) - np.std(cash_hist) | |
| liquidity = max(0.0, liquidity) | |
| else: | |
| liquidity = 0.0 | |
| bonus = min(0.15, liquidity / 50000.0) | |
| score = 1.0 - penalty + bonus | |
| return max(0.0, min(1.0, score)) | |
| GRADERS = { | |
| "easy": lambda logs, cash_hist: grade_easy(logs), | |
| "medium": lambda logs, cash_hist: grade_medium(logs, cash_hist), | |
| "hard": lambda logs, cash_hist: grade_hard(logs, cash_hist), | |
| } | |
| def grade_episode(difficulty, logs, cash_hist): | |
| grader = GRADERS.get(difficulty) | |
| if grader is None: | |
| raise ValueError(f"Unknown difficulty: {difficulty}") | |
| return grader(logs, cash_hist) | |
| def run_task(difficulty, env, policy_fn, seed=42): | |
| try: | |
| from cashflowmanager.server.client import clear_action_cache | |
| except ImportError: | |
| try: | |
| from server.client import clear_action_cache | |
| except ImportError: | |
| from .client import clear_action_cache | |
| clear_action_cache() # Reset stale LLM decisions from prior episodes | |
| obs = env.reset(difficulty=difficulty, seed=seed) | |
| def serialize_invoices(invoices): | |
| return [inv.model_dump() for inv in invoices] | |
| done = False | |
| logs = [] | |
| cash_hist = [obs.cash] | |
| history = [] | |
| day_data = defaultdict(lambda: { | |
| "day": None, | |
| "initial_invoices": [], | |
| "actions": [], | |
| "end_cash": None, | |
| "late_fee": 0.0, | |
| "interest": 0.0, | |
| }) | |
| current_day = obs.day | |
| day_data[current_day]["day"] = current_day | |
| day_data[current_day]["initial_invoices"] = serialize_invoices(obs.invoices) | |
| params = getattr(env, "params", {"max_days": 10, "num_invoices": 3}) | |
| max_steps = params["max_days"] * 5 # Buffer | |
| step_count = 0 | |
| while not done and step_count < max_steps: | |
| action = policy_fn(obs, history) | |
| prev_day = obs.day | |
| # In multi-agent, action.type is a string: pay, negotiate, defer, partial, credit | |
| action_label = action.type.capitalize() | |
| invoice_id = action.invoice_id or "N/A" | |
| obs = env.step(action) | |
| done = obs.done | |
| step_count += 1 | |
| # Log action into the day it was taken | |
| day_data[prev_day]["actions"].append({ | |
| "invoice_id": invoice_id, | |
| "action": action_label, | |
| "reward": round(obs.reward, 4), | |
| "late_fee": round(obs.metadata.get("late_fee", 0), 2), | |
| "interest": round(obs.metadata.get("interest", 0), 4), | |
| "cash_after": round(obs.cash, 2), | |
| "credit_used": round(obs.credit_used, 2), | |
| "advisors": obs.advisor_messages | |
| }) | |
| day_data[prev_day]["late_fee"] += obs.metadata.get("late_fee", 0) | |
| day_data[prev_day]["interest"] += obs.metadata.get("interest", 0) | |
| day_data[prev_day]["end_cash"] = round(obs.cash, 2) | |
| # When day advances, capture new day's invoices | |
| if obs.day != prev_day and not done: | |
| day_data[obs.day]["day"] = obs.day | |
| day_data[obs.day]["initial_invoices"] = serialize_invoices(obs.invoices) | |
| history.append({ | |
| "day": prev_day, | |
| "action": action_label, | |
| "invoice_id": invoice_id, | |
| "reward": obs.reward, | |
| "late_fee": obs.metadata.get("late_fee", 0), | |
| "interest": obs.metadata.get("interest", 0), | |
| "credit_used": obs.credit_used, | |
| }) | |
| logs.append({ | |
| "day": obs.day, | |
| "cash": obs.cash, | |
| "credit_used": obs.credit_used, | |
| "late_fee": obs.metadata.get("late_fee", 0), | |
| "interest": obs.metadata.get("interest", 0), | |
| "reward": obs.reward, | |
| }) | |
| cash_hist.append(obs.cash) | |
| score = grade_episode(difficulty, logs, cash_hist) | |
| #day by day breakdpwn | |
| episode_days = [ | |
| { | |
| "day": v["day"], | |
| "initial_invoices": v["initial_invoices"], | |
| "actions": v["actions"], | |
| "end_cash": v["end_cash"], | |
| "day_late_fee": round(v["late_fee"], 2), | |
| "day_interest": round(v["interest"], 4), | |
| } | |
| for k, v in sorted(day_data.items()) | |
| ] | |
| return logs, cash_hist, score, history, episode_days |