refactor(tool_bias): merge BiasProfile from models.py into tool_bias.py
Per the 4-criteria decision rule: BiasProfile fails C1 (only used by
tool_presets + tool_bias), fails C2 (no state machine), fails C3 (no
dedicated test file), borderline C4. MERGE into the existing
src/tool_bias.py which already has ToolBiasEngine.
This commit:
1. Adds BiasProfile class definition to src/tool_bias.py at the top
(after the dataclass + typing imports).
2. Removes BiasProfile from src/models.py.
3. Adds lazy re-export via the existing __getattr__ in src/models.py
(EAGER would deadlock: tool_presets needs BiasProfile + tool_bias
needs Tool/ToolPreset, and both want models re-exports).
4. Updates src/tool_presets.py to use the local-import pattern for
BiasProfile (in load_all_bias_profiles) + adds
'from __future__ import annotations' so the 'BiasProfile' type
annotation is a string. This breaks the cycle.
5. Updates src/tool_bias.py to import Tool + ToolPreset from
src.tool_presets directly (no longer through models) + adds
'from __future__ import annotations'.
Verification: VC8 (BiasProfile)
from src.tool_bias import BiasProfile # OK
from src.tool_presets import Tool, ToolPreset # OK
from src.models import Tool, ToolPreset, BiasProfile # OK (lazy)
Tool is Tool returns True
ToolPreset is ToolPreset returns True
BiasProfile is BiasProfile returns True
Tests verified (10/10 PASS):
tests/test_tool_preset_manager.py (4 tests)
tests/test_bias_models.py (3 tests)
tests/test_tool_bias.py (3 tests)
Cycle resolution:
models -> tool_presets (lazy via __getattr__)
tool_presets -> tool_bias (local import in function body, only at call time)
tool_bias -> tool_presets (eager; OK because tool_presets is fully
loaded by the time tool_bias's class
definitions need Tool/ToolPreset)
The eager load of tool_bias from tool_presets is what made the
'from __future__ import annotations' necessary in both files (for
Tool/ToolPreset string annotations in tool_bias method signatures).
This commit is contained in:
+8
-25
@@ -276,6 +276,10 @@ def __getattr__(name: str) -> Any:
|
||||
val = _Tool if name == "Tool" else _ToolPreset
|
||||
globals()[name] = val
|
||||
return val
|
||||
if name == "BiasProfile":
|
||||
from src.tool_bias import BiasProfile as _BiasProfile
|
||||
globals()[name] = _BiasProfile
|
||||
return _BiasProfile
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
|
||||
# MMA Core dataclasses (ThinkingSegment, Ticket, Track, WorkerContext, TrackMetadata)
|
||||
@@ -291,31 +295,10 @@ def __getattr__(name: str) -> Any:
|
||||
# src.project_files directly.
|
||||
|
||||
#region: Tool Models
|
||||
# Tool + ToolPreset moved to src/tool_presets.py in
|
||||
# module_taxonomy_refactor_20260627 Phase 3d. The re-exports at the top of
|
||||
# this module keep 'from src.models import Tool' (and ToolPreset) working for
|
||||
# legacy callers. BiasProfile stays here until Phase 3e.
|
||||
|
||||
@dataclass
|
||||
class BiasProfile:
|
||||
name: str
|
||||
tool_weights: Dict[str, int] = field(default_factory=dict)
|
||||
category_multipliers: Dict[str, float] = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> Metadata:
|
||||
return {
|
||||
"name": self.name,
|
||||
"tool_weights": self.tool_weights,
|
||||
"category_multipliers": self.category_multipliers,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Metadata) -> "BiasProfile":
|
||||
return cls(
|
||||
name = data["name"],
|
||||
tool_weights = data.get("tool_weights", {}),
|
||||
category_multipliers = data.get("category_multipliers", {}),
|
||||
)
|
||||
# Tool + ToolPreset moved to src/tool_presets.py in Phase 3d. BiasProfile
|
||||
# moved to src/tool_bias.py in Phase 3e. All three are re-exported lazily
|
||||
# via the __getattr__ below to avoid the circular import (tool_presets and
|
||||
# tool_bias both want to import from each other via models).
|
||||
|
||||
#region: UI/Editor
|
||||
|
||||
|
||||
+28
-2
@@ -1,6 +1,32 @@
|
||||
from typing import List, Dict, Any, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from src.models import Tool, ToolPreset, BiasProfile
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from src.tool_presets import Tool, ToolPreset
|
||||
from src.type_aliases import Metadata
|
||||
|
||||
|
||||
@dataclass
|
||||
class BiasProfile:
|
||||
name: str
|
||||
tool_weights: Dict[str, int] = field(default_factory=dict)
|
||||
category_multipliers: Dict[str, float] = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> Metadata:
|
||||
return {
|
||||
"name": self.name,
|
||||
"tool_weights": self.tool_weights,
|
||||
"category_multipliers": self.category_multipliers,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Metadata) -> "BiasProfile":
|
||||
return cls(
|
||||
name = data["name"],
|
||||
tool_weights = data.get("tool_weights", {}),
|
||||
category_multipliers = data.get("category_multipliers", {}),
|
||||
)
|
||||
|
||||
|
||||
class ToolBiasEngine:
|
||||
|
||||
+4
-2
@@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import tomllib
|
||||
import tomli_w
|
||||
|
||||
@@ -6,7 +8,6 @@ from pathlib import Path
|
||||
from typing import Dict, List, Optional, Union, Any
|
||||
|
||||
from src import paths
|
||||
from src.models import BiasProfile
|
||||
from src.type_aliases import Metadata
|
||||
|
||||
|
||||
@@ -135,10 +136,11 @@ class ToolPresetManager:
|
||||
del data["presets"][name]
|
||||
self._write_raw(path, data)
|
||||
|
||||
def load_all_bias_profiles(self) -> Dict[str, BiasProfile]:
|
||||
def load_all_bias_profiles(self) -> Dict[str, "BiasProfile"]:
|
||||
"""
|
||||
[C: tests/test_tool_preset_manager.py:test_bias_profiles_merged, tests/test_tool_preset_manager.py:test_delete_bias_profile, tests/test_tool_preset_manager.py:test_save_bias_profile]
|
||||
"""
|
||||
from src.tool_bias import BiasProfile
|
||||
global_path = paths.get_global_tool_presets_path()
|
||||
global_data = self._read_raw(global_path).get("bias_profiles", {})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user