Files
goclaw/internal/providers/dashscope.go
T
viettranx bdb60de7ae chore: upgrade Go 1.25 → 1.26 and apply go fix modernizations
- Update go.mod and Dockerfile to Go 1.26
- Apply `go fix ./...` stdlib modernizations across 170+ files
- Add `go fix` to post-implementation checklist in CLAUDE.md
- Fix go fix misapplied rewrite in loop_history.go
2026-03-10 00:09:15 +07:00

84 lines
2.5 KiB
Go

package providers
import (
"context"
"log/slog"
"maps"
)
const (
dashscopeDefaultBase = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
dashscopeDefaultModel = "qwen3-max"
)
// DashScopeProvider wraps OpenAIProvider to handle DashScope-specific behaviors.
// Critical: DashScope does NOT support tools + streaming simultaneously.
// When tools are present, ChatStream falls back to non-streaming Chat().
type DashScopeProvider struct {
*OpenAIProvider
}
func NewDashScopeProvider(apiKey, apiBase, defaultModel string) *DashScopeProvider {
if apiBase == "" {
apiBase = dashscopeDefaultBase
}
if defaultModel == "" {
defaultModel = dashscopeDefaultModel
}
return &DashScopeProvider{
OpenAIProvider: NewOpenAIProvider("dashscope", apiKey, apiBase, defaultModel),
}
}
func (p *DashScopeProvider) Name() string { return "dashscope" }
func (p *DashScopeProvider) SupportsThinking() bool { return true }
// ChatStream handles DashScope's limitation: tools + streaming cannot coexist.
// When tools are present, falls back to non-streaming Chat() and synthesizes
// chunk callbacks for the caller.
func (p *DashScopeProvider) ChatStream(ctx context.Context, req ChatRequest, onChunk func(StreamChunk)) (*ChatResponse, error) {
// Map thinking_level to DashScope-specific params before passing to OpenAI base
if level, ok := req.Options[OptThinkingLevel].(string); ok && level != "" && level != "off" {
// Clone Options to avoid mutating caller's map
opts := make(map[string]any, len(req.Options)+2)
maps.Copy(opts, req.Options)
opts[OptEnableThinking] = true
opts[OptThinkingBudget] = dashscopeThinkingBudget(level)
delete(opts, OptThinkingLevel) // don't pass generic key to OpenAI buildRequestBody
req.Options = opts
}
if len(req.Tools) > 0 {
slog.Debug("dashscope: tools present, falling back to non-streaming Chat")
resp, err := p.Chat(ctx, req)
if err != nil {
return nil, err
}
if onChunk != nil {
if resp.Thinking != "" {
onChunk(StreamChunk{Thinking: resp.Thinking})
}
if resp.Content != "" {
onChunk(StreamChunk{Content: resp.Content})
}
onChunk(StreamChunk{Done: true})
}
return resp, nil
}
return p.OpenAIProvider.ChatStream(ctx, req, onChunk)
}
// dashscopeThinkingBudget maps a thinking level to a DashScope thinking_budget value.
func dashscopeThinkingBudget(level string) int {
switch level {
case "low":
return 4096
case "medium":
return 16384
case "high":
return 32768
default:
return 16384
}
}