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

llm

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 15, 2026 License: MIT Imports: 12 Imported by: 0

README

llm

Вызов языковой модели одним контрактом поверх нескольких плеч. Корень пакета — контракт, повторы, бюджет времени, классы отказа и наблюдатель; протокол поставщика — в подпакете. Сейчас есть ollama (нативный /api/chat), следом gigachat и yandex.

c := llm.New(ollama.New("http://localhost:11434"), 90*time.Second)

res, err := c.Chat(ctx, llm.Request{
	Task:  "receipt",
	Model: "gemma4:31b-cloud",
	Messages: []llm.Message{
		{Role: llm.RoleSystem, Text: prompt},
		{Role: llm.RoleUser, Text: "скриншот", Images: []llm.Image{{MIME: "image/png", Data: png}}},
	},
	Output: llm.Output{Mode: llm.ModeJSON},
})

Что делает клиент

  • Срок стоит на попытке, не на вызове: повтор после просрочки получает полный срок. Budget() считает худший случай — его обязана отвести себе джоба.
  • Классы отказа. immediate (сеть, 5xx, 408, просрочка) и after_delay (429) клиент повторяет сам с удвоением паузы и Retry-After. needs_configuration (401, 402, 403, 404: ключ, баланс, модель) и never (прочие 4xx) — не повторяет; первый при этом восстановим (llm.Recoverable) — чинит оператор, а не запрос.
  • Просьба подождать дольше MaxRetryAfter не ждётся: отказ уходит наверх со сроком как есть. Повторяет его очередь потребителя, а не клиент, заснувший на час.
  • Наблюдатель получает отчёт на каждую попытку и на вызов: исход, фаза, длительность, расход, CallID и номер попытки. Он синхронный, без контекста и без права на ошибку — счётчики, не журнал. Журнал пишет обёртка потребителя по Result.Report и CallError.Report: отчёт есть и у отказа, расход в нём — всех попыток.
  • Расход — по попыткам, Usage.Known ложно, если хоть одна попытка счётчиков не сообщила; счётчики тогда — нижняя граница.
  • Негодный запрос (без модели, schema без схемы) отбивается до плеча классом never.
  • Ollama: ответ принимается только с done=true и ровно одним конвертом — первый фрагмент потока за ответ не сойдёт.

Что остаётся потребителю

Промпты и схемы, разбор ответа, пороги качества, тарифы и стоимость, журнал вызовов, квоты. llm.Fingerprint(...) даёт идентичность вызова до его выполнения — плечо, модель, ревизия, хеш промпта, схема, подготовка входа — чтобы дедупить прогоны и не смешивать плечи.

Documentation

Overview

Package llm — вызов языковой модели одним контрактом поверх нескольких плеч: повторы, бюджет времени, классы отказа и наблюдатель — здесь, протокол поставщика — в подпакете (ollama; gigachat и yandex следом).

Index

Constants

View Source
const (
	DefaultTimeout       = 90 * time.Second
	DefaultAttempts      = 3
	DefaultPause         = 2 * time.Second
	DefaultMaxPause      = time.Minute
	DefaultMaxRetryAfter = 30 * time.Second
)

Умолчания клиента. Attempts × Timeout + паузы — то, что вызывающий обязан себе отвести (Client.Budget), иначе очередь оборвёт последнюю попытку.

View Source
const (
	OutcomeOK          = "ok"
	OutcomeCancelled   = "cancelled"
	OutcomeTimeout     = "timeout"
	OutcomeHTTP4xx     = "http_4xx"
	OutcomeHTTP5xx     = "http_5xx"
	OutcomeBadResponse = "bad_response"
	OutcomeBadRequest  = "bad_request"
	OutcomeNetwork     = "network"
	OutcomeError       = "error"
)

Исходы попытки — про транспорт и годность конверта, а не про контракт модели.

Variables

This section is empty.

Functions

func Fingerprint

func Fingerprint(parts ...string) string

Fingerprint — отпечаток набора строк, из которых складывается идентичность вызова: плечо, модель, ревизия, хеш промпта, схема, опции, подготовка входа. Части кодируются длиной, поэтому границы однозначны; порядок значим. 128 бит hex.

func Latency

func Latency(err error) time.Duration

Latency — сколько длился отказавший вызов; не наш отказ даёт ноль.

func Ptr

func Ptr[T any](v T) *T

Ptr — указатель на значение для необязательных полей запроса.

func Recoverable

func Recoverable(err error) bool

Recoverable отвечает, имеет ли смысл повторить вызов позже: всё, кроме RetryNever.

func RetryAfter

