slop-polis-3-casino-games

This commit is contained in:
Russell Ballestrini 2025-04-05 20:52:58 +00:00
parent d071f6f37e
commit 294e264808
3 changed files with 943 additions and 70 deletions

View file

@ -4,6 +4,8 @@ import os
import json
import expr
from threading import Lock
import random
import uuid
from openai import OpenAI
import logging
from flask_swagger_ui import get_swaggerui_blueprint
@ -14,7 +16,7 @@ LOG_FILE = os.path.join(DATA_DIR, "slop_with_models.log")
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[logging.FileHandler(LOG_FILE), logging.StreamHandler()]
handlers=[logging.FileHandler(LOG_FILE), logging.StreamHandler()],
)
logger = logging.getLogger(__name__)
@ -35,11 +37,16 @@ MODEL_CLIENT_MAP = {}
MEMORY_FILE = os.path.join(DATA_DIR, "memory.json")
memory_lock = Lock()
# Resources setup
RESOURCES_FILE = os.path.join(DATA_DIR, "resources.json")
resource_lock = Lock()
# Ensure data directory exists
if not os.path.exists(DATA_DIR):
os.makedirs(DATA_DIR)
logger.info(f"Created data directory: {DATA_DIR}")
# File-based memory functions
def load_memory_from_file():
try:
@ -51,9 +58,12 @@ def load_memory_from_file():
logger.debug(f"No memory file found at {MEMORY_FILE}, returning empty dict")
return {}
except Exception as e:
logger.error(f"Error loading memory from {MEMORY_FILE}: {str(e)}", exc_info=True)
logger.error(
f"Error loading memory from {MEMORY_FILE}: {str(e)}", exc_info=True
)
return {}
def save_memory_to_file(memory_data):
try:
with open(MEMORY_FILE, "w") as f:
@ -62,6 +72,52 @@ def save_memory_to_file(memory_data):
except Exception as e:
logger.error(f"Error saving memory to {MEMORY_FILE}: {str(e)}", exc_info=True)
# File-based resources functions
def load_resources_from_file():
try:
if os.path.exists(RESOURCES_FILE):
with open(RESOURCES_FILE, "r") as f:
data = json.load(f)
logger.debug(f"Loaded resources from {RESOURCES_FILE}: {data}")
return data
else:
# If file doesn't exist, create it with example resources
initial_resources = {
"hello": {"id": "hello", "content": "Hello, SLOP!"},
"foo/bar": {"id": "foo/bar", "content": "Nested Foo Bar"},
"foo/baz": {"id": "foo/baz", "content": "Nested Foo Baz"},
}
save_resources_to_file(initial_resources)
logger.info(
f"Created new resources file at {RESOURCES_FILE} with initial data"
)
return initial_resources
except Exception as e:
logger.error(
f"Error loading resources from {RESOURCES_FILE}: {str(e)}", exc_info=True
)
# On error, create and return initial resources
initial_resources = {
"hello": {"id": "hello", "content": "Hello, SLOP!"},
"foo/bar": {"id": "foo/bar", "content": "Nested Foo Bar"},
"foo/baz": {"id": "foo/baz", "content": "Nested Foo Baz"},
}
save_resources_to_file(initial_resources)
return initial_resources
def save_resources_to_file(resources_data):
try:
with open(RESOURCES_FILE, "w") as f:
json.dump(resources_data, f)
logger.debug(f"Saved resources to {RESOURCES_FILE}: {resources_data}")
except Exception as e:
logger.error(
f"Error saving resources to {RESOURCES_FILE}: {str(e)}", exc_info=True
)
# Load endpoints
ENDPOINTS = []
for i in range(1000):
@ -79,6 +135,7 @@ for i in range(1000):
else:
logger.debug(f"No endpoint found for MODEL_ENDPOINT_{i}")
def initialize_model_map():
logger.info("Starting model map initialization")
MODEL_CLIENT_MAP.clear()
@ -95,15 +152,21 @@ def initialize_model_map():
logger.debug(f"Attempting to list models from {endpoint_name}")
response = client.models.list()
model_list = response.data
logger.debug(f"Models retrieved from {endpoint_name}: {[m.id for m in model_list]}")
logger.debug(
f"Models retrieved from {endpoint_name}: {[m.id for m in model_list]}"
)
except Exception as e:
logger.error(f"Failed to list models for {endpoint_name}: {str(e)}", exc_info=True)
logger.error(
f"Failed to list models for {endpoint_name}: {str(e)}", exc_info=True
)
continue
for m in model_list:
model_id = m.id
if model_id:
if model_id in MODEL_CLIENT_MAP:
logger.warning(f"Duplicate model ID '{model_id}' found at {endpoint_name}")
logger.warning(
f"Duplicate model ID '{model_id}' found at {endpoint_name}"
)
else:
MODEL_CLIENT_MAP[model_id] = client
logger.info(f"Registered model '{model_id}' from {endpoint_name}")
@ -111,13 +174,18 @@ def initialize_model_map():
logger.warning(f"Model with no ID encountered from {endpoint_name}")
logger.info(f"Model map initialized with models: {list(MODEL_CLIENT_MAP.keys())}")
# SLOP components
tools = {
"calculator": {
"id": "calculator",
"description": "Basic math calculator",
"arguments": [
{ "name": "expression", "type": "str", "description": "the math expression to evaluate."}
{
"name": "expression",
"type": "str",
"description": "the math expression to evaluate.",
}
],
"execute": lambda params: {"result": expr.evaluate(params["expression"])},
},
@ -125,20 +193,399 @@ tools = {
"id": "greet",
"description": "Says hello",
"arguments": [
{ "name": "name", "type": "str", "description": "name of person to greet"}
{"name": "name", "type": "str", "description": "name of person to greet"}
],
"execute": lambda params: {"result": f"Hello, {params['name']}!"},
},
"slots": {
"id": "slots",
"description": "Play a slot machine game with a bet for a specific agent",
"arguments": [
{"name": "bet", "type": "int", "description": "Amount to bet (minimum 1)"},
{
"name": "agent_id",
"type": "str",
"description": "Unique identifier for the agent",
},
],
"execute": lambda params: play_slots(params["bet"], params["agent_id"]),
},
"blackjack": {
"id": "blackjack",
"description": "Play blackjack: start a new game or continue an existing one",
"arguments": [
{
"name": "bet",
"type": "int",
"description": "Amount to bet (required to start new game)",
"optional": True,
},
{
"name": "agent_id",
"type": "str",
"description": "Unique identifier for the agent",
},
{
"name": "game_id",
"type": "str",
"description": "UUID of existing game (optional, omit to start new)",
"optional": True,
},
{
"name": "action",
"type": "str",
"description": "Action: 'hit' or 'stand' (required if game_id provided)",
"optional": True,
},
],
"execute": lambda params: play_blackjack(
params.get("bet"),
params["agent_id"],
params.get("game_id"),
params.get("action"),
),
},
}
resources = {
"hello": {"id": "hello", "content": "Hello, SLOP!"},
"foo/bar": {"id": "foo/bar", "content": "Nested Foo Bar"},
"foo/baz": {"id": "foo/baz", "content": "Nested Foo Baz"},
}
# Load resources from file at startup
resources = load_resources_from_file()
# Helper functions for casino games
def get_or_create_wallet(agent_id):
wallet_key = f"casino/wallet/agent_{agent_id}"
with resource_lock:
if wallet_key not in resources:
resources[wallet_key] = {
"id": wallet_key,
"content": {"balance": 1000, "last_transaction": None},
}
save_resources_to_file(resources)
logger.info(f"Created new wallet for agent {agent_id} with balance 1000")
return resources[wallet_key]["content"]
def get_or_create_stats(game, agent_id):
stats_key = f"casino/{game}/stats/agent_{agent_id}"
with resource_lock:
if stats_key not in resources:
if game == "slots":
resources[stats_key] = {
"id": stats_key,
"content": {
"games_played": 0,
"total_bet": 0,
"total_won": 0,
"wins": 0,
"losses": 0,
},
}
elif game == "blackjack":
resources[stats_key] = {
"id": stats_key,
"content": {
"games_played": 0,
"total_bet": 0,
"total_won": 0,
"wins": 0,
"losses": 0,
"ties": 0,
},
}
save_resources_to_file(resources)
logger.info(f"Created new {game} stats for agent {agent_id}")
return resources[stats_key]["content"]
def update_resource(resource_id, new_content):
with resource_lock:
resources[resource_id]["content"] = new_content
save_resources_to_file(resources)
logger.debug(f"Updated resource {resource_id}: {new_content}")
# Casino game implementations
def play_slots(bet, agent_id):
if bet < 1:
return {"error": "Bet must be at least 1"}
if not agent_id:
return {"error": "agent_id is required"}
wallet = get_or_create_wallet(agent_id)
if wallet["balance"] < bet:
return {
"error": f"Insufficient funds! Current balance: {wallet['balance']}, Bet: {bet}"
}
symbols = ["🍒", "🍋", "🍊", "🍇", "🔔", "💎"]
reels = [random.choice(symbols) for _ in range(3)]
matches = len(set(reels))
if matches == 1:
payout = bet * 10
outcome = "Jackpot! You matched all three symbols."
won = True
elif matches == 2:
payout = bet * 2
outcome = "Two of a kind! You matched two symbols."
won = True
else:
payout = 0
outcome = "No matches. You lose your bet."
won = False
wallet_key = f"casino/wallet/agent_{agent_id}"
wallet["balance"] = wallet["balance"] - bet + payout
wallet["last_transaction"] = {
"game": "slots",
"bet": bet,
"payout": payout,
"timestamp": datetime.now().isoformat(),
}
update_resource(wallet_key, wallet)
stats = get_or_create_stats("slots", agent_id)
stats["games_played"] += 1
stats["total_bet"] += bet
stats["total_won"] += payout
if won:
stats["wins"] += 1
else:
stats["losses"] += 1
update_resource(f"casino/slots/stats/agent_{agent_id}", stats)
explanation = (
"Slot Machine Rules: Bet an amount to spin three reels once. "
"Match all 3 symbols for 10x your bet, 2 symbols for 2x your bet, or lose your bet if no match. "
f"Symbols: {symbols}. "
f"You (agent {agent_id}) bet {bet}. Reels: {reels}. {outcome}"
)
return {
"explanation": explanation,
"game_state": {"reels": reels},
"payout": payout,
"bet": bet,
"agent_id": agent_id,
"new_balance": wallet["balance"],
"game_complete": True,
}
def play_blackjack(bet, agent_id, game_id=None, action=None):
if not agent_id:
return {"error": "agent_id is required"}
# Start a new game
if game_id is None:
if bet is None or bet < 1:
return {"error": "Bet must be at least 1 to start a new game"}
wallet = get_or_create_wallet(agent_id)
if wallet["balance"] < bet:
return {
"error": f"Insufficient funds! Current balance: {wallet['balance']}, Bet: {bet}"
}
game_id = str(uuid.uuid4())
cards = [2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11] * 4
player_hand = [random.choice(cards), random.choice(cards)]
dealer_hand = [random.choice(cards)] # Only show one dealer card initially
player_total = sum(player_hand)
aces = player_hand.count(11)
while player_total > 21 and aces > 0:
player_total -= 10
aces -= 1
game_state = {
"agent_id": agent_id,
"bet": bet,
"player_hand": player_hand,
"player_total": player_total,
"dealer_hand": dealer_hand, # Partial view
"dealer_total": sum(dealer_hand), # Only first card
"status": "active",
}
update_resource(f"casino/blackjack/games/{game_id}", game_state)
explanation = (
"Blackjack Rules: Bet to play against the dealer. Goal is to get closer to 21 without going over. "
"Number cards = face value, Face cards = 10, Ace = 1 or 11 (adjusted automatically). "
"Hit to draw cards, stand to finish. Dealer stands on 17+. Win pays 2x, tie returns bet, loss takes bet. "
f"You (agent {agent_id}) started a new game with bet {bet}. "
f"Your hand: {player_hand} (Total: {player_total}). Dealers up card: {dealer_hand}."
)
return {
"explanation": explanation,
"game_id": game_id,
"game_state": {
"player_hand": player_hand,
"player_total": player_total,
"dealer_hand": dealer_hand,
"dealer_total": sum(dealer_hand),
},
"bet": bet,
"agent_id": agent_id,
"new_balance": wallet["balance"], # Not deducted yet
"game_complete": False,
}
# Continue an existing game
game_key = f"casino/blackjack/games/{game_id}"
with resource_lock:
if game_key not in resources:
return {"error": f"Game {game_id} not found"}
game_state = resources[game_key]["content"]
if game_state["agent_id"] != agent_id:
return {"error": "This game belongs to another agent"}
if game_state["status"] != "active":
return {"error": "This game is already complete"}
if action not in ["hit", "stand"]:
return {"error": "Action must be 'hit' or 'stand'"}
wallet = get_or_create_wallet(agent_id)
cards = [2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11] * 4
if action == "hit":
game_state["player_hand"].append(random.choice(cards))
game_state["player_total"] = sum(game_state["player_hand"])
aces = game_state["player_hand"].count(11)
while game_state["player_total"] > 21 and aces > 0:
game_state["player_total"] -= 10
aces -= 1
if game_state["player_total"] > 21:
game_state["status"] = "bust"
outcome = "Bust! You went over 21 and lose."
payout = 0
result = "loss"
# Update wallet and stats on bust
wallet["balance"] = wallet["balance"] - game_state["bet"]
wallet["last_transaction"] = {
"game": "blackjack",
"bet": game_state["bet"],
"payout": payout,
"timestamp": datetime.now().isoformat(),
}
update_resource(f"casino/wallet/agent_{agent_id}", wallet)
stats = get_or_create_stats("blackjack", agent_id)
stats["games_played"] += 1
stats["total_bet"] += game_state["bet"]
stats["total_won"] += payout
stats["losses"] += 1
update_resource(f"casino/blackjack/stats/agent_{agent_id}", stats)
else:
outcome = "You hit. Game continues."
payout = 0
result = "active"
explanation = (
"Blackjack: You chose to hit. "
f"Your hand: {game_state['player_hand']} (Total: {game_state['player_total']}). "
f"Dealers up card: {game_state['dealer_hand']}. {outcome}"
)
update_resource(game_key, game_state)
return {
"explanation": explanation,
"game_id": game_id,
"game_state": {
"player_hand": game_state["player_hand"],
"player_total": game_state["player_total"],
"dealer_hand": game_state["dealer_hand"],
"dealer_total": game_state["dealer_total"],
},
"bet": game_state["bet"],
"agent_id": agent_id,
"new_balance": wallet["balance"],
"game_complete": game_state["status"] != "active",
}
if action == "stand":
# Reveal dealers full hand
while len(game_state["dealer_hand"]) < 2:
game_state["dealer_hand"].append(random.choice(cards))
game_state["dealer_total"] = sum(game_state["dealer_hand"])
aces = game_state["dealer_hand"].count(11)
while game_state["dealer_total"] < 17:
game_state["dealer_hand"].append(random.choice(cards))
game_state["dealer_total"] = sum(game_state["dealer_hand"])
if game_state["dealer_total"] > 21 and aces > 0:
game_state["dealer_total"] -= 10
aces -= 1
if game_state["dealer_total"] > 21:
outcome = "Dealer busts! You win!"
payout = game_state["bet"] * 2
result = "win"
elif game_state["player_total"] > game_state["dealer_total"]:
outcome = "Your total beats the dealer's! You win!"
payout = game_state["bet"] * 2
result = "win"
elif game_state["dealer_total"] > game_state["player_total"]:
outcome = "Dealers total beats yours. You lose."
payout = 0
result = "loss"
else:
outcome = "Push! Its a tie, your bet is returned."
payout = game_state["bet"]
result = "tie"
game_state["status"] = "complete"
wallet["balance"] = wallet["balance"] - game_state["bet"] + payout
wallet["last_transaction"] = {
"game": "blackjack",
"bet": game_state["bet"],
"payout": payout,
"timestamp": datetime.now().isoformat(),
}
update_resource(f"casino/wallet/agent_{agent_id}", wallet)
stats = get_or_create_stats("blackjack", agent_id)
stats["games_played"] += 1
stats["total_bet"] += game_state["bet"]
stats["total_won"] += payout
if result == "win":
stats["wins"] += 1
elif result == "loss":
stats["losses"] += 1
else:
stats["ties"] += 1
update_resource(f"casino/blackjack/stats/agent_{agent_id}", stats)
update_resource(game_key, game_state)
explanation = (
"Blackjack: You chose to stand. "
f"Your hand: {game_state['player_hand']} (Total: {game_state['player_total']}). "
f"Dealers hand: {game_state['dealer_hand']} (Total: {game_state['dealer_total']}). {outcome}"
)
return {
"explanation": explanation,
"game_id": game_id,
"game_state": {
"player_hand": game_state["player_hand"],
"player_total": game_state["player_total"],
"dealer_hand": game_state["dealer_hand"],
"dealer_total": game_state["dealer_total"],
},
"payout": payout,
"bet": game_state["bet"],
"agent_id": agent_id,
"new_balance": wallet["balance"],
"game_complete": True,
}
# Memory endpoints with lock and timeout
LOCK_TIMEOUT = 2 # Seconds to wait for lock acquisition
@app.route("/memory", methods=["POST"])
def store_memory():
logger.info("Received /memory POST request")
@ -159,9 +606,12 @@ def store_memory():
finally:
memory_lock.release()
else:
logger.error(f"Failed to acquire lock for storing {key} within {LOCK_TIMEOUT} seconds")
logger.error(
f"Failed to acquire lock for storing {key} within {LOCK_TIMEOUT} seconds"
)
return jsonify({"error": "Memory lock timeout"}), 503
@app.route("/memory/<key>", methods=["GET"])
def get_memory(key):
logger.info(f"Received /memory/{key} GET request")
@ -176,9 +626,12 @@ def get_memory(key):
finally:
memory_lock.release()
else:
logger.error(f"Failed to acquire lock for retrieving {key} within {LOCK_TIMEOUT} seconds")
logger.error(
f"Failed to acquire lock for retrieving {key} within {LOCK_TIMEOUT} seconds"
)
return jsonify({"error": "Memory lock timeout"}), 503
@app.route("/memory", methods=["GET"])
def list_memory():
logger.info("Received /memory GET request")
@ -193,9 +646,12 @@ def list_memory():
finally:
memory_lock.release()
else:
logger.error(f"Failed to acquire lock for listing memory within {LOCK_TIMEOUT} seconds")
logger.error(
f"Failed to acquire lock for listing memory within {LOCK_TIMEOUT} seconds"
)
return jsonify({"error": "Memory lock timeout"}), 503
@app.route("/memory/<key>", methods=["DELETE"])
def delete_memory(key):
logger.info(f"Received /memory/{key} DELETE request")
@ -214,10 +670,13 @@ def delete_memory(key):
finally:
memory_lock.release()
else:
logger.error(f"Failed to acquire lock for deleting {key} within {LOCK_TIMEOUT} seconds")
logger.error(
f"Failed to acquire lock for deleting {key} within {LOCK_TIMEOUT} seconds"
)
return jsonify({"error": "Memory lock timeout"}), 503
# Other endpoints (unchanged for brevity)
# Other endpoints
@app.route("/chat", methods=["POST"])
def chat():
logger.info("Received /chat request")
@ -244,19 +703,14 @@ def chat():
response_content = response.choices[0].message.content
logger.debug(f"Chat response from {model_id}: {response_content}")
return (
jsonify(
{
"choices": [
{"message": {"content": response_content}}
]
}
),
jsonify({"choices": [{"message": {"content": response_content}}]}),
200,
)
except Exception as e:
logger.error(f"Chat error with model {model_id}: {str(e)}", exc_info=True)
return jsonify({"error": str(e)}), 500
@app.route("/models", methods=["GET"])
def list_models():
logger.info("Received /models request")
@ -264,13 +718,18 @@ def list_models():
logger.debug(f"Returning models: {models}")
return jsonify({"models": models}), 200
@app.route("/tools", methods=["GET"])
def list_tools():
logger.info("Received /tools request")
tool_list = [{"id": k, "description": v["description"], "arguments": v.get("arguments", [])} for k, v in tools.items()]
tool_list = [
{"id": k, "description": v["description"], "arguments": v.get("arguments", [])}
for k, v in tools.items()
]
logger.debug(f"Returning tools: {tool_list}")
return jsonify({"tools": tool_list}), 200
@app.route("/tools/<tool_id>", methods=["POST"])
def use_tool(tool_id):
logger.info(f"Received /tools/{tool_id} request")
@ -279,10 +738,10 @@ def use_tool(tool_id):
return jsonify({"error": "Tool not found"}), 404
data = request.json or {}
logger.debug(f"Tool {tool_id} input data: {data}")
if "arguments" in tools[tool_id]:
for arg in tools[tool_id]["arguments"]:
if arg["name"] not in data:
if arg["name"] not in data and "optional" not in arg:
logger.warning(f"Missing '{arg['name']}' for {tool_id} tool")
return jsonify({"error": f"Missing '{arg['name']}' parameter"}), 400
@ -294,48 +753,54 @@ def use_tool(tool_id):
logger.error(f"Error executing tool {tool_id}: {str(e)}", exc_info=True)
return jsonify({"error": str(e)}), 500
@app.route("/resources", methods=["GET"])
def list_resources():
logger.info("Received /resources request")
resource_list = list(resources.values())
with resource_lock:
resource_list = list(resources.values())
logger.debug(f"Returning resources: {resource_list}")
return jsonify({"resources": resource_list}), 200
@app.route("/resources/<resource_id>", methods=["GET"])
def get_resource(resource_id):
logger.info(f"Received /resources/{resource_id} GET request")
if resource_id in resources:
resource = resources[resource_id]
logger.debug(f"Exact match found for {resource_id}: {resource}")
return jsonify(resource), 200
else:
# Prefix search for nested resources
prefix = f"{resource_id}/"
matching_resources = [
res for key, res in resources.items() if key.startswith(prefix)
]
if matching_resources:
logger.debug(f"Prefix search for {resource_id} found: {matching_resources}")
return jsonify({"resources": matching_resources}), 200
logger.warning(f"No resource or nested resources found for {resource_id}")
return jsonify({"error": "Resource not found"}), 404
with resource_lock:
if resource_id in resources:
resource = resources[resource_id]
logger.debug(f"Exact match found for {resource_id}: {resource}")
return jsonify(resource), 200
else:
# Prefix search for nested resources
prefix = f"{resource_id}/"
matching_resources = [
res for key, res in resources.items() if key.startswith(prefix)
]
if matching_resources:
logger.debug(
f"Prefix search for {resource_id} found: {matching_resources}"
)
return jsonify({"resources": matching_resources}), 200
logger.warning(f"No resource or nested resources found for {resource_id}")
return jsonify({"error": "Resource not found"}), 404
@app.route("/resources/<resource_id>", methods=["PUT"])
def update_resource(resource_id):
def update_resource_endpoint(resource_id):
logger.info(f"Received /resources/{resource_id} PUT request")
data = request.json
logger.debug(f"Update resource data: {data}")
if not data or "content" not in data:
logger.warning(f"Invalid resource update request: {data}")
return jsonify({"error": "Missing 'content'"}), 400
# Update or create the resource
resources[resource_id] = {
"id": resource_id,
"content": data["content"]
}
with resource_lock:
resources[resource_id] = {"id": resource_id, "content": data["content"]}
save_resources_to_file(resources)
logger.info(f"Updated/created resource {resource_id}: {resources[resource_id]}")
return jsonify({"status": "updated", "resource": resources[resource_id]}), 200
@app.route("/pay", methods=["POST"])
def pay():
logger.info("Received /pay request")
@ -354,6 +819,7 @@ def pay():
200,
)
# Initialize model map on startup
logger.info("Starting application initialization")
initialize_model_map()
@ -362,4 +828,4 @@ logger.info("Application initialization completed")
if __name__ == "__main__":
logger.info("Starting Flask application on port 31337")
app.run(debug=True, port=31337)
logger.info("Flask application stopped")
logger.info("Flask application stopped")

