add contract.py
Browse files- contract.py +44 -0
contract.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Turn-taking benchmark submission interface. See README.md for usage.
|
| 2 |
+
|
| 3 |
+
Implement ONE track:
|
| 4 |
+
- DiscriminativeModel — you declare the floor per timestep; we run it streaming.
|
| 5 |
+
- GenerativeModel — you produce audio; we VAD it into the floor track.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from typing import Literal, Protocol, runtime_checkable
|
| 9 |
+
|
| 10 |
+
import numpy as np
|
| 11 |
+
from numpy.typing import NDArray
|
| 12 |
+
|
| 13 |
+
SAMPLE_RATE = 48_000 # benchmark feed rate: corpus + model input. (Generative output may be any rate.)
|
| 14 |
+
|
| 15 |
+
AudioChunk = NDArray[np.float32] # 1-D mono float32, shape (n_samples,), in [-1, 1]
|
| 16 |
+
# `subject` is the channel whose floor you predict; `other` is the conversation
|
| 17 |
+
# partner. Subject is a role, not a fixed speaker — the runner puts each speaker
|
| 18 |
+
# in the subject slot in turn.
|
| 19 |
+
FloorBit = Literal[0, 1] # 1 = subject holds the floor, 0 = subject does not
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@runtime_checkable
|
| 23 |
+
class DiscriminativeModel(Protocol):
|
| 24 |
+
input_sample_rate: int # runner resamples to this before every call
|
| 25 |
+
|
| 26 |
+
def reset(self) -> None: # clear ALL streaming state; called before each pass
|
| 27 |
+
... # over a conversation (once per subject slot)
|
| 28 |
+
|
| 29 |
+
def step(self, subject_audio: AudioChunk, other_audio: AudioChunk) -> FloorBit: ...
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
@runtime_checkable
|
| 33 |
+
class GenerativeModel(Protocol):
|
| 34 |
+
output_sample_rate: (
|
| 35 |
+
int # the rate your generate() output is at — any value; just report it
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
def reset(self) -> None: # clear ALL streaming state; called before each pass
|
| 39 |
+
... # over a conversation (once per subject slot)
|
| 40 |
+
|
| 41 |
+
def generate(self, subject_audio: AudioChunk) -> AudioChunk:
|
| 42 |
+
# Return your model's raw audio response covering the same DURATION as subject_audio.
|
| 43 |
+
# We assert len(output) / output_sample_rate == len(subject_audio) / SAMPLE_RATE.
|
| 44 |
+
...
|