einoproviders

package module
v0.0.0-...-7924835 Latest Latest
Warning

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

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

README

eino-providers

eino-providers is a shared Go module for constructing CloudWeGo Eino chat model providers across Claude, OpenAI, OpenAI-Codex, OpenCode Go, Gemini, and Ollama backends.

Native agentic models

The module pins Eino v0.9.19 and exposes native model.AgenticModel constructors from concrete provider packages. Import the specific provider; there is intentionally no root-level agentic factory.

Provider Constructor Native protocol
Claude claude.NewAgenticModel Messages
OpenAI openai.NewAgenticModel Responses
Gemini gemini.NewAgenticModel generateContent
Ollama ollama.NewAgenticModel /api/chat
OpenAI-Codex openaicodex.NewAgenticModel / NewAgenticModelWithHTTPClient Responses
OpenCode Go opencodego.NewAgenticModel explicit Responses, Messages, or Chat Completions

Agentic tools are request-time options, for example model.WithTools. A provider returns ErrUnsupportedCapability before dispatch when its selected native protocol cannot represent an input or option. AgenticLimits bounds request and response processing; zero values select documented defaults.

OpenAI exposes SplitAgenticContinuation and RestoreAgenticContinuation to separate a safe display message from opaque provider continuation state before persistence.

package main

import (
	"context"
	"fmt"
	"log"

	einoproviders "github.com/mattsp1290/eino-providers"
	_ "github.com/mattsp1290/eino-providers/claude"
)

func main() {
	ctx := context.Background()

	provider, err := einoproviders.NewProvider(ctx, "claude", "claude-sonnet-4-5", einoproviders.Options{
		APIKey: "your-api-key",
	})
	if err != nil {
		log.Fatal(err)
	}

	text, usage, err := provider.Advise(ctx, "Be concise.", "Summarize Eino.", 512)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s\n%+v\n", text, usage)
}

The first consumers are advisor and local-symphony.

Backend Notes

Backends are opt-in packages. Import the package for each provider name before calling NewProvider; the root package does not import SDKs or Codex OAuth.

Provider Import Provider name Notes
Claude github.com/mattsp1290/eino-providers/claude claude Uses Options.APIKey; forwards Options.BaseURL when non-nil; maps Advise maxTokens to Claude MaxTokens.
OpenAI github.com/mattsp1290/eino-providers/openai openai Uses Options.APIKey; forwards Options.BaseURL when non-nil; maps Advise maxTokens to OpenAI MaxCompletionTokens.
OpenAI-Codex github.com/mattsp1290/eino-providers/openaicodex openai-codex Uses codex-auth-go for OAuth transport; ignores Options.APIKey; rejects non-nil Options.BaseURL and Options.MaxTokens; does not set Eino max-token fields.
OpenCode Go github.com/mattsp1290/eino-providers/opencodego opencode-go Requires an explicit model, native protocol, caller user agent, API key, and request session; also exposes a direct tool-calling chat model.
Gemini github.com/mattsp1290/eino-providers/gemini gemini Uses Options.APIKey unless Options.GenaiClient is supplied; caches the genai.Client at provider construction; maps Advise maxTokens to Gemini MaxTokens.

The Ollama package exposes ollama.NewChatModel rather than a root Provider registration. It validates and pings BaseURL with GET /api/tags before constructing the Eino Ollama chat model. Use KeepAlive for Ollama model residency, including "-1" for indefinite residency.

OpenAI-Codex's single-shot Provider.Advise path sets a non-secret placeholder API key only to satisfy the Eino OpenAI adapter constructor. The Codex HTTP transport owns endpoint rewriting, Bearer-token injection, and token refresh. Plan and quota API responses are classified as ErrProviderAuth and preserve the corresponding codex-auth-go sentinels for errors.Is.

