Configuring the OpenAI-compatible adapter
adapters/openai connects Golato to any service that speaks OpenAI Chat Completions streaming and the OpenAI embeddings
shape. It translates the provider's JSON payloads and HTTP responses into the shared types of generation, embedding,
and message, so the rest of the library never sees provider-specific formats.
Configuring one endpoint
openai.EndpointConfig describes where to call and how to authenticate. One value serves a Generator, an Embedder, or
both.
endpoint := openai.EndpointConfig{
APIKey: os.Getenv("GOLATO_API_KEY"),
BaseURL: os.Getenv("GOLATO_BASE_URL"),
HTTPClient: new(http.Client{Timeout: 60 * time.Second}),
Headers: map[string]string{"X-Application": "my-app"},
}
generator, err := openai.NewGenerator(openai.GeneratorConfig{
EndpointConfig: endpoint,
})
if err != nil {
return err
}
embedder, err := openai.NewEmbedder(endpoint)
if err != nil {
return err
}
The adapter never reads the environment. The application reads the variables and passes the values. An empty BaseURL
uses https://api.openai.com/v1, and a non-empty value must have a scheme and host. The adapter appends
/chat/completions or /embeddings itself.
A non-nil HTTPClient is used exactly as configured and must not be modified while requests are in flight. A nil client
falls back to http.DefaultClient, which carries no timeout of its own. The API key becomes
Authorization: Bearer ..., the configured Headers are applied afterward and may replace it or other defaults, and
their values are copied when the client is built.
Configuring the Generator
GeneratorConfig adds the provider identity, the output-token field, and usage requests.
generator, err := openai.NewGenerator(openai.GeneratorConfig{
EndpointConfig: endpoint,
Provider: "openai-compatible",
OutputTokensField: openai.OutputTokensMaxCompletionTokens,
IncludeUsage: true,
})
Providerlabels returned assistant messages and defaults toopenai.OutputTokensMaxTokensis the default and uses themax_tokensfield.OutputTokensMaxCompletionTokensselects themax_completion_tokensfield.IncludeUsage: trueaddsstream_options.include_usage.- An output limit is sent only when
MaxOutputTokensis set, and it must be positive.
The request model name is forwarded as written. A complete provider usage snapshot is accepted even when IncludeUsage
is false and replaces the previous complete snapshot. Incomplete snapshots are ignored. The adapter translates
generation.ThinkingLevel to reasoning_effort, with off becoming none, and rejects unknown values.
Knowing the stream requirements
Generation uses an SSE response and requires the explicit [DONE] marker. Malformed JSON, an oversized SSE line, or a
clean end before [DONE] publishes a protocol error through the generation stream while preserving accepted partial
content. Other established read failures are *provider.TransportError values in the read phase. A content_filter
finish reason returns provider.ErrContentFiltered, keeps its raw finish reason and partial content on the assistant
message, and an Agent does not execute its tool calls.
The Using a Generator and Observing generation events chapters describe how to consume the stream and read the final result.
Sending embeddings
NewEmbedder returns an EmbeddingClient for the same endpoint. One Embed call is one POST to /embeddings, and the
returned vectors arrive in input order. The details live in Embedding a batch of text.
Classifying errors
The error type, not the diagnostic string, determines what to do.
Shared HTTP failures
| Failure | Error value |
|---|---|
| invalid adapter settings | openai.ErrInvalidConfig |
| invalid input or request compilation | provider.ErrInvalidRequest |
| connection could not be established | *provider.TransportError (establish) |
| non-success HTTP status | *provider.HTTPStatusError |
A non-success status stays an *provider.HTTPStatusError even when reading its body fails, and the read error is
available as Cause. Its StatusCode, bounded response data, and truncation flag remain available. A recognized
context-window message or code can join provider.ErrContextWindowExceeded to the status error, except for HTTP 429 and
rate-limit messages.
Generation failures
| Failure | Error value |
|---|---|
| provider error payload in an established SSE | *provider.StreamError |
| established read failure other than SSE end | *provider.TransportError (read) |
| malformed chunk, oversized line, or early SSE end | provider.ErrProtocol |
content_filter finish reason | provider.ErrContentFiltered |
| recognized context-window provider error | provider.ErrContextWindowExceeded |
Malformed chunk data and a clean end before [DONE] preserve accepted partial content and match provider.ErrProtocol.
The context-window sentinel joins only recognized overflow message, type, or code fields, and rate-limit errors never
use it.
Embedding failures
| Failure | Error value |
|---|---|
| malformed or oversized successful body | provider.ErrInvalidResponse |
io.ErrUnexpectedEOF in successful body | provider.ErrInvalidResponse |
| other successful body read failure | *provider.TransportError (read) |
Sentinels match with errors.Is, and typed fields read with errors.As. Provider messages are untrusted and capped at
4,000 runes in rendered error text, and an HTTP error body is retained up to 1 MiB with truncation marked. Caller
cancellation and deadlines keep their context identity, and the adapter never retries.
Compacting conversation context covers the one
recovery an Agent can perform, while other retry policy belongs to the application.