| iomodo | a97eb22 | 2025-07-26 11:18:17 +0400 | [diff] [blame] | 1 | # LLM Interface Package |
| 2 | |
| 3 | This package provides a generic interface for different Large Language Model (LLM) providers, with OpenAI's API structure as the primary reference. It supports multiple providers including OpenAI, xAI, Claude, Gemini, and local models. |
| 4 | |
| 5 | ## Features |
| 6 | |
| 7 | - **Unified Interface**: Single interface for all LLM providers |
| 8 | - **Multiple Providers**: Support for OpenAI, xAI, Claude, Gemini, and local models |
| 9 | - **Tool/Function Calling**: Support for function calling and tool usage |
| 10 | - **Embeddings**: Generate embeddings for text |
| 11 | - **Configurable**: Flexible configuration options for each provider |
| 12 | - **Thread-Safe**: Thread-safe factory and registry implementations |
| 13 | |
| 14 | ## Quick Start |
| 15 | |
| 16 | ```go |
| 17 | package main |
| 18 | |
| 19 | import ( |
| 20 | "context" |
| 21 | "fmt" |
| 22 | "log" |
| 23 | |
| 24 | "your-project/server/llm" |
| 25 | ) |
| 26 | |
| 27 | func main() { |
| 28 | // Create a configuration for OpenAI |
| 29 | config := llm.Config{ |
| 30 | Provider: llm.ProviderOpenAI, |
| 31 | APIKey: "your-openai-api-key", |
| 32 | BaseURL: "https://api.openai.com/v1", |
| 33 | Timeout: 30 * time.Second, |
| 34 | } |
| 35 | |
| 36 | // Create a provider (you'll need to register the implementation first) |
| 37 | provider, err := llm.CreateProvider(config) |
| 38 | if err != nil { |
| 39 | log.Fatal(err) |
| 40 | } |
| 41 | defer provider.Close() |
| 42 | |
| 43 | // Create a chat completion request |
| 44 | req := llm.ChatCompletionRequest{ |
| 45 | Model: "gpt-3.5-turbo", |
| 46 | Messages: []llm.Message{ |
| 47 | {Role: llm.RoleUser, Content: "Hello, how are you?"}, |
| 48 | }, |
| 49 | MaxTokens: &[]int{100}[0], |
| 50 | Temperature: &[]float64{0.7}[0], |
| 51 | } |
| 52 | |
| 53 | // Get the response |
| 54 | resp, err := provider.ChatCompletion(context.Background(), req) |
| 55 | if err != nil { |
| 56 | log.Fatal(err) |
| 57 | } |
| 58 | |
| 59 | fmt.Println("Response:", resp.Choices[0].Message.Content) |
| 60 | } |
| 61 | ``` |
| 62 | |
| 63 | ## Core Types |
| 64 | |
| 65 | ### Provider |
| 66 | |
| 67 | Represents different LLM service providers: |
| 68 | |
| 69 | ```go |
| 70 | const ( |
| 71 | ProviderOpenAI Provider = "openai" |
| 72 | ProviderXAI Provider = "xai" |
| 73 | ProviderClaude Provider = "claude" |
| 74 | ProviderGemini Provider = "gemini" |
| 75 | ProviderLocal Provider = "local" |
| 76 | ) |
| 77 | ``` |
| 78 | |
| 79 | ### Message |
| 80 | |
| 81 | Represents a single message in a conversation: |
| 82 | |
| 83 | ```go |
| 84 | type Message struct { |
| 85 | Role Role `json:"role"` |
| 86 | Content string `json:"content"` |
| 87 | ToolCalls []ToolCall `json:"tool_calls,omitempty"` |
| 88 | ToolCallID string `json:"tool_call_id,omitempty"` |
| 89 | Name string `json:"name,omitempty"` |
| 90 | } |
| 91 | ``` |
| 92 | |
| 93 | ### ChatCompletionRequest |
| 94 | |
| 95 | Represents a request to complete a chat conversation: |
| 96 | |
| 97 | ```go |
| 98 | type ChatCompletionRequest struct { |
| 99 | Model string `json:"model"` |
| 100 | Messages []Message `json:"messages"` |
| 101 | MaxTokens *int `json:"max_tokens,omitempty"` |
| 102 | Temperature *float64 `json:"temperature,omitempty"` |
| 103 | TopP *float64 `json:"top_p,omitempty"` |
| 104 | Stream *bool `json:"stream,omitempty"` |
| 105 | Tools []Tool `json:"tools,omitempty"` |
| 106 | ToolChoice interface{} `json:"tool_choice,omitempty"` |
| 107 | ResponseFormat *ResponseFormat `json:"response_format,omitempty"` |
| 108 | ExtraParams map[string]interface{} `json:"-"` // Provider-specific parameters |
| 109 | } |
| 110 | ``` |
| 111 | |
| 112 | ## Main Interface |
| 113 | |
| 114 | ### LLMProvider |
| 115 | |
| 116 | The main interface that all LLM providers must implement: |
| 117 | |
| 118 | ```go |
| 119 | type LLMProvider interface { |
| 120 | // ChatCompletion creates a chat completion |
| 121 | ChatCompletion(ctx context.Context, req ChatCompletionRequest) (*ChatCompletionResponse, error) |
| 122 | |
| 123 | // CreateEmbeddings generates embeddings for the given input |
| 124 | CreateEmbeddings(ctx context.Context, req EmbeddingRequest) (*EmbeddingResponse, error) |
| 125 | |
| 126 | // Close performs any necessary cleanup |
| 127 | Close() error |
| 128 | } |
| 129 | ``` |
| 130 | |
| 131 | ## Provider Factory |
| 132 | |
| 133 | The package includes a factory system for creating and managing LLM providers: |
| 134 | |
| 135 | ```go |
| 136 | // Register a provider factory |
| 137 | err := llm.RegisterProvider(llm.ProviderOpenAI, openaiFactory) |
| 138 | |
| 139 | // Create a provider |
| 140 | provider, err := llm.CreateProvider(config) |
| 141 | |
| 142 | // Check if a provider is supported |
| 143 | if llm.SupportsProvider(llm.ProviderOpenAI) { |
| 144 | // Provider is available |
| 145 | } |
| 146 | |
| 147 | // List all registered providers |
| 148 | providers := llm.ListProviders() |
| 149 | ``` |
| 150 | |
| 151 | ## Configuration |
| 152 | |
| 153 | Each provider can be configured with specific settings: |
| 154 | |
| 155 | ```go |
| 156 | config := llm.Config{ |
| 157 | Provider: llm.ProviderOpenAI, |
| 158 | APIKey: "your-api-key", |
| 159 | BaseURL: "https://api.openai.com/v1", |
| 160 | Timeout: 30 * time.Second, |
| 161 | MaxRetries: 3, |
| 162 | ExtraConfig: map[string]interface{}{ |
| 163 | "organization": "your-org-id", |
| 164 | }, |
| 165 | } |
| 166 | ``` |
| 167 | |
| 168 | ## Tool/Function Calling |
| 169 | |
| 170 | Support for function calling and tool usage: |
| 171 | |
| 172 | ```go |
| 173 | tools := []llm.Tool{ |
| 174 | { |
| 175 | Type: "function", |
| 176 | Function: llm.Function{ |
| 177 | Name: "get_weather", |
| 178 | Description: "Get the current weather for a location", |
| 179 | Parameters: map[string]interface{}{ |
| 180 | "type": "object", |
| 181 | "properties": map[string]interface{}{ |
| 182 | "location": map[string]interface{}{ |
| 183 | "type": "string", |
| 184 | "description": "The city and state, e.g. San Francisco, CA", |
| 185 | }, |
| 186 | }, |
| 187 | "required": []string{"location"}, |
| 188 | }, |
| 189 | }, |
| 190 | }, |
| 191 | } |
| 192 | |
| 193 | req := llm.ChatCompletionRequest{ |
| 194 | Model: "gpt-3.5-turbo", |
| 195 | Messages: messages, |
| 196 | Tools: tools, |
| 197 | } |
| 198 | ``` |
| 199 | |
| 200 | ## Embeddings |
| 201 | |
| 202 | Generate embeddings for text: |
| 203 | |
| 204 | ```go |
| 205 | req := llm.EmbeddingRequest{ |
| 206 | Input: "Hello, world!", |
| 207 | Model: "text-embedding-ada-002", |
| 208 | } |
| 209 | |
| 210 | resp, err := provider.CreateEmbeddings(context.Background(), req) |
| 211 | if err != nil { |
| 212 | log.Fatal(err) |
| 213 | } |
| 214 | |
| 215 | fmt.Printf("Embedding dimensions: %d\n", len(resp.Data[0].Embedding)) |
| 216 | ``` |
| 217 | |
| 218 | ## Implementing a New Provider |
| 219 | |
| 220 | To implement a new LLM provider: |
| 221 | |
| 222 | 1. **Implement the LLMProvider interface**: |
| 223 | |
| 224 | ```go |
| 225 | type MyProvider struct { |
| 226 | config llm.Config |
| 227 | client *http.Client |
| 228 | } |
| 229 | |
| 230 | func (p *MyProvider) ChatCompletion(ctx context.Context, req llm.ChatCompletionRequest) (*llm.ChatCompletionResponse, error) { |
| 231 | // Implementation here |
| 232 | } |
| 233 | |
| 234 | func (p *MyProvider) CreateEmbeddings(ctx context.Context, req llm.EmbeddingRequest) (*llm.EmbeddingResponse, error) { |
| 235 | // Implementation here |
| 236 | } |
| 237 | |
| 238 | func (p *MyProvider) Close() error { |
| 239 | // Cleanup implementation |
| 240 | return nil |
| 241 | } |
| 242 | ``` |
| 243 | |
| 244 | 2. **Create a factory**: |
| 245 | |
| 246 | ```go |
| 247 | type MyProviderFactory struct{} |
| 248 | |
| 249 | func (f *MyProviderFactory) CreateProvider(config llm.Config) (llm.LLMProvider, error) { |
| 250 | return &MyProvider{config: config}, nil |
| 251 | } |
| 252 | |
| 253 | func (f *MyProviderFactory) SupportsProvider(provider llm.Provider) bool { |
| 254 | return provider == llm.ProviderMyProvider |
| 255 | } |
| 256 | ``` |
| 257 | |
| 258 | 3. **Register the provider**: |
| 259 | |
| 260 | ```go |
| 261 | func init() { |
| 262 | llm.RegisterProvider(llm.ProviderMyProvider, &MyProviderFactory{}) |
| 263 | } |
| 264 | ``` |
| 265 | |
| 266 | ## Error Handling |
| 267 | |
| 268 | The package defines common error types: |
| 269 | |
| 270 | ```go |
| 271 | var ( |
| 272 | ErrInvalidConfig = fmt.Errorf("invalid configuration") |
| 273 | ErrUnsupportedProvider = fmt.Errorf("unsupported provider") |
| 274 | ErrAPIKeyRequired = fmt.Errorf("API key is required") |
| 275 | ErrModelNotFound = fmt.Errorf("model not found") |
| 276 | ErrRateLimitExceeded = fmt.Errorf("rate limit exceeded") |
| 277 | ErrContextCancelled = fmt.Errorf("context cancelled") |
| 278 | ErrTimeout = fmt.Errorf("request timeout") |
| 279 | ) |
| 280 | ``` |
| 281 | |
| 282 | ## Utilities |
| 283 | |
| 284 | The package includes utility functions: |
| 285 | |
| 286 | ```go |
| 287 | // Validate configuration |
| 288 | err := llm.ValidateConfig(config) |
| 289 | |
| 290 | // Check if provider is valid |
| 291 | if llm.IsValidProvider(llm.ProviderOpenAI) { |
| 292 | // Provider is valid |
| 293 | } |
| 294 | |
| 295 | // Get default configuration |
| 296 | config, err := llm.GetDefaultConfig(llm.ProviderOpenAI) |
| 297 | |
| 298 | // Merge custom config with defaults |
| 299 | config = llm.MergeConfig(customConfig) |
| 300 | |
| 301 | // Validate requests |
| 302 | err := llm.ValidateChatCompletionRequest(req) |
| 303 | err := llm.ValidateEmbeddingRequest(req) |
| 304 | |
| 305 | // Estimate tokens |
| 306 | tokens := llm.EstimateTokens(req) |
| 307 | ``` |
| 308 | |
| 309 | ## Thread Safety |
| 310 | |
| 311 | The factory and registry implementations are thread-safe and can be used concurrently from multiple goroutines. |
| 312 | |
| 313 | ## Default Configurations |
| 314 | |
| 315 | The package provides default configurations for each provider: |
| 316 | |
| 317 | ```go |
| 318 | var DefaultConfigs = map[llm.Provider]llm.Config{ |
| 319 | llm.ProviderOpenAI: { |
| 320 | Provider: llm.ProviderOpenAI, |
| 321 | BaseURL: "https://api.openai.com/v1", |
| 322 | Timeout: 30 * time.Second, |
| 323 | MaxRetries: 3, |
| 324 | }, |
| 325 | // ... other providers |
| 326 | } |
| 327 | ``` |
| 328 | |
| 329 | ## Next Steps |
| 330 | |
| 331 | 1. Implement the actual provider implementations (OpenAI, xAI, Claude, etc.) |
| 332 | 2. Add tests for the interface and implementations |
| 333 | 3. Add more utility functions as needed |
| 334 | 4. Consider adding caching and retry mechanisms |
| 335 | 5. Add support for more provider-specific features |