View file

@ -1,12 +1,12 @@
openapi: 3.0.0
info:
title: SLOP API
description: A SLOP pattern implementation with dynamic model endpoints, tools, memory, resources, and payment simulation.
description: A SLOP pattern implementation with dynamic model endpoints, tools, memory, resources, and payment simulation, including casino games.
version: 1.0.0
servers:
- url: https://slop.unturf.com
description: Production unturf. SLOP server
description: Production unturf SLOP server
- url: http://localhost:31337
description: Local development SLOP server
@ -76,20 +76,101 @@ paths:
required: true
schema:
type: string
enum: [calculator, greet]
enum: [calculator, greet, slots, blackjack]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ToolRequest'
oneOf:
- $ref: '#/components/schemas/CalculatorToolRequest'
- $ref: '#/components/schemas/GreetToolRequest'
- $ref: '#/components/schemas/SlotsToolRequest'
- $ref: '#/components/schemas/BlackjackToolRequest'
examples:
calculator:
summary: Calculator tool request
value:
expression: "2 + 2"
greet:
summary: Greet tool request
value:
name: "Alice"
slots:
summary: Slots game request
value:
bet: 50
agent_id: "player1"
blackjackStart:
summary: Start a new blackjack game
value:
bet: 100
agent_id: "player1"
blackjackHit:
summary: Hit in an existing blackjack game
value:
agent_id: "player1"
game_id: "550e8400-e29b-41d4-a716-446655440000"
action: "hit"
responses:
'200':
description: Tool execution result
content:
application/json:
schema:
$ref: '#/components/schemas/ToolResponse'
oneOf:
- $ref: '#/components/schemas/CalculatorToolResponse'
- $ref: '#/components/schemas/GreetToolResponse'
- $ref: '#/components/schemas/SlotsToolResponse'
- $ref: '#/components/schemas/BlackjackToolResponse'
examples:
calculator:
summary: Calculator result
value:
result: 4
greet:
summary: Greet result
value:
result: "Hello, Alice!"
slots:
summary: Slots game result
value:
explanation: "Slot Machine Rules: Bet an amount to spin three reels once. Match all 3 symbols for 10x your bet, 2 symbols for 2x your bet, or lose your bet if no match. Symbols: ['🍒', '🍋', '🍊', '🍇', '🔔', '💎']. You (agent player1) bet 50. Reels: ['🍒', '🍒', '🍒']. Jackpot! You matched all three symbols."
game_state:
reels: ["🍒", "🍒", "🍒"]
payout: 500
bet: 50
agent_id: "player1"
new_balance: 1450
game_complete: true
blackjackStart:
summary: Start of a blackjack game
value:
explanation: "Blackjack Rules: Bet to play against the dealer. Goal is to get closer to 21 without going over. Number cards = face value, Face cards = 10, Ace = 1 or 11 (adjusted automatically). Hit to draw cards, stand to finish. Dealer stands on 17+. Win pays 2x, tie returns bet, loss takes bet. You (agent player1) started a new game with bet 100. Your hand: [10, 7] (Total: 17). Dealers up card: [6]."
game_id: "550e8400-e29b-41d4-a716-446655440000"
game_state:
player_hand: [10, 7]
player_total: 17
dealer_hand: [6]
dealer_total: 6
bet: 100
agent_id: "player1"
new_balance: 1000
game_complete: false
blackjackHitBust:
summary: Hit resulting in bust
value:
explanation: "Blackjack: You chose to hit. Your hand: [10, 7, 8] (Total: 25). Dealers up card: [6]. Bust! You went over 21 and lose."
game_id: "550e8400-e29b-41d4-a716-446655440000"
game_state:
player_hand: [10, 7, 8]
player_total: 25
dealer_hand: [6]
dealer_total: 6
bet: 100
agent_id: "player1"
new_balance: 900
game_complete: true
'400':
description: Missing required parameter
content:
@ -102,6 +183,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: Server error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/memory:
get:
summary: List all memory keys
@ -252,6 +339,18 @@ paths:
content: "Nested Foo Bar"
- id: "foo/baz"
content: "Nested Foo Baz"
blackjackGame:
summary: Blackjack game state
value:
id: "casino/blackjack/games/550e8400-e29b-41d4-a716-446655440000"
content:
agent_id: "player1"
bet: 100
player_hand: [10, 7]
player_total: 17
dealer_hand: [6]
dealer_total: 6
status: "active"
'404':
description: No resource or nested resources found
content:
@ -307,6 +406,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/PayResponse'
components:
schemas:
ChatRequest:
@ -370,29 +470,192 @@ components:
type: string
description:
type: string
arguments:
type: array
items:
type: object
properties:
name:
type: string
type:
type: string
description:
type: string
optional:
type: boolean
required:
- name
- type
- description
required:
- id
- description
- arguments
required:
- tools
ToolRequest:
CalculatorToolRequest:
type: object
properties:
expression:
type: string
nullable: true
description: The math expression to evaluate (e.g., "2 + 2")
required:
- expression
GreetToolRequest:
type: object
properties:
name:
type: string
description: The name of the person to greet
required:
- name
SlotsToolRequest:
type: object
properties:
bet:
type: integer
minimum: 1
description: Amount to bet on the slot spin
agent_id:
type: string
description: Unique identifier for the agent/player
required:
- bet
- agent_id
BlackjackToolRequest:
type: object
properties:
bet:
type: integer
minimum: 1
description: Amount to bet (required to start a new game)
nullable: true
ToolResponse:
agent_id:
type: string
description: Unique identifier for the agent/player
game_id:
type: string
description: UUID of an existing game (required for hit/stand actions)
nullable: true
action:
type: string
enum: [hit, stand]
description: Action to take in an existing game (required if game_id is provided)
nullable: true
required:
- agent_id
CalculatorToolResponse:
type: object
properties:
result:
oneOf:
- type: string
- type: integer
type: integer
description: The result of the calculation
required:
- result
GreetToolResponse:
type: object
properties:
result:
type: string
description: The greeting message
required:
- result
SlotsToolResponse:
type: object
properties:
explanation:
type: string
description: Description of the game rules and outcome
game_state:
type: object
properties:
reels:
type: array
items:
type: string
description: The symbols on the slot reels
required:
- reels
payout:
type: integer
description: Amount won from the spin (0 if lost)
bet:
type: integer
description: The amount bet on the spin
agent_id:
type: string
description: The agent/player identifier
new_balance:
type: integer
description: Updated balance after the spin
game_complete:
type: boolean
description: Indicates if the game is complete (always true for slots)
required:
- explanation
- game_state
- payout
- bet
- agent_id
- new_balance
- game_complete
BlackjackToolResponse:
type: object
properties:
explanation:
type: string
description: Description of the game rules and current action outcome
game_id:
type: string
description: UUID of the blackjack game (returned when starting a new game)
nullable: true
game_state:
type: object
properties:
player_hand:
type: array
items:
type: integer
description: Cards in the player's hand
player_total:
type: integer
description: Total value of the player's hand
dealer_hand:
type: array
items:
type: integer
description: Cards in the dealer's hand (partial view until game ends)
dealer_total:
type: integer
description: Total value of the dealer's hand (partial until game ends)
required:
- player_hand
- player_total
- dealer_hand
- dealer_total
payout:
type: integer
description: Amount won (0 if ongoing or lost, set when game completes)
nullable: true
bet:
type: integer
description: The amount bet on the game
agent_id:
type: string
description: The agent/player identifier
new_balance:
type: integer
description: Updated balance (updated only when game completes)
game_complete:
type: boolean
description: Indicates if the game is complete (true on bust or stand)
required:
- explanation
- game_state
- bet
- agent_id
- new_balance
- game_complete
MemoryStoreRequest:
type: object
properties:
@ -439,7 +702,8 @@ components:
id:
type: string
content:
type: string
type: object
additionalProperties: true
required:
- id
- content
@ -451,7 +715,8 @@ components:
id:
type: string
content:
type: string
type: object
additionalProperties: true
required:
- id
- content
@ -499,4 +764,4 @@ components:
error:
type: string
required:
- error
- error

