| """ |
| Timing models for simulated annotation behavior. |
| |
| This module provides realistic timing distributions for simulated |
| annotations, supporting various statistical distributions. |
| """ |
|
|
| import random |
| import time |
| from typing import Optional |
|
|
| from .config import TimingConfig |
|
|
|
|
| class TimingModel: |
| """Model for generating realistic annotation timing. |
| |
| Supports multiple distribution types: |
| - uniform: Random time uniformly distributed between min and max |
| - normal: Gaussian distribution with configurable mean and std |
| - exponential: Exponential distribution for realistic variability |
| """ |
|
|
| def __init__(self, config: TimingConfig): |
| """Initialize timing model. |
| |
| Args: |
| config: TimingConfig with distribution parameters |
| """ |
| self.config = config |
|
|
| def get_annotation_time(self) -> float: |
| """Generate annotation time based on configured distribution. |
| |
| Returns: |
| Annotation time in seconds |
| """ |
| if self.config.distribution == "uniform": |
| return random.uniform( |
| self.config.annotation_time_min, self.config.annotation_time_max |
| ) |
|
|
| elif self.config.distribution == "normal": |
| time_val = random.gauss( |
| self.config.annotation_time_mean, self.config.annotation_time_std |
| ) |
| |
| return max( |
| self.config.annotation_time_min, |
| min(self.config.annotation_time_max, time_val), |
| ) |
|
|
| elif self.config.distribution == "exponential": |
| |
| rate = 1.0 / self.config.annotation_time_mean |
| time_val = random.expovariate(rate) |
| |
| return max( |
| self.config.annotation_time_min, |
| min(self.config.annotation_time_max, time_val), |
| ) |
|
|
| |
| return self.config.annotation_time_mean |
|
|
| def get_fast_response_time(self) -> float: |
| """Generate a suspiciously fast response time. |
| |
| Used for testing quality control fast-response detection. |
| |
| Returns: |
| Fast response time in seconds (below threshold) |
| """ |
| return random.uniform(0.1, self.config.fast_response_threshold * 0.9) |
|
|
| def should_respond_fast(self, fast_response_rate: float) -> bool: |
| """Determine if this should be a fast response. |
| |
| Args: |
| fast_response_rate: Probability of fast response (0-1) |
| |
| Returns: |
| True if this should be a fast response |
| """ |
| return random.random() < fast_response_rate |
|
|
| def wait(self, duration: Optional[float] = None) -> float: |
| """Wait for the specified or generated duration. |
| |
| Args: |
| duration: Specific duration in seconds, or None to generate |
| |
| Returns: |
| The actual duration waited |
| """ |
| if duration is None: |
| duration = self.get_annotation_time() |
| time.sleep(duration) |
| return duration |
|
|
| def get_response_time(self, fast_response_rate: float = 0.0) -> float: |
| """Get response time, possibly fast for QC testing. |
| |
| Args: |
| fast_response_rate: Probability of suspiciously fast response |
| |
| Returns: |
| Response time in seconds |
| """ |
| if self.should_respond_fast(fast_response_rate): |
| return self.get_fast_response_time() |
| return self.get_annotation_time() |
|
|
|
|
| class NoWaitTimingModel(TimingModel): |
| """Timing model that records times but doesn't wait. |
| |
| Useful for fast testing where we want to track timing statistics |
| but don't want to actually delay execution. |
| """ |
|
|
| def wait(self, duration: Optional[float] = None) -> float: |
| """Record but don't actually wait. |
| |
| Args: |
| duration: Duration to record (or generate) |
| |
| Returns: |
| The duration that would have been waited |
| """ |
| if duration is None: |
| duration = self.get_annotation_time() |
| return duration |
|
|