serve

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package serve runs a local model server and keeps track of it. It is the step between a built launch plan (a serve command that has not been started) and a model a client can reach: it starts the runtime as a confined background process, waits for its loopback endpoint to answer, and records the running server so a later command can find, reuse, or stop it. Starting a server that is already up is a no-op, so the same call both starts and reuses.

The runtime is the code-execution surface that parses untrusted weights, so it is started inside the sandbox rather than as a bare child. The package depends on small interfaces for launching, probing, and killing, so the whole lifecycle is testable with fakes and no live runtime, while the real wiring uses the sandbox and HTTP.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EmitStats

func EmitStats(ctx context.Context, m observe.Meter, modelID string, s RuntimeStats)

EmitStats records a load snapshot on the meter so a host's telemetry backend sees the same numbers the control plane reads. The Meter port offers counters and histograms but no gauge instrument, so the snapshot's gauge-like values are recorded as histogram observations, which a backend renders as the per-model distribution and last value. A snapshot with Known false, or a nil meter, records nothing.

func EngineStopper

func EngineStopper(engine, id string) error

EngineStopper stops a container-backed server by driving the OCI engine, the production ContainerStopper. It bounds the stop with its own timeout inside the sandbox helper, so a `models stop` for a vLLM container tears it down the same way OSKiller stops a process.

func OSKiller

func OSKiller(pid int) error

OSKiller stops a process by pid through the operating system, the production Killer. It is used to stop a server a previous Flynn invocation started, where only the pid survives in the registry. Killing an already-gone process is reported by the OS and surfaced to the caller, which treats "no such process" as already stopped.

Types

type ContainerEnsureConfig

type ContainerEnsureConfig struct {
	// ModelID is the catalog id, the registry key.
	ModelID string
	// Runtime names the runtime, recorded for display (for example "vllm").
	Runtime string
	// Spec is the validated container request: the digest-pinned image, the untrusted
	// guarantees, the GPU grant, the published loopback port, and the server command.
	Spec sandbox.ContainerSpec
	// BaseURL and Port are the loopback endpoint the served model answers on, the same
	// OpenAI-compatible coordinates a process-backed server reports.
	BaseURL string
	Port    int
	// ReadyTimeout overrides how long to wait for the endpoint to come up. A container
	// runtime can be far slower to first answer than a process (a GPU server loads weights,
	// captures CUDA graphs, and compiles kernels on first start), so the caller raises it
	// above the manager's process-oriented default. Zero uses the manager default.
	ReadyTimeout time.Duration
}

ContainerEnsureConfig is the input to EnsureContainer: which model, the validated container request, and the loopback endpoint to probe. The Spec's Command is the server invocation (the vLLM serve argv), built by the caller from the launch plan.

type ContainerStopper

type ContainerStopper func(engine, id string) error

ContainerStopper stops a container-backed server by its engine and id, the container counterpart to Killer: a container has no host pid, so a separate Flynn process stops it by driving the engine. The real stopper runs the engine CLI; a test stopper records the call. A nil stopper means container-backed records cannot be stopped across invocations, so the production wiring always supplies one.

type Endpoint

type Endpoint struct {
	// ModelID is the catalog id being served.
	ModelID string
	// BaseURL is the OpenAI-compatible endpoint a client targets.
	BaseURL string
	// Port is the loopback port the server listens on.
	Port int
	// PID is the server process id.
	PID int
	// Reused is true when an already-running server was adopted instead of started.
	Reused bool
	// contains filtered or unexported fields
}

Endpoint is a running local model server reachable over loopback.

func (Endpoint) Stop

func (e Endpoint) Stop() error

Stop ends a server this call started. It is only meaningful for a freshly started, non-reused endpoint; for a reused one the owning process or `models stop` controls the lifecycle. It does not remove the registry record.

type EnsureConfig

type EnsureConfig struct {
	// ModelID is the catalog id, the registry key.
	ModelID string
	// Runtime names the runtime, recorded for display.
	Runtime string
	// Plan is the built, not-yet-started serve command and its loopback address.
	Plan launch.Plan
	// Confine requests the sandbox kernel confinement for the runtime process, which is
	// the right default since the runtime parses untrusted weights.
	Confine bool
}

