fix ci
This commit is contained in:
parent
80e0ff8393
commit
da7b5d499c
4 changed files with 66 additions and 134 deletions
|
|
@ -99,6 +99,7 @@ func createCodexAuthProvider() (LLMProvider, error) {
|
||||||
// - Model "openai/gpt-4o" -> ("openai", "gpt-4o")
|
// - Model "openai/gpt-4o" -> ("openai", "gpt-4o")
|
||||||
// - Model "nvidia/z-ai/glm-5.1" -> ("nvidia", "z-ai/glm-5.1")
|
// - Model "nvidia/z-ai/glm-5.1" -> ("nvidia", "z-ai/glm-5.1")
|
||||||
// - Provider "nvidia", Model "z-ai/glm-5.1" -> ("nvidia", "z-ai/glm-5.1")
|
// - Provider "nvidia", Model "z-ai/glm-5.1" -> ("nvidia", "z-ai/glm-5.1")
|
||||||
|
// - Provider "openai", Model "openai/gpt-4o" -> ("openai", "openai/gpt-4o")
|
||||||
// - Model "gpt-4o" -> ("openai", "gpt-4o")
|
// - Model "gpt-4o" -> ("openai", "gpt-4o")
|
||||||
func ExtractProtocol(cfg *config.ModelConfig) (protocol, modelID string) {
|
func ExtractProtocol(cfg *config.ModelConfig) (protocol, modelID string) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
|
|
@ -107,13 +108,7 @@ func ExtractProtocol(cfg *config.ModelConfig) (protocol, modelID string) {
|
||||||
|
|
||||||
model := strings.TrimSpace(cfg.Model)
|
model := strings.TrimSpace(cfg.Model)
|
||||||
if provider := strings.TrimSpace(cfg.Provider); provider != "" {
|
if provider := strings.TrimSpace(cfg.Provider); provider != "" {
|
||||||
normalized := NormalizeProvider(provider)
|
return NormalizeProvider(provider), model
|
||||||
if prefix, rest, found := strings.Cut(model, "/"); found {
|
|
||||||
if NormalizeProvider(prefix) == normalized && strings.TrimSpace(rest) != "" {
|
|
||||||
return normalized, strings.TrimSpace(rest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return normalized, model
|
|
||||||
}
|
}
|
||||||
if model == "" {
|
if model == "" {
|
||||||
return "", ""
|
return "", ""
|
||||||
|
|
|
||||||
|
|
@ -84,16 +84,16 @@ func TestExtractProtocol(t *testing.T) {
|
||||||
wantModelID: "z-ai/glm-5.1",
|
wantModelID: "z-ai/glm-5.1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "explicit provider strips redundant matching prefix",
|
name: "explicit provider preserves matching prefix",
|
||||||
config: &config.ModelConfig{Provider: "openai", Model: "openai/gpt-4o"},
|
config: &config.ModelConfig{Provider: "openai", Model: "openai/gpt-4o"},
|
||||||
wantProtocol: "openai",
|
wantProtocol: "openai",
|
||||||
wantModelID: "gpt-4o",
|
wantModelID: "openai/gpt-4o",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "explicit provider strips redundant aliased prefix",
|
name: "explicit provider preserves aliased prefix",
|
||||||
config: &config.ModelConfig{Provider: "qwen", Model: "qwen/qwen-plus"},
|
config: &config.ModelConfig{Provider: "qwen", Model: "qwen/qwen-plus"},
|
||||||
wantProtocol: "qwen-portal",
|
wantProtocol: "qwen-portal",
|
||||||
wantModelID: "qwen-plus",
|
wantModelID: "qwen/qwen-plus",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty provider segment",
|
name: "empty provider segment",
|
||||||
|
|
@ -164,7 +164,7 @@ func TestCreateProviderFromConfig_UsesExplicitProvider(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateProviderFromConfig_StripsRedundantExplicitProviderPrefix(t *testing.T) {
|
func TestCreateProviderFromConfig_PreservesExplicitProviderPrefixedModel(t *testing.T) {
|
||||||
cfg := &config.ModelConfig{
|
cfg := &config.ModelConfig{
|
||||||
ModelName: "test-openai",
|
ModelName: "test-openai",
|
||||||
Provider: "openai",
|
Provider: "openai",
|
||||||
|
|
@ -180,8 +180,8 @@ func TestCreateProviderFromConfig_StripsRedundantExplicitProviderPrefix(t *testi
|
||||||
if provider == nil {
|
if provider == nil {
|
||||||
t.Fatal("CreateProviderFromConfig() returned nil provider")
|
t.Fatal("CreateProviderFromConfig() returned nil provider")
|
||||||
}
|
}
|
||||||
if modelID != "gpt-4o" {
|
if modelID != "openai/gpt-4o" {
|
||||||
t.Fatalf("modelID = %q, want %q", modelID, "gpt-4o")
|
t.Fatalf("modelID = %q, want %q", modelID, "openai/gpt-4o")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,27 +51,6 @@ type modelResponse struct {
|
||||||
IsVirtual bool `json:"is_virtual"`
|
IsVirtual bool `json:"is_virtual"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeExplicitProviderModel(mc *config.ModelConfig) {
|
|
||||||
if mc == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
provider := strings.TrimSpace(mc.Provider)
|
|
||||||
model := strings.TrimSpace(mc.Model)
|
|
||||||
if provider == "" || model == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix, rest, found := strings.Cut(model, "/")
|
|
||||||
if !found || strings.TrimSpace(rest) == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if providers.NormalizeProvider(prefix) == providers.NormalizeProvider(provider) {
|
|
||||||
mc.Model = strings.TrimSpace(rest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleListModels returns all model_list entries with masked API keys.
|
// handleListModels returns all model_list entries with masked API keys.
|
||||||
//
|
//
|
||||||
// GET /api/models
|
// GET /api/models
|
||||||
|
|
@ -161,7 +140,6 @@ func (h *Handler) handleAddModel(w http.ResponseWriter, r *http.Request) {
|
||||||
if mc.APIKey != "" {
|
if mc.APIKey != "" {
|
||||||
mc.ModelConfig.SetAPIKey(mc.APIKey)
|
mc.ModelConfig.SetAPIKey(mc.APIKey)
|
||||||
}
|
}
|
||||||
normalizeExplicitProviderModel(&mc.ModelConfig)
|
|
||||||
|
|
||||||
cfg, err := config.LoadConfig(h.configPath)
|
cfg, err := config.LoadConfig(h.configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -288,7 +266,6 @@ func (h *Handler) handleUpdateModel(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
normalizeExplicitProviderModel(&mc.ModelConfig)
|
|
||||||
|
|
||||||
cfg.ModelList[idx] = &mc.ModelConfig
|
cfg.ModelList[idx] = &mc.ModelConfig
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -508,7 +508,7 @@ func TestHandleAddModel_PersistsProvider(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleAddModel_NormalizesRedundantExplicitProviderPrefix(t *testing.T) {
|
func TestHandleAddModel_PreservesExplicitProviderPrefixedModel(t *testing.T) {
|
||||||
configPath, cleanup := setupOAuthTestEnv(t)
|
configPath, cleanup := setupOAuthTestEnv(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
|
|
@ -538,8 +538,8 @@ func TestHandleAddModel_NormalizesRedundantExplicitProviderPrefix(t *testing.T)
|
||||||
if got := added.Provider; got != "openai" {
|
if got := added.Provider; got != "openai" {
|
||||||
t.Fatalf("provider = %q, want %q", got, "openai")
|
t.Fatalf("provider = %q, want %q", got, "openai")
|
||||||
}
|
}
|
||||||
if got := added.Model; got != "gpt-4o-mini" {
|
if got := added.Model; got != "openai/gpt-4o-mini" {
|
||||||
t.Fatalf("model = %q, want %q", got, "gpt-4o-mini")
|
t.Fatalf("model = %q, want %q", got, "openai/gpt-4o-mini")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -662,7 +662,7 @@ func TestHandleUpdateModel_PersistsProvider(t *testing.T) {
|
||||||
Model: "gpt-4o",
|
Model: "gpt-4o",
|
||||||
Provider: "openai",
|
Provider: "openai",
|
||||||
}}
|
}}
|
||||||
if err := config.SaveConfig(configPath, cfg); err != nil {
|
if err = config.SaveConfig(configPath, cfg); err != nil {
|
||||||
t.Fatalf("SaveConfig() error = %v", err)
|
t.Fatalf("SaveConfig() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -692,49 +692,7 @@ func TestHandleUpdateModel_PersistsProvider(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleUpdateModel_PreservesProviderWhenOmitted(t *testing.T) {
|
func TestHandleUpdateModel_PreservesExplicitProviderPrefixedModel(t *testing.T) {
|
||||||
configPath, cleanup := setupOAuthTestEnv(t)
|
|
||||||
defer cleanup()
|
|
||||||
|
|
||||||
cfg, err := config.LoadConfig(configPath)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("LoadConfig() error = %v", err)
|
|
||||||
}
|
|
||||||
cfg.ModelList = []*config.ModelConfig{{
|
|
||||||
ModelName: "editable",
|
|
||||||
Model: "z-ai/glm-5.1",
|
|
||||||
Provider: "nvidia",
|
|
||||||
}}
|
|
||||||
if err := config.SaveConfig(configPath, cfg); err != nil {
|
|
||||||
t.Fatalf("SaveConfig() error = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
h := NewHandler(configPath)
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
h.RegisterRoutes(mux)
|
|
||||||
|
|
||||||
rec := httptest.NewRecorder()
|
|
||||||
req := httptest.NewRequest(http.MethodPut, "/api/models/0", bytes.NewBufferString(`{
|
|
||||||
"model_name":"editable",
|
|
||||||
"model":"z-ai/glm-5.1"
|
|
||||||
}`))
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
mux.ServeHTTP(rec, req)
|
|
||||||
|
|
||||||
if rec.Code != http.StatusOK {
|
|
||||||
t.Fatalf("status = %d, want %d, body=%s", rec.Code, http.StatusOK, rec.Body.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
updated, err := config.LoadConfig(configPath)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("LoadConfig() error = %v", err)
|
|
||||||
}
|
|
||||||
if got := updated.ModelList[0].Provider; got != "nvidia" {
|
|
||||||
t.Fatalf("provider = %q, want %q", got, "nvidia")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHandleUpdateModel_NormalizesRedundantExplicitProviderPrefix(t *testing.T) {
|
|
||||||
configPath, cleanup := setupOAuthTestEnv(t)
|
configPath, cleanup := setupOAuthTestEnv(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
|
|
@ -747,7 +705,7 @@ func TestHandleUpdateModel_NormalizesRedundantExplicitProviderPrefix(t *testing.
|
||||||
Model: "gpt-4o",
|
Model: "gpt-4o",
|
||||||
Provider: "openai",
|
Provider: "openai",
|
||||||
}}
|
}}
|
||||||
if err := config.SaveConfig(configPath, cfg); err != nil {
|
if err = config.SaveConfig(configPath, cfg); err != nil {
|
||||||
t.Fatalf("SaveConfig() error = %v", err)
|
t.Fatalf("SaveConfig() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -775,8 +733,54 @@ func TestHandleUpdateModel_NormalizesRedundantExplicitProviderPrefix(t *testing.
|
||||||
if got := updated.ModelList[0].Provider; got != "openai" {
|
if got := updated.ModelList[0].Provider; got != "openai" {
|
||||||
t.Fatalf("provider = %q, want %q", got, "openai")
|
t.Fatalf("provider = %q, want %q", got, "openai")
|
||||||
}
|
}
|
||||||
if got := updated.ModelList[0].Model; got != "gpt-5.4" {
|
if got := updated.ModelList[0].Model; got != "openai/gpt-5.4" {
|
||||||
t.Fatalf("model = %q, want %q", got, "gpt-5.4")
|
t.Fatalf("model = %q, want %q", got, "openai/gpt-5.4")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleListModels_PreservesExplicitProviderPrefixedModel(t *testing.T) {
|
||||||
|
configPath, cleanup := setupOAuthTestEnv(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
cfg, err := config.LoadConfig(configPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("LoadConfig() error = %v", err)
|
||||||
|
}
|
||||||
|
cfg.ModelList = []*config.ModelConfig{{
|
||||||
|
ModelName: "openrouter-auto-explicit",
|
||||||
|
Provider: "openrouter",
|
||||||
|
Model: "openrouter/auto",
|
||||||
|
}}
|
||||||
|
if err := config.SaveConfig(configPath, cfg); err != nil {
|
||||||
|
t.Fatalf("SaveConfig() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := NewHandler(configPath)
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
h.RegisterRoutes(mux)
|
||||||
|
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/api/models", nil)
|
||||||
|
mux.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("status = %d, want %d, body=%s", rec.Code, http.StatusOK, rec.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp struct {
|
||||||
|
Models []modelResponse `json:"models"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatalf("Unmarshal() error = %v", err)
|
||||||
|
}
|
||||||
|
if len(resp.Models) != 1 {
|
||||||
|
t.Fatalf("len(models) = %d, want 1", len(resp.Models))
|
||||||
|
}
|
||||||
|
if got := resp.Models[0].Provider; got != "openrouter" {
|
||||||
|
t.Fatalf("provider = %q, want %q", got, "openrouter")
|
||||||
|
}
|
||||||
|
if got := resp.Models[0].Model; got != "openrouter/auto" {
|
||||||
|
t.Fatalf("model = %q, want %q", got, "openrouter/auto")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -792,7 +796,7 @@ func TestHandleUpdateModel_PreservesLegacyModelPrefixWhenProviderOmitted(t *test
|
||||||
ModelName: "legacy-openrouter",
|
ModelName: "legacy-openrouter",
|
||||||
Model: "openrouter/openai/gpt-5.4",
|
Model: "openrouter/openai/gpt-5.4",
|
||||||
}}
|
}}
|
||||||
if err := config.SaveConfig(configPath, cfg); err != nil {
|
if err = config.SaveConfig(configPath, cfg); err != nil {
|
||||||
t.Fatalf("SaveConfig() error = %v", err)
|
t.Fatalf("SaveConfig() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -813,7 +817,7 @@ func TestHandleUpdateModel_PreservesLegacyModelPrefixWhenProviderOmitted(t *test
|
||||||
var listResp struct {
|
var listResp struct {
|
||||||
Models []modelResponse `json:"models"`
|
Models []modelResponse `json:"models"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(recList.Body.Bytes(), &listResp); err != nil {
|
if err = json.Unmarshal(recList.Body.Bytes(), &listResp); err != nil {
|
||||||
t.Fatalf("Unmarshal() error = %v", err)
|
t.Fatalf("Unmarshal() error = %v", err)
|
||||||
}
|
}
|
||||||
if len(listResp.Models) != 1 {
|
if len(listResp.Models) != 1 {
|
||||||
|
|
@ -862,7 +866,7 @@ func TestHandleUpdateModel_PreservesLegacyModelPrefixWhenProviderOmittedAndModel
|
||||||
ModelName: "legacy-openrouter",
|
ModelName: "legacy-openrouter",
|
||||||
Model: "openrouter/openai/gpt-5.4",
|
Model: "openrouter/openai/gpt-5.4",
|
||||||
}}
|
}}
|
||||||
if err := config.SaveConfig(configPath, cfg); err != nil {
|
if err = config.SaveConfig(configPath, cfg); err != nil {
|
||||||
t.Fatalf("SaveConfig() error = %v", err)
|
t.Fatalf("SaveConfig() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -894,50 +898,6 @@ func TestHandleUpdateModel_PreservesLegacyModelPrefixWhenProviderOmittedAndModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleUpdateModel_OmittedProviderAllowsLegacyProviderChangeForSimpleVisibleModel(t *testing.T) {
|
|
||||||
configPath, cleanup := setupOAuthTestEnv(t)
|
|
||||||
defer cleanup()
|
|
||||||
|
|
||||||
cfg, err := config.LoadConfig(configPath)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("LoadConfig() error = %v", err)
|
|
||||||
}
|
|
||||||
cfg.ModelList = []*config.ModelConfig{{
|
|
||||||
ModelName: "legacy-openai",
|
|
||||||
Model: "openai/gpt-5.4",
|
|
||||||
}}
|
|
||||||
if err := config.SaveConfig(configPath, cfg); err != nil {
|
|
||||||
t.Fatalf("SaveConfig() error = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
h := NewHandler(configPath)
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
h.RegisterRoutes(mux)
|
|
||||||
|
|
||||||
rec := httptest.NewRecorder()
|
|
||||||
req := httptest.NewRequest(http.MethodPut, "/api/models/0", bytes.NewBufferString(`{
|
|
||||||
"model_name":"legacy-openai",
|
|
||||||
"model":"anthropic/claude-sonnet-4.6"
|
|
||||||
}`))
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
mux.ServeHTTP(rec, req)
|
|
||||||
|
|
||||||
if rec.Code != http.StatusOK {
|
|
||||||
t.Fatalf("status = %d, want %d, body=%s", rec.Code, http.StatusOK, rec.Body.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
updated, err := config.LoadConfig(configPath)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("LoadConfig() error = %v", err)
|
|
||||||
}
|
|
||||||
if got := updated.ModelList[0].Provider; got != "" {
|
|
||||||
t.Fatalf("provider = %q, want empty", got)
|
|
||||||
}
|
|
||||||
if got := updated.ModelList[0].Model; got != "anthropic/claude-sonnet-4.6" {
|
|
||||||
t.Fatalf("model = %q, want %q", got, "anthropic/claude-sonnet-4.6")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHandleListModels_ReturnsProviderField(t *testing.T) {
|
func TestHandleListModels_ReturnsProviderField(t *testing.T) {
|
||||||
configPath, cleanup := setupOAuthTestEnv(t)
|
configPath, cleanup := setupOAuthTestEnv(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue