This section documents the core skyl module: the client, the request and
response types, the conversation model, streaming, and the error surface. Every
page follows the same shape — Reference for the exact contract, Usage
for what you actually do with it, Troubleshooting for what goes wrong.
Installing#
go get github.com/BAGOMBEKA-JOB-DEV/skylimport "github.com/BAGOMBEKA-JOB-DEV/skyl"import "github.com/BAGOMBEKA-JOB-DEV/skyl"The core module requires Go 1.22 or later and has zero external dependencies. Importing it pulls in no router, no logger, no vendor SDK.
The modules#
skyl is four Go modules in one repository. Which one a symbol lives in tells you what it costs you.
| Module | Import path | Go | Dependencies |
|---|---|---|---|
| skyl | github.com/BAGOMBEKA-JOB-DEV/skyl | 1.22 | noneThe core library. Zero external dependencies. |
| provider/anthropic | github.com/BAGOMBEKA-JOB-DEV/skyl/provider/anthropic | 1.24 | anthropic-sdk-goA separate module, because the official SDK brings a dozen transitive dependencies. |
| gateway | github.com/BAGOMBEKA-JOB-DEV/skyl/gateway | 1.25 | go-chi/chi, skyl/otelThe optional HTTP service. Importing the core library never pulls in chi. |
| otel | github.com/BAGOMBEKA-JOB-DEV/skyl/otel | 1.25 | go.opentelemetry.io/otelOpenTelemetry instrumentation. Nobody who does not want it pays for it. |
The shape of the API#
Four concepts carry the whole library.
skyl.Client
validation · retry/backoff · timeout · hooks
skyl.Provider
the seam — four methods
anthropic
api.anthropic.com
openai
api.openai.com
gemini
generativelanguage…
openaicompat
any OpenAI-shaped host
Provider is the seam — four methods, small
enough to implement in your own repository. Client
wraps a provider and adds everything cross-cutting, so retry and validation are
written once instead of once per vendor. Request
and Response are the provider-agnostic shapes
that travel between them.
Client#
| Symbol | Signature |
|---|---|
| New | func New(p Provider, opts ...Option) *Client |
| Client.Complete | func (c *Client) Complete(ctx, *Request) (*Response, error) |
| Client.Stream | func (c *Client) Stream(ctx, *Request) (Stream, error) |
| Client.Models | func (c *Client) Models(ctx) ([]ModelInfo, error) |
| Client.Provider | func (c *Client) Provider() Provider |
Options#
Every option is a func(*Client), applied in order by New.
| Option | Default | What it bounds |
|---|---|---|
| WithMaxRetries | 3 | How many times a retryable failure is retried |
| WithRetryDelay | 500ms / 30s | skyl's own computed backoff |
| WithRetryAfterCap | 5m | How long a provider's Retry-After may delay a retry |
| WithTimeout | 10m | A single attempt — not the whole retry sequence |
| WithHook | none | Nothing; it registers an observer |
Requests and responses#
| Field | Type | Description |
|---|---|---|
| Model | string | The provider's model identifier, passed through untouched. skyl never validates it against a list, so a model released after your skyl build works immediately — and a typo surfaces as the provider's own not-found error rather than a local one. Zero value: rejected by Validate: the model is required |
| System | string | The system prompt. Adapters place it where the provider expects — a top-level field for Anthropic, a leading message for OpenAI, systemInstruction for Gemini. Zero value: no system prompt is sent |
| Messages | []Message | The conversation so far. skyl does not police role ordering: providers disagree about what is legal, and rejecting a shape one vendor accepts would be skyl deciding something it has no business deciding. Zero value: rejected by Validate: at least one message is required |
| MaxTokens | int | Caps the response length. Zero value: the provider's default — which for Anthropic is an error, so skyl supplies 4096 there. A negative value is rejected by Validate. |
| Temperature | *float64 | Sampling temperature. A non-nil value is always sent: several current reasoning models reject it outright, and skyl does not second-guess that, because silently dropping a field you set is worse than the provider’s own error. Zero value: nil means the provider's default; leave it nil unless you mean it |
| TopP | *float64 | Nucleus sampling. Same always-sent semantics as Temperature. Zero value: nil means the provider's default |
| Stop | []string | Sequences that end generation. Zero value: no stop sequences |
| Tools | []Tool | Tools the model may call. A tool with no name is rejected by Validate. Zero value: no tools offered |
| ToolChoice | *ToolChoice | Constrains whether and how the model may call tools: auto, none, required, or a named tool. All four modes are mapped on all four adapters. Zero value: nil means ToolChoiceAuto |
| Thinking | *Thinking | Requests reasoning. A nil pointer and a zero value mean different things: nil is "provider default", &Thinking{} is "explicitly off". Zero value: nil means the provider's default |
| ProviderOptions | map[string]any | An escape hatch: arbitrary vendor-specific fields merged into the outbound payload, overriding anything skyl set. skyl does not validate the contents — that is the point. Zero value: nothing extra is sent |
Errors#
skyl classifies every provider failure onto one of eight sentinels, so you
branch with errors.Is rather than on message text.
| Sentinel | Message | Retried? | Meaning |
|---|---|---|---|
ErrAuth | skyl: authentication failed | Never | The credential was missing, malformed, or rejected. The same key will fail again. |
ErrRateLimit | skyl: rate limited | Yes | The provider is throttling. Retried with backoff, honouring Retry-After. |
ErrNotFound | skyl: not found | Never | The model or endpoint does not exist for this account. Because model IDs pass through unvalidated, a typo arrives here rather than failing locally. |
ErrBadRequest | skyl: invalid request | Never | The request was malformed. Often produced locally by Request.Validate. |
ErrServer | skyl: provider server error | Yes | The provider failed on its side. Retried with backoff. |
ErrUnsupported | skyl: unsupported by this provider | Never | This provider cannot express part of the request. Returned instead of silently dropping data, because a quietly discarded image looks like a model that ignored the question. |
ErrRefusal | skyl: model declined the request | Never | The model or its safety classifiers declined. The same prompt gets the same answer. |
ErrStreamClosed | skyl: stream is closed | Never | The stream was used after being closed. |
Full detail: Errors.
Escape hatches#
Two, always present, because every abstraction over a fast-moving API is wrong somewhere:
Request.ProviderOptionssends arbitrary vendor-specific fields, overriding anything skyl set.Response.Rawis the untouched provider body, and is always populated.