Bot-Man-Toe/pyserver/server.py

81 lines
2.5 KiB
Python

"""Stateless Flask server helpers for Bot-Man-Toe games."""
from __future__ import annotations
import os
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.containerized : bool = bool(int(os.environ.get("CONTAINERIZED", "0")))
self.agent = agent
self.app = Flask(import_name)
@self.app.get("/")
def discovery():
return jsonify(self.__discovery())
@self.app.get("/<path:game>")
@self.app.get("/<path:game>/<path:action>")
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["me.noordstar.peanuts.containerized"] = self.containerized
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)