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

llm

package module
v0.7.2 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2026 License: MIT Imports: 15 Imported by: 0

README

llm

Вызов языковой модели одним контрактом поверх нескольких плеч. Корень пакета — контракт, повторы, бюджет времени, классы отказа и наблюдатель; протокол поставщика — в подпакете. Есть ollama (нативный /api/chat), gigachat (OAuth, /files) и yandex (OpenAI-совместимый chat/completions); распознавание речи — yandex.NewSpeech (SpeechKit) и salutespeech, текста — yandex.NewOCR (Vision OCR).

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, llm.ConfigError, непроверенный сертификат: ключ, баланс, модель, CA) и never (прочие 4xx, ответ генерации сверх предела тела) — не повторяет; первый при этом восстановим (llm.Recoverable) — чинит оператор, а не запрос.
  • Просьба подождать дольше MaxRetryAfter не ждётся: отказ уходит наверх со сроком как есть. Повторяет его очередь потребителя, а не клиент, заснувший на час.
  • Наблюдатель получает отчёт на каждую попытку и на вызов: исход, фаза, длительность, расход, CallID и номер попытки. Он синхронный, без контекста и без права на ошибку — счётчики, не журнал. Журнал пишет обёртка потребителя по Result.Report и CallError.Report: отчёт есть и у отказа, расход в нём — всех попыток.
  • Расход — по попыткам и непересекающимися частями: BillableInput и CachedInput — вход, Output и Reasoning — выход, Raw — счётчики поставщика как есть. Usage.Known ложно, если хоть одна попытка генерации счётчиков не сообщила или сообщила противоречивые; счётчики тогда — нижняя граница. Известный ноль — только отказ до генерации: вход, загрузка кадров, 4xx кроме 408; у 5xx и сетевого отказа генерации расход неизвестен.
  • Обрезанный, отфильтрованный и отклонённый моделью ответ (Finish.Kind length, content_filter, refusal) — отказ never с исходом truncated/filtered/refused и расходом: он оплачен, повтор оплатил бы его ещё раз. finish_reason=error GigaChat повторяется как негодный ответ и может оплатиться повторно — не нужен повтор, ставьте Attempts = 1.
  • Уборка кадров (gigachat) идёт после срока попытки своим бюджетом CleanupBudget на попытку — Client.Budget() его не включает, Route.Budget() включает. Неубранное — CleanupWarning в отчёте попытки; Uncertain — загрузки без ответа, чей файл мог остаться с неизвестным id.
  • Негодный запрос (без модели, schema без схемы) и запрос сверх профиля плеча (Provider.Capabilities: кадры, json, схема, опции) отбиваются до плеча классом never; неизвестный профиль не подтверждает ничего.
  • Result и CallError несут начало вызова и отчёты попыток с RequestID поставщика.
  • Ollama: ответ принимается только с done=true и ровно одним конвертом — первый фрагмент потока за ответ не сойдёт.

Маршруты задач

llmconfig.Load разбирает JSON-файл маршрутов (providers, tasks, prices) строго: неизвестное поле, повтор ключа и чужой тип — ошибки с путём, все сразу. Секреты в файле — только ссылки ${ИМЯ}, и разворачиваются они лишь у плеч, на которые ссылается задача: объявленное облако без ключа в окружении dev-стенду не мешает. Механизм сборки плеч — в библиотеке (llmbuild), транспорт и политика — у потребителя.

cfg, err := llmconfig.Load(data, os.LookupEnv)
routes, err := cfg.Routes()
router := &llm.Router{Providers: map[string]llm.Provider{"local": ollama.New(host)}, Tasks: routes}
err = router.Validate(map[string]llm.Needs{"nameplate": {ImagesPerMessage: 1, ImagesPerRequest: 1}, "ask": {}})
route, err := router.Resolve("ask")
res, err := route.Chat(ctx, llm.Input{CallID: id, Messages: msgs, Mode: llm.ModeJSON})
  • Router.Validate отбивает до первого вызова неизвестную и лишнюю задачу, несобранное плечо, неподтверждённый профиль модели и профиль, не принимающий форму, опции или кадры задачи. Resolve кадров не знает: потребитель проверяет полный набор Needs один раз при сборке.
  • Mode во входе стоит задавать константой разбора: пустой принимает форму из конфига. Fingerprint не видит адреса плеча и схемы входа — смена весов под тем же именем модели требует новой revision, а хеш схемы и промпта кладёт в отпечаток потребитель.
  • Provider.AttemptOverhead обязателен: Route.Budget прибавляет его на попытку, и обёртка, встроившая плечо, его не теряет.
  • Route — неизменяемый снимок: модель и опции задаёт маршрут, форма или схема входа, расходящаяся с ним, — отказ never. Descriptor() — источник версии вызова и журнала, Descriptor.Fingerprint не зависит от имени плеча в конфиге и тарифа.
  • Запасного плеча и перечитывания конфига нет намеренно: они делают неоднозначными расход, версию и причину отказа. Смена плеча — правка файла и рестарт.
Сборка из конфига

llmbuild.Router собирает плечи Config.Active() и задачи чата, речи и OCR; конфиг — развёрнутый (Load или Expand): неразвёрнутый ключ плеча — ошибка сборки, а не отказ на первом вызове.

router, err := llmbuild.Router(*cfg, llmbuild.Options{})
  • Options.PEM — корни по имени плеча целиком, без системных; нет ключа — читается ca_file. Options.HTTP — основа клиента по имени и виду плеча; корни ложатся на клон её транспорта.
  • Обёртки, наблюдатель, счётчики и проверки задач под себя потребитель надевает после сборки; llmbuild.Arm собирает одно плечо, если роутер ему не нужен.

Живые проверки

cmd/llmcheck собирается только тегом live и ходит в сеть на деньги аккаунта; go test ./... гоняет его тесты вложенным прогоном без сети.

GIGACHAT_KEY=… go run -tags live ./cmd/llmcheck -config llm.json -tasks nameplate -max-requests 10
  • Проверки: oauth (единицы и срок expires_at), model, vision (кадр в форме маршрута — так проверяется кадр со схемой), reasoning, finish (предел в один токен обязан дать truncated без повтора), limits (предельное число кадров профиля, по умолчанию выключена). Одинаковые маршруты разных задач проверяются один раз.
  • -max-requests считает попытки вызова и запросы проверки oauth; внутренние запросы адаптера (токен, загрузка и удаление файлов) в него не входят. -max-cost — порог, а не резерв: проверка за ним не начинается, вызов, перелетевший его, оплачен целиком и даёт выход 3; расход без тарифа держит только -max-requests.
  • Протокол — markdown без секретов, текста ответов и отказов поставщика и идентификаторов запросов. Выход 1 — нарушение контракта, 2 — флаги или конфиг, 3 — прогон остановлен потолком или сроком.
  • Проверка — контракт плеча, а не качество извлечения: его меряет прогон потребителя на своих данных.

Распознавание речи

Отдельный контракт Transcriber: плечо речи не реализует Provider и не отвечает на чатовые проверки. Вход один — сырые сэмплы LPCM 16 бит моно; контейнер (WAV) снимает потребитель.

router := &llm.Router{Speech: map[string]llm.Transcriber{"sber": asr}, SpeechTasks: speechRoutes}
route, ok, err := router.ResolveSpeech("dictation")
res, err := route.Transcribe(ctx, llm.SpeechInput{CallID: id, SampleRate: 16000, PCM: pcm})
  • Раздел speech в llmconfig необязателен: без него ResolveSpeech даёт false без ошибки, и речь у потребителя выключена. Config.SpeechRoutes собирает задачи речи, плечи — потребитель.
  • Запись длиннее SpeechCapabilities.MaxAudio плеча или тяжелее предела тела API (1 МиБ у SpeechKit, 2 МиБ у SaluteSpeech — выше 16 кГц он наступает раньше срока) отбивается до сети классом never.
  • Повторы — те же классы отказа и паузы, что у чата. Запись идёт в расход при любом отказе, кроме доказуемого до работы плеча (4xx кроме 408, *RequestError, конфигурация, OAuth): 5xx, срок, сеть и негодный ответ — плечо могло распознать запись.
  • Пустой Transcript.Text — не ошибка: что тишина, решает потребитель.
  • llmcheck speech -config llm.json -dir записи/ прогоняет каталог *.wav по маршруту: задержка, секунды и стоимость в протокол, текст — в *.txt рядом с записью. Качество сравнивается глазами.