EnsureConfig is the input to Ensure: which model, the built serve plan, and how it is run.

type Killer

type Killer func(pid int) error

Killer stops a server identified only by its pid, the case where the server was started by an earlier, now-gone Flynn process and no Proc handle is held. The real killer signals the OS process; a test killer records the pid.

type Launcher

type Launcher interface {
	Serve(ctx context.Context, spec sandbox.ServeSpec) (Proc, error)
}

Launcher starts a server process for a serve plan. The real launcher runs it inside the sandbox; a test launcher returns a fake Proc.

type Manager

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

Manager starts, reuses, reports, and stops local model servers. Its dependencies are injected so the lifecycle is exercised without a live runtime.

func NewManager

func NewManager(l Launcher, probe Prober, kill Killer, reg *Registry, opts ...Option) *Manager

NewManager builds a Manager from its launcher, prober, killer, and registry.

func (*Manager) Ensure

func (m *Manager) Ensure(ctx context.Context, cfg EnsureConfig) (Endpoint, error)

Ensure returns a running endpoint for the model, starting the server if one is not already up. If the registry already has a server for this model and its endpoint answers a health probe, that server is adopted (Reused). Otherwise a stale record is pruned, the server is started inside the sandbox from the plan, the manager waits for its endpoint to answer, records it, and returns it. A server that starts but never becomes ready, or that exits while starting, is stopped and reported as an error with its captured output, so a launch never leaves an orphan behind.

func (*Manager) EnsureContainer

func (m *Manager) EnsureContainer(ctx context.Context, cfg ContainerEnsureConfig) (Endpoint, error)

EnsureContainer returns a running endpoint for a container-backed model, the container counterpart to Ensure. It reuses an already-running server when its endpoint answers, otherwise runs the container under the tier's guarantees, waits for the endpoint to come up, records it with its container identity (so a later process can stop it), and returns it. A container that starts but never becomes ready, or exits while starting, is stopped and reported with its captured output, so a launch never leaks a container. It drives the same registry and reuse path as Ensure, so the scheduler and `models status`/`stop` see process- and container-backed servers uniformly: one supervisor, two runtime shapes.

func (*Manager) Stats

func (m *Manager) Stats(ctx context.Context, modelID string) (RuntimeStats, error)

Stats reads a best-effort load snapshot for a running model. It returns an error only when there is no recorded server for the model. A server that is present but cannot be read (no source for its runtime, an unreachable or hung endpoint, an unparseable reply) yields a snapshot with Known false, never an error, so the control plane can always read the load without a failing call breaking it. The reading is normalized and stamped with the manager clock.

func (*Manager) Status

func (m *Manager) Status(ctx context.Context) ([]Record, error)

Status returns the recorded servers whose endpoint currently answers, pruning any record that no longer does so the report reflects reality rather than stale claims. A pruned server's process is reclaimed: a record is only written after the server became ready, so an endpoint that has stopped answering means the runtime died or wedged, and a wedged runtime left running would keep holding device memory after the manager has forgotten it. Killing it before dropping the record prevents that leak; killing an already-exited process is a no-op.

func (*Manager) Stop

func (m *Manager) Stop(modelID string) (bool, error)

Stop ends the server for a model id and removes its record. It returns whether a server was found to stop. The process may belong to an earlier Flynn invocation, so it is stopped by pid through the killer rather than through a Proc handle.

type NopStatsSource

type NopStatsSource struct{}

NopStatsSource reports every runtime as unknown load. It is the default for a runtime with no registered source, so the standalone path stays zero-setup.

func (NopStatsSource) Stats

Stats implements StatsSource by always reporting an unknown load.

type Option

type Option func(*Manager)

Option configures a Manager.

func WithContainerStopper

func WithContainerStopper(s ContainerStopper) Option

WithContainerStopper sets how a container-backed server is stopped across invocations, so `models stop` and the scheduler's evict can tear down a vLLM container the same way they kill a process-backed server. Without one, a container record cannot be stopped by a later process, so the production wiring always supplies it.

func WithPollInterval

func WithPollInterval(d time.Duration) Option

WithPollInterval sets how often Ensure probes a starting server.

func WithReadyTimeout

func WithReadyTimeout(d time.Duration) Option

WithReadyTimeout sets how long Ensure waits for a started server to become ready.

func WithStatsSource

func WithStatsSource(runtime string, src StatsSource) Option

WithStatsSource registers a load reader for servers of a given runtime, keyed by the runtime name recorded on each server (for example "llama.cpp"). Without one for a runtime, Stats reports the load as unknown rather than failing.

type Prober

type Prober func(ctx context.Context, baseURL string) error

Prober reports whether the model endpoint at baseURL is answering. It returns nil once the server is ready, and an error while it is not. The real prober makes a loopback HTTP request; a test prober scripts readiness.

func HTTPProbe

func HTTPProbe(client *http.Client) Prober

HTTPProbe builds a Prober that reports readiness by requesting the model list off the OpenAI-compatible endpoint. A loopback model server answers that route once it has loaded the weights and is ready to serve, so a 2xx is the readiness signal. The probe uses a short per-request timeout so a hung connection does not stall the poll loop.

type Proc

type Proc interface {
	PID() int
	Running() bool
	Output() string
	Done() <-chan struct{}
	Stop() error
}

Proc is the handle to a started background server the manager needs: enough to know whether it is up, read why it failed, wait for it, and stop it. *sandbox.Process satisfies it; a test supplies a fake.

type PromStatsSource

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

PromStatsSource reads a running runtime's Prometheus metrics endpoint over loopback and maps the named metric families onto a RuntimeStats snapshot. The metric names differ per runtime, so the mapping is injected: LlamaCppStatsSource and VLLMStatsSource supply the two dialects Flynn serves. The read is time-boxed and size-bounded, so a hung or hostile endpoint cannot stall or exhaust the caller.

func LlamaCppStatsSource

func LlamaCppStatsSource(client *http.Client) PromStatsSource

LlamaCppStatsSource reads load stats from a llama.cpp server's metrics endpoint. A nil client gets a default with a short per-request timeout.

func VLLMStatsSource

func VLLMStatsSource(client *http.Client) PromStatsSource

VLLMStatsSource reads load stats from a vLLM server's metrics endpoint. A nil client gets a default with a short per-request timeout.

func (PromStatsSource) Stats

func (s PromStatsSource) Stats(ctx context.Context, baseURL string) (RuntimeStats, error)

Stats scrapes the runtime's metrics endpoint and maps it onto a snapshot. A transport error, a non-2xx reply, or a read error is returned as an error for the manager to turn into an unknown reading; a 2xx body that names none of the mapped metrics yields a snapshot with Known false.

type Record

type Record struct {
	// ModelID is the catalog id the server runs, the key a reuse or stop looks up.
	ModelID string `json:"modelID"`
	// PID is the operating-system process id, so a separate process can stop the server. It
	// is set for a process-backed server (the runtime runs as a host process); a
	// container-backed server has no host pid and carries ContainerID instead.
	PID int `json:"pid"`
	// ContainerID and Engine identify a container-backed server, so a later, separate Flynn
	// process can stop it by driving the engine (a container has no host pid to signal).
	// They are empty for a process-backed server. Exactly one of PID or ContainerID
	// identifies a given server.
	ContainerID string `json:"containerID,omitempty"`
	Engine      string `json:"engine,omitempty"`
	// Port and BaseURL are the loopback coordinates a client targets.
	Port    int    `json:"port"`
	BaseURL string `json:"baseURL"`
	// Runtime names the runtime serving the model, for display.
	Runtime string `json:"runtime,omitempty"`
	// StartedAt is the Unix time the server was started, for display.
	StartedAt int64 `json:"startedAt,omitempty"`
}

