"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""

from __future__ import annotations
from datetime import datetime
import pydantic
from pydantic import model_serializer
from tofupilot.v2.types import (
    BaseModel,
    Nullable,
    OptionalNullable,
    UNSET,
    UNSET_SENTINEL,
)
from typing import Any, Dict, List, Literal, Optional, Union
from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict


RunCreateOutcome = Literal["PASS", "FAIL", "ERROR", "TIMEOUT", "ABORTED"]
r"""Overall test result. Use PASS when test succeeds, FAIL when test fails but script execution completed successfully, ERROR when script execution fails, TIMEOUT when test exceeds time limit, ABORTED for manual script interruption."""

RunCreatePhaseOutcome = Literal["PASS", "FAIL", "SKIP", "ERROR"]
r"""Overall result of the phase execution. Use PASS when phase succeeds, FAIL when phase fails but execution completed successfully, ERROR when phase execution fails, SKIP when phase was not executed."""

RunCreateMeasurementOutcome = Literal["PASS", "FAIL", "UNSET"]
r"""Result of the measurement validation. Use PASS when measurement meets all criteria, FAIL when measurement is outside acceptable limits or validation fails, UNSET when no validation was performed."""

XAxisValidatorOutcome = Literal["PASS", "FAIL", "UNSET"]
r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""

XAxisExpectedValueTypedDict = TypeAliasType(
    "XAxisExpectedValueTypedDict", Union[bool, float, str, List[float], List[str]]
)
r"""Expected value for comparison. Type depends on operator."""


XAxisExpectedValue = TypeAliasType(
    "XAxisExpectedValue", Union[bool, float, str, List[float], List[str]]
)
r"""Expected value for comparison. Type depends on operator."""


class XAxisValidatorTypedDict(TypedDict):
    r"""Structured validator specification with operator and expected value."""

    outcome: NotRequired[Nullable[XAxisValidatorOutcome]]
    r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""
    operator: NotRequired[Nullable[str]]
    r"""Comparison operator: \">\", \">=\", \"<\", \"<=\", \"==\", \"!=\", \"matches\", \"in\", \"range\" """
    expected_value: NotRequired[Nullable[XAxisExpectedValueTypedDict]]
    r"""Expected value for comparison. Type depends on operator."""
    expression: NotRequired[Nullable[str]]
    r"""Original expression string for display/audit purposes."""
    is_decisive: NotRequired[Nullable[bool]]
    r"""Whether this validator is decisive (if it fails, measurement fails). False for marginal/warning validators. Defaults to true."""


class XAxisValidator(BaseModel):
    r"""Structured validator specification with operator and expected value."""

    outcome: OptionalNullable[XAxisValidatorOutcome] = UNSET
    r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""

    operator: OptionalNullable[str] = UNSET
    r"""Comparison operator: \">\", \">=\", \"<\", \"<=\", \"==\", \"!=\", \"matches\", \"in\", \"range\" """

    expected_value: OptionalNullable[XAxisExpectedValue] = UNSET
    r"""Expected value for comparison. Type depends on operator."""

    expression: OptionalNullable[str] = UNSET
    r"""Original expression string for display/audit purposes."""

    is_decisive: OptionalNullable[bool] = UNSET
    r"""Whether this validator is decisive (if it fails, measurement fails). False for marginal/warning validators. Defaults to true."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "outcome",
            "operator",
            "expected_value",
            "expression",
            "is_decisive",
        ]
        nullable_fields = [
            "outcome",
            "operator",
            "expected_value",
            "expression",
            "is_decisive",
        ]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


XAxisAggregationOutcome = Literal["PASS", "FAIL", "UNSET"]
r"""Computed result of aggregation validation. Server stores as-is."""

XAxisValueTypedDict = TypeAliasType("XAxisValueTypedDict", Union[float, str, bool])
r"""Computed aggregation value."""


XAxisValue = TypeAliasType("XAxisValue", Union[float, str, bool])
r"""Computed aggregation value."""


XAxisAggregationValidatorOutcome = Literal["PASS", "FAIL", "UNSET"]
r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""

XAxisAggregationExpectedValueTypedDict = TypeAliasType(
    "XAxisAggregationExpectedValueTypedDict",
    Union[bool, float, str, List[float], List[str]],
)
r"""Expected value for comparison. Type depends on operator."""


XAxisAggregationExpectedValue = TypeAliasType(
    "XAxisAggregationExpectedValue", Union[bool, float, str, List[float], List[str]]
)
r"""Expected value for comparison. Type depends on operator."""


class XAxisAggregationValidatorTypedDict(TypedDict):
    r"""Structured validator specification with operator, expected value, and outcome."""

    outcome: NotRequired[Nullable[XAxisAggregationValidatorOutcome]]
    r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""
    operator: NotRequired[Nullable[str]]
    r"""Comparison operator: \">\", \">=\", \"<\", \"<=\", \"==\", \"!=\", \"matches\", \"in\", \"range\" """
    expected_value: NotRequired[Nullable[XAxisAggregationExpectedValueTypedDict]]
    r"""Expected value for comparison. Type depends on operator."""
    expression: NotRequired[Nullable[str]]
    r"""Original expression string for display/audit purposes."""
    is_decisive: NotRequired[Nullable[bool]]
    r"""Whether this validator is decisive (if it fails, measurement fails). False for marginal/warning validators. Defaults to true."""


class XAxisAggregationValidator(BaseModel):
    r"""Structured validator specification with operator, expected value, and outcome."""

    outcome: OptionalNullable[XAxisAggregationValidatorOutcome] = UNSET
    r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""

    operator: OptionalNullable[str] = UNSET
    r"""Comparison operator: \">\", \">=\", \"<\", \"<=\", \"==\", \"!=\", \"matches\", \"in\", \"range\" """

    expected_value: OptionalNullable[XAxisAggregationExpectedValue] = UNSET
    r"""Expected value for comparison. Type depends on operator."""

    expression: OptionalNullable[str] = UNSET
    r"""Original expression string for display/audit purposes."""

    is_decisive: OptionalNullable[bool] = UNSET
    r"""Whether this validator is decisive (if it fails, measurement fails). False for marginal/warning validators. Defaults to true."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "outcome",
            "operator",
            "expected_value",
            "expression",
            "is_decisive",
        ]
        nullable_fields = [
            "outcome",
            "operator",
            "expected_value",
            "expression",
            "is_decisive",
        ]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class XAxisAggregationTypedDict(TypedDict):
    r"""Aggregation specification with computed value and optional validators."""

    type: str
    r"""Aggregation function: \"min\", \"max\", \"avg\", \"sum\", \"count\", \"std\", \"median\", \"percentile_95\", etc."""
    outcome: NotRequired[Nullable[XAxisAggregationOutcome]]
    r"""Computed result of aggregation validation. Server stores as-is."""
    value: NotRequired[Nullable[XAxisValueTypedDict]]
    r"""Computed aggregation value."""
    unit: NotRequired[Nullable[str]]
    r"""Unit for the aggregated value."""
    validators: NotRequired[Nullable[List[XAxisAggregationValidatorTypedDict]]]
    r"""Validators applied to the aggregated value."""


