""" Streamlit UI for ShopManagerEng — Interactive Jewelry Shop Demo. An AI heuristic agent automatically plays through each episode. Users press "New Episode" and watch the agent navigate all 3 phases. """ import os import sys import random import time from pathlib import Path import streamlit as st # ── Ensure imports resolve ────────────────────────────────────────────────── ROOT = Path(__file__).resolve().parent if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) os.environ.setdefault("SHOPMANAGER_MARKET_MODE", "synthetic") from server.ShopManagerEng_environment import JewelryShopEnvironment from models import JewelryAction, PRODUCT_CATALOG # ── Page config ───────────────────────────────────────────────────────────── st.set_page_config( page_title="ShopManagerEng — Jewelry Shop RL", page_icon="💎", layout="wide", initial_sidebar_state="expanded", ) # ── CSS: clean light-theme styling ────────────────────────────────────────── st.markdown(""" """, unsafe_allow_html=True) # ── Heuristic Agent ───────────────────────────────────────────────────────── def heuristic_action(obs): """Simple rule-based agent that plays through all 3 phases.""" if obs.phase == "market": price = float(obs.gold_price or 300) # Buy if we have enough cash for at least 1 oz if obs.cash >= price + 10: return JewelryAction( market_action="buy", gold_qty=1.0, target_price_usd=obs.gold_price, ) return JewelryAction(market_action="wait") if obs.phase == "warehouse": # Pick the highest-demand product we can afford demand = obs.demand or {"ring": 0.5, "necklace": 0.3, "bracelet": 0.2} for name in sorted(demand, key=lambda k: demand.get(k, 0), reverse=True): spec = PRODUCT_CATALOG[name] if obs.gold_oz + 1e-8 >= spec["gold_oz"] and obs.cash >= spec["labor"]: return JewelryAction(product_choice=name) return JewelryAction(product_choice="ring") if obs.phase == "showroom": # Accept if margin > 15% or after round 3 if ( obs.current_offer and obs.cost_basis > 0 and float(obs.current_offer) / float(obs.cost_basis) >= 1.15 ) or (obs.negotiation_round and int(obs.negotiation_round) >= 3): return JewelryAction(message="I accept") offer = float(obs.current_offer or 0) if offer: return JewelryAction(message=f"How about ${offer * 1.08:.2f}?") return JewelryAction(message="I need a better offer") return JewelryAction() # ── Session state ─────────────────────────────────────────────────────────── if "episode_steps" not in st.session_state: st.session_state.episode_steps = None st.session_state.final_reward = None st.session_state.episode_count = 0 def run_episode(task_id): """Run a full episode with the heuristic agent and return step logs.""" env = JewelryShopEnvironment() seed = random.randint(0, 99999) obs = env.reset(seed=seed, market_mode="synthetic", task_id=task_id) steps = [{ "step": 0, "phase": obs.phase, "action": "reset", "msg": obs.message, "reward": 0.0, "cash": obs.cash, "gold_oz": obs.gold_oz, "gold_price": obs.gold_price, "cumulative": float(obs.cumulative_reward), }] for i in range(1, 20): if obs.done: break action = heuristic_action(obs) # Describe the action in human terms if obs.phase == "market": act_str = f"BUY {action.gold_qty} oz" if action.market_action == "buy" else "WAIT" elif obs.phase == "warehouse": act_str = f"CRAFT {action.product_choice}" else: act_str = action.message or "..." obs = env.step(action) steps.append({ "step": i, "phase": obs.phase, "action": act_str, "msg": obs.message, "reward": float(obs.reward), "cash": obs.cash, "gold_oz": obs.gold_oz, "gold_price": getattr(obs, "gold_price", 0), "product": getattr(obs, "product_for_sale", None), "offer": float(obs.current_offer) if obs.current_offer else None, "cost_basis": float(obs.cost_basis) if obs.cost_basis else None, "cumulative": float(obs.cumulative_reward), }) return steps, float(obs.cumulative_reward) # ── Sidebar ───────────────────────────────────────────────────────────────── with st.sidebar: st.markdown("### 💎 ShopManagerEng") st.markdown("---") task = st.selectbox( "🎯 Task Profile", ["profit_negotiator", "market_timing", "demand_crafter"], index=0, ) weights = { "profit_negotiator": "Showroom 60% · Market 20% · Warehouse 20%", "market_timing": "Market 60% · Warehouse 20% · Showroom 20%", "demand_crafter": "Warehouse 60% · Market 20% · Showroom 20%", } st.caption(f"**Weights:** {weights[task]}") st.markdown("---") if st.button("🚀 New Episode", use_container_width=True, type="primary"): steps, reward = run_episode(task) st.session_state.episode_steps = steps st.session_state.final_reward = reward st.session_state.episode_count += 1 st.rerun() st.markdown("---") st.markdown("#### How It Works") st.markdown(""" An AI **heuristic agent** automatically plays through all 3 phases of the jewelry shop: 1. 📈 **Market** — Buy gold at the right price 2. 🏭 **Warehouse** — Craft the most demanded product 3. 🤝 **Showroom** — Negotiate the best sale price Press **🚀 New Episode** to watch the agent play! """) if st.session_state.final_reward is not None: st.markdown("---") reward = st.session_state.final_reward color = "#10b981" if reward >= 0.6 else "#f59e0b" if reward >= 0.4 else "#ef4444" st.markdown(f"**Final Score:** :{'green' if reward >= 0.6 else 'orange' if reward >= 0.4 else 'red'}[{reward:.4f}]") st.metric("Episodes Played", st.session_state.episode_count) # ── Main area ─────────────────────────────────────────────────────────────── st.markdown('
💎 Jewelry Shop Manager
', unsafe_allow_html=True) st.markdown('An RL environment for training LLMs on multi-step business decisions
', unsafe_allow_html=True) # ── No episode yet — show welcome ────────────────────────────────────────── if st.session_state.episode_steps is None: st.info("👋 **Welcome!** Press **🚀 New Episode** in the sidebar to watch the AI agent play through the jewelry shop simulation.") st.markdown("### 📦 Product Catalog") cols = st.columns(3) items = [("💍 Ring", "ring"), ("📿 Necklace", "necklace"), ("⌚ Bracelet", "bracelet")] for i, (icon_name, key) in enumerate(items): spec = PRODUCT_CATALOG[key] with cols[i]: st.markdown(f"""🪙 Gold: {spec['gold_oz']} oz
🔧 Labor: ${spec['labor']:.0f}
📊 Base demand: {spec['base_demand']:.0%}
Buy raw gold at the best price. Prices fluctuate — time your purchase wisely!
Craft a product (ring, necklace, bracelet) that matches market demand.
Negotiate with a customer over 5 rounds to maximize your selling price.