Skip to content

DashScope (Qwen) plugin

The dashscope plugin gives Genkit access to Alibaba Cloud’s Qwen models through DashScope’s OpenAI-compatible mode. Models are named under the dashscope/ provider prefix.

Terminal window
go get github.com/firebase/genkit/go

Add &dashscope.DashScope{} to your plugin list. The plugin reads the API key from the DASHSCOPE_API_KEY environment variable.

package main
import (
"context"
"fmt"
"log"
"github.com/firebase/genkit/go/ai"
"github.com/firebase/genkit/go/genkit"
"github.com/firebase/genkit/go/plugins/compat_oai/dashscope"
)
func main() {
ctx := context.Background()
g := genkit.Init(ctx,
genkit.WithPlugins(&dashscope.DashScope{}),
genkit.WithDefaultModel("dashscope/qwen-plus"),
)
text, err := genkit.GenerateText(ctx, g, ai.WithPrompt("Share a joke about bananas."))
if err != nil {
log.Fatalf("could not generate: %v", err)
}
fmt.Println(text)
}

You must provide an API key from Alibaba Cloud Model Studio. Set DASHSCOPE_API_KEY, or set the APIKey field. Extra OpenAI client request options ride in Opts, applied after the plugin defaults so they win on overlap; option is github.com/openai/openai-go/option.

g := genkit.Init(ctx, genkit.WithPlugins(&dashscope.DashScope{
APIKey: os.Getenv("MY_DASHSCOPE_KEY"),
Opts: []option.RequestOption{
option.WithBaseURL("https://dashscope.aliyuncs.com/compatible-mode/v1"),
},
}))

genkit.Init panics when neither the APIKey field nor DASHSCOPE_API_KEY is set. The endpoint defaults to the shared international one, https://dashscope-intl.aliyuncs.com/compatible-mode/v1. Mainland-China accounts and workspace-dedicated domains, which is Alibaba’s recommended production setup, need a different base URL: set DASHSCOPE_BASE_URL or pass option.WithBaseURL in Opts.

As always, avoid embedding API keys directly in your code.

The plugin registers these Qwen models when it initializes:

  • qwen-flash, qwen-plus
  • qwen3.5-flash, qwen3.5-plus
  • qwen3.6-flash, qwen3.6-plus
  • qwen3.7-plus, qwen3.7-max, qwen3.7-max-2026-06-08
  • qwen3-max, qwen3-vl-plus, qwen3-coder-plus

That list is a starting point rather than a limit. Any other Qwen model ID resolves on demand and takes the plugin’s text-only defaults, so a model Alibaba releases later works without a Genkit upgrade.

Dated snapshots are otherwise folded into their model’s versions rather than curated separately, so pin one through the config’s Version field. qwen3.7-max-2026-06-08 is the exception: DashScope documents image and video input for that snapshot, which the floating qwen3.7-max does not take, so it is registered on its own for media requests to pass validation. It also stays in qwen3.7-max’s versions, so both spellings work.

No Qwen model advertises tool choice, so tool selection is always automatic and a forced tool choice is refused before the request goes out. Constrained generation is unclaimed too: DashScope’s response_format takes json_object only, not json_schema, so an output schema reaches the model as prompt instructions and comes back as the same typed result. qwen3.7-max and qwen3-coder-plus go further and advertise text output only, since Alibaba’s capability tables say structured outputs are unsupported for them.

dashscope.ModelRef pairs a model ID with a typed dashscope.ChatConfig, so the config is checked where you write it and validated against the model’s schema before the request goes out.

resp, err := genkit.Generate(ctx, g,
ai.WithModel(dashscope.ModelRef("qwen-plus", &dashscope.ChatConfig{
EnableThinking: openai.Ptr(true),
ThinkingBudget: openai.Ptr(2048),
})),
ai.WithPrompt("Share a joke about bananas."),
)
if err != nil {
log.Fatalf("could not generate: %v", err)
}
fmt.Println(resp.Text())

openai.Ptr is the OpenAI SDK’s own helper, imported from github.com/openai/openai-go. It is convenient for the pointer fields; any *bool or *int works.

The ID passed to ModelRef works bare or provider-prefixed. You can also name a model as a string with ai.WithModelName("dashscope/qwen-plus") or genkit.WithDefaultModel, and pass the config separately with ai.WithConfig(&dashscope.ChatConfig{...}). The DashScope sample runs this as a streaming flow you can call from the Dev UI.

dashscope.ChatConfig carries the generation fields the compatible mode accepts plus the DashScope-specific controls:

FieldTypeNotes
Temperature*float64Randomness of token selection, from 0 inclusive up to but not including 2.
TopP*float64Nucleus sampling threshold, above 0 and up to 1 inclusive.
MaxOutputTokensintSent as the API’s max_tokens. The default and the ceiling are both the model’s maximum output length.
StopSequences[]stringStop generation when produced by the model.
PresencePenalty*float64-2 to 2. The compatible mode documents no frequency penalty.
Seed*int0 to 2147483647. Makes generation reproducible across calls.
EnableThinking*boolTurns the thinking mode of hybrid Qwen models on or off, sent as the API’s enable_thinking.
ThinkingBudget*intCaps how many tokens the model may think with. Requires EnableThinking.
EnableSearch*boolLets the model consult web search, sent as the API’s enable_search.

Pointer fields separate unset from a deliberate zero.

ChatConfig also embeds compat_oai.RequestConfig, which every plugin in the family shares: a per-request APIKey, a Version pin, and an Extra map whose keys ride to the wire verbatim under DashScope’s own names. See the OpenAI-compatible plugin page.

Correcting what the plugin knows about a model

Section titled “Correcting what the plugin knows about a model”

Every Qwen model works without an entry in Models. Supply one only to correct or extend the capabilities the plugin resolves, most often for a model released after your Genkit version. Keys are the model ID, bare or provider-prefixed, and fields left at their zero value keep what the plugin resolved.

g := genkit.Init(ctx, genkit.WithPlugins(&dashscope.DashScope{
Models: map[string]ai.ModelOptions{
// A model the plugin does not curate resolves with the text-only
// defaults, so an entry is how you tell Genkit it takes images.
"qwen3-vl-flash": {
Supports: &ai.ModelSupports{
Multiturn: true,
Tools: true,
SystemRole: true,
Media: true,
Output: []string{"text", "json"},
},
},
},
}))

Reasoning text, streamed token usage, cached prompt tokens, and mid-generation provider failures are handled the same way for every plugin in this family. See the OpenAI-compatible plugin page.