| 66 | |
| 67 | @dataclass(frozen=True, slots=True) |
| 68 | class SideChannelTaskConfig: |
| 69 | primary: str |
| 70 | fallback: tuple[str, ...] = () |
| 71 | max_tokens: int = 256 |
| 72 | quality: QualityFallbackPolicy = field(default_factory=QualityFallbackPolicy) |
| 73 | |
| 74 | def candidates(self) -> list[str]: |
| 75 | return [self.primary, *self.fallback] |
| 76 | |
| 77 | @classmethod |
| 78 | def from_dict( |
| 79 | cls, |
| 80 | data: Mapping[str, Any] | None, |
| 81 | *, |
| 82 | base: SideChannelTaskConfig, |
| 83 | ) -> SideChannelTaskConfig: |
| 84 | if not data: |
| 85 | return base |
| 86 | primary = str(data.get("primary", base.primary)).strip() |
| 87 | if not primary: |
| 88 | raise ValueError("side-channel primary model cannot be empty") |
| 89 | fallback = tuple( |
| 90 | str(model).strip() |
| 91 | for model in data.get("fallback", base.fallback) |
| 92 | if str(model).strip() |
| 93 | ) |
| 94 | task = cls( |
| 95 | primary=primary, |
| 96 | fallback=fallback, |
| 97 | max_tokens=int(data.get("max_tokens", base.max_tokens)), |
| 98 | quality=QualityFallbackPolicy.from_dict( |
| 99 | data.get("quality"), |
| 100 | base=base.quality, |
| 101 | ), |
| 102 | ) |
| 103 | if task.max_tokens <= 0: |
| 104 | raise ValueError("side-channel max_tokens must be > 0") |
| 105 | return task |
| 106 | |
| 107 | def to_dict(self) -> dict[str, Any]: |
| 108 | return { |
| 109 | "primary": self.primary, |
| 110 | "fallback": list(self.fallback), |
| 111 | "max_tokens": self.max_tokens, |
| 112 | "quality": self.quality.to_dict(), |
| 113 | } |
| 114 | |
| 115 | |
| 116 | @dataclass(frozen=True, slots=True) |
no outgoing calls