""" ProcessSpec: structured specification for process physics. This model defines a JSON-serializable spec that an LLM can generate, which is then compiled deterministically into HIL logic. """ from __future__ import annotations from typing import Literal from pydantic import BaseModel, ConfigDict, Field class WaterTankParams(BaseModel): """Physical parameters for a water tank model.""" model_config = ConfigDict(extra="forbid") level_min: float = Field(ge=0.0, description="Minimum tank level (m)") level_max: float = Field(gt=0.0, description="Maximum tank level (m)") level_init: float = Field(ge=0.0, description="Initial tank level (m)") area: float = Field(gt=0.0, description="Tank cross-sectional area (m^2)") q_in_max: float = Field(ge=0.0, description="Max inflow rate when valve open (m^3/s)") k_out: float = Field(ge=0.0, description="Outflow coefficient (m^2.5/s), Q_out = k_out * sqrt(level)") class WaterTankSignals(BaseModel): """Mapping of logical names to HIL physical_values keys.""" model_config = ConfigDict(extra="forbid") tank_level_key: str = Field(description="physical_values key for tank level (io:output)") valve_open_key: str = Field(description="physical_values key for inlet valve state (io:input)") level_measured_key: str = Field(description="physical_values key for measured level output (io:output)") class ProcessSpec(BaseModel): """ Top-level process specification. Currently supports 'water_tank_v1' model only. Designed to be extensible with additional model types via Literal union. """ model_config = ConfigDict(extra="forbid") model: Literal["water_tank_v1"] = Field(description="Process model type") dt: float = Field(gt=0.0, description="Simulation time step (s)") params: WaterTankParams = Field(description="Physical parameters") signals: WaterTankSignals = Field(description="Signal key mappings") def get_process_spec_json_schema() -> dict: """Return JSON Schema for ProcessSpec, suitable for LLM structured output.""" return ProcessSpec.model_json_schema()