diff --git a/README.ja.md b/README.ja.md index e33b312f9..fdb9cc202 100644 --- a/README.ja.md +++ b/README.ja.md @@ -206,7 +206,7 @@ picoclaw onboard **3. API キーの取得** -- **LLM プロバイダー**: [OpenRouter](https://openrouter.ai/keys) · [Zhipu](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) · [Anthropic](https://console.anthropic.com) · [OpenAI](https://platform.openai.com) · [Gemini](https://aistudio.google.com/api-keys) +- **LLM プロバイダー**: [OpenRouter](https://openrouter.ai/keys) · [Zhipu](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) · [Anthropic](https://console.anthropic.com) · [OpenAI](https://platform.openai.com) · [Gemini](https://aistudio.google.com/api-keys) · [Qwen](https://dashscope.console.aliyun.com) - **Web 検索**(任意): [Brave Search](https://brave.com/search/api) - 無料枠あり(月 2000 リクエスト) > **注意**: 完全な設定テンプレートは `config.example.json` を参照してください。 @@ -765,5 +765,6 @@ Web 検索を有効にするには: |---------|--------|------------| | **OpenRouter** | 月 200K トークン | 複数モデル(Claude, GPT-4 など) | | **Zhipu** | 月 200K トークン | 中国ユーザー向け最適 | +| **Qwen** | 無料枠あり | 通義千問 (Qwen) | | **Brave Search** | 月 2000 クエリ | Web 検索機能 | | **Groq** | 無料枠あり | 高速推論(Llama, Mixtral) | diff --git a/README.md b/README.md index 0a9dacce6..d54e80dcd 100644 --- a/README.md +++ b/README.md @@ -663,6 +663,7 @@ The subagent has access to tools (message, web_search, etc.) and can communicate | `anthropic(To be tested)` | LLM (Claude direct) | [console.anthropic.com](https://console.anthropic.com) | | `openai(To be tested)` | LLM (GPT direct) | [platform.openai.com](https://platform.openai.com) | | `deepseek(To be tested)` | LLM (DeepSeek direct) | [platform.deepseek.com](https://platform.deepseek.com) | +| `qwen` | LLM (Qwen direct) | [dashscope.console.aliyun.com](https://dashscope.console.aliyun.com) | | `groq` | LLM + **Voice transcription** (Whisper) | [console.groq.com](https://console.groq.com) |
diff --git a/README.zh.md b/README.zh.md index 2ca2987bb..e12e401fb 100644 --- a/README.zh.md +++ b/README.zh.md @@ -534,6 +534,7 @@ Agent 读取 HEARTBEAT.md | `anthropic(待测试)` | LLM (Claude 直连) | [console.anthropic.com](https://console.anthropic.com) | | `openai(待测试)` | LLM (GPT 直连) | [platform.openai.com](https://platform.openai.com) | | `deepseek(待测试)` | LLM (DeepSeek 直连) | [platform.deepseek.com](https://platform.deepseek.com) | +| `qwen` | LLM (通义千问) | [dashscope.console.aliyun.com](https://dashscope.console.aliyun.com) | | `groq` | LLM + **语音转录** (Whisper) | [console.groq.com](https://console.groq.com) |
diff --git a/cmd/picoclaw/main.go b/cmd/picoclaw/main.go index 10b53948b..79270378d 100644 --- a/cmd/picoclaw/main.go +++ b/cmd/picoclaw/main.go @@ -726,6 +726,7 @@ func statusCmd() { hasOpenAI := cfg.Providers.OpenAI.APIKey != "" hasGemini := cfg.Providers.Gemini.APIKey != "" hasZhipu := cfg.Providers.Zhipu.APIKey != "" + hasQwen := cfg.Providers.Qwen.APIKey != "" hasGroq := cfg.Providers.Groq.APIKey != "" hasVLLM := cfg.Providers.VLLM.APIBase != "" @@ -740,6 +741,7 @@ func statusCmd() { fmt.Println("OpenAI API:", status(hasOpenAI)) fmt.Println("Gemini API:", status(hasGemini)) fmt.Println("Zhipu API:", status(hasZhipu)) + fmt.Println("Qwen API:", status(hasQwen)) fmt.Println("Groq API:", status(hasGroq)) if hasVLLM { fmt.Printf("vLLM/Local: ✓ %s\n", cfg.Providers.VLLM.APIBase) diff --git a/config/config.example.json b/config/config.example.json index 3c9158e9c..8ba06e0dc 100644 --- a/config/config.example.json +++ b/config/config.example.json @@ -108,6 +108,10 @@ "api_key": "sk-xxx", "api_base": "" }, + "qwen": { + "api_key": "sk-xxx", + "api_base": "" + }, "ollama": { "api_key": "", "api_base": "http://localhost:11434/v1" diff --git a/pkg/channels/telegram.go b/pkg/channels/telegram.go index 5601d508c..e096a0a7a 100644 --- a/pkg/channels/telegram.go +++ b/pkg/channels/telegram.go @@ -59,6 +59,13 @@ func NewTelegramChannel(cfg *config.Config, bus *bus.MessageBus) (*TelegramChann Proxy: http.ProxyURL(proxyURL), }, })) + } else if os.Getenv("HTTP_PROXY") != "" || os.Getenv("HTTPS_PROXY") != "" { + // Use environment proxy if configured + opts = append(opts, telego.WithHTTPClient(&http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + }, + })) } bot, err := telego.NewBot(telegramCfg.Token, opts...) diff --git a/pkg/config/config.go b/pkg/config/config.go index d189ff00b..af2e36e91 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -180,6 +180,7 @@ type ProvidersConfig struct { ShengSuanYun ProviderConfig `json:"shengsuanyun"` DeepSeek ProviderConfig `json:"deepseek"` GitHubCopilot ProviderConfig `json:"github_copilot"` + Qwen ProviderConfig `json:"qwen"` } type ProviderConfig struct { diff --git a/pkg/migrate/config.go b/pkg/migrate/config.go index 9c1e36359..8bb5e14c0 100644 --- a/pkg/migrate/config.go +++ b/pkg/migrate/config.go @@ -19,6 +19,8 @@ var supportedProviders = map[string]bool{ "zhipu": true, "vllm": true, "gemini": true, + "qwen": true, + "deepseek": true, } var supportedChannels = map[string]bool{ @@ -253,6 +255,15 @@ func MergeConfig(existing, incoming *config.Config) *config.Config { if existing.Providers.Gemini.APIKey == "" { existing.Providers.Gemini = incoming.Providers.Gemini } + if existing.Providers.DeepSeek.APIKey == "" { + existing.Providers.DeepSeek = incoming.Providers.DeepSeek + } + if existing.Providers.GitHubCopilot.APIBase == "" { + existing.Providers.GitHubCopilot = incoming.Providers.GitHubCopilot + } + if existing.Providers.Qwen.APIKey == "" { + existing.Providers.Qwen = incoming.Providers.Qwen + } if !existing.Channels.Telegram.Enabled && incoming.Channels.Telegram.Enabled { existing.Channels.Telegram = incoming.Channels.Telegram diff --git a/pkg/providers/http_provider.go b/pkg/providers/http_provider.go index 4cf2c6db2..0c4517ad4 100644 --- a/pkg/providers/http_provider.go +++ b/pkg/providers/http_provider.go @@ -56,7 +56,7 @@ func (p *HTTPProvider) Chat(ctx context.Context, messages []Message, tools []Too // Strip provider prefix from model name (e.g., moonshot/kimi-k2.5 -> kimi-k2.5, groq/openai/gpt-oss-120b -> openai/gpt-oss-120b, ollama/qwen2.5:14b -> qwen2.5:14b) if idx := strings.Index(model, "/"); idx != -1 { prefix := model[:idx] - if prefix == "moonshot" || prefix == "nvidia" || prefix == "groq" || prefix == "ollama" { + if prefix == "moonshot" || prefix == "nvidia" || prefix == "groq" || prefix == "ollama" || prefix == "qwen" { model = model[idx+1:] } } @@ -324,6 +324,14 @@ func CreateProvider(cfg *config.Config) (LLMProvider, error) { model = "deepseek-chat" } } + case "qwen": + if cfg.Providers.Qwen.APIKey != "" { + apiKey = cfg.Providers.Qwen.APIKey + apiBase = cfg.Providers.Qwen.APIBase + if apiBase == "" { + apiBase = "https://dashscope.aliyuncs.com/compatible-mode/v1" + } + } case "github_copilot", "copilot": if cfg.Providers.GitHubCopilot.APIBase != "" { apiBase = cfg.Providers.GitHubCopilot.APIBase @@ -402,6 +410,14 @@ func CreateProvider(cfg *config.Config) (LLMProvider, error) { apiBase = "https://api.groq.com/openai/v1" } + case (strings.Contains(lowerModel, "qwen") || strings.HasPrefix(model, "qwen/")) && cfg.Providers.Qwen.APIKey != "": + apiKey = cfg.Providers.Qwen.APIKey + apiBase = cfg.Providers.Qwen.APIBase + proxy = cfg.Providers.Qwen.Proxy + if apiBase == "" { + apiBase = "https://dashscope.aliyuncs.com/compatible-mode/v1" + } + case (strings.Contains(lowerModel, "nvidia") || strings.HasPrefix(model, "nvidia/")) && cfg.Providers.Nvidia.APIKey != "": apiKey = cfg.Providers.Nvidia.APIKey apiBase = cfg.Providers.Nvidia.APIBase