Ctrl K
ComunidadGuías…

internal/pluginhost/plugin_refresh_compat_executor.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package pluginhost

import (
	"context"
	"fmt"
	"net/http"
	"strings"

	"github.com/router-for-me/CLIProxyAPI/v8/internal/config"
	"github.com/router-for-me/CLIProxyAPI/v8/internal/runtime/executor/helps"
	coreauth "github.com/router-for-me/CLIProxyAPI/v8/sdk/cliproxy/auth"
	cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v8/sdk/cliproxy/executor"
)

// pluginRefreshCompatExecutor keeps native OpenAI-compat inference while
// routing credential refresh to a plugin AuthProvider.
//
// Plugins often set Attributes["base_url"] so host routing uses the built-in
// OpenAI-compat executor. That binding previously swallowed refresh because
// OpenAICompatExecutor.Refresh is a no-op for non-Home providers. This wrapper
// preserves native Execute* paths and delegates Refresh to Host.RefreshAuth.
type pluginRefreshCompatExecutor struct {
	inner    coreauth.ProviderExecutor
	host     *Host
	cfg      *config.Config
	provider string
}

// NewPluginRefreshCompatExecutor wraps a native provider executor so Refresh is
// handled by the plugin AuthProvider for the same provider key.
func NewPluginRefreshCompatExecutor(inner coreauth.ProviderExecutor, host *Host, cfg *config.Config) coreauth.ProviderExecutor {
	if inner == nil {
		return nil
	}
	provider := strings.ToLower(strings.TrimSpace(inner.Identifier()))
	return &pluginRefreshCompatExecutor{
		inner:    inner,
		host:     host,
		cfg:      cfg,
		provider: provider,
	}
}

// IsPluginRefreshCompatExecutor reports whether executor is a plugin-refresh wrapper.
func IsPluginRefreshCompatExecutor(executor coreauth.ProviderExecutor) bool {
	_, ok := executor.(*pluginRefreshCompatExecutor)
	return ok
}

// UnwrapPluginRefreshCompatExecutor returns the inner native executor when executor
// is a plugin-refresh wrapper.
func UnwrapPluginRefreshCompatExecutor(executor coreauth.ProviderExecutor) (coreauth.ProviderExecutor, bool) {
	wrapper, ok := executor.(*pluginRefreshCompatExecutor)
	if !ok || wrapper == nil || wrapper.inner == nil {
		return nil, false
	}
	return wrapper.inner, true
}

func (e *pluginRefreshCompatExecutor) UsesConfig(cfg *config.Config) bool {
	return e != nil && e.cfg == cfg
}

func (e *pluginRefreshCompatExecutor) Identifier() string {
	if e == nil {
		return ""
	}
	if e.provider != "" {
		return e.provider
	}
	if e.inner != nil {
		return e.inner.Identifier()
	}
	return ""
}

func (e pluginRefreshCompatExecutor) ForAPIKey() coreauth.ProviderExecutor {
	if scoped, ok := e.inner.(coreauth.APIKeyConfigExecutor); ok {
		e.inner = scoped.ForAPIKey()
	}
	return &e
}

func (e *pluginRefreshCompatExecutor) Execute(ctx context.Context, auth *coreauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (cliproxyexecutor.Response, error) {
	if e == nil || e.inner == nil {
		return cliproxyexecutor.Response{}, fmt.Errorf("plugin refresh compat executor is unavailable")
	}
	return e.inner.Execute(ctx, auth, req, opts)
}

func (e *pluginRefreshCompatExecutor) ExecuteStream(ctx context.Context, auth *coreauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (*cliproxyexecutor.StreamResult, error) {
	if e == nil || e.inner == nil {
		return nil, fmt.Errorf("plugin refresh compat executor is unavailable")
	}
	return e.inner.ExecuteStream(ctx, auth, req, opts)
}

func (e *pluginRefreshCompatExecutor) CountTokens(ctx context.Context, auth *coreauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (cliproxyexecutor.Response, error) {
	if e == nil || e.inner == nil {
		return cliproxyexecutor.Response{}, fmt.Errorf("plugin refresh compat executor is unavailable")
	}
	return e.inner.CountTokens(ctx, auth, req, opts)
}

func (e *pluginRefreshCompatExecutor) HttpRequest(ctx context.Context, auth *coreauth.Auth, req *http.Request) (*http.Response, error) {
	if e == nil || e.inner == nil {
		return nil, fmt.Errorf("plugin refresh compat executor is unavailable")
	}
	return e.inner.HttpRequest(ctx, auth, req)
}

// PrepareRequest forwards credential injection to the inner executor when supported.
func (e *pluginRefreshCompatExecutor) PrepareRequest(req *http.Request, auth *coreauth.Auth) error {
	if e == nil || e.inner == nil {
		return fmt.Errorf("plugin refresh compat executor is unavailable")
	}
	preparer, ok := e.inner.(interface {
		PrepareRequest(*http.Request, *coreauth.Auth) error
	})
	if !ok || preparer == nil {
		return nil
	}
	return preparer.PrepareRequest(req, auth)
}

func (e *pluginRefreshCompatExecutor) Refresh(ctx context.Context, auth *coreauth.Auth) (*coreauth.Auth, error) {
	if e == nil {
		return nil, fmt.Errorf("plugin refresh compat executor is unavailable")
	}
	if ctx == nil {
		ctx = context.Background()
	}
	if refreshed, handled, errHome := helps.RefreshAuthViaHome(ctx, e.cfg, auth); handled {
		return refreshed, errHome
	}
	if e.host != nil {
		if refreshed, handled, errRefresh := e.host.RefreshAuth(ctx, auth); handled {
			return refreshed, errRefresh
		}
	}
	if authHasRefreshToken(auth) {
		provider := e.Identifier()
		if provider == "" && auth != nil {
			provider = strings.TrimSpace(auth.Provider)
		}
		return nil, fmt.Errorf("plugin auth provider refresh is unavailable for provider %s", provider)
	}
	if auth == nil {
		return nil, nil
	}
	return auth.Clone(), nil
}

func authHasRefreshToken(auth *coreauth.Auth) bool {
	if auth == nil || auth.Metadata == nil {
		return false
	}
	if token, _ := auth.Metadata["refresh_token"].(string); strings.TrimSpace(token) != "" {
		return true
	}
	if token, _ := auth.Metadata["refreshToken"].(string); strings.TrimSpace(token) != "" {
		return true
	}
	return false
}

// SupportsApplyPatch delegates only the inner executor's optional contract.
func (e *pluginRefreshCompatExecutor) SupportsApplyPatch() bool {
	if e == nil || e.inner == nil {
		return false
	}
	support, okSupport := e.inner.(coreauth.ApplyPatchSupport)
	return okSupport && support.SupportsApplyPatch()
}