Documentation
¶
Overview ¶
Package sorus reads the [models.dev] catalog of LLM providers and models, and exposes a provider-agnostic Client interface that hides each provider's wire-protocol details behind a single fluent Request builder.
The typical flow is:
cat, _ := sorus.LoadCatalog(ctx)
client, _ := sorus.New(ctx, cat, "openrouter") // reads OPENROUTER_API_KEY from env
req := sorus.NewRequest(cat.Provider("openrouter").MustModel("anthropic/claude-sonnet-4-5")).
System("...").
Temperature(0.7).
User("Hello")
resp, err := client.Chat(ctx, req)
Supported wire protocols are listed on New: today, anything whose catalog `npm` is one of the supported values.
Index ¶
- Variables
- type Catalog
- func (c *Catalog) FindModel(qualifiedID string) (*Provider, *Model, bool)
- func (c *Catalog) Models() []*Model
- func (c *Catalog) ModelsByFamily(family string) []*Model
- func (c *Catalog) MustProvider(id string) *Provider
- func (c *Catalog) Provider(id string) *Provider
- func (c *Catalog) Providers() []*Provider
- type CatalogOption
- type Client
- type Event
- type EventType
- type ImageData
- type ImageURL
- type JSONSchema
- type Message
- type Model
- type ModelCost
- type ModelLimit
- type ModelModalities
- type Option
- type Part
- type Provider
- type Reasoning
- type ReasoningOption
- type ReasoningOptionType
- type Request
- func (r *Request) Assistant(text string) *Request
- func (r *Request) Clone() *Request
- func (r *Request) MaxTokens(v int) *Request
- func (r *Request) Message(msg Message) *Request
- func (r *Request) Messages() []Message
- func (r *Request) Model() *Model
- func (r *Request) Reasoning(r2 Reasoning) *Request
- func (r *Request) ResponseFormat(rf *ResponseFormat) *Request
- func (r *Request) Stop(seqs ...string) *Request
- func (r *Request) System(text string) *Request
- func (r *Request) Temperature(v float64) *Request
- func (r *Request) ToolChoice(choice ToolChoice) *Request
- func (r *Request) Tools(tool ...Tool) *Request
- func (r *Request) TopP(v float64) *Request
- func (r *Request) User(text string) *Request
- type Response
- type ResponseFormat
- type Role
- type Stream
- type Text
- type Tool
- type ToolCall
- type ToolChoice
- type ToolChoiceMode
- type ToolResult
- type Usage
Constants ¶
This section is empty.
Variables ¶
var ( ErrUnknownProvider = errors.New("sorus: unknown provider id") ErrUnsupportedProvider = errors.New("sorus: provider npm not supported by this build") ErrMissingAPIKey = errors.New("sorus: no API key found in expected env vars") ErrCatalogFetch = errors.New("sorus: catalog fetch failed") ErrCatalogParse = errors.New("sorus: catalog parse failed") ErrStreamClosed = errors.New("sorus: stream is closed") )
Errors returned by catalog and client operations.
Functions ¶
This section is empty.
Types ¶
type Catalog ¶
type Catalog struct {
// contains filtered or unexported fields
}
Catalog is the loaded models.dev catalog.
func LoadCatalog ¶
func LoadCatalog(ctx context.Context, opts ...CatalogOption) (*Catalog, error)
LoadCatalog fetches the models.dev catalog and parses it. The result is cached in memory for the lifetime of the process; subsequent calls with default options return the cached instance.
LoadCatalogFromBytes and LoadCatalogFromFS bypass the cache because they take user-supplied data and never touch the network.
func LoadCatalogFromBytes ¶
LoadCatalogFromBytes parses an in-memory catalog JSON blob.
func LoadCatalogFromFS ¶
LoadCatalogFromFS parses a catalog stored in the given fs.FS.
func (*Catalog) FindModel ¶
FindModel looks up a model by qualified id of the form "provider/model". Returns the provider, the model, and ok.
func (*Catalog) Models ¶
Models returns every model across every provider (cross-provider enumeration).
func (*Catalog) ModelsByFamily ¶
ModelsByFamily returns every model across every provider whose Family matches.
func (*Catalog) MustProvider ¶
MustProvider returns the provider with the given id, panicking if unknown.
type CatalogOption ¶
type CatalogOption func(*catalogConfig)
CatalogOption configures LoadCatalog.
func WithCatalogURL ¶
func WithCatalogURL(url string) CatalogOption
WithCatalogURL overrides the URL to fetch the catalog from. The default is the upstream https://models.dev/api.json. Any non-default URL bypasses the in-memory cache and re-fetches on every call.
Use a file:// URI for a vendored copy.
func WithFetchHTTPClient ¶
func WithFetchHTTPClient(h *http.Client) CatalogOption
WithFetchHTTPClient lets callers inject a custom http.Client used for fetching the catalog (timeouts, retries, transport instrumentation). Supplying one bypasses the in-memory cache so the custom client is actually used.
func WithRefresh ¶
func WithRefresh() CatalogOption
WithRefresh forces a fresh fetch even when an in-memory cache exists.
type Client ¶
type Client interface {
// Chat sends req and returns a single non-streaming [Response].
Chat(ctx context.Context, req *Request) (*Response, error)
// Stream sends req and returns a streaming [Stream]. Always call
// [Stream.Close] when done, even on error paths.
Stream(ctx context.Context, req *Request) (*Stream, error)
}
Client is the provider-agnostic chat surface. Implementations are selected by New based on the provider's `npm` value in the catalog.
A Client is bound to one provider. To switch providers, call New again.
func New ¶
New builds a Client for the named provider.
The provider's `env` list determines API-key resolution: the first name that's set in the process environment wins. Override with WithAPIKey if you already have a credential in hand (tests, multi-tenant apps, key rings).
Returns ErrUnknownProvider if the provider id isn't in the catalog, ErrUnsupportedProvider if the provider's `npm` value isn't handled by this build (today: anything other than `@ai-sdk/openai-compatible` or `@ai-sdk/openai`), or ErrMissingAPIKey if no credential was found and no WithAPIKey was supplied.
type Event ¶
type Event struct {
Type EventType
// EventContentDelta:
TextDelta string
// EventReasoningDelta:
ReasoningDelta string
// EventToolCallStart: the assembled tool call with ID and Name; Arguments
// may be empty if the model streams them separately.
ToolCall *ToolCall
// EventToolCallDelta: incremental JSON fragment of the in-progress
// arguments.
ArgumentDelta string
// EventDone: aggregated terminal fields.
StopReason string
Usage *Usage
Message *Message
// EventError:
Err error
}
Event is a single event yielded by a Stream.
type EventType ¶
type EventType string
EventType identifies the kind of Event.
const ( EventContentStart EventType = "content_start" EventContentDelta EventType = "content_delta" EventContentEnd EventType = "content_end" EventReasoningStart EventType = "reasoning_start" EventReasoningDelta EventType = "reasoning_delta" EventReasoningEnd EventType = "reasoning_end" EventToolCallStart EventType = "tool_call_start" EventToolCallDelta EventType = "tool_call_delta" EventToolCallEnd EventType = "tool_call_end" EventDone EventType = "done" EventError EventType = "error" )
type ImageURL ¶
type ImageURL struct {
URL string
Detail string // "auto" | "low" | "high"; empty means provider default
}
ImageURL is an image referenced by URL.
type JSONSchema ¶
type JSONSchema struct {
Name string
Schema map[string]any
Strict bool // best-effort "strict" mode; provider-dependent whether honored
}
JSONSchema is the JSON Schema for the response. Schema is the schema body as a Go map. Use a schema-builder library like invopop/jsonschema if you want typesafety.
type Message ¶
type Message struct {
Role Role
Content []Part
ToolCalls []ToolCall // populated on assistant messages that emit tool calls
}
Message is one turn in a conversation.
func AssistantMessage ¶
AssistantMessage constructs a plain-text assistant message.
func SystemMessage ¶
SystemMessage constructs a plain-text system message.
func UserMessage ¶
UserMessage constructs a plain-text user message.
type Model ¶
type Model struct {
ID string // provider-local id, e.g. "anthropic/claude-sonnet-4-5"
Name string // "Claude Sonnet 4.5"
Family string // "claude-sonnet"
Description string
Modalities ModelModalities // {input: [...], output: [...]}
Limit ModelLimit // {context, input, output}
Cost ModelCost // per-1M-token USD prices; zero means "not advertised"
Reasoning bool
ReasoningOptions []ReasoningOption // effort enum / toggle / budget_tokens
ToolCall bool
StructuredOutput bool
Temperature bool
Attachment bool
Knowledge string // cutoff, "YYYY-MM" or "YYYY-MM-DD"
ReleaseDate string // "YYYY-MM-DD"
LastUpdated string // "YYYY-MM-DD"
Status string // "", "alpha", "beta", "deprecated"
OpenWeights bool
// contains filtered or unexported fields
}
Model is one model record on a provider. Model is pure description — no configuration is attached. Configuration lives on Request.
type ModelLimit ¶
type ModelModalities ¶
type Option ¶
type Option func(*config)
Option configures New.
func WithAPIKey ¶
WithAPIKey overrides the API-key lookup. By default the catalog's `env` list is consulted for an env-var that's set.
func WithHTTPClient ¶
WithHTTPClient injects a custom *http.Client used by the underlying provider transport.
type Part ¶
type Part interface {
// contains filtered or unexported methods
}
Part is a content part of a Message. The interface is sealed: only Text, ImageURL, ImageData, and ToolResult implement it. Adding new part types is purely additive because the marker method is unexported.
type Provider ¶
type Provider struct {
ID string // short id, e.g. "openrouter"
NPM string // the upstream AI SDK package identifier, e.g. "@ai-sdk/openai-compatible"
Name string // human-readable name, e.g. "OpenRouter"
Env []string // env-var names this provider reads for credentials, in priority order
API string // base URL, e.g. "https://openrouter.ai/api/v1"; empty if the SDK ships its own default
Doc string // docs URL (may be empty)
// contains filtered or unexported fields
}
Provider is one catalog provider entry. It carries the metadata needed to pick a wire-protocol implementation and to look up credentials in the process environment.
func (*Provider) ModelsByFamily ¶
ModelsByFamily returns models on this provider whose Family matches.
type Reasoning ¶
type Reasoning struct {
// Effort is a categorical effort level: "low", "medium", "high",
// "xhigh", etc. Provider-specific values are accepted verbatim.
Effort string
// BudgetTokens is the reasoning token budget. Zero means unset.
// Honored by providers that expose a budget-style control
// (Anthropic `thinking.budget_tokens`, Gemini `thinking_budget`,
// DeepSeek `thinking_budget`); ignored by chat-completions-style
// providers that don't take a budget.
BudgetTokens int
}
Reasoning configures model reasoning effort/budget.
Each implementation reads only the fields it understands; over-populating is harmless. For chat-completions providers only Effort is honored today (it maps to the upstream `reasoning_effort` parameter).
type ReasoningOption ¶
type ReasoningOption struct {
Type ReasoningOptionType
Values []string // populated when Type == ReasoningEffort
Min *int // populated when Type == ReasoningBudgetTokens
Max *int // populated when Type == ReasoningBudgetTokens (may be nil)
}
ReasoningOption describes a model's reasoning control surface.
type ReasoningOptionType ¶
type ReasoningOptionType string
ReasoningOptionType is the shape of one entry in Model.ReasoningOptions.
const ( ReasoningEffort ReasoningOptionType = "effort" ReasoningToggle ReasoningOptionType = "toggle" ReasoningBudgetTokens ReasoningOptionType = "budget_tokens" )
type Request ¶
type Request struct {
// contains filtered or unexported fields
}
Request is the unit of work sent to a Client. It is built by fluent configuration starting with NewRequest.
Internally, all optional fields are stored as pointers (or sentinel zero values) so we can distinguish "unset" from "set to zero". Externally, callers see only literal setters taking primitive types.
func NewRequest ¶
NewRequest begins a fluent request bound to model. Initial messages can be passed positionally.
func (*Request) Clone ¶
Clone returns an independent copy of r. Mutations on the clone do not affect the original.
func (*Request) ResponseFormat ¶
func (r *Request) ResponseFormat(rf *ResponseFormat) *Request
ResponseFormat sets the structured-output format.
func (*Request) System ¶
System appends a plain-text system message. Equivalent to Message(SystemMessage(text)).
func (*Request) Temperature ¶
Temperature sets the sampling temperature.
func (*Request) ToolChoice ¶
func (r *Request) ToolChoice(choice ToolChoice) *Request
ToolChoice sets the tool-choice mode for this request.
type Response ¶
type Response struct {
ID string
Model string
Message Message
StopReason string // "end_turn" | "max_tokens" | "tool_use" | "stop_sequence" | provider-specific
Usage Usage
Raw any // provider-specific raw payload; nil if the implementation doesn't expose one
}
Response is what Client.Chat returns.
type ResponseFormat ¶
type ResponseFormat struct {
Type string // "json_object" | "json_schema"
JSONSchema *JSONSchema // required when Type == "json_schema"
}
ResponseFormat asks the model to produce structured output.
Type is "json_object" for unconstrained JSON output, or "json_schema" to constrain by a schema. JSONSchema is required when Type == "json_schema".
type Stream ¶
type Stream struct {
// contains filtered or unexported fields
}
Stream is a streaming response. Iterate with Stream.Next, inspect the latest event with Stream.Event, then call Stream.Close. Read errors via Stream.Err.
func (*Stream) Close ¶
Close releases the stream's underlying resources. Calling Close after Close is a no-op.
func (*Stream) Event ¶
Event returns the most recent event from the most recent successful Stream.Next call.
func (*Stream) Next ¶
Next advances the stream. Returns false when the stream is exhausted or closed. After Next returns false, Stream.Err reports any terminal error.
type Tool ¶
Tool describes one tool the model may call. Parameters is a JSON Schema object; build it manually or with invopop/jsonschema.
type ToolCall ¶
ToolCall is one tool call emitted by the model. Arguments is a JSON string; json.Unmarshal it to get structured input.
type ToolChoice ¶
type ToolChoice struct {
Mode ToolChoiceMode
Name string
}
ToolChoice controls how the model picks tools. Mode is required; Name is only meaningful when Mode == ToolChoiceSpecific.
type ToolChoiceMode ¶
type ToolChoiceMode string
ToolChoiceMode enumerates the tool-selection modes.
const ( ToolChoiceAuto ToolChoiceMode = "auto" ToolChoiceNone ToolChoiceMode = "none" ToolChoiceRequired ToolChoiceMode = "required" ToolChoiceSpecific ToolChoiceMode = "specific" )
type ToolResult ¶
ToolResult is one tool result in a Message with role RoleTool.