Skip to navigation

nemo_gym.core.failure_kinds

View as Markdown

Shared failure names and episode execution stages.

Gym describes failures with free text and component-local labels, so the same failure arrives at a collector, a log line and a metric under three different names and cannot be grouped. This module holds the one vocabulary those producers share.

A name says what kind of thing went wrong and nothing else. Whether to retry, whether the request may be replayed, whether a completed result should be masked, and what to tell a person are all properties of the occurrence, not of the name — they live on the failure record, on the verify response, and in failure_reason respectively. Keeping them out is deliberate: metadata attached to a name goes stale the moment one caller wants to retry what another caller does not.

Names are low cardinality on purpose. They are safe as a metric label or a span dimension; failure_reason never is, because it carries occurrence detail and can be unbounded.

Classification configuration uses Pydantic, but this module imports no server code.

Module Contents

Classes

NameDescription
FailureClassConfigurable responsibility for reporting; no class implies an action or health verdict.
FailureClassificationConfigOne shared stage/kind mapping and explicit HTTP evidence rules.
StageClassificationConfigClassification overrides for an established originating stage.

Functions

NameDescription
_default_classification_configPrivate read-only default for classification; caller-created configs remain independent.
_default_stage_classification-
_merge_stage_overridesMerge partial stage configurations before their concrete models validate them.
_validate_classification_kinds-
classify_failureInterpret only supplied evidence; never walk causes or mutate the observed kind/stage.
is_evaluation_sideWhether a registered kind has a shared evaluation-side default without extra context.
is_gate_derivedWhether name is a class the acceptance gate derives from saved rows (GATE_DERIVED_KINDS).
is_namespacedWhether name is an environment’s own <server>:<kind> extension.
is_registeredWhether name is part of the shared vocabulary.
is_server_failureServer subcategory for an already established evaluation failure, never a class decision.
is_server_statusWhether a recorded HTTP status is an integer in the 5xx class.
quota_reset_textThe UTC time a provider_quota_exhausted reason names (resets at YYYY-MM-DD HH:MM UTC), else None.
quota_reset_unixquota_reset_text as a unix time, else None.
reporting_failure_kindProject observed HTTP evidence to a reporting label without changing the event.
require_failure_kindValidate a canonical event kind; legacy outcome envelopes keep their migration validator.
validate_failure_kindReturn name unchanged, warning once per unregistered value.

Data

AGENT_COMMAND_NOT_ALLOWED

AGENT_INVALID_OUTPUT

AGENT_LIMIT

AGENT_REQUEST_FAILED

AGENT_ROUTE_FAILED

AGENT_RUN_ERROR

AGENT_TIMEOUT

AGENT_TIMEOUT_INFRASTRUCTURE

CANCELLED

COHORT_INCOMPLETE

COLLECTION_DISPATCH_FAILED

COLLECTION_RESUME_REFUSED

DRIVER_INSTALL_FAILED

EMPTY_ANSWER

ENVIRONMENT_PREPARE_FAILED

ENVIRONMENT_PROTOCOL_VIOLATION

ENVIRONMENT_SERVER_FAILED

EPISODE_FAILED

FAILURE_KINDS

FailureStage

GATE_DERIVED_KINDS

GRADING_WITHOUT_VERDICT

HARBOR_FAILED

HARNESS_TERMINATION_UNCONFIRMED

INFRASTRUCTURE_ERROR

INVALID_OUTPUT_BUDGET

INVALID_REQUEST_PARAMETER

JUDGE_FAILED

JUDGE_FAILURE_KINDS

JUDGE_SKIPPED

JUDGE_UNPARSEABLE

KILL_SHAPED

MALFORMED_ANSWER

MODEL_CONTEXT_LIMIT

MODEL_FAULT_KINDS

MODEL_POLICY_RESPONSE_TIMEOUT

MODEL_REQUEST_ERROR

MODEL_SERVER_ERROR

MODEL_TRANSPORT_ERROR

NON_POLICY_MODEL_REJECTION

PERSISTENCE_FAILED

PROVIDER_COMMAND_OUTCOME_UNKNOWN

PROVIDER_COMMAND_TIMEOUT

PROVIDER_CREATE_PENDING

