""" Gradio App - Text Adventure AI Agent Assignment A simple interface for the text adventure AI agent assignment. """ import gradio as gr from huggingface_hub import HfApi from datetime import datetime import json TITLE = "Playing Zork has never been so boring" DESCRIPTION = """ Build AI agents to play classic text adventure games (Zork, Colossal Cave, Enchanter, etc.) using the Model Context Protocol (MCP) and HuggingFace models. This project provides: - **MCP Server** - Exposes text adventure games as MCP tools using FastMCP - **ReAct Agent** - An agent that uses MCP tools to play games with reasoning - **Submission Template** - Starter code for students to implement their own solutions - **Evaluation System** - Deterministic evaluation with seeded runs - **57 Games** - Zork trilogy, Infocom classics, and many more Z-machine games """ CLONE_INSTRUCTIONS = """ ## Getting Started ### 0. Clone this space ```bash git clone https://huggingface.co/spaces/LLM-course/Agentic-zork ``` This includes: - run_agent.py: Script to run agents on text adventure games - evaluation/: Evaluation scripts and utilities - games/: Text adventure game environments - submission_template/: Template for your agent submission ### 1. Fork the template space Fork the template space on Hugging Face: ``` https://huggingface.co/spaces/LLM-course/text-adventure-template ``` ### 2. Clone your fork locally ```bash git clone https://huggingface.co/spaces/YOUR_USERNAME/text-adventure-agent ``` ### 3. Implement your agent Edit these files: - `agent.py` - Your ReAct agent implementation (implement the `StudentAgent` class) - `mcp_server.py` - Your MCP server implementation (add tools like `play_action`, `memory`, etc.) ### 4. Test locally ```bash # Test MCP server interactively fastmcp dev mcp_server.py # Run your agent python run_agent.py --agent . --game lostpig -v -n 20 ``` ### 5. Push to your space ### 6. Submit your space URL """ DATASET_REPO = "LLM-course/zork-submission" SUBMISSIONS_FILE_JSONL = "submissions.jsonl" SUBMISSIONS_FILE_JSON = "submissions.json" def submit_space(space_url: str, profile: gr.OAuthProfile | None) -> str: """Submit a space URL to the dataset.""" if profile is None: return "Please log in with your HuggingFace account first (button above)." if not space_url or not space_url.strip(): return "Please enter your Space URL." space_url = space_url.strip() # Validate URL format if not ("huggingface.co/spaces/" in space_url or "hf.co/spaces/" in space_url): return "Invalid Space URL. It should look like: https://huggingface.co/spaces/username/space-name" username = profile.username try: api = HfApi() # Try to load existing submissions from JSONL first try: submissions_path = api.hf_hub_download( repo_id=DATASET_REPO, filename=SUBMISSIONS_FILE_JSONL, repo_type="dataset", ) with open(submissions_path, "r") as f: submissions = [ json.loads(line) for line in f if line.strip() ] except Exception: # Fallback for backward compatibility with old JSON format try: submissions_path = api.hf_hub_download( repo_id=DATASET_REPO, filename=SUBMISSIONS_FILE_JSON, repo_type="dataset", ) with open(submissions_path, "r") as f: submissions = json.load(f) except Exception: submissions = [] # Backward compatibility: migrate old dict format to # row-based list format if isinstance(submissions, dict): submissions = [ { "username": user, "space_url": data.get("space_url", ""), "submitted_at": data.get("submitted_at", ""), } for user, data in submissions.items() ] elif not isinstance(submissions, list): submissions = [] # Update with new submission (overwrites previous for same user) new_submission = { "username": username, "space_url": space_url, "submitted_at": datetime.now().isoformat(), } existing_index = next( ( index for index, entry in enumerate(submissions) if entry.get("username") == username ), None, ) if existing_index is not None: submissions[existing_index] = new_submission else: submissions.append(new_submission) # Save back to dataset as JSONL (one JSON object per line) submissions_jsonl = "\n".join( json.dumps(entry, ensure_ascii=False) for entry in submissions ) + "\n" api.upload_file( path_or_fileobj=submissions_jsonl.encode(), path_in_repo=SUBMISSIONS_FILE_JSONL, repo_id=DATASET_REPO, repo_type="dataset", commit_message=f"Update submission for {username}", ) return f"Submission successful! Space URL '{space_url}' recorded for user '{username}'." except Exception as e: return f"Error submitting: {str(e)}" demo = gr.Blocks(title=TITLE) with demo: gr.Markdown(f"# {TITLE}") gr.Markdown(DESCRIPTION) gr.Markdown("---") gr.Markdown(CLONE_INSTRUCTIONS) # Submission section gr.LoginButton() space_input = gr.Textbox( label="Your Space URL", placeholder="https://huggingface.co/spaces/your-username/text-adventure-agent", ) submit_btn = gr.Button("Click HERE to Submit", variant="primary") result_text = gr.Textbox(label="Status", interactive=False) submit_btn.click( fn=submit_space, inputs=[space_input], outputs=[result_text], ) if __name__ == "__main__": demo.launch()