Principle:Helicone Helicone Provider Registration
| Knowledge Sources | |
|---|---|
| Domains | LLM Gateway, Service Registry, Software Architecture |
| Last Updated | 2026-02-14 00:00 GMT |
Overview
Provider registration is the process of mapping provider name strings to their concrete handler instances in a central lookup table, enabling the gateway to resolve any provider by name at runtime.
Description
An LLM gateway that supports many providers needs a single, authoritative place where provider names are associated with their handler implementations. Without this, resolving "which object handles requests for provider X?" would require scattered conditional logic or dynamic class loading throughout the codebase.
The Provider Registration principle solves this by maintaining a flat, typed dictionary that maps each provider's canonical string name (e.g., "anthropic", "openai", "bedrock") to a singleton instance of its corresponding handler class. Because the handlers are stateless, a single shared instance per provider is safe and avoids unnecessary object allocation.
This registry serves as the single source of truth for "which providers exist" and the TypeScript union type of all valid provider names is derived directly from the registry's keys, guaranteeing compile-time safety when referencing providers elsewhere in the codebase.
Usage
Use the provider registration pattern when:
- The gateway needs to resolve a provider handler from a string name at runtime
- You are adding a new provider and need to make it discoverable by the routing layer
- You need a compile-time type representing all valid provider names
Theoretical Basis
Provider Registration is an application of the Service Locator pattern. A central registry holds references to service implementations keyed by a well-known identifier. Consumers query the registry by key to obtain the implementation, decoupling them from the concrete classes.
The registry also applies the Singleton pattern: because provider handlers are stateless (pure methods, no mutable fields), a single instance per provider is shared application-wide, reducing memory overhead and ensuring consistent behavior.
Pseudocode:
registry = {
"provider-a": new ProviderAHandler(),
"provider-b": new ProviderBHandler(),
...
}
type ProviderName = keyof registry
function getProvider(name: ProviderName) -> BaseProvider:
return registry[name]
Deriving the ProviderName type from the registry keys ensures that any code referencing a provider name is validated at compile time. Adding a new entry to the registry automatically extends the type.