openai

package
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Jul 29, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package openai implements ports.IOAdapter against any OpenAI-compatible Chat Completions endpoint (OpenAI itself, Azure OpenAI, Ollama, vLLM, LM Studio, Groq, and others that speak the same wire format).

It is the client-adapter half of api/llm's declare → register → handle pattern: api/llm.Call declares a system prompt plus typed input/output codecs, protocol-agnostically; CallAdapter supplies the OpenAI wire format and implements ports.IOAdapter, so an LLM completion becomes a normal ports.IOPort step in a pipeline — indistinguishable in shape from an HTTP call, a SQL query, or a cache lookup.

Zero external SDK dependency: plain net/http + encoding/json, matching adapters/nethttp/adapters/chi's stdlib-only precedent. Any OpenAI-compatible provider works by pointing CallAdapterOptions.BaseURL at a different host.

Usage

handle, err := domain.Summarize.PluginLLMPattern(domain.SummarizePattern)
domain.Summarize.Bind(ctx, openai.CallAdapter(http.DefaultClient, handle, openai.CallAdapterOptions{
    Model:      "gpt-4o-mini",
    APIKey:     os.Getenv("OPENAI_API_KEY"),
    MaxRetries: 1,
}))

// Plain-Go consumption style — no forge/gstream:
summary, err := domain.Summarize.Call(ctx, article)

Structured outputs + local re-validation

Every completion request sets response_format to {"type":"json_schema","json_schema":{"schema":...,"strict":true}} using llm.CallHandle.ResponseSchema — OpenAI's structured-outputs guarantee that the raw completion conforms to the given JSON Schema. The adapter then ALSO decodes the completion through llm.CallHandle.DecodeResponse, which runs every [codex.Codec.Refine] constraint on the response codec — cross-field invariants, custom formats, and other checks a JSON Schema alone cannot express.

Retry on invalid completion