View file

@ -1,4 +1,3 @@
# streamlit_slop_with_models.py
import streamlit as st
import requests
@ -130,6 +129,145 @@ def tools_interface():
except requests.RequestException as e:
st.error(f"Error: {str(e)}")
elif tool_id == "slots":
st.subheader("Slots")
agent_id = st.text_input("Your Agent ID", value="player1")
bet = st.number_input("Bet Amount", min_value=1, step=1, value=10)
if st.button("Spin"):
try:
response = requests.post(
f"{BASE_URL}/tools/slots",
json={"bet": bet, "agent_id": agent_id},
timeout=5,
)
response.raise_for_status()
result = response.json()
if "error" in result:
st.error(f"Error: {result['error']}")
else:
st.write(f"**Result**: {result['explanation']}")
st.write(f"**Reels**: {result['game_state']['reels']}")
st.write(f"**Payout**: {result['payout']}")
st.write(f"**New Balance**: {result['new_balance']}")
except requests.RequestException as e:
st.error(f"Error: {str(e)}")
elif tool_id == "blackjack":
st.subheader("Blackjack")
agent_id = st.text_input(
"Your Agent ID", value="player1", key="blackjack_agent"
)
# Initialize session state for blackjack game ID
if "blackjack_game_id" not in st.session_state:
st.session_state.blackjack_game_id = None
# Start a new game
if st.session_state.blackjack_game_id is None:
bet = st.number_input("Bet Amount", min_value=1, step=1, value=10)
if st.button("Start Game"):
try:
response = requests.post(
f"{BASE_URL}/tools/blackjack",
json={"bet": bet, "agent_id": agent_id},
timeout=5,
)
response.raise_for_status()
result = response.json()
if "error" in result:
st.error(f"Error: {result['error']}")
else:
st.session_state.blackjack_game_id = result["game_id"]
st.write(f"**Game Started**: {result['explanation']}")
st.write(
f"**Your Hand**: {result['game_state']['player_hand']} (Total: {result['game_state']['player_total']})"
)
st.write(
f"**Dealers Up Card**: {result['game_state']['dealer_hand']}"
)
st.rerun()
except requests.RequestException as e:
st.error(f"Error: {str(e)}")
else:
# Display current game state and allow hit/stand
try:
response = requests.get(
f"{BASE_URL}/resources/casino/blackjack/games/{st.session_state.blackjack_game_id}",
timeout=5,
)
response.raise_for_status()
game_state = response.json()["content"]
st.write(
f"**Current Game (ID: {st.session_state.blackjack_game_id})**:"
)
st.write(
f"**Your Hand**: {game_state['player_hand']} (Total: {game_state['player_total']})"
)
st.write(f"**Dealers Up Card**: {game_state['dealer_hand']}")
st.write(f"**Bet**: {game_state['bet']}")
col1, col2 = st.columns(2)
with col1:
if st.button("Hit"):
try:
response = requests.post(
f"{BASE_URL}/tools/blackjack",
json={
"agent_id": agent_id,
"game_id": st.session_state.blackjack_game_id,
"action": "hit",
},
timeout=5,
)
response.raise_for_status()
result = response.json()
if "error" in result:
st.error(f"Error: {result['error']}")
else:
st.write(f"**Result**: {result['explanation']}")
if result["game_complete"]:
st.write(f"**Payout**: {result['payout']}")
st.write(
f"**New Balance**: {result['new_balance']}"
)
st.session_state.blackjack_game_id = None
st.rerun()
except requests.RequestException as e:
st.error(f"Error: {str(e)}")
with col2:
if st.button("Stand"):
try:
response = requests.post(
f"{BASE_URL}/tools/blackjack",
json={
"agent_id": agent_id,
"game_id": st.session_state.blackjack_game_id,
"action": "stand",
},
timeout=5,
)
response.raise_for_status()
result = response.json()
if "error" in result:
st.error(f"Error: {result['error']}")
else:
st.write(f"**Result**: {result['explanation']}")
st.write(
f"**Your Hand**: {result['game_state']['player_hand']} (Total: {result['game_state']['player_total']})"
)
st.write(
f"**Dealers Hand**: {result['game_state']['dealer_hand']} (Total: {result['game_state']['dealer_total']})"
)
st.write(f"**Payout**: {result['payout']}")
st.write(f"**New Balance**: {result['new_balance']}")
st.session_state.blackjack_game_id = None
st.rerun()
except requests.RequestException as e:
st.error(f"Error: {str(e)}")
except requests.RequestException as e:
st.error(f"Error fetching game state: {str(e)}")
st.session_state.blackjack_game_id = None # Reset if game not found
def memory_interface():
st.header("Memory")
@ -190,7 +328,7 @@ def memory_interface():
def resources_interface():
st.header("Resources")
# Fetch available resources
try:
response = requests.get(f"{BASE_URL}/resources", timeout=5)
@ -228,18 +366,22 @@ def resources_interface():
new_resource_id = st.text_input("Resource ID", value=resource_id)
content = st.text_area("Content", height=100)
submit_button = st.form_submit_button(label="Update/Create", type="primary")
if submit_button and new_resource_id and content:
try:
response = requests.put(
f"{BASE_URL}/resources/{new_resource_id}",
json={"content": content},
timeout=5
timeout=5,
)
response.raise_for_status()
result = response.json()
st.success(f"Resource {result['resource']['id']} updated/created successfully!")
st.write(f"**{result['resource']['id']}**: {result['resource']['content']}")
st.success(
f"Resource {result['resource']['id']} updated/created successfully!"
)
st.write(
f"**{result['resource']['id']}**: {result['resource']['content']}"
)
except requests.RequestException as e:
st.error(f"Error: {str(e)}")