diff --git a/README.md b/README.md index a612084..aa82944 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,11 @@ STT → 두뇌 → TTS부터 완성**하는 단계다. - 실행 위치: 이 리눅스 호스트(.9, RTX 5050 8GB / ffmpeg / node v22 있음) -- 현재 상태: 음성 루프 뼈대 동작 — 파이프라인이 **눈 없이(화면공유 없이)** 돌아간다 - (`python -m wsai --voice`). STT/TTS/두뇌는 아직 mock이며, 실제 엔진 연결이 다음 목표. +- 현재 상태: 음성 루프 **실엔진 동작** — GPU STT(faster-whisper), Claude 두뇌(Haiku), + GPU TTS(MeloTTS, 감정 톤 반영)가 모두 붙었고 디스코드 봇과 왕복하는 **voice-server** + (`python -m wsai --voice-server`)가 실서비스로 돈다. 상태 대시보드(:8787)에서 봇 정보· + 서버/음성채널 선택·참여자·발화자·로그·프롬프트·화이트/블랙리스트를 실시간 제어한다(9장). + 남은 것은 실제 사람 발화로 오디오 왕복을 눈으로 확인하는 최종 라이브 검증뿐. - 보류 중: 화면공유 **비디오** 수신(눈). 단, STT 입력은 **디스코드 보이스로 유저 음성을 수신**하므로 보이스 접속 자체는 지금도 쓴다(비디오만 미룸). 이 보이스 접속은 **공식 Discord 봇**(`dave/bot.mjs`, discord.js + @discordjs/voice)으로 하며 DAVE/MLS @@ -212,3 +215,52 @@ ffmpeg 디코드 → 프레임. DAVE는 `@snazzah/davey`(Rust NAPI), 수신은 ` - **M3** faster-whisper STT(부분전사+VAD) → **M4** Claude OAuth(Haiku) 두뇌 - **M5** 한국어 TTS 첫 구절 청크를 보이스로 송신(DAVE 암호화) + barge-in - **M6** 통합 + 워밍업 + .9 GPU 도커 이미지화, 채널 라이브 테스트 + +--- + +## 9. 운영 음성 서버 + 대시보드 (구현됨) + +디스코드 봇이 발화 wav를 `POST /api/voice-turn`으로 올리면, voice-server가 GPU STT → +Claude 두뇌 → GPU TTS를 돌려 응답 wav를 돌려주고 봇이 채널에 재생한다. 같은 서버가 +:8787에 상태 대시보드(단일 HTML, 외부 자산 없음)를 띄운다. + +```bash +.venv/bin/python -m wsai --voice-server --host 0.0.0.0 --port 8787 # 실서비스(wsai-voice.service) +``` + +### 감정 TTS (대괄호 태그) +- 두뇌가 답변에 `[감정]` 태그를 넣으면 그 태그는 **읽지 않고** 뒤 문장의 피치·속도를 바꿔 + 감정을 표현한다. 답변 중간에 감정이 바뀌면 그 지점부터 톤이 바뀐다. + 예: `[속상함] 정말 힘들었겠다. [힘차게] 하지만 넌 할 수 있어!` +- 감정 어휘는 Azure Neural TTS speaking styles + Ekman 기본감정을 한국어로 매핑 + (`wsai/backends/emotion.py`). 감정 단어가 아닌 대괄호(예: `[1번]`)는 내용을 그대로 읽는다. +- 음성이 아닌 잡음으로 판단되면 답변을 `[잡음]`으로 두고 아무것도 재생하지 않는다. +- 구현: 세그먼트별 합성 후 librosa 피치 시프트로 이어붙임(`melo_worker.py`), 시작 시 예열. + +### 대시보드 기능 +- **봇 프롬프트 실시간 수정** — 상단 "📝 프롬프트" 팝업에서 현재 시스템 프롬프트를 보고 + 수정→저장하면 다음 답변부터 즉시 반영(빈칸 저장 시 기본값 복원). `prompt_store` 영속화. +- **봇 제어 바** — 봇 정보/연결 상태, 참여 가능 서버 선택(상단 "없음"), 선택 서버의 음성채널 + 선택(상단 "없음"), 현재 음성채널 참여자(말하는 사람 🔊). +- **화이트/블랙리스트** — 팝업에서 서버의 유저/역할을 검색해 추가/제거. 화이트리스트가 있으면 + 그 대상만 청취(비어있으면 전체), 블랙리스트는 제외. 봇이 발화자 SSRC→유저/역할로 필터 + (`dave/filter.mjs`의 `isAllowed`). +- **대화 카드** — 들음 / 생각(감정 톤 계획) / 답변 3분할, 발화자(🗣) 표시, 단계별·총 소요시간. +- **로그 패널** — 하단 고정 VSCode 터미널식, 열고닫기, 시작부터 기록. 텍스트/레벨 검색, + 전체·라인별 삭제/수정(`/api/logs/{clear,delete,edit}`). + +### 대시보드 ↔ 봇 제어 채널 +봇은 아웃바운드 HTTP만 쓴다. 봇이 상태(정체성·서버·음성채널·참여자·역할·멤버)를 +`POST /api/bot/report`로 올리면, 응답에 대시보드가 쌓아둔 **명령**(채널 join/leave)과 +**청취 필터**가 실려 온다. 서버/채널 선택은 `POST /api/bot/select`, 필터는 +`POST /api/bot/lists`로 저장한다(`wsai/bot_control.py`). + +### 관련 파일 +| 경로 | 역할 | +|------|------| +| `wsai/dashboard.py` | voice-turn 엔드포인트 + 상태 대시보드(HTML/JS) + 모든 API | +| `wsai/monitor.py` | 턴/스텝/이벤트 텔레메트리 (thought·speaker·이벤트 id 포함) | +| `wsai/backends/emotion.py` | `[감정]` 태그 파싱 + 감정→피치/속도 매핑 | +| `wsai/prompt_store.py` | 실시간 편집되는 시스템 프롬프트 영속화 | +| `wsai/bot_control.py` | 대시보드↔봇 제어 플레인(상태·명령·화이트/블랙리스트) | +| `dave/filter.mjs` | 청취 화이트/블랙리스트 판정(`isAllowed`, 순수 함수) |