The openaicodex package also exposes openaicodex.NewChatModel / NewChatModelWithHTTPClient (returning model.ToolCallingChatModel): a native Responses-API streaming, tool-calling chat model for agent loops. It speaks the Codex Responses wire protocol directly (not Chat Completions), supports Generate, Stream, and WithTools (immutable, concurrency-safe), threads reasoning across turns for multi-turn tool calling, and preserves the same auth/plan/quota error sentinels. See openaicodex/examples/toolcall for a verified 2-turn tool-calling example.

OpenCode Go

Import github.com/mattsp1290/eino-providers/opencodego to register the opencode-go provider or to call opencodego.NewChatModel directly. Model and protocol selection belong to the caller. The package does not discover models, infer capabilities, or fall back between the native protocols chat-completions, messages, and responses.

The registered provider uses the root options surface:

import (
    "context"

    einoproviders "github.com/mattsp1290/eino-providers"
    "github.com/mattsp1290/eino-providers/opencodego"
)

provider, err := einoproviders.NewProvider(ctx, "opencode-go", modelID,
    einoproviders.Options{
        Protocol:  string(opencodego.ProtocolResponses),
        UserAgent: "my-coding-agent/1.0",
        SessionID: conversationID,
    })

A nonempty Options.APIKey wins; an empty value reads OPENCODE_GO_API_KEY. The backend does not inspect login files or key stores. UserAgent must identify the host coding agent. Every request also needs a valid conversation ID. SessionID is the configured fallback, while a value attached to the operation context takes precedence:

import opencodeauth "github.com/mattsp1290/opencode-auth-go"

requestCtx := opencodeauth.WithSessionID(ctx, conversationID)
message, err := chatModel.Generate(requestCtx, input)

The library does not create, persist, or print session IDs. An explicitly empty context override is invalid and does not fall back to the configured value.

BaseURL is the API root. The backend appends /chat/completions, /messages, or /responses; do not pass a complete inference endpoint. Nil uses the authentication library default. HTTPClient is a trusted, unauthenticated base client: the authentication library copies it, preserves settings such as its timeout and transport, and decorates the copy with credentials, caller identity, session headers, and redirect protection. Its transport can therefore observe injected credentials, and its timeout also bounds streaming.

For a reusable model.ToolCallingChatModel, construct the model directly:

cap := 512
chatModel, err := opencodego.NewChatModel(ctx, opencodego.ChatModelConfig{
    Model:      modelID,
    Protocol:   opencodego.ProtocolMessages,
    UserAgent:  "my-coding-agent/1.0",
    SessionID:  conversationID,
    MaxTokens:  &cap,
})

A direct Messages model requires a positive configured MaxTokens. The registered provider may omit it because each positive Advise cap overrides the configuration. Chat Completions and Responses permit a nil cap.

Across the three protocols, the supported surface is text generation, streaming, function calls and tool results, and usage metadata. Responses also replays encrypted reasoning items across a stateless tool loop. Multimodal inputs, server-hosted built-in tools, previous_response_id state, unsupported roles, and unsupported invocation options are outside this package's contract. See opencodego/examples/toolcall for a bounded two-turn streaming function-tool exchange.

Messages uses the pinned Anthropic SDK retry policy: two retries, for at most three replayable attempts, on retryable statuses such as 408, 409, 429, and 5xx. X-Should-Retry directives and Retry-After are honored; permanent 4xx responses normally receive one attempt. The authentication transport does not add retries, and streams are never retried after they start. Cancellation stops a subsequent attempt, although the pinned SDK can remain in its current time.Sleep backoff before returning.

Errors retain stable classification and safe HTTP metadata without exposing response bodies, credentials, sessions, or request URLs in their strings:

if errors.Is(err, einoproviders.ErrProviderAuth) ||
    errors.Is(err, einoproviders.ErrProviderAPI) ||
    errors.Is(err, einoproviders.ErrProviderTimeout) {
    var httpErr *opencodeauth.HTTPError
    if errors.As(err, &httpErr) {
        // Inspect StatusCode, Kind, RetryAfter, and HasRetryAfter.
    }
}

The optional live gate has not been run as part of ordinary validation. It requires explicit credentials and model choices for all three protocols:

OPENCODE_GO_LIVE_TEST=1 go test ./opencodego -run '^TestLiveOpenCodeGo$' -count=1 -timeout=6m

Roll out the backend by importing opencodego and selecting opencode-go explicitly in the host. Roll back by reverting that host selection and the provider dependency; no compatibility flag or migration is required because there are no existing OpenCode Go consumers.

API Stability

The v0.1.x public API is intentionally small:

  • Provider, Usage, Options, NewProvider, RegisterProvider, error sentinels, Classify, and wrapper helpers in the root package.
  • Backend constructors documented by their package comments and exported functions.
  • ollama.NewChatModel and ollama.Config.
  • openaicodex.NewChatModel, openaicodex.NewChatModelWithHTTPClient, and openaicodex.ChatModelConfig.
  • opencodego.NewChatModel, opencodego.ChatModelConfig, and the opencodego.Protocol* constants.

Use keyed struct literals for Options and backend config types:

einoproviders.Options{
	APIKey: "your-api-key",
}

Unkeyed Options literals are not supported. New optional fields may be added in minor releases when they preserve zero-value behavior for existing keyed literals.

RegisterProvider exists for backend package self-registration and advanced tests. Treat direct third-party registration as experimental until the registry contract is explicitly stabilized.

The current Provider.Advise API remains the v0.1.x compatibility surface. Backend-specific reusable chat-model constructors are available where listed above; a uniform root-level factory remains a possible later API.

Documentation

Overview

Package einoproviders provides shared CloudWeGo Eino provider construction for agentic applications, including the opt-in OpenCode Go backend.

The root package defines the single-shot Provider contract, common Options, usage extraction, error classification, and a registry-backed NewProvider factory.

Backend packages register themselves from init. Import only the backends a program needs, usually for side effects:

import (
	einoproviders "github.com/mattsp1290/eino-providers"
	_ "github.com/mattsp1290/eino-providers/claude"
	_ "github.com/mattsp1290/eino-providers/opencodego"
)

The root package intentionally does not import backend subpackages. This keeps backend SDKs, Codex OAuth, and OpenCode Go authentication dependencies opt-in.

RegisterProvider is public so backend packages can self-register and tests can install local constructors. Direct third-party registration should be treated as experimental until the registry API is stabilized in a later minor release.

Index

Constants

View Source
const (
	DefaultAgenticMaxRequestBytes        int64 = 16 << 20
	DefaultAgenticMaxEventBytes          int64 = 2 << 20
	DefaultAgenticMaxResponseBytes       int64 = 32 << 20
	DefaultAgenticMaxInlineMediaBytes    int64 = 8 << 20
	DefaultAgenticMaxContentBlocks             = 1024
	DefaultAgenticMaxErrorBodyBytes      int64 = 1 << 20
	DefaultAgenticMaxFixtureCaptureBytes int64 = 32 << 20

	HardAgenticMaxRequestBytes        int64 = 64 << 20
	HardAgenticMaxEventBytes          int64 = 8 << 20
	HardAgenticMaxResponseBytes       int64 = 128 << 20
	HardAgenticMaxInlineMediaBytes    int64 = 32 << 20
	HardAgenticMaxContentBlocks             = 4096
	HardAgenticMaxErrorBodyBytes      int64 = 4 << 20
	HardAgenticMaxFixtureCaptureBytes int64 = 64 << 20
)

Variables

