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

llm

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 28, 2026 License: Apache-2.0, BSD-3-Clause Imports: 14 Imported by: 0

README

llm-sdk-go

One Go client for Anthropic, OpenAI, Google Gemini and OpenRouter, built on top of each provider's official Go SDK.

Write your code once against a small Client interface. Switch provider and model by changing a config value.

import llm "github.com/morsalin1342/llm-sdk-go"

c, err := llm.New(llm.Config{
    Provider:   llm.Anthropic,           // or llm.OpenAI, llm.Gemini, llm.OpenRouter
    APIKey:     os.Getenv("API_KEY"),
    Model:      "claude-sonnet-5-5",     // the provider's own model name
    HTTPClient: http.DefaultClient,      // required: you decide how requests leave your process
})
if err != nil { /* ... */ }

res, err := c.Complete(ctx, llm.Request{
    System:    "You are a concise assistant.",
    MaxTokens: 512,
    Messages:  []llm.Message{{Role: llm.User, Text: "Why is my site slow?"}},
})
fmt.Println(res.Text, res.Usage.Input, res.Usage.Output, res.Stop)

Streaming:

res, err := c.Stream(ctx, req, func(ch llm.Chunk) error {
    fmt.Print(ch.Text)
    return nil // return an error to stop the stream
})

What you get

Providers Anthropic, OpenAI (Chat Completions), Google Gemini (Gemini API), OpenRouter
Calls Complete and Stream, text in and text out
One answer shape Text, Stop (end, length, refused, other), Model, token Usage
One error shape *llm.Error with a Kind: auth, rate_limit, too_large, bad_request, unavailable, canceled, internal. Match with errors.Is(err, &llm.Error{Kind: llm.ErrRateLimit})

Design choices

  • Official SDKs only. Each adapter calls the provider's own Go SDK. No hand-written HTTP clients and no compatibility shims.
  • You own the HTTP client. Config.HTTPClient is required and is the only way a request leaves the process, so you can put an egress policy, proxy, tracing or timeouts in it.
  • No hidden retries. The SDKs' own retries are switched off. You decide when and how to retry; a rate_limit or unavailable error says it is worth trying again.
  • Keys stay out of errors. A key is sent to the provider and nowhere else. Errors carry a kind, a status and a short reason, never the key and never the provider's response body.
  • Same behaviour, tested the same way. One test suite runs every adapter against a local fake of that provider's real wire format. No keys, no network.

Not yet

Tool calls, images, structured output and embeddings. Open an issue if you need one.

SDKs this builds on

Provider SDK Version
Anthropic anthropic-sdk-go v1.81.0
OpenAI openai-go v3.78.0
Google Gemini google.golang.org/genai v1.75.0
OpenRouter go-sdk (beta) v0.9.54

Requires Go 1.26 or newer.

License

Apache-2.0. See LICENSE.

Documentation

Overview

Package llm is the one client every AI feature of the panel uses to talk to a language model (TASK-0190, ADR-0041, REQ-0109). The caller names a provider (Anthropic, OpenAI, Google Gemini or OpenRouter), a model and a key; each provider is reached through its official Go SDK, and the code that asks does not change with the provider. Answers and errors have one shape.

The SDKs never open a connection of their own: the caller gives the HTTP client, which in the panel is the safe outbound client (REQ-0092). A key is used for the call and is never logged, returned or put in an error.

Index

Constants

View Source
const (
	User      = "user"
	Assistant = "assistant"
)

Roles of a message.

View Source
const (
	StopEnd     = "end"     // the model finished
	StopLength  = "length"  // it reached MaxTokens
	StopRefused = "refused" // it declined, or the provider's safety filter did
	StopOther   = "other"
)

Stop reasons, the same for every provider.

Variables

Providers lists them in the order screens show them.

Functions

This section is empty.

Types

type Chunk

type Chunk struct{ Text string }

Chunk is a piece of text of an answer being written.

type Client

type Client interface {
	Provider() Provider
	// Complete asks and waits for the whole answer.
	Complete(ctx context.Context, r Request) (Response, error)
	// Stream asks and calls emit with each piece as it arrives; it returns the
	// whole answer. A non-nil error from emit stops the stream and is returned.
	Stream(ctx context.Context, r Request, emit func(Chunk) error) (Response, error)
}

Client talks to one provider with one key and one model.

func New

func New(c Config) (Client, error)

New returns a client for the configured provider.

type Config

type Config struct {
	Provider Provider
	APIKey   string
	Model    string
	// BaseURL replaces the provider's address; tests point it at a fake.
	BaseURL string
	// HTTPClient carries every request. It is required: the SDKs must not use a
	// client the panel did not give them.
	HTTPClient *http.Client
}

Config makes a client.

type Error

type Error struct {
	Kind     Kind
	Provider Provider
	Status   int    // the HTTP status, when there was one
	Message  string // a short reason in words
}

Error is a failure of a call. Its text never holds the key or the provider's body.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Is

func (e *Error) Is(target error) bool

Is lets errors.Is match on the kind: errors.Is(err, &llm.Error{Kind: llm.ErrRateLimit}).

type Kind

type Kind string

Kind classifies an error the same way for every provider.

const (
	ErrAuth        Kind = "auth"        // the key was refused or lacks the right
	ErrRateLimit   Kind = "rate_limit"  // too many requests, or the quota is used up
	ErrTooLarge    Kind = "too_large"   // the request is more than the model takes
	ErrBadRequest  Kind = "bad_request" // the request or the model name is wrong
	ErrUnavailable Kind = "unavailable" // the provider is down, overloaded or unreachable
	ErrCanceled    Kind = "canceled"    // the caller stopped it
	ErrInternal    Kind = "internal"    // anything else
)

Error kinds.

type Message

type Message struct {
	Role string // User or Assistant
	Text string
}

Message is one turn of the conversation, as text.

type Provider

type Provider string

Provider names a service.

const (
	Anthropic  Provider = "anthropic"
	OpenAI     Provider = "openai"
	Gemini     Provider = "gemini"
	OpenRouter Provider = "openrouter"
)

The four providers (ADR-0041).

func (Provider) Valid

func (p Provider) Valid() bool

Valid reports whether p is one of the four.

type Request

type Request struct {
	System      string
	Messages    []Message // alternating, starting with a user turn
	MaxTokens   int       // the most the model may write; required
	Temperature *float64  // nil: the provider's default
}

Request is what is asked of the model.

func (Request) Check

func (r Request) Check() error

Check reports what is wrong with a request before any provider is asked.

type Response

type Response struct {
	Text  string
	Stop  string
	Model string
	Usage Usage
}

Response is the model's answer.

type Usage

type Usage struct {
	Input  int
	Output int
}

Usage counts tokens.

Jump to

Keyboard shortcuts

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