Распознавание текста

Третий контракт — Recognizer, устроен как речь: свои плечи, раздел ocr в llmconfig, маршрут и цикл попыток на общих паузах, сроке и классах отказа. Вход — один кадр JPEG или PNG; PDF растеризует потребитель (страница — кадр), библиотека его не принимает.

routes, err := cfg.OCRRoutes() // "ocr": {"scan": {"provider": "vision", "model": "page", "languages": ["ru", "en"]}}
router := &llm.Router{OCR: map[string]llm.Recognizer{"vision": ocr}, OCRTasks: routes}
route, ok, err := router.ResolveOCR("scan")
res, err := route.Recognize(ctx, llm.OCRInput{CallID: id, Image: jpeg, MIME: llm.MIMEJPEG})
  • Раздел ocr необязателен: без него ResolveOCR даёт false без ошибки. languages обязательны; плечо visionocr годится только задачам OCR, чатовое и речевое у задачи OCR — ошибка разбора.
  • yandex.NewOCR, как и чат и речь Яндекса, шлёт x-data-logging-enabled: false всегда: в кадрах чеки и документы людей, логирование у Яндекса выключено без настройки.
  • Кадр больше OCRCapabilities.MaxBytes (у page — 10 МБ) отбивается до сети классом never.
  • Расход — страницы: Pages в отчётах, тариф page за страницу. Страница идёт в расход по тому же правилу, что запись у речи. OCRDescriptor.Fingerprint — вид плеча, модель и языки, без имени плеча и тарифа.
  • Лимит Vision — запрос в секунду на облако, а не на процесс: 429 переживается паузой, темп задаёт очередь потребителя.
  • Пустой Text — успех: текста на кадре нет.
  • llmcheck ocr -config llm.json -dir кадры/ прогоняет каталог *.jpg и *.png: задержка, страницы и стоимость в протокол, текст — в *.txt рядом с кадром. Кадры с общей основой имени (scan.jpg и scan.png) отбивают прогон до первого вызова.

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

Промпты и схемы, разбор ответа, пороги качества, тарифы, журнал вызовов, квоты. pricing.Estimate и pricing.EstimateCall оценивают стоимость по отчётам попыток и тарифам потребителя — оценка, а не счёт: неизвестный расход или тариф дают unknown, а не ноль. EstimateCall ждёт тарифы одного маршрута с различными ValidFrom: одинаковые разрешаются порядком аргументов. 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"
	OutcomeTruncated   = "truncated"
	OutcomeFiltered    = "filtered"
	OutcomeRefused     = "refused"
	OutcomeCancelled   = "cancelled"
	OutcomeTimeout     = "timeout"
	OutcomeHTTP4xx     = "http_4xx"
	OutcomeHTTP5xx     = "http_5xx"
	OutcomeBadResponse = "bad_response"
	OutcomeBadRequest  = "bad_request"
	OutcomeNetwork     = "network"
	OutcomeConfig      = "config"
	OutcomeError       = "error"
)

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

View Source
const (
	MIMEJPEG = "image/jpeg"
	MIMEPNG  = "image/png"
)

MIME-типы кадра, которые принимает контракт OCR; PDF растеризует потребитель.

Variables

View Source
var (
	ErrTruncated = errors.New("ответ обрезан пределом длины")
	ErrFiltered  = errors.New("ответ остановлен фильтром")
	ErrRefused   = errors.New("модель отказалась отвечать")
)

ErrTruncated, ErrFiltered и ErrRefused — генерация кончилась пределом длины, фильтром или отказом модели: ответ оплачен, и повтор оплатил бы тот же исход ещё раз.

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
	RequestID      string
	Finish         Finish
	StartedAt      time.Time
	Duration       time.Duration
	ServerLatency  time.Duration
	Usage          Usage
	// AudioMillis — запись, отправленная плечу речи; у чата ноль.
	AudioMillis int64
	// Pages — страницы, отправленные плечу OCR; у чата и речи ноль.
	Pages   int
	Cleanup *CleanupWarning
}

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

type CallError

type CallError struct {
	Provider   string
	Model      string
	Phase      Phase
	Status     int // код HTTP; 0 — ответа не было (сеть, просрочка, отмена)
	Message    string
	RetryAfter time.Duration
	Finish     Finish
	StartedAt  time.Time
	Latency    time.Duration
	Class      RetryClass
	Report     CallReport
	Attempts   []AttemptReport
	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
	AudioMillis    int64
	Pages          int
}

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

type Capabilities added in v0.3.0

type Capabilities struct {
	Vision              bool
	JSON                bool
	Schema              bool
	Strict              bool
	SchemaName          bool // схема без имени отбивается
	MaxImagesPerMessage int
	MaxImagesPerRequest int
	Temperature         bool
	Reasoning           bool
	MaxOutputTokens     bool
}

Capabilities — что подтверждено у сочетания плеча, модели и режима API. Предел кадров ноль — предела нет; незаявленная возможность отбивается до вызова.

func (Capabilities) Check added in v0.3.0

func (c Capabilities) Check(r Request) error

Check отбивает запрос, требующий неподтверждённого профилем; ошибка — *RequestError.

type Chat

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

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

type CleanupWarning added in v0.3.0

type CleanupWarning struct {
	Files     []string
	Uncertain int
	Err       error
}

CleanupWarning — созданное попыткой у поставщика могло остаться неубранным: генерацию это не отменяет, и повтор ради уборки не оплачивается. Files — что осталось, Err — почему; Uncertain — загрузки без ответа: файл мог создаться, а id его неизвестен.

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 — все попытки со своими таймаутами плюс худшие паузы. Уборку плеча после срока попытки (gigachat.CleanupBudget) не включает; переполнение насыщается.

func (*Client) Chat

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

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

type ConfigError added in v0.4.0

type ConfigError struct {
	Message string
	Err     error
}

ConfigError — отказ до отправки запроса, который чинит оператор, а не повтор: пустой ключ, отозванная подпись. Временный сбой источника подписи (сеть до IAM) ConfigError не является.

func (*ConfigError) Error added in v0.4.0

func (e *ConfigError) Error() string

func (*ConfigError) Unwrap added in v0.4.0

func (e *ConfigError) Unwrap() error

type Descriptor added in v0.4.0

type Descriptor struct {
	Task      string
	Provider  string
	Kind      string
	Model     string
	Revision  string
	Output    Output
	Options   Options
	PricePlan string
}

Descriptor — всё, что маршрут фиксирует о вызове: источник версии вызова и полей журнала. Provider — имя плеча в конфиге, Kind — адаптер.

func (Descriptor) Fingerprint added in v0.4.0

func (d Descriptor) Fingerprint(parts ...string) string

Fingerprint — отпечаток маршрута вместе с частями потребителя (хеш промпта, схемы, подготовка входа). Имя плеча в конфиге, тариф и бюджет не входят: переименование и смена цены вызов не меняют.

type Effort added in v0.3.0

type Effort string

Effort — глубина рассуждений модели; пустое — умолчание модели.

const (
	EffortLow    Effort = "low"
	EffortMedium Effort = "medium"
	EffortHigh   Effort = "high"
)

Глубина рассуждений.

type Finish added in v0.3.0

type Finish struct {
	Raw  string
	Kind FinishKind
}

Finish — причина конца генерации: как её назвал поставщик и нормализованная.

type FinishKind added in v0.3.0

type FinishKind string

FinishKind — нормализованная причина конца генерации; пустая — поставщик не сообщил или сообщил неизвестную.

const (
	FinishStop          FinishKind = "stop"
	FinishLength        FinishKind = "length"
	FinishContentFilter FinishKind = "content_filter"
	FinishRefusal       FinishKind = "refusal"
	FinishToolCall      FinishKind = "tool_call"
)

Причины конца генерации.

type Image

type Image struct {
	MIME string
	Data []byte
}

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

type Input added in v0.4.0

type Input struct {
	CallID   string
	Messages []Message
	Mode     Mode
	Schema   json.RawMessage
}

