234 lines
7.5 KiB
Python
234 lines
7.5 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
"""Track which GenerationRequest fields the user explicitly provided.
|
|
|
|
When translating a GenerationRequest into a legacy SamplingParam we must
|
|
distinguish user-provided values (which should override model defaults)
|
|
from schema defaults (which should NOT override model defaults).
|
|
|
|
The mechanism: a single ``_fastvideo_explicit_paths`` set stored on the
|
|
root ``GenerationRequest``. It holds dotted leaf paths (e.g.
|
|
``"sampling.guidance_scale"``) the user has touched, either via raw
|
|
config at bind time or via attribute assignment at runtime. A patched
|
|
``__setattr__`` on the request dataclass types records assignments into
|
|
this set.
|
|
|
|
The set holds leaf paths only. Nested dataclass or mapping assignments
|
|
are flattened to their leaves at record time.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable, Mapping
|
|
import dataclasses
|
|
from typing import Any, cast
|
|
|
|
from fastvideo.api.schema import (
|
|
ContinuationState,
|
|
GenerationPlan,
|
|
GenerationRequest,
|
|
InputConfig,
|
|
OutputConfig,
|
|
PlannedStage,
|
|
RequestRuntimeConfig,
|
|
RunConfig,
|
|
SamplingConfig,
|
|
ServeConfig,
|
|
)
|
|
|
|
EXPLICIT_PATHS_ATTR = "_fastvideo_explicit_paths"
|
|
|
|
_TRACKING_ROOT_ATTR = "_fastvideo_request_tracking_root"
|
|
_TRACKING_PATH_ATTR = "_fastvideo_request_tracking_path"
|
|
_TRACKING_PATCHED_ATTR = "_fastvideo_request_tracking_patched"
|
|
|
|
_TRACKED_REQUEST_TYPES = (
|
|
GenerationRequest,
|
|
InputConfig,
|
|
SamplingConfig,
|
|
RequestRuntimeConfig,
|
|
OutputConfig,
|
|
ContinuationState,
|
|
PlannedStage,
|
|
GenerationPlan,
|
|
)
|
|
|
|
|
|
def bind_generation_request_raw(
|
|
request: GenerationRequest,
|
|
raw: Mapping[str, Any] | None,
|
|
) -> GenerationRequest:
|
|
"""Install explicit-path tracking on *request*.
|
|
|
|
*raw* is the parsed config dict (YAML/JSON/kwargs); every leaf key
|
|
in it becomes an explicit path. Subsequent attribute assignments on
|
|
*request* or its nested dataclasses are recorded automatically via a
|
|
patched ``__setattr__``.
|
|
"""
|
|
_ensure_request_tracking()
|
|
# Disable recording while we walk the tree to install roots.
|
|
object.__setattr__(request, EXPLICIT_PATHS_ATTR, None)
|
|
_set_tracking_roots(request, request, "")
|
|
paths: set[str] = set()
|
|
_record_value_paths(raw or {}, "", paths)
|
|
object.__setattr__(request, EXPLICIT_PATHS_ATTR, paths)
|
|
return request
|
|
|
|
|
|
def bind_run_config_raw(
|
|
config: RunConfig,
|
|
raw: Mapping[str, Any],
|
|
) -> RunConfig:
|
|
request_raw = raw.get("request")
|
|
if isinstance(request_raw, Mapping):
|
|
bind_generation_request_raw(config.request, request_raw)
|
|
else:
|
|
bind_generation_request_raw(config.request, {})
|
|
return config
|
|
|
|
|
|
def bind_serve_config_raw(
|
|
config: ServeConfig,
|
|
raw: Mapping[str, Any],
|
|
) -> ServeConfig:
|
|
default_request_raw = raw.get("default_request")
|
|
if isinstance(default_request_raw, Mapping):
|
|
bind_generation_request_raw(config.default_request, default_request_raw)
|
|
else:
|
|
bind_generation_request_raw(config.default_request, {})
|
|
return config
|
|
|
|
|
|
def get_explicit_paths(request: GenerationRequest) -> frozenset[str]:
|
|
"""Return a snapshot of the explicit paths set on *request*."""
|
|
paths = getattr(request, EXPLICIT_PATHS_ATTR, None)
|
|
if isinstance(paths, set | frozenset):
|
|
return frozenset(paths)
|
|
return frozenset()
|
|
|
|
|
|
def reset_tracking_roots(request: GenerationRequest) -> None:
|
|
"""Re-install tracking roots after a deepcopy or manual clone.
|
|
|
|
The paths set itself deepcopies correctly; we only need to repoint
|
|
the tracking root on nested dataclasses at the new root.
|
|
"""
|
|
_ensure_request_tracking()
|
|
_set_tracking_roots(request, request, "")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Path recording
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _record_value_paths(
|
|
value: Any,
|
|
prefix: str,
|
|
out: set[str],
|
|
) -> None:
|
|
"""Add every leaf path under *value* to *out*.
|
|
|
|
A leaf is any terminal value (non-dataclass, non-mapping, or empty
|
|
mapping/dataclass). ``prefix`` is the dotted path at which *value*
|
|
sits. When called with an empty ``prefix`` (the root), leaves are
|
|
recorded at their own key.
|
|
"""
|
|
if dataclasses.is_dataclass(value) and not isinstance(value, type):
|
|
dc_fields = dataclasses.fields(value)
|
|
if not dc_fields:
|
|
if prefix:
|
|
out.add(prefix)
|
|
return
|
|
for field in dc_fields:
|
|
child = getattr(value, field.name)
|
|
path = f"{prefix}.{field.name}" if prefix else field.name
|
|
_record_value_paths(child, path, out)
|
|
return
|
|
if isinstance(value, Mapping):
|
|
if not value:
|
|
if prefix:
|
|
out.add(prefix)
|
|
return
|
|
for key, child in value.items():
|
|
path = f"{prefix}.{key}" if prefix else key
|
|
_record_value_paths(child, path, out)
|
|
return
|
|
if prefix:
|
|
out.add(prefix)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# __setattr__ patching
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _ensure_request_tracking() -> None:
|
|
for config_type in _TRACKED_REQUEST_TYPES:
|
|
_patch_tracking_setattr(config_type)
|
|
|
|
|
|
def _patch_tracking_setattr(config_type: type[Any]) -> None:
|
|
if getattr(config_type, _TRACKING_PATCHED_ATTR, False):
|
|
return
|
|
|
|
original_setattr = cast(
|
|
Callable[[Any, str, Any], None],
|
|
config_type.__setattr__,
|
|
)
|
|
field_names = {field.name for field in dataclasses.fields(config_type)}
|
|
|
|
def _tracking_setattr(self: Any, name: str, value: Any) -> None:
|
|
if name.startswith("_fastvideo_") or name not in field_names:
|
|
original_setattr(self, name, value)
|
|
return
|
|
|
|
original_setattr(self, name, value)
|
|
|
|
root = getattr(self, _TRACKING_ROOT_ATTR, None)
|
|
if root is None:
|
|
return
|
|
paths = getattr(root, EXPLICIT_PATHS_ATTR, None)
|
|
if not isinstance(paths, set):
|
|
return
|
|
|
|
prefix = getattr(self, _TRACKING_PATH_ATTR, "")
|
|
path = f"{prefix}.{name}" if prefix else name
|
|
# Wholesale dataclass replacement: install roots on the new
|
|
# instance so its future mutations are tracked too.
|
|
if dataclasses.is_dataclass(value) and not isinstance(value, type):
|
|
_set_tracking_roots(root, value, path)
|
|
_record_value_paths(value, path, paths)
|
|
|
|
type.__setattr__(config_type, "__setattr__", _tracking_setattr)
|
|
setattr(config_type, _TRACKING_PATCHED_ATTR, True)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tree walk to set tracking root/path on nested dataclasses
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _set_tracking_roots(
|
|
root: GenerationRequest,
|
|
obj: Any,
|
|
prefix: str,
|
|
) -> None:
|
|
if not dataclasses.is_dataclass(obj) or isinstance(obj, type):
|
|
return
|
|
object.__setattr__(obj, _TRACKING_ROOT_ATTR, root)
|
|
object.__setattr__(obj, _TRACKING_PATH_ATTR, prefix)
|
|
for field in dataclasses.fields(obj):
|
|
child = getattr(obj, field.name)
|
|
child_path = f"{prefix}.{field.name}" if prefix else field.name
|
|
if dataclasses.is_dataclass(child) and not isinstance(child, type):
|
|
_set_tracking_roots(root, child, child_path)
|
|
|
|
|
|
__all__ = [
|
|
"EXPLICIT_PATHS_ATTR",
|
|
"bind_generation_request_raw",
|
|
"bind_run_config_raw",
|
|
"bind_serve_config_raw",
|
|
"get_explicit_paths",
|
|
"reset_tracking_roots",
|
|
]
|