81 lines
2.5 KiB
Python
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)
|