Skip to content

Commit 572e827

Browse files
committed
Release clovapi v0.2.1 updates
1 parent 4ad776c commit 572e827

40 files changed

Lines changed: 1020 additions & 475 deletions

core/cmd/cli_smoke_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ func TestCLISmokeProfilesLoadSaveRoundtrip(t *testing.T) {
164164
"id": "smoke-model",
165165
"label": "Smoke Model",
166166
"model": "smoke-model",
167-
"apiStyle": "openai-chat",
167+
"apiStyle": "chat",
168168
},
169169
},
170170
},

core/cmd/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ func cmdSet() *cobra.Command {
180180
},
181181
}
182182
c.Flags().StringVar(&name, "name", "", "Profile name (required)")
183-
c.Flags().StringVar(&styleStr, "api-style", "", "API style: claude|openai-chat|openai-responses|gemini (alias openai鈫抩penai-responses)")
183+
c.Flags().StringVar(&styleStr, "api-style", "", "API style: chat|responses|message|gemini (legacy aliases: openai-chat|openai-responses|claude|openai)")
184184
c.Flags().StringVar(&baseURL, "base-url", "", "Upstream base URL")
185185
c.Flags().StringVar(&apiKey, "api-key", "", "API key (or prompt)")
186186
c.Flags().StringVar(&model, "model", "", "Default model id (required; used for connectivity test)")

core/internal/apistyle/apistyle.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,37 @@ import (
99
type Style string
1010

1111
const (
12-
Claude Style = "claude"
13-
OpenAIChat Style = "openai-chat" // POST /v1/chat/completions
14-
OpenAIResponses Style = "openai-responses" // POST /v1/responses (Codex wire_api)
12+
Claude Style = "message" // POST .../v1/messages (Anthropic Messages)
13+
OpenAIChat Style = "chat" // POST .../v1/chat/completions
14+
OpenAIResponses Style = "responses" // POST .../v1/responses (Codex wire_api)
1515
Gemini Style = "gemini"
1616
)
1717

18-
// Parse accepts claude, openai-chat, openai-responses, gemini, and legacy alias openai → openai-responses.
18+
// Parse accepts canonical styles (message, chat, responses, gemini) plus legacy aliases.
1919
func Parse(s string) (Style, error) {
2020
switch strings.ToLower(strings.TrimSpace(s)) {
2121
case string(Claude):
2222
return Claude, nil
23+
case "claude", "anthropic", "messages":
24+
return Claude, nil
2325
case string(OpenAIChat):
2426
return OpenAIChat, nil
27+
case "openai-chat":
28+
return OpenAIChat, nil
2529
case string(OpenAIResponses):
2630
return OpenAIResponses, nil
27-
case "openai":
31+
case "openai", "openai-responses":
2832
return OpenAIResponses, nil
2933
case string(Gemini):
3034
return Gemini, nil
3135
default:
32-
return "", fmt.Errorf("unknown api style %q (want claude|openai-chat|openai-responses|gemini; alias openaiopenai-responses)", s)
36+
return "", fmt.Errorf("unknown api style %q (want chat|responses|message|gemini; legacy aliases: openai-chat|openai-responses|claude|openai)", s)
3337
}
3438
}
3539

3640
func (s Style) String() string { return string(s) }
3741

3842
// All lists styles shown in help / matrices (stable order).
3943
func All() []Style {
40-
return []Style{Claude, OpenAIChat, OpenAIResponses, Gemini}
44+
return []Style{OpenAIChat, OpenAIResponses, Claude, Gemini}
4145
}

core/internal/buildinfo/buildinfo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import "strings"
44

55
// Set at link time via -ldflags (see .goreleaser.yaml).
66
var (
7-
Version = "dev0.2.1"
7+
Version = "dev0.2.13"
88
Commit = "none"
99
Date = "unknown"
1010
)

core/internal/desktop/models.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -828,19 +828,19 @@ func ListVendorModels(vendorName string) ListModelsResult {
828828
if adapterID == "" {
829829
adapterID = "openai-compatible"
830830
}
831-
defaultStyle := "openai-chat"
831+
defaultStyle := "chat"
832832
switch adapterID {
833833
case "manual":
834834
return ListModelsResult{OK: false, Error: "手动维护供应商不支持自动拉取模型"}
835835
case "openai-compatible":
836-
defaultStyle = "openai-chat"
836+
defaultStyle = "chat"
837837
case "ollama":
838-
defaultStyle = "openai-chat"
838+
defaultStyle = "chat"
839839
case "subscription":
840840
if strings.TrimSpace(vendor.SubscriptionProviderID) == provider.CodexProviderID {
841-
defaultStyle = "openai-responses"
841+
defaultStyle = "responses"
842842
} else {
843-
defaultStyle = "claude"
843+
defaultStyle = "message"
844844
}
845845
}
846846

core/internal/desktop/models_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ func TestParseCodexSubscriptionModelsSupportsCurrentShapes(t *testing.T) {
256256
t.Fatalf("expected displayName label, got %q", models[0].Label)
257257
}
258258
if models[0].APIStyle != apistyle.OpenAIResponses {
259-
t.Fatalf("expected openai-responses api style, got %q", models[0].APIStyle)
259+
t.Fatalf("expected responses api style, got %q", models[0].APIStyle)
260260
}
261261
}
262262

core/internal/desktop/test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ type TestResult struct {
2424
Error string `json:"error,omitempty"`
2525
}
2626

27+
const defaultModelTestAPIKey = "clovapi-test"
28+
2729
func proxyConfigForTest(s *profile.Store, portOverride int) profile.ProxyConfig {
2830
cfg := profile.ProxyConfig{Enabled: true, Host: profile.DefaultProxyHost, Port: profile.DefaultProxyPort}
2931
if s != nil {
@@ -149,7 +151,7 @@ func TestProviderModel(providerID, modelID string, portOverride int) TestResult
149151
responsesBaseURL := provider.BuildProxyIngressBaseURL(proxyCfg.Port, providerID)
150152
claudeBaseURL := provider.BuildProxyIngressBaseURL(proxyCfg.Port, providerID)
151153

152-
if err := testclient.ProbeToolRoundTrip(responsesBaseURL, claudeBaseURL, "clovapi-local", modelWire); err != nil {
154+
if err := testclient.ProbeToolRoundTrip(responsesBaseURL, claudeBaseURL, defaultModelTestAPIKey, modelWire); err != nil {
153155
return TestResult{
154156
OK: true, Passed: false, Summary: "Test failed",
155157
Text: fmt.Sprintf("connectivity probe failed: %v", err),
@@ -158,6 +160,6 @@ func TestProviderModel(providerID, modelID string, portOverride int) TestResult
158160
}
159161
return TestResult{
160162
OK: true, Passed: true, Summary: "Test passed",
161-
Text: fmt.Sprintf("Local proxy probe passed for %s/%s (%s): openai-responses + anthropic messages.", providerID, pathModelID, modelWire),
163+
Text: fmt.Sprintf("Local proxy probe passed for %s/%s (%s): responses + message.", providerID, pathModelID, modelWire),
162164
}
163165
}

core/internal/profile/desktop.go

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,9 @@ var subscriptionVendorDefs = []struct {
2525
{SubscriptionProviderID: provider.CodexProviderID, Name: provider.CodexVendorName},
2626
}
2727

28-
var allowedAPIStyles = map[string]apistyle.Style{
29-
"claude": apistyle.Claude,
30-
"openai-chat": apistyle.OpenAIChat,
31-
"openai-responses": apistyle.OpenAIResponses,
32-
"gemini": apistyle.Gemini,
33-
}
34-
3528
// NormalizeAPIStyle maps legacy/desktop style strings to canonical apistyle values.
3629
func NormalizeAPIStyle(raw string) apistyle.Style {
37-
s := strings.ToLower(strings.TrimSpace(raw))
38-
if s == "openai" {
39-
return apistyle.OpenAIResponses
40-
}
41-
if s == "anthropic" {
42-
return apistyle.Claude
43-
}
44-
if st, ok := allowedAPIStyles[s]; ok {
30+
if st, err := apistyle.Parse(raw); err == nil {
4531
return st
4632
}
4733
return apistyle.OpenAIResponses

core/internal/profile/store.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"slices"
1111
"strings"
1212

13-
"github.com/clovapi/switcher/internal/apistyle"
1413
cfgpkg "github.com/clovapi/switcher/internal/config"
1514
"github.com/clovapi/switcher/internal/syslog"
1615
)
@@ -113,11 +112,12 @@ func migrateLegacyCurrentIntoProfiles(raw []byte, s *Store) {
113112
s.List = append(s.List, cur)
114113
}
115114

116-
// migrateLegacyAPIStyles maps pre-split JSON api_style "openai" to openai-responses (Codex wire_api).
115+
// migrateLegacyAPIStyles maps older JSON api_style names to canonical styles.
117116
func migrateLegacyAPIStyles(s *Store) {
118117
for i := range s.List {
119-
if string(s.List[i].APIStyle) == "openai" {
120-
s.List[i].APIStyle = apistyle.OpenAIResponses
118+
s.List[i].APIStyle = NormalizeAPIStyle(string(s.List[i].APIStyle))
119+
for j := range s.List[i].Models {
120+
s.List[i].Models[j].APIStyle = NormalizeAPIStyle(string(s.List[i].Models[j].APIStyle))
121121
}
122122
}
123123
}

core/internal/provider/registry.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"net/url"
55
"strconv"
66
"strings"
7+
8+
"github.com/clovapi/switcher/internal/apistyle"
79
)
810

911
const (
@@ -130,14 +132,18 @@ func parseLegacyProxyIngressParts(parts []string) (Ingress, bool) {
130132
if err != nil {
131133
return Ingress{}, false
132134
}
135+
normalizedAPIStyle, err := apistyle.Parse(apiStyle)
136+
if err != nil {
137+
return Ingress{}, false
138+
}
133139
pathSuffix := "/"
134140
if len(parts) > 4 {
135141
pathSuffix = "/" + strings.Join(parts[4:], "/")
136142
}
137143
if !IsFixedProviderID(providerID) || strings.TrimSpace(modelID) == "" || strings.TrimSpace(apiStyle) == "" {
138144
return Ingress{}, false
139145
}
140-
return Ingress{ProviderID: providerID, ModelID: modelID, APIStyle: strings.ToLower(apiStyle), PathSuffix: pathSuffix}, true
146+
return Ingress{ProviderID: providerID, ModelID: modelID, APIStyle: normalizedAPIStyle.String(), PathSuffix: pathSuffix}, true
141147
}
142148

143149
// normalizeProxyIngressPath collapses /v1/v1/ from Anthropic clients whose base URL already ends with /v1.
@@ -158,15 +164,15 @@ func apiStyleFromPathSuffix(pathSuffix string) string {
158164
}
159165
switch {
160166
case p == "/messages" || strings.HasPrefix(p, "/messages/"):
161-
return "claude"
167+
return apistyle.Claude.String()
162168
case p == "/responses" || strings.HasPrefix(p, "/responses/"):
163-
return "openai-responses"
169+
return apistyle.OpenAIResponses.String()
164170
case p == "/chat/completions" || strings.HasPrefix(p, "/chat/completions/"):
165-
return "openai-chat"
171+
return apistyle.OpenAIChat.String()
166172
case strings.HasPrefix(p, "/models/") && (strings.Contains(p, ":generatecontent") || strings.Contains(p, ":streamgeneratecontent")):
167-
return "gemini"
173+
return apistyle.Gemini.String()
168174
case p == "/models" || strings.HasPrefix(p, "/models/"):
169-
return "openai-responses"
175+
return apistyle.OpenAIResponses.String()
170176
default:
171177
return ""
172178
}

0 commit comments

Comments
 (0)