# Custom Provider Guide

Learn how to implement custom AI providers for the Go-AI SDK.

## Provider Interface

```go
type Provider interface {
    Name() string

    LanguageModel(modelID string) (LanguageModel, error)
    EmbeddingModel(modelID string) (EmbeddingModel, error)
    ImageModel(modelID string) (ImageModel, error)
    SpeechModel(modelID string) (SpeechModel, error)
    TranscriptionModel(modelID string) (TranscriptionModel, error)
    RerankingModel(modelID string) (RerankingModel, error)
}
```

## Implementing a Language Model

```go
package main

import (
    "context"
    "fmt"

    "github.com/digitallysavvy/go-ai/pkg/provider"
    "github.com/digitallysavvy/go-ai/pkg/provider/types"
)

type CustomLanguageModel struct {
    modelID  string
    apiKey   string
}

func (m *CustomLanguageModel) SpecificationVersion() string {
    return "v3"
}

func (m *CustomLanguageModel) Provider() string {
    return "custom"
}

func (m *CustomLanguageModel) ModelID() string {
    return m.modelID
}

func (m *CustomLanguageModel) SupportsTools() bool {
    return false
}

func (m *CustomLanguageModel) SupportsStructuredOutput() bool {
    return false
}

func (m *CustomLanguageModel) SupportsImageInput() bool {
    return false
}

func int64Ptr(v int64) *int64 {
    return &v
}

func (m *CustomLanguageModel) DoGenerate(ctx context.Context, opts *provider.GenerateOptions) (*types.GenerateResult, error) {
    // Implement your API call here
    return &types.GenerateResult{
        Text:         "Generated text",
        FinishReason: types.FinishReasonStop,
        Usage: types.Usage{
            InputTokens:  int64Ptr(10),
            OutputTokens: int64Ptr(20),
            TotalTokens:  int64Ptr(30),
        },
    }, nil
}

func (m *CustomLanguageModel) DoStream(ctx context.Context, opts *provider.GenerateOptions) (provider.TextStream, error) {
    return nil, fmt.Errorf("streaming not implemented")
}
```

## Implementing a Provider

```go
type CustomProvider struct {
    apiKey string
}

func NewCustomProvider(apiKey string) *CustomProvider {
    return &CustomProvider{apiKey: apiKey}
}

func (p *CustomProvider) Name() string {
    return "custom"
}

func (p *CustomProvider) LanguageModel(modelID string) (provider.LanguageModel, error) {
    return &CustomLanguageModel{
        modelID: modelID,
        apiKey:  p.apiKey,
    }, nil
}

func (p *CustomProvider) EmbeddingModel(modelID string) (provider.EmbeddingModel, error) {
    return nil, fmt.Errorf("embedding models not supported")
}

func (p *CustomProvider) ImageModel(modelID string) (provider.ImageModel, error) {
    return nil, fmt.Errorf("image models not supported")
}

func (p *CustomProvider) SpeechModel(modelID string) (provider.SpeechModel, error) {
    return nil, fmt.Errorf("speech models not supported")
}

func (p *CustomProvider) TranscriptionModel(modelID string) (provider.TranscriptionModel, error) {
    return nil, fmt.Errorf("transcription models not supported")
}

func (p *CustomProvider) RerankingModel(modelID string) (provider.RerankingModel, error) {
    return nil, fmt.Errorf("reranking models not supported")
}
```

## Using Your Custom Provider

```go
func main() {
    // Create provider
    provider := NewCustomProvider("your-api-key")

    // Get model
    model, err := provider.LanguageModel("custom-model")
    if err != nil {
        log.Fatal(err)
    }

    // Use with ai package
    result, err := ai.GenerateText(context.Background(), ai.GenerateTextOptions{
        Model:  model,
        Prompt: "Hello!",
    })
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(result.Text)
}
```

## Implementing Streaming

`provider.TextStream` only has `Next`, `Err`, and `Close` — it does **not**
implement `io.Reader`. Don't add a `Read` method to a stream wrapper; consume
chunks with `Next()` in a loop, as shown in the [`provider.TextStream` doc
comment](https://pkg.go.dev/github.com/digitallysavvy/go-ai/pkg/provider#TextStream).

```go
type CustomTextStream struct {
    chunks chan *provider.StreamChunk
    err    error
}

func NewCustomTextStream() *CustomTextStream {
    return &CustomTextStream{
        chunks: make(chan *provider.StreamChunk, 10),
    }
}

func (s *CustomTextStream) Next() (*provider.StreamChunk, error) {
    chunk, ok := <-s.chunks
    if !ok {
        return nil, io.EOF
    }
    return chunk, nil
}

func (s *CustomTextStream) Close() error {
    close(s.chunks)
    return nil
}

func (s *CustomTextStream) Err() error {
    return s.err
}
```

## See Also

- [LanguageModel](https://goaisdk.com/docs/reference/providers/language-model.md) - Language model interface
- [EmbeddingModel](https://goaisdk.com/docs/reference/providers/embedding-model.md) - Embedding model interface
- [Provider Registry](https://goaisdk.com/docs/reference/registry/provider-registry.md) - Registering providers