Record is the persisted description of a model server that was started, so a later, separate Flynn process (a `models status` or `models stop` invocation) can find it, report it, reach it, or kill it. It holds only loopback coordinates and a pid; no secret is ever recorded.

type Registry

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

Registry persists the set of running model servers to a single JSON file under the data directory, keyed by model id. It is the cross-process record that lets a fresh Flynn invocation see and control servers an earlier one started. Access is guarded by a mutex within a process; the file is rewritten atomically so a concurrent reader never sees a half-written file. It does not itself decide liveness: a record is a claim that a server was started, and the manager confirms it with a health probe before trusting it, pruning a record whose server no longer answers.

func NewRegistry

func NewRegistry(dir string) *Registry

NewRegistry returns a registry backed by servers.json under dir (created on first write). dir is typically the data directory's run area.

func (*Registry) Delete

func (r *Registry) Delete(modelID string) error

Delete removes the record for a model id, if present, and persists the registry. Deleting an absent id is not an error.

func (*Registry) Get

func (r *Registry) Get(modelID string) (Record, bool, error)

Get returns the record for a model id, and whether one is present.

func (*Registry) List

func (r *Registry) List() ([]Record, error)

List returns all recorded servers, sorted by model id for a stable display. A missing or unreadable file is treated as an empty registry, not an error, so a first run and a cleared registry behave the same.

func (*Registry) Put

func (r *Registry) Put(rec Record) error

Put inserts or replaces the record for its model id and persists the registry.

type RuntimeStats

type RuntimeStats struct {
	// Known is true when a usable reading was obtained and parsed. A false Known means the
	// snapshot carries no information and the load must be treated as unknown, not as zero.
	Known bool
	// KVCacheUsage is the fraction of the key/value cache in use, in [0,1]. It is the
	// primary memory-pressure signal an eviction policy reads.
	KVCacheUsage float64
	// RequestsRunning is the number of requests the server is actively decoding.
	RequestsRunning int
	// RequestsWaiting is the number of requests queued behind the running ones.
	RequestsWaiting int
	// DecodeTokensPerSec is the recent generation throughput, in tokens per second.
	DecodeTokensPerSec float64
	// PromptTokensPerSec is the recent prefill throughput, in tokens per second.
	PromptTokensPerSec float64
	// CollectedAt is when the reading was taken, stamped from the manager clock so it is
	// deterministic in tests and consistent across the process.
	CollectedAt time.Time
}

RuntimeStats is a point-in-time, best-effort reading of how loaded a running model server is: how full its key/value cache is and how many requests it is working on. It is the control-plane view the orchestrator and router read to make eviction and routing decisions, kept distinct from the metrics pushed to a backend through observe.Meter.

Fields are best-effort. A runtime that does not report a value leaves it at its zero, and Known reports whether any usable reading was obtained at all: a false Known means the snapshot carries no information (the runtime was unreachable, hung, or spoke an unparseable dialect) and a reader must treat the load as unknown rather than as zero.

type SandboxLauncher

type SandboxLauncher struct {
	// SB is the sandbox the server runs inside. Its working directory is where the
	// runtime is confined and its environment is the deny-by-default baseline.
	SB *sandbox.Local
}

SandboxLauncher starts a server process inside a Local sandbox, the production Launcher. The runtime parses untrusted weights, so running it through the sandbox rather than as a bare child is the point: confinement is applied per the Local's configuration when the serve spec requests it.

func (SandboxLauncher) Serve

func (s SandboxLauncher) Serve(ctx context.Context, spec sandbox.ServeSpec) (Proc, error)

Serve starts the spec's command in the sandbox and adapts the concrete process handle to the Proc the manager consumes.

type StatsSource

type StatsSource interface {
	Stats(ctx context.Context, baseURL string) (RuntimeStats, error)
}

StatsSource reads a load snapshot from a runtime serving at a loopback base URL. There is one implementation per runtime dialect, because the metric names differ. It returns a snapshot with Known false (and a nil error) when the runtime is reachable but reports nothing usable, and an error only when the read itself fails, which the manager turns into an unknown reading.

Jump to

Keyboard shortcuts

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