from flask import Flask, request, jsonify, render_template_string from datetime import datetime 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 # Configure logging DATA_DIR = "data" 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()], ) logger = logging.getLogger(__name__) app = Flask(__name__) # Swagger UI setup SWAGGER_URL = "/openapi" API_URL = "/static/openapi.yaml" swaggerui_blueprint = get_swaggerui_blueprint( SWAGGER_URL, API_URL, config={"app_name": "SLOP API"} ) app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL) # Global model-to-client map MODEL_CLIENT_MAP = {} # Memory setup 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: if os.path.exists(MEMORY_FILE): with open(MEMORY_FILE, "r") as f: data = json.load(f) logger.debug(f"Loaded memory from {MEMORY_FILE}: {data}") return data 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 ) return {} def save_memory_to_file(memory_data): try: with open(MEMORY_FILE, "w") as f: json.dump(memory_data, f) f.flush() # Ensure write is completed os.fsync(f.fileno()) # Sync to disk logger.debug(f"Saved memory to {MEMORY_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) f.flush() # Ensure write is completed os.fsync(f.fileno()) # Sync to disk 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): endpoint = os.getenv(f"MODEL_ENDPOINT_{i}") if endpoint: api_key = os.getenv(f"MODEL_API_KEY_{i}", "not-needed") logger.info(f"Loaded endpoint {i}: {endpoint} with API key: {api_key[:4]}...") ENDPOINTS.append( { "name": f"endpoint_{i}", "base_url": endpoint, "api_key": api_key, } ) 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() if not ENDPOINTS: logger.warning("No endpoints configured to initialize models") return for ep in ENDPOINTS: base_url = ep["base_url"] api_key = ep["api_key"] endpoint_name = ep["name"] logger.info(f"Initializing client for {endpoint_name} at {base_url}") client = OpenAI(base_url=base_url, api_key=api_key) try: 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]}" ) except Exception as e: 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}" ) else: MODEL_CLIENT_MAP[model_id] = client logger.info(f"Registered model '{model_id}' from {endpoint_name}") else: 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.", } ], "execute": lambda params: {"result": expr.evaluate(params["expression"])}, }, "greet": { "id": "greet", "description": "Says hello", "arguments": [ {"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"), ), }, } # Load resources from file at startup (initial load only) 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: resources = load_resources_from_file() # Always load fresh 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: resources = load_resources_from_file() # Always load fresh 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 = load_resources_from_file() # Always load fresh resources[resource_id] = {"id": 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_hands": [player_hand], # List to support splitting "player_totals": [player_total], "dealer_hand": dealer_hand, "dealer_total": sum(dealer_hand), "status": "active", "active_hand": 0, # Index of the current hand being played } 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, split if two cards match. Dealer stands on 17+. " 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_hands": [player_hand], "player_totals": [player_total], "dealer_hand": dealer_hand, "dealer_total": sum(dealer_hand), "can_split": len(player_hand) == 2 and player_hand[0] == player_hand[1], }, "bet": bet, "agent_id": agent_id, "new_balance": wallet["balance"], "game_complete": False, } # Continue an existing game game_key = f"casino/blackjack/games/{game_id}" with resource_lock: resources = load_resources_from_file() 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", "split"]: return {"error": "Action must be 'hit', 'stand', or 'split'"} wallet = get_or_create_wallet(agent_id) cards = [2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11] * 4 if ( action == "split" and len(game_state["player_hands"]) == 1 and len(game_state["player_hands"][0]) == 2 and game_state["player_hands"][0][0] == game_state["player_hands"][0][1] ): if wallet["balance"] < game_state["bet"] * 2: return { "error": f"Insufficient funds to split! Need {game_state['bet'] * 2}, have {wallet['balance']}" } hand = game_state["player_hands"][0] game_state["player_hands"] = [[hand[0]], [hand[1]]] game_state["player_hands"][0].append(random.choice(cards)) game_state["player_hands"][1].append(random.choice(cards)) game_state["player_totals"] = [ sum(game_state["player_hands"][0]), sum(game_state["player_hands"][1]), ] for i in range(2): aces = game_state["player_hands"][i].count(11) while game_state["player_totals"][i] > 21 and aces > 0: game_state["player_totals"][i] -= 10 aces -= 1 game_state["bet"] *= 2 # Double the bet for two hands explanation = ( f"Blackjack: You chose to split your hand {hand}. " f"Hand 1: {game_state['player_hands'][0]} (Total: {game_state['player_totals'][0]}). " f"Hand 2: {game_state['player_hands'][1]} (Total: {game_state['player_totals'][1]}). " f"Dealer’s up card: {game_state['dealer_hand']}. Game continues." ) update_resource(game_key, game_state) return { "explanation": explanation, "game_id": game_id, "game_state": { "player_hands": game_state["player_hands"], "player_totals": game_state["player_totals"], "dealer_hand": game_state["dealer_hand"], "dealer_total": game_state["dealer_total"], "can_split": False, # No further splitting after initial split }, "bet": game_state["bet"], "agent_id": agent_id, "new_balance": wallet["balance"], "game_complete": False, } if action == "hit": active_hand = game_state["active_hand"] game_state["player_hands"][active_hand].append(random.choice(cards)) game_state["player_totals"][active_hand] = sum( game_state["player_hands"][active_hand] ) aces = game_state["player_hands"][active_hand].count(11) while game_state["player_totals"][active_hand] > 21 and aces > 0: game_state["player_totals"][active_hand] -= 10 aces -= 1 if game_state["player_totals"][active_hand] > 21: if active_hand + 1 < len(game_state["player_hands"]): game_state["active_hand"] += 1 # Move to next hand if split outcome = ( f"Bust on hand {active_hand + 1}! Moving to hand {active_hand + 2}." ) else: game_state["status"] = "bust" outcome = "Bust! You went over 21 and lose." payout = 0 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 = f"You hit on hand {active_hand + 1}. Game continues." payout = 0 explanation = ( f"Blackjack: You chose to hit on hand {active_hand + 1}. " f"Hand {active_hand + 1}: {game_state['player_hands'][active_hand]} (Total: {game_state['player_totals'][active_hand]}). " 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_hands": game_state["player_hands"], "player_totals": game_state["player_totals"], "dealer_hand": game_state["dealer_hand"], "dealer_total": game_state["dealer_total"], "can_split": False, }, "bet": game_state["bet"], "agent_id": agent_id, "new_balance": wallet["balance"], "game_complete": game_state["status"] != "active", } if action == "stand": active_hand = game_state["active_hand"] if active_hand + 1 < len(game_state["player_hands"]): game_state["active_hand"] += 1 # Move to next hand if split explanation = ( f"Blackjack: You chose to stand on hand {active_hand + 1}. " f"Hand {active_hand + 1}: {game_state['player_hands'][active_hand]} (Total: {game_state['player_totals'][active_hand]}). " f"Dealer’s up card: {game_state['dealer_hand']}. Moving to hand {active_hand + 2}." ) update_resource(game_key, game_state) return { "explanation": explanation, "game_id": game_id, "game_state": { "player_hands": game_state["player_hands"], "player_totals": game_state["player_totals"], "dealer_hand": game_state["dealer_hand"], "dealer_total": game_state["dealer_total"], "can_split": False, }, "bet": game_state["bet"], "agent_id": agent_id, "new_balance": wallet["balance"], "game_complete": False, } # 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 # Resolve all hands outcomes = [] payouts = [] bet_per_hand = game_state["bet"] // len(game_state["player_hands"]) for i, total in enumerate(game_state["player_totals"]): if game_state["dealer_total"] > 21: outcome = f"Hand {i + 1}: Dealer busts! You win!" payout = bet_per_hand * 2 result = "win" elif total > game_state["dealer_total"]: outcome = f"Hand {i + 1}: Your total beats the dealer's! You win!" payout = bet_per_hand * 2 result = "win" elif game_state["dealer_total"] > total: outcome = f"Hand {i + 1}: Dealer’s total beats yours. You lose." payout = 0 result = "loss" else: outcome = f"Hand {i + 1}: Push! It’s a tie, your bet is returned." payout = bet_per_hand result = "tie" outcomes.append(outcome) payouts.append(payout) game_state["status"] = "complete" total_payout = sum(payouts) wallet["balance"] = wallet["balance"] - game_state["bet"] + total_payout wallet["last_transaction"] = { "game": "blackjack", "bet": game_state["bet"], "payout": total_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"] += total_payout for result in [o.split(": ")[1].split("!")[0] for o in outcomes]: if "win" in result.lower(): stats["wins"] += 1 elif "lose" in result.lower(): 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 hands: {', '.join([f'Hand {i+1}: {h} (Total: {t})' for i, (h, t) in enumerate(zip(game_state['player_hands'], game_state['player_totals']))])}. " f"Dealer’s hand: {game_state['dealer_hand']} (Total: {game_state['dealer_total']}). " f"{' '.join(outcomes)}" ) return { "explanation": explanation, "game_id": game_id, "game_state": { "player_hands": game_state["player_hands"], "player_totals": game_state["player_totals"], "dealer_hand": game_state["dealer_hand"], "dealer_total": game_state["dealer_total"], "can_split": False, }, "payout": total_payout, "bet": game_state["bet"], "agent_id": agent_id, "new_balance": wallet["balance"], "game_complete": True, "outcomes": outcomes, } # Memory endpoints with lock and timeout LOCK_TIMEOUT = 2 # Seconds to wait for lock acquisition @app.route("/", methods=["GET"]) def index(): logger.info("Received / GET request") html_template = """ SLOP API Server