Input — то, что задаёт вызывающий. Модель и опции задаёт маршрут; Mode — форма, которую ждёт разбор потребителя: пустое принимает маршрут, иное обязано с ним совпасть.

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 Needs added in v0.4.0

type Needs struct {
	ImagesPerMessage int
	ImagesPerRequest int
}

Needs — сколько кадров задача шлёт на сообщение и на запрос; нулевое — задача текстовая.

type OCRCapabilities added in v0.6.0

type OCRCapabilities struct {
	MaxBytes int
}

OCRCapabilities — что подтверждено у модели OCR; MaxBytes ноль — предела нет.

type OCRDescriptor added in v0.6.0

type OCRDescriptor struct {
	Task      string
	Provider  string
	Kind      string
	Model     string
	Languages []string
	PricePlan string
}

OCRDescriptor — что маршрут OCR фиксирует о вызове. Provider — имя плеча в конфиге, Kind — адаптер.

func (OCRDescriptor) Fingerprint added in v0.6.0

func (d OCRDescriptor) Fingerprint(parts ...string) string

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

type OCRInput added in v0.6.0

type OCRInput struct {
	CallID string
	Image  []byte
	MIME   string
}

OCRInput — кадр от вызывающего: JPEG или PNG целиком. Модель и языки задаёт маршрут.

type OCRRequest added in v0.6.0

type OCRRequest struct {
	Model     string
	Image     []byte
	MIME      string
	Languages []string
}

OCRRequest — один кадр на распознавание: JPEG или PNG целиком.

func (OCRRequest) Validate added in v0.6.0

func (r OCRRequest) Validate(maxBytes int) error

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

type OCRResult added in v0.6.0

type OCRResult struct {
	OCRText

	StartedAt time.Time
	Latency   time.Duration
	Attempts  []AttemptReport
	Report    CallReport
}

OCRResult — распознанный текст кадра с отчётом вызова; Model — из ответа, иначе запрошенная.

type OCRRoute added in v0.6.0

type OCRRoute struct {
	// contains filtered or unexported fields
}

OCRRoute — задача OCR, разрешённая в плечо; нулевое значение не вызывает ничего. Клиент в нём — только бюджет, паузы и наблюдатель: чатового плеча у него нет.

func (OCRRoute) Budget added in v0.6.0

func (r OCRRoute) Budget() time.Duration

Budget — Client.Budget маршрута: все попытки со сроками и худшими паузами.

func (OCRRoute) Descriptor added in v0.6.0

func (r OCRRoute) Descriptor() OCRDescriptor

Descriptor — копия дескриптора.

func (OCRRoute) Recognize added in v0.6.0

func (r OCRRoute) Recognize(ctx context.Context, in OCRInput) (OCRResult, error)

Recognize распознаёт кадр маршрутом с повторами тех же классов, что у Client.Chat. Кадр, который профиль не примет, отбивается до плеча отказом RetryNever.

type OCRTaskConfig added in v0.6.0

type OCRTaskConfig struct {
	Provider       string
	Model          string
	Languages      []string
	AttemptTimeout time.Duration
	Attempts       int
	PricePlan      string
}

OCRTaskConfig — привязка задачи OCR к плечу распознавания; нулевые срок и число попыток — умолчания клиента.

type OCRText added in v0.6.0

type OCRText struct {
	Text      string
	Model     string
	RequestID string
}

OCRText — распознанный текст кадра; пустой Text — успех, текста на кадре нет.

type Observer

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

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

type Options added in v0.3.0

type Options struct {
	Temperature     *float64
	Reasoning       Effort
	MaxOutputTokens int
}

Options — параметры генерации; нулевое значение поля — умолчание модели, не ноль.

type Output

type Output struct {
	Mode   Mode
	Schema json.RawMessage
	Name   string
	Strict bool
}

Output — требуемая форма ответа; Schema обязательна при ModeSchema. Name и Strict — только при схеме: имя схемы требует OpenAI-совместимый протокол, Strict — точное соблюдение.

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
	Capabilities(model string) (Capabilities, bool)
	Complete(ctx context.Context, req Request) (Result, error)
	// AttemptOverhead — время плеча после срока попытки (уборка файлов); Route.Budget прибавляет его
	// на попытку. Обязательный метод, а не опция: обёртка потребителя иначе прятала бы его молча.
	AttemptOverhead() time.Duration
}

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

