Source: https://ailoot.ai/models/en/qwen-audio-3-0-realtime-plus/
Canonical: https://ailoot.ai/models/en/qwen-audio-3-0-realtime-plus/
Language: en

# qwen-audio-3.0-realtime-plus

[Markdown](https://ailoot.ai/models/en/qwen-audio-3-0-realtime-plus/index.md) · [JSON](https://ailoot.ai/models/en/qwen-audio-3-0-realtime-plus/facts.json)

## [qwen-audio-3.0-realtime-plus](https://ailoot.ai/models/en/qwen-audio-3-0-realtime-plus/)

A real-time voice conversation model that accepts speech and text and responds with voice or text. Designed for a natural conversational pace and interaction with tools.

- [**Ailoot · Realtime voice** `GET /v1/realtime?model=qwen-audio-3.0-realtime-plus` `qwen-audio-3.0-realtime-plus`](https://ailoot.ai/models/en/qwen-audio-3-0-realtime-plus/#method-0)

About the model

This description covers the model’s capabilities. Available in Ailoot inputs, outputs and options are listed for the selected method below.

Qwen-Audio-3.0-Realtime-Plus is an Alibaba model for real-time voice conversation. It accepts speech and text, responds with audio and text, and supports a two-way conversation with natural turn-taking. Audio can be sent and received in chunks, allowing the interaction to develop during the conversation.

#### Knowing when a speaker has finished

Several ways to detect turn boundaries are provided: voice activity, semantic completion and explicit control. The manufacturer's mode is selected before the first audio is sent; changing it after a conversation has started requires a new connection. Support for this control depends on the selected method. The semantic mode considers pauses and fillers: a brief hesitation does not necessarily mean that the user has finished their thought. New user speech can interrupt a response being generated so that the conversation continues in light of the next turn. In semantic mode, recognition can be guided by a speech sample of the selected speaker to reduce distraction from other voices and background noise.

Instructions define the assistant's role and conversational style. Vocal delivery can reflect tone, pace and emotion; version 3.0 provides five system voices and voices based on a speech sample. The model supports 11 languages, including Russian, as well as the Chinese dialect variants specified by the manufacturer. In the manufacturer's API, the voice is specified in the first update of the session settings; subsequent attempts to supply a new voice in the same conversation are ignored. These settings require support from the selected access method.

#### Tools and conversation history

Function calling is supported. The model produces the requested action, the connected application executes it and returns the result to the conversation. This allows a conversation to be based on actual data and continued after an operation has been completed. A voice assistant can use tool results while retaining the context of the discussion. The manufacturer's capabilities also include built-in web search when provided by the selected method. Web search and function calls cannot both be enabled in the same session.

By default, the history contains 20 conversation turns, with a supported range of one to 50. Accumulated audio is limited to 300 seconds; older parts of the history are removed when the limits are exceeded. This differs from the length of an individual turn: the history determines which earlier material remains available as the dialogue continues.

#### Text context and audio

The formal context window is 40,960 tokens. The maximum input length is 16,384 tokens and the maximum output length is 8192. The same input and output limits are specified for reasoning mode, with a separate reasoning-chain limit of 1024 tokens.

Streaming audio uses mono, 16-bit PCM: 16 kHz for input and 24 kHz for output. Text and audio belong to the same conversation. Visual input and image output are not stated for this particular model; it specializes in speech, text and voice interaction.

**Ailoot · Realtime voice** `GET /v1/realtime?model=qwen-audio-3.0-realtime-plus` `qwen-audio-3.0-realtime-plus`

[`qwen-audio-3.0-realtime-plus`](https://ailoot.ai/models/en/#m=qwen-audio-3.0-realtime-plus&route=Core&caller=qwen-audio-3.0-realtime-plus&fmt=realtime)

Alibaba /`qwen-audio-3.0-realtime-plus`

### Overview

A real-time voice conversation model that accepts speech and text and responds with voice or text. Designed for a natural conversational pace and interaction with tools.

Accepted inputs

Text · Audio

Results

Text · Audio

### Available in Ailoot · inputs & methods

**Ailoot · Realtime voice** `GET /v1/realtime?model=qwen-audio-3.0-realtime-plus`

Input types

`text`, `audio`

Output types

`text`, `audio`

Output format

session events

| Field | Type | Requirement | Values & limits | Default |
| --- | --- | --- | --- | --- |
| Field`query.model` | Type`string` | RequirementRequired | Values & limits qwen-audio-3.0-realtime-plus | Default— |
| Field`session.input_audio_format` | Type`string` | RequirementOptional | Values & limits pcm16, pcm | Default— |
| Field`session.output_audio_format` | Type`string` | RequirementOptional | Values & limits pcm16, pcm | Default— |
| Field`session.instructions` | Type`string` | RequirementOptional | Values & limits Limit applies to: selected session policy | Default— |
| Field`session.max_history_turns` | Type`integer` | RequirementOptional | Values & limits Minimum: 1 Limit applies to: selected session policy | Default— |
| Field`input_audio_buffer.append.audio` | Type`base64_string` | RequirementRequired | Values & limits Input content: PCM16 mono; 16 kHz input, 24 kHz output; nonempty even byte length | Default— |

Input material combinations

**realtime\_text\_audio**

Method parameters and lifecycle

| Parameter | Contract |
| --- | --- |
| Parameter`material_types` | Contracttext, audio |
| Parameter`required` | Contractnull |
| Parameter`optional_controls` | Contractnull |
| Parameter`content_type` | ContractWebSocket |

Lifecycle: `exact retained published method`

Method shapes and IDs are exact; supplier capability is separate from admission.

### Pricing

#### Ailoot method rates

Input

$0.0008 */ 1K*

Output

$0.0064 */ 1K*

Input

$0.0048 *per minute of audio*

Output

$0.018 *per minute of output audio*

Ailoot reserves an estimated amount before a request. After the result, it settles the final charge from measured usage. Streaming settlement uses the final usage record. [How reservation and settlement work →](https://ailoot.ai/docs/en/billing/reserve/)

### Example requests

#### Ailoot · Realtime voice

Python WebSocket · textPython WebSocket · audio

```python
# Install the documented client once: python3 -m pip install websockets
import base64, json, os, time, wave
from pathlib import Path
from websockets.sync.client import connect

key = os.environ.get("AILOOT_API_KEY", "")
if not key.strip():
    raise SystemExit("Set your AILOOT_API_KEY before connecting")
file = Path(os.environ.get("AILOOT_TEXT_FILE", ""))
if not file.is_file(): raise SystemExit("Set AILOOT_TEXT_FILE to your actual UTF-8 material")
material = file.read_text(encoding="utf-8")
if not material.strip(): raise SystemExit("Text material must not be empty")
out = Path("realtime-results")
out.mkdir(exist_ok=True)
audio, texts, complete = {}, {}, []
current_response = None
pending = False
deadline = time.monotonic() + 570  # Local client bound; lower server/session limits still apply.

def receive(ws, log):
    global current_response, pending
    left = deadline - time.monotonic()
    if left <= 0:
        raise TimeoutError("Client session limit; preserve IDs and partial results, do not replay")
    event = json.loads(ws.recv(timeout=min(60, left)))
    log.write(json.dumps(event, ensure_ascii=False) + "\n")
    log.flush()
    kind = event.get("type")
    if kind == "error":
        raise RuntimeError(json.dumps(event, ensure_ascii=False))
    if kind == "response.created":
        current_response = event["response"]["id"]
    if kind in {"response.audio.delta", "response.output_audio.delta", "response.text.delta", "response.output_text.delta", "response.audio_transcript.delta", "response.output_audio_transcript.delta", "response.text.done", "response.audio_transcript.done"}:
        identity = (event["response_id"], event["item_id"], event["output_index"], event["content_index"])
        if kind in {"response.audio.delta", "response.output_audio.delta"}:
            raw = base64.b64decode(event["delta"], validate=True)
            if len(raw) % 2: raise ValueError("Output PCM16 chunk must have an even byte length")
            audio.setdefault(identity, bytearray()).extend(raw)
        elif kind.endswith(".done"):
            texts[identity] = event.get("text", event.get("transcript", ""))
        else:
            texts[identity] = texts.get(identity, "") + event["delta"]
            print(event["delta"], end="", flush=True)
    if kind == "response.done":
        response = event["response"]
        complete.append(response)
        if current_response is None: current_response = response["id"]
        if response["id"] == current_response: pending = False
    return event

def wait_for(ws, log, kind):
    while True:
        event = receive(ws, log)
        if event.get("type") == kind: return event

def send(ws, event):
    ws.send(json.dumps(event, ensure_ascii=False))

try:
    # One exact model query; no reconnect/retry loop and no provider credentials.
    with connect("wss://api.ailoot.ai/v1/realtime?model=qwen-audio-3.0-realtime-plus", additional_headers={"Authorization": "Bearer " + key}) as ws:
        with (out / "events.jsonl").open("w", encoding="utf-8") as log:
            wait_for(ws, log, "session.created")
            send(ws, {"type":"session.update", "session":{"modalities":["audio","text"], "input_audio_format":"pcm", "output_audio_format":"pcm", "turn_detection":None}})
            wait_for(ws, log, "session.updated")
            send(ws, {"type":"conversation.item.create", "item":{"type":"message", "role":"user", "content":[{"type":"input_text", "text":material}]}})
            send(ws, {"type":"response.create"})
            pending = True
            try:
                while pending: receive(ws, log)
            except KeyboardInterrupt:
                # Cancel the actual current inference, then preserve its returned status.
                send(ws, {"type":"response.cancel"})
                while pending: receive(ws, log)
    if not complete or complete[-1].get("status") != "completed":
        raise RuntimeError("Response did not complete normally; inspect saved events and status")
finally:
    manifest = {"responses":complete, "parts":[]}
    for number, identity in enumerate(dict.fromkeys(list(audio) + list(texts))):
        record = {"response_id":identity[0], "item_id":identity[1], "output_index":identity[2], "content_index":identity[3]}
        if identity in audio:
            raw = bytes(audio[identity])
            pcm_file = out / ("audio-" + str(number) + ".pcm")
            wav_file = out / ("audio-" + str(number) + ".wav")
            pcm_file.write_bytes(raw)
            with wave.open(str(wav_file), "wb") as wav:
                wav.setnchannels(1); wav.setsampwidth(2); wav.setframerate(24000); wav.writeframes(raw)
            record.update({"pcm":str(pcm_file), "wav":str(wav_file), "sample_rate_hz":24000})
        if identity in texts:
            text_file = out / ("text-" + str(number) + ".txt")
            text_file.write_text(texts[identity], encoding="utf-8")
            record["text"] = str(text_file)
        manifest["parts"].append(record)
    (out / "results.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8")
    print("\nSaved every received event and result in", out)
```

```python
# Install the documented client once: python3 -m pip install websockets
import base64, json, os, time, wave
from pathlib import Path
from websockets.sync.client import connect

key = os.environ.get("AILOOT_API_KEY", "")
if not key.strip():
    raise SystemExit("Set your AILOOT_API_KEY before connecting")
file = Path(os.environ.get("AILOOT_AUDIO_FILE", ""))
if not file.is_file(): raise SystemExit("Set AILOOT_AUDIO_FILE to your raw PCM16 mono 16-kHz recording")
material = file.read_bytes()
if not material or len(material) % 2 or material.startswith(b"RIFF"):
    raise SystemExit("Use nonempty signed little-endian PCM16 mono 16-kHz bytes, not a WAV header")
out = Path("realtime-results")
out.mkdir(exist_ok=True)
audio, texts, complete = {}, {}, []
current_response = None
pending = False
deadline = time.monotonic() + 570  # Local client bound; lower server/session limits still apply.

def receive(ws, log):
    global current_response, pending
    left = deadline - time.monotonic()
    if left <= 0:
        raise TimeoutError("Client session limit; preserve IDs and partial results, do not replay")
    event = json.loads(ws.recv(timeout=min(60, left)))
    log.write(json.dumps(event, ensure_ascii=False) + "\n")
    log.flush()
    kind = event.get("type")
    if kind == "error":
        raise RuntimeError(json.dumps(event, ensure_ascii=False))
    if kind == "response.created":
        current_response = event["response"]["id"]
    if kind in {"response.audio.delta", "response.output_audio.delta", "response.text.delta", "response.output_text.delta", "response.audio_transcript.delta", "response.output_audio_transcript.delta", "response.text.done", "response.audio_transcript.done"}:
        identity = (event["response_id"], event["item_id"], event["output_index"], event["content_index"])
        if kind in {"response.audio.delta", "response.output_audio.delta"}:
            raw = base64.b64decode(event["delta"], validate=True)
            if len(raw) % 2: raise ValueError("Output PCM16 chunk must have an even byte length")
            audio.setdefault(identity, bytearray()).extend(raw)
        elif kind.endswith(".done"):
            texts[identity] = event.get("text", event.get("transcript", ""))
        else:
            texts[identity] = texts.get(identity, "") + event["delta"]
            print(event["delta"], end="", flush=True)
    if kind == "response.done":
        response = event["response"]
        complete.append(response)
        if current_response is None: current_response = response["id"]
        if response["id"] == current_response: pending = False
    return event

def wait_for(ws, log, kind):
    while True:
        event = receive(ws, log)
        if event.get("type") == kind: return event

def send(ws, event):
    ws.send(json.dumps(event, ensure_ascii=False))

try:
    # One exact model query; no reconnect/retry loop and no provider credentials.
    with connect("wss://api.ailoot.ai/v1/realtime?model=qwen-audio-3.0-realtime-plus", additional_headers={"Authorization": "Bearer " + key}) as ws:
        with (out / "events.jsonl").open("w", encoding="utf-8") as log:
            wait_for(ws, log, "session.created")
            send(ws, {"type":"session.update", "session":{"modalities":["audio","text"], "input_audio_format":"pcm", "output_audio_format":"pcm", "turn_detection":None}})
            wait_for(ws, log, "session.updated")
            for offset in range(0, len(material), 640):
                send(ws, {"type":"input_audio_buffer.append", "audio":base64.b64encode(material[offset:offset+640]).decode("ascii")})
                time.sleep(0.02)
            send(ws, {"type":"input_audio_buffer.commit"})
            wait_for(ws, log, "input_audio_buffer.committed")
            send(ws, {"type":"response.create"})
            pending = True
            try:
                while pending: receive(ws, log)
            except KeyboardInterrupt:
                # Cancel the actual current inference, then preserve its returned status.
                send(ws, {"type":"response.cancel"})
                while pending: receive(ws, log)
    if not complete or complete[-1].get("status") != "completed":
        raise RuntimeError("Response did not complete normally; inspect saved events and status")
finally:
    manifest = {"responses":complete, "parts":[]}
    for number, identity in enumerate(dict.fromkeys(list(audio) + list(texts))):
        record = {"response_id":identity[0], "item_id":identity[1], "output_index":identity[2], "content_index":identity[3]}
        if identity in audio:
            raw = bytes(audio[identity])
            pcm_file = out / ("audio-" + str(number) + ".pcm")
            wav_file = out / ("audio-" + str(number) + ".wav")
            pcm_file.write_bytes(raw)
            with wave.open(str(wav_file), "wb") as wav:
                wav.setnchannels(1); wav.setsampwidth(2); wav.setframerate(24000); wav.writeframes(raw)
            record.update({"pcm":str(pcm_file), "wav":str(wav_file), "sample_rate_hz":24000})
        if identity in texts:
            text_file = out / ("text-" + str(number) + ".txt")
            text_file.write_text(texts[identity], encoding="utf-8")
            record["text"] = str(text_file)
        manifest["parts"].append(record)
    (out / "results.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8")
    print("\nSaved every received event and result in", out)
```

### Receive and save results

#### Ailoot · Realtime voice

session events

##### Keep all completed results

Keep every complete returned response, file and content part.

Assemble every returned audio delta and text/event result for each actual response ID.

##### If the original response needs checking

Keepactualsession/response/eventIDs andreceipts.

Errors and next steps

| Response | Condition | Next step |
| --- | --- | --- |
| Response426 · `invalid_request_error` | ConditionNo WebSocket upgrade | Next stepUse a WebSocket client with Upgrade: websocket. |

### Aliases

#### Model aliases

**Ailoot** [`qwen-audio-3.0-realtime-plus`](https://ailoot.ai/models/en/#m=qwen-audio-3.0-realtime-plus&route=Core&caller=qwen-audio-3.0-realtime-plus&fmt=realtime)

#### Method path aliases

Use the method paths shown above.

#### Related caller IDs

[`qwen-audio-3.0-realtime-plus`](https://ailoot.ai/models/en/#m=qwen-audio-3.0-realtime-plus&route=Core&caller=qwen-audio-3.0-realtime-plus&fmt=realtime)

FAQ · qwen-audio-3.0-realtime-plus

What inputs and results are available?

`GET /v1/realtime?model=qwen-audio-3.0-realtime-plus` · `qwen-audio-3.0-realtime-plus`: `text`, `audio` → `text`, `audio`

What limits apply?

`GET /v1/realtime?model=qwen-audio-3.0-realtime-plus` · `qwen-audio-3.0-realtime-plus`: `session.max_history_turns.minimum`: `1`

Connect Ailoot to Claude Code, OpenCode, Hermes or your own app.

## The company and its project portfolio

The company and its project portfolio: millions of customers; monthly sales in the billions.

## Buy tokens at a discount.

Subscription discounts for regular use. Promo codes offer up to 50% off token purchases.

Subscription

Choose a discounted plan for your regular requests.

Promo code: up to 50% off

Enter your code at checkout. The price updates to reflect the current promotion.

Spending in your history

The model, tokens and amount are recorded for each request.

[Choose a subscription](https://ailoot.ai/docs/en/billing/subscriptions/) · [View model prices](https://ailoot.ai/models/en/)

## Connect the API to a product you sell.

Use Ailoot in SaaS, apps and bots under its official service offer and licence agreement. Request a reseller licence for resale.

Commercial application

Add text, image, video and audio generation to paid features in your service.

Reseller licence

Send a description of your project and how you provide access. Request written terms for use and resale.

Documents on the website

The service offer, licence agreement and data processing policy are available before you connect.

[Request a reseller licence](mailto:support@ailoot.ai) · [Service offer and licence agreement](https://ailoot.ai/legal/offer?lang=en)

## Ailoot · The company and its project portfolio





Ailoot combines established operating experience, a large client base, stable service and ongoing KPI control.





The company and its project portfolio: millions of customers; monthly sales in the billions.





Ailoot brings that operating experience to API access for AI features in customer products, agents, applications and automations.