CallAdapterOptions.MaxRetries bounds a re-prompt loop: when DecodeResponse fails (the provider's structured-outputs guarantee did not hold, or a Refine constraint rejected an otherwise schema-valid value), the adapter appends the invalid assistant response plus a new user message describing the validation error, then re-sends the full conversation. With MaxRetries: 0 (the default) the first decode failure is returned as-is (a plain llm.ResponseDecodeError); once retries are exhausted, RetriesExhaustedError is returned instead.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CallAdapter

func CallAdapter[Req, Resp any](
	client *http.Client,
	handle *llm.CallHandle[Req, Resp],
	opts CallAdapterOptions,
) ports.IOAdapter[Req, Resp]

CallAdapter returns a ports.IOAdapter[Req,Resp] that fulfills the port's Connect/Call by completing a Chat Completions request against an OpenAI-compatible endpoint. Use with ports.IOPort.Bind:

handle, _ := domain.Summarize.PluginLLMPattern(domain.SummarizePattern)
domain.Summarize.Bind(ctx, openai.CallAdapter(httpClient, handle, openai.CallAdapterOptions{
    Model: "gpt-4o-mini", APIKey: os.Getenv("OPENAI_API_KEY"),
}))

Every request is validated at the API level via llm.CallHandle.ResponseSchema (OpenAI-style "strict structured outputs") AND re-validated locally through llm.CallHandle.DecodeResponse — belt-and-suspenders: the JSON Schema constrains the shape at generation time, [codex.Codec.Refine] catches what a bare schema cannot express (cross-field invariants, custom constraints). CallAdapterOptions.MaxRetries bounds a re-prompt loop for the latter case.

Types

type CallAdapterOptions

type CallAdapterOptions struct {
	// BaseURL defaults to "https://api.openai.com/v1". Point at any
	// OpenAI-compatible endpoint (Azure OpenAI, Ollama, vLLM, LM Studio, ...).
	BaseURL string

	// Model is the model identifier sent on every request (e.g. "gpt-4o-mini").
	Model string

	// APIKey is sent as `Authorization: Bearer <APIKey>`. Use CredentialFunc
	// instead for per-request/rotating credentials.
	APIKey string
	// CredentialFunc, if set, is called per request and takes priority over
	// APIKey — mirrors [nethttp.CallOptions.CredentialFunc]'s role, adapted
	// to a single bearer-token string (OpenAI's wire format has no
	// structured security-requirement negotiation to pass through).
	CredentialFunc func(ctx context.Context) (string, error)

	// Temperature, MaxTokens: optional, nil/0 = provider default.
	Temperature *float64
	MaxTokens   *int

	// MaxRetries bounds the re-prompt-on-invalid-completion loop (default 0
	// = no retry; the first codec-validation failure is returned as-is).
	// On failure, the adapter appends the invalid assistant response plus a
	// new user message describing the validation error, then re-sends the
	// full conversation — up to MaxRetries additional attempts.
	MaxRetries int

	Observer stats.Observer
}

CallAdapterOptions configures CallAdapter.

type NoChoicesError

type NoChoicesError struct {
	// Name is the [llm.Call]'s name, as passed to [llm.NewCall] — lets
	// callers disambiguate which declared Call failed when multiple Calls
	// share the same Model.
	Name string
	// Model is the model identifier the request was made against.
	Model string
}

NoChoicesError is returned by CallAdapter when the provider's response contains zero completion choices — a malformed or empty API response.

func (NoChoicesError) Error

func (e NoChoicesError) Error() string

func (NoChoicesError) LogValue

func (e NoChoicesError) LogValue() slog.Value

LogValue implements slog.LogValuer for structured logging.

type RequestBuildError

type RequestBuildError struct {
	// Name is the [llm.Call]'s name, as passed to [llm.NewCall] — lets
	// callers disambiguate which declared Call failed when multiple Calls
	// share the same Model.
	Name string
	// Err is the underlying error from [http.NewRequestWithContext].
	Err error
}

RequestBuildError is returned by CallAdapter when constructing the outgoing *http.Request fails (e.g. malformed BaseURL or context already cancelled). Mirrors [nethttp.RequestBuildError].

func (RequestBuildError) Error

func (e RequestBuildError) Error() string

func (RequestBuildError) LogValue

func (e RequestBuildError) LogValue() slog.Value

LogValue implements slog.LogValuer for structured logging.

func (RequestBuildError) Unwrap

func (e RequestBuildError) Unwrap() error

Unwrap allows errors.Is and errors.As to traverse the underlying error.

type RequestError

type RequestError struct {
	// Name is the [llm.Call]'s name, as passed to [llm.NewCall] — lets
	// callers disambiguate which declared Call failed when multiple Calls
	// share the same Model.
	Name string
	// Model is the model identifier the request was made against.
	Model string
	// Err is the underlying transport error from [http.Client.Do].
	Err error
}

RequestError is returned by CallAdapter when executing the HTTP call fails (network error, DNS failure, TLS error, or context cancellation). Mirrors [nethttp.RequestError].

func (RequestError) Error

func (e RequestError) Error() string

func (RequestError) LogValue

func (e RequestError) LogValue() slog.Value

LogValue implements slog.LogValuer for structured logging.

func (RequestError) Unwrap

func (e RequestError) Unwrap() error

Unwrap allows errors.Is and errors.As to traverse the underlying error.

type ResponseBodyError

type ResponseBodyError struct {
	// Name is the [llm.Call]'s name, as passed to [llm.NewCall] — lets
	// callers disambiguate which declared Call failed when multiple Calls
	// share the same Model.
	Name string
	// Err is the underlying error from reading the response body.
	Err error
}

ResponseBodyError is returned by CallAdapter when reading the HTTP response body fails after a successful connection. Mirrors [nethttp.ResponseBodyError].

func (ResponseBodyError) Error

func (e ResponseBodyError) Error() string

func (ResponseBodyError) LogValue

func (e ResponseBodyError) LogValue() slog.Value

LogValue implements slog.LogValuer for structured logging.

func (ResponseBodyError) Unwrap

func (e ResponseBodyError) Unwrap() error

Unwrap allows errors.Is and errors.As to traverse the underlying error.

type RetriesExhaustedError

type RetriesExhaustedError struct {
	// Name is the [llm.Call]'s name, as passed to [llm.NewCall] — lets
	// callers disambiguate which declared Call failed when multiple Calls
	// share the same Model.
	Name string
	// Model is the model identifier the request was made against.
	Model string
	// Attempts is the total number of completion attempts made (1 + MaxRetries).
	Attempts int
	// LastErr is the last [llm.ResponseDecodeError] encountered.
	LastErr error
}

RetriesExhaustedError is returned by CallAdapter when CallAdapterOptions.MaxRetries re-prompt attempts are exhausted without ever producing a codec-valid completion.

func (RetriesExhaustedError) Error

func (e RetriesExhaustedError) Error() string

func (RetriesExhaustedError) LogValue

func (e RetriesExhaustedError) LogValue() slog.Value

LogValue implements slog.LogValuer for structured logging.

func (RetriesExhaustedError) Unwrap

func (e RetriesExhaustedError) Unwrap() error

Unwrap allows errors.Is and errors.As to traverse the underlying error.

type UnexpectedStatusError

type UnexpectedStatusError struct {
	// Name is the [llm.Call]'s name, as passed to [llm.NewCall] — lets
	// callers disambiguate which declared Call failed when multiple Calls
	// share the same Model.
	Name string
	// Model is the model identifier the request was made against.
	Model string
	// StatusCode is the HTTP response status code returned by the provider.
	StatusCode int
	// Body is the raw response body returned by the provider (may be empty).
	Body string
}

UnexpectedStatusError is returned by CallAdapter when the provider responds with a non-2xx HTTP status code. Mirrors [nethttp.UnexpectedStatusError].

func (UnexpectedStatusError) Error

func (e UnexpectedStatusError) Error() string

func (UnexpectedStatusError) LogValue

func (e UnexpectedStatusError) LogValue() slog.Value

LogValue implements slog.LogValuer for structured logging.

Jump to

Keyboard shortcuts

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