type Recognizer added in v0.6.0

type Recognizer interface {
	Name() string
	OCRCapabilities(model string) (OCRCapabilities, bool)
	Recognize(ctx context.Context, req OCRRequest) (OCRText, error)
}

Recognizer — плечо распознавания текста на изображении. Чатового Provider не реализует.

type Request

type Request struct {
	CallID   string
	Task     string
	Model    string
	Messages []Message
	Output   Output
	Options  Options
}

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
	Finish        Finish
	RequestID     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
	Finish Finish
	Usage  Usage
	// RequestID — идентификатор запроса у поставщика, если он его вернул.
	RequestID string
	// ServerLatency — время на стороне поставщика, если он его сообщает; ноль — не сообщил.
	ServerLatency time.Duration
	// Cleanup — уборка удавшейся попытки не удалась; nil — убирать было нечего или убрано.
	Cleanup *CleanupWarning
	// StartedAt — начало вызова; Latency — сколько ждал вызывающий: все попытки и паузы.
	StartedAt time.Time
	Latency   time.Duration
	Report    CallReport
	Attempts  []AttemptReport
}

Result — ответ модели. Usage, Finish и 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 Route added in v0.4.0

type Route struct {
	// contains filtered or unexported fields
}

Route — задача, разрешённая в плечо; нулевое значение не вызывает ничего.

func (Route) Budget added in v0.4.0

func (r Route) Budget() time.Duration

Budget — Client.Budget маршрута плюс Provider.AttemptOverhead на каждую попытку.

func (Route) Chat added in v0.4.0

func (r Route) Chat(ctx context.Context, in Input) (Result, error)

Chat вызывает модель маршрутом. Форма или схема входа, расходящаяся с маршрутом, — отказ never до плеча: подмена формы конфигом иначе дошла бы до разбора потребителя молча.

func (Route) Descriptor added in v0.4.0

func (r Route) Descriptor() Descriptor

Descriptor — копия дескриптора: правка её маршрут не меняет.

type Router added in v0.4.0

type Router struct {
	Providers   map[string]Provider
	Tasks       map[string]TaskConfig
	Speech      map[string]Transcriber
	SpeechTasks map[string]SpeechTaskConfig
	OCR         map[string]Recognizer
	OCRTasks    map[string]OCRTaskConfig
	Observe     Observer
}

Router — задачи потребителя и собранные плечи: чат, речь и OCR, у каждого контракта свои. Собирается один раз на процесс: запасного плеча и перечитывания конфига нет намеренно — они делают неоднозначными расход, версию и причину отказа. Смена плеча — правка конфига и рестарт.

func (*Router) Resolve added in v0.4.0

func (r *Router) Resolve(task string) (Route, error)

Resolve разрешает задачу в неизменяемый маршрут; правка Router после него маршрут не меняет. Кадры задачи Resolve не знает — их сверяет Validate и каждый вызов.

func (*Router) ResolveOCR added in v0.6.0

func (r *Router) ResolveOCR(task string) (OCRRoute, bool, error)

ResolveOCR разрешает задачу OCR в неизменяемый маршрут; false без ошибки — задача не объявлена.

func (*Router) ResolveSpeech added in v0.5.0

func (r *Router) ResolveSpeech(task string) (SpeechRoute, bool, error)

ResolveSpeech разрешает задачу речи в неизменяемый маршрут; false без ошибки — задача не объявлена.

func (*Router) Validate added in v0.4.0

func (r *Router) Validate(needs map[string]Needs) error

Validate проверяет задачи потребителя до первого вызова: каждая из needs объявлена, лишних нет, плечо собрано, профиль модели подтверждён и принимает конфиг задачи вместе с её кадрами. nil — требований нет, задачи проверяются как текстовые; пустая карта — задачи не нужны вовсе. Задачи речи и OCR проверяются все: needs их не касается.

type SpeechCapabilities added in v0.5.0

type SpeechCapabilities struct {
	MaxAudio time.Duration
}

