solvela

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Apr 28, 2026 License: MIT Imports: 17 Imported by: 0

README

solvela-go

Go SDK for Solvela — a Solana-native AI agent payment gateway.

AI agents pay for LLM API calls with USDC-SPL on Solana via the x402 protocol. No API keys, no accounts, just wallets.

Install

go get github.com/solvela-ai/solvela-go

Quick Start

package main

import (
	"context"
	"fmt"
	"log"

	solvela "github.com/solvela-ai/solvela-go"
)

func main() {
	wallet, _, err := solvela.CreateWallet()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Wallet:", wallet.Address())

	client := solvela.NewClient(wallet, nil,
		solvela.WithGatewayURL("https://api.solvela.ai"),
		solvela.WithCache(true),
		solvela.WithSessions(true),
	)

	resp, err := client.Chat(context.Background(), &solvela.ChatRequest{
		Model: "gpt-4o-mini",
		Messages: []solvela.ChatMessage{
			{Role: solvela.RoleUser, Content: "Hello!"},
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Choices[0].Message.Content)
}

Status

The core SDK (transport, caching, sessions, quality checking, streaming, balance monitoring) is fully implemented and tested.

KeypairSigner is not yet implemented. The bundled KeypairSigner type returns an error when SignPayment is called — it is a placeholder, not a working signer. To make payments you have two options:

  1. Use a different SDK — the Python SDK and TypeScript SDK include working KeypairSigner implementations backed by their respective Solana libraries.
  2. Implement a custom Signer — the Signer interface is pluggable. Provide your own implementation using crypto/ed25519 (already in the Go standard library) and a Solana JSON-RPC client of your choice.
type MySigner struct{ wallet *solvela.Wallet }

func (s *MySigner) SignPayment(ctx context.Context, amount uint64, recipient string, resource solvela.Resource, accepted solvela.PaymentAccept) (*solvela.PaymentPayload, error) {
    // build and sign a USDC-SPL transfer transaction, return PaymentPayload
}

client := solvela.NewClient(wallet, &MySigner{wallet: wallet}, ...)

Features

  • Automatic x402 payment flow (402 detection, signing, retry)
  • Response caching with LRU eviction and dedup window
  • Session tracking with three-strike model escalation
  • Quality checking with automatic retry on degraded responses
  • SSE streaming support
  • Balance monitoring with low-balance callbacks
  • Pluggable Signer interface for custom payment signing

Configuration

client := solvela.NewClient(wallet, signer,
	solvela.WithGatewayURL("https://api.solvela.ai"),
	solvela.WithTimeout(60 * time.Second),
	solvela.WithCache(true),
	solvela.WithSessions(true),
	solvela.WithQualityCheck(true),
	solvela.WithMaxQualityRetries(2),
	solvela.WithExpectedRecipient("expected-wallet-address"),
	solvela.WithMaxPaymentAmount(100000), // atomic USDC units
	solvela.WithFreeFallbackModel("gpt-4o-mini"),
)

Testing

go test ./... -v -count=1

# Live tests (requires running gateway)
go test ./... -v -tags=live

License

MIT

Documentation

Index

Constants

View Source
const (
	X402Version        = 2
	USDCMint           = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
	SolanaNetwork      = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
	MaxTimeoutSeconds  = 300
	PlatformFeePercent = 5
)

Variables

This section is empty.

Functions

func CacheKey

func CacheKey(model string, messages []ChatMessage) uint64

CacheKey computes a deterministic key from model and messages.

func DeriveSessionID

func DeriveSessionID(messages []ChatMessage) string

DeriveSessionID generates a deterministic session ID from the first message.

Types

type AmountExceedsMaxError

type AmountExceedsMaxError struct {
	Amount, MaxAmount uint64
}

AmountExceedsMaxError indicates the payment amount exceeds the configured maximum.

func (*AmountExceedsMaxError) Error

func (e *AmountExceedsMaxError) Error() string

type BalanceMonitor

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

BalanceMonitor periodically checks wallet balance and fires callbacks on low balance.

func NewBalanceMonitor

func NewBalanceMonitor(
	fetchBalance func() (float64, error),
	pollInterval time.Duration,
	lowBalanceThreshold *float64,
	onLowBalance func(float64),
) *BalanceMonitor

NewBalanceMonitor creates a new balance monitor.

func (*BalanceMonitor) LastKnownBalance

func (m *BalanceMonitor) LastKnownBalance() *float64

LastKnownBalance returns the most recently fetched balance, or nil if none.

func (*BalanceMonitor) Start

func (m *BalanceMonitor) Start()

Start begins polling in a background goroutine.

func (*BalanceMonitor) Stop

func (m *BalanceMonitor) Stop()

Stop halts the polling loop. Safe to call multiple times.

type ChatChoice

type ChatChoice struct {
	Index        int         `json:"index"`
	Message      ChatMessage `json:"message"`
	FinishReason *string     `json:"finish_reason,omitempty"`
}

ChatChoice represents a single completion choice.

type ChatChunk

type ChatChunk struct {
	ID      string            `json:"id"`
	Object  string            `json:"object"`
	Created int64             `json:"created"`
	Model   string            `json:"model"`
	Choices []ChatChunkChoice `json:"choices"`
}

ChatChunk represents a streaming chat completion chunk.

type ChatChunkChoice

type ChatChunkChoice struct {
	Index        int       `json:"index"`
	Delta        ChatDelta `json:"delta"`
	FinishReason *string   `json:"finish_reason,omitempty"`
}

ChatChunkChoice represents a single streaming choice.

type ChatChunkOrError

type ChatChunkOrError struct {
	Chunk *ChatChunk
	Err   error
}

ChatChunkOrError holds either a streaming chunk or an error.

type ChatDelta

type ChatDelta struct {
	Role      *Role           `json:"role,omitempty"`
	Content   *string         `json:"content,omitempty"`
	ToolCalls []ToolCallDelta `json:"tool_calls,omitempty"`
}

ChatDelta represents incremental content in a streaming chunk.

type ChatMessage

type ChatMessage struct {
	Role       Role       `json:"role"`
	Content    string     `json:"content"`
	Name       *string    `json:"name,omitempty"`
	ToolCalls  []ToolCall `json:"tool_calls,omitempty"`
	ToolCallID *string    `json:"tool_call_id,omitempty"`
}

ChatMessage represents a single message in a chat conversation.

type ChatRequest

type ChatRequest struct {
	Model       string           `json:"model"`
	Messages    []ChatMessage    `json:"messages"`
	MaxTokens   *int             `json:"max_tokens,omitempty"`
	Temperature *float64         `json:"temperature,omitempty"`
	TopP        *float64         `json:"top_p,omitempty"`
	Stream      bool             `json:"stream"`
	Tools       []ToolDefinition `json:"tools,omitempty"`
	ToolChoice  interface{}      `json:"tool_choice,omitempty"`
}

ChatRequest represents a chat completion request.

type ChatResponse

type ChatResponse struct {
	ID      string       `json:"id"`
	Object  string       `json:"object"`
	Created int64        `json:"created"`
	Model   string       `json:"model"`
	Choices []ChatChoice `json:"choices"`
	Usage   *Usage       `json:"usage,omitempty"`
}

ChatResponse represents a chat completion response.

type ClientConfig

type ClientConfig struct {
	GatewayURL         string
	RPCURL             string
	PreferEscrow       bool
	Timeout            time.Duration
	ExpectedRecipient  string
	MaxPaymentAmount   *uint64
	EnableCache        bool
	EnableSessions     bool
	SessionTTL         time.Duration
	EnableQualityCheck bool
	MaxQualityRetries  int
	FreeFallbackModel  string
}

ClientConfig holds all configuration for a Solvela client.

func DefaultConfig

func DefaultConfig() ClientConfig

DefaultConfig returns a ClientConfig with sensible defaults.

type ClientError

type ClientError struct {
	Message string
}

ClientError represents a general client error.

func (*ClientError) Error

func (e *ClientError) Error() string

type CostBreakdown

type CostBreakdown struct {
	ProviderCost string `json:"provider_cost"`
	PlatformFee  string `json:"platform_fee"`
	Total        string `json:"total"`
	Currency     string `json:"currency"`
	FeePercent   int    `json:"fee_percent"`
}

CostBreakdown provides the cost details for a request.

type DegradedReason

type DegradedReason string

DegradedReason describes why a response was considered degraded.

const (
	DegradedEmptyContent     DegradedReason = "empty_content"
	DegradedKnownErrorPhrase DegradedReason = "known_error_phrase"
	DegradedRepetitiveLoop   DegradedReason = "repetitive_loop"
	DegradedTruncatedMidWord DegradedReason = "truncated_mid_word"
)

func CheckDegraded

func CheckDegraded(content string) DegradedReason

CheckDegraded checks if response content is degraded. Returns empty string if OK, or a DegradedReason.

type EscrowPayload

type EscrowPayload struct {
	DepositTx   string `json:"deposit_tx"`
	ServiceID   string `json:"service_id"`
	AgentPubkey string `json:"agent_pubkey"`
}

EscrowPayload is the payload for escrow-based payments.

type FunctionCall

type FunctionCall struct {
	Name      string `json:"name"`
	Arguments string `json:"arguments"`
}

FunctionCall represents the function details in a tool call.

type FunctionCallDelta

type FunctionCallDelta struct {
	Name      *string `json:"name,omitempty"`
	Arguments *string `json:"arguments,omitempty"`
}

FunctionCallDelta represents incremental function call data.

type FunctionDefinitionInner

type FunctionDefinitionInner struct {
	Name        string      `json:"name"`
	Description *string     `json:"description,omitempty"`
	Parameters  interface{} `json:"parameters,omitempty"`
}

FunctionDefinitionInner represents the function details in a tool definition.

type GatewayError

type GatewayError struct {
	Status  int
	Message string
}

GatewayError represents an HTTP error from the gateway.

func (*GatewayError) Error

func (e *GatewayError) Error() string

type InsufficientBalanceError

type InsufficientBalanceError struct {
	Have, Need uint64
}

InsufficientBalanceError indicates the wallet does not have enough funds.

func (*InsufficientBalanceError) Error

func (e *InsufficientBalanceError) Error() string

type KeypairSigner

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

KeypairSigner signs real Solana USDC-SPL transfer transactions.

func NewKeypairSigner

func NewKeypairSigner(wallet *Wallet, rpcURL string) *KeypairSigner

NewKeypairSigner creates a signer from a wallet and optional Solana RPC URL. If rpcURL is empty, defaults to mainnet-beta.

func (*KeypairSigner) SignPayment

SignPayment is not yet implemented in solvela-go.

Use the Python SDK (solvela-python) or TypeScript SDK (solvela-ts) for production payment signing, or implement a custom Signer using the standard library's crypto/ed25519 package and a Solana JSON-RPC client of your choice.

type ModelInfo

type ModelInfo struct {
	ID                       string  `json:"id"`
	Provider                 string  `json:"provider"`
	ModelID                  string  `json:"model_id"`
	DisplayName              string  `json:"display_name"`
	InputCostPerMillion      float64 `json:"input_cost_per_million"`
	OutputCostPerMillion     float64 `json:"output_cost_per_million"`
	ContextWindow            int     `json:"context_window"`
	SupportsStreaming        bool    `json:"supports_streaming"`
	SupportsTools            bool    `json:"supports_tools"`
	SupportsVision           bool    `json:"supports_vision"`
	Reasoning                bool    `json:"reasoning"`
	SupportsStructuredOutput bool    `json:"supports_structured_output"`
	SupportsBatch            bool    `json:"supports_batch"`
	MaxOutputTokens          *int    `json:"max_output_tokens,omitempty"`
}

ModelInfo describes a model available on the gateway.

type Option

type Option func(*ClientConfig)

Option is a functional option for configuring ClientConfig.

func WithCache

func WithCache(enable bool) Option

WithCache enables or disables response caching.

func WithExpectedRecipient

func WithExpectedRecipient(r string) Option

WithExpectedRecipient sets the expected payment recipient for verification.

func WithFreeFallbackModel

func WithFreeFallbackModel(model string) Option

WithFreeFallbackModel sets the free model to fall back to.

func WithGatewayURL

func WithGatewayURL(url string) Option

WithGatewayURL sets the gateway URL.

func WithMaxPaymentAmount

func WithMaxPaymentAmount(max uint64) Option

WithMaxPaymentAmount sets the maximum payment amount in atomic units.

func WithMaxQualityRetries

func WithMaxQualityRetries(n int) Option

WithMaxQualityRetries sets the maximum number of quality retries.

func WithQualityCheck

func WithQualityCheck(enable bool) Option

WithQualityCheck enables or disables quality checking.

func WithRPCURL

func WithRPCURL(url string) Option

WithRPCURL sets the Solana RPC URL.

func WithSessionTTL

func WithSessionTTL(d time.Duration) Option

WithSessionTTL sets the session time-to-live.

func WithSessions

func WithSessions(enable bool) Option

WithSessions enables or disables session tracking.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the request timeout.

type PaymentAccept

type PaymentAccept struct {
	Scheme            string  `json:"scheme"`
	Network           string  `json:"network"`
	Amount            string  `json:"amount"`
	Asset             string  `json:"asset"`
	PayTo             string  `json:"pay_to"`
	MaxTimeoutSeconds int     `json:"max_timeout_seconds"`
	EscrowProgramID   *string `json:"escrow_program_id,omitempty"`
}

PaymentAccept describes an accepted payment scheme.

type PaymentPayload

type PaymentPayload struct {
	X402Version int           `json:"x402_version"`
	Resource    Resource      `json:"resource"`
	Accepted    PaymentAccept `json:"accepted"`
	Payload     interface{}   `json:"payload"`
}

PaymentPayload represents a payment attached to a request.

type PaymentRejectedError

type PaymentRejectedError struct {
	Reason string
}

PaymentRejectedError indicates the payment was not accepted.

func (*PaymentRejectedError) Error

func (e *PaymentRejectedError) Error() string

type PaymentRequired

type PaymentRequired struct {
	X402Version   int             `json:"x402_version"`
	Resource      Resource        `json:"resource"`
	Accepts       []PaymentAccept `json:"accepts"`
	CostBreakdown CostBreakdown   `json:"cost_breakdown"`
	Error         string          `json:"error"`
}

PaymentRequired represents a 402 Payment Required response.

type PaymentRequiredError

type PaymentRequiredError struct {
	PaymentRequired PaymentRequired
}

PaymentRequiredError wraps a 402 response.

func (*PaymentRequiredError) Error

func (e *PaymentRequiredError) Error() string

type RecipientMismatchError

type RecipientMismatchError struct {
	Expected, Actual string
}

RecipientMismatchError indicates the payment recipient does not match expectations.

func (*RecipientMismatchError) Error

func (e *RecipientMismatchError) Error() string

type Resource

type Resource struct {
	URL    string `json:"url"`
	Method string `json:"method"`
}

Resource represents the API resource being accessed.

type ResponseCache

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

ResponseCache is a thread-safe LRU cache for chat responses.

func NewResponseCache

func NewResponseCache() *ResponseCache

NewResponseCache creates a cache with default settings (100 entries, 5m TTL, 2s dedup).

func NewResponseCacheWithConfig

func NewResponseCacheWithConfig(maxEntries int, ttl, dedupWindow time.Duration) *ResponseCache

NewResponseCacheWithConfig creates a cache with custom settings.

func (*ResponseCache) Get

func (c *ResponseCache) Get(key uint64) (ChatResponse, bool)

Get retrieves a cached response. Returns the response and true if found and not expired.

func (*ResponseCache) Put

func (c *ResponseCache) Put(key uint64, response ChatResponse)

Put adds a response to the cache. If the cache is full, the least recently used entry is evicted. If the key was inserted within the dedup window, the put is ignored.

type Role

type Role string

Role represents a chat message role.

const (
	RoleSystem    Role = "system"
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
	RoleTool      Role = "tool"
	RoleDeveloper Role = "developer"
)

type SendChatResult

type SendChatResult struct {
	Response        *ChatResponse
	PaymentRequired *PaymentRequired
}

SendChatResult is either a ChatResponse or PaymentRequired.

type SessionInfo

type SessionInfo struct {
	Model     string
	Escalated bool
}

SessionInfo holds the current state of a session.

type SessionStore

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

SessionStore manages conversation sessions with model escalation.

func NewSessionStore

func NewSessionStore(ttl time.Duration) *SessionStore

NewSessionStore creates a session store with the given TTL.

func (*SessionStore) CleanupExpired

func (s *SessionStore) CleanupExpired()

CleanupExpired removes all sessions that have exceeded their TTL.

func (*SessionStore) GetOrCreate

func (s *SessionStore) GetOrCreate(sessionID, defaultModel string) SessionInfo

GetOrCreate returns session info for the given ID, creating a new session if needed. Expired sessions are replaced with fresh ones.

func (*SessionStore) RecordRequest

func (s *SessionStore) RecordRequest(sessionID string, requestHash uint64)

RecordRequest records a request hash. If 3 or more identical hashes appear, the session is escalated (three-strike rule).

type Signer

type Signer interface {
	SignPayment(ctx context.Context, amountAtomic uint64, recipient string, resource Resource, accepted PaymentAccept) (*PaymentPayload, error)
}

Signer is a pluggable interface for signing payment transactions.

type SignerError

type SignerError struct {
	Message string
}

SignerError represents a signing-related error.

func (*SignerError) Error

func (e *SignerError) Error() string

type SolanaPayload

type SolanaPayload struct {
	Transaction string `json:"transaction"`
}

SolanaPayload is the payload for exact Solana payments.

type SolvelaClient

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

SolvelaClient is the main client for interacting with the Solvela gateway.

func NewClient

func NewClient(wallet *Wallet, signer Signer, opts ...Option) *SolvelaClient

NewClient creates a new SolvelaClient with functional options.

func (*SolvelaClient) Chat

func (c *SolvelaClient) Chat(ctx context.Context, request *ChatRequest) (*ChatResponse, error)

Chat sends a non-streaming chat request with automatic payment, caching, session tracking, and quality checking.

func (*SolvelaClient) ChatStream

func (c *SolvelaClient) ChatStream(ctx context.Context, request *ChatRequest) (<-chan ChatChunkOrError, error)

ChatStream sends a streaming chat request.

func (*SolvelaClient) LastKnownBalance

func (c *SolvelaClient) LastKnownBalance() *float64

LastKnownBalance returns the most recently known wallet balance, or nil.

func (*SolvelaClient) Models

func (c *SolvelaClient) Models(ctx context.Context) ([]ModelInfo, error)

Models retrieves available models from the gateway.

func (*SolvelaClient) String

func (c *SolvelaClient) String() string

String returns a debug-safe representation with redacted secrets.

type TimeoutError

type TimeoutError struct {
	TimeoutSecs float64
}

TimeoutError indicates a request timed out.

func (*TimeoutError) Error

func (e *TimeoutError) Error() string

type ToolCall

type ToolCall struct {
	ID       string       `json:"id"`
	Type     string       `json:"type"`
	Function FunctionCall `json:"function"`
}

ToolCall represents a tool call in a message.

type ToolCallDelta

type ToolCallDelta struct {
	Index    int                `json:"index"`
	ID       *string            `json:"id,omitempty"`
	Type     *string            `json:"type,omitempty"`
	Function *FunctionCallDelta `json:"function,omitempty"`
}

ToolCallDelta represents an incremental tool call in streaming.

type ToolDefinition

type ToolDefinition struct {
	Type     string                  `json:"type"`
	Function FunctionDefinitionInner `json:"function"`
}

ToolDefinition represents a tool that can be used by the model.

type Transport

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

Transport handles HTTP communication with the Solvela gateway.

func NewTransport

func NewTransport(baseURL string, timeout time.Duration) *Transport

NewTransport creates a new Transport with the given base URL and timeout.

func (*Transport) FetchModels

func (t *Transport) FetchModels(ctx context.Context) ([]ModelInfo, error)

FetchModels retrieves available models from the gateway.

func (*Transport) SendChat

func (t *Transport) SendChat(ctx context.Context, request *ChatRequest, paymentSignature string, extraHeaders map[string]string) (*SendChatResult, error)

SendChat sends a non-streaming chat request.

func (*Transport) SendChatStream

func (t *Transport) SendChatStream(ctx context.Context, request *ChatRequest, paymentSignature string, extraHeaders map[string]string) (<-chan ChatChunkOrError, error)

SendChatStream sends a streaming chat request and returns a channel of chunks.

type Usage

type Usage struct {
	PromptTokens     int `json:"prompt_tokens"`
	CompletionTokens int `json:"completion_tokens"`
	TotalTokens      int `json:"total_tokens"`
}

Usage represents token usage statistics.

type Wallet

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

Wallet holds an ed25519 keypair for Solana operations.

func CreateWallet

func CreateWallet() (*Wallet, string, error)

CreateWallet generates a new random wallet. Returns the wallet and a placeholder mnemonic string.

func WalletFromEnv

func WalletFromEnv(varName string) (*Wallet, error)

WalletFromEnv creates a wallet from a base58-encoded keypair stored in an environment variable.

func WalletFromKeypairB58

func WalletFromKeypairB58(b58 string) (*Wallet, error)

WalletFromKeypairB58 creates a wallet from a base58-encoded keypair.

func WalletFromKeypairBytes

func WalletFromKeypairBytes(raw []byte) (*Wallet, error)

WalletFromKeypairBytes creates a wallet from raw 64-byte ed25519 keypair bytes.

func (*Wallet) Address

func (w *Wallet) Address() string

Address returns the base58-encoded public key (Solana address).

func (*Wallet) PrivateKey

func (w *Wallet) PrivateKey() ed25519.PrivateKey

PrivateKey returns the ed25519 private key.

func (*Wallet) PublicKey

func (w *Wallet) PublicKey() ed25519.PublicKey

PublicKey returns the ed25519 public key.

func (*Wallet) String

func (w *Wallet) String() string

String returns a debug-safe representation that redacts the secret key.

func (*Wallet) ToKeypairB58

func (w *Wallet) ToKeypairB58() string

ToKeypairB58 returns the base58-encoded keypair.

func (*Wallet) ToKeypairBytes

func (w *Wallet) ToKeypairBytes() []byte

ToKeypairBytes returns the raw 64-byte keypair.

type WalletError

type WalletError struct {
	Message string
}

WalletError represents a wallet-related error.

func (*WalletError) Error

func (e *WalletError) Error() string

Jump to

Keyboard shortcuts

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