Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import (
_ "github.com/awsl-project/maxx/internal/adapter/provider/antigravity"
_ "github.com/awsl-project/maxx/internal/adapter/provider/custom"
"github.com/awsl-project/maxx/internal/cooldown"
"github.com/awsl-project/maxx/internal/event"
"github.com/awsl-project/maxx/internal/executor"
"github.com/awsl-project/maxx/internal/handler"
"github.com/awsl-project/maxx/internal/repository/cached"
"github.com/awsl-project/maxx/internal/repository/sqlite"
"github.com/awsl-project/maxx/internal/router"
"github.com/awsl-project/maxx/internal/service"
"github.com/awsl-project/maxx/internal/waiter"
"github.com/wailsapp/wails/v2/pkg/runtime"
)

Expand Down Expand Up @@ -174,7 +176,13 @@ func (a *App) initializeServices() {

a.antigravitySvc = NewAntigravityService(antigravityQuotaRepo, a.wsHub, a.adminService)

exec := executor.NewExecutor(r, proxyRequestRepo, attemptRepo, cachedRetryConfigRepo, a.wsHub, "")
// Create broadcaster (wraps WebSocket hub)
broadcaster := event.NewWailsBroadcaster(a.wsHub)

// Create project waiter for force project binding
projectWaiter := waiter.NewProjectWaiter(cachedSessionRepo, settingRepo, broadcaster)

exec := executor.NewExecutor(r, proxyRequestRepo, attemptRepo, cachedRetryConfigRepo, cachedSessionRepo, broadcaster, projectWaiter, "")

proxyHandler := handler.NewProxyHandler(clientAdapter, exec, cachedSessionRepo)
projectProxyHandler := handler.NewProjectProxyHandler(proxyHandler, cachedProjectRepo)
Expand Down
17 changes: 17 additions & 0 deletions app/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,20 @@ func (a *App) StartAntigravityOAuth(ctx context.Context) (map[string]interface{}
redirectURI := "http://localhost:19380/antigravity/oauth/callback"
return a.antigravitySvc.StartOAuth(ctx, redirectURI)
}

// Antigravity Global Settings methods

//wails:bind
func (a *App) GetAntigravityGlobalSettings() (*service.AntigravityGlobalSettings, error) {
return a.adminService.GetAntigravityGlobalSettings()
}

//wails:bind
func (a *App) UpdateAntigravityGlobalSettings(settings *service.AntigravityGlobalSettings) error {
return a.adminService.UpdateAntigravityGlobalSettings(settings)
}

//wails:bind
func (a *App) ResetAntigravityGlobalSettings() (*service.AntigravityGlobalSettings, error) {
return a.adminService.ResetAntigravityGlobalSettings()
}
17 changes: 15 additions & 2 deletions cmd/maxx/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import (
"time"

"github.com/awsl-project/maxx/internal/adapter/client"
_ "github.com/awsl-project/maxx/internal/adapter/provider/antigravity" // Register antigravity adapter
_ "github.com/awsl-project/maxx/internal/adapter/provider/custom" // Register custom adapter
"github.com/awsl-project/maxx/internal/adapter/provider/antigravity"
_ "github.com/awsl-project/maxx/internal/adapter/provider/custom" // Register custom adapter
"github.com/awsl-project/maxx/internal/cooldown"
"github.com/awsl-project/maxx/internal/domain"
"github.com/awsl-project/maxx/internal/executor"
"github.com/awsl-project/maxx/internal/handler"
"github.com/awsl-project/maxx/internal/repository/cached"
Expand Down Expand Up @@ -187,6 +188,18 @@ func main() {
r, // Router implements ProviderAdapterRefresher interface
)

// Initialize Antigravity global settings getter
antigravity.SetGlobalSettingsGetter(func() (*antigravity.GlobalSettings, error) {
rulesJSON, _ := settingRepo.Get(domain.SettingKeyAntigravityModelMapping)
rules, err := antigravity.ParseModelMappingRules(rulesJSON)
if err != nil {
return nil, err
}
return &antigravity.GlobalSettings{
ModelMappingRules: rules,
}, nil
})

// Create handlers
proxyHandler := handler.NewProxyHandler(clientAdapter, exec, cachedSessionRepo)
adminHandler := handler.NewAdminHandler(adminService, logPath)
Expand Down
7 changes: 1 addition & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,4 @@ require (
golang.org/x/text v0.22.0 // indirect
)

require (
github.com/emersion/go-autostart v0.0.0-20250403115856-34830d6457d2
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/wailsapp/wails/v2 v2.11.0
)
require github.com/emersion/go-autostart v0.0.0-20250403115856-34830d6457d2
65 changes: 52 additions & 13 deletions internal/adapter/provider/antigravity/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (a *AntigravityAdapter) Execute(ctx context.Context, w http.ResponseWriter,
clientType := ctxutil.GetClientType(ctx)
baseCtx := ctx
requestModel := ctxutil.GetRequestModel(ctx) // Original model from request (e.g., "claude-3-5-sonnet-20241022-online")
mappedModel := ctxutil.GetMappedModel(ctx) // Mapped model after route resolution
mappedModel := ctxutil.GetMappedModel(ctx) // Mapped model after executor's unified mapping
requestBody := ctxutil.GetRequestBody(ctx)
backgroundDowngrade := false
backgroundModel := ""
Expand All @@ -72,29 +72,23 @@ func (a *AntigravityAdapter) Execute(ctx context.Context, w http.ResponseWriter,
}
}

// [Model Mapping] Apply Antigravity model mapping (like Antigravity-Manager)
// We'll attempt at most twice: original + retry without thinking on signature errors
retriedWithoutThinking := false

for attemptIdx := 0; attemptIdx < 2; attemptIdx++ {
ctx = ctxutil.WithRequestModel(baseCtx, requestModel)
ctx = ctxutil.WithRequestBody(ctx, requestBody)

// Only map if route didn't provide a mapping (mappedModel empty or same as request)
// Apply background downgrade override if needed
config := provider.Config.Antigravity
if mappedModel == "" || mappedModel == requestModel {
// Route didn't provide mapping, use our internal mapping with haikuTarget config
haikuTarget := ""
if config != nil {
haikuTarget = config.HaikuTarget
}
mappedModel = MapClaudeModelToGeminiWithConfig(requestModel, haikuTarget)
}
if backgroundDowngrade && backgroundModel != "" {
mappedModel = backgroundModel
}
// If route provided a different mappedModel, trust it and don't re-map
// (user/route has explicitly configured the target model)

// Update attempt record with the final mapped model (in case of background downgrade)
if attempt := ctxutil.GetUpstreamAttempt(ctx); attempt != nil {
attempt.MappedModel = mappedModel
}

// Get streaming flag from context (already detected correctly for Gemini URL path)
stream := ctxutil.GetIsStream(ctx)
Expand Down Expand Up @@ -538,6 +532,9 @@ func (a *AntigravityAdapter) handleNonStreamResponse(ctx context.Context, w http
attempt.Cache1hWriteCount = metrics.Cache1hCreationCount
}

// Extract modelVersion from Gemini response
attempt.ResponseModel = extractModelVersion(unwrappedBody)

// Broadcast attempt update with token info
if bc := ctxutil.GetBroadcaster(ctx); bc != nil {
bc.BroadcastProxyUpstreamAttempt(attempt)
Expand Down Expand Up @@ -629,6 +626,12 @@ func (a *AntigravityAdapter) handleStreamResponse(ctx context.Context, w http.Re
attempt.Cache5mWriteCount = metrics.Cache5mCreationCount
attempt.Cache1hWriteCount = metrics.Cache1hCreationCount
}
// Extract responseModel from claudeState (for Claude clients) or SSE content
if claudeState != nil {
attempt.ResponseModel = claudeState.GetModelVersion()
} else {
attempt.ResponseModel = extractModelVersionFromSSE(sseBuffer.String())
}
// Broadcast attempt update with token info
if bc := ctxutil.GetBroadcaster(ctx); bc != nil {
bc.BroadcastProxyUpstreamAttempt(attempt)
Expand Down Expand Up @@ -846,6 +849,12 @@ func (a *AntigravityAdapter) handleCollectedStreamResponse(ctx context.Context,
attempt.Cache5mWriteCount = metrics.Cache5mCreationCount
attempt.Cache1hWriteCount = metrics.Cache1hCreationCount
}
// Extract responseModel from claudeState (for Claude clients) or SSE content
if claudeState != nil {
attempt.ResponseModel = claudeState.GetModelVersion()
} else {
attempt.ResponseModel = extractModelVersionFromSSE(upstreamSSE.String())
}
if bc := ctxutil.GetBroadcaster(ctx); bc != nil {
bc.BroadcastProxyUpstreamAttempt(attempt)
}
Expand Down Expand Up @@ -998,3 +1007,33 @@ func (a *AntigravityAdapter) parseRateLimitInfo(ctx context.Context, body []byte
ClientType: "", // Global cooldown
}, updateChan
}

// extractModelVersion extracts modelVersion from Gemini response JSON
func extractModelVersion(body []byte) string {
var resp struct {
ModelVersion string `json:"modelVersion"`
}
if err := json.Unmarshal(body, &resp); err != nil {
return ""
}
return resp.ModelVersion
}

// extractModelVersionFromSSE extracts modelVersion from SSE content
// Looks for the last "modelVersion" field in the SSE data
func extractModelVersionFromSSE(sseContent string) string {
var lastModelVersion string
for _, line := range strings.Split(sseContent, "\n") {
if !strings.HasPrefix(line, "data: ") {
continue
}
data := strings.TrimPrefix(line, "data: ")
var chunk struct {
ModelVersion string `json:"modelVersion"`
}
if err := json.Unmarshal([]byte(data), &chunk); err == nil && chunk.ModelVersion != "" {
lastModelVersion = chunk.ModelVersion
}
}
return lastModelVersion
}
5 changes: 5 additions & 0 deletions internal/adapter/provider/antigravity/claude_streaming.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ func NewClaudeStreamingStateWithSession(_ string, requestModel string) *ClaudeSt
}
}

// GetModelVersion returns the upstream model version captured during streaming
func (s *ClaudeStreamingState) GetModelVersion() string {
return s.modelVersion
}

// GeminiPart represents a part in Gemini response
type GeminiPart struct {
Text string `json:"text,omitempty"`
Expand Down
Loading