mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-07-31 16:21:30 +00:00
- 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
84 lines
2.5 KiB
Go
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
|
|
}
|
|
}
|