View Source
var (
	ErrProviderInit       = errors.New("provider init error")
	ErrProviderTimeout    = errors.New("provider timeout")
	ErrProviderAPI        = errors.New("provider API error")
	ErrProviderAuth       = errors.New("provider auth error")
	ErrUnknownProvider    = errors.New("unknown provider")
	ErrBackendUnreachable = errors.New("backend unreachable")
	// ErrUnsupportedCapability reports an agentic input or option that the
	// selected native provider protocol cannot represent. It is always raised
	// before a request is dispatched.
	ErrUnsupportedCapability = errors.New("unsupported agentic capability")
	// ErrResourceLimit reports an agentic request or response that exceeded a
	// configured boundary.
	ErrResourceLimit = errors.New("agentic resource limit exceeded")
)

Provider-error sentinels used for errors.Is classification.

Functions

func NewAgenticLimitedHTTPClient

func NewAgenticLimitedHTTPClient(source *http.Client, limits AgenticLimits) (*http.Client, error)

NewAgenticLimitedHTTPClient returns a copy of source whose transport bounds native agentic request, successful response, and error response bodies. It never mutates the caller-owned client or transport. Protocol decoders remain responsible for enforcing framing-specific limits such as a single SSE event.

func NewBoundedAgenticModel

func NewBoundedAgenticModel(delegate model.AgenticModel, limits AgenticLimits) (model.AgenticModel, error)

NewBoundedAgenticModel adds pre-dispatch and returned-message content-block validation to a native model. It complements the HTTP limiter, which owns byte boundaries at the transport layer.

func NewIdentifiedAgenticModel

func NewIdentifiedAgenticModel(delegate model.AgenticModel, identity AgenticResponseIdentity) model.AgenticModel

NewIdentifiedAgenticModel preserves a delegate's native extension while attaching the stable identity contract required by this module.

func RegisterProvider

func RegisterProvider(name string, ctor ProviderCtor)

RegisterProvider registers a provider constructor by name.

Backend packages should call RegisterProvider from init. Duplicate names are programming errors and panic, matching database/sql driver registration.

func RestoreAgenticContinuationForProvider

func RestoreAgenticContinuationForProvider(public *schema.AgenticMessage, state AgenticContinuationState, provider, protocol string) (*schema.AgenticMessage, error)

RestoreAgenticContinuationForProvider recombines a public clone and opaque provider state without mutating either input.

func ValidateAgenticContentBlocks

func ValidateAgenticContentBlocks(messages []*schema.AgenticMessage, limits AgenticLimits) error

ValidateAgenticContentBlocks rejects a message collection whose total block count exceeds the configured boundary without inspecting private block data.

func WrapAuthError

func WrapAuthError(err error) error

WrapAuthError tags err as a provider authentication failure.

The returned error matches ErrProviderAuth with errors.Is and preserves err.Error() as its own Error string. Nil input returns nil.

func WrapInitError

func WrapInitError(err error) error

WrapInitError tags err as a provider initialization failure.

The returned error matches ErrProviderInit with errors.Is and preserves err.Error() as its own Error string. Nil input returns nil.

Types

type AgenticContinuationState

type AgenticContinuationState struct {
	Provider      string `json:"provider"`
	Protocol      string `json:"protocol"`
	Version       uint32 `json:"version"`
	CorrelationID string `json:"correlation_id,omitempty"`
	Opaque        []byte `json:"opaque,omitempty"`
}

AgenticContinuationState is an opaque, versioned provider payload returned by a provider's SplitAgenticContinuation helper. Applications may persist it alongside the sanitized public message, but must not inspect or display it. The type intentionally has no String method to avoid accidental disclosure.

func SplitAgenticContinuationForProvider

func SplitAgenticContinuationForProvider(msg *schema.AgenticMessage, provider, protocol string) (*schema.AgenticMessage, AgenticContinuationState, error)

SplitAgenticContinuationForProvider creates an immutable display-safe projection and a provider-tagged opaque continuation envelope. It removes extras and reasoning signatures from the public clone only; callers must retain the returned state to resume provider-native execution.

type AgenticLimits

