fix(auth): preserve model_list and use gpt-5.2 for Codex API

Auth fixes:
- Fix OpenAI/Anthropic OAuth and token login to update ModelList
- Fix logout to clear AuthMethod in ModelList
- Add helper functions: isOpenAIModel, isAnthropicModel, isAntigravityModel
- Fix slice bounds panic in isAntigravityModel using strings.HasPrefix
- All auth operations now preserve existing model_list configuration

Factory provider fixes:
- Add OAuth support for openai protocol in CreateProviderFromConfig
- CodexAuthProvider is now used when auth_method is oauth/token

Default model updates:
- OpenAI login: set default model to gpt-5.2
- Anthropic login: set default model to claude-sonnet-4
- Antigravity login: set default model to gemini-flash (remove provider field)

Model changes:
- Change default OpenAI model from gpt-4o to gpt-5.2
- gpt-5.2 is compatible with Codex API (chatgpt.com backend)
- Update all README files, config examples, and migration code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
yinwm
2026-02-20 10:48:27 +08:00
parent df6958f312
commit 6ad85d225b
14 changed files with 234 additions and 91 deletions
+9 -9
View File
@@ -833,8 +833,8 @@ Cette conception permet également le **support multi-agent** avec une sélectio
{
"model_list": [
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-your-openai-key"
},
{
@@ -850,7 +850,7 @@ Cette conception permet également le **support multi-agent** avec une sélectio
],
"agents": {
"defaults": {
"model": "gpt-4o"
"model": "gpt-5.2"
}
}
}
@@ -861,8 +861,8 @@ Cette conception permet également le **support multi-agent** avec une sélectio
**OpenAI**
```json
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-..."
}
```
@@ -894,14 +894,14 @@ Configurez plusieurs points de terminaison pour le même nom de modèle—PicoCl
{
"model_list": [
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_base": "https://api1.example.com/v1",
"api_key": "sk-key1"
},
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_base": "https://api2.example.com/v1",
"api_key": "sk-key2"
}
+9 -9
View File
@@ -769,8 +769,8 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る
{
"model_list": [
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-your-openai-key"
},
{
@@ -786,7 +786,7 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る
],
"agents": {
"defaults": {
"model": "gpt-4o"
"model": "gpt-5.2"
}
}
}
@@ -797,8 +797,8 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る
**OpenAI**
```json
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-..."
}
```
@@ -830,14 +830,14 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る
{
"model_list": [
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_base": "https://api1.example.com/v1",
"api_key": "sk-key1"
},
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_base": "https://api2.example.com/v1",
"api_key": "sk-key2"
}
+10 -10
View File
@@ -218,7 +218,7 @@ picoclaw onboard
"model_list": [
{
"model_name": "gpt4",
"model": "openai/gpt-4o",
"model": "openai/gpt-5.2",
"api_key": "your-api-key"
},
{
@@ -728,8 +728,8 @@ This design also enables **multi-agent support** with flexible provider selectio
{
"model_list": [
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-your-openai-key"
},
{
@@ -745,7 +745,7 @@ This design also enables **multi-agent support** with flexible provider selectio
],
"agents": {
"defaults": {
"model": "gpt-4o"
"model": "gpt-5.2"
}
}
}
@@ -756,8 +756,8 @@ This design also enables **multi-agent support** with flexible provider selectio
**OpenAI**
```json
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-..."
}
```
@@ -816,14 +816,14 @@ Configure multiple endpoints for the same model name—PicoClaw will automatical
{
"model_list": [
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_base": "https://api1.example.com/v1",
"api_key": "sk-key1"
},
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_base": "https://api2.example.com/v1",
"api_key": "sk-key2"
}
+9 -9
View File
@@ -834,8 +834,8 @@ Este design também possibilita o **suporte multi-agent** com seleção flexíve
{
"model_list": [
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-your-openai-key"
},
{
@@ -851,7 +851,7 @@ Este design também possibilita o **suporte multi-agent** com seleção flexíve
],
"agents": {
"defaults": {
"model": "gpt-4o"
"model": "gpt-5.2"
}
}
}
@@ -862,8 +862,8 @@ Este design também possibilita o **suporte multi-agent** com seleção flexíve
**OpenAI**
```json
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-..."
}
```
@@ -895,14 +895,14 @@ Configure vários endpoints para o mesmo nome de modelo—PicoClaw fará round-r
{
"model_list": [
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_base": "https://api1.example.com/v1",
"api_key": "sk-key1"
},
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_base": "https://api2.example.com/v1",
"api_key": "sk-key2"
}
+9 -9
View File
@@ -811,8 +811,8 @@ Thiết kế này cũng cho phép **hỗ trợ đa tác nhân** với lựa ch
{
"model_list": [
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-your-openai-key"
},
{
@@ -828,7 +828,7 @@ Thiết kế này cũng cho phép **hỗ trợ đa tác nhân** với lựa ch
],
"agents": {
"defaults": {
"model": "gpt-4o"
"model": "gpt-5.2"
}
}
}
@@ -839,8 +839,8 @@ Thiết kế này cũng cho phép **hỗ trợ đa tác nhân** với lựa ch
**OpenAI**
```json
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-..."
}
```
@@ -872,14 +872,14 @@ Thiết kế này cũng cho phép **hỗ trợ đa tác nhân** với lựa ch
{
"model_list": [
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_base": "https://api1.example.com/v1",
"api_key": "sk-key1"
},
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_base": "https://api2.example.com/v1",
"api_key": "sk-key2"
}
+10 -10
View File
@@ -227,7 +227,7 @@ picoclaw onboard
"model_list": [
{
"model_name": "gpt4",
"model": "openai/gpt-4o",
"model": "openai/gpt-5.2",
"api_key": "your-api-key"
},
{
@@ -605,8 +605,8 @@ Agent 读取 HEARTBEAT.md
{
"model_list": [
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-your-openai-key"
},
{
@@ -622,7 +622,7 @@ Agent 读取 HEARTBEAT.md
],
"agents": {
"defaults": {
"model": "gpt-4o"
"model": "gpt-5.2"
}
}
}
@@ -633,8 +633,8 @@ Agent 读取 HEARTBEAT.md
**OpenAI**
```json
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-..."
}
```
@@ -693,14 +693,14 @@ Agent 读取 HEARTBEAT.md
{
"model_list": [
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_base": "https://api1.example.com/v1",
"api_key": "sk-key1"
},
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_base": "https://api2.example.com/v1",
"api_key": "sk-key2"
}
+129 -5
View File
@@ -9,6 +9,7 @@ import (
"io"
"net/http"
"os"
"strings"
"time"
"github.com/sipeed/picoclaw/pkg/auth"
@@ -118,7 +119,31 @@ func authLoginOpenAI(useDeviceCode bool) {
appCfg, err := loadConfig()
if err == nil {
// Update Providers (legacy format)
appCfg.Providers.OpenAI.AuthMethod = "oauth"
// Update or add openai in ModelList
foundOpenAI := false
for i := range appCfg.ModelList {
if isOpenAIModel(appCfg.ModelList[i].Model) {
appCfg.ModelList[i].AuthMethod = "oauth"
foundOpenAI = true
break
}
}
// If no openai in ModelList, add it
if !foundOpenAI {
appCfg.ModelList = append(appCfg.ModelList, config.ModelConfig{
ModelName: "gpt-5.2",
Model: "openai/gpt-5.2",
AuthMethod: "oauth",
})
}
// Update default model to use OpenAI
appCfg.Agents.Defaults.Model = "gpt-5.2"
if err := config.SaveConfig(getConfigPath(), appCfg); err != nil {
fmt.Printf("Warning: could not update config: %v\n", err)
}
@@ -128,6 +153,7 @@ func authLoginOpenAI(useDeviceCode bool) {
if cred.AccountID != "" {
fmt.Printf("Account: %s\n", cred.AccountID)
}
fmt.Println("Default model set to: gpt-5.2")
}
func authLoginGoogleAntigravity() {
@@ -167,20 +193,38 @@ func authLoginGoogleAntigravity() {
appCfg, err := loadConfig()
if err == nil {
// Update Providers (legacy format, for backward compatibility)
appCfg.Providers.Antigravity.AuthMethod = "oauth"
if appCfg.Agents.Defaults.Provider == "" {
appCfg.Agents.Defaults.Provider = "antigravity"
// Update or add antigravity in ModelList
foundAntigravity := false
for i := range appCfg.ModelList {
if isAntigravityModel(appCfg.ModelList[i].Model) {
appCfg.ModelList[i].AuthMethod = "oauth"
foundAntigravity = true
break
}
}
if appCfg.Agents.Defaults.Provider == "antigravity" || appCfg.Agents.Defaults.Provider == "google-antigravity" {
appCfg.Agents.Defaults.Model = "gemini-3-flash"
// If no antigravity in ModelList, add it
if !foundAntigravity {
appCfg.ModelList = append(appCfg.ModelList, config.ModelConfig{
ModelName: "gemini-flash",
Model: "antigravity/gemini-3-flash",
AuthMethod: "oauth",
})
}
// Update default model
appCfg.Agents.Defaults.Model = "gemini-flash"
if err := config.SaveConfig(getConfigPath(), appCfg); err != nil {
fmt.Printf("Warning: could not update config: %v\n", err)
}
}
fmt.Println("\n✓ Google Antigravity login successful!")
fmt.Println("Config updated: provider=antigravity, model=gemini-3-flash")
fmt.Println("Default model set to: gemini-flash")
fmt.Println("Try it: picoclaw agent -m \"Hello world\"")
}
@@ -229,8 +273,44 @@ func authLoginPasteToken(provider string) {
switch provider {
case "anthropic":
appCfg.Providers.Anthropic.AuthMethod = "token"
// Update ModelList
found := false
for i := range appCfg.ModelList {
if isAnthropicModel(appCfg.ModelList[i].Model) {
appCfg.ModelList[i].AuthMethod = "token"
found = true
break
}
}
if !found {
appCfg.ModelList = append(appCfg.ModelList, config.ModelConfig{
ModelName: "claude-sonnet-4",
Model: "anthropic/claude-sonnet-4-20250514",
AuthMethod: "token",
})
}
// Update default model
appCfg.Agents.Defaults.Model = "claude-sonnet-4"
case "openai":
appCfg.Providers.OpenAI.AuthMethod = "token"
// Update ModelList
found := false
for i := range appCfg.ModelList {
if isOpenAIModel(appCfg.ModelList[i].Model) {
appCfg.ModelList[i].AuthMethod = "token"
found = true
break
}
}
if !found {
appCfg.ModelList = append(appCfg.ModelList, config.ModelConfig{
ModelName: "gpt-5.2",
Model: "openai/gpt-5.2",
AuthMethod: "token",
})
}
// Update default model
appCfg.Agents.Defaults.Model = "gpt-5.2"
}
if err := config.SaveConfig(getConfigPath(), appCfg); err != nil {
fmt.Printf("Warning: could not update config: %v\n", err)
@@ -238,6 +318,7 @@ func authLoginPasteToken(provider string) {
}
fmt.Printf("Token saved for %s!\n", provider)
fmt.Printf("Default model set to: %s\n", appCfg.Agents.Defaults.Model)
}
func authLogoutCmd() {
@@ -262,6 +343,24 @@ func authLogoutCmd() {
appCfg, err := loadConfig()
if err == nil {
// Clear AuthMethod in ModelList
for i := range appCfg.ModelList {
switch provider {
case "openai":
if isOpenAIModel(appCfg.ModelList[i].Model) {
appCfg.ModelList[i].AuthMethod = ""
}
case "anthropic":
if isAnthropicModel(appCfg.ModelList[i].Model) {
appCfg.ModelList[i].AuthMethod = ""
}
case "google-antigravity", "antigravity":
if isAntigravityModel(appCfg.ModelList[i].Model) {
appCfg.ModelList[i].AuthMethod = ""
}
}
}
// Clear AuthMethod in Providers (legacy)
switch provider {
case "openai":
appCfg.Providers.OpenAI.AuthMethod = ""
@@ -282,6 +381,11 @@ func authLogoutCmd() {
appCfg, err := loadConfig()
if err == nil {
// Clear all AuthMethods in ModelList
for i := range appCfg.ModelList {
appCfg.ModelList[i].AuthMethod = ""
}
// Clear all AuthMethods in Providers (legacy)
appCfg.Providers.OpenAI.AuthMethod = ""
appCfg.Providers.Anthropic.AuthMethod = ""
appCfg.Providers.Antigravity.AuthMethod = ""
@@ -384,3 +488,23 @@ func authModelsCmd() {
fmt.Printf(" %s %s\n", status, name)
}
}
// isAntigravityModel checks if a model string belongs to antigravity provider
func isAntigravityModel(model string) bool {
return model == "antigravity" ||
model == "google-antigravity" ||
strings.HasPrefix(model, "antigravity/") ||
strings.HasPrefix(model, "google-antigravity/")
}
// isOpenAIModel checks if a model string belongs to openai provider
func isOpenAIModel(model string) bool {
return model == "openai" ||
strings.HasPrefix(model, "openai/")
}
// isAnthropicModel checks if a model string belongs to anthropic provider
func isAnthropicModel(model string) bool {
return model == "anthropic" ||
strings.HasPrefix(model, "anthropic/")
}
+3 -3
View File
@@ -12,7 +12,7 @@
"model_list": [
{
"model_name": "gpt4",
"model": "openai/gpt-4o",
"model": "openai/gpt-5.2",
"api_key": "sk-your-openai-key",
"api_base": "https://api.openai.com/v1"
},
@@ -34,13 +34,13 @@
},
{
"model_name": "loadbalanced-gpt4",
"model": "openai/gpt-4o",
"model": "openai/gpt-5.2",
"api_key": "sk-key1",
"api_base": "https://api1.example.com/v1"
},
{
"model_name": "loadbalanced-gpt4",
"model": "openai/gpt-4o",
"model": "openai/gpt-5.2",
"api_key": "sk-key2",
"api_base": "https://api2.example.com/v1"
}
+6 -6
View File
@@ -66,7 +66,7 @@ Problem: Agent needs to know both `provider` and `model`, adding complexity.
Inspired by [LiteLLM](https://docs.litellm.ai/docs/proxy/configs) design:
1. **Model-centric**: Users care about models, not providers
2. **Protocol prefix**: Use `protocol/model_name` format, e.g., `openai/gpt-4o`, `anthropic/claude-3-sonnet`
2. **Protocol prefix**: Use `protocol/model_name` format, e.g., `openai/gpt-5.2`, `anthropic/claude-3-sonnet`
3. **Configuration-driven**: Adding new Providers only requires config changes, no code changes
### 2.2 New Configuration Structure
@@ -81,8 +81,8 @@ Inspired by [LiteLLM](https://docs.litellm.ai/docs/proxy/configs) design:
"api_key": "sk-xxx"
},
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"model_name": "gpt-5.2",
"model": "openai/gpt-5.2",
"api_key": "sk-xxx"
},
{
@@ -128,7 +128,7 @@ type Config struct {
type ModelConfig struct {
// Required
ModelName string `json:"model_name"` // user-facing name (alias)
Model string `json:"model"` // protocol/model, e.g., openai/gpt-4o
Model string `json:"model"` // protocol/model, e.g., openai/gpt-5.2
// Common config
APIBase string `json:"api_base,omitempty"`
@@ -180,7 +180,7 @@ Identify protocol via prefix in `model` field:
"model": "deepseek-chat"
},
"coder": {
"model": "gpt-4o",
"model": "gpt-5.2",
"system_prompt": "You are a coding assistant..."
},
"translator": {
@@ -200,7 +200,7 @@ Each Agent only needs to specify `model` (corresponds to `model_name` in `model_
model_list:
- model_name: gpt-4o
litellm_params:
model: openai/gpt-4o
model: openai/gpt-5.2
api_key: xxx
- model_name: my-custom
litellm_params:
+7 -7
View File
@@ -40,7 +40,7 @@ The new `model_list` configuration offers several advantages:
"agents": {
"defaults": {
"provider": "openai",
"model": "gpt-4o"
"model": "gpt-5.2"
}
}
}
@@ -53,7 +53,7 @@ The new `model_list` configuration offers several advantages:
"model_list": [
{
"model_name": "gpt4",
"model": "openai/gpt-4o",
"model": "openai/gpt-5.2",
"api_key": "sk-your-openai-key",
"api_base": "https://api.openai.com/v1"
},
@@ -82,7 +82,7 @@ The `model` field uses a protocol prefix format: `[protocol/]model-identifier`
| Prefix | Description | Example |
|--------|-------------|---------|
| `openai/` | OpenAI API (default) | `openai/gpt-4o` |
| `openai/` | OpenAI API (default) | `openai/gpt-5.2` |
| `anthropic/` | Anthropic API | `anthropic/claude-3-opus` |
| `antigravity/` | Google via Antigravity OAuth | `antigravity/gemini-2.0-flash` |
| `claude-cli/` | Claude CLI (local) | `claude-cli/claude-3-sonnet` |
@@ -101,7 +101,7 @@ The `model` field uses a protocol prefix format: `[protocol/]model-identifier`
| Field | Required | Description |
|-------|----------|-------------|
| `model_name` | Yes | User-facing alias for the model |
| `model` | Yes | Protocol and model identifier (e.g., `openai/gpt-4o`) |
| `model` | Yes | Protocol and model identifier (e.g., `openai/gpt-5.2`) |
| `api_base` | No | API endpoint URL |
| `api_key` | No* | API authentication key |
| `proxy` | No | HTTP proxy URL |
@@ -121,19 +121,19 @@ Configure multiple endpoints for the same model to distribute load:
"model_list": [
{
"model_name": "gpt4",
"model": "openai/gpt-4o",
"model": "openai/gpt-5.2",
"api_key": "sk-key1",
"api_base": "https://api1.example.com/v1"
},
{
"model_name": "gpt4",
"model": "openai/gpt-4o",
"model": "openai/gpt-5.2",
"api_key": "sk-key2",
"api_base": "https://api2.example.com/v1"
},
{
"model_name": "gpt4",
"model": "openai/gpt-4o",
"model": "openai/gpt-5.2",
"api_key": "sk-key3",
"api_base": "https://api3.example.com/v1"
}
+6 -6
View File
@@ -105,8 +105,8 @@ func DefaultConfig() *Config {
// OpenAI - https://platform.openai.com/api-keys
{
ModelName: "gpt-4o",
Model: "openai/gpt-4o",
ModelName: "gpt-5.2",
Model: "openai/gpt-5.2",
APIBase: "https://api.openai.com/v1",
APIKey: "",
},
@@ -161,8 +161,8 @@ func DefaultConfig() *Config {
// OpenRouter (100+ models) - https://openrouter.ai/keys
{
ModelName: "openrouter-gpt-4o",
Model: "openrouter/openai/gpt-4o",
ModelName: "openrouter-gpt-5.2",
Model: "openrouter/openai/gpt-5.2",
APIBase: "https://openrouter.ai/api/v1",
APIKey: "",
},
@@ -208,8 +208,8 @@ func DefaultConfig() *Config {
// GitHub Copilot - https://github.com/settings/tokens
{
ModelName: "copilot-gpt-4o",
Model: "github-copilot/gpt-4o",
ModelName: "copilot-gpt-5.2",
Model: "github-copilot/gpt-5.2",
APIBase: "http://localhost:4321",
AuthMethod: "oauth",
},
+2 -2
View File
@@ -50,7 +50,7 @@ func ConvertProvidersToModelList(cfg *Config) []ModelConfig {
}
return ModelConfig{
ModelName: "openai",
Model: "openai/gpt-4o",
Model: "openai/gpt-5.2",
APIKey: p.OpenAI.APIKey,
APIBase: p.OpenAI.APIBase,
Proxy: p.OpenAI.Proxy,
@@ -276,7 +276,7 @@ func ConvertProvidersToModelList(cfg *Config) []ModelConfig {
}
return ModelConfig{
ModelName: "github-copilot",
Model: "github-copilot/gpt-4o",
Model: "github-copilot/gpt-5.2",
APIBase: p.GitHubCopilot.APIBase,
ConnectMode: p.GitHubCopilot.ConnectMode,
}, true
+4 -4
View File
@@ -31,8 +31,8 @@ func TestConvertProvidersToModelList_OpenAI(t *testing.T) {
if result[0].ModelName != "openai" {
t.Errorf("ModelName = %q, want %q", result[0].ModelName, "openai")
}
if result[0].Model != "openai/gpt-4o" {
t.Errorf("Model = %q, want %q", result[0].Model, "openai/gpt-4o")
if result[0].Model != "openai/gpt-5.2" {
t.Errorf("Model = %q, want %q", result[0].Model, "openai/gpt-5.2")
}
if result[0].APIKey != "sk-test-key" {
t.Errorf("APIKey = %q, want %q", result[0].APIKey, "sk-test-key")
@@ -331,8 +331,8 @@ func TestConvertProvidersToModelList_MultipleProviders_PreservesUserModel(t *tes
for _, mc := range result {
switch mc.ModelName {
case "openai":
if mc.Model != "openai/gpt-4o" {
t.Errorf("OpenAI Model = %q, want %q (default)", mc.Model, "openai/gpt-4o")
if mc.Model != "openai/gpt-5.2" {
t.Errorf("OpenAI Model = %q, want %q (default)", mc.Model, "openai/gpt-5.2")
}
case "deepseek":
if mc.Model != "deepseek/deepseek-reasoner" {
+21 -2
View File
@@ -67,10 +67,29 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err
protocol, modelID := ExtractProtocol(cfg.Model)
switch protocol {
case "openai", "openrouter", "groq", "zhipu", "gemini", "nvidia",
case "openai":
// OpenAI with OAuth/token auth (Codex-style)
if cfg.AuthMethod == "oauth" || cfg.AuthMethod == "token" {
provider, err := createCodexAuthProvider()
if err != nil {
return nil, "", err
}
return provider, modelID, nil
}
// OpenAI with API key
if cfg.APIKey == "" && cfg.APIBase == "" {
return nil, "", fmt.Errorf("api_key or api_base is required for HTTP-based protocol %q", protocol)
}
apiBase := cfg.APIBase
if apiBase == "" {
apiBase = getDefaultAPIBase(protocol)
}
return NewHTTPProviderWithMaxTokensField(cfg.APIKey, apiBase, cfg.Proxy, cfg.MaxTokensField), modelID, nil
case "openrouter", "groq", "zhipu", "gemini", "nvidia",
"ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras",
"volcengine", "vllm", "qwen":
// All OpenAI-compatible HTTP providers
// All other OpenAI-compatible HTTP providers
if cfg.APIKey == "" && cfg.APIBase == "" {
return nil, "", fmt.Errorf("api_key or api_base is required for HTTP-based protocol %q", protocol)
}