slop-polis-3-casino-games
This commit is contained in:
parent
d071f6f37e
commit
294e264808
3 changed files with 943 additions and 70 deletions
|
|
@ -4,6 +4,8 @@ import os
|
||||||
import json
|
import json
|
||||||
import expr
|
import expr
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
import random
|
||||||
|
import uuid
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
import logging
|
import logging
|
||||||
from flask_swagger_ui import get_swaggerui_blueprint
|
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(
|
logging.basicConfig(
|
||||||
level=logging.DEBUG,
|
level=logging.DEBUG,
|
||||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -35,11 +37,16 @@ MODEL_CLIENT_MAP = {}
|
||||||
MEMORY_FILE = os.path.join(DATA_DIR, "memory.json")
|
MEMORY_FILE = os.path.join(DATA_DIR, "memory.json")
|
||||||
memory_lock = Lock()
|
memory_lock = Lock()
|
||||||
|
|
||||||
|
# Resources setup
|
||||||
|
RESOURCES_FILE = os.path.join(DATA_DIR, "resources.json")
|
||||||
|
resource_lock = Lock()
|
||||||
|
|
||||||
# Ensure data directory exists
|
# Ensure data directory exists
|
||||||
if not os.path.exists(DATA_DIR):
|
if not os.path.exists(DATA_DIR):
|
||||||
os.makedirs(DATA_DIR)
|
os.makedirs(DATA_DIR)
|
||||||
logger.info(f"Created data directory: {DATA_DIR}")
|
logger.info(f"Created data directory: {DATA_DIR}")
|
||||||
|
|
||||||
|
|
||||||
# File-based memory functions
|
# File-based memory functions
|
||||||
def load_memory_from_file():
|
def load_memory_from_file():
|
||||||
try:
|
try:
|
||||||
|
|
@ -51,9 +58,12 @@ def load_memory_from_file():
|
||||||
logger.debug(f"No memory file found at {MEMORY_FILE}, returning empty dict")
|
logger.debug(f"No memory file found at {MEMORY_FILE}, returning empty dict")
|
||||||
return {}
|
return {}
|
||||||
except Exception as e:
|
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 {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def save_memory_to_file(memory_data):
|
def save_memory_to_file(memory_data):
|
||||||
try:
|
try:
|
||||||
with open(MEMORY_FILE, "w") as f:
|
with open(MEMORY_FILE, "w") as f:
|
||||||
|
|
@ -62,6 +72,52 @@ def save_memory_to_file(memory_data):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error saving memory to {MEMORY_FILE}: {str(e)}", exc_info=True)
|
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
|
# Load endpoints
|
||||||
ENDPOINTS = []
|
ENDPOINTS = []
|
||||||
for i in range(1000):
|
for i in range(1000):
|
||||||
|
|
@ -79,6 +135,7 @@ for i in range(1000):
|
||||||
else:
|
else:
|
||||||
logger.debug(f"No endpoint found for MODEL_ENDPOINT_{i}")
|
logger.debug(f"No endpoint found for MODEL_ENDPOINT_{i}")
|
||||||
|
|
||||||
|
|
||||||
def initialize_model_map():
|
def initialize_model_map():
|
||||||
logger.info("Starting model map initialization")
|
logger.info("Starting model map initialization")
|
||||||
MODEL_CLIENT_MAP.clear()
|
MODEL_CLIENT_MAP.clear()
|
||||||
|
|
@ -95,15 +152,21 @@ def initialize_model_map():
|
||||||
logger.debug(f"Attempting to list models from {endpoint_name}")
|
logger.debug(f"Attempting to list models from {endpoint_name}")
|
||||||
response = client.models.list()
|
response = client.models.list()
|
||||||
model_list = response.data
|
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:
|
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
|
continue
|
||||||
for m in model_list:
|
for m in model_list:
|
||||||
model_id = m.id
|
model_id = m.id
|
||||||
if model_id:
|
if model_id:
|
||||||
if model_id in MODEL_CLIENT_MAP:
|
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:
|
else:
|
||||||
MODEL_CLIENT_MAP[model_id] = client
|
MODEL_CLIENT_MAP[model_id] = client
|
||||||
logger.info(f"Registered model '{model_id}' from {endpoint_name}")
|
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.warning(f"Model with no ID encountered from {endpoint_name}")
|
||||||
logger.info(f"Model map initialized with models: {list(MODEL_CLIENT_MAP.keys())}")
|
logger.info(f"Model map initialized with models: {list(MODEL_CLIENT_MAP.keys())}")
|
||||||
|
|
||||||
|
|
||||||
# SLOP components
|
# SLOP components
|
||||||
tools = {
|
tools = {
|
||||||
"calculator": {
|
"calculator": {
|
||||||
"id": "calculator",
|
"id": "calculator",
|
||||||
"description": "Basic math calculator",
|
"description": "Basic math calculator",
|
||||||
"arguments": [
|
"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"])},
|
"execute": lambda params: {"result": expr.evaluate(params["expression"])},
|
||||||
},
|
},
|
||||||
|
|
@ -125,20 +193,399 @@ tools = {
|
||||||
"id": "greet",
|
"id": "greet",
|
||||||
"description": "Says hello",
|
"description": "Says hello",
|
||||||
"arguments": [
|
"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']}!"},
|
"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!"},
|
# Load resources from file at startup
|
||||||
"foo/bar": {"id": "foo/bar", "content": "Nested Foo Bar"},
|
resources = load_resources_from_file()
|
||||||
"foo/baz": {"id": "foo/baz", "content": "Nested Foo Baz"},
|
|
||||||
}
|
|
||||||
|
# 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}). Dealer’s 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"Dealer’s 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 dealer’s 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 = "Dealer’s total beats yours. You lose."
|
||||||
|
payout = 0
|
||||||
|
result = "loss"
|
||||||
|
else:
|
||||||
|
outcome = "Push! It’s 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"Dealer’s 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
|
# Memory endpoints with lock and timeout
|
||||||
LOCK_TIMEOUT = 2 # Seconds to wait for lock acquisition
|
LOCK_TIMEOUT = 2 # Seconds to wait for lock acquisition
|
||||||
|
|
||||||
|
|
||||||
@app.route("/memory", methods=["POST"])
|
@app.route("/memory", methods=["POST"])
|
||||||
def store_memory():
|
def store_memory():
|
||||||
logger.info("Received /memory POST request")
|
logger.info("Received /memory POST request")
|
||||||
|
|
@ -159,9 +606,12 @@ def store_memory():
|
||||||
finally:
|
finally:
|
||||||
memory_lock.release()
|
memory_lock.release()
|
||||||
else:
|
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
|
return jsonify({"error": "Memory lock timeout"}), 503
|
||||||
|
|
||||||
|
|
||||||
@app.route("/memory/<key>", methods=["GET"])
|
@app.route("/memory/<key>", methods=["GET"])
|
||||||
def get_memory(key):
|
def get_memory(key):
|
||||||
logger.info(f"Received /memory/{key} GET request")
|
logger.info(f"Received /memory/{key} GET request")
|
||||||
|
|
@ -176,9 +626,12 @@ def get_memory(key):
|
||||||
finally:
|
finally:
|
||||||
memory_lock.release()
|
memory_lock.release()
|
||||||
else:
|
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
|
return jsonify({"error": "Memory lock timeout"}), 503
|
||||||
|
|
||||||
|
|
||||||
@app.route("/memory", methods=["GET"])
|
@app.route("/memory", methods=["GET"])
|
||||||
def list_memory():
|
def list_memory():
|
||||||
logger.info("Received /memory GET request")
|
logger.info("Received /memory GET request")
|
||||||
|
|
@ -193,9 +646,12 @@ def list_memory():
|
||||||
finally:
|
finally:
|
||||||
memory_lock.release()
|
memory_lock.release()
|
||||||
else:
|
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
|
return jsonify({"error": "Memory lock timeout"}), 503
|
||||||
|
|
||||||
|
|
||||||
@app.route("/memory/<key>", methods=["DELETE"])
|
@app.route("/memory/<key>", methods=["DELETE"])
|
||||||
def delete_memory(key):
|
def delete_memory(key):
|
||||||
logger.info(f"Received /memory/{key} DELETE request")
|
logger.info(f"Received /memory/{key} DELETE request")
|
||||||
|
|
@ -214,10 +670,13 @@ def delete_memory(key):
|
||||||
finally:
|
finally:
|
||||||
memory_lock.release()
|
memory_lock.release()
|
||||||
else:
|
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
|
return jsonify({"error": "Memory lock timeout"}), 503
|
||||||
|
|
||||||
# Other endpoints (unchanged for brevity)
|
|
||||||
|
# Other endpoints
|
||||||
@app.route("/chat", methods=["POST"])
|
@app.route("/chat", methods=["POST"])
|
||||||
def chat():
|
def chat():
|
||||||
logger.info("Received /chat request")
|
logger.info("Received /chat request")
|
||||||
|
|
@ -244,19 +703,14 @@ def chat():
|
||||||
response_content = response.choices[0].message.content
|
response_content = response.choices[0].message.content
|
||||||
logger.debug(f"Chat response from {model_id}: {response_content}")
|
logger.debug(f"Chat response from {model_id}: {response_content}")
|
||||||
return (
|
return (
|
||||||
jsonify(
|
jsonify({"choices": [{"message": {"content": response_content}}]}),
|
||||||
{
|
|
||||||
"choices": [
|
|
||||||
{"message": {"content": response_content}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
),
|
|
||||||
200,
|
200,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Chat error with model {model_id}: {str(e)}", exc_info=True)
|
logger.error(f"Chat error with model {model_id}: {str(e)}", exc_info=True)
|
||||||
return jsonify({"error": str(e)}), 500
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
@app.route("/models", methods=["GET"])
|
@app.route("/models", methods=["GET"])
|
||||||
def list_models():
|
def list_models():
|
||||||
logger.info("Received /models request")
|
logger.info("Received /models request")
|
||||||
|
|
@ -264,13 +718,18 @@ def list_models():
|
||||||
logger.debug(f"Returning models: {models}")
|
logger.debug(f"Returning models: {models}")
|
||||||
return jsonify({"models": models}), 200
|
return jsonify({"models": models}), 200
|
||||||
|
|
||||||
|
|
||||||
@app.route("/tools", methods=["GET"])
|
@app.route("/tools", methods=["GET"])
|
||||||
def list_tools():
|
def list_tools():
|
||||||
logger.info("Received /tools request")
|
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}")
|
logger.debug(f"Returning tools: {tool_list}")
|
||||||
return jsonify({"tools": tool_list}), 200
|
return jsonify({"tools": tool_list}), 200
|
||||||
|
|
||||||
|
|
||||||
@app.route("/tools/<tool_id>", methods=["POST"])
|
@app.route("/tools/<tool_id>", methods=["POST"])
|
||||||
def use_tool(tool_id):
|
def use_tool(tool_id):
|
||||||
logger.info(f"Received /tools/{tool_id} request")
|
logger.info(f"Received /tools/{tool_id} request")
|
||||||
|
|
@ -282,7 +741,7 @@ def use_tool(tool_id):
|
||||||
|
|
||||||
if "arguments" in tools[tool_id]:
|
if "arguments" in tools[tool_id]:
|
||||||
for arg in tools[tool_id]["arguments"]:
|
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")
|
logger.warning(f"Missing '{arg['name']}' for {tool_id} tool")
|
||||||
return jsonify({"error": f"Missing '{arg['name']}' parameter"}), 400
|
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)
|
logger.error(f"Error executing tool {tool_id}: {str(e)}", exc_info=True)
|
||||||
return jsonify({"error": str(e)}), 500
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
@app.route("/resources", methods=["GET"])
|
@app.route("/resources", methods=["GET"])
|
||||||
def list_resources():
|
def list_resources():
|
||||||
logger.info("Received /resources request")
|
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}")
|
logger.debug(f"Returning resources: {resource_list}")
|
||||||
return jsonify({"resources": resource_list}), 200
|
return jsonify({"resources": resource_list}), 200
|
||||||
|
|
||||||
|
|
||||||
@app.route("/resources/<resource_id>", methods=["GET"])
|
@app.route("/resources/<resource_id>", methods=["GET"])
|
||||||
def get_resource(resource_id):
|
def get_resource(resource_id):
|
||||||
logger.info(f"Received /resources/{resource_id} GET request")
|
logger.info(f"Received /resources/{resource_id} GET request")
|
||||||
if resource_id in resources:
|
with resource_lock:
|
||||||
resource = resources[resource_id]
|
if resource_id in resources:
|
||||||
logger.debug(f"Exact match found for {resource_id}: {resource}")
|
resource = resources[resource_id]
|
||||||
return jsonify(resource), 200
|
logger.debug(f"Exact match found for {resource_id}: {resource}")
|
||||||
else:
|
return jsonify(resource), 200
|
||||||
# Prefix search for nested resources
|
else:
|
||||||
prefix = f"{resource_id}/"
|
# Prefix search for nested resources
|
||||||
matching_resources = [
|
prefix = f"{resource_id}/"
|
||||||
res for key, res in resources.items() if key.startswith(prefix)
|
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}")
|
if matching_resources:
|
||||||
return jsonify({"resources": matching_resources}), 200
|
logger.debug(
|
||||||
logger.warning(f"No resource or nested resources found for {resource_id}")
|
f"Prefix search for {resource_id} found: {matching_resources}"
|
||||||
return jsonify({"error": "Resource not found"}), 404
|
)
|
||||||
|
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"])
|
@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")
|
logger.info(f"Received /resources/{resource_id} PUT request")
|
||||||
data = request.json
|
data = request.json
|
||||||
logger.debug(f"Update resource data: {data}")
|
logger.debug(f"Update resource data: {data}")
|
||||||
if not data or "content" not in data:
|
if not data or "content" not in data:
|
||||||
logger.warning(f"Invalid resource update request: {data}")
|
logger.warning(f"Invalid resource update request: {data}")
|
||||||
return jsonify({"error": "Missing 'content'"}), 400
|
return jsonify({"error": "Missing 'content'"}), 400
|
||||||
# Update or create the resource
|
with resource_lock:
|
||||||
resources[resource_id] = {
|
resources[resource_id] = {"id": resource_id, "content": data["content"]}
|
||||||
"id": resource_id,
|
save_resources_to_file(resources)
|
||||||
"content": data["content"]
|
|
||||||
}
|
|
||||||
logger.info(f"Updated/created resource {resource_id}: {resources[resource_id]}")
|
logger.info(f"Updated/created resource {resource_id}: {resources[resource_id]}")
|
||||||
return jsonify({"status": "updated", "resource": resources[resource_id]}), 200
|
return jsonify({"status": "updated", "resource": resources[resource_id]}), 200
|
||||||
|
|
||||||
|
|
||||||
@app.route("/pay", methods=["POST"])
|
@app.route("/pay", methods=["POST"])
|
||||||
def pay():
|
def pay():
|
||||||
logger.info("Received /pay request")
|
logger.info("Received /pay request")
|
||||||
|
|
@ -354,6 +819,7 @@ def pay():
|
||||||
200,
|
200,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Initialize model map on startup
|
# Initialize model map on startup
|
||||||
logger.info("Starting application initialization")
|
logger.info("Starting application initialization")
|
||||||
initialize_model_map()
|
initialize_model_map()
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
openapi: 3.0.0
|
openapi: 3.0.0
|
||||||
info:
|
info:
|
||||||
title: SLOP API
|
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
|
version: 1.0.0
|
||||||
|
|
||||||
servers:
|
servers:
|
||||||
- url: https://slop.unturf.com
|
- url: https://slop.unturf.com
|
||||||
description: Production unturf. SLOP server
|
description: Production unturf SLOP server
|
||||||
- url: http://localhost:31337
|
- url: http://localhost:31337
|
||||||
description: Local development SLOP server
|
description: Local development SLOP server
|
||||||
|
|
||||||
|
|
@ -76,20 +76,101 @@ paths:
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
enum: [calculator, greet]
|
enum: [calculator, greet, slots, blackjack]
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
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:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Tool execution result
|
description: Tool execution result
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
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). Dealer’s 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). Dealer’s 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':
|
'400':
|
||||||
description: Missing required parameter
|
description: Missing required parameter
|
||||||
content:
|
content:
|
||||||
|
|
@ -102,6 +183,12 @@ paths:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/ErrorResponse'
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
'500':
|
||||||
|
description: Server error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
/memory:
|
/memory:
|
||||||
get:
|
get:
|
||||||
summary: List all memory keys
|
summary: List all memory keys
|
||||||
|
|
@ -252,6 +339,18 @@ paths:
|
||||||
content: "Nested Foo Bar"
|
content: "Nested Foo Bar"
|
||||||
- id: "foo/baz"
|
- id: "foo/baz"
|
||||||
content: "Nested 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':
|
'404':
|
||||||
description: No resource or nested resources found
|
description: No resource or nested resources found
|
||||||
content:
|
content:
|
||||||
|
|
@ -307,6 +406,7 @@ paths:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/PayResponse'
|
$ref: '#/components/schemas/PayResponse'
|
||||||
|
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
ChatRequest:
|
ChatRequest:
|
||||||
|
|
@ -370,29 +470,192 @@ components:
|
||||||
type: string
|
type: string
|
||||||
description:
|
description:
|
||||||
type: string
|
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:
|
required:
|
||||||
- id
|
- id
|
||||||
- description
|
- description
|
||||||
|
- arguments
|
||||||
required:
|
required:
|
||||||
- tools
|
- tools
|
||||||
ToolRequest:
|
CalculatorToolRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
expression:
|
expression:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
description: The math expression to evaluate (e.g., "2 + 2")
|
||||||
|
required:
|
||||||
|
- expression
|
||||||
|
GreetToolRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
name:
|
name:
|
||||||
type: string
|
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
|
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
|
type: object
|
||||||
properties:
|
properties:
|
||||||
result:
|
result:
|
||||||
oneOf:
|
type: integer
|
||||||
- type: string
|
description: The result of the calculation
|
||||||
- type: integer
|
|
||||||
required:
|
required:
|
||||||
- result
|
- 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:
|
MemoryStoreRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -439,7 +702,8 @@ components:
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
content:
|
content:
|
||||||
type: string
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- content
|
- content
|
||||||
|
|
@ -451,7 +715,8 @@ components:
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
content:
|
content:
|
||||||
type: string
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- content
|
- content
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
# streamlit_slop_with_models.py
|
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
@ -130,6 +129,145 @@ def tools_interface():
|
||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
st.error(f"Error: {str(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"**Dealer’s 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"**Dealer’s 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"**Dealer’s 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():
|
def memory_interface():
|
||||||
st.header("Memory")
|
st.header("Memory")
|
||||||
|
|
@ -234,12 +372,16 @@ def resources_interface():
|
||||||
response = requests.put(
|
response = requests.put(
|
||||||
f"{BASE_URL}/resources/{new_resource_id}",
|
f"{BASE_URL}/resources/{new_resource_id}",
|
||||||
json={"content": content},
|
json={"content": content},
|
||||||
timeout=5
|
timeout=5,
|
||||||
)
|
)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
result = response.json()
|
result = response.json()
|
||||||
st.success(f"Resource {result['resource']['id']} updated/created successfully!")
|
st.success(
|
||||||
st.write(f"**{result['resource']['id']}**: {result['resource']['content']}")
|
f"Resource {result['resource']['id']} updated/created successfully!"
|
||||||
|
)
|
||||||
|
st.write(
|
||||||
|
f"**{result['resource']['id']}**: {result['resource']['content']}"
|
||||||
|
)
|
||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
st.error(f"Error: {str(e)}")
|
st.error(f"Error: {str(e)}")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue