nexus

package module
v1.9.2 Latest Latest
Warning

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

Go to latest
Published: Nov 4, 2026 License: MIT Imports: 29 Imported by: 0

README

Nexus, an AI gateway library for Go with an OpenAI-compatible proxy, provider routing, guardrails and usage budgets

Nexus: AI gateway and LLM proxy library for Go

Nexus is an open-source AI gateway library for Go. You put it in front of OpenAI, Anthropic, Gemini, Bedrock, Ollama and 25 other LLM providers, and every request goes through one pipeline: provider routing, model aliases, response caching, PII and prompt-injection guardrails, multi-tenant gateway keys, rate limits and monthly budgets, and usage tracking with exact cost. It serves an OpenAI-compatible API with SSE, NDJSON and WebSocket streaming, and it runs inside your own process, either standalone or as a Forge extension.

CI Go Reference GitHub Stars

Documentation · Quick start · Features · Providers · Pipeline · Forge · Examples · FAQ

Quick start

You need Go 1.26 or later and an OpenAI API key. Each provider is its own Go module, so you add the root and the providers you call:

go get github.com/xraph/nexus@latest
go get github.com/xraph/nexus/providers/openai@latest

Save this as main.go:

package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/xraph/nexus"
	"github.com/xraph/nexus/model"
	"github.com/xraph/nexus/providers/openai"
	"github.com/xraph/nexus/proxy"
)

func main() {
	ctx := context.Background()

	gw := nexus.New(
		nexus.WithProvider(openai.New(os.Getenv("OPENAI_API_KEY"))),
		nexus.WithAlias("fast", model.AliasTarget{Provider: "openai", Model: "gpt-4o-mini"}),
		// An open gateway, for trying it out locally. Remove this line
		// before anyone else can reach the port.
		nexus.WithRequireAPIKey(false),
	)
	if err := gw.Initialize(ctx); err != nil {
		log.Fatal(err)
	}
	defer gw.Shutdown(ctx)

	srv := &http.Server{
		Addr:              ":8080",
		Handler:           proxy.New(gw.Engine()),
		ReadHeaderTimeout: 10 * time.Second,
	}
	log.Println("Nexus listening on http://localhost:8080/v1")
	if err := srv.ListenAndServe(); err != nil {
		log.Println(err)
	}
}

Run it, then call it the way you'd call OpenAI:

OPENAI_API_KEY=sk-... go run .

curl http://localhost:8080/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model": "fast", "messages": [{"role": "user", "content": "Say hello"}]}'

fast is an alias, so the alias stage rewrites the model to gpt-4o-mini before the provider call. Any OpenAI SDK works too: set its base URL to http://localhost:8080/v1. Add "stream": true to the body and you get server-sent events in the chat.completion.chunk shape the SDKs expect.

The gateway keeps tenants, keys and usage in memory here, so they're gone when you stop it. Drop the WithRequireAPIKey(false) line and every /v1 request needs an nxs_ gateway key. The proxy example creates a tenant and a key at startup, and Persistence shows where they go in production.

Why Nexus?

You probably started by calling one provider's SDK straight from your handlers. That works until you need a second provider, a budget per customer or a record of who spent what, and then each of your services grows its own half-finished copy of the same routing, retry and metering code. Nexus is that layer as a Go library. You compose a nexus.Gateway from options and mount it on your own server, so you don't deploy anything new.

You need Nexus gives you
Several LLM providers behind one API 30 released provider modules, an OpenAI-compatible HTTP API, and Engine for calls from Go
Control over which provider answers Routing to the provider a request names, its alias target or the provider that lists the model, then priority, cost, latency, round-robin or weighted strategies, plus model aliases with per-tenant overrides
Fewer paid calls for repeated prompts Response caching in memory or Redis, and record-and-replay caching for streamed responses
Safer prompts and outputs Guardrails for PII (block, redact or warn), prompt injection, blocklists and custom regex
Customers with their own limits Tenants, nxs_ gateway keys with scopes, RPM and TPM windows, daily request caps, monthly budgets in exact USD, and each tenant's own provider keys
Streaming that fits your client OpenAI-style SSE, Nexus SSE with named events, NDJSON, and a bidirectional WebSocket at /v1/realtime
A record of every request Usage records with tokens, latency and cost, OpenTelemetry spans and GenAI metrics, and 19 lifecycle hooks for audit, webhooks and metrics
Data in your own database PostgreSQL, SQLite and MongoDB stores through Grove, or in memory

Providers

Each provider lives in providers/<name> as its own module, so you only download what you import. They all implement provider.Provider, and most of the OpenAI-compatible ones wrap providers/openai.

Group Providers
Frontier APIs openai, anthropic, gemini, mistral, cohere, xai, deepseek, ai21
Cloud platforms azureopenai, bedrock, vertex, nvidia
Fast inference hosts groq, cerebras, sambanova, fireworks, together, deepinfra, hyperbolic, lepton, nebius, novita, anyscale, perplexity, openrouter
Embeddings only voyageai, jinaai
Local models ollama, lmstudio
Anything else that speaks OpenAI opencompat, with opencompat.New(name, baseURL, apiKey)

geminilive and openairealtime (realtime audio) are in the repo too, but they haven't been tagged yet, so you can't go get them at a released version.

The bedrock catalog is out of date. It still lists an old Claude id at its old price and wasn't updated in v1.9.0, so price the Bedrock models you call with nexus.WithPriceOverrides until it is.

Register as many as you like. A request goes to the provider it names in Provider, else to its alias target's provider, else to a provider whose catalog lists the model. Only a model nobody lists falls to the routing strategy, and without one Nexus uses the priority strategy, which goes by the order you registered providers in:

gw := nexus.New(
	nexus.WithProvider(openai.New(os.Getenv("OPENAI_API_KEY"))),
	nexus.WithProvider(anthropic.New(os.Getenv("ANTHROPIC_API_KEY"))),
	nexus.WithRouter(strategies.NewCostOptimized()),
)
Strategy Constructor
Priority (default) strategies.NewPriority(order ...string)
Cost-optimized strategies.NewCostOptimized()
Latency-optimized strategies.NewLatencyOptimized()
Round-robin strategies.NewRoundRobin()
Weighted strategies.NewWeighted(map[string]float64{"openai": 0.7, "anthropic": 0.3})

Call it from Go

You don't need HTTP at all. gw.Engine() sends requests through the same pipeline, guardrails and cache included:

gw := nexus.New(
	nexus.WithProvider(openai.New(os.Getenv("OPENAI_API_KEY"))),
	nexus.WithCache(stores.NewMemory()),
	nexus.WithGuard(guards.NewPII(guard.ActionRedact)),
	nexus.WithGuard(guards.NewInjection()),
)
if err := gw.Initialize(ctx); err != nil {
	log.Fatal(err)
}
defer gw.Shutdown(ctx)

resp, err := gw.Engine().Complete(ctx, &provider.CompletionRequest{
	Model:     "gpt-4o-mini",
	Messages:  []provider.Message{{Role: "user", Content: "What is the capital of France?"}},
	MaxTokens: 100,
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(resp.Choices[0].Message.Content, resp.Usage.TotalTokens)

Engine also has CompleteStream, Embed and ListModels. The streaming example reads a stream three ways: as an iterator, over a channel, and through an accumulator.

The request pipeline

Every request runs through the same chain of middleware, whether it came in over HTTP, over gRPC or through Engine. Stages are sorted by priority, lower first. A stage you didn't configure isn't in the chain at all, so you only pay for what you turn on:

Priority Stage When it's there
5 Request ID always
10 Telemetry: OpenTelemetry span and request metrics, then your own tracer always; the tracer with WithTracer
15 Usage recording unless usage is off
20 Timeout when the timeout is above zero (30s by default)
30, 40 Identity, then tenant status and key scopes always
50 Quota: token cap, daily requests, budget, RPM, TPM always, with the default usage service
60 Stream lifecycle hooks always, short-circuits for non-streams
150 Input and output guardrails WithGuard
155 Output guardrails for streams, buffered by default a guard that checks output, or WithStreamGuard
200 Transforms WithTransforms
250, 260 Alias resolution, then the tenant's model allow and block lists aliases only with WithAlias
280 Cache WithCache or WithStreamCache
340 Retry when retries are above zero (2 by default)
350 Provider call always, last

WithMiddleware adds your own stage at whatever priority it reports. A stage that turns a request away returns *nexus.RefusalError, with an HTTP status, a code and, when a limit applies, Limit and RetryAfter.

Transforms live in transform: NewSystemPrompt, NewRAG for retrieval context, NewAnonymizer and NewAnonymizerOutput, and NewNormalizer. Put them in a transform.NewRegistry() and pass it with WithTransforms.

Tenants, keys and persistence

A tenant carries a quota (RPM, TPM, DailyRequests, MonthlyBudgetUSD, MaxTokensPerReq, and stream caps) and its own allowed or blocked models. Gateway keys belong to a tenant, look like nxs_ plus 64 hex characters, carry scopes, and are stored as hashes. You create them through gw.Tenants() and gw.Keys() in Go, or over the admin API once you have an admin key.

If you don't pass a store, everything lives in memory and is gone when the process exits. For anything you want to keep, pass a Grove-backed store:

Backend Package
PostgreSQL store/postgres, postgres.New(db *grove.DB)
SQLite store/sqlite, sqlite.New(db *grove.DB)
MongoDB store/mongo, mongo.New(db *grove.DB)
Memory store.NewMemory()

Hand it to the gateway with nexus.WithDatabase(s) and call s.Migrate() once at startup. Money is money.USD, an exact decimal, so compare amounts with Equal and never with ==.

HTTP API

Two packages serve HTTP, and both are plain net/http handlers you can mount on any router:

Package Routes
proxy, proxy.New(gw.Engine()) POST /v1/chat/completions, POST /v1/embeddings, GET /v1/models, GET /v1/models/{model}, GET /health, /v1/realtime
api, api.New(gw).Handler() the same /v1 routes plus /admin/tenants, /admin/keys, /admin/usage and /admin/providers

The /admin routes always need a key with the admin scope, whatever RequireAPIKey says. Streaming responses pick their wire format from ?stream_format= (sse, nexus, ndjson) or the Accept header. grpcsrv is a separate module that serves completions over gRPC server-streaming, and the gRPC example shows it with key auth.

Use it with Forge

The extension package turns Nexus into a Forge extension. It reads config under extensions.nexus (or nexus), builds a store from a *grove.DB in the container when it finds one, migrates it on start, and registers *nexus.Gateway for the rest of your app.

import (
	"github.com/xraph/forge"
	nexusext "github.com/xraph/nexus/extension"
	"github.com/xraph/nexus/providers/openai"
)

app := forge.NewApp(forge.AppConfig{
	Name:        "ai-gateway",
	HTTPAddress: ":8080",
	Extensions: []forge.Extension{
		nexusext.New(
			nexusext.WithProvider(openai.New(os.Getenv("OPENAI_API_KEY"))),
		),
	},
})

The extension builds the gateway in Register and puts it in the container, so another extension can resolve it with vessel.Inject[*nexus.Gateway](app.Container()) from its own Register. Its engine answers nexus.ErrNotInitialized until Start has run. The extension doesn't register HTTP routes, so serve proxy.New(gw.Engine()) or api.New(gw).Handler() where you want the API to live. base_path and disable_routes are deprecated and ignored.

extensions:
  nexus:
    default_timeout: 30s
    default_max_retries: 2
    global_rate_limit: 1000   # requests per minute, 0 means unlimited
    require_api_key: true
    enable_usage: true
    enable_cache: false
    grove_database: ""        # a named grove.DB; empty uses the default one
    disable_migrate: false
    log_level: info

The admin API needs a first admin key, and no route can create one. Set NEXUS_BOOTSTRAP_ADMIN_KEY (or bootstrap_admin_key, though a secret in a committed YAML file stays there forever) to nxs_ followed by the output of openssl rand -hex 32. On start the extension stores it under a tenant with the slug operator, once, and never logs it.

Option What it does
WithProvider(p) Registers an LLM provider
WithDatabase(s) Uses this store, unless the container has a *grove.DB (see below)
WithGroveDatabase(name) Resolves a named *grove.DB from the container
WithGatewayOption(opt) Passes any nexus.Option through to the gateway
WithStreamCache(sc, opts), WithStreamLifecycleConfig(cfg) Stream caching and hook tuning
WithConfig(cfg), WithRequireConfig(true) Sets config in code, or fails Register when the YAML key is missing
WithDisableMigrate() Leaves migrations to you

WithDatabase doesn't stop Grove discovery. Register still looks for a *grove.DB (the named one from WithGroveDatabase, or the default one) and, when it finds it, builds a store from it after your options, so the container's store wins over the one you passed. If you want your own store, keep a *grove.DB out of the container.

The Forge extension guide covers how YAML and code options merge.

Documentation

Topic Read
First gateway Getting started, architecture
Providers, routing and aliases Providers, routing, aliases
Tenant keys and prices Tenant credentials, pricing
Caching, guardrails and transforms Caching, guardrails, transforms
Tenants and keys Multi-tenancy, identity
Streaming and the HTTP API Streaming guide, HTTP API, streaming API
Storage PostgreSQL, SQLite, MongoDB
Hooks, audit and tracing Plugins, audit, observability

The Go API is on pkg.go.dev, and each provider has its own page there under github.com/xraph/nexus/providers/<name>.

Upgrading? MIGRATION.md lists what changed in each release that can change how your code or your gateway behaves.

Examples

Example What it shows
Standalone A gateway with routing, cache, guardrails and an alias, called from Go
Proxy The OpenAI-compatible proxy with SSE, NDJSON, WebSocket, stream caching and a gateway key
Streaming Iterator, channel and accumulator stream APIs
Multi-tenant Tenants, gateway keys and usage tracking
gRPC Server-streaming completions over gRPC
Realtime, Live OpenAI Realtime and Gemini Live sessions
Forge Building the Forge extension

Most of them read OPENAI_API_KEY. The ones without a go.mod sit in the root module under _examples, which go build ./... skips, so run them by path: go run ./_examples/proxy.

FAQ

Can I use Nexus as an OpenAI-compatible proxy?

Yes. proxy.New(gw.Engine()) serves /v1/chat/completions, /v1/embeddings and /v1/models in OpenAI's request and response shapes. Point an OpenAI SDK's base URL at it and pass an nxs_ gateway key as the API key.

How do I set a per-customer budget or rate limit for LLM calls?

Create a tenant with a Quota: RPM, TPM, DailyRequests, MonthlyBudgetUSD and MaxTokensPerReq. Give the customer a key under that tenant. The quota stage refuses a request over any limit with a RefusalError that carries RetryAfter when waiting would help.

Does Nexus fall back to another provider when one fails?

The retry stage retries a failed call twice by default, with backoff (WithMaxRetries changes the count). Which provider answers is up to the routing strategy, and the priority strategy tries providers in registration order. A fallback package with a circuit breaker exists, but the default pipeline doesn't use it yet.

Can I use Nexus without Forge?

Yes. The root module, proxy, api and every provider work with plain net/http. The extension package is the only part that imports Forge.

Do I need an API key to run the tests?

No. Provider tests run against mock HTTP servers from testutil. Postgres and MongoDB store tests only run when you set NEXUS_TEST_POSTGRES_DSN or NEXUS_TEST_MONGO_URI.

Contributing

Issues and focused pull requests are welcome. Open an issue first for anything large.

git clone https://github.com/xraph/nexus.git
cd nexus
make test     # go test ./... in every module
make lint     # golangci-lint in every module
make fmt      # gofmt and goimports

The repo is many Go modules (the root, config, grpcsrv and one per provider), tied together by the committed go.work. Run a command with GOWORK=off when you want to see how one module resolves on its own. Skip make run, make dev and make build-bin, which point at a cmd/nexus that doesn't exist. AGENTS.md has the full command list, the lint rules and how releases are cut. Commits follow Conventional Commits with a scope, as in fix(sqlite): ....

License

MIT. See LICENSE.

Nexus is part of the forgery stack built on Forge, by Rex Raphael and contributors.

Documentation

Overview

Package nexus is a composable AI gateway library for Go. Route, cache, guard, and observe LLM traffic at scale.

Nexus is a library, not a SaaS. Import it, compose your AI gateway, and own your infrastructure.

gw := nexus.New(
    nexus.WithProvider(openai.New("sk-...")),
    nexus.WithProvider(anthropic.New("sk-ant-...")),
    nexus.WithRouter(router.NewCostOptimized()),
    nexus.WithCache(cache.NewRedis(redisClient)),
    nexus.WithGuard(guard.NewPII(guard.ActionRedact)),
)
gw.Initialize(ctx)
gw.Mount(router, "/ai")

Index

Constants

This section is empty.

Variables

View Source
var (
	// Provider errors
	ErrProviderNotFound    = errors.New("nexus: provider not found")
	ErrProviderUnavailable = errors.New("nexus: provider unavailable")
	ErrModelNotSupported   = errors.New("nexus: model not supported by provider")
	ErrAllProvidersFailed  = errors.New("nexus: all providers failed")

	// Auth errors
	ErrUnauthorized  = errors.New("nexus: unauthorized")
	ErrAPIKeyInvalid = errors.New("nexus: invalid API key")
	ErrAPIKeyRevoked = errors.New("nexus: API key revoked")

	// Tenant errors
	ErrTenantNotFound = errors.New("nexus: tenant not found")
	ErrTenantDisabled = errors.New("nexus: tenant disabled")
	ErrQuotaExceeded  = errors.New("nexus: quota exceeded")
	ErrBudgetExceeded = errors.New("nexus: budget exceeded")

	// Rate limiting
	ErrRateLimited = errors.New("nexus: rate limited")

	// Guardrail errors
	ErrContentBlocked    = errors.New("nexus: content blocked by guardrail")
	ErrPIIDetected       = errors.New("nexus: PII detected in request")
	ErrInjectionDetected = errors.New("nexus: prompt injection detected")

	// Cache errors
	ErrCacheNotConfigured = errors.New("nexus: cache not configured")

	// Pipeline errors
	ErrPipelineAborted = errors.New("nexus: pipeline aborted")
	ErrCircuitOpen     = errors.New("nexus: circuit breaker open")

	// Context & tokens
	ErrContextOverflow     = errors.New("nexus: request exceeds context window")
	ErrTokenEstimateFailed = errors.New("nexus: token estimation failed")

	// Model aliases
	ErrAliasNotFound      = errors.New("nexus: model alias not found")
	ErrNoTargetsAvailable = errors.New("nexus: no alias targets available")

	// Extended thinking
	ErrThinkingNotSupported = errors.New("nexus: provider does not support extended thinking")

	// Batch
	ErrBatchTooLarge = errors.New("nexus: batch exceeds maximum size")
	ErrBatchNotFound = errors.New("nexus: batch job not found")

	// ErrModelUnpriced refuses a request for a model with no price at the
	// provider it was routed to (RefuseUnpriced). It is a *RefusalError with
	// code model_unpriced and status 400.
	ErrModelUnpriced = middlewares.ErrModelUnpriced

	// Credentials
	ErrCredentialExpired = errors.New("nexus: provider credential expired")
	// ErrCredentialNotFound is provider.ErrCredentialNotFound: a credential
	// provider returns it to serve a request with the provider's own key.
	ErrCredentialNotFound = provider.ErrCredentialNotFound
	// ErrCredentialUnavailable refuses a request whose credential could not
	// be resolved, or that the chosen provider cannot use
	// (WithCredentialProvider). It is a *RefusalError with code unavailable
	// and status 503; nothing is sent to the provider.
	ErrCredentialUnavailable = middlewares.ErrCredentialUnavailable
)
View Source
var ErrNotInitialized = errors.New("nexus: gateway not initialized")

ErrNotInitialized is returned by the engine's calls before Gateway.Initialize has finished.

Functions

func RefuseUnpriced

func RefuseUnpriced(ctx context.Context, tenantID, providerName, modelID string) error

RefuseUnpriced is the UnpricedPolicy that refuses every request for a model with no price with ErrModelUnpriced (400, model_unpriced), before any provider is called.

Types

type CompletionRequest

type CompletionRequest = provider.CompletionRequest

CompletionRequest is an alias for provider.CompletionRequest.

type CompletionResponse

type CompletionResponse = provider.CompletionResponse

CompletionResponse is an alias for provider.CompletionResponse.

type Config

type Config struct {
	// BasePath is the HTTP base path for routes (default: "/nexus").
	BasePath string

	// DefaultTimeout is the default timeout for provider requests (default: 30s).
	DefaultTimeout time.Duration

	// DefaultMaxRetries is the default number of retries per request (default: 2).
	DefaultMaxRetries int

	// EnableUsage enables usage tracking (default: true).
	EnableUsage bool

	// EnableCache enables caching (default: false, must provide cache).
	EnableCache bool

	// RequireAPIKey makes the HTTP edges (api, proxy) refuse a request
	// without a valid nxs_ key. Default true. False keeps an open gateway
	// for local work; a key that is presented is still checked.
	RequireAPIKey bool

	// GlobalRateLimit is the global rate limit in requests per minute (0 = unlimited).
	GlobalRateLimit int

	// LogLevel is the log level for the internal logger (default: "info").
	LogLevel string
}

Config holds the gateway configuration.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns the default gateway configuration.

type Engine

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

Engine processes AI requests without any HTTP concerns. It delegates to the pipeline for the actual processing.

The engine exists from New on, but Complete, CompleteStream, Embed and ListModels return ErrNotInitialized until Gateway.Initialize finishes.

func NewEngine

func NewEngine(opts ...Option) *Engine

NewEngine creates a standalone engine (no HTTP).

func (*Engine) Complete

Complete sends a chat completion request through the full pipeline.

func (*Engine) CompleteStream

func (e *Engine) CompleteStream(ctx context.Context, req *provider.CompletionRequest) (provider.Stream, error)

CompleteStream sends a streaming chat completion request.

func (*Engine) Embed

Embed sends an embedding request.

func (*Engine) Gateway

func (e *Engine) Gateway() *Gateway

Gateway returns the underlying Gateway.

func (*Engine) ListModels

func (e *Engine) ListModels(ctx context.Context) ([]provider.Model, error)

ListModels returns available models across all providers.

type Gateway

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

Gateway is the root Nexus instance. It can be used standalone or mounted into a Forge application.

func New

func New(opts ...Option) *Gateway

New creates a new Nexus Gateway with the given options.

func (*Gateway) Aliases

func (gw *Gateway) Aliases() []model.Alias

Aliases lists the configured model aliases, or nil when there are none.

func (*Gateway) Cache

func (gw *Gateway) Cache() cache.Service

Cache returns the cache service.

func (*Gateway) CacheKinds

func (gw *Gateway) CacheKinds() (completion, stream string)

CacheKinds reports completion and stream cache backends separately. A custom cache without a Kind method is unknown; an absent cache is none.

func (*Gateway) Config

func (gw *Gateway) Config() *Config

Config returns the gateway configuration.

func (*Gateway) Engine

func (gw *Gateway) Engine() *Engine

Engine returns the core engine for programmatic usage.

func (*Gateway) EnsureBootstrapAdminKey

func (gw *Gateway) EnsureBootstrapAdminKey(ctx context.Context) error

EnsureBootstrapAdminKey makes sure the key set with WithBootstrapAdminKey exists, so an operator can reach the admin API without writing Go. It does nothing when no key was set. Otherwise it finds or creates the active tenant with slug "operator", then stores the key there with the admin scope. Running it again changes nothing.

A key revoked since the last run stays revoked and is reported with a warning that names its id, so revoking the bootstrap key retires it. The same warning covers a key that exists without the admin scope or under another tenant. An "operator" tenant that is not active is an error. A key of the wrong shape is an error that gives its length. The raw key never appears in an error or a log line.

Call it after the gateway is initialized and the store is migrated: Store().Migrate() must have run, or the lookups fail. The Forge extension does this in Start.

func (*Gateway) Extensions

func (gw *Gateway) Extensions() *plugin.Registry

Extensions returns the extension registry.

func (*Gateway) FlushUsage

func (gw *Gateway) FlushUsage(ctx context.Context) error

FlushUsage waits until every usage record taken so far is stored, without closing the stage. It also waits for streams that are still open, until ctx ends. Shutdown flushes on its own.

func (*Gateway) Guard

func (gw *Gateway) Guard() guard.Service

Guard returns the guard service.

func (*Gateway) Health

func (gw *Gateway) Health(_ context.Context) error

Health reports whether the gateway serves requests. It returns ErrNotInitialized before Initialize and after Shutdown, as the engine does.

func (*Gateway) Initialize

func (gw *Gateway) Initialize(_ context.Context) error

Initialize sets up all services and validates configuration. A second call on an initialized gateway does nothing. After Shutdown it fails with an error that wraps ErrNotInitialized: a gateway is not restarted, so create a new one.

func (*Gateway) Initialized

func (gw *Gateway) Initialized() bool

Initialized reports whether Initialize has succeeded and Shutdown has not run since. The engine serves requests only while it is true.

func (*Gateway) Keys

func (gw *Gateway) Keys() key.Service

Keys returns the API key service.

func (*Gateway) Limiter

func (gw *Gateway) Limiter() ratelimit.Limiter

Limiter is the RPM and TPM limiter in use.

func (*Gateway) LimiterErrors

func (gw *Gateway) LimiterErrors() int64

LimiterErrors counts limiter failures that let a request through.

func (*Gateway) Logger

func (gw *Gateway) Logger() Logger

Logger returns the gateway logger.

func (*Gateway) Models

func (gw *Gateway) Models() model.Service

Models returns the model service.

func (*Gateway) Mount

func (gw *Gateway) Mount(mux Router, basePath ...string)

Mount registers Nexus HTTP handlers on the given router.

func (*Gateway) Pipeline

func (gw *Gateway) Pipeline() pipeline.Service

Pipeline returns the pipeline service.

func (*Gateway) PipelineStages

func (gw *Gateway) PipelineStages() []pipeline.Stage

PipelineStages lists the pipeline's stages in the order they run, or nil for a custom pipeline that cannot list them.

func (*Gateway) PriceBook

func (gw *Gateway) PriceBook() *model.PriceBook

PriceBook returns the price book the usage stage prices requests with. It exists from New, so overrides can be set before Initialize.

func (*Gateway) Providers

func (gw *Gateway) Providers() provider.Registry

Providers returns the provider registry.

func (*Gateway) RouterService

func (gw *Gateway) RouterService() router.Service

RouterService returns the routing service.

func (*Gateway) RoutingStrategy

func (gw *Gateway) RoutingStrategy() string

RoutingStrategy is the name of the routing strategy in use.

func (*Gateway) Shutdown

func (gw *Gateway) Shutdown(ctx context.Context) error

Shutdown gracefully stops all services. It stops the usage stage taking new records, waits until ctx is done for the streams still open and the records still being stored, then closes the store only if the gateway built it. A store passed to WithDatabase belongs to the caller, who closes it after Shutdown. When ctx ends first, the records not yet stored are lost: Shutdown logs how many and returns the flush error, joined with any error from closing the store.

Shutdown clears Gateway.Initialized, so the engine refuses new calls with ErrNotInitialized. It is safe on a gateway that was never initialized. A gateway is not restarted after Shutdown: Initialize returns an error.

func (*Gateway) Store

func (gw *Gateway) Store() store.Store

Store returns the persistence store.

func (*Gateway) Tenants

func (gw *Gateway) Tenants() tenant.Service

Tenants returns the tenant service.

func (*Gateway) Transforms

func (gw *Gateway) Transforms() *transform.Registry

Transforms returns the transform registry, or nil when none is configured.

func (*Gateway) Usage

func (gw *Gateway) Usage() usage.Service

Usage returns the usage service.

func (*Gateway) UsageInsertErrors

func (gw *Gateway) UsageInsertErrors() int64

UsageInsertErrors is how many usage records failed to store since start.

type ID

type ID = id.ID

ID is the primary identifier type for all Nexus entities.

type Logger

type Logger interface {
	Debug(msg string, args ...any)
	Info(msg string, args ...any)
	Warn(msg string, args ...any)
	Error(msg string, args ...any)
}

Logger is a minimal structured logger interface. Compatible with slog, zap, Forge logger, etc.

func NewLevelLogger

func NewLevelLogger(l Logger, level string) Logger

NewLevelLogger drops messages below level: "debug", "info", "warn" or "error". Any other value means "info".

func NewLogger

func NewLogger(l golog.Logger) Logger

NewLogger wraps a go-utils log.Logger as a nexus Logger.

func NewNoopLogger

func NewNoopLogger() Logger

NewNoopLogger returns a Logger that discards all output.

type Message

type Message = provider.Message

Message is an alias for provider.Message.

type Option

type Option func(*Gateway)

Option configures a Gateway.

func WithAlias

func WithAlias(name string, targets ...model.AliasTarget) Option

WithAlias registers a model alias that maps a virtual model name to one or more concrete provider+model targets.

Example:

nexus.WithAlias("fast", model.AliasTarget{Provider: "anthropic", Model: "claude-3.5-haiku"})
nexus.WithAlias("cheap",
    model.AliasTarget{Provider: "openai", Model: "gpt-4o-mini", Weight: 0.7},
    model.AliasTarget{Provider: "anthropic", Model: "claude-3.5-haiku", Weight: 0.3},
)

func WithAuth deprecated

func WithAuth(a auth.Provider) Option

WithAuth sets the auth provider.

Deprecated: the provider is stored and never consulted. Gateway keys are checked by auth.KeyAuth on the HTTP routes and grpcsrv.KeyAuth on gRPC, and WithAuth changes neither. It stays only so existing code compiles.

func WithBasePath

func WithBasePath(path string) Option

WithBasePath sets the HTTP base path.

func WithBootstrapAdminKey

func WithBootstrapAdminKey(raw string) Option

WithBootstrapAdminKey sets the raw value of the first admin key. After Initialize and Store().Migrate(), EnsureBootstrapAdminKey stores it under the "operator" tenant with the admin scope, so the admin API is reachable without writing Go. The value must be "nxs_" and 64 lowercase hex digits, for example "nxs_" + the output of `openssl rand -hex 32`. It is a secret: keep it out of source control.

func WithCache

func WithCache(c cache.Cache) Option

WithCache enables caching with the given cache store.

func WithCacheEnabled

func WithCacheEnabled(on bool) Option

WithCacheEnabled turns the response cache on. Without WithCache it is an in-memory cache with the default size and TTL.

func WithConfig

func WithConfig(cfg *Config) Option

WithConfig replaces the whole gateway configuration. Start from DefaultConfig() and change fields: a zero Config has EnableUsage false, so the gateway records no usage, and no timeout or retries.

func WithCredentialProvider

func WithCredentialProvider(cp provider.CredentialProvider) Option

WithCredentialProvider resolves the credential for each provider call, so one gateway serves every tenant on its own key (and, for an OpenAI-compatible provider, its own base URL), with each provider's configured key as the fallback. The gateway asks cp after routing and the unpriced policy, once per attempt, with pipeline.TenantID(ctx) set to the request's nexus tenant. A cp that implements provider.CredentialResolver can set the base URL as well; any other gives only a key.

provider.ErrCredentialNotFound (or an empty credential) serves the request with the provider's own key. Any other error refuses it with ErrCredentialUnavailable (503) and sends nothing: there is no silent fall back to the operator's key when a tenant's key store is down. A credential for a provider that cannot take one (not provider.CredentialAware) is refused the same way. Anthropic, OpenAI and opencompat take them. Like the unpriced policy, it is part of the default pipeline only (add middlewares.NewProviderCall(...).WithCredentialProvider to one set with WithPipeline).

func WithDatabase

func WithDatabase(s store.Store) Option

WithDatabase sets the persistence store. The caller owns it: Shutdown does not close it.

func WithExtension

func WithExtension(e plugin.Extension) Option

WithExtension registers a lifecycle extension (audit_hook, observability, relay_hook, etc.).

func WithGuard

func WithGuard(g guard.Guard) Option

WithGuard adds a guardrail to the pipeline. A guard whose phase is output or both also checks streamed replies; see WithStreamGuardStrategy.

func WithHealthTracker

func WithHealthTracker(h provider.HealthTracker) Option

WithHealthTracker sets the provider health tracker.

func WithKeyService

func WithKeyService(s key.Service) Option

WithKeyService replaces the API key service Initialize would build.

func WithLimiter

func WithLimiter(l ratelimit.Limiter) Option

WithLimiter sets the RPM and TPM limiter. The default is in-process (ratelimit.NewMemory), which applies per replica.

func WithLogger

func WithLogger(l Logger) Option

WithLogger sets a custom logger.

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries sets the default max retries.

func WithMetrics

func WithMetrics(m gometrics.Metrics) Option

WithMetrics records the gateway's metrics on m: the GenAI client duration and token histograms, time to first chunk, requests by outcome, guardrail blocks, cache lookups, usage insert failures and limiter failures. In a Forge app pass app.Metrics(); the Forge extension does this for you. A nil m records nothing. Spans need no option: they go to the global tracer provider, which forge.New installs.

func WithMiddleware

func WithMiddleware(m pipeline.Middleware) Option

WithMiddleware adds a custom middleware to the pipeline.

func WithPipeline

func WithPipeline(p pipeline.Service) Option

WithPipeline sets a fully custom pipeline, replacing the default. The default carries the access stage (tenant status and key scopes) and the quota stage (token cap, daily requests, budget, RPM, TPM). A custom pipeline without them enforces none of that, while the HTTP edge still authenticates keys, so a key without the completions scope is served. Include middlewares.NewAccess ahead of middlewares.NewQuota: quota reads the tenant access publishes.

func WithPriceOverrides

func WithPriceOverrides(overrides ...model.PriceOverride) Option

WithPriceOverrides prices models at the providers that serve them, over the providers' own list prices and for models they do not list. Each override names the provider by its registered name. Overrides from every WithPriceOverrides add up; a later one for the same model and provider wins. To replace them all at runtime, call PriceBook().SetOverrides.

An override the price book rejects (see model.PriceBook.SetOverrides) leaves the overrides unset, and Initialize returns the error unless a later PriceBook().SetOverrides call with valid prices succeeds first.

func WithProvider

func WithProvider(p provider.Provider) Option

WithProvider registers an LLM provider.

func WithProviderTransport

func WithProviderTransport(wrap func(http.RoundTripper) http.RoundTripper) Option

WithProviderTransport wraps the HTTP transport of every registered provider that implements provider.TransportWrapper, once, when the gateway is initialized. The Forge extension passes forge.InstrumentTransport, so each call to a provider gets an HTTP client span under its GenAI span. Outside Forge, pass otelhttp.NewTransport or your own wrapper. Providers registered after Initialize are not wrapped.

func WithRateLimit

func WithRateLimit(rpm int) Option

WithRateLimit sets global rate limit (requests per minute).

func WithRequireAPIKey

func WithRequireAPIKey(on bool) Option

WithRequireAPIKey turns key enforcement at the HTTP edges on or off.

func WithRouter

func WithRouter(r router.Strategy) Option

WithRouter sets the routing strategy.

func WithStreamCache

func WithStreamCache(sc cache.StreamCache, opts cache.StreamCacheOptions) Option

WithStreamCache enables record-and-replay caching for streaming responses using the given backend and options. Streams that produce a cache hit are served from the recorded frames; misses pass through and are recorded for later use. Independent of WithCache (which only handles non-streaming).

func WithStreamGuard

func WithStreamGuard(g guard.StreamGuard) Option

WithStreamGuard adds a guard that checks every streamed chunk, under guard.StrategyBuffer before anything is delivered and under guard.StrategyChunkwise before each chunk goes out. guard.StrategyPassthrough does not run it. It is not one of the WithGuard guards: its Check method is not called.

func WithStreamGuardStrategy

func WithStreamGuardStrategy(s guard.StreamStrategy) Option

WithStreamGuardStrategy sets when streamed output reaches the consumer relative to the output guards' verdict. Output guards are the WithGuard guards whose phase is output or both.

  • guard.StrategyBuffer (the default) delivers nothing until the whole reply has passed. A block ends the stream with *guard.BlockedError, and a redacting guard's rewrite is what the consumer receives.
  • guard.StrategyPassthrough streams as the provider answers and checks the reply when it ends: a block replaces io.EOF with *guard.BlockedError, so run tool calls only after a clean end. A rewrite comes too late to apply; it is logged as a warning and the stream ends normally. Pick guard.StrategyBuffer when a redacting guard must take effect.
  • guard.StrategyChunkwise runs the WithStreamGuard guards on every chunk before delivering it, then checks the reply as StrategyPassthrough does.

func WithStreamLifecycleConfig

func WithStreamLifecycleConfig(cfg middlewares.StreamLifecycleConfig) Option

WithStreamLifecycleConfig tunes per-chunk plugin hook fan-out for streamed responses. Default (zero value) emits OnChunkReceived for every chunk.

Set EmitEveryNChunks to 16 or 32 in latency-sensitive deployments where per-chunk hooks would otherwise dominate the hot path.

func WithTenantAlias

func WithTenantAlias(tenantID, name string, targets ...model.AliasTarget) Option

WithTenantAlias registers a per-tenant model alias override.

func WithTenantService

func WithTenantService(s tenant.Service) Option

WithTenantService replaces the tenant service Initialize would build.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the default request timeout.

func WithTracer

func WithTracer(t observability.Tracer) Option

WithTracer adds a stage that reports each request to t, for a tracer that is not OpenTelemetry. The gateway already starts OpenTelemetry spans on the global tracer provider without it (see the observability docs), so with an OpenTelemetry bridge you would see each request twice.

func WithTransforms

func WithTransforms(r *transform.Registry) Option

WithTransforms sets the transform registry for input/output transforms.

func WithUnpricedPolicy

func WithUnpricedPolicy(p UnpricedPolicy) Option

WithUnpricedPolicy sets the policy for requests to models with no price. Without one every such request is served, counted in nexus_usage_unpriced_total and warned about once per provider and model. Pass RefuseUnpriced so per-tenant budgets cannot miss spend on unpriced models. A priced call whose token counts never arrive (a stream that fails or is closed before its usage, a mid-stream StreamGuard block) still records an unknown cost; usage Series counts those in Unpriced. The policy is part of the default pipeline: one set with WithPipeline does not get it (add middlewares.NewProviderCall(...).WithUnpricedPolicy yourself).

func WithUsageEnabled

func WithUsageEnabled(on bool) Option

WithUsageEnabled turns usage recording on or off (default on). Off is the one way to stop recording; the usage service is still built for reads.

func WithUsageService

func WithUsageService(s usage.Service) Option

WithUsageService replaces the usage service Initialize would build.

type Prefix

type Prefix = id.Prefix

Prefix identifies the entity type encoded in a TypeID.

type RefusalError

type RefusalError = pipeline.RefusalError

RefusalError is the typed refusal every enforcement stage returns. It lives in pipeline so stages can return it without importing this package.

type Router

type Router interface {
	http.Handler
	Handle(pattern string, handler http.Handler)
	HandleFunc(pattern string, handler http.HandlerFunc)
}

Router is a minimal interface for HTTP routing. Compatible with chi.Router, forge.Router, http.ServeMux, etc.

type Stream

type Stream = provider.Stream

Stream is an alias for provider.Stream.

type UnpricedPolicy

type UnpricedPolicy func(ctx context.Context, tenantID, providerName, modelID string) error

UnpricedPolicy decides whether a request for a model with no price may reach its provider. The gateway asks it after routing and before each provider call, and only when the routed model has no price at the routed provider for the kind of request: no input or output price for a completion or a stream, no embedding price for an embedding. tenantID is the request's nexus tenant, empty for an unattributed request.

A nil error lets the call through: it is served and recorded with an unknown cost, which no budget counts. A refusal such as ErrModelUnpriced refuses the request at $0; any other error fails it. Neither is retried.

Directories

Path Synopsis
_examples
forge command
Example: using Nexus as a Forge extension.
Example: using Nexus as a Forge extension.
multi-tenant command
Example: multi-tenant SaaS with API keys and usage tracking.
Example: multi-tenant SaaS with API keys and usage tracking.
proxy command
Example: OpenAI-compatible proxy server.
Example: OpenAI-compatible proxy server.
standalone command
Example: standalone Nexus gateway usage (no Forge).
Example: standalone Nexus gateway usage (no Forge).
streaming command
Example: streaming completions via the channel-based Go API.
Example: streaming completions via the channel-based Go API.
Package api provides HTTP handlers for the Nexus gateway.
Package api provides HTTP handlers for the Nexus gateway.
Package audit_hook provides the audit extension for Nexus.
Package audit_hook provides the audit extension for Nexus.
Package auth authenticates gateway keys at the HTTP and gRPC edges: KeyAuth, RequireScope, Authenticate and AuthenticateWithTenants.
Package auth authenticates gateway keys at the HTTP and gRPC edges: KeyAuth, RequireScope, Authenticate and AuthenticateWithTenants.
authsome
Package authsome provides an adapter that bridges an external Authsome authentication service to the Nexus auth.Provider interface.
Package authsome provides an adapter that bridges an external Authsome authentication service to the Nexus auth.Provider interface.
Package batch provides batch processing for multiple LLM requests.
Package batch provides batch processing for multiple LLM requests.
Package cache defines the caching interface for Nexus.
Package cache defines the caching interface for Nexus.
stores
Package stores provides cache implementations for Nexus.
Package stores provides cache implementations for Nexus.
config module
Package extension provides the Forge extension adapter for Nexus.
Package extension provides the Forge extension adapter for Nexus.
contract
Package contract serves the operator-wide Nexus dashboard contract.
Package contract serves the operator-wide Nexus dashboard contract.
Package fallback defines the resilience and fallback subsystem.
Package fallback defines the resilience and fallback subsystem.
Package guard defines the guardrails subsystem for content safety.
Package guard defines the guardrails subsystem for content safety.
guards
Package guards provides built-in guardrail implementations.
Package guards provides built-in guardrail implementations.
shieldadapter
Package shieldadapter provides a guardrail adapter that bridges an external Shield content safety engine to the Nexus guard.Guard interface.
Package shieldadapter provides a guardrail adapter that bridges an external Shield content safety engine to the Nexus guard.Guard interface.
Package httpstream provides HTTP streaming wire formats for Nexus.
Package httpstream provides HTTP streaming wire formats for Nexus.
Package id defines TypeID-based identity types for all Nexus entities.
Package id defines TypeID-based identity types for all Nexus entities.
internal
telemetry
Package telemetry holds the OpenTelemetry plumbing Nexus's stages share: the tracer, the span attribute keys, how an error is named, and the metric families recorded on a go-utils metrics sink.
Package telemetry holds the OpenTelemetry plumbing Nexus's stages share: the tracer, the span attribute keys, how an error is named, and the metric families recorded on a go-utils metrics sink.
telemetry/telemetrytest
Package telemetrytest records the spans and metrics Nexus emits, so a test can assert on them without a Forge app.
Package telemetrytest records the spans and metrics Nexus emits, so a test can assert on them without a Forge app.
Package key defines the API key management types and service.
Package key defines the API key management types and service.
Package mcp provides Model Context Protocol support for Nexus.
Package mcp provides Model Context Protocol support for Nexus.
Package model defines model alias resolution and model metadata services.
Package model defines model alias resolution and model metadata services.
Package money holds exact amounts of US dollars.
Package money holds exact amounts of US dollars.
Package observability provides tracing and metrics middleware for Nexus.
Package observability provides tracing and metrics middleware for Nexus.
Package paging holds the cursor rules every Nexus list shares.
Package paging holds the cursor rules every Nexus list shares.
Package pipeline defines the composable middleware pipeline that processes every request through an ordered chain of middleware.
Package pipeline defines the composable middleware pipeline that processes every request through an ordered chain of middleware.
middlewares
Package middlewares provides built-in pipeline middleware for Nexus.
Package middlewares provides built-in pipeline middleware for Nexus.
Package plugin defines the extension system for Nexus.
Package plugin defines the extension system for Nexus.
Package provider defines the unified interface for LLM providers.
Package provider defines the unified interface for LLM providers.
providers
ai21 module
anthropic module
anyscale module
azureopenai module
bedrock module
cerebras module
cohere module
deepinfra module
deepseek module
fireworks module
gemini module
groq module
hyperbolic module
jinaai module
lepton module
lmstudio module
mistral module
nebius module
novita module
nvidia module
ollama module
openai module
opencompat module
openrouter module
perplexity module
sambanova module
together module
vertex module
voyageai module
xai module
Package providertest provides shared test utilities for provider conformance testing.
Package providertest provides shared test utilities for provider conformance testing.
Package proxy provides an OpenAI-compatible HTTP server.
Package proxy provides an OpenAI-compatible HTTP server.
Package ratelimit counts requests and tokens in fixed windows for the quota stage's RPM and TPM checks.
Package ratelimit counts requests and tokens in fixed windows for the quota stage's RPM and TPM checks.
redislimit
Package redislimit is a ratelimit.Limiter whose windows live in Redis, so every replica shares them.
Package redislimit is a ratelimit.Limiter whose windows live in Redis, so every replica shares them.
Package relay_hook bridges Nexus lifecycle events to webhook notifications.
Package relay_hook bridges Nexus lifecycle events to webhook notifications.
Package router defines the routing engine that selects the best provider for a request.
Package router defines the routing engine that selects the best provider for a request.
strategies
Package strategies provides built-in routing strategies for Nexus.
Package strategies provides built-in routing strategies for Nexus.
Package store defines the aggregate persistence interface for Nexus.
Package store defines the aggregate persistence interface for Nexus.
internal/conv
Package conv holds the conversions every database backend uses at its model boundary, so the backends cannot each invent their own.
Package conv holds the conversions every database backend uses at its model boundary, so the backends cannot each invent their own.
mongo
Package mongo provides a MongoDB-backed store implementation for Nexus using grove ORM with the mongodriver.
Package mongo provides a MongoDB-backed store implementation for Nexus using grove ORM with the mongodriver.
postgres
Package postgres provides a PostgreSQL-backed store implementation for Nexus using grove ORM with programmatic migrations.
Package postgres provides a PostgreSQL-backed store implementation for Nexus using grove ORM with programmatic migrations.
sqlite
Package sqlite provides a SQLite-backed store implementation for Nexus using grove ORM with programmatic migrations.
Package sqlite provides a SQLite-backed store implementation for Nexus using grove ORM with programmatic migrations.
storetest
Package storetest runs one conformance test body against every Nexus store backend: memory and SQLite always, PostgreSQL when NEXUS_TEST_POSTGRES_DSN is set and MongoDB when NEXUS_TEST_MONGO_URI is set.
Package storetest runs one conformance test body against every Nexus store backend: memory and SQLite always, PostgreSQL when NEXUS_TEST_POSTGRES_DSN is set and MongoDB when NEXUS_TEST_MONGO_URI is set.
Package tenant defines the multi-tenant management types and service.
Package tenant defines the multi-tenant management types and service.
Package testutil provides shared test utilities including mock HTTP servers.
Package testutil provides shared test utilities including mock HTTP servers.
Package transform provides request/response transformation middleware for Nexus.
Package transform provides request/response transformation middleware for Nexus.
Package usage defines usage tracking types and service.
Package usage defines usage tracking types and service.

Jump to

Keyboard shortcuts

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