ModelSiteAPI

Qwen Models

Scenario-based examples for qwen3.7-max / qwen3.7-plus / qwen3.8-max / qwen3-vl-plus: thinking, tool calls, JSON output, web search, image and video understanding, caching

Qwen models are called through /v1/chat/completions or /v1/messages. The platform passes the request body through: Qwen-specific parameters (enable_thinking, thinking_budget, top_k, enable_search and so on) are forwarded unchanged and the upstream model decides whether they take effect. This page is organised by scenario, following Alibaba Cloud Model Studio's official docs (deep thinking, web search, vision, structured output, function calling, context caching and so on). What is supported is decided by the upstream we actually connect to, not by the official site.

Every scenario carries an evidence label:

  • Tested (live test account, 2026-10-19): called for real on production; responses come from real output (id and timestamps replaced with placeholders).
  • Per official docs (not tested): checked against the official documentation only, no real request.

Supported models

Model idDescriptionContext windowMax outputCapability tags
qwen3.8-maxQwen3.8 flagship, text + image / video understanding1,000,000131,072chat, streaming, vision, tools, thinking, cache
qwen3.7-maxQwen3.7 flagship, text1,000,000131,072chat, streaming, tools, thinking, cache
qwen3.7-plusQwen3.7 balanced, text + image / video understanding1,000,000131,072chat, streaming, vision, tools, thinking, cache
qwen3-vl-plusQwen3-VL vision understanding262,14432,768chat, streaming, vision, tools, thinking, cache

Capability tags are whatever GET /v1/models returns. qwen3.7-max has no vision tag and cannot take images (it errors in testing, see Image and video input).

The platform never switches thinking on or off for you. It does not inject enable_thinking, thinking_budget, reasoning_effort or temperature by default. If you omit them the upstream default applies: the official docs say qwen3.8-max, qwen3.7-max and qwen3.7-plus think by default; the default for qwen3-vl-plus is not stated. Pass enable_thinking explicitly when you need deterministic behaviour.

Control Qwen thinking with enable_thinking, not thinking. In both the OpenAI and Anthropic formats use the top-level enable_thinking / thinking_budget (in extra_body for the Python SDK). A thinking field you send is forwarded unchanged (except adaptive, which is not forwarded) but is not converted into enable_thinking, thinking_budget or reasoning_effort; the upstream of qwen3.7-plus validates thinking.type (only enabled / disabled / auto), and the other models are unverified, so do not depend on it.

Scenario overview

ScenarioOfficial docs cover itOur statusExample
Deep thinking (enable_thinking switch, reading the reasoning)YesSupportedThinking mode
Thinking budget thinking_budget, effort reasoning_effort, preserve_thinkingYesPartly supported: per official docs, not testedNotes in Thinking mode
StreamingYesPartly supported: same pipeline, not tested on Qwen specificallyStreaming
Function calling (including parallel calls)YesSupportedTool calls
Streamed tool-call arguments (tool_stream)YesPartly supported: per official docs, not testedNotes in Tool calls
Structured output: json_objectYesSupportedJSON output
Structured output: json_schemaYes (some models)Supported (tested on qwen3.7-max; qwen3-vl-plus is not on the official list)JSON Schema
Web search (enable_search)YesPartly supported: basic search tested; sources, strategies and other options not tested; the official docs say Chat Completions does not return search sourcesWeb search
Image understanding (single image)YesPartly supported: qwen3-vl-plus tested; qwen3.8-max and qwen3.7-plus per official docs (not tested); qwen3.7-max not supportedImage and video input
Multi-image, video understanding, OCR, grounding, document parsingYesPartly supported: per official docs, not testedImage and video input
Context caching: implicitYesSupported (automatic, no parameter)Context caching
Context caching: explicit (cache_control)YesPartly supported: depends on an update that is not released yet, currently has no effect in productionContext caching
Anthropic-format callsYes (official Anthropic compatibility)Partly supported: same pipeline, not tested on Qwen specificallyAnthropic format
tool_choice: "required"Official docs say Qwen does not support itNot supportedNone
Named function in thinking modeOfficial docs say not supportedNot supported; set enable_thinking: false firstSee Tool calls
Long-document Q&A by file id (qwen-long)Yes (separate model)Not integrated: the platform has no file upload endpoint and no such modelNone
Role-play dedicated modelsYes (separate models)Not integratedNone
BatchYesNot supported: the platform offers no batch processingNone
Image input on qwen3.7-maxNo (text model)Not supported: returns 400 in testingSee Image and video input

