diff options
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..d08b0bd --- /dev/null +++ b/README.md @@ -0,0 +1,127 @@ +# Hot functions API + +A small HTTP service that registers typed Python functions and calls them with +JSON. It derives input and output schemas from type annotations; there is no +separate schema registration step or UI. + +## Run locally + +Python 3.10 or newer is needed for `int | str` annotations in the examples. +The `curl` examples use Bash's `$'...'` quoting for multiline source. + +```sh +python -m venv .venv +. .venv/bin/activate +pip install -r requirements.txt +python server.py +``` + +The service listens on port 8000. Functions live in memory and disappear when +the server stops. + +## Register and call a function + +Send a complete, synchronous function definition. The function name must match +the URL, and every parameter and the return value must have a type annotation. + +```sh +curl -X PATCH http://localhost:8000/double \ + --data-binary $'def double(x: int) -> int:\n return x * 2' +``` + +With one parameter, POST either a bare JSON value or a named argument: + +```sh +curl -X POST http://localhost:8000/double \ + -H 'Content-Type: application/json' -d '4' +# 8 + +curl -X POST http://localhost:8000/double \ + -H 'Content-Type: application/json' -d '{"x":4}' +# 8 +``` + +The response is the function's JSON result, without a `result` wrapper. Input +and output are validated against the annotations in Pydantic's strict mode. + +### Multiple arguments and union types + +```sh +curl -X PATCH http://localhost:8000/label \ + --data-binary $'def label(x: int | str, prefix: str = "item") -> str:\n return f"{prefix}:{x}"' + +curl -X POST http://localhost:8000/label \ + -H 'Content-Type: application/json' -d '{"x":4,"prefix":"order"}' +# "order:4" + +curl -X POST http://localhost:8000/label \ + -H 'Content-Type: application/json' -d '{"x":"abc"}' +# "item:abc" +``` + +For multiple parameters, POST a JSON object with parameter names as keys. +Defaults may be omitted. Unrecognized keys are rejected. + +A single parameter may accept any JSON value its annotation permits: an object, +array, string, number, boolean, or `null`. For example, a function annotated +`x: dict[str, int]` can receive `{"count":2}` directly or as +`{"x":{"count":2}}`. If an object also looks like valid named arguments, the +named-argument form takes precedence. Structured types can use Pydantic models +defined in the uploaded source. + +## Discover functions and schemas + +```sh +curl http://localhost:8000/double +curl http://localhost:8000/ +``` + +`GET /double` returns an object containing `name`, `source`, and `schema`. +`GET /` returns `{"functions": [...]}` with the same objects for all registered +functions. For `double`, the input schema describes both accepted POST forms: + +```json +{ + "name": "double", + "source": "def double(x: int) -> int:\n return x * 2", + "schema": { + "input": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "anyOf": [ + { + "type": "object", + "properties": {"x": {"title": "X", "type": "integer"}}, + "required": ["x"], + "additionalProperties": false + }, + {"type": "integer"} + ] + }, + "output": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "integer" + } + } +} +``` + +The input and output schemas are JSON Schema Draft 2020-12 documents. Replacing +a function with `PATCH` also regenerates its schemas. + +## API responses + +| Request | Result | +| --- | --- | +| `PATCH /name` | `200` on registration; `400` for an invalid definition | +| `POST /name` | `200` with the JSON result; `400` for invalid input; `404` if undefined; `500` if execution or output validation fails | +| `GET /name` | `200` with the function description; `404` if undefined | +| `GET /` | `200` with all defined functions | + +Run the tests with `python -m unittest -v test_server`. + +## Trust boundary + +Uploaded definitions execute as Python in the server process, including at +registration time. The server has no authentication, isolation, request-size +limit, or execution timeout and binds to `0.0.0.0`. Only run it where every +caller is fully trusted; it is not safe to expose to untrusted clients. |