SpeechCapabilities — что подтверждено у модели распознавания; MaxAudio ноль — предела нет.

type SpeechDescriptor added in v0.5.0

type SpeechDescriptor struct {
	Task      string
	Provider  string
	Kind      string
	Model     string
	Language  string
	PricePlan string
}

SpeechDescriptor — что маршрут речи фиксирует о вызове. Provider — имя плеча в конфиге, Kind — адаптер.

func (SpeechDescriptor) Fingerprint added in v0.5.0

func (d SpeechDescriptor) Fingerprint(parts ...string) string

Fingerprint — отпечаток маршрута речи с частями потребителя; имя плеча, тариф и бюджет не входят.

type SpeechInput added in v0.5.0

type SpeechInput struct {
	CallID     string
	SampleRate int
	PCM        []byte
}

SpeechInput — запись от вызывающего: сырые сэмплы LPCM, 16 бит, моно. Модель и язык задаёт маршрут.

type SpeechRequest added in v0.5.0

type SpeechRequest struct {
	Model      string
	Language   string
	SampleRate int
	PCM        []byte
}

SpeechRequest — один вызов распознавания: сырые сэмплы LPCM, 16 бит, моно, little-endian.

func (SpeechRequest) Duration added in v0.5.0

func (r SpeechRequest) Duration() time.Duration

Duration — длительность записи по числу сэмплов; при негодной частоте ноль.

func (SpeechRequest) Validate added in v0.5.0

func (r SpeechRequest) Validate(maxAudio time.Duration) error

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

type SpeechResult added in v0.5.0

type SpeechResult struct {
	Transcript

	StartedAt time.Time
	Latency   time.Duration
	Attempts  []AttemptReport
	Report    CallReport
}

SpeechResult — распознанный текст с отчётом вызова; Model — из ответа, иначе запрошенная.

type SpeechRoute added in v0.5.0

type SpeechRoute struct {
	// contains filtered or unexported fields
}

SpeechRoute — задача речи, разрешённая в плечо; нулевое значение не вызывает ничего. Клиент в нём — только бюджет, паузы и наблюдатель: чатового плеча у него нет.

func (SpeechRoute) Budget added in v0.5.0

func (r SpeechRoute) Budget() time.Duration

Budget — Client.Budget маршрута: все попытки со сроками и худшими паузами.

func (SpeechRoute) Descriptor added in v0.5.0

func (r SpeechRoute) Descriptor() SpeechDescriptor

Descriptor — копия дескриптора.

func (SpeechRoute) Transcribe added in v0.5.0

func (r SpeechRoute) Transcribe(ctx context.Context, in SpeechInput) (SpeechResult, error)

Transcribe распознаёт запись маршрутом с повторами тех же классов, что у Client.Chat. Запись, которую профиль не примет, отбивается до плеча отказом RetryNever.

type SpeechTaskConfig added in v0.5.0

type SpeechTaskConfig struct {
	Provider       string
	Model          string
	Language       string
	AttemptTimeout time.Duration
	Attempts       int
	PricePlan      string
}

SpeechTaskConfig — привязка задачи речи к плечу распознавания; нулевые срок и число попыток — умолчания клиента.

type StatusError

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

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

func (*StatusError) Error

func (e *StatusError) Error() string

type TaskConfig added in v0.4.0

type TaskConfig struct {
	Provider       string
	Model          string
	Revision       string
	Output         Output
	Options        Options
	AttemptTimeout time.Duration
	Attempts       int
	PricePlan      string
}

TaskConfig — привязка задачи к плечу: модель, ревизия, форма ответа без схемы, опции и бюджет. Схема ответа — артефакт потребителя и едет входом; нулевые срок и число попыток — умолчания клиента.

type Transcriber added in v0.5.0

type Transcriber interface {
	Name() string
	SpeechCapabilities(model string) (SpeechCapabilities, bool)
	Transcribe(ctx context.Context, req SpeechRequest) (Transcript, error)
}

Transcriber — плечо распознавания речи. Чатового Provider не реализует: probe и Complete у него бессмысленны.

type Transcript added in v0.5.0

type Transcript struct {
	Text        string
	Model       string
	RequestID   string
	AudioMillis int64
}

