#!/usr/bin/env bash # Xiaomi MiMo ASR wrapper for Hermes command STT provider. # # Usage (called by Hermes automatically via placeholders): # xiaomi-asr.sh [model] [language] # # Xiaomi ASR uses chat/completions API with input_audio format, # NOT the standard /v1/audio/transcriptions endpoint. # Only wav and mp3 are accepted — other formats are auto-converted # to 16kHz mono WAV via ffmpeg. set -euo pipefail INPUT="${1:?usage: xiaomi-asr.sh [model] [language]}" OUTPUT="${2:?usage: xiaomi-asr.sh [model] [language]}" MODEL="${3:-mimo-v2.5-asr}" LANGUAGE="${4:-zh}" API_KEY="${XIAOMI_API_KEY:?XIAOMI_API_KEY is not set}" BASE_URL="${XIAOMI_BASE_URL:-https://api.xiaomimimo.com/v1}" # Determine audio format from extension EXT="${INPUT##*.}" case "$EXT" in wav|WAV) FORMAT="wav" ;; mp3|MP3) FORMAT="mp3" ;; *) FORMAT="wav" ;; # Will convert to WAV via ffmpeg esac # If the file is not already wav/mp3, convert to wav via ffmpeg AUDIO_INPUT="$INPUT" if [ "$FORMAT" = "wav" ] && [[ ! "$EXT" =~ ^[Ww][Aa][Vv]$ ]] && [[ ! "$EXT" =~ ^[Mm][Pp]3$ ]]; then if command -v ffmpeg &>/dev/null; then AUDIO_INPUT=$(mktemp /tmp/hermes-asr-XXXXXX.wav) ffmpeg -y -i "$INPUT" -ar 16000 -ac 1 -f wav "$AUDIO_INPUT" 2>/dev/null trap 'rm -f "$AUDIO_INPUT"' EXIT else echo "ffmpeg is required to convert .$EXT to wav/mp3 for Xiaomi ASR" >&2 exit 1 fi fi # Build JSON payload and call API using Python to avoid arg-length limits RESPONSE=$(python - "$AUDIO_INPUT" "$MODEL" "$FORMAT" "$API_KEY" "$BASE_URL" << 'PYEOF' import base64, json, sys, urllib.request, urllib.error, ssl input_path = sys.argv[1] model = sys.argv[2] fmt = sys.argv[3] api_key = sys.argv[4] base_url = sys.argv[5] with open(input_path, "rb") as f: audio_b64 = base64.b64encode(f.read()).decode() payload = json.dumps({ "model": model, "messages": [{ "role": "user", "content": [ {"type": "input_audio", "input_audio": {"data": audio_b64, "format": fmt}}, ] }] }).encode() req = urllib.request.Request( f"{base_url}/chat/completions", data=payload, headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, ) ctx = ssl.create_default_context() try: resp = urllib.request.urlopen(req, context=ctx, timeout=300) print(resp.read().decode()) except urllib.error.HTTPError as e: body = e.read().decode() print(f"HTTP {e.code}: {body}", file=sys.stderr) sys.exit(1) PYEOF ) # Extract the transcribed text from the response TRANSCRIPT=$(python -c " import json, sys resp = json.loads(sys.stdin.read()) choices = resp.get('choices', []) if choices: msg = choices[0].get('message', {}) content = msg.get('content', '') print(content.strip()) else: err = resp.get('error', {}) print(err.get('message', 'No transcription result'), file=sys.stderr) sys.exit(1) " <<< "$RESPONSE") if [ -z "$TRANSCRIPT" ]; then echo "Xiaomi ASR returned empty transcript" >&2 exit 1 fi # Write the transcript to the output file echo "$TRANSCRIPT" > "$OUTPUT" echo "$TRANSCRIPT"