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:
parent
df6958f312
commit
6ad85d225b
14 changed files with 234 additions and 91 deletions
18
README.fr.md
18
README.fr.md
|
|
@ -833,8 +833,8 @@ Cette conception permet également le **support multi-agent** avec une sélectio
|
||||||
{
|
{
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-your-openai-key"
|
"api_key": "sk-your-openai-key"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -850,7 +850,7 @@ Cette conception permet également le **support multi-agent** avec une sélectio
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
"defaults": {
|
"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**
|
**OpenAI**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-..."
|
"api_key": "sk-..."
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -894,14 +894,14 @@ Configurez plusieurs points de terminaison pour le même nom de modèle—PicoCl
|
||||||
{
|
{
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_key": "sk-key1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"api_base": "https://api2.example.com/v1",
|
||||||
"api_key": "sk-key2"
|
"api_key": "sk-key2"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
README.ja.md
18
README.ja.md
|
|
@ -769,8 +769,8 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る
|
||||||
{
|
{
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-your-openai-key"
|
"api_key": "sk-your-openai-key"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -786,7 +786,7 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"model": "gpt-4o"
|
"model": "gpt-5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -797,8 +797,8 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る
|
||||||
**OpenAI**
|
**OpenAI**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-..."
|
"api_key": "sk-..."
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -830,14 +830,14 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る
|
||||||
{
|
{
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_key": "sk-key1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"api_base": "https://api2.example.com/v1",
|
||||||
"api_key": "sk-key2"
|
"api_key": "sk-key2"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
README.md
20
README.md
|
|
@ -218,7 +218,7 @@ picoclaw onboard
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt4",
|
"model_name": "gpt4",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "your-api-key"
|
"api_key": "your-api-key"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -728,8 +728,8 @@ This design also enables **multi-agent support** with flexible provider selectio
|
||||||
{
|
{
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-your-openai-key"
|
"api_key": "sk-your-openai-key"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -745,7 +745,7 @@ This design also enables **multi-agent support** with flexible provider selectio
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"model": "gpt-4o"
|
"model": "gpt-5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -756,8 +756,8 @@ This design also enables **multi-agent support** with flexible provider selectio
|
||||||
**OpenAI**
|
**OpenAI**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-..."
|
"api_key": "sk-..."
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -816,14 +816,14 @@ Configure multiple endpoints for the same model name—PicoClaw will automatical
|
||||||
{
|
{
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_key": "sk-key1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"api_base": "https://api2.example.com/v1",
|
||||||
"api_key": "sk-key2"
|
"api_key": "sk-key2"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -834,8 +834,8 @@ Este design também possibilita o **suporte multi-agent** com seleção flexíve
|
||||||
{
|
{
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-your-openai-key"
|
"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": {
|
"agents": {
|
||||||
"defaults": {
|
"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**
|
**OpenAI**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-..."
|
"api_key": "sk-..."
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -895,14 +895,14 @@ Configure vários endpoints para o mesmo nome de modelo—PicoClaw fará round-r
|
||||||
{
|
{
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_key": "sk-key1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"api_base": "https://api2.example.com/v1",
|
||||||
"api_key": "sk-key2"
|
"api_key": "sk-key2"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
README.vi.md
18
README.vi.md
|
|
@ -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_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-your-openai-key"
|
"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": {
|
"agents": {
|
||||||
"defaults": {
|
"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**
|
**OpenAI**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-..."
|
"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_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_key": "sk-key1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"api_base": "https://api2.example.com/v1",
|
||||||
"api_key": "sk-key2"
|
"api_key": "sk-key2"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
README.zh.md
20
README.zh.md
|
|
@ -227,7 +227,7 @@ picoclaw onboard
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt4",
|
"model_name": "gpt4",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "your-api-key"
|
"api_key": "your-api-key"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -605,8 +605,8 @@ Agent 读取 HEARTBEAT.md
|
||||||
{
|
{
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-your-openai-key"
|
"api_key": "sk-your-openai-key"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -622,7 +622,7 @@ Agent 读取 HEARTBEAT.md
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"model": "gpt-4o"
|
"model": "gpt-5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -633,8 +633,8 @@ Agent 读取 HEARTBEAT.md
|
||||||
**OpenAI**
|
**OpenAI**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-..."
|
"api_key": "sk-..."
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -693,14 +693,14 @@ Agent 读取 HEARTBEAT.md
|
||||||
{
|
{
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_key": "sk-key1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"api_base": "https://api2.example.com/v1",
|
||||||
"api_key": "sk-key2"
|
"api_key": "sk-key2"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sipeed/picoclaw/pkg/auth"
|
"github.com/sipeed/picoclaw/pkg/auth"
|
||||||
|
|
@ -118,7 +119,31 @@ func authLoginOpenAI(useDeviceCode bool) {
|
||||||
|
|
||||||
appCfg, err := loadConfig()
|
appCfg, err := loadConfig()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
// Update Providers (legacy format)
|
||||||
appCfg.Providers.OpenAI.AuthMethod = "oauth"
|
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 {
|
if err := config.SaveConfig(getConfigPath(), appCfg); err != nil {
|
||||||
fmt.Printf("Warning: could not update config: %v\n", err)
|
fmt.Printf("Warning: could not update config: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
@ -128,6 +153,7 @@ func authLoginOpenAI(useDeviceCode bool) {
|
||||||
if cred.AccountID != "" {
|
if cred.AccountID != "" {
|
||||||
fmt.Printf("Account: %s\n", cred.AccountID)
|
fmt.Printf("Account: %s\n", cred.AccountID)
|
||||||
}
|
}
|
||||||
|
fmt.Println("Default model set to: gpt-5.2")
|
||||||
}
|
}
|
||||||
|
|
||||||
func authLoginGoogleAntigravity() {
|
func authLoginGoogleAntigravity() {
|
||||||
|
|
@ -167,20 +193,38 @@ func authLoginGoogleAntigravity() {
|
||||||
|
|
||||||
appCfg, err := loadConfig()
|
appCfg, err := loadConfig()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
// Update Providers (legacy format, for backward compatibility)
|
||||||
appCfg.Providers.Antigravity.AuthMethod = "oauth"
|
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 {
|
if err := config.SaveConfig(getConfigPath(), appCfg); err != nil {
|
||||||
fmt.Printf("Warning: could not update config: %v\n", err)
|
fmt.Printf("Warning: could not update config: %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("\n✓ Google Antigravity login successful!")
|
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\"")
|
fmt.Println("Try it: picoclaw agent -m \"Hello world\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,8 +273,44 @@ func authLoginPasteToken(provider string) {
|
||||||
switch provider {
|
switch provider {
|
||||||
case "anthropic":
|
case "anthropic":
|
||||||
appCfg.Providers.Anthropic.AuthMethod = "token"
|
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":
|
case "openai":
|
||||||
appCfg.Providers.OpenAI.AuthMethod = "token"
|
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 {
|
if err := config.SaveConfig(getConfigPath(), appCfg); err != nil {
|
||||||
fmt.Printf("Warning: could not update config: %v\n", err)
|
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("Token saved for %s!\n", provider)
|
||||||
|
fmt.Printf("Default model set to: %s\n", appCfg.Agents.Defaults.Model)
|
||||||
}
|
}
|
||||||
|
|
||||||
func authLogoutCmd() {
|
func authLogoutCmd() {
|
||||||
|
|
@ -262,6 +343,24 @@ func authLogoutCmd() {
|
||||||
|
|
||||||
appCfg, err := loadConfig()
|
appCfg, err := loadConfig()
|
||||||
if err == nil {
|
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 {
|
switch provider {
|
||||||
case "openai":
|
case "openai":
|
||||||
appCfg.Providers.OpenAI.AuthMethod = ""
|
appCfg.Providers.OpenAI.AuthMethod = ""
|
||||||
|
|
@ -282,6 +381,11 @@ func authLogoutCmd() {
|
||||||
|
|
||||||
appCfg, err := loadConfig()
|
appCfg, err := loadConfig()
|
||||||
if err == nil {
|
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.OpenAI.AuthMethod = ""
|
||||||
appCfg.Providers.Anthropic.AuthMethod = ""
|
appCfg.Providers.Anthropic.AuthMethod = ""
|
||||||
appCfg.Providers.Antigravity.AuthMethod = ""
|
appCfg.Providers.Antigravity.AuthMethod = ""
|
||||||
|
|
@ -384,3 +488,23 @@ func authModelsCmd() {
|
||||||
fmt.Printf(" %s %s\n", status, name)
|
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/")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt4",
|
"model_name": "gpt4",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-your-openai-key",
|
"api_key": "sk-your-openai-key",
|
||||||
"api_base": "https://api.openai.com/v1"
|
"api_base": "https://api.openai.com/v1"
|
||||||
},
|
},
|
||||||
|
|
@ -34,13 +34,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "loadbalanced-gpt4",
|
"model_name": "loadbalanced-gpt4",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-key1",
|
"api_key": "sk-key1",
|
||||||
"api_base": "https://api1.example.com/v1"
|
"api_base": "https://api1.example.com/v1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "loadbalanced-gpt4",
|
"model_name": "loadbalanced-gpt4",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-key2",
|
"api_key": "sk-key2",
|
||||||
"api_base": "https://api2.example.com/v1"
|
"api_base": "https://api2.example.com/v1"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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:
|
Inspired by [LiteLLM](https://docs.litellm.ai/docs/proxy/configs) design:
|
||||||
|
|
||||||
1. **Model-centric**: Users care about models, not providers
|
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
|
3. **Configuration-driven**: Adding new Providers only requires config changes, no code changes
|
||||||
|
|
||||||
### 2.2 New Configuration Structure
|
### 2.2 New Configuration Structure
|
||||||
|
|
@ -81,8 +81,8 @@ Inspired by [LiteLLM](https://docs.litellm.ai/docs/proxy/configs) design:
|
||||||
"api_key": "sk-xxx"
|
"api_key": "sk-xxx"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-4o",
|
"model_name": "gpt-5.2",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-xxx"
|
"api_key": "sk-xxx"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -128,7 +128,7 @@ type Config struct {
|
||||||
type ModelConfig struct {
|
type ModelConfig struct {
|
||||||
// Required
|
// Required
|
||||||
ModelName string `json:"model_name"` // user-facing name (alias)
|
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
|
// Common config
|
||||||
APIBase string `json:"api_base,omitempty"`
|
APIBase string `json:"api_base,omitempty"`
|
||||||
|
|
@ -180,7 +180,7 @@ Identify protocol via prefix in `model` field:
|
||||||
"model": "deepseek-chat"
|
"model": "deepseek-chat"
|
||||||
},
|
},
|
||||||
"coder": {
|
"coder": {
|
||||||
"model": "gpt-4o",
|
"model": "gpt-5.2",
|
||||||
"system_prompt": "You are a coding assistant..."
|
"system_prompt": "You are a coding assistant..."
|
||||||
},
|
},
|
||||||
"translator": {
|
"translator": {
|
||||||
|
|
@ -200,7 +200,7 @@ Each Agent only needs to specify `model` (corresponds to `model_name` in `model_
|
||||||
model_list:
|
model_list:
|
||||||
- model_name: gpt-4o
|
- model_name: gpt-4o
|
||||||
litellm_params:
|
litellm_params:
|
||||||
model: openai/gpt-4o
|
model: openai/gpt-5.2
|
||||||
api_key: xxx
|
api_key: xxx
|
||||||
- model_name: my-custom
|
- model_name: my-custom
|
||||||
litellm_params:
|
litellm_params:
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ The new `model_list` configuration offers several advantages:
|
||||||
"agents": {
|
"agents": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"provider": "openai",
|
"provider": "openai",
|
||||||
"model": "gpt-4o"
|
"model": "gpt-5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +53,7 @@ The new `model_list` configuration offers several advantages:
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt4",
|
"model_name": "gpt4",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-your-openai-key",
|
"api_key": "sk-your-openai-key",
|
||||||
"api_base": "https://api.openai.com/v1"
|
"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 |
|
| 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` |
|
| `anthropic/` | Anthropic API | `anthropic/claude-3-opus` |
|
||||||
| `antigravity/` | Google via Antigravity OAuth | `antigravity/gemini-2.0-flash` |
|
| `antigravity/` | Google via Antigravity OAuth | `antigravity/gemini-2.0-flash` |
|
||||||
| `claude-cli/` | Claude CLI (local) | `claude-cli/claude-3-sonnet` |
|
| `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 |
|
| Field | Required | Description |
|
||||||
|-------|----------|-------------|
|
|-------|----------|-------------|
|
||||||
| `model_name` | Yes | User-facing alias for the model |
|
| `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_base` | No | API endpoint URL |
|
||||||
| `api_key` | No* | API authentication key |
|
| `api_key` | No* | API authentication key |
|
||||||
| `proxy` | No | HTTP proxy URL |
|
| `proxy` | No | HTTP proxy URL |
|
||||||
|
|
@ -121,19 +121,19 @@ Configure multiple endpoints for the same model to distribute load:
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt4",
|
"model_name": "gpt4",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-key1",
|
"api_key": "sk-key1",
|
||||||
"api_base": "https://api1.example.com/v1"
|
"api_base": "https://api1.example.com/v1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt4",
|
"model_name": "gpt4",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-key2",
|
"api_key": "sk-key2",
|
||||||
"api_base": "https://api2.example.com/v1"
|
"api_base": "https://api2.example.com/v1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt4",
|
"model_name": "gpt4",
|
||||||
"model": "openai/gpt-4o",
|
"model": "openai/gpt-5.2",
|
||||||
"api_key": "sk-key3",
|
"api_key": "sk-key3",
|
||||||
"api_base": "https://api3.example.com/v1"
|
"api_base": "https://api3.example.com/v1"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,8 +105,8 @@ func DefaultConfig() *Config {
|
||||||
|
|
||||||
// OpenAI - https://platform.openai.com/api-keys
|
// OpenAI - https://platform.openai.com/api-keys
|
||||||
{
|
{
|
||||||
ModelName: "gpt-4o",
|
ModelName: "gpt-5.2",
|
||||||
Model: "openai/gpt-4o",
|
Model: "openai/gpt-5.2",
|
||||||
APIBase: "https://api.openai.com/v1",
|
APIBase: "https://api.openai.com/v1",
|
||||||
APIKey: "",
|
APIKey: "",
|
||||||
},
|
},
|
||||||
|
|
@ -161,8 +161,8 @@ func DefaultConfig() *Config {
|
||||||
|
|
||||||
// OpenRouter (100+ models) - https://openrouter.ai/keys
|
// OpenRouter (100+ models) - https://openrouter.ai/keys
|
||||||
{
|
{
|
||||||
ModelName: "openrouter-gpt-4o",
|
ModelName: "openrouter-gpt-5.2",
|
||||||
Model: "openrouter/openai/gpt-4o",
|
Model: "openrouter/openai/gpt-5.2",
|
||||||
APIBase: "https://openrouter.ai/api/v1",
|
APIBase: "https://openrouter.ai/api/v1",
|
||||||
APIKey: "",
|
APIKey: "",
|
||||||
},
|
},
|
||||||
|
|
@ -208,8 +208,8 @@ func DefaultConfig() *Config {
|
||||||
|
|
||||||
// GitHub Copilot - https://github.com/settings/tokens
|
// GitHub Copilot - https://github.com/settings/tokens
|
||||||
{
|
{
|
||||||
ModelName: "copilot-gpt-4o",
|
ModelName: "copilot-gpt-5.2",
|
||||||
Model: "github-copilot/gpt-4o",
|
Model: "github-copilot/gpt-5.2",
|
||||||
APIBase: "http://localhost:4321",
|
APIBase: "http://localhost:4321",
|
||||||
AuthMethod: "oauth",
|
AuthMethod: "oauth",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ func ConvertProvidersToModelList(cfg *Config) []ModelConfig {
|
||||||
}
|
}
|
||||||
return ModelConfig{
|
return ModelConfig{
|
||||||
ModelName: "openai",
|
ModelName: "openai",
|
||||||
Model: "openai/gpt-4o",
|
Model: "openai/gpt-5.2",
|
||||||
APIKey: p.OpenAI.APIKey,
|
APIKey: p.OpenAI.APIKey,
|
||||||
APIBase: p.OpenAI.APIBase,
|
APIBase: p.OpenAI.APIBase,
|
||||||
Proxy: p.OpenAI.Proxy,
|
Proxy: p.OpenAI.Proxy,
|
||||||
|
|
@ -276,7 +276,7 @@ func ConvertProvidersToModelList(cfg *Config) []ModelConfig {
|
||||||
}
|
}
|
||||||
return ModelConfig{
|
return ModelConfig{
|
||||||
ModelName: "github-copilot",
|
ModelName: "github-copilot",
|
||||||
Model: "github-copilot/gpt-4o",
|
Model: "github-copilot/gpt-5.2",
|
||||||
APIBase: p.GitHubCopilot.APIBase,
|
APIBase: p.GitHubCopilot.APIBase,
|
||||||
ConnectMode: p.GitHubCopilot.ConnectMode,
|
ConnectMode: p.GitHubCopilot.ConnectMode,
|
||||||
}, true
|
}, true
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ func TestConvertProvidersToModelList_OpenAI(t *testing.T) {
|
||||||
if result[0].ModelName != "openai" {
|
if result[0].ModelName != "openai" {
|
||||||
t.Errorf("ModelName = %q, want %q", result[0].ModelName, "openai")
|
t.Errorf("ModelName = %q, want %q", result[0].ModelName, "openai")
|
||||||
}
|
}
|
||||||
if 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-4o")
|
t.Errorf("Model = %q, want %q", result[0].Model, "openai/gpt-5.2")
|
||||||
}
|
}
|
||||||
if result[0].APIKey != "sk-test-key" {
|
if result[0].APIKey != "sk-test-key" {
|
||||||
t.Errorf("APIKey = %q, want %q", 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 {
|
for _, mc := range result {
|
||||||
switch mc.ModelName {
|
switch mc.ModelName {
|
||||||
case "openai":
|
case "openai":
|
||||||
if mc.Model != "openai/gpt-4o" {
|
if mc.Model != "openai/gpt-5.2" {
|
||||||
t.Errorf("OpenAI Model = %q, want %q (default)", mc.Model, "openai/gpt-4o")
|
t.Errorf("OpenAI Model = %q, want %q (default)", mc.Model, "openai/gpt-5.2")
|
||||||
}
|
}
|
||||||
case "deepseek":
|
case "deepseek":
|
||||||
if mc.Model != "deepseek/deepseek-reasoner" {
|
if mc.Model != "deepseek/deepseek-reasoner" {
|
||||||
|
|
|
||||||
|
|
@ -67,10 +67,29 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err
|
||||||
protocol, modelID := ExtractProtocol(cfg.Model)
|
protocol, modelID := ExtractProtocol(cfg.Model)
|
||||||
|
|
||||||
switch protocol {
|
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",
|
"ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras",
|
||||||
"volcengine", "vllm", "qwen":
|
"volcengine", "vllm", "qwen":
|
||||||
// All OpenAI-compatible HTTP providers
|
// All other OpenAI-compatible HTTP providers
|
||||||
if cfg.APIKey == "" && cfg.APIBase == "" {
|
if cfg.APIKey == "" && cfg.APIBase == "" {
|
||||||
return nil, "", fmt.Errorf("api_key or api_base is required for HTTP-based protocol %q", protocol)
|
return nil, "", fmt.Errorf("api_key or api_base is required for HTTP-based protocol %q", protocol)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue