Axemere Gateway — Go SDK
A typed Go client for the Axemere AI Gateway. Explicit request/response structs, context-aware cancellation, and idiomatic error handling: built for production Go services.
GitHub → Axemere-LLC/axemere-go
Install
go get github.com/Axemere-LLC/axemere-go/gateway
Requires Go 1.24+.
Quick start
package main import ( "context" "fmt" "log" "github.com/Axemere-LLC/axemere-go/gateway" ) func main() { // reads AXEMERE_GATEWAY_URL and AXEMERE_GATEWAY_TOKEN from env cfg, err := gateway.NewConfig() if err != nil { log.Fatal(err) } client, err := gateway.NewClient(cfg) if err != nil { log.Fatal(err) } result, err := client.Execute(context.Background(), gateway.ExecuteRequest{ Provider: "openai", Model: "gpt-4o-mini", Messages: []gateway.Message{ {Role: "user", Content: "Hello"}, }, }) if err != nil { log.Fatal(err) } fmt.Println(result.Content) fmt.Printf("Cost: $%s Tokens in: %d Tokens out: %d\n", result.Metering.CostUSD, result.Metering.TokensIn, result.Metering.TokensOut) }
Configuration
Config
type Config struct { GatewayURL string GatewayToken string DefaultProvider string DefaultModel string WorkloadID string ProjectID string AccountID string CustomerID string Labels map[string]string ProviderAPIKey string Timeout time.Duration // default 120s }
| Field | Env var | Description |
|---|---|---|
GatewayURL | AXEMERE_GATEWAY_URL | Gateway base URL (required) |
GatewayToken | AXEMERE_GATEWAY_TOKEN | Bearer token for authentication |
DefaultProvider | AXEMERE_PROVIDER | Provider used when ExecuteRequest.Provider is empty |
DefaultModel | AXEMERE_MODEL | Model used when ExecuteRequest.Model is empty |
WorkloadID | AXEMERE_WORKLOAD_ID | Workload ID for attribution and policy matching |
ProjectID | AXEMERE_PROJECT_ID | Project ID for budget controls and reporting |
AccountID | AXEMERE_ACCOUNT_ID | Account ID for attribution |
CustomerID | AXEMERE_CUSTOMER_ID | Customer ID for attribution |
Labels | AXEMERE_LABELS (JSON) | Arbitrary key-value labels attached to every request |
ProviderAPIKey | AXEMERE_PROVIDER_API_KEY | BYOK provider key forwarded to upstream |
Timeout | AXEMERE_TIMEOUT_SECONDS | HTTP timeout; default 120s |
NewConfig(opts ...Option) (*Config, error)
Reads environment variables, then applies functional options. Options take precedence over env vars. Returns an error if AXEMERE_LABELS contains invalid JSON.
cfg, err := gateway.NewConfig( gateway.WithGatewayURL("http://localhost:7080"), gateway.WithGatewayToken("tok_..."), gateway.WithWorkloadID("wl_my_service"), )
Functional options
| Option | Sets |
|---|---|
WithGatewayURL(url string) | GatewayURL |
WithGatewayToken(token string) | GatewayToken |
WithDefaultProvider(p string) | DefaultProvider |
WithDefaultModel(m string) | DefaultModel |
WithWorkloadID(id string) | WorkloadID |
WithProjectID(id string) | ProjectID |
WithAccountID(id string) | AccountID |
WithCustomerID(id string) | CustomerID |
WithLabels(labels map[string]string) | Labels |
WithProviderAPIKey(key string) | ProviderAPIKey |
WithTimeout(d time.Duration) | Timeout |
(*Config).SetDefaults(opts ...Option)
Applies options in place. Each option replaces the corresponding field; it does not merge. Useful for switching providers or models inside a request loop.
(*Config).ProxyURL(provider string) string
Returns the proxy base URL for the given provider. Use as the base URL for an existing provider SDK (see Proxy mode).
Format: {GatewayURL}/proxy/{provider}[/k/{token}][/w/{workload}][/p/{project}][/a/{account}][/c/{customer}]/
Client
NewClient(cfg *Config) (*Client, error)
Creates a new client. Returns an error if cfg is nil or GatewayURL is empty. Safe for concurrent use.
(*Client).Execute(ctx context.Context, req ExecuteRequest) (*ExecuteResponse, error)
Sends a governed request and returns the full response.
result, err := client.Execute(ctx, gateway.ExecuteRequest{ Provider: "anthropic", Model: "claude-haiku-4-5-20251001", Messages: []gateway.Message{ {Role: "user", Content: "What is 2+2?"}, }, Params: map[string]any{ "max_tokens": 128, }, })
(*Client).Stream(ctx context.Context, req ExecuteRequest) (<-chan StreamChunk, error)
Sends a governed streaming request. Returns a channel that delivers StreamChunk values and is closed after the final chunk or on error.
Important: Cancel the context when finished reading, even if you break out of the range loop early. A background goroutine reads from the network and only unblocks when the channel is drained or the context is cancelled.
ctx, cancel := context.WithCancel(context.Background()) defer cancel() ch, err := client.Stream(ctx, gateway.ExecuteRequest{ Provider: "openai", Model: "gpt-4o-mini", Messages: []gateway.Message{{Role: "user", Content: "Count to 5"}}, }) if err != nil { log.Fatal(err) } for chunk := range ch { if chunk.Err != nil { log.Fatal(chunk.Err) } if chunk.IsFinal { fmt.Printf("\nRecord: %s\n", chunk.RecordID) break } fmt.Print(chunk.Content) }
Request types
ExecuteRequest
| Field | Type | Description |
|---|---|---|
Messages | []Message | Conversation turns to send |
Provider | string | Provider name; overrides Config.DefaultProvider |
Model | string | Model identifier; overrides Config.DefaultModel |
WorkloadID | string | Overrides Config.WorkloadID for this request |
ProjectID | string | Overrides Config.ProjectID for this request |
AccountID | string | Overrides Config.AccountID for this request |
CustomerID | string | Overrides Config.CustomerID for this request |
Labels | map[string]string | Merged on top of Config.Labels; per-request keys win |
ProviderAPIKey | string | BYOK key for this request; overrides Config.ProviderAPIKey |
DelegationToken | string | Delegation token for scoped access |
Params | map[string]any | Extra provider parameters merged into action.params |
Message
gateway.Message{Role: "user", Content: "Hello"} gateway.Message{Role: "assistant", Content: "Hi there!"} gateway.Message{Role: "system", Content: "You are a helpful assistant."}
Response types
ExecuteResponse
| Field | Type | Description |
|---|---|---|
Content | string | Assistant text extracted from the provider response |
RecordID | string | Gateway record ID for audit lookup |
RecordHash | string | Cryptographic hash of the record |
Provider | string | Provider that handled the request |
Model | string | Model that generated the response |
Metering | Metering | Token counts and cost |
ProviderResponse | map[string]any | Raw provider response body |
StreamChunk
| Field | Type | Description |
|---|---|---|
Content | string | Text delta (empty on final chunk) |
IsFinal | bool | true on the last chunk |
RecordID | string | Populated on the final chunk |
Metering | *Metering | Populated on the final chunk |
Err | error | Non-nil when the stream terminates with an error |
Metering
| Field | Type | Description |
|---|---|---|
CostUSD | string | Total request cost in USD |
TokensIn | int | Input token count |
TokensOut | int | Output token count |
BytesIn | int | Request bytes |
BytesOut | int | Response bytes |
CacheHitTokens | *int | Cached prompt tokens (Anthropic) |
CacheMissTokens | *int | Uncached prompt tokens (Anthropic) |
CacheCreationTokens | *int | Cache-write tokens (Anthropic) |
ReasoningTokens | *int | Reasoning tokens (o-series models) |
MarkupMultiplierApplied | string | Org pricing markup factor |
CostBreakdown | []CostBreakdownItem | Per-tier cost line items |
Errors
import "errors" result, err := client.Execute(ctx, req) if err != nil { var denied *gateway.PolicyDeniedError var quota *gateway.QuotaExceededError var timeout *gateway.GatewayTimeoutError var gwErr *gateway.GatewayError switch { case errors.As(err, &denied): log.Printf("Policy denied: %s (record: %s)", denied.Reason, denied.RecordID) case errors.As(err, "a): log.Printf("Quota exceeded. Upgrade: %s", quota.UpgradeURL) case errors.As(err, &timeout): log.Printf("Gateway timed out") case errors.As(err, &gwErr): log.Printf("Gateway error (HTTP %d): %s", gwErr.StatusCode, gwErr.Message) } }
| Type | When returned | Extra fields |
|---|---|---|
*GatewayError | Base type; network failure, non-2xx HTTP, malformed response | Message, StatusCode, ResponseBody, Err |
*PolicyDeniedError | Request denied by gateway policy (HTTP 403) | Reason, Trace, RecordID |
*QuotaExceededError | Budget or quota exceeded (HTTP 429) | UpgradeURL, RetryAfter |
*GatewayTimeoutError | Connector timed out (HTTP 504) | — |
All types implement error. GatewayError implements Unwrap() so errors.Is and errors.As traverse the chain correctly.
Proxy mode
Use cfg.ProxyURL() to point any existing Go provider SDK at the gateway without changing packages.
import ( "github.com/Axemere-LLC/axemere-go/gateway" "github.com/openai/openai-go" "github.com/openai/openai-go/option" ) cfg, _ := gateway.NewConfig() client := openai.NewClient( option.WithBaseURL(cfg.ProxyURL("openai")), option.WithAPIKey("placeholder"), // gateway handles auth )
Note: The gateway token is embedded in the proxy URL path (
/k/{token}). URLs appear in access logs. PreferClient.Execute()/Client.Stream(): they send the token in theAuthorizationheader.
Get a gateway
- Free tier — self-hosted, no account required → Install
- Self-Hosted — team features + control plane → Get started
- Managed — fully hosted, zero-ops → Get started