mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
fix: expose mapped antigravity test models
This commit is contained in:
@@ -876,6 +876,10 @@ Antigravity accounts support optional **hybrid scheduling**. When enabled, the g
|
||||
|
||||
> **⚠️ Warning**: Anthropic Claude and Antigravity Claude **cannot be mixed within the same conversation context**. Use groups to isolate them properly.
|
||||
|
||||
### Admin Account Test Models
|
||||
|
||||
For Antigravity accounts, the connectivity test model selector uses the request-side keys of an explicitly configured account `model_mapping`. If the mapping is missing or empty, it uses the built-in model catalog. The selected key is still mapped to its upstream value when the test runs.
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
@@ -792,6 +792,10 @@ Antigravity 账户支持可选的**混合调度**功能。开启后,通用端
|
||||
|
||||
> **⚠️ 注意**:Anthropic Claude 和 Antigravity Claude **不能在同一上下文中混合使用**,请通过分组功能做好隔离。
|
||||
|
||||
### 管理端账号测试模型
|
||||
|
||||
在管理端打开“测试账号连接”时,Antigravity 账户的可选测试模型遵循账号映射配置:配置了账号级 `model_mapping` 时,列表使用映射的请求侧 key;未配置或映射为空时,列表使用内置默认模型目录。选择映射 key 后,测试请求仍会按映射 value 转换为实际发送给上游的模型。
|
||||
|
||||
---
|
||||
|
||||
## 项目结构
|
||||
|
||||
@@ -2894,10 +2894,9 @@ func (h *AccountHandler) GetAvailableModels(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle Antigravity accounts: return Claude + Gemini models
|
||||
// Explicit account mappings expose their request-side names to connectivity tests.
|
||||
if account.Platform == service.PlatformAntigravity {
|
||||
// 直接复用 antigravity.DefaultModels(),与 /v1/models 端点保持同步
|
||||
response.Success(c, antigravity.DefaultModels())
|
||||
response.Success(c, antigravityAccountTestModels(account.Credentials["model_mapping"]))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2998,6 +2997,48 @@ func (h *AccountHandler) GetAvailableModels(c *gin.Context) {
|
||||
response.Success(c, models)
|
||||
}
|
||||
|
||||
// antigravityAccountTestModels uses the stored mapping rather than GetModelMapping,
|
||||
// which supplies defaults and compatibility aliases that the administrator did not configure.
|
||||
func antigravityAccountTestModels(rawMapping any) []antigravity.ClaudeModel {
|
||||
var mappedIDs []string
|
||||
switch mapping := rawMapping.(type) {
|
||||
case map[string]any:
|
||||
for id := range mapping {
|
||||
if strings.TrimSpace(id) != "" {
|
||||
mappedIDs = append(mappedIDs, id)
|
||||
}
|
||||
}
|
||||
case map[string]string:
|
||||
for id := range mapping {
|
||||
if strings.TrimSpace(id) != "" {
|
||||
mappedIDs = append(mappedIDs, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(mappedIDs) == 0 {
|
||||
return antigravity.DefaultModels()
|
||||
}
|
||||
|
||||
sort.Strings(mappedIDs)
|
||||
defaultByID := make(map[string]antigravity.ClaudeModel)
|
||||
for _, model := range antigravity.DefaultModels() {
|
||||
defaultByID[model.ID] = model
|
||||
}
|
||||
models := make([]antigravity.ClaudeModel, 0, len(mappedIDs))
|
||||
for _, id := range mappedIDs {
|
||||
if model, ok := defaultByID[id]; ok {
|
||||
models = append(models, model)
|
||||
continue
|
||||
}
|
||||
models = append(models, antigravity.ClaudeModel{
|
||||
ID: id,
|
||||
Type: "model",
|
||||
DisplayName: id,
|
||||
})
|
||||
}
|
||||
return models
|
||||
}
|
||||
|
||||
// SyncUpstreamModels handles syncing live supported models from an account's upstream.
|
||||
// POST /api/v1/admin/accounts/:id/models/sync-upstream
|
||||
func (h *AccountHandler) SyncUpstreamModels(c *gin.Context) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/config"
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/antigravity"
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/tlsfingerprint"
|
||||
"github.com/Wei-Shaw/sub2api/internal/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -330,6 +331,49 @@ func TestAccountHandlerGetAvailableModels_GeminiGoogleOneUsesConservativeCatalog
|
||||
require.NotContains(t, ids, "gemini-2.5-flash-image")
|
||||
}
|
||||
|
||||
func TestAccountHandlerGetAvailableModels_Antigravity(t *testing.T) {
|
||||
defaults := antigravity.DefaultModels()
|
||||
known := defaults[0]
|
||||
custom := antigravity.ClaudeModel{ID: "claude-custom-alias", Type: "model", DisplayName: "claude-custom-alias"}
|
||||
cases := []struct {
|
||||
name string
|
||||
credentials map[string]any
|
||||
want []antigravity.ClaudeModel
|
||||
}{
|
||||
{name: "nil credentials", want: defaults},
|
||||
{name: "missing mapping", credentials: map[string]any{}, want: defaults},
|
||||
{name: "empty JSON mapping", credentials: map[string]any{"model_mapping": map[string]any{}}, want: defaults},
|
||||
{name: "empty string mapping", credentials: map[string]any{"model_mapping": map[string]string{}}, want: defaults},
|
||||
{name: "blank key", credentials: map[string]any{"model_mapping": map[string]any{" ": "upstream-model"}}, want: defaults},
|
||||
{name: "JSON mapping keys and metadata", credentials: map[string]any{"model_mapping": map[string]any{
|
||||
known.ID: known.ID, custom.ID: "upstream-model",
|
||||
}}, want: []antigravity.ClaudeModel{custom, known}},
|
||||
{name: "string mapping keys and metadata", credentials: map[string]any{"model_mapping": map[string]string{
|
||||
known.ID: known.ID, custom.ID: "upstream-model",
|
||||
}}, want: []antigravity.ClaudeModel{custom, known}},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, accountType := range []string{service.AccountTypeOAuth, service.AccountTypeAPIKey} {
|
||||
t.Run(accountType, func(t *testing.T) {
|
||||
svc := &availableModelsAdminService{
|
||||
stubAdminService: newStubAdminService(),
|
||||
account: service.Account{ID: 51, Platform: service.PlatformAntigravity, Type: accountType, Credentials: tc.credentials},
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
setupAvailableModelsRouter(svc).ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/api/v1/admin/accounts/51/models", nil))
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
var resp struct {
|
||||
Data []antigravity.ClaudeModel `json:"data"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
|
||||
require.Equal(t, tc.want, resp.Data)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountHandlerSyncUpstreamModels_ConfigErrorReturnsBadRequest(t *testing.T) {
|
||||
svc := &availableModelsAdminService{
|
||||
stubAdminService: newStubAdminService(),
|
||||
|
||||
Reference in New Issue
Block a user