summaryrefslogtreecommitdiff
path: root/xiaomi-asr.sh
diff options
context:
space:
mode:
Diffstat (limited to 'xiaomi-asr.sh')
-rw-r--r--xiaomi-asr.sh107
1 files changed, 107 insertions, 0 deletions
diff --git a/xiaomi-asr.sh b/xiaomi-asr.sh
new file mode 100644
index 0000000..532233d
--- /dev/null
+++ b/xiaomi-asr.sh
@@ -0,0 +1,107 @@
+#!/usr/bin/env bash
+# Xiaomi MiMo ASR wrapper for Hermes command STT provider.
+#
+# Usage (called by Hermes automatically via placeholders):
+# xiaomi-asr.sh <input_audio> <output_txt> [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 <input> <output> [model] [language]}"
+OUTPUT="${2:?usage: xiaomi-asr.sh <input> <output> [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=$(python3 - "$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=$(python3 -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"