# retoor <retoor@molodetz.nl>
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Literal
ParamLocation = Literal["path", "query", "body", "file"]
@dataclass(frozen=True)
class Param:
name: str
location: ParamLocation
description: str
required: bool = False
type: str = "string"
@dataclass(frozen=True)
class Action:
name: str
method: str
path: str
summary: str
description: str = ""
params: tuple[Param, ...] = ()
requires_auth: bool = True
requires_admin: bool = False
handler: Literal[
"http",
"login",
"logout",
"status",
"task",
"agentic",
"avatar",
"client",
"fetch",
"docs",
"cost",
"chunks",
"rsearch",
"container",
] = "http"
freeform_body: bool = False
ajax: bool = False
read_only: bool = False
@property
def is_read_only(self) -> bool:
return self.read_only or self.method.upper() == "GET"
def tool_schema(self) -> dict[str, Any]:
properties: dict[str, Any] = {}
required: list[str] = []
for param in self.params:
if param.type == "array":
properties[param.name] = {
"type": "array",
"items": {"type": "object"},
"description": param.description,
}
elif param.type == "object":
properties[param.name] = {
"type": "object",
"description": param.description,
"additionalProperties": True,
}
else:
properties[param.name] = {
"type": param.type,
"description": param.description,
}
if param.required:
required.append(param.name)
if self.freeform_body:
properties["form_fields"] = {
"type": "object",
"description": "Additional form fields as key/value string pairs.",
"additionalProperties": {"type": "string"},
}
text = (
self.summary
if not self.description
else f"{self.summary}. {self.description}"
)
return {
"type": "function",
"function": {
"name": self.name,
"description": text,
"parameters": {
"type": "object",
"properties": properties,
"required": required,
"additionalProperties": False,
},
},
}
@dataclass(frozen=True)
class Catalog:
actions: tuple[Action, ...] = field(default_factory=tuple)
def by_name(self) -> dict[str, Action]:
return {action.name: action for action in self.actions}
def tool_schemas(self) -> list[dict[str, Any]]:
return [action.tool_schema() for action in self.actions]
def tool_schemas_for(
self, authenticated: bool, is_admin: bool = False
) -> list[dict[str, Any]]:
return [
action.tool_schema()
for action in self.actions
if (authenticated or not action.requires_auth)
and (is_admin or not action.requires_admin)
]