type AgenticLimits struct {
	MaxRequestBytes        int64
	MaxEventBytes          int64
	MaxResponseBytes       int64
	MaxInlineMediaBytes    int64
	MaxContentBlocks       int
	MaxErrorBodyBytes      int64
	MaxFixtureCaptureBytes int64
}

AgenticLimits bounds request parsing and fixture capture. A zero field uses the documented default; negative values and values above hard caps are rejected by Validate.

func (AgenticLimits) Validate

func (l AgenticLimits) Validate() (AgenticLimits, error)

Validate resolves defaults and rejects invalid or unsafe hard-limit values.

func (AgenticLimits) WithDefaults

func (l AgenticLimits) WithDefaults() AgenticLimits

WithDefaults returns limits with every zero field replaced by its default.

type AgenticResponseIdentity

type AgenticResponseIdentity struct {
	Provider       string `json:"provider"`
	Protocol       string `json:"protocol"`
	RequestedModel string `json:"requested_model,omitempty"`
	ReturnedModel  string `json:"returned_model,omitempty"`
	CorrelationID  string `json:"correlation_id,omitempty"`
}

AgenticResponseIdentity records the provider identity observed for a native agentic response. RequestedModel and ReturnedModel are deliberately kept distinct because compatible endpoints may route to a different model.

type AgenticResponseMetadata

type AgenticResponseMetadata struct {
	Identity AgenticResponseIdentity `json:"identity"`
	Native   any                     `json:"native,omitempty"`
}

AgenticResponseMetadata preserves a provider adapter's native response extension while adding the stable identity fields exposed by this module. It is used where a protocol adapter already returned extension data that callers may rely on; replacing that data would lose correlation details.

type Capability

type Capability string

Capability identifies a native protocol feature for capability errors. It intentionally contains no caller content, credentials, or opaque state.

type ErrorClass

type ErrorClass int

ErrorClass is the switch-friendly classification returned by Classify.

const (
	ErrorClassUnknown ErrorClass = iota
	ErrorClassProviderInit
	ErrorClassProviderTimeout
	ErrorClassProviderAPI
	ErrorClassProviderAuth
	ErrorClassUnknownProvider
	ErrorClassBackendUnreachable
	ErrorClassUnsupportedCapability
	ErrorClassResourceLimit
)

func Classify

func Classify(err error) ErrorClass

Classify returns the first matching provider error class for err.

type Options

type Options struct {
	// APIKey configures API-key authenticated backends such as Claude, OpenAI,
	// Gemini, and OpenCode Go. It is ignored by Ollama and OpenAI-Codex.
	APIKey string

	// Protocol selects the OpenCode Go native inference protocol. It is ignored
	// by every other backend. Use the typed constants from the opencodego
	// package when setting this string field.
	Protocol string

	// UserAgent identifies the host coding agent to OpenCode Go. It is required
	// by that backend and ignored by every other backend.
	UserAgent string

	// SessionID supplies the configured fallback OpenCode Go conversation
	// identifier. Each request needs a valid configured or context session; a
	// session attached to an operation context takes precedence. It is ignored by
	// every other backend.
	SessionID string

	// BaseURL overrides the backend endpoint. Nil means use the backend
	// default. OpenCode Go interprets it as the API root and appends the selected
	// native inference route. OpenAI-Codex rejects non-nil BaseURL because its
	// transport owns endpoint rewriting.
	BaseURL *string

	// MaxTokens requests an output-token cap where the backend supports one.
	// OpenCode Go's direct Messages chat-model constructor requires a positive
	// cap. Its registered Provider may leave this nil because Advise supplies
	// the positive cap per invocation. Chat Completions and Responses allow nil.
	// OpenAI-Codex ignores this because the Codex endpoint manages output
	// length server-side.
	MaxTokens *int

	// KeepAlive configures Ollama model residency. Other backends ignore it.
	KeepAlive *time.Duration

	// HTTPClient supplies transport customization. OpenCode Go treats it as a
	// trusted, unauthenticated base client that opencode-auth-go copies and decorates.
	// OpenAI-Codex requires an authenticated client from codex-auth-go; other
	// backends may ignore it.
	HTTPClient *http.Client

	// GenaiClient reuses an existing Gemini client and skips cold-start client
	// construction when non-nil.
	GenaiClient *genai.Client
}

