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

openaicompat

package
v0.0.21 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package openaicompat is a stdlib-only LEAF adapter that fetches a LIVE model catalog from any endpoint speaking the OpenAI-shaped GET /v1/models protocol — it names the PROTOCOL, not a specific vendor. It carries no behaviour beyond the HTTP GET + JSON→neutral mapping; nothing here knows about the agent, providers, ports, the composition layer, ToolHive, or any other adapter.

Provenance and wire shape

The endpoint is:

GET <baseURL>/models

where baseURL already ends in "/v1" (e.g. "http://127.0.0.1:14000/v1"). An optional bearer token is sent as `Authorization: Bearer <token>` when non-empty (many OpenAI-compatible gateways — including the ToolHive LLM gateway proxy — accept a placeholder credential rather than none at all). The response envelope is {"object":"list","data":[{"id","object","created","owned_by","display_name","context_window"}]} — decode only `id`, `display_name`, and `context_window`; everything else is ignored by encoding/json. Wire shape pinned to stacklok-enterprise-platform#2270 ("New data-plane GET /v1/models intercept... returns an OpenAI-shaped response (`{object:"list", data:[{id, object, created, owned_by, display_name}]}`)"), and extended by stacklok-enterprise-platform#3288; the fixture in testdata/ mirrors that shape.

Layering

LEAF adapter: net/http + encoding/json + stdlib ONLY. It imports NO domain (session/prompt/tool/governance), NO port, NO internal/app, NO providercatalog, and NO other adapter — and, critically, NO ToolHive Go package: this lister only ever speaks the generic OpenAI-shaped protocol. Its Model type is package-own and must never leak into the domain or port — only the composition layer (internal/app) reads it and maps it to its own neutral modelEntry type (so there is no import cycle: the adapter does not import internal/app).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RefuseRedirects

func RefuseRedirects(*http.Request, []*http.Request) error

RefuseRedirects is the shared CheckRedirect policy (CWE-918): a loopback gateway endpoint must never be allowed to bounce a request off-loopback via a redirect response. It is exported so the INFERENCE path (the openai adapter's WithHTTPClient, wired for the ToolHive gateway registry entry only — see internal/app/registry.go's newGatewayEntry) and this LISTING path share the exact same policy and cannot drift on wording/behaviour.

Types

type Lister

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

Lister fetches a live OpenAI-shaped model catalog over an INJECTED *http.Client (tests pass a mock transport; production gets a default client with a sane timeout).

func NewLister

func NewLister(baseURL, bearerToken string, client *http.Client) *Lister

NewLister constructs a Lister against baseURL (already ending in "/v1") with an optional bearerToken (sent as `Authorization: Bearer <token>` when non-empty). NOTE the argument order — (baseURL, bearerToken, client) — deliberately differs from the sibling provider/anthropic.NewLister's (key, baseURL, client): don't copy-paste call sites between the two without checking. A nil client yields a default client with defaultTimeout AND RefuseRedirects (CWE-918): baseURL is a loopback address the CALLER already validated (composition never lets an operator point this at a remote host in v1), so a hostile/misconfigured listener on that port answering with a redirect must never be allowed to bounce the request off-loopback — production wiring may pass nil and tests inject a mock transport (which bypasses CheckRedirect entirely, so tests exercising the redirect gate use a real httptest server).

func (*Lister) ListModels

func (l *Lister) ListModels(ctx context.Context) ([]Model, error)

ListModels GETs the live catalog and maps it to []Model. It is read-only and fail-safe to the caller: any transport, status, size, or parse error returns a non-nil error (the composition layer then classifies it and falls back to the embedded catalog / last-known-good). It NEVER panics.

type Model

type Model struct {
	ID           string
	DisplayName  string
	ContextLimit int
}

Model is the adapter's OWN neutral result type. The composition layer maps it to its composition-local modelEntry; it never leaves this package's caller as-is and carries nothing provider-private (no key, no URL).

type StatusError

type StatusError struct {
	Code int
}

StatusError is returned when the endpoint answers with a non-2xx status. The composition layer classifies it via errors.As (401/403 ⇒ "unauthorized"; every other status, or a non-StatusError failure, ⇒ "unreachable").

func (*StatusError) Error

func (e *StatusError) Error() string

func (*StatusError) StatusCode

func (e *StatusError) StatusCode() int

StatusCode exposes the response code for provider-neutral classification.

Jump to

Keyboard shortcuts

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