|
# 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)
|
|
]
|