harrrshall's picture
Release BarunAction-35M candidate-v2
5a46e5d verified
Raw
History Blame Contribute Delete
13.9 kB
"""Strict caller-supplied tool and request contracts for BarunAction-35M."""
from __future__ import annotations
import json
import unicodedata
from collections.abc import Mapping, Sequence
from collections.abc import Set as AbstractSet
from dataclasses import dataclass
from datetime import datetime
from types import MappingProxyType
from typing import Any
from barunlm.evaluation.action_ir import (
ActionIRError,
JSONType,
ToolSchema,
ValueSchema,
canonical_json_value,
decode_json_object,
)
RESERVED_TOKENS = ("<bos>", "<system>", "<user>", "<assistant>", "<eos>")
SCHEMA_CONTRACT_VERSION = "barunaction-tool-schema-v1"
class ContractError(ValueError):
"""A stable, machine-readable caller-contract error."""
def __init__(self, code: str, message: str, path: str = "$") -> None:
super().__init__(f"{path}: {message}")
self.code = code
self.message = message
self.path = path
def to_dict(self) -> dict[str, str]:
return {"code": self.code, "message": self.message, "path": self.path, "stage": "input"}
@dataclass(frozen=True, slots=True)
class ToolDeclaration:
"""Prompt metadata paired with the evaluator's canonical typed schema."""
description: str
schema: ToolSchema
argument_order: tuple[str, ...]
type_labels: Mapping[str, str]
def render(self) -> str:
arguments = ", ".join(
f"{name}:{self.type_labels[name]}{'!' if name in self.schema.required else ''}"
for name in self.argument_order
)
return f"{self.schema.name}({arguments}): {self.description}"
@dataclass(frozen=True, slots=True)
class PreparedInput:
request: str
now: str
context_json: str
declarations: tuple[ToolDeclaration, ...]
registry: Mapping[str, ToolSchema]
def _exact_fields(
value: Mapping[str, Any],
*,
required: AbstractSet[str],
optional: AbstractSet[str] = frozenset(),
path: str,
) -> None:
missing = required.difference(value)
if missing:
raise ContractError("missing_field", f"missing fields: {sorted(missing)!r}", path)
unknown = set(value).difference(required | optional)
if unknown:
raise ContractError("unknown_field", f"unknown fields: {sorted(unknown)!r}", path)
def _text(value: Any, *, path: str, single_line: bool = False) -> str:
if not isinstance(value, str) or not value:
raise ContractError("type_mismatch", "must be a non-empty string", path)
normalized = unicodedata.normalize("NFC", value)
if single_line and ("\n" in normalized or "\r" in normalized):
raise ContractError("multiline_text", "must be a single-line string", path)
lowered = normalized.casefold()
for token in RESERVED_TOKENS:
if token in lowered:
raise ContractError("reserved_token", f"contains reserved token {token!r}", path)
return normalized
def _bool(value: Any, *, path: str) -> bool:
if type(value) is not bool:
raise ContractError("type_mismatch", "must be a boolean", path)
return value
def _unique_names(value: Any, *, path: str) -> tuple[str, ...]:
if not isinstance(value, list):
raise ContractError("type_mismatch", "must be an array", path)
names = tuple(
_text(item, path=f"{path}[{index}]", single_line=True) for index, item in enumerate(value)
)
if len(names) != len(set(names)):
raise ContractError("duplicate_name", "must not contain duplicate names", path)
return names
def _value_schema(value: Any, *, path: str) -> tuple[ValueSchema, str]:
if not isinstance(value, Mapping):
raise ContractError("type_mismatch", "value schema must be an object", path)
_exact_fields(
value,
required={"type", "description"},
optional={
"enum",
"items",
"properties",
"required",
"additional_properties",
"set_semantics",
},
path=path,
)
type_name = _text(value["type"], path=f"{path}.type", single_line=True)
try:
kind = JSONType(type_name)
except ValueError as error:
raise ContractError(
"invalid_type", f"unknown JSON type {type_name!r}", f"{path}.type"
) from error
_text(value["description"], path=f"{path}.description", single_line=True)
enum: tuple[Any, ...] | None = None
if "enum" in value:
raw_enum = value["enum"]
if not isinstance(raw_enum, list) or not raw_enum:
raise ContractError("invalid_enum", "enum must be a non-empty array", f"{path}.enum")
enum = tuple(raw_enum)
common = {"type", "description", "enum"}
if kind is JSONType.ARRAY:
if enum is not None:
raise ContractError(
"invalid_enum", "enum values are supported only for scalar schemas", f"{path}.enum"
)
allowed = common | {"items", "set_semantics"}
if set(value) != allowed and set(value) != allowed - {"enum"}:
raise ContractError(
"schema_shape",
"array schema requires items and set_semantics and forbids object fields",
path,
)
item_schema, item_label = _value_schema(value["items"], path=f"{path}.items")
try:
schema = ValueSchema(
kind=kind,
items=item_schema,
set_semantics=_bool(value["set_semantics"], path=f"{path}.set_semantics"),
)
except (TypeError, ValueError) as error:
raise ContractError("invalid_schema", str(error), path) from error
return schema, f"array<{item_label}>"
if kind is JSONType.OBJECT:
if enum is not None:
raise ContractError(
"invalid_enum", "enum values are supported only for scalar schemas", f"{path}.enum"
)
expected = common | {"properties", "required", "additional_properties"}
if set(value) != expected and set(value) != expected - {"enum"}:
raise ContractError(
"schema_shape",
"object schema requires properties, required, and additional_properties",
path,
)
raw_properties = value["properties"]
if not isinstance(raw_properties, Mapping):
raise ContractError(
"type_mismatch", "properties must be an object", f"{path}.properties"
)
properties: dict[str, ValueSchema] = {}
labels: list[str] = []
for raw_name, raw_schema in raw_properties.items():
name = _text(raw_name, path=f"{path}.properties", single_line=True)
if name in properties:
raise ContractError(
"duplicate_name", f"duplicate property {name!r}", f"{path}.properties"
)
child, label = _value_schema(raw_schema, path=f"{path}.properties.{name}")
properties[name] = child
labels.append(f"{name}:{label}")
required = _unique_names(value["required"], path=f"{path}.required")
try:
schema = ValueSchema(
kind=kind,
properties=properties,
required=frozenset(required),
additional_properties=_bool(
value["additional_properties"], path=f"{path}.additional_properties"
),
)
except (TypeError, ValueError) as error:
raise ContractError("invalid_schema", str(error), path) from error
return schema, "object{" + ",".join(labels) + "}"
if set(value).difference(common):
raise ContractError("schema_shape", "scalar schema contains collection-only fields", path)
try:
return ValueSchema(kind=kind, enum=enum), kind.value
except (TypeError, ValueError) as error:
raise ContractError("invalid_schema", str(error), path) from error
def parse_tool_declarations(value: Any) -> tuple[ToolDeclaration, ...]:
"""Parse the versioned caller schema into prompt and Action IR representations."""
if not isinstance(value, Sequence) or isinstance(value, (str, bytes)) or not value:
raise ContractError("type_mismatch", "tool_schemas must be a non-empty array", "$.tools")
declarations: list[ToolDeclaration] = []
names: set[str] = set()
for index, raw_tool in enumerate(value):
path = f"$.tools[{index}]"
if not isinstance(raw_tool, Mapping):
raise ContractError("type_mismatch", "tool declaration must be an object", path)
_exact_fields(
raw_tool,
required={
"name",
"description",
"arguments",
"required",
"additional_arguments",
"side_effecting",
},
path=path,
)
name = _text(raw_tool["name"], path=f"{path}.name", single_line=True)
if name in names:
raise ContractError("duplicate_tool", f"duplicate tool {name!r}", f"{path}.name")
names.add(name)
description = _text(raw_tool["description"], path=f"{path}.description", single_line=True)
raw_arguments = raw_tool["arguments"]
if not isinstance(raw_arguments, Mapping):
raise ContractError("type_mismatch", "arguments must be an object", f"{path}.arguments")
arguments: dict[str, ValueSchema] = {}
labels: dict[str, str] = {}
order: list[str] = []
for raw_name, raw_schema in raw_arguments.items():
argument_name = _text(raw_name, path=f"{path}.arguments", single_line=True)
if argument_name in arguments:
raise ContractError(
"duplicate_name", f"duplicate argument {argument_name!r}", f"{path}.arguments"
)
schema, label = _value_schema(raw_schema, path=f"{path}.arguments.{argument_name}")
arguments[argument_name] = schema
labels[argument_name] = label
order.append(argument_name)
required = _unique_names(raw_tool["required"], path=f"{path}.required")
try:
schema = ToolSchema(
name=name,
arguments=arguments,
required=frozenset(required),
additional_arguments=_bool(
raw_tool["additional_arguments"], path=f"{path}.additional_arguments"
),
side_effecting=_bool(raw_tool["side_effecting"], path=f"{path}.side_effecting"),
)
except (TypeError, ValueError) as error:
raise ContractError("invalid_schema", str(error), path) from error
declarations.append(
ToolDeclaration(
description=description,
schema=schema,
argument_order=tuple(order),
type_labels=MappingProxyType(labels),
)
)
return tuple(declarations)
def _validate_json_strings(value: Any, *, path: str = "$.context") -> None:
if isinstance(value, str):
_text(value, path=path)
return
if value is None or isinstance(value, (bool, int, float)):
return
if isinstance(value, Mapping):
for raw_key, child in value.items():
key = _text(raw_key, path=path, single_line=True)
_validate_json_strings(child, path=f"{path}.{key}")
return
if isinstance(value, (list, tuple)):
for index, child in enumerate(value):
_validate_json_strings(child, path=f"{path}[{index}]")
return
raise ContractError("type_mismatch", f"unsupported JSON value {type(value).__name__}", path)
def canonical_context(value: Any) -> str:
if not isinstance(value, Mapping):
raise ContractError("type_mismatch", "context must be a JSON object", "$.context")
_validate_json_strings(value)
try:
encoded = json.dumps(
value,
ensure_ascii=False,
allow_nan=False,
sort_keys=True,
separators=(",", ":"),
)
canonical = decode_json_object(encoded)
except (TypeError, ValueError, ActionIRError) as error:
code = error.code if isinstance(error, ActionIRError) else "invalid_context"
raise ContractError(code, "context must be strict finite JSON", "$.context") from error
return canonical_json_value(canonical)
def canonical_now(value: Any) -> str:
now = _text(value, path="$.now", single_line=True)
try:
parsed = datetime.fromisoformat(now.replace("Z", "+00:00"))
except ValueError as error:
raise ContractError("invalid_now", "NOW must be an ISO-8601 timestamp", "$.now") from error
if parsed.tzinfo is None or parsed.utcoffset() is None:
raise ContractError("timezone_required", "NOW must include an explicit UTC offset", "$.now")
return parsed.isoformat()
def prepare_input(
*,
request: Any,
tool_schemas: Any,
context: Any,
now: Any,
) -> PreparedInput:
normalized_request = _text(request, path="$.request")
if not normalized_request.strip():
raise ContractError(
"empty_request", "request must contain non-whitespace text", "$.request"
)
declarations = parse_tool_declarations(tool_schemas)
registry = MappingProxyType({item.schema.name: item.schema for item in declarations})
return PreparedInput(
request=normalized_request,
now=canonical_now(now),
context_json=canonical_context(context),
declarations=declarations,
registry=registry,
)
__all__ = [
"SCHEMA_CONTRACT_VERSION",
"ContractError",
"PreparedInput",
"ToolDeclaration",
"canonical_context",
"canonical_now",
"parse_tool_declarations",
"prepare_input",
]