ryomo's picture
feat: add available advice options and list functionality
abe8a34
raw
history blame
7.49 kB
import os
import sys
# Add src directory to Python path for Hugging Face Spaces compatibility
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
import logging
import uuid
import gradio as gr
import spaces
from unpredictable_lord.chat import chat_with_llm_stream
from unpredictable_lord.game_state import (
ADVICE_DESCRIPTIONS,
PERSONALITY_DESCRIPTIONS,
create_session,
get_available_advice,
get_session,
)
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
logger.info(f"ZeroGPU: {spaces.config.Config.zero_gpu}")
def init_game(personality: str = "cautious") -> dict:
"""
Initialize a new game session and return the session information.
This MCP tool creates a new game session with the specified lord personality.
Use the returned session_id for all subsequent game operations.
Args:
personality: The lord's personality type.
Options: "cautious" (risk-averse), "idealist" (emotional idealist),
"populist" (popularity-focused). Defaults to "cautious".
Returns:
dict: Session information including session_id and initial game state.
"""
session_id = str(uuid.uuid4())
# Validate personality
if personality not in PERSONALITY_DESCRIPTIONS:
personality = "cautious"
# Create game session using game_state module
state = create_session(session_id, personality)
logger.info(
f"New game session created: {session_id} with personality: {personality}"
)
return {
"session_id": session_id,
"message": f"New game started! You are now the advisor to a {personality} lord.",
"personality_description": PERSONALITY_DESCRIPTIONS[personality],
"available_advice": ADVICE_DESCRIPTIONS,
"game_state": state.to_dict(),
}
def get_game_state(session_id: str) -> dict:
"""
Get the current game state for a session.
This MCP tool retrieves the current state of a game session,
including all parameters, turn number, and game status.
Args:
session_id: The session ID returned from init_game.
Returns:
dict: Current game state or error message if session not found.
"""
state = get_session(session_id)
if state is None:
return {
"error": "Session not found",
"message": f"No game session found with ID: {session_id}. Please call init_game first.",
}
return {
"session_id": session_id,
"game_state": state.to_dict(),
"status_summary": state.get_status_summary(),
}
def list_available_advice() -> dict:
"""
Get all available advice options that can be given to the lord.
This MCP tool returns a list of all possible advice types that can be
used with execute_turn(). The Lord AI should interpret the user's
free-form suggestion and map it to one of these options.
Returns:
dict: Dictionary containing all available advice options with
their names, descriptions, and expected effects.
"""
return {
"advice_options": get_available_advice(),
"usage": "Interpret the user's advice and select the most appropriate option from the list above.",
}
# Gradio UI
with gr.Blocks(title="Unpredictable Lord") as demo:
gr.Markdown("# Unpredictable Lord\nLord Advisor AI Simulation")
with gr.Tabs():
# Chat Tab
with gr.TabItem("Chat"):
chatbot = gr.Chatbot(label="Lord AI", height=600, type="messages")
with gr.Row():
msg = gr.Textbox(
label="Your Advice",
placeholder="My Lord, I have a proposal...",
scale=4,
)
submit_btn = gr.Button("Submit", scale=1)
clear = gr.Button("Clear History")
def user(user_message, history):
# Append user message to history in messages format
return "", history + [{"role": "user", "content": user_message}]
def bot(history):
# The last message is the user's message
user_message = history[-1]["content"]
history_for_model = history[:-1]
for updated_history in chat_with_llm_stream(
user_message, history_for_model
):
yield updated_history
msg.submit(
user, [msg, chatbot], [msg, chatbot], queue=False, show_api=False
).then(bot, chatbot, chatbot, show_api=False)
submit_btn.click(
user, [msg, chatbot], [msg, chatbot], queue=False, show_api=False
).then(bot, chatbot, chatbot, show_api=False)
clear.click(lambda: None, None, chatbot, queue=False, show_api=False)
# MCP Server Tab
with gr.TabItem("MCP Server"):
gr.Markdown(
"""
## MCP Server Guide
This application functions as an **MCP (Model Context Protocol) Server**.
External LLMs can connect to this server and use game management tools.
### Connection URL
```
https://<space-name>.hf.space/gradio_api/mcp/
```
For local development:
```
http://localhost:7860/gradio_api/mcp/
```
### How to Connect
#### Claude Desktop / Cursor / VS Code
Add the following to your MCP settings configuration:
```json
{
"mcpServers": {
"unpredictable-lord": {
"url": "https://<space-name>.hf.space/gradio_api/mcp/"
}
}
}
```
### Available Tools
| Tool | Description |
|------|-------------|
| `init_game` | Initialize a new game session. Returns a session_id and available advice options. |
| `get_game_state` | Get the current game state for a session. |
| `list_available_advice` | Get all available advice options for execute_turn. |
### Usage Flow
1. Call `init_game(personality)` to start a new game session
2. Use the returned `session_id` for all subsequent game operations
3. Game state is managed server-side
"""
)
gr.Markdown("### Test: Initialize Game")
with gr.Row():
personality_input = gr.Dropdown(
choices=["cautious", "idealist", "populist"],
value="cautious",
label="Lord Personality",
)
init_btn = gr.Button("Start New Game")
init_output = gr.JSON(label="Game Session Info")
init_btn.click(fn=init_game, inputs=personality_input, outputs=init_output)
gr.Markdown("### Test: Get Game State")
with gr.Row():
session_id_input = gr.Textbox(
label="Session ID",
placeholder="Enter session_id from init_game",
)
get_state_btn = gr.Button("Get State")
state_output = gr.JSON(label="Current Game State")
get_state_btn.click(
fn=get_game_state, inputs=session_id_input, outputs=state_output
)
gr.Markdown("### Test: List Available Advice")
list_advice_btn = gr.Button("List Advice Options")
advice_output = gr.JSON(label="Available Advice Options")
list_advice_btn.click(
fn=list_available_advice, inputs=[], outputs=advice_output
)
if __name__ == "__main__":
demo.launch(mcp_server=True)