회기 무발화 0턴 분리, 자기예측 락 불변식 및 TDD 회귀 검증 완료
Some checks failed
API contract / OpenAPI type drift (push) Failing after 3m27s

This commit is contained in:
Yun Chan 2026-09-08 23:28:06 +09:00
parent a479db7a5a
commit a0311c5957
100 changed files with 4884 additions and 11210 deletions

View file

@ -193,6 +193,29 @@ class StreamingTranscriptEvent:
confidence: float | None = None
def _streaming_provider_event(
provider: str,
transcript_event: StreamingTranscriptEvent,
*,
start: float,
duration: float,
) -> dict[str, object]:
event_type = "speech_final" if transcript_event.speech_final else (
"speech_end" if transcript_event.final else "voice_activity"
)
provider_event: dict[str, object] = {
"type": event_type,
"provider": provider,
"source": "streaming_stt",
"start_ms": round(start * 1000),
"duration_ms": round(duration * 1000),
"is_final": transcript_event.final,
}
if transcript_event.confidence is not None:
provider_event["confidence"] = transcript_event.confidence
return provider_event
class DeepgramStreamingSession:
"""One Deepgram Listen WebSocket, scoped to exactly one learner utterance."""
@ -364,29 +387,21 @@ class DeepgramStreamingSession:
if not display_text and not speech_final:
return
event_type = "speech_final" if speech_final else (
"speech_end" if is_final else "voice_activity"
transcript_event = StreamingTranscriptEvent(
text=display_text,
final=is_final,
speech_final=speech_final,
confidence=confidence,
)
provider_event = _streaming_provider_event(
"deepgram",
transcript_event,
start=start,
duration=duration,
)
provider_event: dict[str, object] = {
"type": event_type,
"provider": "deepgram",
"source": "streaming_stt",
"start_ms": round(start * 1000),
"duration_ms": round(duration * 1000),
"is_final": is_final,
}
if confidence is not None:
provider_event["confidence"] = confidence
if is_final or speech_final:
self._provider_events.append(provider_event)
await self._on_event(
StreamingTranscriptEvent(
text=display_text,
final=is_final,
speech_final=speech_final,
confidence=confidence,
)
)
await self._on_event(transcript_event)
def _consume_final_words(self, value: object) -> None:
if not isinstance(value, list):
@ -620,29 +635,21 @@ class LocalWhisperStreamingSession:
if not display_text and not speech_final:
return
event_type = "speech_final" if speech_final else (
"speech_end" if is_final else "voice_activity"
transcript_event = StreamingTranscriptEvent(
text=display_text,
final=is_final,
speech_final=speech_final,
confidence=confidence,
)
provider_event = _streaming_provider_event(
"local_whisper",
transcript_event,
start=start,
duration=duration,
)
provider_event: dict[str, object] = {
"type": event_type,
"provider": "local_whisper",
"source": "streaming_stt",
"start_ms": round(start * 1000),
"duration_ms": round(duration * 1000),
"is_final": is_final,
}
if confidence is not None:
provider_event["confidence"] = confidence
if is_final or speech_final:
self._provider_events.append(provider_event)
await self._on_event(
StreamingTranscriptEvent(
text=display_text,
final=is_final,
speech_final=speech_final,
confidence=confidence,
)
)
await self._on_event(transcript_event)
def _consume_final_words(self, value: object, *, offset: float) -> None:
if not isinstance(value, list):