Thinking mode

Evidence: tested (live test account, 2026-10-19), qwen3.8-max (on) and qwen3.7-plus (off).

When to use: turn thinking on for reasoning, code and maths that need several steps; turn it off for plain chat to save latency and tokens. enable_thinking is not an OpenAI standard field: put it in extra_body with the Python SDK, at the top level of the body for raw HTTP.

Request

from openai import OpenAI

client = OpenAI(base_url="https://api.modelsite.ai/v1", api_key="sk-ms-xxxxxxxx")

# Thinking on
resp = client.chat.completions.create(
    model="qwen3.8-max",
    messages=[{"role": "user", "content": "9.11 和 9.9 哪个大?一句话回答。"}],
    max_tokens=1500,
    extra_body={"enable_thinking": True},
)
msg = resp.choices[0].message
print(getattr(msg, "reasoning_content", None))  # the reasoning
print(msg.content)                              # the final answer

# Disable thinking
resp = client.chat.completions.create(
    model="qwen3.7-plus",
    messages=[{"role": "user", "content": "用一句话解释量子纠缠"}],
    max_tokens=300,
    extra_body={"enable_thinking": False},
)

Response (thinking on, qwen3.8-max): the reasoning is in reasoning_content, the answer in content, thinking tokens in usage.completion_tokens_details.reasoning_tokens.

{
  "id": "chatcmpl-xxxx",
  "object": "chat.completion",
  "created": 1760000000,
  "model": "qwen3.8-max",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "9.9 更大。",
        "refusal": null,
        "reasoning_content": "We need answer user's simple Chinese question: \"9.11 和 9.9 哪个大?一句话回答。\" Need answer one sentence. Need be careful: numeric comparison 9.11 vs 9.9. 9.9 = 9.90, so 9.9 larger than 9.11. Need final one sentence in Chinese."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 78,
    "completion_tokens": 90,
    "total_tokens": 168,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "text_tokens": 78
    },
    "completion_tokens_details": {
      "reasoning_tokens": 81,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0,
      "text_tokens": 90
    }
  }
}

Response (thinking off, qwen3.7-plus): no reasoning_content, reasoning_tokens is 0.

{
  "id": "chatcmpl-xxxx",
  "object": "chat.completion",
  "created": 1760000000,
  "model": "qwen3.7-plus",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "量子纠缠是指两个或多个粒子形成一种特殊的关联状态,使得无论它们相距多远,对其中一个粒子的测量会瞬间决定另一个粒子的状态。",
        "refusal": null
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 17,
    "completion_tokens": 31,
    "total_tokens": 48,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "text_tokens": 17
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  }
}

Notes

  • thinking_budget (per official docs, not tested): an integer, the maximum number of tokens the thinking part may use, after which the model answers directly; officially supported on Qwen3.8 / 3.7 / 3.6 / 3.5 / Qwen3-VL, default is the model's maximum chain-of-thought length; put it in extra_body.
  • reasoning_effort (per official docs, not tested): officially listed only for the qwen3.8 family, values xhigh (default), medium, low, with none turning thinking off; it cannot be set together with thinking_budget. qwen3.7-max, qwen3.7-plus and qwen3-vl-plus are not listed for this parameter, do not depend on it.
  • preserve_thinking (per official docs, not tested): boolean, default false; when true the reasoning_content of assistant messages in history is also used as input, the official docs require passing it back unchanged, and the history thinking counts as input tokens and is billed. The platform forwards reasoning_content in history to the upstream; your client must put the previous round's reasoning back into the history itself.
  • Sampling: temperature ranges over [0, 2) and its default depends on the model and on thinking; the official advice is to set only one of temperature and top_p; top_k officially defaults to 20, is not an OpenAI field, put it in extra_body; the platform does not range-check, so out-of-range values go to the upstream unchanged.
  • max_completion_tokens is the total output cap, covering thinking and the answer, and is recommended for thinking models; qwen3-vl-plus is not in its official support list, so use max_tokens; when both are sent the platform forwards only max_completion_tokens.

Streaming

Evidence: per official docs (not tested); the streaming pipeline matches the real responses on the DeepSeek and GLM pages.

When to use: show output as it is generated; especially recommended for thinking models (thinking takes long and non-streaming calls can time out). Pass stream: true; reasoning arrives in delta.reasoning_content, the answer in delta.content.