func RetryAfter(h http.Header) time.Duration

RetryAfter разбирает заголовок в обеих формах: секунды числом и HTTP-дата. Отсутствующий, непонятный и отрицательный — ноль; чрезмерный насыщается.

Types

type AttemptReport

type AttemptReport struct {
	CallID         string
	Attempt        int
	Provider       string
	RequestedModel string
	Model          string
	Task           string
	Outcome        string
	Phase          Phase
	Duration       time.Duration
	ServerLatency  time.Duration
	Usage          Usage
}

AttemptReport — одна попытка. Model — имя из ответа, когда поставщик его назвал.

type CallError

type CallError struct {
	Provider   string
	Model      string
	Phase      Phase
	Status     int // код HTTP; 0 — ответа не было (сеть, просрочка, отмена)
	Message    string
	Attempts   int
	RetryAfter time.Duration
	Latency    time.Duration
	Class      RetryClass
	Report     CallReport
	Err        error
}

CallError — отказ вызова целиком, с решением о повторе, потраченным временем и тем же отчётом, что получает наблюдатель.

func (*CallError) Error

func (e *CallError) Error() string

func (*CallError) Unwrap

func (e *CallError) Unwrap() error

type CallReport

type CallReport struct {
	CallID         string
	Provider       string
	RequestedModel string
	Model          string
	Task           string
	Outcome        string
	Class          RetryClass
	Attempts       int
	Duration       time.Duration
	Usage          Usage
}

CallReport — вызов целиком: исход, класс отказа, число попыток, расход всех попыток.

type Chat

type Chat interface {
	Chat(ctx context.Context, req Request) (Result, error)
}

Chat — узкий интерфейс потребителя: то, что делает Client.

type Client

type Client struct {
	Provider Provider
	// Timeout — на одну попытку, не на весь вызов: повтор после просрочки получает полный срок.
	Timeout  time.Duration
	Attempts int
	// Pause — базовая пауза между попытками, дальше удваивается до MaxPause.
	Pause    time.Duration
	MaxPause time.Duration
	// MaxRetryAfter — потолок просьбы подождать: дольше — отказ [RetryAfterDelay]
	// без ожидания, срок в нём исходный; повторяет его очередь потребителя.
	MaxRetryAfter time.Duration
	// Observe — приёмник отчётов; пустое поле не считает ничего.
	Observe Observer
}

Client — вызов с повторами поверх одного плеча. Поля открыты: бюджет сверяется с таймаутом джобы снаружи, а не прячется в константах.

func New

func New(p Provider, timeout time.Duration) *Client

New собирает клиента с умолчаниями бюджета.

func (*Client) Budget

func (c *Client) Budget() time.Duration

Budget — верхняя оценка времени вызова: все попытки со своими таймаутами плюс худшие паузы; переполнение насыщается до максимальной длительности.

func (*Client) Chat

func (c *Client) Chat(ctx context.Context, req Request) (Result, error)

Chat вызывает модель, повторяя отказы классов RetryImmediate и RetryAfterDelay в пределах числа попыток и потолка ожидания. Негодный запрос отбивается до плеча.

type Image

type Image struct {
	MIME string
	Data []byte
}

Image — кадр при сообщении: байты и MIME, как их видит поставщик.

type Message

type Message struct {
	Role   Role
	Text   string
	Images []Image
}

Message — одно сообщение разговора; кадры едут при данных, а не при инструкции.

type Mode

type Mode string

Mode — форма ответа: свободный текст, любой JSON или JSON по схеме.

const (
	ModeText   Mode = "text"
	ModeJSON   Mode = "json"
	ModeSchema Mode = "schema"
)

Формы ответа. Схему соблюдают не все модели — контракт полей живёт и в промпте.

type Observer

type Observer interface {
	Attempt(AttemptReport)
	Call(CallReport)
}

Observer — приёмник отчётов: быстрый и потокобезопасный, зовётся синхронно внутри вызова без контекста и без права на ошибку — счётчики, не журнал. Запись в базу делает обёртка потребителя по Result.Report и CallError.Report.

type Output

type Output struct {
	Mode   Mode
	Schema json.RawMessage
}

Output — требуемая форма ответа; Schema обязательна при ModeSchema.

type Phase

type Phase string

Phase — где отказал вызов: у облачных плеч перед генерацией стоят вход и загрузка кадров.

const (
	PhaseAuth      Phase = "auth"
	PhaseUpload    Phase = "upload"
	PhaseInference Phase = "inference"
)

Фазы вызова.

type PhaseError added in v0.2.0

