# Provider Registry

The provider registry allows you to register and access AI providers globally.

## Registry Functions

The package-level functions operate on a shared global registry:

```go
// Register a provider in the global registry
func RegisterProvider(name string, p provider.Provider)

// Get a registered provider from the global registry
func GetProvider(name string) (provider.Provider, error)

// Resolve a "provider:model" string directly to a model, using the global registry
func ResolveLanguageModel(model string) (provider.LanguageModel, error)
func ResolveEmbeddingModel(model string) (provider.EmbeddingModel, error)
func ResolveImageModel(model string) (provider.ImageModel, error)
func ResolveSpeechModel(model string) (provider.SpeechModel, error)
func ResolveTranscriptionModel(model string) (provider.TranscriptionModel, error)
func ResolveRerankingModel(model string) (provider.RerankingModel, error)

// GetGlobalRegistry returns the shared *Registry instance the above functions use
func GetGlobalRegistry() *Registry
```

`NoSuchProviderError` is returned when a provider ID can't be resolved or
doesn't support the requested model type:

```go
type NoSuchProviderError struct {
    ProviderID         string
    ModelID            string
    ModelType          string
    AvailableProviders []string
    Reason             string
}
```

## Registry Type

For an isolated registry (instead of the shared global one), use `NewRegistry`:

```go
func NewRegistry(opts ...RegistryOption) *Registry

func WithSeparator(separator string) RegistryOption              // default ":"
func WithLanguageModelMiddleware(mw ...*middleware.LanguageModelMiddleware) RegistryOption
func WithImageModelMiddleware(mw ...*middleware.ImageModelMiddleware) RegistryOption
```

`*Registry` exposes the same operations as methods: `RegisterProvider`,
`GetProvider`, `ListProviders`, `ResolveLanguageModel`,
`ResolveEmbeddingModel`, `ResolveImageModel`, `ResolveSpeechModel`,
`ResolveTranscriptionModel`, `ResolveRerankingModel`, plus alias and tool
helpers (`RegisterAlias`, `ListAliases`, `RegisterTool`, `LookupTool`,
`ListTools`).

## Examples

### Registering Providers

```go
package main

import (
    "github.com/digitallysavvy/go-ai/pkg/providers/anthropic"
    "github.com/digitallysavvy/go-ai/pkg/providers/openai"
    "github.com/digitallysavvy/go-ai/pkg/registry"
)

func main() {
    // Register OpenAI
    registry.RegisterProvider("openai", openai.New(openai.Config{
        APIKey: "openai-key",
    }))

    // Register Anthropic
    registry.RegisterProvider("anthropic", anthropic.New(anthropic.Config{
        APIKey: "anthropic-key",
    }))
}
```

### Getting Providers

```go
// Get a provider by name
p, err := registry.GetProvider("openai")
if err != nil {
    log.Fatal(err)
}

// Get a model
model, err := p.LanguageModel("gpt-4")
if err != nil {
    log.Fatal(err)
}
```

### Resolving a "provider:model" String Directly

```go
// Equivalent to GetProvider("openai") then LanguageModel("gpt-4")
model, err := registry.ResolveLanguageModel("openai:gpt-4")
if err != nil {
    log.Fatal(err)
}
```

### Listing Registered Providers

```go
providers := registry.GetGlobalRegistry().ListProviders()
fmt.Println("Available providers:")
for _, name := range providers {
    fmt.Printf("  - %s\n", name)
}
```

### Checking Provider Existence

There's no dedicated `Has`-style check; call `GetProvider` and inspect the error:

```go
if _, err := registry.GetProvider("openai"); err == nil {
    fmt.Println("OpenAI is registered")
}
```

### Using with Configuration

```go
type Config struct {
    ProviderName string
    ModelID      string
}

func getModel(cfg Config) (provider.LanguageModel, error) {
    p, err := registry.GetProvider(cfg.ProviderName)
    if err != nil {
        return nil, fmt.Errorf("provider not found: %w", err)
    }

    model, err := p.LanguageModel(cfg.ModelID)
    if err != nil {
        return nil, fmt.Errorf("model not found: %w", err)
    }

    return model, nil
}

// Usage
config := Config{
    ProviderName: "openai",
    ModelID:      "gpt-4",
}

model, err := getModel(config)
if err != nil {
    log.Fatal(err)
}
```

### Dynamic Provider Selection

```go
func generateWithProvider(providerName, modelID, prompt string) (string, error) {
    // Get provider from registry
    p, err := registry.GetProvider(providerName)
    if err != nil {
        return "", fmt.Errorf("provider %s not found: %w", providerName, err)
    }

    // Get model
    model, err := p.LanguageModel(modelID)
    if err != nil {
        return "", fmt.Errorf("model %s not found: %w", modelID, err)
    }

    // Generate
    result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
        Model:  model,
        Prompt: prompt,
    })
    if err != nil {
        return "", err
    }

    return result.Text, nil
}

// Usage
text, err := generateWithProvider("openai", "gpt-4", "Hello!")
```

### Multi-Provider Setup

```go
func setupProviders(config map[string]string) error {
    providers := map[string]provider.Provider{
        "openai": openai.New(openai.Config{
            APIKey: config["openai_key"],
        }),
        "anthropic": anthropic.New(anthropic.Config{
            APIKey: config["anthropic_key"],
        }),
        "google": google.New(google.Config{
            APIKey: config["google_key"],
        }),
    }

    for name, p := range providers {
        registry.RegisterProvider(name, p)
    }

    return nil
}

// Usage
config := map[string]string{
    "openai_key":    os.Getenv("OPENAI_API_KEY"),
    "anthropic_key": os.Getenv("ANTHROPIC_API_KEY"),
    "google_key":    os.Getenv("GOOGLE_GENERATIVE_AI_API_KEY"),
}

if err := setupProviders(config); err != nil {
    log.Fatal(err)
}
```

### Provider Fallback

```go
func generateWithFallback(modelConfigs []struct{ Provider, Model string }, prompt string) (string, error) {
    var lastErr error

    for _, cfg := range modelConfigs {
        p, err := registry.GetProvider(cfg.Provider)
        if err != nil {
            lastErr = err
            continue
        }

        model, err := p.LanguageModel(cfg.Model)
        if err != nil {
            lastErr = err
            continue
        }

        result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
            Model:  model,
            Prompt: prompt,
        })
        if err != nil {
            lastErr = err
            continue
        }

        return result.Text, nil
    }

    return "", fmt.Errorf("all providers failed: %w", lastErr)
}

// Usage with fallback chain
configs := []struct{ Provider, Model string }{
    {"openai", "gpt-4"},
    {"anthropic", "claude-3-opus"},
    {"google", "gemini-pro"},
}

text, err := generateWithFallback(configs, "Hello!")
```

## Error Handling

```go
_, err := registry.GetProvider("unknown")
if err != nil {
    var notFound *registry.NoSuchProviderError
    if errors.As(err, &notFound) {
        log.Printf("Provider %q is not registered (available: %v)", notFound.ProviderID, notFound.AvailableProviders)
    } else {
        log.Fatal("Registry error:", err)
    }
}
```

## See Also

- [Custom Provider](https://goaisdk.com/docs/reference/providers/custom-provider.md) - Implementing custom providers
- [LanguageModel Interface](https://goaisdk.com/docs/reference/providers/language-model.md) - Language model interface
- [Provider Guide](https://goaisdk.com/docs/providers/overview.md) - Provider overview
