Help us shape the future of Go! Take the 2026 Go Developer Survey

proxy

package
v0.3.20 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 5, 2026 License: MIT Imports: 39 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewArtifactGzipWriter added in v0.3.13

func NewArtifactGzipWriter(w io.Writer) *gzip.Writer

NewArtifactGzipWriter wraps any writer in the one gzip codec for saved artifacts. The caller owns Close on the normal path so the deflate trailer flushes; an abandoned stream is a correct truncated artifact.

func WriteArtifactHeaders added in v0.3.13

func WriteArtifactHeaders(w http.ResponseWriter, kind, ext, contentType string, at time.Time, fallback string)

WriteArtifactHeaders is THE owner of a downloaded artifact's response headers, shared by the backup, logs export and capture download surfaces: it composes the millivolt-<kind>-<stamp>.<ext> attachment name - the stamp is the artifact's moment in artifactStampLayout, always rendered UTC, one naming policy for every surface - and stages Content-Disposition, the surface's content type and Cache-Control: no-store, so no shared cache retains a downloaded artifact. fallback replaces the stamp segment when the artifact's moment is unknown (the capture route's damaged-payload row names the record id); a surface whose moment is always known passes the empty string beside a non-zero moment. Precondition: at must be non-zero or fallback non-empty - a zero moment with an empty fallback would publish a name with an empty stamp segment - and fallback must be quote-safe (no double quote or backslash): the name is spliced inside a quoted Content-Disposition, and a quote-bearing fallback would break the header. Unreachable today: every surface passes a real moment (the backup and export pass time.Now(), the capture passes the document's decoded moment or the record-id fallback, and the only realizable fallback is the minted request id, which is quote-safe).

Types

type ConversationTracker

type ConversationTracker struct {
	// contains filtered or unexported fields
}

ConversationTracker assigns a stable conversation id to each request. It is safe for concurrent use and bounds memory by expiring idle sessions lazily.

func NewConversationTracker

func NewConversationTracker(idleGap time.Duration, maxOpen int) *ConversationTracker

NewConversationTracker creates a tracker. idleGap is the inactivity window after which a request is treated as a new conversation; maxOpen caps concurrently-open conversations per partition (oldest evicted past it).

func (*ConversationTracker) Assign

func (t *ConversationTracker) Assign(client, keyHash, explicitID, trackedID string, totalTurns int, now time.Time) string

Assign returns the conversation id for a request. explicitID (from X-Proxy-Session) wins when present, then trackedID (a sub_conversations body-param value); each is namespaced by its own owner so neither identity can collide with the other or with an auto id. Otherwise the request is auto-grouped by turn count. totalTurns is turns_user+turns_assistant+turns_tool.

func (*ConversationTracker) UpdateLimits

func (t *ConversationTracker) UpdateLimits(idleGap time.Duration, maxOpen int)

UpdateLimits hot-applies new idleGap/maxOpen on a live tracker (config reload). Existing sessions are unaffected; the new limits govern subsequent matching, eviction, and sweeps.

type Server

type Server struct {

	// ModelObserver is wired before serving. Debug state uses the same Go
	// canonical-name metadata provider as all other dashboard observers.
	ModelObserver func([]string) any
	// contains filtered or unexported fields
}

Server is the transparent reverse proxy. A single Server shares one http.Transport so upstream connections are pooled across all requests.

func New

func New(cfg *config.Config, rec metrics.Recorder) *Server

New builds a Server. rec may be nil (uses a Noop recorder). All tunables come from cfg (see internal/config); nothing is hardcoded here.

func (*Server) AttachPausePersist

func (s *Server) AttachPausePersist(p pausePersist)

AttachPausePersist restores the hold from durable storage and seeds the known-client / known-provider lists. Call once after New when a store is open.

func (*Server) DebugSnapshot

func (s *Server) DebugSnapshot() map[string]any

DebugSnapshot builds the GET /admin/debug state document - the single owner of that shape, shared by the HTTP handler and the dashboard bootstrap payload (so the two can never disagree).

func (*Server) HandleDebug

func (s *Server) HandleDebug(w http.ResponseWriter, r *http.Request)

