- harness/es.py: append-only event store + проекции (образец — кооп-портал) - harness/levels.py: уровни 1с/1м/1ч/1д, контекст ∝ масштабу - harness/scheduler.py: уровневые потоки, идемпотентные тики, absorbed-стабы - harness/isolated_runner.py + _llm_worker.py: LLM-тик в сабпроцессе (глобальный asyncio-рантайм Strands не переживает 2+ create_harness) - cli.py: init/task/tasks/status/events + requeue (написан самим минутным агентом) - 9router: отдельный ключ sk-simbiot, модель auto (glm-5.3 первым)
168 lines
5.2 KiB
Python
168 lines
5.2 KiB
Python
"""Configuration types and helpers for bidirectional model providers."""
|
|
|
|
import copy
|
|
from collections.abc import Mapping
|
|
from typing import Any, Literal
|
|
|
|
from typing_extensions import Required, TypedDict
|
|
|
|
from ....models._validation import validate_config_keys
|
|
from ..types.events import AudioChannel, AudioFormat
|
|
|
|
|
|
class AudioStreamConfig(TypedDict):
|
|
"""Resolved format of an audio stream.
|
|
|
|
Attributes:
|
|
sample_rate: Sample rate in Hz.
|
|
channels: Number of audio channels.
|
|
format: Audio encoding.
|
|
"""
|
|
|
|
sample_rate: int
|
|
channels: AudioChannel
|
|
format: AudioFormat
|
|
|
|
|
|
class AudioConfig(TypedDict):
|
|
"""Resolved input and output formats consumed by audio I/O.
|
|
|
|
Pass provider-specific audio options to the model constructor and use
|
|
``get_audio_config()`` to obtain the resulting stream formats.
|
|
|
|
Attributes:
|
|
input: Audio format configured for model input.
|
|
output: Audio format produced by the model.
|
|
"""
|
|
|
|
input: AudioStreamConfig
|
|
output: AudioStreamConfig
|
|
|
|
|
|
class BedrockNovaSonicAudioStreamConfig(TypedDict):
|
|
"""Nova Sonic stream options. Audio uses mono PCM.
|
|
|
|
Attributes:
|
|
sample_rate: Sample rate in Hz.
|
|
"""
|
|
|
|
sample_rate: Literal[8000, 16000, 24000]
|
|
|
|
|
|
class BedrockNovaSonicAudioConfig(TypedDict, total=False):
|
|
"""Nova Sonic input and output audio options.
|
|
|
|
Omitted streams use a sample rate of 16000 Hz.
|
|
|
|
Attributes:
|
|
input: Input stream options.
|
|
output: Output stream options.
|
|
"""
|
|
|
|
input: BedrockNovaSonicAudioStreamConfig
|
|
output: BedrockNovaSonicAudioStreamConfig
|
|
|
|
|
|
class GoogleGeminiLiveAudioStreamConfig(TypedDict):
|
|
"""Gemini Live input stream options. Audio uses mono PCM.
|
|
|
|
Attributes:
|
|
sample_rate: Input sample rate in Hz.
|
|
"""
|
|
|
|
sample_rate: int
|
|
|
|
|
|
class GoogleGeminiLiveAudioConfig(TypedDict, total=False):
|
|
"""Gemini Live audio options. Output is mono PCM at 24000 Hz.
|
|
|
|
Omitting the input stream uses a sample rate of 16000 Hz.
|
|
|
|
Attributes:
|
|
input: Input stream options.
|
|
"""
|
|
|
|
input: GoogleGeminiLiveAudioStreamConfig
|
|
|
|
|
|
class ConnectionConfig(TypedDict, total=False):
|
|
"""Declared reconnect timing for a bidirectional model.
|
|
|
|
Providers declare this so the agent loop can reconnect proactively, before the provider
|
|
terminates the connection on its own limit. A provider that declares nothing (empty config)
|
|
keeps reactive-only behavior: no proactive timer, reconnect only after the provider reports
|
|
a timeout.
|
|
|
|
All fields are optional. The proactive timer arms only when ``restart_after_s`` is declared.
|
|
|
|
Attributes:
|
|
restart_after_s: Seconds after a connection is established at which to proactively
|
|
reconnect. Set it at least ~10s below the provider's own connection limit:
|
|
the reconnect may wait briefly for the current turn to finish (aligning the swap to a
|
|
turn boundary), and that wait plus the swap must complete before the provider's limit.
|
|
auto_reconnect: Whether the loop reconnects automatically (default True).
|
|
"""
|
|
|
|
restart_after_s: int
|
|
auto_reconnect: bool
|
|
|
|
|
|
class ModelConfig(TypedDict, total=False):
|
|
"""Configuration shared by bidirectional model providers.
|
|
|
|
Attributes:
|
|
model_id: Provider model identifier.
|
|
params: Provider-specific keyword arguments passed to the model request or session.
|
|
connection: Reconnect timing overrides.
|
|
"""
|
|
|
|
model_id: Required[str]
|
|
params: dict[str, Any] | None
|
|
connection: ConnectionConfig
|
|
|
|
|
|
class ModelUpdateConfig(TypedDict, total=False):
|
|
"""Partial configuration update shared by bidirectional model providers.
|
|
|
|
Attributes:
|
|
model_id: Provider model identifier.
|
|
params: Provider-specific keyword arguments passed to the model request or session.
|
|
connection: Reconnect timing overrides.
|
|
"""
|
|
|
|
model_id: str
|
|
params: dict[str, Any] | None
|
|
connection: ConnectionConfig
|
|
|
|
|
|
def _validate_model_config(config: Mapping[str, Any]) -> None:
|
|
"""Validate shared bidirectional model configuration."""
|
|
missing_keys = ModelConfig.__required_keys__ - config.keys()
|
|
if missing_keys:
|
|
raise ValueError(f"Missing required configuration parameters: {sorted(missing_keys)}.")
|
|
|
|
model_id = config["model_id"]
|
|
if not isinstance(model_id, str) or not model_id:
|
|
raise ValueError("model_id must be a non-empty string")
|
|
|
|
validate_config_keys(config, ModelConfig)
|
|
validate_config_keys(config.get("connection", {}), ConnectionConfig)
|
|
|
|
|
|
def _validate_audio_config(config: AudioConfig) -> None:
|
|
"""Validate shared audio configuration."""
|
|
validate_config_keys(config, AudioConfig)
|
|
validate_config_keys(config["input"], AudioStreamConfig)
|
|
validate_config_keys(config["output"], AudioStreamConfig)
|
|
|
|
|
|
def _merge_config(config: dict[str, Any], overrides: dict[str, Any]) -> dict[str, Any]:
|
|
"""Recursively merge configs without modifying either input."""
|
|
merged = copy.deepcopy(config)
|
|
for key, value in overrides.items():
|
|
if isinstance(value, dict):
|
|
existing = merged.get(key)
|
|
merged[key] = _merge_config(existing if isinstance(existing, dict) else {}, value)
|
|
else:
|
|
merged[key] = copy.deepcopy(value)
|
|
return merged
|