class XAxisAggregation(BaseModel):
    r"""Aggregation specification with computed value and optional validators."""

    type: str
    r"""Aggregation function: \"min\", \"max\", \"avg\", \"sum\", \"count\", \"std\", \"median\", \"percentile_95\", etc."""

    outcome: OptionalNullable[XAxisAggregationOutcome] = UNSET
    r"""Computed result of aggregation validation. Server stores as-is."""

    value: OptionalNullable[XAxisValue] = UNSET
    r"""Computed aggregation value."""

    unit: OptionalNullable[str] = UNSET
    r"""Unit for the aggregated value."""

    validators: OptionalNullable[List[XAxisAggregationValidator]] = UNSET
    r"""Validators applied to the aggregated value."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["outcome", "value", "unit", "validators"]
        nullable_fields = ["outcome", "value", "unit", "validators"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class XAxisTypedDict(TypedDict):
    r"""X-axis data series for multi-dimensional measurements. Use with y_axis for structured multi-dimensional data with per-axis validators/aggregations."""

    data: List[float]
    r"""Array of numeric data points for this axis."""
    units: NotRequired[Nullable[str]]
    r"""Unit for this axis."""
    description: NotRequired[Nullable[str]]
    r"""Description of this data series."""
    validators: NotRequired[Nullable[List[XAxisValidatorTypedDict]]]
    r"""Validators for this specific axis/series."""
    aggregations: NotRequired[Nullable[List[XAxisAggregationTypedDict]]]
    r"""Aggregations computed over this axis data (min, max, avg, etc.)."""


class XAxis(BaseModel):
    r"""X-axis data series for multi-dimensional measurements. Use with y_axis for structured multi-dimensional data with per-axis validators/aggregations."""

    data: List[float]
    r"""Array of numeric data points for this axis."""

    units: OptionalNullable[str] = UNSET
    r"""Unit for this axis."""

    description: OptionalNullable[str] = UNSET
    r"""Description of this data series."""

    validators: OptionalNullable[List[XAxisValidator]] = UNSET
    r"""Validators for this specific axis/series."""

    aggregations: OptionalNullable[List[XAxisAggregation]] = UNSET
    r"""Aggregations computed over this axis data (min, max, avg, etc.)."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["units", "description", "validators", "aggregations"]
        nullable_fields = ["units", "description", "validators", "aggregations"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


YAxiValidatorOutcome = Literal["PASS", "FAIL", "UNSET"]
r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""

YAxiExpectedValueTypedDict = TypeAliasType(
    "YAxiExpectedValueTypedDict", Union[bool, float, str, List[float], List[str]]
)
r"""Expected value for comparison. Type depends on operator."""


YAxiExpectedValue = TypeAliasType(
    "YAxiExpectedValue", Union[bool, float, str, List[float], List[str]]
)
r"""Expected value for comparison. Type depends on operator."""


class YAxiValidatorTypedDict(TypedDict):
    r"""Structured validator specification with operator and expected value."""

    outcome: NotRequired[Nullable[YAxiValidatorOutcome]]
    r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""
    operator: NotRequired[Nullable[str]]
    r"""Comparison operator: \">\", \">=\", \"<\", \"<=\", \"==\", \"!=\", \"matches\", \"in\", \"range\" """
    expected_value: NotRequired[Nullable[YAxiExpectedValueTypedDict]]
    r"""Expected value for comparison. Type depends on operator."""
    expression: NotRequired[Nullable[str]]
    r"""Original expression string for display/audit purposes."""
    is_decisive: NotRequired[Nullable[bool]]
    r"""Whether this validator is decisive (if it fails, measurement fails). False for marginal/warning validators. Defaults to true."""


class YAxiValidator(BaseModel):
    r"""Structured validator specification with operator and expected value."""

    outcome: OptionalNullable[YAxiValidatorOutcome] = UNSET
    r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""

    operator: OptionalNullable[str] = UNSET
    r"""Comparison operator: \">\", \">=\", \"<\", \"<=\", \"==\", \"!=\", \"matches\", \"in\", \"range\" """

    expected_value: OptionalNullable[YAxiExpectedValue] = UNSET
    r"""Expected value for comparison. Type depends on operator."""

    expression: OptionalNullable[str] = UNSET
    r"""Original expression string for display/audit purposes."""

    is_decisive: OptionalNullable[bool] = UNSET
    r"""Whether this validator is decisive (if it fails, measurement fails). False for marginal/warning validators. Defaults to true."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "outcome",
            "operator",
            "expected_value",
            "expression",
            "is_decisive",
        ]
        nullable_fields = [
            "outcome",
            "operator",
            "expected_value",
            "expression",
            "is_decisive",
        ]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


YAxiAggregationOutcome = Literal["PASS", "FAIL", "UNSET"]
r"""Computed result of aggregation validation. Server stores as-is."""

YAxiValueTypedDict = TypeAliasType("YAxiValueTypedDict", Union[float, str, bool])
r"""Computed aggregation value."""


YAxiValue = TypeAliasType("YAxiValue", Union[float, str, bool])
r"""Computed aggregation value."""


YAxiAggregationValidatorOutcome = Literal["PASS", "FAIL", "UNSET"]
r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""

YAxiAggregationExpectedValueTypedDict = TypeAliasType(
    "YAxiAggregationExpectedValueTypedDict",
    Union[bool, float, str, List[float], List[str]],
)
r"""Expected value for comparison. Type depends on operator."""


YAxiAggregationExpectedValue = TypeAliasType(
    "YAxiAggregationExpectedValue", Union[bool, float, str, List[float], List[str]]
)
r"""Expected value for comparison. Type depends on operator."""


class YAxiAggregationValidatorTypedDict(TypedDict):
    r"""Structured validator specification with operator, expected value, and outcome."""

    outcome: NotRequired[Nullable[YAxiAggregationValidatorOutcome]]
    r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""
    operator: NotRequired[Nullable[str]]
    r"""Comparison operator: \">\", \">=\", \"<\", \"<=\", \"==\", \"!=\", \"matches\", \"in\", \"range\" """
    expected_value: NotRequired[Nullable[YAxiAggregationExpectedValueTypedDict]]
    r"""Expected value for comparison. Type depends on operator."""
    expression: NotRequired[Nullable[str]]
    r"""Original expression string for display/audit purposes."""
    is_decisive: NotRequired[Nullable[bool]]
    r"""Whether this validator is decisive (if it fails, measurement fails). False for marginal/warning validators. Defaults to true."""


class YAxiAggregationValidator(BaseModel):
    r"""Structured validator specification with operator, expected value, and outcome."""

    outcome: OptionalNullable[YAxiAggregationValidatorOutcome] = UNSET
    r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""

    operator: OptionalNullable[str] = UNSET
    r"""Comparison operator: \">\", \">=\", \"<\", \"<=\", \"==\", \"!=\", \"matches\", \"in\", \"range\" """

    expected_value: OptionalNullable[YAxiAggregationExpectedValue] = UNSET
    r"""Expected value for comparison. Type depends on operator."""

    expression: OptionalNullable[str] = UNSET
    r"""Original expression string for display/audit purposes."""

    is_decisive: OptionalNullable[bool] = UNSET
    r"""Whether this validator is decisive (if it fails, measurement fails). False for marginal/warning validators. Defaults to true."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "outcome",
            "operator",
            "expected_value",
            "expression",
            "is_decisive",
        ]
        nullable_fields = [
            "outcome",
            "operator",
            "expected_value",
            "expression",
            "is_decisive",
        ]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class YAxiAggregationTypedDict(TypedDict):
    r"""Aggregation specification with computed value and optional validators."""

    type: str
    r"""Aggregation function: \"min\", \"max\", \"avg\", \"sum\", \"count\", \"std\", \"median\", \"percentile_95\", etc."""
    outcome: NotRequired[Nullable[YAxiAggregationOutcome]]
    r"""Computed result of aggregation validation. Server stores as-is."""
    value: NotRequired[Nullable[YAxiValueTypedDict]]
    r"""Computed aggregation value."""
    unit: NotRequired[Nullable[str]]
    r"""Unit for the aggregated value."""
    validators: NotRequired[Nullable[List[YAxiAggregationValidatorTypedDict]]]
    r"""Validators applied to the aggregated value."""


class YAxiAggregation(BaseModel):
    r"""Aggregation specification with computed value and optional validators."""

    type: str
    r"""Aggregation function: \"min\", \"max\", \"avg\", \"sum\", \"count\", \"std\", \"median\", \"percentile_95\", etc."""

    outcome: OptionalNullable[YAxiAggregationOutcome] = UNSET
    r"""Computed result of aggregation validation. Server stores as-is."""

    value: OptionalNullable[YAxiValue] = UNSET
    r"""Computed aggregation value."""

    unit: OptionalNullable[str] = UNSET
    r"""Unit for the aggregated value."""

    validators: OptionalNullable[List[YAxiAggregationValidator]] = UNSET
    r"""Validators applied to the aggregated value."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["outcome", "value", "unit", "validators"]
        nullable_fields = ["outcome", "value", "unit", "validators"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class YAxiTypedDict(TypedDict):
    r"""Data series with numeric data, unit, and optional validators/aggregations."""

    data: List[float]
    r"""Array of numeric data points for this axis."""
    units: NotRequired[Nullable[str]]
    r"""Unit for this axis."""
    description: NotRequired[Nullable[str]]
    r"""Description of this data series."""
    validators: NotRequired[Nullable[List[YAxiValidatorTypedDict]]]
    r"""Validators for this specific axis/series."""
    aggregations: NotRequired[Nullable[List[YAxiAggregationTypedDict]]]
    r"""Aggregations computed over this axis data (min, max, avg, etc.)."""


class YAxi(BaseModel):
    r"""Data series with numeric data, unit, and optional validators/aggregations."""

    data: List[float]
    r"""Array of numeric data points for this axis."""

    units: OptionalNullable[str] = UNSET
    r"""Unit for this axis."""

    description: OptionalNullable[str] = UNSET
    r"""Description of this data series."""

    validators: OptionalNullable[List[YAxiValidator]] = UNSET
    r"""Validators for this specific axis/series."""

    aggregations: OptionalNullable[List[YAxiAggregation]] = UNSET
    r"""Aggregations computed over this axis data (min, max, avg, etc.)."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["units", "description", "validators", "aggregations"]
        nullable_fields = ["units", "description", "validators", "aggregations"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


RunCreateMeasuredValue2TypedDict = TypeAliasType(
    "RunCreateMeasuredValue2TypedDict", Union[Dict[str, Any], List[Any]]
)


RunCreateMeasuredValue2 = TypeAliasType(
    "RunCreateMeasuredValue2", Union[Dict[str, Any], List[Any]]
)


RunCreateMeasuredValue1TypedDict = TypeAliasType(
    "RunCreateMeasuredValue1TypedDict",
    Union[float, str, bool, List[List[float]], RunCreateMeasuredValue2TypedDict],
)
r"""The actual value captured. [LEGACY for multi-dim] For multi-dimensional with per-axis validators/aggregations, use x_axis/y_axis instead."""


RunCreateMeasuredValue1 = TypeAliasType(
    "RunCreateMeasuredValue1",
    Union[float, str, bool, List[List[float]], RunCreateMeasuredValue2],
)
r"""The actual value captured. [LEGACY for multi-dim] For multi-dimensional with per-axis validators/aggregations, use x_axis/y_axis instead."""


RunCreateUnitsTypedDict = TypeAliasType(
    "RunCreateUnitsTypedDict", Union[str, List[str]]
)
r"""[LEGACY for multi-dim] Units of measurement. For structured multi-dimensional, use units within x_axis/y_axis instead."""


RunCreateUnits = TypeAliasType("RunCreateUnits", Union[str, List[str]])
r"""[LEGACY for multi-dim] Units of measurement. For structured multi-dimensional, use units within x_axis/y_axis instead."""


ValidatorsOutcome = Literal["PASS", "FAIL", "UNSET"]
r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""

ValidatorsExpectedValueTypedDict = TypeAliasType(
    "ValidatorsExpectedValueTypedDict", Union[bool, float, str, List[float], List[str]]
)
r"""Expected value for comparison. Type depends on operator."""


ValidatorsExpectedValue = TypeAliasType(
    "ValidatorsExpectedValue", Union[bool, float, str, List[float], List[str]]
)
r"""Expected value for comparison. Type depends on operator."""


class ValidatorsTypedDict(TypedDict):
    r"""Structured validator specification with operator and expected value."""

    outcome: NotRequired[Nullable[ValidatorsOutcome]]
    r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""
    operator: NotRequired[Nullable[str]]
    r"""Comparison operator: \">\", \">=\", \"<\", \"<=\", \"==\", \"!=\", \"matches\", \"in\", \"range\" """
    expected_value: NotRequired[Nullable[ValidatorsExpectedValueTypedDict]]
    r"""Expected value for comparison. Type depends on operator."""
    expression: NotRequired[Nullable[str]]
    r"""Original expression string for display/audit purposes."""
    is_decisive: NotRequired[Nullable[bool]]
    r"""Whether this validator is decisive (if it fails, measurement fails). False for marginal/warning validators. Defaults to true."""


class Validators(BaseModel):
    r"""Structured validator specification with operator and expected value."""

    outcome: OptionalNullable[ValidatorsOutcome] = UNSET
    r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""

    operator: OptionalNullable[str] = UNSET
    r"""Comparison operator: \">\", \">=\", \"<\", \"<=\", \"==\", \"!=\", \"matches\", \"in\", \"range\" """

    expected_value: OptionalNullable[ValidatorsExpectedValue] = UNSET
    r"""Expected value for comparison. Type depends on operator."""

    expression: OptionalNullable[str] = UNSET
    r"""Original expression string for display/audit purposes."""

    is_decisive: OptionalNullable[bool] = UNSET
    r"""Whether this validator is decisive (if it fails, measurement fails). False for marginal/warning validators. Defaults to true."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "outcome",
            "operator",
            "expected_value",
            "expression",
            "is_decisive",
        ]
        nullable_fields = [
            "outcome",
            "operator",
            "expected_value",
            "expression",
            "is_decisive",
        ]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


ValidatorsUnionTypedDict = TypeAliasType(
    "ValidatorsUnionTypedDict", Union[List[ValidatorsTypedDict], List[str]]
)
r"""Validators for this measurement. Use structured ValidatorSpec objects with operator and expected_value. Legacy string format (e.g. \"x >= 3\") is also accepted and stored as expression."""


ValidatorsUnion = TypeAliasType("ValidatorsUnion", Union[List[Validators], List[str]])
r"""Validators for this measurement. Use structured ValidatorSpec objects with operator and expected_value. Legacy string format (e.g. \"x >= 3\") is also accepted and stored as expression."""


RunCreateAggregationOutcome = Literal["PASS", "FAIL", "UNSET"]
r"""Computed result of aggregation validation. Server stores as-is."""

RunCreateValueTypedDict = TypeAliasType(
    "RunCreateValueTypedDict", Union[float, str, bool]
)
r"""Computed aggregation value."""


RunCreateValue = TypeAliasType("RunCreateValue", Union[float, str, bool])
r"""Computed aggregation value."""


RunCreateAggregationValidatorOutcome = Literal["PASS", "FAIL", "UNSET"]
r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""

RunCreateAggregationExpectedValueTypedDict = TypeAliasType(
    "RunCreateAggregationExpectedValueTypedDict",
    Union[bool, float, str, List[float], List[str]],
)
r"""Expected value for comparison. Type depends on operator."""


RunCreateAggregationExpectedValue = TypeAliasType(
    "RunCreateAggregationExpectedValue", Union[bool, float, str, List[float], List[str]]
)
r"""Expected value for comparison. Type depends on operator."""


class RunCreateAggregationValidatorTypedDict(TypedDict):
    r"""Structured validator specification with operator, expected value, and outcome."""

    outcome: NotRequired[Nullable[RunCreateAggregationValidatorOutcome]]
    r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""
    operator: NotRequired[Nullable[str]]
    r"""Comparison operator: \">\", \">=\", \"<\", \"<=\", \"==\", \"!=\", \"matches\", \"in\", \"range\" """
    expected_value: NotRequired[Nullable[RunCreateAggregationExpectedValueTypedDict]]
    r"""Expected value for comparison. Type depends on operator."""
    expression: NotRequired[Nullable[str]]
    r"""Original expression string for display/audit purposes."""
    is_decisive: NotRequired[Nullable[bool]]
    r"""Whether this validator is decisive (if it fails, measurement fails). False for marginal/warning validators. Defaults to true."""


class RunCreateAggregationValidator(BaseModel):
    r"""Structured validator specification with operator, expected value, and outcome."""

    outcome: OptionalNullable[RunCreateAggregationValidatorOutcome] = UNSET
    r"""Pre-computed validation result from test framework. Server stores as-is, does not re-evaluate."""

    operator: OptionalNullable[str] = UNSET
    r"""Comparison operator: \">\", \">=\", \"<\", \"<=\", \"==\", \"!=\", \"matches\", \"in\", \"range\" """

    expected_value: OptionalNullable[RunCreateAggregationExpectedValue] = UNSET
    r"""Expected value for comparison. Type depends on operator."""

    expression: OptionalNullable[str] = UNSET
    r"""Original expression string for display/audit purposes."""

    is_decisive: OptionalNullable[bool] = UNSET
    r"""Whether this validator is decisive (if it fails, measurement fails). False for marginal/warning validators. Defaults to true."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "outcome",
            "operator",
            "expected_value",
            "expression",
            "is_decisive",
        ]
        nullable_fields = [
            "outcome",
            "operator",
            "expected_value",
            "expression",
            "is_decisive",
        ]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class RunCreateAggregationTypedDict(TypedDict):
    r"""Aggregation specification with computed value and optional validators."""

    type: str
    r"""Aggregation function: \"min\", \"max\", \"avg\", \"sum\", \"count\", \"std\", \"median\", \"percentile_95\", etc."""
    outcome: NotRequired[Nullable[RunCreateAggregationOutcome]]
    r"""Computed result of aggregation validation. Server stores as-is."""
    value: NotRequired[Nullable[RunCreateValueTypedDict]]
    r"""Computed aggregation value."""
    unit: NotRequired[Nullable[str]]
    r"""Unit for the aggregated value."""
    validators: NotRequired[Nullable[List[RunCreateAggregationValidatorTypedDict]]]
    r"""Validators applied to the aggregated value."""


class RunCreateAggregation(BaseModel):
    r"""Aggregation specification with computed value and optional validators."""

    type: str
    r"""Aggregation function: \"min\", \"max\", \"avg\", \"sum\", \"count\", \"std\", \"median\", \"percentile_95\", etc."""

    outcome: OptionalNullable[RunCreateAggregationOutcome] = UNSET
    r"""Computed result of aggregation validation. Server stores as-is."""

    value: OptionalNullable[RunCreateValue] = UNSET
    r"""Computed aggregation value."""

    unit: OptionalNullable[str] = UNSET
    r"""Unit for the aggregated value."""

    validators: OptionalNullable[List[RunCreateAggregationValidator]] = UNSET
    r"""Validators applied to the aggregated value."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["outcome", "value", "unit", "validators"]
        nullable_fields = ["outcome", "value", "unit", "validators"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class RunCreateMeasurementTypedDict(TypedDict):
    name: str
    r"""Name identifier for the measurement. Each measurement should have a descriptive name that identifies the specific data point being captured. Analytics at measurement level are computed using this name as unique identifier."""
    outcome: RunCreateMeasurementOutcome
    r"""Result of the measurement validation. Use PASS when measurement meets all criteria, FAIL when measurement is outside acceptable limits or validation fails, UNSET when no validation was performed."""
    x_axis: NotRequired[Nullable[XAxisTypedDict]]
    r"""X-axis data series for multi-dimensional measurements. Use with y_axis for structured multi-dimensional data with per-axis validators/aggregations."""
    y_axis: NotRequired[Nullable[List[YAxiTypedDict]]]
    r"""Y-axis data series (one or more) for multi-dimensional measurements. Each series can have its own validators and aggregations."""
    measured_value: NotRequired[Nullable[RunCreateMeasuredValue1TypedDict]]
    r"""The actual value captured. [LEGACY for multi-dim] For multi-dimensional with per-axis validators/aggregations, use x_axis/y_axis instead."""
    units: NotRequired[Nullable[RunCreateUnitsTypedDict]]
    r"""[LEGACY for multi-dim] Units of measurement. For structured multi-dimensional, use units within x_axis/y_axis instead."""
    lower_limit: NotRequired[float]
    r"""Use validators with operator \">=\" instead. Will be converted to a validator automatically."""
    upper_limit: NotRequired[float]
    r"""Use validators with operator \"<=\" instead. Will be converted to a validator automatically."""
    validators: NotRequired[Nullable[ValidatorsUnionTypedDict]]
    r"""Validators for this measurement. Use structured ValidatorSpec objects with operator and expected_value. Legacy string format (e.g. \"x >= 3\") is also accepted and stored as expression."""
    aggregations: NotRequired[Nullable[List[RunCreateAggregationTypedDict]]]
    r"""Aggregations computed over measurement values (min, max, avg, etc.). Each aggregation can have its own validators."""
    docstring: NotRequired[Nullable[str]]
    r"""Additional notes or documentation about this measurement."""


class RunCreateMeasurement(BaseModel):
    name: str
    r"""Name identifier for the measurement. Each measurement should have a descriptive name that identifies the specific data point being captured. Analytics at measurement level are computed using this name as unique identifier."""

    outcome: RunCreateMeasurementOutcome
    r"""Result of the measurement validation. Use PASS when measurement meets all criteria, FAIL when measurement is outside acceptable limits or validation fails, UNSET when no validation was performed."""

    x_axis: OptionalNullable[XAxis] = UNSET
    r"""X-axis data series for multi-dimensional measurements. Use with y_axis for structured multi-dimensional data with per-axis validators/aggregations."""

    y_axis: OptionalNullable[List[YAxi]] = UNSET
    r"""Y-axis data series (one or more) for multi-dimensional measurements. Each series can have its own validators and aggregations."""

    measured_value: OptionalNullable[RunCreateMeasuredValue1] = UNSET
    r"""The actual value captured. [LEGACY for multi-dim] For multi-dimensional with per-axis validators/aggregations, use x_axis/y_axis instead."""

    units: OptionalNullable[RunCreateUnits] = UNSET
    r"""[LEGACY for multi-dim] Units of measurement. For structured multi-dimensional, use units within x_axis/y_axis instead."""

    lower_limit: Annotated[
        Optional[float],
        pydantic.Field(
            deprecated="warning: ** DEPRECATED ** - This will be removed in a future release, please migrate away from it as soon as possible."
        ),
    ] = None
    r"""Use validators with operator \">=\" instead. Will be converted to a validator automatically."""

    upper_limit: Annotated[
        Optional[float],
        pydantic.Field(
            deprecated="warning: ** DEPRECATED ** - This will be removed in a future release, please migrate away from it as soon as possible."
        ),
    ] = None
    r"""Use validators with operator \"<=\" instead. Will be converted to a validator automatically."""

    validators: OptionalNullable[ValidatorsUnion] = UNSET
    r"""Validators for this measurement. Use structured ValidatorSpec objects with operator and expected_value. Legacy string format (e.g. \"x >= 3\") is also accepted and stored as expression."""

    aggregations: OptionalNullable[List[RunCreateAggregation]] = UNSET
    r"""Aggregations computed over measurement values (min, max, avg, etc.). Each aggregation can have its own validators."""

    docstring: OptionalNullable[str] = UNSET
    r"""Additional notes or documentation about this measurement."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "x_axis",
            "y_axis",
            "measured_value",
            "units",
            "lower_limit",
            "upper_limit",
            "validators",
            "aggregations",
            "docstring",
        ]
        nullable_fields = [
            "x_axis",
            "y_axis",
            "measured_value",
            "units",
            "validators",
            "aggregations",
            "docstring",
        ]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class RunCreatePhaseTypedDict(TypedDict):
    name: str
    r"""Name identifier for the test phase. Each phase should have a descriptive name that identifies the specific stage of testing being performed. Analytics at phase level are computed using this name as unique identifier."""
    outcome: RunCreatePhaseOutcome
    r"""Overall result of the phase execution. Use PASS when phase succeeds, FAIL when phase fails but execution completed successfully, ERROR when phase execution fails, SKIP when phase was not executed."""
    started_at: datetime
    r"""ISO 8601 timestamp when the phase execution began."""
    ended_at: datetime
    r"""ISO 8601 timestamp when the phase execution completed."""
    docstring: NotRequired[Nullable[str]]
    r"""Additional notes or documentation about this test phase."""
    measurements: NotRequired[Nullable[List[RunCreateMeasurementTypedDict]]]
    r"""Array of measurements collected during this phase. Each measurement captures specific test data points with values, limits, and validation results. If no measurements are specified, the phase will be created without measurement data."""


class RunCreatePhase(BaseModel):
    name: str
    r"""Name identifier for the test phase. Each phase should have a descriptive name that identifies the specific stage of testing being performed. Analytics at phase level are computed using this name as unique identifier."""

    outcome: RunCreatePhaseOutcome
    r"""Overall result of the phase execution. Use PASS when phase succeeds, FAIL when phase fails but execution completed successfully, ERROR when phase execution fails, SKIP when phase was not executed."""

    started_at: datetime
    r"""ISO 8601 timestamp when the phase execution began."""

    ended_at: datetime
    r"""ISO 8601 timestamp when the phase execution completed."""

    docstring: OptionalNullable[str] = UNSET
    r"""Additional notes or documentation about this test phase."""

    measurements: OptionalNullable[List[RunCreateMeasurement]] = UNSET
    r"""Array of measurements collected during this phase. Each measurement captures specific test data points with values, limits, and validation results. If no measurements are specified, the phase will be created without measurement data."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["docstring", "measurements"]
        nullable_fields = ["docstring", "measurements"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


RunCreateLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
r"""Severity level of the log message following standard system logging levels. Use DEBUG for detailed diagnostic information, INFO for general execution information, WARNING for unexpected events or potential issues, ERROR for serious problems that prevented function execution, CRITICAL for severe errors that may cause program termination."""


class RunCreateLogTypedDict(TypedDict):
    level: RunCreateLevel
    r"""Severity level of the log message following standard system logging levels. Use DEBUG for detailed diagnostic information, INFO for general execution information, WARNING for unexpected events or potential issues, ERROR for serious problems that prevented function execution, CRITICAL for severe errors that may cause program termination."""
    timestamp: datetime
    r"""ISO 8601 timestamp when the log message was generated."""
    message: str
    r"""Content of the log message. Contains the actual log text describing the event, error, or information being logged. Messages longer than 10,000 characters will be truncated."""
    source_file: str
    r"""Name or path of the source file where the log message originated. Helps identify the code location that generated the log entry."""
    line_number: float
    r"""Line number in the source file where the log message was generated. Used for debugging and tracing log origins."""


class RunCreateLog(BaseModel):
    level: RunCreateLevel
    r"""Severity level of the log message following standard system logging levels. Use DEBUG for detailed diagnostic information, INFO for general execution information, WARNING for unexpected events or potential issues, ERROR for serious problems that prevented function execution, CRITICAL for severe errors that may cause program termination."""

    timestamp: datetime
    r"""ISO 8601 timestamp when the log message was generated."""

    message: str
    r"""Content of the log message. Contains the actual log text describing the event, error, or information being logged. Messages longer than 10,000 characters will be truncated."""

    source_file: str
    r"""Name or path of the source file where the log message originated. Helps identify the code location that generated the log entry."""

    line_number: float
    r"""Line number in the source file where the log message was generated. Used for debugging and tracing log origins."""


class RunCreateRequestTypedDict(TypedDict):
    outcome: RunCreateOutcome
    r"""Overall test result. Use PASS when test succeeds, FAIL when test fails but script execution completed successfully, ERROR when script execution fails, TIMEOUT when test exceeds time limit, ABORTED for manual script interruption."""
    procedure_id: str
    r"""Procedure ID. Create the procedure in the app first, then find the auto-generated ID on the procedure page."""
    started_at: datetime
    r"""ISO 8601 timestamp when the test run began execution. This timestamp will be used to track when the test execution started and for historical analysis of test runs. A separate created_at timestamp is stored internally server side to track upload date."""
    ended_at: datetime
    r"""ISO 8601 timestamp when the test run finished execution."""
    serial_number: str
    r"""Unique serial number of the unit under test. Matched case-insensitively. If no unit with this serial number exists, one will be created."""
    procedure_version: NotRequired[Nullable[str]]
    r"""Specific version of the test procedure used for the run. Matched case-insensitively. If none exist, a procedure with this procedure version will be created. If no procedure version is specified, the run will not be linked to any specific version."""
    operated_by: NotRequired[str]
    r"""Email address of the operator who executed the test run. The operator must exist as a user in the system. The run will be linked to this user to track who performed the test."""
    part_number: NotRequired[str]
    r"""Component part number for the unit. Matched case-insensitively. This field is required if the part number cannot be extracted from the serial number (as set in the settings). This field takes precedence over extraction from serial number. A component with the provided or extracted part number will be created if one does not exist."""
    revision_number: NotRequired[str]
    r"""Hardware revision identifier for the unit. Matched case-insensitively. If none exist, a revision with this number will be created. If no revision is specified, the unit will be linked to the default revision of the part number."""
    batch_number: NotRequired[str]
    r"""Production batch identifier for grouping units manufactured together. Matched case-insensitively. If none exist, a batch with this batch number will be created. If no batch number is specified, the unit will not be linked to any batch."""
    sub_units: NotRequired[List[str]]
    r"""Array of sub-unit serial numbers that are part of this main unit. Matched case-insensitively. Each sub-unit must already exist and will be linked as a sub-component of the main unit under test. If no sub-units are specified, the unit will be created without sub-unit relationships."""
    docstring: NotRequired[str]
    r"""Additional notes or documentation about this test run."""
    phases: NotRequired[List[RunCreatePhaseTypedDict]]
    r"""Array of test phases with measurements and results. Each phase represents a distinct stage of the test execution with timing information, outcome status, and optional measurements. If no phases are specified, the run will be created without phase-level organization of test data."""
    logs: NotRequired[List[RunCreateLogTypedDict]]
    r"""Array of log messages generated during the test execution. Each log entry captures events, errors, and diagnostic information with severity levels and source code references. If no logs are specified, the run will be created without log entries."""


class RunCreateRequest(BaseModel):
    outcome: RunCreateOutcome
    r"""Overall test result. Use PASS when test succeeds, FAIL when test fails but script execution completed successfully, ERROR when script execution fails, TIMEOUT when test exceeds time limit, ABORTED for manual script interruption."""

    procedure_id: str
    r"""Procedure ID. Create the procedure in the app first, then find the auto-generated ID on the procedure page."""

    started_at: datetime
    r"""ISO 8601 timestamp when the test run began execution. This timestamp will be used to track when the test execution started and for historical analysis of test runs. A separate created_at timestamp is stored internally server side to track upload date."""

    ended_at: datetime
    r"""ISO 8601 timestamp when the test run finished execution."""

    serial_number: str
    r"""Unique serial number of the unit under test. Matched case-insensitively. If no unit with this serial number exists, one will be created."""

    procedure_version: OptionalNullable[str] = UNSET
    r"""Specific version of the test procedure used for the run. Matched case-insensitively. If none exist, a procedure with this procedure version will be created. If no procedure version is specified, the run will not be linked to any specific version."""

    operated_by: Optional[str] = None
    r"""Email address of the operator who executed the test run. The operator must exist as a user in the system. The run will be linked to this user to track who performed the test."""

    part_number: Optional[str] = None
    r"""Component part number for the unit. Matched case-insensitively. This field is required if the part number cannot be extracted from the serial number (as set in the settings). This field takes precedence over extraction from serial number. A component with the provided or extracted part number will be created if one does not exist."""

    revision_number: Optional[str] = None
    r"""Hardware revision identifier for the unit. Matched case-insensitively. If none exist, a revision with this number will be created. If no revision is specified, the unit will be linked to the default revision of the part number."""

    batch_number: Optional[str] = None
    r"""Production batch identifier for grouping units manufactured together. Matched case-insensitively. If none exist, a batch with this batch number will be created. If no batch number is specified, the unit will not be linked to any batch."""

    sub_units: Optional[List[str]] = None
    r"""Array of sub-unit serial numbers that are part of this main unit. Matched case-insensitively. Each sub-unit must already exist and will be linked as a sub-component of the main unit under test. If no sub-units are specified, the unit will be created without sub-unit relationships."""

    docstring: Optional[str] = None
    r"""Additional notes or documentation about this test run."""

    phases: Optional[List[RunCreatePhase]] = None
    r"""Array of test phases with measurements and results. Each phase represents a distinct stage of the test execution with timing information, outcome status, and optional measurements. If no phases are specified, the run will be created without phase-level organization of test data."""

    logs: Optional[List[RunCreateLog]] = None
    r"""Array of log messages generated during the test execution. Each log entry captures events, errors, and diagnostic information with severity levels and source code references. If no logs are specified, the run will be created without log entries."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "procedure_version",
            "operated_by",
            "part_number",
            "revision_number",
            "batch_number",
            "sub_units",
            "docstring",
            "phases",
            "logs",
        ]
        nullable_fields = ["procedure_version"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class RunCreateResponseTypedDict(TypedDict):
    r"""Run created successfully"""

    id: str
    r"""Unique identifier of the created run."""


class RunCreateResponse(BaseModel):
    r"""Run created successfully"""

    id: str
    r"""Unique identifier of the created run."""
