Go SDK for the Portkey AI Gateway - A blazing fast AI Gateway for routing to 200+ LLMs with a unified API.
This SDK is designed for self-hosted deployments of the Portkey Gateway. You manage your own API keys and infrastructure.
- 🚀 Universal LLM Access: Connect to 200+ LLM providers through a single API
- 🔄 Automatic Retries: Built-in exponential backoff for resilient requests
- 🌊 Streaming Support: Full SSE streaming support for real-time responses
- 🎯 Smart Routing: Fallback and load balancing strategies
- 📊 Observability: Complete request tracing and metadata support
- 💾 Caching: Semantic and simple caching for cost optimization
- 🛡️ Type-Safe: Comprehensive type definitions with full IDE support
- 🎨 Fluent API: Intuitive configuration builder for complex routing
go get github.com/youssefsiam38/portkeyai- Deploy the Portkey Gateway (self-hosted)
- Get your LLM provider API keys (e.g., OpenAI, Anthropic, etc.)
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/youssefsiam38/portkeyai"
)
func main() {
// Create a client pointing to your self-hosted gateway
client, err := portkeyai.NewClient(
"", // No Portkey API key needed for self-hosted
portkeyai.WithBaseURL("http://localhost:8787"), // Your gateway URL
portkeyai.WithProvider("openai"),
portkeyai.WithAuthorization("Bearer " + os.Getenv("OPENAI_API_KEY")),
)
if err != nil {
log.Fatal(err)
}
// Create a chat completion request
req := portkeyai.NewChatCompletionRequest(
"gpt-4",
portkeyai.NewSystemMessage("You are a helpful assistant."),
portkeyai.NewUserMessage("What is the capital of France?"),
)
// Send the request
resp, err := client.Chat.Create(context.Background(), req, nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Choices[0].Message.Content)
}stream, err := client.Chat.CreateStream(ctx, req, nil)
if err != nil {
log.Fatal(err)
}
defer stream.Close()
for {
chunk, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
if len(chunk.Choices) > 0 {
fmt.Print(chunk.Choices[0].Delta.Content)
}
}For self-hosted Portkey Gateway, you manage your own API keys:
Pass your provider API key directly to the gateway:
client, err := portkeyai.NewClient(
"", // Empty for self-hosted (or omit Portkey API key validation)
portkeyai.WithBaseURL("http://localhost:8787"), // Your gateway URL
portkeyai.WithProvider("openai"),
portkeyai.WithAuthorization("Bearer sk-..."),
)You can also override the provider per request:
// Create client without default provider
client, err := portkeyai.NewClient(
"",
portkeyai.WithBaseURL("http://localhost:8787"),
)
// Specify provider in request options
opts := &portkeyai.RequestOptions{
Provider: "openai",
Authorization: "Bearer sk-...",
}
resp, err := client.Chat.Create(ctx, req, opts)For advanced routing (fallback, load balancing), use config:
// Build a fallback config with your API keys
config := portkeyai.NewConfig().
WithFallbackStrategy().
AddTarget(portkeyai.NewTarget().
WithProvider("openai").
WithAPIKey("sk-openai-key").
Build()).
AddTarget(portkeyai.NewTarget().
WithProvider("anthropic").
WithAPIKey("sk-ant-key").
Build())
configJSON, _ := config.Build()
client, err := portkeyai.NewClient(
"",
portkeyai.WithBaseURL("http://localhost:8787"),
portkeyai.WithConfig(configJSON),
)If using Portkey's hosted platform instead of self-hosted, virtual keys are supported:
client, err := portkeyai.NewClient(
"your-portkey-api-key",
// BaseURL defaults to https://api.portkey.ai/v1
portkeyai.WithVirtualKey("your-virtual-key"),
)Note: Virtual keys are a feature of Portkey's hosted platform. For self-hosted gateways, use direct provider authentication.
req := portkeyai.NewChatCompletionRequest(
"gpt-4",
portkeyai.NewSystemMessage("You are a helpful assistant."),
portkeyai.NewUserMessage("Hello!"),
)
resp, err := client.Chat.Create(ctx, req, nil)req := portkeyai.ChatCompletionRequest{
Model: "gpt-4",
Messages: []portkeyai.ChatCompletionMessage{
portkeyai.NewUserMessage("Write a poem"),
},
Temperature: float64Ptr(0.7),
MaxTokens: intPtr(150),
TopP: float64Ptr(0.9),
}tool := portkeyai.Tool{
Type: "function",
Function: portkeyai.Function{
Name: "get_weather",
Description: "Get the current weather",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"location": map[string]interface{}{
"type": "string",
"description": "City name",
},
},
"required": []string{"location"},
},
},
}
req := portkeyai.ChatCompletionRequest{
Model: "gpt-4",
Messages: []portkeyai.ChatCompletionMessage{
portkeyai.NewUserMessage("What's the weather in Paris?"),
},
Tools: []portkeyai.Tool{tool},
}msg := portkeyai.NewUserMessageWithImages(
"What's in this image?",
"https://example.com/image.jpg",
"https://example.com/image2.jpg",
)
req := portkeyai.NewChatCompletionRequest("gpt-4-vision-preview", msg)req := portkeyai.NewEmbeddingRequest(
"text-embedding-ada-002",
"The quick brown fox jumps over the lazy dog",
)
resp, err := client.Embeddings.Create(ctx, req, nil)
fmt.Printf("Embedding: %v\n", resp.Data[0].Embedding)texts := []string{
"First text to embed",
"Second text to embed",
"Third text to embed",
}
req := portkeyai.NewEmbeddingRequest("text-embedding-ada-002", texts)
resp, err := client.Embeddings.Create(ctx, req, nil)dimensions := 512
req := portkeyai.NewEmbeddingRequestWithOptions(
"text-embedding-3-small",
"Text to embed",
"float",
&dimensions,
)Automatically fallback to alternative providers when the primary fails:
// Simple fallback
config, err := portkeyai.NewFallbackConfig(
"openai-key",
"anthropic-key",
"google-key",
).Build()
client, err := portkeyai.NewClient(
"portkey-api-key",
portkeyai.WithConfig(config),
)config := portkeyai.NewConfig().
WithFallbackStrategy().
WithDefaultRetry().
AddTarget(portkeyai.NewTarget().
WithVirtualKey("openai-key").
WithModelOverride("gpt-4").
Build()).
AddTarget(portkeyai.NewTarget().
WithVirtualKey("anthropic-key").
WithModelOverride("claude-3-opus-20240229").
Build())Distribute requests across multiple providers:
// Equal distribution
config, err := portkeyai.NewLoadBalanceConfig(
"provider-1",
"provider-2",
"provider-3",
).Build()// 70% cheap, 30% premium
config, err := portkeyai.NewLoadBalanceConfigWeighted(map[string]float64{
"cheap-provider": 0.7,
"premium-provider": 0.3,
}).Build()// Custom retry settings
client, err := portkeyai.NewClient(
"portkey-api-key",
portkeyai.WithVirtualKey("virtual-key"),
portkeyai.WithMaxRetries(5),
portkeyai.WithRetryableStatusCodes([]int{429, 500, 502, 503, 504}),
)config := portkeyai.NewConfig().
WithRetry(5, []int{429, 500, 502, 503}).
AddVirtualKeyTarget("provider-key")Track requests across your application:
opts := &portkeyai.RequestOptions{
TraceID: "user-session-12345",
SpanName: "question-answering",
}
resp, err := client.Chat.Create(ctx, req, opts)// Parent request
parentOpts := &portkeyai.RequestOptions{
TraceID: "workflow-123",
SpanID: "parent-span",
SpanName: "Main Workflow",
}
// Child request
childOpts := &portkeyai.RequestOptions{
TraceID: "workflow-123",
SpanID: "child-span",
ParentSpanID: "parent-span",
SpanName: "Sub-task",
}Attach custom metadata to requests:
opts := &portkeyai.RequestOptions{
Metadata: map[string]interface{}{
"user_id": "user_123",
"environment": "production",
"feature_flag": "new_model_v2",
},
}Partition your cache by custom namespaces:
opts := &portkeyai.RequestOptions{
CacheNamespace: "math-questions",
}
// First request - cached
resp1, _ := client.Chat.Create(ctx, req, opts)
// Second request - from cache
resp2, _ := client.Chat.Create(ctx, req, opts)opts := &portkeyai.RequestOptions{
CacheNamespace: "questions",
CacheForceRefresh: true,
}client, err := portkeyai.NewClient(
"api-key",
portkeyai.WithVirtualKey("virtual-key"),
portkeyai.WithBaseURL("https://custom.gateway.ai"),
portkeyai.WithConfig("pc-config-id"),
portkeyai.WithMaxRetries(5),
portkeyai.WithRetryDelay(2 * time.Second),
portkeyai.WithMaxRetryDelay(60 * time.Second),
portkeyai.WithHTTPClient(customHTTPClient),
portkeyai.WithDefaultRequestOptions(&portkeyai.RequestOptions{
Metadata: map[string]interface{}{
"app": "my-app",
},
}),
)client.SetVirtualKey("new-virtual-key")
client.SetConfig("new-config")
client.SetProvider("anthropic")
client.SetAuthorization("Bearer new-token")Convert a stream to a complete response:
stream, err := client.Chat.CreateStream(ctx, req, nil)
if err != nil {
log.Fatal(err)
}
// Consume entire stream and get complete response
resp, err := portkeyai.StreamToCompletion(ctx, stream)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Choices[0].Message.Content)stream, _ := client.Chat.CreateStream(ctx, req, nil)
defer stream.Close()
acc := portkeyai.NewChatCompletionAccumulator()
for {
chunk, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
acc.AddChunk(chunk)
fmt.Print(chunk.Choices[0].Delta.Content)
}
// Get complete message
message := acc.ToMessage()
fmt.Printf("\n\nFull content: %s\n", acc.GetContent())
fmt.Printf("Finish reason: %s\n", acc.GetFinishReason())The SDK provides type-safe error handling:
resp, err := client.Chat.Create(ctx, req, nil)
if err != nil {
switch e := err.(type) {
case *portkeyai.APIError:
fmt.Printf("API Error %d: %s\n", e.StatusCode, e.Message)
if e.IsRetryable() {
// Handle retryable error
}
case *portkeyai.NetworkError:
fmt.Printf("Network Error: %v\n", e.Err)
case *portkeyai.ValidationError:
fmt.Printf("Validation Error: %s - %s\n", e.Field, e.Message)
default:
fmt.Printf("Unknown error: %v\n", err)
}
return
}The SDK comes with production-ready defaults:
- ✅ Automatic Retries: 3 attempts with exponential backoff
- ✅ Retryable Status Codes: 429, 500, 502, 503, 504
- ✅ No Timeout: Supports long-running streaming requests
- ✅ Connection Pooling: Reuses HTTP connections for performance
- ✅ Exponential Backoff: With jitter to prevent thundering herd
client, err := portkeyai.NewClient(
"api-key",
portkeyai.WithMaxRetries(0), // Disable retries
portkeyai.WithRetryDelay(500 * time.Millisecond),
portkeyai.WithMaxRetryDelay(10 * time.Second),
)Comprehensive examples are available in the /examples directory:
examples/chat/basic- Basic chat completionexamples/chat/streaming- Streaming responsesexamples/chat/with_tools- Function callingexamples/embeddings/basic_example- Text embeddingsexamples/config/fallback- Fallback configurationexamples/config/loadbalance- Load balancingexamples/config/retry- Retry strategiesexamples/advanced/custom_headers- Tracing & metadata
Run examples with:
go run examples/chat/basic/main.go
go run examples/chat/streaming/main.goRun the test suite:
go test ./tests/...With coverage:
go test -cover ./tests/...Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
- ✅ Chat completions API (streaming & non-streaming)
- ✅ Embeddings API
- ✅ Automatic retries with exponential backoff
- ✅ Fluent configuration builder
- ✅ Gateway routing (fallback & load balancing)
- ✅ Full observability support (traces, spans, metadata)
- ✅ Caching with namespaces
- ✅ Type-safe error handling
- ✅ Comprehensive examples and documentation
Built with ❤️ by the Portkey community