Spaces:
Sleeping
Sleeping
| """ | |
| Configuration loader for streaming intent router. | |
| """ | |
| import os | |
| from pathlib import Path | |
| from typing import Dict, List, Optional | |
| from dataclasses import dataclass, field | |
| import yaml | |
| import numpy as np | |
| class StreamingConfig: | |
| """Configuration for streaming intent router.""" | |
| intents: List[str] = field(default_factory=list) | |
| prior: Dict[str, float] = field(default_factory=dict) | |
| transition_matrix: np.ndarray = field(default_factory=lambda: np.array([])) | |
| intent_to_idx: Dict[str, int] = field(default_factory=dict) | |
| idx_to_intent: Dict[int, str] = field(default_factory=dict) | |
| # Emission parameters | |
| alpha: float = 1.5 | |
| epsilon: float = 1e-8 | |
| # Decision thresholds | |
| theta_hi: float = 0.85 | |
| theta_med: float = 0.60 | |
| theta_lock: float = 0.70 | |
| K: int = 3 | |
| # Window parameters | |
| max_tokens: int = 64 | |
| max_tokens_limit: int = 128 | |
| # Debounce parameters | |
| debounce_ms: int = 150 | |
| min_change_chars: int = 3 | |
| # Model parameters | |
| model_max_length: int = 48 | |
| # Emission temperature (for temperature scaling transform) | |
| temperature: float = 1.0 | |
| # SPRT parameters | |
| sprt_alpha: float = 0.05 | |
| sprt_beta: float = 0.10 | |
| sprt_p0: float = 0.20 | |
| sprt_p1: float = 0.60 | |
| def from_yaml(cls, path: Optional[str] = None) -> "StreamingConfig": | |
| """Load configuration from YAML file.""" | |
| if path is None: | |
| path = Path(__file__).parent.parent / "config" / "streaming_intent.yaml" | |
| with open(path, "r") as f: | |
| data = yaml.safe_load(f) | |
| config = cls() | |
| config.intents = data.get("intents", []) | |
| config.prior = data.get("prior", {}) | |
| # Build intent index mappings | |
| config.intent_to_idx = {intent: i for i, intent in enumerate(config.intents)} | |
| config.idx_to_intent = {i: intent for i, intent in enumerate(config.intents)} | |
| # Build transition matrix as numpy array | |
| n = len(config.intents) | |
| config.transition_matrix = np.zeros((n, n)) | |
| trans_dict = data.get("transition_matrix", {}) | |
| for from_intent, to_probs in trans_dict.items(): | |
| if from_intent in config.intent_to_idx: | |
| i = config.intent_to_idx[from_intent] | |
| for to_intent, prob in to_probs.items(): | |
| if to_intent in config.intent_to_idx: | |
| j = config.intent_to_idx[to_intent] | |
| config.transition_matrix[i, j] = prob | |
| # Normalize rows (ensure they sum to 1) | |
| row_sums = config.transition_matrix.sum(axis=1, keepdims=True) | |
| row_sums[row_sums == 0] = 1 # Avoid division by zero | |
| config.transition_matrix = config.transition_matrix / row_sums | |
| # Emission parameters | |
| emission = data.get("emission", {}) | |
| config.alpha = emission.get("alpha", 1.5) | |
| config.epsilon = emission.get("epsilon", 1e-8) | |
| # Decision thresholds | |
| thresholds = data.get("thresholds", {}) | |
| config.theta_hi = thresholds.get("theta_hi", 0.85) | |
| config.theta_med = thresholds.get("theta_med", 0.60) | |
| config.theta_lock = thresholds.get("theta_lock", 0.70) | |
| config.K = thresholds.get("K", 3) | |
| # Window parameters | |
| window = data.get("window", {}) | |
| config.max_tokens = window.get("max_tokens", 64) | |
| config.max_tokens_limit = window.get("max_tokens_limit", 128) | |
| # Debounce parameters | |
| debounce = data.get("debounce", {}) | |
| config.debounce_ms = debounce.get("debounce_ms", 150) | |
| config.min_change_chars = debounce.get("min_change_chars", 3) | |
| # Model parameters | |
| model = data.get("model", {}) | |
| config.model_max_length = model.get("max_length", 64) | |
| # SPRT parameters | |
| sprt = data.get("sprt", {}) | |
| config.sprt_alpha = sprt.get("alpha", 0.05) | |
| config.sprt_beta = sprt.get("beta", 0.10) | |
| config.sprt_p0 = sprt.get("p0", 0.20) | |
| config.sprt_p1 = sprt.get("p1", 0.60) | |
| # Emission temperature | |
| config.temperature = emission.get("temperature", 1.0) | |
| # Validate configuration | |
| if not config.intents: | |
| raise ValueError( | |
| f"No intents found in config file: {path}. " | |
| "The 'intents' list must contain at least one intent." | |
| ) | |
| return config | |
| def get_prior_vector(self) -> np.ndarray: | |
| """Get prior distribution as numpy array.""" | |
| if not self.intents: | |
| raise ValueError( | |
| "Cannot compute prior vector: intents list is empty. " | |
| "Ensure streaming_intent.yaml contains valid intent definitions." | |
| ) | |
| prior = np.zeros(len(self.intents)) | |
| for intent, prob in self.prior.items(): | |
| if intent in self.intent_to_idx: | |
| prior[self.intent_to_idx[intent]] = prob | |
| # Normalize | |
| if prior.sum() > 0: | |
| prior = prior / prior.sum() | |
| else: | |
| # Fallback to uniform distribution | |
| prior = np.ones(len(self.intents)) / len(self.intents) | |
| return prior | |
| def save_transition_matrix(self, path: Optional[str] = None) -> None: | |
| """Save current transition matrix back to config file.""" | |
| if path is None: | |
| path = Path(__file__).parent.parent / "config" / "streaming_intent.yaml" | |
| with open(path, "r") as f: | |
| data = yaml.safe_load(f) | |
| # Update transition matrix in data | |
| trans_dict = {} | |
| for i, from_intent in enumerate(self.intents): | |
| trans_dict[from_intent] = {} | |
| for j, to_intent in enumerate(self.intents): | |
| trans_dict[from_intent][to_intent] = float(self.transition_matrix[i, j]) | |
| data["transition_matrix"] = trans_dict | |
| with open(path, "w") as f: | |
| yaml.dump(data, f, default_flow_style=False, sort_keys=False) | |