08/Documentation
음성 입력 및 Web Push
작성기의 마이크, 에이전트가 입력을 대기할 때 보내는 알림
두 기능 모두 기본적으로 비활성화되어 있습니다. composer에서 마이크 버튼을 활성화하고 에이전트가 입력을 기다릴 때 트리거되는 브라우저 알림을 활성화할 수 있습니다.
음성 입력 (선택 사항)
작성기의 마이크 버튼, 그리고 설정의 핸즈프리 스위치입니다. 버튼을 탭하고 말하면 텍스트로 변환된 내용이 메시지 상자에 입력되어 확인 후 전송할 수 있습니다. 핸즈프리를 켜면 입력한 메시지와 동일한 보호된 응답 경로를 통해 자동으로 전송되며, 우회하지 않습니다.
상자가 비어 있는 동안에는 줄 끝의 둥근 버튼이 마이크로 바뀝니다; 첫 글자를 입력하면 다시 전송 버튼으로 바뀝니다. 메시지를 말로 입력하거나 직접 타이핑하므로, 입력란 너비를 두고 두 동작이 경쟁하지 않고 하나의 기본 동작만 유지됩니다.
collie stt setup을(를) 실행하기 전까지는 존재하지 않습니다. 버튼이 표시되지 않고, 오디오가 휴대폰 외부로 나가지 않으며, 자격 증명이 저장되지 않고, 하위 프로세스가 실행되지 않습니다. 비활성화된 것이 아니라 아예 존재하지 않는 상태입니다. 두 가지 제공자가 있습니다:
| 제공자 | 설명 |
|---|---|
openai-compatible | 공개 OpenAI API, 클라우드 Whisper 클론 또는 동일한 머신의 로컬 엔진으로, 외부 유출이 전혀 없는 선택지입니다(아래) 등 POST /audio/transcriptions 프로토콜을 지원하는 모든 엔드포인트입니다. |
codex | 이미 신뢰하는 codex 바이너리를 활용하여 수명이 짧은 토큰을 가져옵니다. 새 계정이나 새 키가 필요하지 않으며, yes을(를) 직접 입력해야 하는 동의 단계가 포함된 비공개, 미지원 엔드포인트를 사용합니다(아래). |
페어링이(가) CLI 작업인 이유와 마찬가지로 설정도 CLI 작업입니다. 자격 증명을 입력받는 영역이므로 호스트의 키보드에서 직접 처리해야 합니다. 웹 설정 폼은 제공되지 않습니다.
$ bin/collie stt setup
Which speech-to-text provider?
openai-compatible any endpoint that speaks POST /audio/transcriptions —
the public OpenAI API, or a local whisper.cpp / parakeet.cpp
server, which is the zero-egress choice and the one to prefer.
codex borrow your own `codex` sign-in. No new key, no new account —
and a private endpoint that may break without notice.
provider [openai-compatible]:
The API base, INCLUDING its version prefix — the provider appends /audio/transcriptions.
local http://127.0.0.1:8080/v1 (whisper.cpp / parakeet.cpp — nothing leaves the host)
cloud https://api.openai.com/v1 (room audio leaves this machine)
base URL: http://127.0.0.1:8080/v1
The model the endpoint understands. Empty takes Collie's default, gpt-transcribe.
model [gpt-transcribe]: whisper-1
API key [none]:
The language you speak, as a two-letter ISO-639-1 code — en, de, tr, ja.
LEAVE IT EMPTY to let the model detect it, which is what you want if you mix languages in one
sentence. Name one only if short clips keep coming back in a language you did not speak: a few
seconds of accented audio is too little for the model to detect from, and it guesses.
spoken language [auto-detect]: en
✓ speech-to-text configured — /home/you/.local/state/collie/stt.json (owner-only)
Live immediately — no restart needed. The bridge re-reads this file per request.
Check it end to end with `collie stt test`.위의 모든 질문에는 플래그(--provider · --url · --model · --key · --lang)가 있으므로 프로비저닝 실행 시 터미널 상호작용이 필요하지 않습니다. 키를 비워두는 것도 지원되는 모드입니다. 키가 없는 엔드포인트는 빈 헤더 대신 Authorization 헤더를 아예 포함하지 않고 호출합니다.
음성 언어 설정은 한 가지 오류 상황에서만 유용합니다. 기본값인 빈 상태로 두면 모델이 언어를 자동으로 감지하며, 이는 한 문장에 두 언어를 섞어 쓰는 사용자에게 적합합니다. 짧은 클립이 말하지 않은 언어로 계속 변환되는 경우에만 설정하십시오. 억양이 섞인 몇 초 분량의 오디오는 언어를 감지하기에 너무 짧아 모델이 잘못 추측할 수 있습니다. 2자리 코드 또는 Collie가 자동으로 축약하는 지역 태그(en-GB → en)를 사용할 수 있습니다. 이 기능은 openai-compatible 제공자에서만 작동합니다. codex 엔드포인트는 언어 설정을 받지 않으며, collie stt status 설정 시 이를 명확히 안내합니다.
녹음 시간이 길수록 허용 대기 시간도 길어집니다. 브라우저의 단일 클립 허용 시간은 고정된 수치가 아니라 해당 클립 크기에 비례합니다. 안정적인 256 kb/s 업링크를 가정하고 여기에 브릿지 자체의 제공자 대기 시간을 더하므로, 최대 8 MiB 크기에는 6분 조금 안 되는 시간이 허용됩니다. Collie가 녹음하도록 허용한 클립이라면 그만큼 기다릴 준비도 되어 있습니다. 업로드가 진행되는 동안 Collie는 폴링을 중지하고 연결 배너 상태를 악화시키지 않습니다. 휴대폰의 업링크를 오디오 파일이 점유하는 것은 장애가 아니며 장애로 보고되어서도 안 됩니다.
작동을 확인하셨습니까? stt test 명령은 스마트폰이 녹음할 수 있는 컨테이너마다 한 번씩, 생성된 0.2초의 무음을 실제 제공자에게 전송합니다.
$ bin/collie stt test
provider: openai-compatible (http://127.0.0.1:8080/v1, model whisper-1, language en)
sending: 0.2 s of generated silence as audio/wav (the setup probe) … ✓ 214 ms
transcript: (empty) — expected from silence, and the empty answer still proves the pipeline.
sending: 0.2 s of generated silence as audio/webm;codecs=opus (Chrome, Android, Firefox) … ✓ 198 ms
sending: 0.2 s of generated silence as audio/mp4 (Safari, iOS) … ✓ 190 ms빈 텍스트로 변환되면 성공한 것입니다. 무음은 빈 텍스트로 변환되므로 왕복 통신이 정상 작동하는지만 검증합니다. 실패하면 오류 유형(인증, 엔드포인트, 응답 형식)이 표시됩니다. 그런 다음 휴대폰에서 Collie를 새로고침하면 메시지 상자 옆에 마이크가 나타납니다. collie stt status 명령은 현재 설정된 값과 각 설정값의 출처(파일 또는 우선순위가 더 높은 환경 변수)을(를) 표시합니다. collie stt off 명령으로 stt.json을(를) 제거하면 버튼이 다시 사라지며, 두 작업 모두 재시작이 필요하지 않습니다.
컨테이너 지원 여부는 제공자마다 다릅니다
스마트폰은 WAV로 녹음하지 않습니다. Chrome, Android, Firefox에서는 WebM 컨테이너의 Opus로 녹음하고, Safari와 iOS에서는 MP4 컨테이너의 AAC로 녹음하여 해당 바이트를 그대로 전송합니다. WAV를 전사하는 제공자라도 이 두 형식에는 400으로 응답할 수 있으며, 이 경우 stt test 상태가 정상으로 보여도 모든 받아쓰기가 "refused" 오류와 함께 실패합니다. stt test 명령이 세 가지 클립을 모두 전송하는 이유가 바로 이것입니다. 거부 여부를 스마트폰이 아니라 설정 단계에서 감지합니다.
2026-09-01에 OpenRouter의 POST /v1/audio/transcriptions 모델을 대상으로 확인한 사례입니다.
| 형식 | mistralai/voxtral-small-24b-2507-stt | openai/whisper-large-v3-turbo |
|---|---|---|
| wav | 예 | 예 |
| ogg/opus | 예 | 예 |
| webm/opus | 미지원, 400 | 예 |
| mp4/m4a AAC | 미지원, 400 | 예 |
해결 방법은 키가 아니라 모델을 변경하는 것입니다. 동일한 OpenRouter 키가 네 가지 형식을 모두 지원하는 openai/whisper-large-v3-turbo 모델을 가리키도록 설정하십시오.
bin/collie stt setup --provider openai-compatible \
--url https://openrouter.ai/api/v1 --model openai/whisper-large-v3-turbo --key <key>이제 전사가 거부되면 업스트림 상태와 전송된 컨테이너 이름이 함께 표시되므로, 스마트폰의 오류 메시지에서 제공자가 어떤 형식을 거부했는지 확인할 수 있습니다. (#148, @drewbitt 님께 감사드립니다)
외부 유출 제로, 자체 엔진 지정
openai-compatible을(를) 사용해야 하는 이유: 로컬 기본 URL을 지정하면 실내 음성이 호스트 외부로 절대 유출되지 않습니다. OpenAI 호환 텍스트 변환 엔드포인트를 제공하는 두 가지 엔진은 whisper.cpp에 번들된 server과(와) mudler/parakeet.cpp(MIT)입니다. 각 가이드에 따라 빌드하거나 설치한 후 로프백에서 실행하고 --url을(를) 해당 주소로 지정하십시오.
bin/collie stt setup --provider openai-compatible --url http://127.0.0.1:8080/v1통합의 전부는 이것입니다. Collie는 어떤 엔진이 응답하든 관여하지 않습니다.
Mistral의 Voxtral은 자체 지원이 필요하지 않습니다이며, 이 규격을 따르는 다른 어떤 것도 마찬가지입니다. 이것이 접점의 목적입니다. vLLM은 /v1/audio/transcriptions에서 오픈 가중치 Voxtral 모델을 제공하므로, 로컬 모델은 다른 엔진과 동일한 --url입니다. 호스팅 모델은 Mistral 자체 베이스에서 동일한 요청을 사용합니다:
bin/collie stt setup --provider openai-compatible \
--url https://api.mistral.ai/v1 --model voxtral-mini-latest --key <key> --lang enVoxtral Mini Transcribe는 13개 언어를 지원하며 Collie가 이미 전송하는 동일한 ISO-639-1 language 필드를 사용합니다. 신뢰하기 전에 collie stt test 명령으로 확인하십시오. "OpenAI 호환"은 각 엔드포인트가 자체적으로 주장하는 내용일 뿐이며, 해당 verb는 이를 점검하기 위해 존재합니다.
codex 공급자: 수락하는 내용
녹음 파일이 사용자의 로그인으로 인증된 문서화되지 않고 지원되지 않는 ChatGPT 엔드포인트 엔드포인트로 전송되어 ChatGPT 계정에 속도 제한 및 차단 위험이 발생하고 예고 없이 중단될 수 있으므로, collie stt setup --provider codex 명령은 동의 블록을 출력하고 yes을(를) 입력할 때까지 대기합니다.
Collie는 해당 엔드포인트에 먼저 자체 이름으로 요청합니다. 본래의 식별자가 거부된 경우에만 Codex CLI의 헤더로 폴백하며, 이 폴백은 collie stt status 명령이 읽어주는 단어로 설정에 기록됩니다. Collie는 ~/.codex/auth.json을(를) 절대 읽거나 저장하지 않습니다. 이미 신뢰하고 있는 바이너리만이 이에 접근합니다.
위의 모든 결정에 대한 근거(두 번 거절된 이유, 변경된 점, 접점이 이렇게 구성된 이유)는 ADR 0029에 있습니다.
웹 푸시(선택 사항)
기본적으로 비활성화되어 있습니다. 설정에는 3단계가 필요합니다. 발신자 라이브러리(web-push)는 빌드 시 선택적 종속성으로 포함됩니다:
collie push-keys # 1. generate + write the VAPID keys
collie restart # 2. Collie reads them at start
# 3. on your phone: Settings → notificationspush-keys 명령은 키쌍을 생성하고 활성 .env에 파일 모드 600으로 COLLIE_VAPID_PUBLIC 및 COLLIE_VAPID_PRIVATE을(를) 작성합니다. 이는 바이너리 설치의 경우 ~/.config/collie/.env이고 Herdr 설치의 경우 Herdr의 플러그인 설정 디렉터리에 있는 것입니다.
RFC 8292 subject 클레임을 설정하려면 연락처 URI를 인수로 전달하십시오:
collie push-keys mailto:you@example.comHerdr로 관리되는 설치 환경에서는 두 단계 모두 액션(herdr plugin action invoke push-keys --plugin herdr.collie 및 restart)으로 제공됩니다. Herdr 액션은 위치 인수를 허용하지 않으므로, subject를 설정하려면 셸에서 명령을 직접 실행해야 합니다.
키 처리 세부 정보:
이 명령은 --force을(를) 전달하지 않는 한 기존 키를 덮어쓰지 않습니다. 키를 교체하면 현재의 모든 구독이 무효화되므로 모든 기기가 알림을 다시 받으려면 재구독해야 합니다. 기존 구성에서 subject 인수를 전달하면 연락처 주소만 업데이트되고 현재 키는 유지됩니다.
참고. 0.8.0 이전 Herdr 버전에서는 액션이 초기 플러그인 설치 중 캐시된 세트(ADR 0006)로 고정된 상태로 유지됩니다.herdr plugin install를 실행할 때까지push-keys및push-test액션이 표시되지 않습니다. 대신bash scripts/collie-ctl.sh push-keys를 직접 실행하십시오. 래퍼 스크립트가 명령을 바이너리로 직접 전달합니다.
구독된 모든 기기에서 전송 경로를 테스트합니다:
collie push-test # or: push-test "Title" "Body"전송에는 1~2초가 소요됩니다. 명령에서 푸시가 비활성화되었다고 보고하면 생성된 키를 로드하도록 서비스를 다시 시작하십시오. 구독된 기기가 없다고 보고하면 휴대폰 브라우저에서 3단계를 완료하십시오.
웹 푸시에는 보안 컨텍스트(HTTPS)가 필요합니다. 이는 tailscale serve(MagicDNS 인증서) 또는 TLS를 종단하는 외부 리버스 프록시(변형 C)에 의해 제공됩니다. 일반 HTTP 설정(COLLIE_SERVE_MODE=http)에는 보안 컨텍스트가 없으므로 브라우저가 설정의 구독 제어를 비활성화합니다.
Collie는 에이전트가 blocked 또는 done 상태로 전환될 때 알림을 보내며, 본문에 에이전트 메시지를 포함합니다. 알림을 선택하면 웹 UI에서 해당 에이전트로 바로 이동합니다.
홈 화면 재설치 및 서비스 워커 초기화로 인해 항상 HTTP 410이 반환되지 않고 새 엔드포인트가 생성되므로 시간이 지남에 따라 오래된 구독이 누적될 수 있습니다. Collie는 기기가 다시 등록할 때 레코드를 업데이트합니다. 저장된 엔드포인트를 직접 확인하고 삭제할 수 있습니다:
# one line per device: service, since, user agent, endpoint tail
bin/collie push list
bin/collie push forget <substring> # or: push forget --all