Request

from openai import OpenAI

client = OpenAI(base_url="https://api.modelsite.ai/v1", api_key="sk-ms-xxxxxxxx")

stream = client.chat.completions.create(
    model="qwen3.8-max",
    messages=[{"role": "user", "content": "数到五,用逗号分隔。"}],
    max_tokens=1000,
    stream=True,
    extra_body={"enable_thinking": True},
)
reasoning, content = "", ""
for chunk in stream:
    if not chunk.choices:
        continue
    d = chunk.choices[0].delta
    if getattr(d, "reasoning_content", None):
        reasoning += d.reasoning_content
    elif d.content:
        content += d.content
print(content)

Response: the SSE chunk structure is the same as for other models (see the streaming response on the DeepSeek page), with usage at the end.

Notes

  • If stream_options is not set the gateway adds include_usage.
  • Official advice: thinking takes long, so for long prompts stream the response or set the client timeout to 180 seconds or more.

Tool calls

Evidence: tested (live test account, 2026-10-19), qwen3.7-plus, first round only (the model returning tool calls); the second round that sends tool results back is per official docs (not tested).

When to use: let the model decide to call functions you define; with parallel_tool_calls: true it returns several independent calls at once. The model does not run functions: your code runs them and puts each result back as a role: "tool" message (with the matching tool_call_id) before the next request.

First request

from openai import OpenAI

client = OpenAI(base_url="https://api.modelsite.ai/v1", api_key="sk-ms-xxxxxxxx")

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get weather of a location, the user should supply a location first.",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "The city and state, e.g. San Francisco, CA"}},
            "required": ["location"],
        },
    },
}]
messages = [{"role": "user", "content": "查询北京和上海现在的天气。"}]
resp = client.chat.completions.create(
    model="qwen3.7-plus", messages=messages, tools=tools,
    parallel_tool_calls=True, max_tokens=500,
    extra_body={"enable_thinking": False},
)
msg = resp.choices[0].message
messages.append(msg)  # put the assistant message with tool_calls back into history

First response: two tool_calls at once (Beijing and Shanghai).

{
  "id": "chatcmpl-xxxx",
  "object": "chat.completion",
  "created": 1760000000,
  "model": "qwen3.7-plus",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "我来为您查询北京和上海的天气。\n\n",
        "refusal": null,
        "tool_calls": [
          {
            "id": "call_xxxx",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"location\": \"北京\"}"
            }
          },
          {
            "id": "call_xxxx",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"location\": \"上海\"}"
            },
            "index": 1
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ],
  "usage": {
    "prompt_tokens": 300,
    "completion_tokens": 60,
    "total_tokens": 360,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "text_tokens": 300
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  }
}

Second round (per official docs, not tested): append one role: "tool" message per tool_calls entry with matching tool_call_id, then request again; the official docs advise removing tool_choice when summarising tool output, otherwise the API returns tool calls again.

for call in msg.tool_calls:
    messages.append({"role": "tool", "tool_call_id": call.id, "content": "Sunny, 26°C"})
resp = client.chat.completions.create(
    model="qwen3.7-plus", messages=messages, tools=tools, max_tokens=500,
    extra_body={"enable_thinking": False},
)
print(resp.choices[0].message.content)

Notes

  • tool_choice: officially default auto; none disables tools; required is documented as not supported for Qwen models; a named function is not supported in thinking mode, so set enable_thinking: false first when you must force a tool.
  • parallel_tool_calls officially defaults to false and must be true for parallel calls; use it only for independent tasks, dependent calls need a serial loop.
  • tool_stream (per official docs, not tested): boolean, default false, only together with stream: true, streams tool arguments incrementally; the official docs list only some qwen3.8 / 3.7 tiers; put it in extra_body.
  • n must be 1 when tools is set.
  • Anthropic-format tool_choice: {"type": "none"} becomes tool_choice: "none"; disable_parallel_tool_use: true becomes parallel_tool_calls: false.

JSON output

Evidence: tested (live test account, 2026-10-19), qwen3.7-max, thinking off.

When to use: stable JSON, for example information extraction. Set response_format: {"type": "json_object"}; the prompt must explicitly contain the word JSON, otherwise the request errors (official).

Request

import json
from openai import OpenAI

client = OpenAI(base_url="https://api.modelsite.ai/v1", api_key="sk-ms-xxxxxxxx")