Options configures provider construction.

Use field names in struct literals. Backends ignore fields that do not apply to them and validate fields that are forbidden for their endpoint semantics.

type Provider

type Provider interface {
	Advise(ctx context.Context, system, user string, maxTokens int) (text string, usage Usage, err error)
}

Provider is the contract every single-shot backend must satisfy.

Advise sends a single-turn request consisting of a system message and a user message, then returns the model's text reply. Implementations must honour ctx cancellation and deadlines, clamp maxTokens at the backend's Eino config layer, report unavailable usage as Usage{Available: false}, and wrap upstream errors with a short backend prefix.

func NewProvider

func NewProvider(ctx context.Context, name, model string, opts Options) (Provider, error)

NewProvider constructs a registered single-shot provider by name.

Backend packages register themselves from init. Consumers must import the backend packages they want, usually for side effects. If no backend registered name, NewProvider returns an error matching ErrUnknownProvider.

type ProviderCtor

type ProviderCtor func(ctx context.Context, model string, opts Options) (Provider, error)

ProviderCtor constructs a single-shot Provider for a registered backend.

type ResourceLimitError

type ResourceLimitError struct {
	Resource string
	Limit    int64
	Actual   int64
}

ResourceLimitError identifies a bounded agentic resource without retaining the content that exceeded it.

func (*ResourceLimitError) Error

func (e *ResourceLimitError) Error() string

func (*ResourceLimitError) Is

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

type UnsupportedCapabilityError

type UnsupportedCapabilityError struct {
	Provider   string
	Protocol   string
	Capability Capability
}

UnsupportedCapabilityError is returned before transport dispatch when a concrete agentic provider cannot faithfully encode a requested feature.

func (*UnsupportedCapabilityError) Error

func (*UnsupportedCapabilityError) Is

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

type Usage

type Usage struct {
	InputTokens  int
	OutputTokens int
	Available    bool
}

Usage carries token accounting from a single provider call.

Available is false when the upstream provider did not return usage data. Consumers must not interpret InputTokens or OutputTokens unless Available is true.

func ExtractUsage

func ExtractUsage(msg *schema.Message) Usage

ExtractUsage converts Eino message usage data into a Usage struct.

Directories

Path Synopsis
Package claude provides Claude provider and native Messages AgenticModel constructors.
Package claude provides Claude provider and native Messages AgenticModel constructors.
Package gemini provides Gemini provider and native generateContent AgenticModel constructors.
Package gemini provides Gemini provider and native generateContent AgenticModel constructors.
Package ollama provides native Ollama chat and AgenticModel constructors.
Package ollama provides native Ollama chat and AgenticModel constructors.
Package openai provides OpenAI provider and native Responses AgenticModel constructors.
Package openai provides OpenAI provider and native Responses AgenticModel constructors.
Package openaicodex provides OAuth-authenticated native Codex Responses models.
Package openaicodex provides OAuth-authenticated native Codex Responses models.
examples/toolcall command
Command toolcall demonstrates a 2-turn tool-calling exchange against the OpenAI-Codex subscription backend using only the public eino-providers surface.
Command toolcall demonstrates a 2-turn tool-calling exchange against the OpenAI-Codex subscription backend using only the public eino-providers surface.
Package opencodego integrates the OpenCode Go inference service with Eino.
Package opencodego integrates the OpenCode Go inference service with Eino.
examples/toolcall command
Command toolcall demonstrates a bounded, two-turn OpenCode Go function-tool exchange.
Command toolcall demonstrates a bounded, two-turn OpenCode Go function-tool exchange.

Jump to

Keyboard shortcuts

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