Skip to content

Commit 139acc4

Browse files
jopemachineclaude
andcommitted
refactor(BA-5983): defer default-value application to strict Pydantic types
``ModelHealthCheckDraft.to_resolved`` and ``ModelServiceConfigDraft.to_resolved`` previously duplicated every default value (``10.0``, ``10``, ``15.0``, ``200``, ``60.0`` for the health-check fields; ``[]`` and ``"/bin/bash"`` for the service config) inline as ``if self.x is not None else <default>`` branches. Those literals were already declared on the strict ``ModelHealthCheck`` / ``ModelServiceConfig`` classes via ``Field(default=...)``, so the project carried the same constants in two places at risk of drift. Switch ``to_resolved`` to drop ``None`` scalars via ``model_dump(exclude_none=True)`` and let the strict type's field defaults apply during ``model_validate``/constructor call. Required- field checks (``path``, ``port``) stay as explicit ``ValueError`` raises so the error message remains domain-specific rather than a generic ``pydantic.ValidationError``. The nested ``health_check`` draft is resolved out-of-band before the strict service config is composed, since it carries its own required-field check. Behavior-preserving: - Every ``Field(default=...)`` on the strict type matches the literal the old code wrote inline (verified field-by-field). - ``pre_start_actions``: old ``or []`` and new ``exclude_none=True`` produce identical results for the two reachable shapes (``None`` and ``list[PreStartAction]``). - Field-level constraints (``gt``, ``ge``) still fire because both the old constructor call and the new ``model_validate`` execute Pydantic validators. Existing tests in test_config / test_revision_draft_merge / test_model_definition_start_command_compat / test_revision_draft_reader still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8366021 commit 139acc4

1 file changed

Lines changed: 9 additions & 15 deletions

File tree

src/ai/backend/common/config.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -534,16 +534,9 @@ class ModelHealthCheckDraft(BaseConfigModel):
534534
def to_resolved(self) -> ModelHealthCheck:
535535
if self.path is None:
536536
raise ValueError("ModelHealthCheck.path is required")
537-
return ModelHealthCheck(
538-
interval=self.interval if self.interval is not None else 10.0,
539-
path=self.path,
540-
max_retries=self.max_retries if self.max_retries is not None else 10,
541-
max_wait_time=self.max_wait_time if self.max_wait_time is not None else 15.0,
542-
expected_status_code=(
543-
self.expected_status_code if self.expected_status_code is not None else 200
544-
),
545-
initial_delay=self.initial_delay if self.initial_delay is not None else 60.0,
546-
)
537+
# Drop unset (None) fields so the strict type's ``Field(default=...)``
538+
# declarations remain the single source of truth for default values.
539+
return ModelHealthCheck.model_validate(self.model_dump(exclude_none=True))
547540

548541

549542
class ModelServiceConfigDraft(BaseConfigModel):
@@ -561,12 +554,13 @@ def _coerce_start_command(cls, value: Any) -> Any:
561554
def to_resolved(self) -> ModelServiceConfig:
562555
if self.port is None:
563556
raise ValueError("ModelServiceConfig.port is required")
557+
# Drop unset (None) scalars so the strict type's ``Field(default=...)``
558+
# declarations remain the single source of truth for default values;
559+
# resolve the nested ``health_check`` draft explicitly so its own
560+
# required-field check (``path``) fires with a clear error message.
564561
return ModelServiceConfig(
565-
pre_start_actions=self.pre_start_actions or [],
566-
start_command=self.start_command,
567-
shell=self.shell if self.shell is not None else "/bin/bash",
568-
port=self.port,
569-
health_check=(self.health_check.to_resolved() if self.health_check else None),
562+
**self.model_dump(exclude_none=True, exclude={"health_check"}),
563+
health_check=self.health_check.to_resolved() if self.health_check else None,
570564
)
571565

572566

0 commit comments

Comments
 (0)