vibe-proxy/backend/sdk/api/handlers/handlers_context.go
2026-08-24 00:10:41 +02:00

208 lines
5.8 KiB
Go

package handlers
import (
"net/http"
"net/url"
"strings"
"sync"
"github.com/gin-gonic/gin"
"golang.org/x/net/context"
)
type pinnedAuthContextKey struct{}
type selectedAuthCallbackContextKey struct{}
type preparedModelRouteContextKey struct{}
type executionSessionContextKey struct{}
type disallowFreeAuthContextKey struct{}
type nestedExecutionTrackerKey struct{}
type nestedExecutionTracker struct {
mu sync.Mutex
called bool
}
func (t *nestedExecutionTracker) mark() {
if t == nil {
return
}
t.mu.Lock()
t.called = true
t.mu.Unlock()
}
func (t *nestedExecutionTracker) hasNestedExecution() bool {
if t == nil {
return false
}
t.mu.Lock()
defer t.mu.Unlock()
return t.called
}
func withNestedExecutionTracker(ctx context.Context) (context.Context, *nestedExecutionTracker) {
if ctx == nil {
ctx = context.Background()
}
if existing, ok := ctx.Value(nestedExecutionTrackerKey{}).(*nestedExecutionTracker); ok && existing != nil {
return ctx, existing
}
tracker := &nestedExecutionTracker{}
return context.WithValue(ctx, nestedExecutionTrackerKey{}, tracker), tracker
}
func markNestedExecution(ctx context.Context) {
if ctx == nil {
return
}
if tracker, ok := ctx.Value(nestedExecutionTrackerKey{}).(*nestedExecutionTracker); ok && tracker != nil {
tracker.mark()
}
}
// WithPinnedAuthID returns a child context that requests execution on a specific auth ID.
func WithPinnedAuthID(ctx context.Context, authID string) context.Context {
authID = strings.TrimSpace(authID)
if authID == "" {
return ctx
}
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, pinnedAuthContextKey{}, authID)
}
// WithSelectedAuthIDCallback returns a child context that receives the selected auth ID.
func WithSelectedAuthIDCallback(ctx context.Context, callback func(string)) context.Context {
if callback == nil {
return ctx
}
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, selectedAuthCallbackContextKey{}, callback)
}
// PrepareStreamModelRoute resolves a stream route once and stores it on the returned context for execution.
// The boolean reports whether the route overrides normal model-to-provider resolution.
func (h *BaseAPIHandler) PrepareStreamModelRoute(ctx context.Context, handlerType string, modelName string, rawJSON []byte) (context.Context, bool) {
if ctx == nil {
ctx = context.Background()
}
decision := h.applyModelRouter(ctx, handlerType, modelName, rawJSON, true, modelExecutionOptions{})
ctx = context.WithValue(ctx, preparedModelRouteContextKey{}, decision)
hasOverride := strings.TrimSpace(decision.ExecutorPluginID) != "" || strings.TrimSpace(decision.Provider) != ""
return ctx, hasOverride
}
func preparedModelRouteFromContext(ctx context.Context, skipRouterPluginID string) (modelRouteDecision, bool) {
// A host.model.execute_stream callback is a nested execution. Its caller is
// excluded from model routing, so an outer prepared route cannot be reused:
// it may point straight back at that caller.
if ctx == nil || strings.TrimSpace(skipRouterPluginID) != "" {
return modelRouteDecision{}, false
}
decision, ok := ctx.Value(preparedModelRouteContextKey{}).(modelRouteDecision)
return decision, ok
}
// WithExecutionSessionID returns a child context tagged with a long-lived execution session ID.
func WithExecutionSessionID(ctx context.Context, sessionID string) context.Context {
sessionID = strings.TrimSpace(sessionID)
if sessionID == "" {
return ctx
}
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, executionSessionContextKey{}, sessionID)
}
// WithDisallowFreeAuth returns a child context that requests skipping known free-tier credentials.
func WithDisallowFreeAuth(ctx context.Context) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, disallowFreeAuthContextKey{}, true)
}
// headersFromContext extracts the original HTTP request headers from the gin context
// embedded in the provided context. This allows session affinity selectors to read
// client-provided session headers.
func headersFromContext(ctx context.Context) http.Header {
if ctx == nil {
return nil
}
if ginCtx, ok := ctx.Value("gin").(*gin.Context); ok && ginCtx != nil && ginCtx.Request != nil {
return ginCtx.Request.Header.Clone()
}
return nil
}
// queryFromContext extracts the original HTTP request query parameters from the
// gin context embedded in the provided context. Mirrors headersFromContext so
// model routers can observe inbound query parameters for plain HTTP requests,
// where execOptions.Query is not populated by callers.
func queryFromContext(ctx context.Context) url.Values {
if ctx == nil {
return nil
}
if ginCtx, ok := ctx.Value("gin").(*gin.Context); ok && ginCtx != nil && ginCtx.Request != nil && ginCtx.Request.URL != nil {
return ginCtx.Request.URL.Query()
}
return nil
}
func pinnedAuthIDFromContext(ctx context.Context) string {
if ctx == nil {
return ""
}
raw := ctx.Value(pinnedAuthContextKey{})
switch v := raw.(type) {
case string:
return strings.TrimSpace(v)
case []byte:
return strings.TrimSpace(string(v))
default:
return ""
}
}
func selectedAuthIDCallbackFromContext(ctx context.Context) func(string) {
if ctx == nil {
return nil
}
raw := ctx.Value(selectedAuthCallbackContextKey{})
if callback, ok := raw.(func(string)); ok && callback != nil {
return callback
}
return nil
}
func executionSessionIDFromContext(ctx context.Context) string {
if ctx == nil {
return ""
}
raw := ctx.Value(executionSessionContextKey{})
switch v := raw.(type) {
case string:
return strings.TrimSpace(v)
case []byte:
return strings.TrimSpace(string(v))
default:
return ""
}
}
func disallowFreeAuthFromContext(ctx context.Context) bool {
if ctx == nil {
return false
}
raw, ok := ctx.Value(disallowFreeAuthContextKey{}).(bool)
return ok && raw
}