Files
auto_agent/llm_server/test_asr.py
T

132 lines
4.7 KiB
Python

"""
Test script for Qwen3-Omni-30B-A3B-Captioner via vLLM Chat Completions API.
Note:
This model is an audio captioner, not a pure ASR. It generates
detailed descriptions of audio content (speech, environment, music, etc.)
without any text prompt. Only the audio file is sent.
It is recommended to keep audio length <= 30 seconds for best detail.
"""
import requests
import argparse
import os
import sys
import base64
import mimetypes
DEFAULT_URL = "http://127.0.0.1:8901/v1/chat/completions"
# ----------------------------------------------------------------------
# Optional: check audio duration (requires soundfile)
# ----------------------------------------------------------------------
def get_audio_duration_sec(file_path: str) -> float:
"""Return audio duration in seconds, or -1.0 if unable to read."""
try:
import soundfile as sf
info = sf.info(file_path)
return info.duration
except Exception:
try:
# Fallback using librosa (if available)
import librosa
return librosa.get_duration(path=file_path)
except Exception:
return -1.0
# ----------------------------------------------------------------------
def file_to_url(file_path: str, use_base64: bool = False) -> str:
"""Convert local file path to a URL accepted by vLLM.
When the server is remote, use base64 data URL instead of file://
since the remote server cannot access local filesystem paths.
"""
if use_base64:
mime_type, _ = mimetypes.guess_type(file_path)
if mime_type is None:
mime_type = "audio/wav"
with open(file_path, "rb") as f:
b64_data = base64.b64encode(f.read()).decode("utf-8")
return f"data:{mime_type};base64,{b64_data}"
else:
abs_path = os.path.abspath(file_path)
return "file://" + abs_path.replace("\\", "/")
def caption_audio(audio_path: str, server_url: str = DEFAULT_URL, use_base64: bool = False) -> str:
"""
Send audio to the Qwen3-Omni-30B-A3B-Captioner and return its description.
"""
if not os.path.exists(audio_path):
raise FileNotFoundError(f"Audio file not found: {audio_path}")
# Check duration (warning only, not blocking)
duration = get_audio_duration_sec(audio_path)
if duration > 30:
print(
f"⚠️ Audio length is {duration:.1f}s (>30s). "
"The model may lose fine detail. Consider trimming to ≤30s.",
file=sys.stderr
)
audio_url = file_to_url(audio_path, use_base64=use_base64)
# The model expects NO text prompt. Only the audio input is required.
payload = {
"messages": [
{
"role": "user",
"content": [
{"type": "audio_url", "audio_url": {"url": audio_url}}
]
}
]
}
headers = {"Content-Type": "application/json"}
try:
resp = requests.post(server_url, headers=headers, json=payload, timeout=120)
resp.raise_for_status()
result = resp.json()
# Extract assistant reply
text = result["choices"][0]["message"]["content"]
return text.strip()
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}", file=sys.stderr)
if hasattr(e, 'response') and e.response is not None:
print(f"Server response: {e.response.text}", file=sys.stderr)
raise
def main():
parser = argparse.ArgumentParser(
description="Test Qwen3-Omni-Captioner (audio captioning, NOT pure ASR)"
)
parser.add_argument("--audio", "-a", required=True,
help="Path to input audio file (mp3, wav, etc.)")
parser.add_argument("--url", "-u", default=DEFAULT_URL,
help="vLLM chat completions endpoint")
parser.add_argument("--output", "-o", default=None,
help="Output text file (if not provided, print to stdout)")
parser.add_argument("--verbose", "-v", action="store_true",
help="Print detailed request/response info")
parser.add_argument("--base64", "-b", action="store_true",
help="Send audio as base64 data URL (use when server is remote)")
args = parser.parse_args()
if args.verbose:
print(f"Audio file: {args.audio}")
print(f"Server URL: {args.url}")
print(f"Base64 mode: {args.base64}")
caption_text = caption_audio(args.audio, args.url, use_base64=args.base64)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(caption_text)
print(f"Caption saved to: {args.output}")
else:
print(caption_text)
if __name__ == "__main__":
main()