PROVIDER_CREATE_TIMEOUT

PROVIDER_CREDENTIALS_REJECTED

PROVIDER_FAILURE_KINDS

PROVIDER_INTERNAL_ERROR

PROVIDER_IO_TIMEOUT

PROVIDER_NOT_READY

PROVIDER_OOM_KILLED

PROVIDER_PROTOCOL_ERROR

PROVIDER_PTY_BUSY

PROVIDER_PTY_UNAVAILABLE

PROVIDER_QUOTA_EXHAUSTED

PROVIDER_REQUEST_FAILED

PROVIDER_SETUP_FAILED

PROVIDER_STATUS_UNAVAILABLE

PROVIDER_UNAVAILABLE

QUOTA_RESET_REGEX

ROLLOUT_ALLOCATION_ENDED

SERVER_FAULT_KINDS

SERVER_SPINUP_TIMEOUT

SERVER_START_FAILED

SESSION_EXPIRED

SESSION_LOST

SESSION_RELEASE_FAILED

SHUTDOWN

TRANSPORT_LOCAL_RESOURCE

TRANSPORT_PEER_DROP

TRANSPORT_TIMEOUT

TRANSPORT_UNREACHABLE

TRUNCATED_ANSWER

VERIFIER_ERROR

_DEFAULT_CLASSIFICATION

_NAMESPACED_PATTERN

_NAME_PATTERN

_QUOTA_RESET_PATTERN

_WARNED_UNKNOWN

logger

API

class nemo_gym.core.failure_kinds.FailureClass

Bases: enum.Enum

Configurable responsibility for reporting; no class implies an action or health verdict.

EVALUATION
= 'evaluation'
MODEL
= 'model'
UNKNOWN
= 'unknown'
class nemo_gym.core.failure_kinds.FailureClassificationConfig()

Bases: BaseModel

One shared stage/kind mapping and explicit HTTP evidence rules.

Class overrides by HTTP status take precedence, followed by stage kind/default, HTTP-derived evidence, kind, and the unknown fallback. Stage rules require context admitted by the caller; a provider cannot establish agent ownership on its own. Partial overrides retain unspecified defaults. HTTP-derived labels are reporting projections only: never write them back into observed failure events.

by_failure_kind
dict[str, FailureClass]
by_stage
dict[FailureStage, StageClassificationConfig]
by_status_code
dict[Annotated[int, Field(ge=400, le=599)], FailureClass] = Field(default_factory=dict)
default_class
FailureClass = FailureClass.UNKNOWN
model_config
= ConfigDict(extra='forbid')
non_policy_reporting_kind_by_status_code
dict[Annotated[int, Field(ge=400, le=599)], str | None]
reporting_kind_by_status_code
dict[Annotated[int, Field(ge=400, le=599)], str | None]
nemo_gym.core.failure_kinds.FailureClassificationConfig.merge_rules(
value: dict,
info: pydantic.ValidationInfo
) -> dict
classmethod
nemo_gym.core.failure_kinds.FailureClassificationConfig.merge_stages(
value: object
) -> object
classmethod
classmethod
nemo_gym.core.failure_kinds.FailureClassificationConfig.validate_reporting_kinds(
value: dict[int, str | None]
) -> dict[int, str | None]
classmethod
class nemo_gym.core.failure_kinds.StageClassificationConfig()

Bases: BaseModel

Classification overrides for an established originating stage.

