"""Stateless Flask server helpers for Bot-Man-Toe games.""" from __future__ import annotations from flask import Flask, jsonify, request from agents import Agent from typing import Any PayloadType = dict[str, Any] class PyServer: """ A tiny stateless Flask app that serves discovery and routes game behavior to an agent. """ def __init__(self, agent : Agent, import_name : str = __name__) -> None: """ Create a PyServer that serves the behavior of an Agent. :param agent: The agent that one can communicate with. :type agent: Agent :param import_name: Flask application import name :type import_name: str """ self.agent = agent self.app = Flask(import_name) @self.app.get("/") def discovery(): return jsonify(self.__discovery()) @self.app.get("/") @self.app.get("//") def dispatch(game: str, action: str | None = None): return jsonify(self.__dispatch(game=game, action=action)) def __discovery(self) -> PayloadType: """ Return the server's discovery information. :return: The personal discovery information. :rtype: dict[str, Any] """ d = dict(name=self.agent.name, author=self.agent.author) if self.agent.version is not None: d["version"] = self.agent.version d = { **self.agent.profile, **d } d["games"] = {} for game, (profile, _) in self.agent.registered_games.items(): d["games"][game] = profile return d def __dispatch(self, game: str, action: str | None) -> PayloadType: """ Resolve a request against the agent's action lookup. """ payload = request.get_json(silent=True) or {} func = self.agent.get_action( game=game.strip("/"), action=action.strip("/") if isinstance(action, str) else None, ) if func is None: return {} result = func(payload) or {} return result if isinstance(result, dict) else {} def start(self, host: str = "127.0.0.1", port: int = 5000, debug: bool = False, **kwargs: Any) -> None: """Start the Flask development server.""" self.app.run(host=host, port=port, debug=debug, **kwargs)