SLOP API Server

Welcome to the SLOP API Server v1.0

API Endpoints:

""" return render_template_string(html_template) @app.route("/memory", methods=["POST"]) def store_memory(): logger.info("Received /memory POST request") data = request.json logger.debug(f"Store memory data: {data}") if not data or "key" not in data or "value" not in data: logger.warning(f"Invalid memory store request: {data}") return jsonify({"error": "Missing 'key' or 'value'"}), 400 key, value = data["key"], data["value"] if memory_lock.acquire(timeout=LOCK_TIMEOUT): try: memory = load_memory_from_file() # Always load fresh state memory[key] = value save_memory_to_file(memory) logger.info(f"Stored in memory: {key} = {value}") logger.debug(f"Current memory state: {memory}") return jsonify({"status": "stored"}), 200 finally: memory_lock.release() else: logger.error( f"Failed to acquire lock for storing {key} within {LOCK_TIMEOUT} seconds" ) return jsonify({"error": "Memory lock timeout"}), 503 @app.route("/memory/", methods=["GET"]) def get_memory(key): logger.info(f"Received /memory/{key} GET request") if memory_lock.acquire(timeout=LOCK_TIMEOUT): try: memory = load_memory_from_file() # Always load fresh state value = memory.get(key) logger.debug(f"Retrieved memory for {key}: {value}") if value is None: logger.warning(f"Key not found in memory: {key}") return jsonify({"value": value}), 200 finally: memory_lock.release() else: 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") if memory_lock.acquire(timeout=LOCK_TIMEOUT): try: memory = load_memory_from_file() # Always load fresh state keys = list(memory.keys()) memory_state = memory.copy() logger.debug(f"Memory keys: {keys}") logger.debug(f"Full memory state: {memory_state}") return jsonify({"keys": keys}), 200 finally: memory_lock.release() else: logger.error( f"Failed to acquire lock for listing memory within {LOCK_TIMEOUT} seconds" ) return jsonify({"error": "Memory lock timeout"}), 503 @app.route("/memory/", methods=["DELETE"]) def delete_memory(key): logger.info(f"Received /memory/{key} DELETE request") if memory_lock.acquire(timeout=LOCK_TIMEOUT): try: memory = load_memory_from_file() # Always load fresh state if key not in memory: logger.warning(f"Key not found for deletion: {key}") return jsonify({"error": "Key not found"}), 404 old_value = memory[key] del memory[key] save_memory_to_file(memory) logger.info(f"Deleted from memory: {key} (was {old_value})") logger.debug(f"Updated memory state: {memory}") return jsonify({"status": "deleted"}), 200 finally: memory_lock.release() else: logger.error( f"Failed to acquire lock for deleting {key} within {LOCK_TIMEOUT} seconds" ) return jsonify({"error": "Memory lock timeout"}), 503 # Other endpoints @app.route("/chat", methods=["POST"]) def chat(): logger.info("Received /chat request") data = request.json logger.debug(f"Request data: {data}") message = data["messages"][0]["content"] if data.get("messages") else "nothing" model_id = data.get("model") or ( list(MODEL_CLIENT_MAP.keys())[0] if MODEL_CLIENT_MAP else None ) logger.info(f"Selected model: {model_id}, message: {message}") if not model_id or model_id not in MODEL_CLIENT_MAP: logger.error(f"Invalid or missing model_id: {model_id}") return jsonify({"error": "Model not found"}), 404 client = MODEL_CLIENT_MAP[model_id] try: response = client.chat.completions.create( model=model_id, messages=[ {"role": m["role"], "content": m["content"]} for m in data.get("messages", []) ] or [{"role": "user", "content": message}], ) response_content = response.choices[0].message.content logger.debug(f"Chat response from {model_id}: {response_content}") return ( 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") models = list(MODEL_CLIENT_MAP.keys()) 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() ] logger.debug(f"Returning tools: {tool_list}") return jsonify({"tools": tool_list}), 200 @app.route("/tools/", methods=["POST"]) def use_tool(tool_id): logger.info(f"Received /tools/{tool_id} request") if tool_id not in tools: logger.error(f"Tool not found: {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 and "optional" not in arg: logger.warning(f"Missing '{arg['name']}' for {tool_id} tool") return jsonify({"error": f"Missing '{arg['name']}' parameter"}), 400 try: result = tools[tool_id]["execute"](data) logger.debug(f"Tool {tool_id} result: {result}") return jsonify(result), 200 except Exception as e: 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") with resource_lock: resources = load_resources_from_file() resource_list = list(resources.values()) logger.debug(f"Returning resources: {resource_list}") return jsonify({"resources": resource_list}), 200 @app.route("/resources/", methods=["GET"]) def get_resource(resource_id): logger.info(f"Received /resources/{resource_id} GET request") with resource_lock: resources = load_resources_from_file() logger.debug(f"Resources after reload: {list(resources.keys())}") 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/", methods=["PUT"]) 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 with resource_lock: resources = load_resources_from_file() 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") data = request.json logger.debug(f"Pay request data: {data}") amount = data.get("amount", 0) transaction_id = f"tx_{int(datetime.now().timestamp())}" logger.info(f"Processed payment of {amount} with transaction_id: {transaction_id}") return ( jsonify( { "transaction_id": transaction_id, "status": "success", } ), 200, ) # Initialize model map on startup logger.info("Starting application initialization") initialize_model_map() 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")