fix(realtime): 오디오 스트림의 contentType 을 AI 에 전달 - #192
Merged
Conversation
AI 의 `/internal/voice/stream` 은 `contentType` 쿼리로 STT 세션의 디코더를 고른다
(`provider.open_session(content_type=...)`). 그런데 RealTime 이 업스트림 URL 을 만들 때
`sessionId`·`messageId` 만 붙이고 있었다:
aiURL := fmt.Sprintf("%s?sessionId=%d&messageId=%d", h.AIBaseURL, sid, mid)
결과적으로 브라우저가 무엇을 보내든 AI 는 기본값 `audio/webm` 으로 가정한다. MediaRecorder 는
Safari 에서 `audio/mp4` 를 만들므로 그 경우 잘못된 디코더로 세션이 열린다. 아직 프론트가
RT3 를 쓰지 않아 드러나지 않았을 뿐, 연결하는 순간 webm 이 아닌 브라우저에서 깨진다.
브라우저의 `?contentType=` 을 그대로 전달한다. 다만 사용자 제어 값이므로:
- `"audio/webm;codecs=opus"` 같은 값에서 base MIME 만 뽑고 소문자로 정규화
- 화이트리스트(webm/ogg/mp4/mpeg/wav — Core 가 저장을 허용하는 것과 동일)를 통과한 값만 사용
- 그 외에는 기존 동작과 같은 `audio/webm` 으로 폴백
- URL 에 붙일 때 `url.QueryEscape` (슬래시가 이스케이프되지 않으면 AI 쪽 파싱이 어긋난다)
테스트 (10) — 코덱 파라미터 제거·대소문자·공백, 허용 목록 밖/빈 값의 기본값 폴백,
쿼리 주입 시도(`audio/webm&apiKey=...`)가 기본값으로 떨어지는지, URL 조립과 이스케이프.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
배경
RT3(실시간 스트리밍 음성)를 프론트에 연결하려고 양쪽 와이어 규약을 읽다가 발견했다.
AI 의
/internal/voice/stream은contentType쿼리로 STT 세션의 디코더를 고른다:그런데 RealTime 이 업스트림 URL 을 만들 때 그 값을 붙이지 않았다:
결과적으로 브라우저가 무엇을 보내든 AI 는 기본값
audio/webm으로 가정한다.MediaRecorder는 Safari 에서audio/mp4를 만들므로 그 경우 잘못된 디코더로 세션이 열린다.아직 프론트가 RT3 를 쓰지 않아 드러나지 않았을 뿐, 연결하는 순간 webm 이 아닌 브라우저에서 깨진다. 지금 고쳐 두면 나중에 배선할 때 이 함정을 밟지 않는다.
수정
브라우저의
?contentType=을 전달하되, 사용자 제어 값이므로 그대로 붙이지 않는다:"audio/webm;codecs=opus"에서 base MIME 만 뽑고 소문자 정규화audio/webm으로 폴백 (동작 회귀 없음)url.QueryEscape— 슬래시가 이스케이프되지 않으면 AI 쪽 쿼리 파싱이 어긋난다임의 문자열을 업스트림 URL 에 붙이지 않는 건 방금 고친 #191(셸 주입)과 같은 원칙이다.
테스트 (10)
코덱 파라미터 제거·대소문자·공백 처리, 허용 목록 밖/빈 값의 기본값 폴백, 쿼리 주입 시도(
audio/webm&apiKey=...)가 기본값으로 떨어지는지, URL 조립과 이스케이프.로컬에 Go 툴체인이 없어 CI 로 검증한다.
RT3 프론트 배선은 이번에 하지 않았다 — 이유
전송 클라이언트(WS 프로토콜)까지는 어렵지 않은데, 컴포저 통합에 라이브 검증이 필요한 지점이 있다:
useVoiceRecorder가 스트림과 MediaRecorder 를 소유한다. 스트리밍은 timeslice 기반 청크가 필요해 recorder 를 하나 더 붙이거나(이중 인코딩) 훅 구조를 바꿔야 한다stream-begin+ 토큰 발급(HTTP 2회)이 끝나야 소켓이 열린다. 그 사이 녹음을 미루면 첫 단어가 잘리고, 먼저 녹음하면 소켓 오픈 전 청크를 버퍼링·플러시해야 한다Idempotency-Key로 배치 업로드하면 같은 메시지에 이어붙는다(설계는 확인했다). 다만 이 경로는 실제 네트워크 단절로 시험해 봐야 한다전부 마이크와 실행 중인 스택이 있어야 확인 가능한 것들이라, 검증 없이 기존의 동작하는 배치 경로를 대체하는 건 위험하다고 판단했다. 팀에서 5분이면 확인할 수 있는 환경이니 함께 하는 편이 낫겠다.
영향 범위
contentType미지정 시 기존과 동일(audio/webm)