Source code for app.schema.axis
"""
Axis primitives shared across the Axis Descriptor Lab API.
This module contains the authoritative deterministic payload structures used
by both the character-description and chat-translation flows. These models
are intentionally small and validation-focused because they are reused by
multiple higher-level request/response models across the API.
"""
from __future__ import annotations
from pydantic import BaseModel, Field, field_validator
[docs]
class AxisValue(BaseModel):
"""
A single named axis entry consisting of a human-readable label and a
normalised score in [0, 1].
The *label* is the interpretive colour (e.g. "resentful", "weary").
The *score* is the underlying deterministic value produced by the engine.
Both are forwarded verbatim to the LLM; the LLM must not treat them as
facts – they are tonal hints only.
"""
label: str = Field(
...,
description="Short human-readable descriptor for this axis position.",
examples=["resentful", "weary", "questioned"],
)
score: float = Field(
...,
ge=0.0,
le=1.0,
description="Normalised axis score in the closed interval [0, 1].",
examples=[0.5, 0.62],
)
[docs]
@field_validator("label")
@classmethod
def label_not_empty(cls, v: str) -> str:
"""Ensure the label is not a blank string."""
v = v.strip()
if not v:
raise ValueError("label must not be empty or whitespace")
return v
[docs]
class AxisPayload(BaseModel):
"""
The complete deterministic state object that drives a single descriptive
generation. This is the "source of truth" handed to the LLM as a JSON
string in the user turn.
`axes` is keyed by axis name (for example, `"demeanor"`) and stores
`AxisValue` entries. `policy_hash` captures the SHA-256 digest of the
policy rules in force when the payload was produced. `seed` records the
deterministic RNG seed used to produce the scores, and `world_id`
identifies the Pipe-Works world context.
"""
axes: dict[str, AxisValue] = Field(
...,
description="Map of axis name → AxisValue. At least one entry required.",
)
policy_hash: str = Field(
...,
description=(
"SHA-256 hex digest of the axis policy in force. "
"Forwarded to the LLM but the system prompt instructs the model "
"never to mention or interpret it."
),
)
seed: int = Field(
...,
description="RNG seed that produced the axis scores. Used for reproducibility.",
)
world_id: str = Field(
...,
description="Identifier for the Pipe-Works world (e.g. 'pipeworks_web').",
)
[docs]
@field_validator("axes")
@classmethod
def axes_not_empty(cls, v: dict[str, AxisValue]) -> dict[str, AxisValue]:
"""Require at least one axis."""
if not v:
raise ValueError("axes must contain at least one entry")
return v