From e9707c2f9ee45c228bb6a2afb594e0df12e06056 Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Wed, 24 Sep 2025 10:24:12 +0800 Subject: [PATCH] refactor(gemini-web): Move provider logic to its own package The Gemini Web API client logic has been relocated from `internal/client/gemini-web` to a new, more specific `internal/provider/gemini-web` package. This refactoring improves code organization and modularity by better isolating provider-specific implementations. As a result of this move, the `GeminiWebState` struct and its methods have been exported (capitalized) to make them accessible from the executor. All call sites have been updated to use the new package path and the exported identifiers. --- .../{client => provider}/gemini-web/auth.go | 0 .../{client => provider}/gemini-web/client.go | 0 .../gemini-web/convert_ext.go | 0 .../{client => provider}/gemini-web/errors.go | 0 .../gemini-web/logging.go | 0 .../{client => provider}/gemini-web/media.go | 0 .../{client => provider}/gemini-web/models.go | 0 .../gemini-web/persistence.go | 0 .../{client => provider}/gemini-web/prompt.go | 0 .../gemini-web/request.go | 0 .../gemini-web/state.go} | 170 +++++++++++------- .../{client => provider}/gemini-web/types.go | 0 .../runtime/executor/gemini_web_executor.go | 29 +-- sdk/cliproxy/service.go | 2 +- 14 files changed, 117 insertions(+), 84 deletions(-) rename internal/{client => provider}/gemini-web/auth.go (100%) rename internal/{client => provider}/gemini-web/client.go (100%) rename internal/{client => provider}/gemini-web/convert_ext.go (100%) rename internal/{client => provider}/gemini-web/errors.go (100%) rename internal/{client => provider}/gemini-web/logging.go (100%) rename internal/{client => provider}/gemini-web/media.go (100%) rename internal/{client => provider}/gemini-web/models.go (100%) rename internal/{client => provider}/gemini-web/persistence.go (100%) rename internal/{client => provider}/gemini-web/prompt.go (100%) rename internal/{client => provider}/gemini-web/request.go (100%) rename internal/{runtime/executor/gemini_web_state.go => provider/gemini-web/state.go} (68%) rename internal/{client => provider}/gemini-web/types.go (100%) diff --git a/internal/client/gemini-web/auth.go b/internal/provider/gemini-web/auth.go similarity index 100% rename from internal/client/gemini-web/auth.go rename to internal/provider/gemini-web/auth.go diff --git a/internal/client/gemini-web/client.go b/internal/provider/gemini-web/client.go similarity index 100% rename from internal/client/gemini-web/client.go rename to internal/provider/gemini-web/client.go diff --git a/internal/client/gemini-web/convert_ext.go b/internal/provider/gemini-web/convert_ext.go similarity index 100% rename from internal/client/gemini-web/convert_ext.go rename to internal/provider/gemini-web/convert_ext.go diff --git a/internal/client/gemini-web/errors.go b/internal/provider/gemini-web/errors.go similarity index 100% rename from internal/client/gemini-web/errors.go rename to internal/provider/gemini-web/errors.go diff --git a/internal/client/gemini-web/logging.go b/internal/provider/gemini-web/logging.go similarity index 100% rename from internal/client/gemini-web/logging.go rename to internal/provider/gemini-web/logging.go diff --git a/internal/client/gemini-web/media.go b/internal/provider/gemini-web/media.go similarity index 100% rename from internal/client/gemini-web/media.go rename to internal/provider/gemini-web/media.go diff --git a/internal/client/gemini-web/models.go b/internal/provider/gemini-web/models.go similarity index 100% rename from internal/client/gemini-web/models.go rename to internal/provider/gemini-web/models.go diff --git a/internal/client/gemini-web/persistence.go b/internal/provider/gemini-web/persistence.go similarity index 100% rename from internal/client/gemini-web/persistence.go rename to internal/provider/gemini-web/persistence.go diff --git a/internal/client/gemini-web/prompt.go b/internal/provider/gemini-web/prompt.go similarity index 100% rename from internal/client/gemini-web/prompt.go rename to internal/provider/gemini-web/prompt.go diff --git a/internal/client/gemini-web/request.go b/internal/provider/gemini-web/request.go similarity index 100% rename from internal/client/gemini-web/request.go rename to internal/provider/gemini-web/request.go diff --git a/internal/runtime/executor/gemini_web_state.go b/internal/provider/gemini-web/state.go similarity index 68% rename from internal/runtime/executor/gemini_web_state.go rename to internal/provider/gemini-web/state.go index 2b10a3f0..aed61b74 100644 --- a/internal/runtime/executor/gemini_web_state.go +++ b/internal/provider/gemini-web/state.go @@ -1,4 +1,4 @@ -package executor +package geminiwebapi import ( "bytes" @@ -10,8 +10,8 @@ import ( "sync" "time" + "github.com/gin-gonic/gin" "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/gemini" - geminiwebapi "github.com/router-for-me/CLIProxyAPI/v6/internal/client/gemini-web" "github.com/router-for-me/CLIProxyAPI/v6/internal/config" "github.com/router-for-me/CLIProxyAPI/v6/internal/constant" "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces" @@ -25,7 +25,7 @@ const ( geminiWebDefaultTimeoutSec = 300 ) -type geminiWebState struct { +type GeminiWebState struct { cfg *config.Config token *gemini.GeminiWebTokenStorage storagePath string @@ -34,29 +34,29 @@ type geminiWebState struct { accountID string reqMu sync.Mutex - client *geminiwebapi.GeminiClient + client *GeminiClient tokenMu sync.Mutex tokenDirty bool convMu sync.RWMutex convStore map[string][]string - convData map[string]geminiwebapi.ConversationRecord + convData map[string]ConversationRecord convIndex map[string]string lastRefresh time.Time } -func newGeminiWebState(cfg *config.Config, token *gemini.GeminiWebTokenStorage, storagePath string) *geminiWebState { - state := &geminiWebState{ +func NewGeminiWebState(cfg *config.Config, token *gemini.GeminiWebTokenStorage, storagePath string) *GeminiWebState { + state := &GeminiWebState{ cfg: cfg, token: token, storagePath: storagePath, convStore: make(map[string][]string), - convData: make(map[string]geminiwebapi.ConversationRecord), + convData: make(map[string]ConversationRecord), convIndex: make(map[string]string), } - suffix := geminiwebapi.Sha256Hex(token.Secure1PSID) + suffix := Sha256Hex(token.Secure1PSID) if len(suffix) > 16 { suffix = suffix[:16] } @@ -75,39 +75,39 @@ func newGeminiWebState(cfg *config.Config, token *gemini.GeminiWebTokenStorage, return state } -func (s *geminiWebState) loadConversationCaches() { +func (s *GeminiWebState) loadConversationCaches() { if path := s.convStorePath(); path != "" { - if store, err := geminiwebapi.LoadConvStore(path); err == nil { + if store, err := LoadConvStore(path); err == nil { s.convStore = store } } if path := s.convDataPath(); path != "" { - if items, index, err := geminiwebapi.LoadConvData(path); err == nil { + if items, index, err := LoadConvData(path); err == nil { s.convData = items s.convIndex = index } } } -func (s *geminiWebState) convStorePath() string { +func (s *GeminiWebState) convStorePath() string { base := s.storagePath if base == "" { base = s.accountID + ".json" } - return geminiwebapi.ConvStorePath(base) + return ConvStorePath(base) } -func (s *geminiWebState) convDataPath() string { +func (s *GeminiWebState) convDataPath() string { base := s.storagePath if base == "" { base = s.accountID + ".json" } - return geminiwebapi.ConvDataPath(base) + return ConvDataPath(base) } -func (s *geminiWebState) getRequestMutex() *sync.Mutex { return &s.reqMu } +func (s *GeminiWebState) GetRequestMutex() *sync.Mutex { return &s.reqMu } -func (s *geminiWebState) ensureClient() error { +func (s *GeminiWebState) EnsureClient() error { if s.client != nil && s.client.Running { return nil } @@ -115,7 +115,7 @@ func (s *geminiWebState) ensureClient() error { if s.cfg != nil { proxyURL = s.cfg.ProxyURL } - s.client = geminiwebapi.NewGeminiClient( + s.client = NewGeminiClient( s.token.Secure1PSID, s.token.Secure1PSIDTS, proxyURL, @@ -129,13 +129,13 @@ func (s *geminiWebState) ensureClient() error { return nil } -func (s *geminiWebState) refresh(ctx context.Context) error { +func (s *GeminiWebState) Refresh(ctx context.Context) error { _ = ctx proxyURL := "" if s.cfg != nil { proxyURL = s.cfg.ProxyURL } - s.client = geminiwebapi.NewGeminiClient( + s.client = NewGeminiClient( s.token.Secure1PSID, s.token.Secure1PSIDTS, proxyURL, @@ -158,7 +158,7 @@ func (s *geminiWebState) refresh(ctx context.Context) error { return nil } -func (s *geminiWebState) tokenSnapshot() *gemini.GeminiWebTokenStorage { +func (s *GeminiWebState) TokenSnapshot() *gemini.GeminiWebTokenStorage { s.tokenMu.Lock() defer s.tokenMu.Unlock() c := *s.token @@ -170,15 +170,15 @@ type geminiWebPrepared struct { translatedRaw []byte prompt string uploaded []string - chat *geminiwebapi.ChatSession - cleaned []geminiwebapi.RoleText + chat *ChatSession + cleaned []RoleText underlying string reuse bool tagged bool originalRaw []byte } -func (s *geminiWebState) prepare(ctx context.Context, modelName string, rawJSON []byte, stream bool, original []byte) (*geminiWebPrepared, *interfaces.ErrorMessage) { +func (s *GeminiWebState) prepare(ctx context.Context, modelName string, rawJSON []byte, stream bool, original []byte) (*geminiWebPrepared, *interfaces.ErrorMessage) { res := &geminiWebPrepared{originalRaw: original} res.translatedRaw = bytes.Clone(rawJSON) if handler, ok := ctx.Value("handler").(interfaces.APIHandler); ok && handler != nil { @@ -187,14 +187,14 @@ func (s *geminiWebState) prepare(ctx context.Context, modelName string, rawJSON } recordAPIRequest(ctx, s.cfg, res.translatedRaw) - messages, files, mimes, msgFileIdx, err := geminiwebapi.ParseMessagesAndFiles(res.translatedRaw) + messages, files, mimes, msgFileIdx, err := ParseMessagesAndFiles(res.translatedRaw) if err != nil { return nil, &interfaces.ErrorMessage{StatusCode: 400, Error: fmt.Errorf("bad request: %w", err)} } - cleaned := geminiwebapi.SanitizeAssistantMessages(messages) + cleaned := SanitizeAssistantMessages(messages) res.cleaned = cleaned - res.underlying = geminiwebapi.MapAliasToUnderlying(modelName) - model, err := geminiwebapi.ModelFromName(res.underlying) + res.underlying = MapAliasToUnderlying(modelName) + model, err := ModelFromName(res.underlying) if err != nil { return nil, &interfaces.ErrorMessage{StatusCode: 400, Error: err} } @@ -210,11 +210,11 @@ func (s *geminiWebState) prepare(ctx context.Context, modelName string, rawJSON res.reuse = true meta = reuseMeta if len(remaining) == 1 { - useMsgs = []geminiwebapi.RoleText{remaining[0]} + useMsgs = []RoleText{remaining[0]} } else if len(remaining) > 1 { useMsgs = remaining } else if len(cleaned) > 0 { - useMsgs = []geminiwebapi.RoleText{cleaned[len(cleaned)-1]} + useMsgs = []RoleText{cleaned[len(cleaned)-1]} } if len(useMsgs) == 1 && len(messages) > 0 && len(msgFileIdx) == len(messages) { lastIdx := len(msgFileIdx) - 1 @@ -242,8 +242,8 @@ func (s *geminiWebState) prepare(ctx context.Context, modelName string, rawJSON } } else { if len(cleaned) >= 2 && strings.EqualFold(cleaned[len(cleaned)-2].Role, "assistant") { - keyUnderlying := geminiwebapi.AccountMetaKey(s.accountID, res.underlying) - keyAlias := geminiwebapi.AccountMetaKey(s.accountID, modelName) + keyUnderlying := AccountMetaKey(s.accountID, res.underlying) + keyAlias := AccountMetaKey(s.accountID, modelName) s.convMu.RLock() fallbackMeta := s.convStore[keyUnderlying] if len(fallbackMeta) == 0 { @@ -252,7 +252,7 @@ func (s *geminiWebState) prepare(ctx context.Context, modelName string, rawJSON s.convMu.RUnlock() if len(fallbackMeta) > 0 { meta = fallbackMeta - useMsgs = []geminiwebapi.RoleText{cleaned[len(cleaned)-1]} + useMsgs = []RoleText{cleaned[len(cleaned)-1]} res.reuse = true filesSubset = nil mimesSubset = nil @@ -260,8 +260,8 @@ func (s *geminiWebState) prepare(ctx context.Context, modelName string, rawJSON } } } else { - keyUnderlying := geminiwebapi.AccountMetaKey(s.accountID, res.underlying) - keyAlias := geminiwebapi.AccountMetaKey(s.accountID, modelName) + keyUnderlying := AccountMetaKey(s.accountID, res.underlying) + keyAlias := AccountMetaKey(s.accountID, modelName) s.convMu.RLock() if v, ok := s.convStore[keyUnderlying]; ok && len(v) > 0 { meta = v @@ -271,26 +271,26 @@ func (s *geminiWebState) prepare(ctx context.Context, modelName string, rawJSON s.convMu.RUnlock() } - res.tagged = geminiwebapi.NeedRoleTags(useMsgs) + res.tagged = NeedRoleTags(useMsgs) if res.reuse && len(useMsgs) == 1 { res.tagged = false } enableXML := s.cfg != nil && s.cfg.GeminiWeb.CodeMode - useMsgs = geminiwebapi.AppendXMLWrapHintIfNeeded(useMsgs, !enableXML) + useMsgs = AppendXMLWrapHintIfNeeded(useMsgs, !enableXML) - res.prompt = geminiwebapi.BuildPrompt(useMsgs, res.tagged, res.tagged) + res.prompt = BuildPrompt(useMsgs, res.tagged, res.tagged) if strings.TrimSpace(res.prompt) == "" { return nil, &interfaces.ErrorMessage{StatusCode: 400, Error: errors.New("bad request: empty prompt after filtering system/thought content")} } - uploaded, upErr := geminiwebapi.MaterializeInlineFiles(filesSubset, mimesSubset) + uploaded, upErr := MaterializeInlineFiles(filesSubset, mimesSubset) if upErr != nil { return nil, upErr } res.uploaded = uploaded - if err = s.ensureClient(); err != nil { + if err = s.EnsureClient(); err != nil { return nil, &interfaces.ErrorMessage{StatusCode: 500, Error: err} } chat := s.client.StartChat(model, s.getConfiguredGem(), meta) @@ -300,14 +300,14 @@ func (s *geminiWebState) prepare(ctx context.Context, modelName string, rawJSON return res, nil } -func (s *geminiWebState) send(ctx context.Context, modelName string, reqPayload []byte, opts cliproxyexecutor.Options) ([]byte, *interfaces.ErrorMessage, *geminiWebPrepared) { +func (s *GeminiWebState) Send(ctx context.Context, modelName string, reqPayload []byte, opts cliproxyexecutor.Options) ([]byte, *interfaces.ErrorMessage, *geminiWebPrepared) { prep, errMsg := s.prepare(ctx, modelName, reqPayload, opts.Stream, opts.OriginalRequest) if errMsg != nil { return nil, errMsg, nil } - defer geminiwebapi.CleanupFiles(prep.uploaded) + defer CleanupFiles(prep.uploaded) - output, err := geminiwebapi.SendWithSplit(prep.chat, prep.prompt, prep.uploaded, s.cfg) + output, err := SendWithSplit(prep.chat, prep.prompt, prep.uploaded, s.cfg) if err != nil { return nil, s.wrapSendError(err), nil } @@ -331,7 +331,7 @@ func (s *geminiWebState) send(ctx context.Context, modelName string, reqPayload } } - gemBytes, err := geminiwebapi.ConvertOutputToGemini(&output, modelName, prep.prompt) + gemBytes, err := ConvertOutputToGemini(&output, modelName, prep.prompt) if err != nil { return nil, &interfaces.ErrorMessage{StatusCode: 500, Error: err}, nil } @@ -341,13 +341,13 @@ func (s *geminiWebState) send(ctx context.Context, modelName string, reqPayload return gemBytes, nil, prep } -func (s *geminiWebState) wrapSendError(genErr error) *interfaces.ErrorMessage { +func (s *GeminiWebState) wrapSendError(genErr error) *interfaces.ErrorMessage { status := 500 - var usage *geminiwebapi.UsageLimitExceeded - var blocked *geminiwebapi.TemporarilyBlocked - var invalid *geminiwebapi.ModelInvalid - var valueErr *geminiwebapi.ValueError - var timeout *geminiwebapi.TimeoutError + var usage *UsageLimitExceeded + var blocked *TemporarilyBlocked + var invalid *ModelInvalid + var valueErr *ValueError + var timeout *TimeoutError switch { case errors.As(genErr, &usage): status = 429 @@ -363,14 +363,14 @@ func (s *geminiWebState) wrapSendError(genErr error) *interfaces.ErrorMessage { return &interfaces.ErrorMessage{StatusCode: status, Error: genErr} } -func (s *geminiWebState) persistConversation(modelName string, prep *geminiWebPrepared, output *geminiwebapi.ModelOutput) { +func (s *GeminiWebState) persistConversation(modelName string, prep *geminiWebPrepared, output *ModelOutput) { if output == nil || prep == nil || prep.chat == nil { return } metadata := prep.chat.Metadata() if len(metadata) > 0 { - keyUnderlying := geminiwebapi.AccountMetaKey(s.accountID, prep.underlying) - keyAlias := geminiwebapi.AccountMetaKey(s.accountID, modelName) + keyUnderlying := AccountMetaKey(s.accountID, prep.underlying) + keyAlias := AccountMetaKey(s.accountID, modelName) s.convMu.Lock() s.convStore[keyUnderlying] = metadata s.convStore[keyAlias] = metadata @@ -384,18 +384,18 @@ func (s *geminiWebState) persistConversation(modelName string, prep *geminiWebPr storeSnapshot[k] = cp } s.convMu.Unlock() - _ = geminiwebapi.SaveConvStore(s.convStorePath(), storeSnapshot) + _ = SaveConvStore(s.convStorePath(), storeSnapshot) } if !s.useReusableContext() { return } - rec, ok := geminiwebapi.BuildConversationRecord(prep.underlying, s.stableClientID, prep.cleaned, output, metadata) + rec, ok := BuildConversationRecord(prep.underlying, s.stableClientID, prep.cleaned, output, metadata) if !ok { return } - stableHash := geminiwebapi.HashConversation(rec.ClientID, prep.underlying, rec.Messages) - accountHash := geminiwebapi.HashConversation(s.accountID, prep.underlying, rec.Messages) + stableHash := HashConversation(rec.ClientID, prep.underlying, rec.Messages) + accountHash := HashConversation(s.accountID, prep.underlying, rec.Messages) s.convMu.Lock() s.convData[stableHash] = rec @@ -403,7 +403,7 @@ func (s *geminiWebState) persistConversation(modelName string, prep *geminiWebPr if accountHash != stableHash { s.convIndex["hash:"+accountHash] = stableHash } - dataSnapshot := make(map[string]geminiwebapi.ConversationRecord, len(s.convData)) + dataSnapshot := make(map[string]ConversationRecord, len(s.convData)) for k, v := range s.convData { dataSnapshot[k] = v } @@ -412,14 +412,14 @@ func (s *geminiWebState) persistConversation(modelName string, prep *geminiWebPr indexSnapshot[k] = v } s.convMu.Unlock() - _ = geminiwebapi.SaveConvData(s.convDataPath(), dataSnapshot, indexSnapshot) + _ = SaveConvData(s.convDataPath(), dataSnapshot, indexSnapshot) } -func (s *geminiWebState) addAPIResponseData(ctx context.Context, line []byte) { +func (s *GeminiWebState) addAPIResponseData(ctx context.Context, line []byte) { appendAPIResponseChunk(ctx, s.cfg, line) } -func (s *geminiWebState) convertToTarget(ctx context.Context, modelName string, prep *geminiWebPrepared, gemBytes []byte) []byte { +func (s *GeminiWebState) ConvertToTarget(ctx context.Context, modelName string, prep *geminiWebPrepared, gemBytes []byte) []byte { if prep == nil || prep.handlerType == "" { return gemBytes } @@ -437,7 +437,7 @@ func (s *geminiWebState) convertToTarget(ctx context.Context, modelName string, return []byte(out) } -func (s *geminiWebState) convertStream(ctx context.Context, modelName string, prep *geminiWebPrepared, gemBytes []byte) []string { +func (s *GeminiWebState) ConvertStream(ctx context.Context, modelName string, prep *geminiWebPrepared, gemBytes []byte) []string { if prep == nil || prep.handlerType == "" { return []string{string(gemBytes)} } @@ -448,7 +448,7 @@ func (s *geminiWebState) convertStream(ctx context.Context, modelName string, pr return translator.Response(prep.handlerType, constant.GeminiWeb, ctx, modelName, prep.originalRaw, prep.translatedRaw, gemBytes, ¶m) } -func (s *geminiWebState) doneStream(ctx context.Context, modelName string, prep *geminiWebPrepared) []string { +func (s *GeminiWebState) DoneStream(ctx context.Context, modelName string, prep *geminiWebPrepared) []string { if prep == nil || prep.handlerType == "" { return nil } @@ -459,24 +459,56 @@ func (s *geminiWebState) doneStream(ctx context.Context, modelName string, prep return translator.Response(prep.handlerType, constant.GeminiWeb, ctx, modelName, prep.originalRaw, prep.translatedRaw, []byte("[DONE]"), ¶m) } -func (s *geminiWebState) useReusableContext() bool { +func (s *GeminiWebState) useReusableContext() bool { if s.cfg == nil { return true } return s.cfg.GeminiWeb.Context } -func (s *geminiWebState) findReusableSession(modelName string, msgs []geminiwebapi.RoleText) ([]string, []geminiwebapi.RoleText) { +func (s *GeminiWebState) findReusableSession(modelName string, msgs []RoleText) ([]string, []RoleText) { s.convMu.RLock() items := s.convData index := s.convIndex s.convMu.RUnlock() - return geminiwebapi.FindReusableSessionIn(items, index, s.stableClientID, s.accountID, modelName, msgs) + return FindReusableSessionIn(items, index, s.stableClientID, s.accountID, modelName, msgs) } -func (s *geminiWebState) getConfiguredGem() *geminiwebapi.Gem { +func (s *GeminiWebState) getConfiguredGem() *Gem { if s.cfg != nil && s.cfg.GeminiWeb.CodeMode { - return &geminiwebapi.Gem{ID: "coding-partner", Name: "Coding partner", Predefined: true} + return &Gem{ID: "coding-partner", Name: "Coding partner", Predefined: true} } return nil } + +// recordAPIRequest stores the upstream request payload in Gin context for request logging. +func recordAPIRequest(ctx context.Context, cfg *config.Config, payload []byte) { + if cfg == nil || !cfg.RequestLog || len(payload) == 0 { + return + } + if ginCtx, ok := ctx.Value("gin").(*gin.Context); ok && ginCtx != nil { + ginCtx.Set("API_REQUEST", bytes.Clone(payload)) + } +} + +// appendAPIResponseChunk appends an upstream response chunk to Gin context for request logging. +func appendAPIResponseChunk(ctx context.Context, cfg *config.Config, chunk []byte) { + if cfg == nil || !cfg.RequestLog { + return + } + data := bytes.TrimSpace(bytes.Clone(chunk)) + if len(data) == 0 { + return + } + if ginCtx, ok := ctx.Value("gin").(*gin.Context); ok && ginCtx != nil { + if existing, exists := ginCtx.Get("API_RESPONSE"); exists { + if prev, okBytes := existing.([]byte); okBytes { + prev = append(prev, data...) + prev = append(prev, []byte("\n\n")...) + ginCtx.Set("API_RESPONSE", prev) + return + } + } + ginCtx.Set("API_RESPONSE", data) + } +} diff --git a/internal/client/gemini-web/types.go b/internal/provider/gemini-web/types.go similarity index 100% rename from internal/client/gemini-web/types.go rename to internal/provider/gemini-web/types.go diff --git a/internal/runtime/executor/gemini_web_executor.go b/internal/runtime/executor/gemini_web_executor.go index a9cc57c5..1d42b182 100644 --- a/internal/runtime/executor/gemini_web_executor.go +++ b/internal/runtime/executor/gemini_web_executor.go @@ -9,6 +9,7 @@ import ( "time" "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/gemini" + geminiwebapi "github.com/router-for-me/CLIProxyAPI/v6/internal/provider/gemini-web" "github.com/router-for-me/CLIProxyAPI/v6/internal/config" "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces" cliproxyauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" @@ -35,23 +36,23 @@ func (e *GeminiWebExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth if err != nil { return cliproxyexecutor.Response{}, err } - if err = state.ensureClient(); err != nil { + if err = state.EnsureClient(); err != nil { return cliproxyexecutor.Response{}, err } reporter := newUsageReporter(ctx, e.Identifier(), req.Model, auth) - mutex := state.getRequestMutex() + mutex := state.GetRequestMutex() if mutex != nil { mutex.Lock() defer mutex.Unlock() } payload := bytes.Clone(req.Payload) - resp, errMsg, prep := state.send(ctx, req.Model, payload, opts) + resp, errMsg, prep := state.Send(ctx, req.Model, payload, opts) if errMsg != nil { return cliproxyexecutor.Response{}, geminiWebErrorFromMessage(errMsg) } - resp = state.convertToTarget(ctx, req.Model, prep, resp) + resp = state.ConvertToTarget(ctx, req.Model, prep, resp) reporter.publish(ctx, parseGeminiUsage(resp)) from := opts.SourceFormat @@ -67,17 +68,17 @@ func (e *GeminiWebExecutor) ExecuteStream(ctx context.Context, auth *cliproxyaut if err != nil { return nil, err } - if err = state.ensureClient(); err != nil { + if err = state.EnsureClient(); err != nil { return nil, err } reporter := newUsageReporter(ctx, e.Identifier(), req.Model, auth) - mutex := state.getRequestMutex() + mutex := state.GetRequestMutex() if mutex != nil { mutex.Lock() } - gemBytes, errMsg, prep := state.send(ctx, req.Model, bytes.Clone(req.Payload), opts) + gemBytes, errMsg, prep := state.Send(ctx, req.Model, bytes.Clone(req.Payload), opts) if errMsg != nil { if mutex != nil { mutex.Unlock() @@ -90,8 +91,8 @@ func (e *GeminiWebExecutor) ExecuteStream(ctx context.Context, auth *cliproxyaut to := sdktranslator.FromString("gemini-web") var param any - lines := state.convertStream(ctx, req.Model, prep, gemBytes) - done := state.doneStream(ctx, req.Model, prep) + lines := state.ConvertStream(ctx, req.Model, prep, gemBytes) + done := state.DoneStream(ctx, req.Model, prep) out := make(chan cliproxyexecutor.StreamChunk) go func() { defer close(out) @@ -124,10 +125,10 @@ func (e *GeminiWebExecutor) Refresh(ctx context.Context, auth *cliproxyauth.Auth if err != nil { return nil, err } - if err = state.refresh(ctx); err != nil { + if err = state.Refresh(ctx); err != nil { return nil, err } - ts := state.tokenSnapshot() + ts := state.TokenSnapshot() if auth.Metadata == nil { auth.Metadata = make(map[string]any) } @@ -139,10 +140,10 @@ func (e *GeminiWebExecutor) Refresh(ctx context.Context, auth *cliproxyauth.Auth } type geminiWebRuntime struct { - state *geminiWebState + state *geminiwebapi.GeminiWebState } -func (e *GeminiWebExecutor) stateFor(auth *cliproxyauth.Auth) (*geminiWebState, error) { +func (e *GeminiWebExecutor) stateFor(auth *cliproxyauth.Auth) (*geminiwebapi.GeminiWebState, error) { if auth == nil { return nil, fmt.Errorf("gemini-web executor: auth is nil") } @@ -175,7 +176,7 @@ func (e *GeminiWebExecutor) stateFor(auth *cliproxyauth.Auth) (*geminiWebState, storagePath = p } } - state := newGeminiWebState(cfg, ts, storagePath) + state := geminiwebapi.NewGeminiWebState(cfg, ts, storagePath) runtime := &geminiWebRuntime{state: state} auth.Runtime = runtime return state, nil diff --git a/sdk/cliproxy/service.go b/sdk/cliproxy/service.go index 9d6a34d5..23c37614 100644 --- a/sdk/cliproxy/service.go +++ b/sdk/cliproxy/service.go @@ -10,7 +10,7 @@ import ( "time" "github.com/router-for-me/CLIProxyAPI/v6/internal/api" - geminiwebclient "github.com/router-for-me/CLIProxyAPI/v6/internal/client/gemini-web" + geminiwebclient "github.com/router-for-me/CLIProxyAPI/v6/internal/provider/gemini-web" "github.com/router-for-me/CLIProxyAPI/v6/internal/config" "github.com/router-for-me/CLIProxyAPI/v6/internal/registry" "github.com/router-for-me/CLIProxyAPI/v6/internal/runtime/executor"