Transcript — распознанный текст; пустой Text — успех, в записи не расслышали речи.

type Usage

type Usage struct {
	Raw           map[string]int
	BillableInput int
	CachedInput   int
	Reasoning     int
	Output        int
	Known         bool
}

Usage — расход токенов. Части не пересекаются: вход — BillableInput плюс CachedInput, выход — Output плюс Reasoning; Raw — счётчики поставщика как есть. Known ложно, когда хотя бы часть расхода не сообщена: счёт тогда — нижняя граница.

func (Usage) Add added in v0.2.0

func (u Usage) Add(part Usage) Usage

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

func (Usage) InputTokens

func (u Usage) InputTokens() int

InputTokens — весь вход, с кешем.

func (Usage) OutputTokens

func (u Usage) OutputTokens() int

OutputTokens — весь выход, с рассуждениями.

type WarnedError added in v0.3.0

type WarnedError struct {
	Err     error
	Cleanup *CleanupWarning
}

WarnedError — отказ попытки вместе с предупреждением уборки; цепочка ведёт к отказу, а не к уборке.

func (*WarnedError) Error added in v0.3.0

func (e *WarnedError) Error() string

func (*WarnedError) Unwrap added in v0.3.0

func (e *WarnedError) Unwrap() error

Directories

Path Synopsis
Package gigachat — плечо GigaChat: OAuth-токен с кешем на клиенте, корневой сертификат Минцифры в пуле транспорта, кадры через `/files` и `attachments`.
Package gigachat — плечо GigaChat: OAuth-токен с кешем на клиенте, корневой сертификат Минцифры в пуле транспорта, кадры через `/files` и `attachments`.
internal
httpjson
Package httpjson — общее у плеч поверх JSON по HTTP: запрос, ограниченное тело, отказ по статусу.
Package httpjson — общее у плеч поверх JSON по HTTP: запрос, ограниченное тело, отказ по статусу.
sberauth
Package sberauth — OAuth Сбера, общий у GigaChat и SaluteSpeech: токен на экземпляр плеча и его пару (ключ, scope), корни Минцифры в транспорте, один повтор на 401.
Package sberauth — OAuth Сбера, общий у GigaChat и SaluteSpeech: токен на экземпляр плеча и его пару (ключ, scope), корни Минцифры в транспорте, один повтор на 401.
trust
Package trust — HTTP-клиент с корнями TLS целиком, без системных, на клоне транспорта основы.
Package trust — HTTP-клиент с корнями TLS целиком, без системных, на клоне транспорта основы.
Package llmbuild собирает плечи и llm.Router из llmconfig.Config: вид плеча — конструктор адаптера.
Package llmbuild собирает плечи и llm.Router из llmconfig.Config: вид плеча — конструктор адаптера.
Package llmconfig — маршруты задач в JSON: плечи, задачи, тарифы.
Package llmconfig — маршруты задач в JSON: плечи, задачи, тарифы.
Package ollama — плечо Ollama поверх нативного `/api/chat`: кадры base64 в сообщении, `format` подсказкой или схемой, серверные длительности в ответе.
Package ollama — плечо Ollama поверх нативного `/api/chat`: кадры base64 в сообщении, `format` подсказкой или схемой, серверные длительности в ответе.
Package pricing оценивает стоимость вызова по расходу попыток и тарифу потребителя.
Package pricing оценивает стоимость вызова по расходу попыток и тарифу потребителя.
Package salutespeech — синхронное распознавание SaluteSpeech: OAuth Сбера со своим ключом и scope, корни Минцифры — как у GigaChat.
Package salutespeech — синхронное распознавание SaluteSpeech: OAuth Сбера со своим ключом и scope, корни Минцифры — как у GigaChat.
Package yandex — плечо Yandex AI Studio через OpenAI-совместимый `chat/completions`: ключ API или IAM-токен, каталог заголовком `OpenAI-Project`, кадры data-URI в частях сообщения.
Package yandex — плечо Yandex AI Studio через OpenAI-совместимый `chat/completions`: ключ API или IAM-токен, каталог заголовком `OpenAI-Project`, кадры data-URI в частях сообщения.

Jump to

Keyboard shortcuts

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