HandleDebug is GET/POST /admin/debug. GET returns current sessions; POST applies {"enabled": bool} (required - deny by default). enabled:true adds one session (clients/providers/models AND, duration). enabled:true with id updates that session in place. A replace that omits scope / duration keeps the previous values. enabled:false clears everything, or just {"id"} when set. Overlapping filters return 409.

func (*Server) HandleDebugCapture

func (s *Server) HandleDebugCapture(w http.ResponseWriter, r *http.Request)

HandleDebugCapture is GET /admin/debug/capture?id= - the drawer fetch for one sidecar document. 404 when missing/expired. Never on the live SSE path. The plain response is the drawer's render JSON; download=1 serves the same document as a saved gzip artifact. The query is parsed from the raw string through the shared strict owner (adminjson.StrictQuery) so no malformed pair is silently dropped: an unparsable query, a repeated flag or id, or a flag outside the standing 0/1 grammar is a 400, never first-wins; an empty download value means the render mode. The kinship with the export owner stops at unknown keys, a deliberate split in the three-class route taxonomy: a filter grammar (the export) rejects unknown keys, an idempotent fetch like this one ignores them - only the consumed id and download are validated - and a mutating closed-set route (the backup and restore plane) rejects every key outside its consumed set. The method, id, no-store and 404 semantics are identical in both modes.

func (*Server) HandlePause

func (s *Server) HandlePause(w http.ResponseWriter, r *http.Request)

HandlePause is GET/POST /admin/pause. GET returns current state; POST applies {"paused": bool} (required - deny by default). paused:true adds one hold (all/new/clients/providers/duration/max_queued). paused:true with id updates that hold in place (queued waiters stay parked). A replace that omits scope / duration keeps the previous values - {"paused":true,"id"} is not a global hold. Compat {"paused":true} with no id and no scope still is. paused:false clears everything, or just {"id"} when set. In-flight requests are never cancelled.

func (*Server) HandleQuotaPause added in v0.3.13

func (s *Server) HandleQuotaPause(w http.ResponseWriter, r *http.Request)

HandleQuotaPause serves GET /admin/quota (the shared storm snapshot, which includes open quota gates) and POST {"provider": string, "resume": true}, the operator action that closes a provider's open retry-mode quota gate without waiting for the next recovery probe; parked sends resume immediately. Resuming a provider without an open gate is a no-op state report (the pause may have recovered between banner and click). Manual-mode quota holds are ordinary operator holds and resume through the pause surface, not here.

func (*Server) HandleThrottle

func (s *Server) HandleThrottle(w http.ResponseWriter, r *http.Request)

HandleThrottle is GET/POST /admin/throttle. GET returns every active cap plus known providers (history ∪ current caps) so the dashboard can set a limit on a seen provider. POST requires {"provider"} (deny by default). clear:true drops the cap. Otherwise any supplied dimension merges; omitted dimensions keep their current value; 0 / empty window turns that dimension off.

func (*Server) PauseSnapshot

func (s *Server) PauseSnapshot() map[string]any

PauseSnapshot builds the GET /admin/pause state document - the single owner of that shape, shared by the HTTP handler and the dashboard bootstrap payload (so the two can never disagree).

func (*Server) Reload

func (s *Server) Reload(cfg *config.Config)

Reload hot-applies a freshly-loaded config with ZERO dropped requests: the listener and HTTP server are never touched, so no connection can be dropped. It swaps the config snapshot (per-request reads pick it up atomically), rebuilds the upstream transport (new pool sizes + header timeout apply to new connections; in-flight streams keep the transport they started on), and re-applies the scheduler/conversation tunables. Fields that are bound at process startup and cannot move (listen address, db path, storage pipeline, HTTP server timeouts, history size) are NOT re-applied - they require a restart and are the caller's responsibility to flag.

func (*Server) ServeHTTP

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler.

func (*Server) StormSnapshot added in v0.3.0

func (s *Server) StormSnapshot() map[string]any

StormSnapshot shares scheduler-authoritative status with embedded bootstrap and ordinary dashboard refreshes. It does no history or storage work.

func (*Server) ThrottleSnapshot

func (s *Server) ThrottleSnapshot() map[string]any

ThrottleSnapshot builds the GET /admin/throttle state document - the single owner of that shape, shared by the HTTP handler and the dashboard bootstrap payload (so the two can never disagree).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL