gateway: to the key magpie-claude-desktop, /v1/models lists every model by an id Claude Desktop takes — anthropic/<id> for one that doesn't say claude, sonnet, opus, haiku or anthropic — so its Test connection passes and its models can be picked from the list
This commit is contained in:
2 files changed
+32
-2
No files matched your search
@@ -38,6 +38,16 @@ func TestAnthropicPrefixedModel(t *testing.T) {
|
||||
if rec.Code != 200 {
|
||||
t.Fatalf("HEAD /api/hello: %d", rec.Code)
|
||||
}
|
||||
req := httptest.NewRequest("GET", "/v1/models", nil)
|
||||
req.Header.Set("x-api-key", Token+"-claude-desktop")
|
||||
rec = httptest.NewRecorder()
|
||||
New().Handler().ServeHTTP(rec, req)
|
||||
if !strings.Contains(rec.Body.String(), `"id":"anthropic/fake/m1"`) {
|
||||
t.Fatalf("Claude Desktop's list: %s", rec.Body)
|
||||
}
|
||||
if claudeLooking("devin/claude-opus-5-5") != "devin/claude-opus-5-5" {
|
||||
t.Fatal("a Claude id was prefixed")
|
||||
}
|
||||
if got := unprefixed("claude-sonnet-4-5"); got != "claude-sonnet-4-5" {
|
||||
t.Fatalf("unprefixed changed %q", got)
|
||||
}
|
||||
|
||||
@@ -302,8 +302,13 @@ func catalogFor(r *http.Request) []provider.Entry {
|
||||
|
||||
func (s *Server) models(w http.ResponseWriter, r *http.Request) {
|
||||
data := []map[string]any{}
|
||||
desktop := agentOf(r) == "claude-desktop"
|
||||
for _, e := range catalogFor(r) {
|
||||
data = append(data, modelObject(e))
|
||||
m := modelObject(e)
|
||||
if desktop {
|
||||
m["id"] = claudeLooking(e.ID)
|
||||
}
|
||||
data = append(data, m)
|
||||
}
|
||||
out := map[string]any{"object": "list", "data": data, "has_more": false}
|
||||
if len(data) > 0 {
|
||||
@@ -327,7 +332,8 @@ func (s *Server) model(w http.ResponseWriter, r *http.Request) {
|
||||
// front of it. Claude Desktop, pointed at a third-party gateway, takes only
|
||||
// models whose id says claude, sonnet, opus, haiku or anthropic, so any of
|
||||
// magpie's is asked for there as anthropic/<its id>. An id that is magpie's
|
||||
// as it stands (a provider named anthropic) is left alone.
|
||||
// as it stands (a provider named anthropic) is left alone. With the key
|
||||
// magpie-claude-desktop, /v1/models lists them so.
|
||||
func unprefixed(id string) string {
|
||||
rest, ok := strings.CutPrefix(id, "anthropic/")
|
||||
if !ok || rest == "" {
|
||||
@@ -342,6 +348,20 @@ func unprefixed(id string) string {
|
||||
return rest
|
||||
}
|
||||
|
||||
// claudeLooking is a model's id as Claude Desktop is shown it, to the key
|
||||
// magpie-claude-desktop: it takes only an id that says claude, sonnet, opus,
|
||||
// haiku or anthropic, so any other is put after anthropic/ (unprefixed
|
||||
// serves it again).
|
||||
func claudeLooking(id string) string {
|
||||
l := strings.ToLower(id)
|
||||
for _, w := range []string{"claude", "sonnet", "opus", "haiku", "anthropic"} {
|
||||
if strings.Contains(l, w) {
|
||||
return id
|
||||
}
|
||||
}
|
||||
return "anthropic/" + id
|
||||
}
|
||||
|
||||
// countTokens answers Anthropic's count_tokens: through the provider when
|
||||
// it implements counting, else a rough estimate. A failed connection or
|
||||
// limited key yields to the next key; other failures reach the client.
|
||||
|
||||
Reference in new issue
Block a user