resp = client.chat.completions.create(
    model="qwen3.7-max",
    messages=[
        {"role": "system", "content": "提取姓名和年龄,以 JSON 输出,格式:{\"name\": \"...\", \"age\": 0}"},
        {"role": "user", "content": "大家好,我叫张三,今年 20 岁。"},
    ],
    response_format={"type": "json_object"},
    extra_body={"enable_thinking": False},
)
print(json.loads(resp.choices[0].message.content))

Response

{
  "id": "chatcmpl-xxxx",
  "object": "chat.completion",
  "created": 1760000000,
  "model": "qwen3.7-max",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "{\"name\": \"张三\", \"age\": 20}",
        "refusal": null
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 54,
    "completion_tokens": 13,
    "total_tokens": 67,
    "prompt_tokens_details": {
      "cached_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  }
}

Notes

  • Official advice: do not set max_tokens for structured output, it can cut the JSON; field names and types may vary between calls, so validate before using downstream.
  • In thinking mode json_object may not return strict JSON; turning thinking off is advised.

JSON Schema

Evidence: tested (live test account, 2026-10-19), qwen3.7-max, thinking off.

When to use: automated parsing that needs precise control of fields and types. Use json_schema in response_format, preferably with strict: true and additionalProperties: false; the prompt does not need to contain the word JSON. Officially only some families (Qwen3.7-Plus, Qwen3.7-Max, Qwen3.8-Max and so on) are open, and qwen3-vl-plus is not on the list.

Request

from openai import OpenAI

client = OpenAI(base_url="https://api.modelsite.ai/v1", api_key="sk-ms-xxxxxxxx")

resp = client.chat.completions.create(
    model="qwen3.7-max",
    messages=[{"role": "user", "content": "大家好,我叫张三,今年 20 岁。"}],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "user_info",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {"name": {"type": "string"}, "age": {"type": "integer"}},
                "required": ["name", "age"],
                "additionalProperties": False,
            },
        },
    },
    extra_body={"enable_thinking": False},
)
print(resp.choices[0].message.content)

Response (the keys came back in the order age, name, different from the json_object call above, which shows the output is driven by the schema)

{
  "id": "chatcmpl-xxxx",
  "object": "chat.completion",
  "created": 1760000000,
  "model": "qwen3.7-max",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "{\"age\": 20, \"name\": \"张三\"}",
        "refusal": null
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 26,
    "completion_tokens": 14,
    "total_tokens": 40,
    "prompt_tokens_details": {
      "cached_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  }
}

Notes

  • In strict mode keywords such as multipleOf, uniqueItems, contains, patternProperties and propertyNames return 400 (official).
  • Image, video and audio input do not support json_schema; they fall back to json_object and the schema is not enforced (official).

Evidence: tested (live test account, 2026-10-19), qwen3.7-max, only the most basic enable_search: true; the other options are per official docs (not tested).

When to use: questions that depend on live information (weather, news, quotes). enable_search is not an OpenAI field: put it in extra_body with the Python SDK; once on, the model decides whether a search is needed.

Request

from openai import OpenAI

client = OpenAI(base_url="https://api.modelsite.ai/v1", api_key="sk-ms-xxxxxxxx")

resp = client.chat.completions.create(
    model="qwen3.7-max",
    messages=[{"role": "user", "content": "今天杭州的天气怎么样?一句话。"}],
    max_tokens=500,
    extra_body={"enable_search": True, "enable_thinking": False},
)
print(resp.choices[0].message.content)

Response: note that prompt_tokens is 4270 while the question itself is only a dozen tokens: the searched web content is injected into the prompt as input and billed as input tokens.

{
  "id": "chatcmpl-xxxx",
  "object": "chat.completion",
  "created": 1760000000,
  "model": "qwen3.7-max",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "今天杭州多云,气温23~31℃。",
        "refusal": null
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 4270,
    "completion_tokens": 12,
    "total_tokens": 4282,
    "prompt_tokens_details": {
      "cached_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  }
}

Notes

  • Searching adds a lot of input tokens; estimate the cost yourself.
  • Other options per official docs (not tested): search_options.forced_search (search every time), search_strategy (turbo default / max / agent / agent_max, the agent kinds are charged extra per call), enable_search_extension (vertical data such as weather and stocks), freshness, assigned_site_list and so on. The platform forwards all of these fields unchanged; whether they take effect is up to the upstream.
  • The official docs say the OpenAI-compatible Chat Completions does not return search sources or citation marks (enable_source / enable_citation work only on the native DashScope interface).
  • Per the official docs the Qwen3.8 multimodal interface does not support the agent strategy; thinking models can also set enable_search.

Image and video input

Evidence: tested (live test account, 2026-10-19), qwen3-vl-plus (single image) and qwen3.7-max (confirmed unsupported); qwen3.8-max, qwen3.7-plus, multi-image, video, OCR and grounding are per official docs (not tested).

When to use: visual Q&A, description, OCR, chart analysis, screenshot to code, multi-image comparison, video understanding. Put images in the content array; image_url.url is a public URL or a Base64 data URL. Only models whose capability tags include vision can take images: qwen3.8-max, qwen3.7-plus, qwen3-vl-plus.

Single-image request (qwen3-vl-plus, a solid red 64x64 PNG)

import base64
from openai import OpenAI

client = OpenAI(base_url="https://api.modelsite.ai/v1", api_key="sk-ms-xxxxxxxx")

b64 = base64.b64encode(open("red.png", "rb").read()).decode()
resp = client.chat.completions.create(
    model="qwen3-vl-plus",
    messages=[{"role": "user", "content": [
        {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}},
        {"type": "text", "text": "这张图是什么颜色?一句话。"},
    ]}],
    max_tokens=200,
)
print(resp.choices[0].message.content)

Response: usage.prompt_tokens_details.image_tokens is the number of tokens the image takes. qwen3-vl-plus did not think in this request (officially off by default), so reasoning_content is an empty string.

{
  "id": "chatcmpl-xxxx",
  "object": "chat.completion",
  "created": 1760000000,
  "model": "qwen3-vl-plus",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "这张图是纯红色。",
        "refusal": null,
        "reasoning_content": ""
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 81,
    "completion_tokens": 7,
    "total_tokens": 88,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "text_tokens": 15,
      "image_tokens": 66
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0,
      "text_tokens": 7
    }
  }
}

Unsupported case: qwen3.7-max has no vision tag; sending an image returned 400 in testing:

{
  "error": {
    "code": "api_error",
    "message": "The request could not be completed. (",
    "type": "api_error"
  }
}

Other vision scenarios (per official docs, not tested)

  • Multiple images: put several image_url blocks in content. qwen3.8-max and qwen3.7-plus officially take up to 2048 images, qwen3-vl-plus up to 256; the token total of all images and text must stay below the model's maximum input.
  • Video file: use a video_url block; for an image sequence treated as video use a video block (an array of image URLs). fps controls frame sampling, officially 0.1 to 10 with default 2.0; see the official docs for where the field goes. Official video-file duration limits: 2 seconds to 2 hours for Qwen3.8 / 3.7, 2 seconds to 1 hour for qwen3-vl-plus. The platform has not verified how video is passed or its size limits, so try a short sample first.
  • OCR, information extraction, problem solving, screenshot to code, grounding, document parsing: all are the same image input with a different prompt, no extra parameter; the official advice is to avoid a system message for general chat and put the instructions in the user message.
resp = client.chat.completions.create(
    model="qwen3.8-max",
    messages=[{"role": "user", "content": [
        {"type": "video_url", "video_url": {"url": "https://example.com/demo.mp4"}},
        {"type": "text", "text": "Summarise this video and give the time of the key events."},
    ]}],
    max_tokens=1000,
    extra_body={"enable_thinking": False},
)

Notes

  • Image limits (official): width and height above 10 pixels, aspect ratio at most 200:1; up to 4K BMP, JPEG, PNG, TIFF, WEBP and HEIC, from 4K to 8K only JPEG and PNG; at most 20 MB per image.
  • The platform and the upstream gateway have their own request-body size limits; try large images and long videos on a small scale first.
  • qwen3-vl-plus officially has thinking off by default, qwen3.8-max and qwen3.7-plus have it on; pass enable_thinking explicitly for deterministic behaviour.

Context caching

Evidence: tested (live test account, 2026-10-19), qwen3.7-max: implicit cache hits were tested; explicit caching depends on an update that is not released yet and showed no effect in production.

When to use: the same long document or long system prompt asked about repeatedly, or multi-round chat.

Implicit caching (automatic): cannot be turned off, no request parameter, hits are not guaranteed; officially the minimum is about 1024 tokens and a hit is usually billed at about 20% of the input price (varies by model); put repeated content at the start of the prompt and changing content at the end. Hits are in usage.prompt_tokens_details.cached_tokens.

