client

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jul 9, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package client is a thin Go client that speaks the relay canonical shape (sdk/v1). Callers build a *v1.Request once and get a *Response or a stream of canonical events — regardless of the target.

Use Relay for the primary relay-server path (key pooling, routing, limits). Use For(ref, apiKey) to call any catalogued upstream host directly by model ref ("gpt-4o", "openai/gpt-4o", "gpt-4o@openai-direct") with zero manual baseURL/path/auth wiring. OpenAI, Anthropic, and Gemini constructors remain for explicit off-catalog targets.

Configuration mirrors the OpenAI SDK: base URL, API key, auth header/scheme, extra default headers, request path, and HTTP client are all settable.

Imports only the standard library, pkg/relay/v1, and the pure vendor translators — none of relay's server-side dependency graph.

Index

Constants

View Source
const (
	EnvBaseURL = "WR_BASE_URL"
	EnvAPIKey  = "WR_API_KEY"
	EnvUsage   = "WR_USAGE"
	EnvHeaders = "WR_HEADERS"
	EnvTimeout = "WR_TIMEOUT"
)

Relay env vars, mirroring the OpenAI SDK's OPENAI_BASE_URL / OPENAI_API_KEY convention. Only the relay target consults them — direct-to-vendor clients do not, so an upstream call never depends on relay config.

WR_BASE_URL  relay endpoint           (fallback when baseURL == "")
WR_API_KEY   relay key                (fallback when relayKey == "")
WR_USAGE     default X-WR-Usage value  (e.g. "full" to echo usage inline)
WR_HEADERS   default headers, "k1=v1,k2=v2"
WR_TIMEOUT   sync-call timeout, a Go duration (e.g. "30s"); streams are
             unaffected — a stream needs no overall deadline.
View Source
const (
	EnvOpenAIKey    = "OPENAI_API_KEY"
	EnvAnthropicKey = "ANTHROPIC_API_KEY"
	// Gemini reads GEMINI_API_KEY first, then GOOGLE_API_KEY — matching the
	// google-genai SDK precedence.
	EnvGeminiKey = "GEMINI_API_KEY"
	EnvGoogleKey = "GOOGLE_API_KEY"
)

Conventional per-vendor key env vars, read when a direct-to-vendor constructor is called with an empty apiKey. These are the vendor's own credential — never WR_API_KEY, which is a relay key bound to a relay endpoint and meaningless to a vendor. An empty key after fallback is left as-is (Ollama and other keyless OpenAI-compatible hosts are valid).

Variables

This section is empty.

Functions

func KnownAdapterNames

func KnownAdapterNames() []string

KnownAdapterNames returns every adapter name registered in the SDK.

Types

type APIError

type APIError struct {
	StatusCode int
	Host       string
	Code       string
	Message    string
	Raw        []byte
}

APIError is a non-2xx response from the target. Host names the upstream the call actually hit (a relay deployment or a vendor host dialed directly), so the error self-identifies its origin.

func (*APIError) Error

func (e *APIError) Error() string

type Adapter

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

Adapter is the atomic wire bundle: translator, path resolution, and auth always travel together. Selecting an adapter swaps all three at once.

func AdapterByName

func AdapterByName(name string) (Adapter, bool)

AdapterByName returns a registered adapter bundle, or false.

type Auth

type Auth struct {
	Header string // header name, e.g. "Authorization" or "x-api-key"
	Scheme string // value prefix, e.g. "Bearer"; "" sends the raw key
}

Auth describes how the API key is attached to requests.

type Client

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

Client sends canonical requests to one target (relay or a vendor).

func Anthropic

func Anthropic(baseURL, apiKey string, opts ...Option) *Client

Anthropic targets the Anthropic Messages API directly. Bypasses relay. Empty apiKey falls back to ANTHROPIC_API_KEY.

func For

func For(ref, apiKey string, opts ...TargetOption) (*Client, error)

For resolves a model ref against the embedded catalog and returns a wired client. ref forms: "gpt-4o", "openai/gpt-4o", "gpt-4o@openai-direct".

func Gemini

func Gemini(baseURL, apiKey string, opts ...Option) *Client

Gemini targets the Gemini native generateContent API directly. Bypasses relay. Point baseURL at the API host (e.g. https://generativelanguage.googleapis.com). Empty apiKey falls back to GEMINI_API_KEY, then GOOGLE_API_KEY. Auth is the raw key in x-goog-api-key.

func New

func New(translator v1.Translator, baseURL, path, apiKey string, opts ...Option) *Client

New builds a client for an arbitrary translator/target — the extension point for future adapters. Defaults to Authorization: Bearer auth.

func OpenAI

func OpenAI(baseURL, apiKey string, opts ...Option) *Client

OpenAI targets the OpenAI Chat Completions API directly (also Ollama and any OpenAI-compatible host — point baseURL at it). Bypasses relay. Empty apiKey falls back to OPENAI_API_KEY.

func OpenAIResponses

func OpenAIResponses(baseURL, apiKey string, opts ...Option) *Client

OpenAIResponses targets the OpenAI Responses API (`/responses`) directly, the same wire the Codex/ChatGPT subscription backend speaks. Point baseURL at the host (e.g. https://chatgpt.com/backend-api/codex). Bypasses relay. Empty apiKey falls back to OPENAI_API_KEY; for OAuth, pass a placeholder + WithAuth (Auth{}) and supply the bearer via WithHTTPClient. The Responses translator always sends store:false, so no server-side persistence is requested.

func Relay

func Relay(baseURL, relayKey string, opts ...Option) *Client

Relay targets a relay server's canonical endpoint (POST /v1/generate). The primary use: full key pooling, routing, limits, and observability.

Empty baseURL/relayKey fall back to WR_BASE_URL / WR_API_KEY; WR_USAGE, WR_HEADERS, and WR_TIMEOUT supply further defaults. Explicit opts override env-derived headers. Any missing/invalid config is returned on the client anyway and surfaces on the first Generate/GenerateStream call — never at construction.

func RelayWS

func RelayWS(baseURL, relayKey string, opts ...Option) *Client

RelayWS targets a relay server's canonical WebSocket endpoint (/v1/ws). One long-lived connection carries every request, so the TLS + auth handshake is paid once instead of per turn — the win for agent loops doing many sequential generations against the same relay.

The connection is stateless: each turn still sends the full request (relay holds no conversation state). This v1 client is sequential — one request in flight at a time per Client; concurrent multiplexing from a single Client is a future enhancement. Call Close when done.

func (*Client) Close

func (c *Client) Close() error

Close releases the client's transport. For the default HTTP transport it is a no-op; for a WebSocket client (RelayWS) it closes the connection. Safe to call once when done with the client.

func (*Client) Generate

func (c *Client) Generate(ctx context.Context, req *v1.Request) (*Response, error)

Generate runs a non-streaming generation. OutputMode is forced to sync; the caller's request is not mutated.

func (*Client) GenerateStream

func (c *Client) GenerateStream(ctx context.Context, req *v1.Request) (*Stream, error)

GenerateStream runs a streaming generation. The returned *Stream yields canonical events until io.EOF; the caller must Close it. OutputMode is forced to stream.

type Event

type Event struct {
	Type string
	Data []byte
}

Event is one canonical stream event: its name (a v1.Event* constant) and the raw JSON payload. Decode Data into the matching v1 event struct as needed.

type Option

type Option func(*Client)

Option configures a Client. Options apply over the preset defaults.

func WithAuth

func WithAuth(a Auth) Option

WithAuth overrides the auth header/scheme (e.g. to send a raw token).

func WithHTTPClient

func WithHTTPClient(h *http.Client) Option

WithHTTPClient overrides the *http.Client. Streaming needs a client without a short overall timeout.

func WithHeader

func WithHeader(k, v string) Option

WithHeader sets one extra default header sent on every request (e.g. an OpenAI-Organization header, or anthropic-version override).

func WithPath

func WithPath(p string) Option

WithPath overrides the request path (e.g. an Azure-style deployment path).

func WithPathFn

func WithPathFn(fn func(model string, stream bool) string) Option

WithPathFn sets a per-call path resolver, for targets that encode the model or the stream/sync choice in the URL path (e.g. Gemini). When set it overrides the static path. The model is the request's first model ref.

type Response

type Response struct {
	*v1.Response
	// contains filtered or unexported fields
}

Response is a non-streaming generation result with optional catalog pricing.

func (*Response) Cost

func (r *Response) Cost() (float64, bool)

Cost returns total cost from the target's pricing rate sheet. ok is false for relay targets, unpriced hosts, and clients built without catalog resolution.

type Stream

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

Stream iterates canonical events. For a relay target the upstream stream is already canonical (toCanon is nil, frames pass through); for a vendor target toCanon converts each vendor SSE frame to canonical.

func (*Stream) Close

func (s *Stream) Close() error

Close releases the underlying response body.

func (*Stream) Cost

func (s *Stream) Cost() (float64, bool)

Cost returns total cost from the target's pricing rate sheet after the stream's terminal usage event has been received. ok is false for relay targets, unpriced hosts, and when usage is not yet available.

func (*Stream) Recv

func (s *Stream) Recv() (*Event, error)

Recv returns the next canonical event, or io.EOF at end.

func (*Stream) Timing

func (s *Stream) Timing() StreamTiming

Timing reports the stream timing observed so far. Call it after draining the stream for the full picture; mid-stream it reflects events up to the last Recv (ResponseEnd is zero until io.EOF).

type StreamTiming

type StreamTiming struct {
	Upstream  usage.UpstreamTiming   // Start == ResponseStart == TTFT; ResponseEnd == close
	Reasoning *usage.ReasoningTiming // nil when the stream carried no reasoning
}

StreamTiming is the client-side timing of one streamed generation, in the exact shape relay ships server-side (usage.UpstreamTiming + usage.ReasoningTiming, microseconds from the GenerateStream call). A consumer reads identical fields whether it called relay or a provider directly — no drift, no branching on the path.

The client has no separate relay→upstream handoff, so Upstream.Start is collapsed onto ResponseStart (both = TTFT); when relay is in the path the relay server records its own earlier Start in its own event.

type Target

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

Target is a fully-resolved, self-consistent upstream call config.

binding (and therefore the pricing behind Cost) reflects the catalog entry the ref resolved to. Overrides (WithBaseURL/WithAdapterName) change where and how the call is made but keep that pricing — fine for a same-price proxy, potentially stale if pointed at a genuinely different upstream.

type TargetOption

type TargetOption func(*Target) error

TargetOption overrides one whole piece of a catalog-resolved Target before the client is built. Each option yields a consistent config — path and translator are only ever swapped via an Adapter.

func WithAdapterName

func WithAdapterName(name string) TargetOption

WithAdapterName swaps the whole wire bundle (translator + path + auth).

func WithBaseURL

func WithBaseURL(url string) TargetOption

WithBaseURL overrides the resolved host base URL (proxy/gateway). Adapter and auth are unchanged.

func WithClient

func WithClient(opts ...Option) TargetOption

WithClient attaches client Options (custom *http.Client, extra headers, sync timeout, ...) to a catalog-resolved client. They apply over the adapter's defaults at construction.

func WithUpstreamModel

func WithUpstreamModel(name string) TargetOption

WithUpstreamModel overrides the wire model name sent upstream.

Jump to

Keyboard shortcuts

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