type PhaseError struct {
	Phase Phase
	Err   error
}

PhaseError помечает фазой любую причину — сеть при OAuth, отказ загрузки кадра.

func (*PhaseError) Error added in v0.2.0

func (e *PhaseError) Error() string

func (*PhaseError) Unwrap added in v0.2.0

func (e *PhaseError) Unwrap() error

type Provider

type Provider interface {
	Name() string
	Complete(ctx context.Context, req Request) (Result, error)
}

Provider — одна попытка вызова у конкретного плеча. Отказ HTTP возвращается *StatusError, негодный ответ — *ResponseError (с расходом, если он был в конверте), негодный запрос — *RequestError, сеть и контекст — как есть.

type Request

type Request struct {
	CallID      string
	Task        string
	Model       string
	Messages    []Message
	Output      Output
	Temperature *float64
}

Request — один вызов модели. CallID связывает отчёты попыток и вызова между собой и с журналом потребителя; Task — имя задачи для метрик, набор значений ограничен конфигом потребителя.

func (Request) Validate added in v0.2.0

func (r Request) Validate() error

Validate отбивает запрос, который поставщик не сможет собрать: без модели, с неизвестным режимом, со схемой без схемы. Ошибка — *RequestError.

type RequestError added in v0.2.0

type RequestError struct {
	Message string
	Err     error
}

RequestError — запрос не собрать: поставщика не звали, повтор бессмыслен.

func (*RequestError) Error added in v0.2.0

func (e *RequestError) Error() string

func (*RequestError) Unwrap added in v0.2.0

func (e *RequestError) Unwrap() error

type ResponseError

type ResponseError struct {
	Message       string
	Usage         Usage
	Model         string
	ServerLatency time.Duration
	Err           error
}

ResponseError — поставщик ответил, но ответа модели в теле нет: отказ маршрута под кодом 200, битый или незаконченный конверт, пустое содержимое. Причина сохраняется: просрочка при чтении тела обязана остаться просрочкой.

func (*ResponseError) Error

func (e *ResponseError) Error() string

func (*ResponseError) Unwrap added in v0.2.0

func (e *ResponseError) Unwrap() error

type Result

type Result struct {
	// Model — имя из ответа поставщика; пустое у ответа заменяется запрошенным.
	Model        string
	Text         string
	FinishReason string
	Usage        Usage
	// ServerLatency — время на стороне поставщика, если он его сообщает; ноль — не сообщил.
	ServerLatency time.Duration
	// Latency — сколько ждал вызывающий: все попытки и паузы между ними.
	Latency time.Duration
	Report  CallReport
}

Result — ответ модели. Usage и ServerLatency — удавшейся попытки; расход всех попыток и число попыток — в Report, тот же отчёт получает наблюдатель.

type RetryClass

type RetryClass string

RetryClass — что делать с отказом. Немедленный повтор и повтор после паузы делает клиент; NeedsConfiguration и Never клиент не повторяет, но первый остаётся восстановимым — его чинит оператор, а не запрос.

const (
	RetryImmediate          RetryClass = "immediate"
	RetryAfterDelay         RetryClass = "after_delay"
	RetryNeedsConfiguration RetryClass = "needs_configuration"
	RetryNever              RetryClass = "never"
)

Классы отказа.

type Role

type Role string

Role — роль сообщения. Инструкция и данные едут разными ролями: приехавшее с данными «забудь инструкции» иначе получило бы ту же роль, что правила.

const (
	RoleSystem    Role = "system"
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
)

Роли сообщений.

type StatusError

type StatusError struct {
	Status     int
	Message    string
	RetryAfter time.Duration
	Phase      Phase
}

StatusError — не-2xx от поставщика вместе с просьбой подождать.

func (*StatusError) Error

func (e *StatusError) Error() string

type Usage

type Usage struct {
	InputTokens  int
	OutputTokens int
	Known        bool
}

Usage — расход токенов. Known ложно, когда хотя бы часть расхода поставщик не сообщил: счётчики тогда — известная нижняя граница, а не полный счёт.

func (Usage) Add added in v0.2.0

func (u Usage) Add(part Usage) Usage

Add складывает расход; неполнота любой части и переполнение делают сумму неполной.

Directories

Path Synopsis
Package ollama — плечо Ollama поверх нативного `/api/chat`: кадры base64 в сообщении, `format` подсказкой или схемой, серверные длительности в ответе.
Package ollama — плечо Ollama поверх нативного `/api/chat`: кадры base64 в сообщении, `format` подсказкой или схемой, серверные длительности в ответе.

Jump to

Keyboard shortcuts

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