Spaces:
Sleeping
Sleeping
| """ | |
| run_baseline.py β Run a Groq-powered agent against all 3 task tiers of the | |
| Customer Support OpenEnv and record scores. | |
| Usage: | |
| # Start the server first: | |
| # uvicorn server.app:app --host 0.0.0.0 --port 7860 | |
| # | |
| # Then run: | |
| # python run_baseline.py | |
| Environment variables: | |
| GROQ_API_KEY β Required. Your Groq API key. | |
| ENV_BASE_URL β Optional. Defaults to http://localhost:7860. | |
| """ | |
| import json | |
| import os | |
| import sys | |
| from typing import Any, Dict, List | |
| import httpx | |
| from groq import Groq | |
| from dotenv import load_dotenv | |
| # Load variables from .env if present | |
| load_dotenv() | |
| # ------------------------------------------------------------------ # | |
| # Configuration | |
| # ------------------------------------------------------------------ # | |
| GROQ_API_KEY = os.environ.get("GROQ_API_KEY") | |
| if not GROQ_API_KEY: | |
| print("ERROR: GROQ_API_KEY environment variable is not set.") | |
| sys.exit(1) | |
| ENV_BASE_URL = os.environ.get("ENV_BASE_URL", "http://localhost:7860") | |
| MODEL = "llama-3.1-8b-instant" | |
| EPISODES_PER_TASK = 5 | |
| # ------------------------------------------------------------------ # | |
| # System prompt | |
| # ------------------------------------------------------------------ # | |
| SYSTEM_PROMPT = """\ | |
| You are a professional customer support agent. Your job is to help customers \ | |
| resolve their issues efficiently and politely. | |
| For the EASY task: Read the customer message and reply with ONLY the category label. | |
| Valid categories are: refund, technical, shipping, billing, account | |
| For the MEDIUM task: Write a single, complete, helpful reply that addresses the \ | |
| customer's issue. | |
| Include specific actions you are taking (e.g. "I have initiated a refund..."). | |
| Keep it under 150 words. | |
| For the HARD task (multi-turn): | |
| - Turn 1: Ask ONE clarifying question to better understand the issue. | |
| - Turn 2: Provide a concrete solution based on what the customer told you. | |
| - Turn 3: Close the conversation politely \ | |
| (e.g. "Happy to help! Is there anything else I can assist you with?") | |
| """ | |
| # ------------------------------------------------------------------ # | |
| # Groq client | |
| # ------------------------------------------------------------------ # | |
| ai_client = Groq(api_key=GROQ_API_KEY) | |
| def get_agent_reply(conversation: List[str], task_name: str, turn: int) -> str: | |
| """Ask Groq for the next agent reply. | |
| Args: | |
| conversation: Full conversation history so far. | |
| task_name: Current task tier (easy, medium, hard). | |
| turn: Current turn number (1-indexed). | |
| Returns: | |
| The agent's text reply. | |
| """ | |
| # Build the chat messages from conversation history | |
| messages = [{"role": "system", "content": SYSTEM_PROMPT}] | |
| for i, msg in enumerate(conversation): | |
| role = "user" if i % 2 == 0 else "assistant" | |
| messages.append({"role": role, "content": msg}) | |
| # Add a turn-specific hint for hard tasks | |
| if task_name == "hard": | |
| hints = { | |
| 1: "This is turn 1. Ask a clarifying question.", | |
| 2: "This is turn 2. Provide a concrete solution.", | |
| 3: "This is turn 3. Close the conversation politely.", | |
| } | |
| hint = hints.get(turn, "Continue the conversation appropriately.") | |
| messages.append({"role": "system", "content": f"[HINT FOR THIS TURN: {hint}]"}) | |
| try: | |
| response = ai_client.chat.completions.create( | |
| model=MODEL, | |
| messages=messages, | |
| temperature=0.3, | |
| max_tokens=300, | |
| ) | |
| return response.choices[0].message.content.strip() | |
| except Exception as e: | |
| print(f" [Groq error] {e}") | |
| return "I apologize for the inconvenience. Let me help you with that." | |
| # ------------------------------------------------------------------ # | |
| # Environment API helpers | |
| # ------------------------------------------------------------------ # | |
| def env_reset(client: httpx.Client, task_name: str, seed: int) -> Dict[str, Any]: | |
| """POST /reset β start a new episode.""" | |
| response = client.post( | |
| f"{ENV_BASE_URL}/reset", | |
| json={"task_name": task_name, "seed": seed}, | |
| ) | |
| response.raise_for_status() | |
| return response.json() | |
| def env_step(client: httpx.Client, session_id: str, message: str, intent: str = None) -> Dict[str, Any]: | |
| """POST /step β submit an agent action.""" | |
| payload = {"session_id": session_id, "message": message} | |
| if intent: | |
| payload["intent"] = intent | |
| response = client.post(f"{ENV_BASE_URL}/step", json=payload) | |
| response.raise_for_status() | |
| return response.json() | |
| # ------------------------------------------------------------------ # | |
| # Run episodes | |
| # ------------------------------------------------------------------ # | |
| def run_task(client: httpx.Client, task_name: str) -> List[float]: | |
| """Run EPISODES_PER_TASK episodes for a given task tier.""" | |
| rewards = [] | |
| for ep in range(EPISODES_PER_TASK): | |
| try: | |
| reset_data = env_reset(client, task_name, seed=ep) | |
| session_id = reset_data["session_id"] | |
| obs = reset_data.get("observation", {}) | |
| done = obs.get("done", False) | |
| reward = obs.get("reward", None) | |
| turn = 0 | |
| while not done: | |
| turn += 1 | |
| conversation = obs.get("conversation", []) | |
| # Get the agent's reply from Groq | |
| agent_reply = get_agent_reply(conversation, task_name, turn) | |
| if task_name == "easy": | |
| intent = "classify" | |
| elif task_name == "medium": | |
| intent = "respond" | |
| else: | |
| intent_map = {1: "clarify", 2: "respond", 3: "close"} | |
| intent = intent_map.get(turn, "respond") | |
| step_data = env_step(client, session_id, agent_reply, intent) | |
| obs = step_data.get("observation", {}) | |
| done = obs.get("done", False) | |
| reward = obs.get("reward", None) | |
| if turn >= 15: | |
| print(f" [Warning] Episode {ep + 1} exceeded 15 turns, breaking.") | |
| break | |
| episode_reward = reward if reward is not None else 0.0 | |
| rewards.append(episode_reward) | |
| print(f" Episode {ep + 1}/{EPISODES_PER_TASK}: reward = {episode_reward:.2f}") | |
| except Exception as e: | |
| print(f" Episode {ep + 1}/{EPISODES_PER_TASK}: ERROR β {e}") | |
| rewards.append(0.0) | |
| return rewards | |
| # ------------------------------------------------------------------ # | |
| # Main | |
| # ------------------------------------------------------------------ # | |
| def main(): | |
| print("=" * 60) | |
| print(" Customer Support OpenEnv β Baseline Evaluation") | |
| print(f" Model: {MODEL}") | |
| print(f" Server: {ENV_BASE_URL}") | |
| print(f" Episodes per task: {EPISODES_PER_TASK}") | |
| print("=" * 60) | |
| results = {} | |
| with httpx.Client(timeout=60.0) as client: | |
| for task_name in ["easy", "medium", "hard"]: | |
| print(f"\n{'β' * 40}") | |
| print(f" Task: {task_name.upper()}") | |
| print(f"{'β' * 40}") | |
| rewards = run_task(client, task_name) | |
| avg_reward = sum(rewards) / len(rewards) if rewards else 0.0 | |
| results[task_name] = { | |
| "average_score": round(avg_reward, 4), | |
| "scores": [round(r, 4) for r in rewards], | |
| "episodes": len(rewards), | |
| } | |
| print(f"\n{'=' * 60}") | |
| print(" RESULTS SUMMARY") | |
| print(f"{'=' * 60}") | |
| print(f" {'Task':<12} {'Avg Score':<12} {'Episodes':<10} {'Scores'}") | |
| print(f" {'β' * 50}") | |
| for task_name in ["easy", "medium", "hard"]: | |
| r = results[task_name] | |
| scores_str = ", ".join(f"{s:.2f}" for s in r["scores"]) | |
| print(f" {task_name:<12} {r['average_score']:<12.4f} {r['episodes']:<10} [{scores_str}]") | |
| print(f"{'=' * 60}\n") | |
| output_path = os.path.join(os.path.dirname(__file__), "baseline_scores.json") | |
| with open(output_path, "w", encoding="utf-8") as f: | |
| json.dump(results, f, indent=2) | |
| print(f" Results saved to: {output_path}") | |
| if __name__ == "__main__": | |
| main() | |