# Understanding the CCX Provider Interface: Abstracting AI Upstreams in Go

> The CCX Provider interface standardizes AI upstreaming in Go. Discover how it unifies HTTP requests, normalizes responses, and handles SSE streaming for diverse model providers.

- Repository: [Benedict King/ccx](https://github.com/BenedictKing/ccx)
- Tags: deep-dive
- Published: 2026-05-29

---

**The CCX Provider interface is a Go contract defined in [`backend-go/internal/providers/provider.go`](https://github.com/BenedictKing/ccx/blob/main/backend-go/internal/providers/provider.go) that standardizes how multi-upstream AI proxies convert HTTP requests, normalize responses into a unified ClaudeResponse format, and handle Server-Sent Event (SSE) streaming across diverse model providers.**

The BenedictKing/ccx repository implements a multi-upstream AI API proxy that routes requests between various language model providers. At its architectural core, the **CCX Provider interface** defines the behavioral contract that every upstream service must satisfy, enabling seamless integration of OpenAI, Gemini, Claude, and other endpoints through a common abstraction layer.

## Interface Definition and Core Methods

The `Provider` interface resides in [`backend-go/internal/providers/provider.go`](https://github.com/BenedictKing/ccx/blob/main/backend-go/internal/providers/provider.go) and specifies three essential methods that each concrete implementation must satisfy:

```go
type Provider interface {
    ConvertToProviderRequest(c *gin.Context, upstream *config.UpstreamConfig, apiKey string) (*http.Request, []byte, error)
    ConvertToClaudeResponse(providerResp *types.ProviderResponse) (*types.ClaudeResponse, error)
    HandleStreamResponse(body io.ReadCloser) (<-chan string, <-chan error, error)
}

```

### ConvertToProviderRequest

**`ConvertToProviderRequest`** transforms incoming Gin HTTP contexts into provider-specific `http.Request` objects. This method accepts the Gin context, upstream configuration, and API key, returning the constructed request alongside the raw request body bytes for audit logging. The implementation uses helper functions like `getRequestBodyBytes` to cache request payloads before forwarding to upstream endpoints.

### ConvertToClaudeResponse

**`ConvertToClaudeResponse`** normalizes diverse upstream response formats into CCX's internal `ClaudeResponse` structure defined in [`backend-go/internal/types/types.go`](https://github.com/BenedictKing/ccx/blob/main/backend-go/internal/types/types.go). Regardless of whether the upstream returns OpenAI-compatible JSON or Gemini-specific formatting, this method ensures the proxy returns a standardized structure that downstream consumers can process uniformly.

### HandleStreamResponse

**`HandleStreamResponse`** manages Server-Sent Events (SSE) streaming by reading from the response body and converting events into string channels. The method returns two read-only channels: one for SSE data strings and one for errors, allowing concurrent processing of streaming responses while normalizing `data:` and `event:` fields through functions like `normalizeSSEFieldLine`.

## Factory Pattern and Provider Registration

CCX utilizes a factory function called `GetProvider` (located in [`backend-go/internal/providers/provider.go`](https://github.com/BenedictKing/ccx/blob/main/backend-go/internal/providers/provider.go)) to instantiate concrete providers without exposing implementation details to callers. This function maps `serviceType` strings such as `openai`, `gemini`, `claude`, and `responses` to their respective implementations:

```go
// Conceptual usage within the proxy layer
upstream := config.LoadUpstream("openai")
provider := providers.GetProvider(upstream.Service) // Returns OpenAIProvider instance

```

Concrete implementations including `OpenAIProvider`, `GeminiProvider`, `ClaudeProvider`, and `ResponsesProvider` reside in separate files within the same package ([`openai.go`](https://github.com/BenedictKing/ccx/blob/main/openai.go), [`gemini.go`](https://github.com/BenedictKing/ccx/blob/main/gemini.go), [`claude.go`](https://github.com/BenedictKing/ccx/blob/main/claude.go), [`responses.go`](https://github.com/BenedictKing/ccx/blob/main/responses.go)), each satisfying the three-method contract.

## Practical Usage Example

The following example demonstrates the complete request lifecycle within a Gin handler, utilizing the CCX Provider interface to proxy requests:

```go
func handleProxy(c *gin.Context) {
    upstream := config.LoadUpstream("openai")
    apiKey := c.GetHeader("Authorization")
    
    // Retrieve appropriate provider via factory
    prov := providers.GetProvider(upstream.Service)
    
    // Convert Gin request to upstream format
    req, rawBody, err := prov.ConvertToProviderRequest(c, upstream, apiKey)
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    
    // Execute upstream request
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
        return
    }
    defer resp.Body.Close()
    
    // Wrap response in generic ProviderResponse
    providerResp := &types.ProviderResponse{
        StatusCode: resp.StatusCode,
        Headers:    resp.Header,
        Body:       mustReadAll(resp.Body),
        Stream:     strings.Contains(resp.Header.Get("Content-Type"), "text/event-stream"),
    }
    
    // Normalize to unified ClaudeResponse
    claudeResp, err := prov.ConvertToClaudeResponse(providerResp)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    
    c.JSON(claudeResp.Status, claudeResp.Body)
}

```

## Implementing Custom Providers

To extend CCX with additional AI services, developers implement the `Provider` interface. Here is a minimal `MockProvider` demonstrating the required method signatures:

```go
type MockProvider struct{}

func (m *MockProvider) ConvertToProviderRequest(c *gin.Context, _ *config.UpstreamConfig, _ string) (*http.Request, []byte, error) {
    r, _ := http.NewRequest(http.MethodPost, "https://mock.api/v1", bytes.NewReader([]byte(`{}`)))
    return r, []byte(`{}`), nil
}

func (m *MockProvider) ConvertToClaudeResponse(pr *types.ProviderResponse) (*types.ClaudeResponse, error) {
    return &types.ClaudeResponse{
        Status: http.StatusOK,
        Body:   map[string]any{"mock": "ok"},
    }, nil
}

func (m *MockProvider) HandleStreamResponse(body io.ReadCloser) (<-chan string, <-chan error, error) {
    ch := make(chan string)
    errCh := make(chan error)
    close(ch)
    close(errCh)
    return ch, errCh, nil
}

```

Registration with the factory enables automatic routing:

```go
func init() {
    providers.Register("mock", func() providers.Provider { return &MockProvider{} })
}

```

## Key Source Files and Architecture

| File Path | Purpose |
|-----------|---------|
| [`backend-go/internal/providers/provider.go`](https://github.com/BenedictKing/ccx/blob/main/backend-go/internal/providers/provider.go) | Defines the `Provider` interface, `GetProvider` factory, and request caching utilities |
| [`backend-go/internal/types/types.go`](https://github.com/BenedictKing/ccx/blob/main/backend-go/internal/types/types.go) | Contains `ProviderRequest`, `ProviderResponse`, and `ClaudeResponse` type definitions |
| [`backend-go/internal/providers/openai.go`](https://github.com/BenedictKing/ccx/blob/main/backend-go/internal/providers/openai.go) | OpenAI API implementation of the Provider interface |
| [`backend-go/internal/providers/gemini.go`](https://github.com/BenedictKing/ccx/blob/main/backend-go/internal/providers/gemini.go) | Google Gemini implementation |
| [`backend-go/internal/providers/claude.go`](https://github.com/BenedictKing/ccx/blob/main/backend-go/internal/providers/claude.go) | Anthropic Claude direct implementation |
| [`backend-go/internal/providers/responses.go`](https://github.com/BenedictKing/ccx/blob/main/backend-go/internal/providers/responses.go) | Claude Responses API implementation with session management |
| [`backend-go/internal/config/upstream.go`](https://github.com/BenedictKing/ccx/blob/main/backend-go/internal/config/upstream.go) | `UpstreamConfig` structure for provider-specific settings |

## Summary

- The **CCX Provider interface** consists of three methods: `ConvertToProviderRequest`, `ConvertToClaudeResponse`, and `HandleStreamResponse`, defining the complete lifecycle of proxying AI requests.
- **Request transformation** converts Gin HTTP contexts into provider-specific requests while preserving raw bodies for logging purposes.
- **Response normalization** unifies diverse upstream formats into the internal `ClaudeResponse` structure regardless of the source provider.
- **Stream handling** abstracts SSE parsing into channels, standardizing real-time response processing across different AI services.
- The **factory pattern** via `GetProvider` decouples routing logic from implementation details, allowing seamless addition of new upstream providers.

## Frequently Asked Questions

### What is the purpose of the CCX Provider interface?

The CCX Provider interface serves as an architectural abstraction layer that enables the BenedictKing/ccx proxy to communicate with multiple AI service providers through a unified contract. By implementing this three-method interface, developers can add support for new language model APIs without modifying the core routing or response handling logic of the proxy.

### How does the Provider interface handle streaming responses?

The `HandleStreamResponse` method abstracts Server-Sent Events (SSE) processing by consuming an `io.ReadCloser` and returning two channels: one for data strings and one for errors. This design allows the proxy to process streaming responses from providers like OpenAI or Claude concurrently, normalizing event fields such as `data:` and `event:` before downstream delivery.

### Where are the concrete Provider implementations located in the repository?

Concrete implementations reside in `backend-go/internal/providers/` with service-specific files including [`openai.go`](https://github.com/BenedictKing/ccx/blob/main/openai.go), [`gemini.go`](https://github.com/BenedictKing/ccx/blob/main/gemini.go), [`claude.go`](https://github.com/BenedictKing/ccx/blob/main/claude.go), and [`responses.go`](https://github.com/BenedictKing/ccx/blob/main/responses.go). Each file contains a struct (e.g., `OpenAIProvider`) that satisfies the `Provider` interface methods, handling provider-specific request formatting and response parsing while conforming to the standardized contract defined in [`provider.go`](https://github.com/BenedictKing/ccx/blob/main/provider.go).

### What types does the Provider interface use for request and response handling?

The interface utilizes types defined in [`backend-go/internal/types/types.go`](https://github.com/BenedictKing/ccx/blob/main/backend-go/internal/types/types.go), specifically `ProviderResponse` for wrapping upstream HTTP responses and `ClaudeResponse` for the internal standardized format. The `ConvertToProviderRequest` method returns raw `[]byte` for request body logging alongside the constructed `*http.Request`, while `ConvertToClaudeResponse` transforms the provider-specific `ProviderResponse` into the unified `ClaudeResponse` structure.