by_failure_kind
dict[str, FailureClass] = Field(default_factory=dict)
default_class
FailureClass | None = None
model_config
= ConfigDict(extra='forbid')
nemo_gym.core.failure_kinds.StageClassificationConfig.validate_kinds(
classmethod
nemo_gym.core.failure_kinds._default_classification_config() -> nemo_gym.core.failure_kinds.FailureClassificationConfig

Private read-only default for classification; caller-created configs remain independent.

nemo_gym.core.failure_kinds._default_stage_classification() -> dict[nemo_gym.core.failure_kinds.FailureStage, nemo_gym.core.failure_kinds.StageClassificationConfig]
nemo_gym.core.failure_kinds._merge_stage_overrides(
defaults: dict[str, pydantic.BaseModel],
value: object,
dict_fields: tuple[str, ...]
) -> object

Merge partial stage configurations before their concrete models validate them.

nemo_gym.core.failure_kinds.classify_failure(
failure_kind: str | None,
http_status: object = None,
policy: bool = True,

Interpret only supplied evidence; never walk causes or mutate the observed kind/stage.

nemo_gym.core.failure_kinds.is_evaluation_side(
name: str,
) -> bool

Whether a registered kind has a shared evaluation-side default without extra context.

nemo_gym.core.failure_kinds.is_gate_derived(
name: str
) -> bool

Whether name is a class the acceptance gate derives from saved rows (GATE_DERIVED_KINDS).

nemo_gym.core.failure_kinds.is_namespaced(
name: str
) -> bool

Whether name is an environment’s own <server>:<kind> extension.

nemo_gym.core.failure_kinds.is_registered(
name: str
) -> bool

Whether name is part of the shared vocabulary.

nemo_gym.core.failure_kinds.is_server_failure(
kind: str | None,
) -> bool

Server subcategory for an already established evaluation failure, never a class decision.

Callers check the applied class first. In particular, OOM can be model-side or evaluation-side depending on its originating stage and admitted ownership.

nemo_gym.core.failure_kinds.is_server_status(
status: object
) -> bool

Whether a recorded HTTP status is an integer in the 5xx class.

nemo_gym.core.failure_kinds.quota_reset_text(
reason: object
) -> str | None

The UTC time a provider_quota_exhausted reason names (resets at YYYY-MM-DD HH:MM UTC), else None.

nemo_gym.core.failure_kinds.quota_reset_unix(
reason: object
) -> float | None

quota_reset_text as a unix time, else None.

nemo_gym.core.failure_kinds.reporting_failure_kind(
kind: str | None,
http_status: object,
policy: bool = True,
) -> str | None

Project observed HTTP evidence to a reporting label without changing the event.

nemo_gym.core.failure_kinds.require_failure_kind(
name: str | None
) -> str

Validate a canonical event kind; legacy outcome envelopes keep their migration validator.

A gate-derived class (GATE_DERIVED_KINDS) is no producer’s kind and is refused like any unregistered name.

nemo_gym.core.failure_kinds.validate_failure_kind(
name: str | None
) -> str | None

Return name unchanged, warning once per unregistered value.

Producers call this at their boundary. It warns rather than raises so a component that still emits an old label stays visible during migration — rejecting it outright would replace an observable wrong name with an invisible dropped failure, which is worse than the problem this vocabulary exists to fix.

nemo_gym.core.failure_kinds.AGENT_COMMAND_NOT_ALLOWED = 'agent_command_not_allowed'
nemo_gym.core.failure_kinds.AGENT_INVALID_OUTPUT = 'agent_invalid_output'
nemo_gym.core.failure_kinds.AGENT_LIMIT = 'agent_limit'
nemo_gym.core.failure_kinds.AGENT_REQUEST_FAILED = 'agent_request_failed'
nemo_gym.core.failure_kinds.AGENT_ROUTE_FAILED = 'agent_route_failed'
nemo_gym.core.failure_kinds.AGENT_RUN_ERROR = 'agent_run_error'
nemo_gym.core.failure_kinds.AGENT_TIMEOUT = 'agent_timeout'
nemo_gym.core.failure_kinds.AGENT_TIMEOUT_INFRASTRUCTURE = 'agent_timeout_infrastructure'
nemo_gym.core.failure_kinds.CANCELLED = 'cancelled'
nemo_gym.core.failure_kinds.COHORT_INCOMPLETE = 'cohort_incomplete'
nemo_gym.core.failure_kinds.COLLECTION_DISPATCH_FAILED = 'collection_dispatch_failed'
nemo_gym.core.failure_kinds.COLLECTION_RESUME_REFUSED = 'collection_resume_refused'
nemo_gym.core.failure_kinds.DRIVER_INSTALL_FAILED = 'driver_install_failed'
nemo_gym.core.failure_kinds.EMPTY_ANSWER = 'empty_answer'
nemo_gym.core.failure_kinds.ENVIRONMENT_PREPARE_FAILED = 'environment_prepare_failed'
nemo_gym.core.failure_kinds.ENVIRONMENT_PROTOCOL_VIOLATION = 'environment_protocol_violation'
nemo_gym.core.failure_kinds.ENVIRONMENT_SERVER_FAILED = 'environment_server_failed'
nemo_gym.core.failure_kinds.EPISODE_FAILED = 'environment_episode_failed'
nemo_gym.core.failure_kinds.FAILURE_KINDS: frozenset[str] = frozenset({TRANSPORT_UNREACHABLE, TRANSPORT_PEER_DROP, TRANSPORT_TIMEOUT, TRANSP...
nemo_gym.core.failure_kinds.FailureStage = Literal['admission', 'seed', 'agent', 'verification', 'cleanup']
nemo_gym.core.failure_kinds.GATE_DERIVED_KINDS: frozenset[str] = frozenset({GRADING_WITHOUT_VERDICT, TRUNCATED_ANSWER, EMPTY_ANSWER, MALFORMED_AN...
nemo_gym.core.failure_kinds.GRADING_WITHOUT_VERDICT = 'grading_without_verdict'
nemo_gym.core.failure_kinds.HARBOR_FAILED = 'harbor_failed'
nemo_gym.core.failure_kinds.HARNESS_TERMINATION_UNCONFIRMED = 'harness_termination_unconfirmed'
nemo_gym.core.failure_kinds.INFRASTRUCTURE_ERROR = 'infrastructure_error'
nemo_gym.core.failure_kinds.INVALID_OUTPUT_BUDGET = 'invalid_output_budget'
nemo_gym.core.failure_kinds.INVALID_REQUEST_PARAMETER = 'invalid_request_parameter'
nemo_gym.core.failure_kinds.JUDGE_FAILED = 'judge_failed'
nemo_gym.core.failure_kinds.JUDGE_FAILURE_KINDS = ('judge_failed', 'judge_invalid', JUDGE_UNPARSEABLE)

The judge failures whose saved answer is judged again, never regenerated: a failed judge call, a verdict that did not parse (judge_unparseable), and collection’s own legacy class for an invalid judge response (judge_invalid).

nemo_gym.core.failure_kinds.JUDGE_SKIPPED = 'judge_skipped'
nemo_gym.core.failure_kinds.JUDGE_UNPARSEABLE = 'judge_unparseable'
nemo_gym.core.failure_kinds.KILL_SHAPED = 'kill_shaped'
nemo_gym.core.failure_kinds.MALFORMED_ANSWER = 'malformed_answer'
nemo_gym.core.failure_kinds.MODEL_CONTEXT_LIMIT = 'model_context_limit'
nemo_gym.core.failure_kinds.MODEL_FAULT_KINDS: frozenset[str] = frozenset(kind for kind, (classification, _) in (_DEFAULT_CLASSIFICATION.items()...
nemo_gym.core.failure_kinds.MODEL_POLICY_RESPONSE_TIMEOUT = 'model_policy_response_timeout'
nemo_gym.core.failure_kinds.MODEL_REQUEST_ERROR = 'model_request_error'
nemo_gym.core.failure_kinds.MODEL_SERVER_ERROR = 'model_server_error'
nemo_gym.core.failure_kinds.MODEL_TRANSPORT_ERROR = 'model_transport_error'
nemo_gym.core.failure_kinds.NON_POLICY_MODEL_REJECTION = 'non_policy_model_rejection'
nemo_gym.core.failure_kinds.PERSISTENCE_FAILED = 'persistence_failed'
nemo_gym.core.failure_kinds.PROVIDER_COMMAND_OUTCOME_UNKNOWN = 'provider_command_outcome_unknown'
nemo_gym.core.failure_kinds.PROVIDER_COMMAND_TIMEOUT = 'provider_command_timeout'
nemo_gym.core.failure_kinds.PROVIDER_CREATE_PENDING = 'provider_create_pending'
nemo_gym.core.failure_kinds.PROVIDER_CREATE_TIMEOUT = 'provider_create_timeout'
nemo_gym.core.failure_kinds.PROVIDER_CREDENTIALS_REJECTED = 'provider_credentials_rejected'
nemo_gym.core.failure_kinds.PROVIDER_FAILURE_KINDS: frozenset[str] = frozenset({PROVIDER_CREATE_TIMEOUT, PROVIDER_CREATE_PENDING, PROVIDER_NOT_READY,...

Registered provider observations; membership does not assign responsibility.

nemo_gym.core.failure_kinds.PROVIDER_INTERNAL_ERROR = 'provider_internal_error'
nemo_gym.core.failure_kinds.PROVIDER_IO_TIMEOUT = 'provider_io_timeout'
nemo_gym.core.failure_kinds.PROVIDER_NOT_READY = 'provider_not_ready'
nemo_gym.core.failure_kinds.PROVIDER_OOM_KILLED = 'provider_oom_killed'
nemo_gym.core.failure_kinds.PROVIDER_PROTOCOL_ERROR = 'provider_protocol_error'
nemo_gym.core.failure_kinds.PROVIDER_PTY_BUSY = 'provider_pty_busy'
nemo_gym.core.failure_kinds.PROVIDER_PTY_UNAVAILABLE = 'provider_pty_unavailable'
nemo_gym.core.failure_kinds.PROVIDER_QUOTA_EXHAUSTED = 'provider_quota_exhausted'
nemo_gym.core.failure_kinds.PROVIDER_REQUEST_FAILED = 'provider_request_failed'
nemo_gym.core.failure_kinds.PROVIDER_SETUP_FAILED = 'provider_setup_failed'
nemo_gym.core.failure_kinds.PROVIDER_STATUS_UNAVAILABLE = 'provider_status_unavailable'
nemo_gym.core.failure_kinds.PROVIDER_UNAVAILABLE = 'provider_unavailable'
nemo_gym.core.failure_kinds.QUOTA_RESET_REGEX = 'resets at (\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2} UTC)'
nemo_gym.core.failure_kinds.ROLLOUT_ALLOCATION_ENDED = 'rollout_allocation_ended'
nemo_gym.core.failure_kinds.SERVER_FAULT_KINDS: frozenset[str] = frozenset(kind for kind, (classification, category) in (_DEFAULT_CLASSIFICATION....
nemo_gym.core.failure_kinds.SERVER_SPINUP_TIMEOUT = 'server_spinup_timeout'
nemo_gym.core.failure_kinds.SERVER_START_FAILED = 'server_start_failed'
nemo_gym.core.failure_kinds.SESSION_EXPIRED = 'session_expired'
nemo_gym.core.failure_kinds.SESSION_LOST = 'session_lost'
nemo_gym.core.failure_kinds.SESSION_RELEASE_FAILED = 'session_release_failed'
nemo_gym.core.failure_kinds.SHUTDOWN = 'shutdown'
nemo_gym.core.failure_kinds.TRANSPORT_LOCAL_RESOURCE = 'transport_local_resource'
nemo_gym.core.failure_kinds.TRANSPORT_PEER_DROP = 'transport_peer_drop'
nemo_gym.core.failure_kinds.TRANSPORT_TIMEOUT = 'transport_timeout'
nemo_gym.core.failure_kinds.TRANSPORT_UNREACHABLE = 'transport_unreachable'
nemo_gym.core.failure_kinds.TRUNCATED_ANSWER = 'truncated_answer'
nemo_gym.core.failure_kinds.VERIFIER_ERROR = 'verifier_error'
nemo_gym.core.failure_kinds._DEFAULT_CLASSIFICATION: dict[str, tuple[FailureClass, str | None]] = {kind: (FailureClass.EVALUATION, None) for kind in (FAILURE_KINDS | GATE_DERIVED...
nemo_gym.core.failure_kinds._NAMESPACED_PATTERN = re.compile('[a-z][a-z0-9_]*:[a-z][a-z0-9_]*')
nemo_gym.core.failure_kinds._NAME_PATTERN = re.compile('[a-z][a-z0-9_]*')
nemo_gym.core.failure_kinds._QUOTA_RESET_PATTERN = re.compile(QUOTA_RESET_REGEX)
nemo_gym.core.failure_kinds._WARNED_UNKNOWN: set[str] = set()
nemo_gym.core.failure_kinds.logger = logging.getLogger(__name__)