Request: the same system prompt of about 3,300 tokens, followed by different questions.

from openai import OpenAI

client = OpenAI(base_url="https://api.modelsite.ai/v1", api_key="sk-ms-xxxxxxxx")

KNOWLEDGE = "...a very long policy document (a few thousand tokens)..."

for q in ["退货期限是几天?一句话", "退款几个工作日到账?一句话"]:
    resp = client.chat.completions.create(
        model="qwen3.7-max",
        messages=[
            {"role": "system", "content": "你是客服助手,根据下面的制度回答。\n" + KNOWLEDGE},
            {"role": "user", "content": q},
        ],
        max_tokens=60,
        extra_body={"enable_thinking": False},
    )
    print(resp.choices[0].message.content, resp.usage.prompt_tokens_details.cached_tokens)

Response (only usage shown): 2304 of the 3316 input tokens hit the cache.

{
  "usage": {
    "prompt_tokens": 3316,
    "completion_tokens": 8,
    "total_tokens": 3324,
    "prompt_tokens_details": {
      "cached_tokens": 2304
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  }
}

The request in this test put a cache_control marker on the system message's content block, yet usage has no cache_creation_input_tokens: the hit came from the implicit cache, which means the marker did not trigger explicit caching.

Explicit caching (cache_control: {"type": "ephemeral"}, depends on an update not released yet): the official rules are to put the marker on a content block of messages, only ephemeral, 5-minute lifetime, at most 4 markers per request, cached block at least 1024 tokens, markers only inside content; creating a cache is billed at 125% of the input price and a hit usually at 10%; usage appears in cache_creation_input_tokens and cached_tokens. Production currently strips these markers from the request, so explicit caching has no effect; they will be forwarded once the update ships. Explicit and implicit caching are mutually exclusive: one request can use only one of them.

Anthropic format

Evidence: per official docs (not tested); the same pipeline was tested on DeepSeek and MiniMax, not on Qwen specifically.

When to use: the client only speaks /v1/messages. See the Messages page for usage and authentication (the SDK needs auth_token, not api_key).

Request

from anthropic import Anthropic

client = Anthropic(base_url="https://api.modelsite.ai", auth_token="sk-ms-xxxxxxxx")

resp = client.messages.create(
    model="qwen3.8-max",
    max_tokens=1500,
    messages=[{"role": "user", "content": "用一句话解释量子纠缠"}],
    extra_body={"enable_thinking": True},
)
for block in resp.content:
    if block.type == "thinking":
        print("thinking:", block.thinking)
    elif block.type == "text":
        print("answer:", block.text)

Notes

  • Control thinking with the top-level enable_thinking / thinking_budget, not Anthropic's thinking field (see the notice at the top).
  • In the Anthropic format top_k can use the standard top-level field; stop_sequences maps to stop; metadata.user_id maps to user.

Parameter quick reference

ParameterNotes
enable_thinking, thinking_budget, preserve_thinkingQwen-specific, put in extra_body, forwarded unchanged
reasoning_effortOfficially listed only for the qwen3.8 family; cannot be set together with thinking_budget
thinkingForwarded unchanged, not converted; adaptive is not forwarded; the qwen3.7-plus upstream accepts only enabled / disabled / auto
top_k, repetition_penalty, seedNot OpenAI fields, put in extra_body; top_k officially defaults to 20
max_completion_tokens / max_tokensThe former includes thinking; qwen3-vl-plus uses max_tokens; when both are sent only max_completion_tokens is forwarded
tool_choiceauto / none / named function (non-thinking); required is not supported
parallel_tool_calls, tool_streamOfficially default false, put tool_stream in extra_body
n1 to 4, must be 1 when tools is set
response_formattext / json_object / json_schema (some models)
enable_search, search_optionsWeb search, put in extra_body
vl_high_resolution_images, fpsVision related; see the official docs for the field shape, whether the upstream honours them is unverified
cache_controlExplicit-cache marker, currently stripped in production, see Context caching
Unknown top-level fields, keys in extra_bodyForwarded flat, unchanged
developer roleConverted to system

Also: the reasoning object (OpenRouter style) is handled by the platform itself, and the converted fields may not take effect on Qwen, so use enable_thinking to switch thinking; reasoning.exclude: true or include_reasoning: false removes the reasoning from the response, thinking tokens are still billed.

Official docs

On this page