How to Configure Channel Model Mappings for Automatic Model Name Rewriting in AxonHub
AxonHub rewrites incoming model names before forwarding requests to providers by applying modelMappings defined in ChannelSettings or API-key profiles, then restores the original name in the response via inbound and outbound middleware.
AxonHub is an open-source LLM gateway that normalizes requests across multiple providers. When you configure channel model mappings for automatic model name rewriting, you create aliases that translate client-facing model names into provider-specific identifiers without changing client code.
Understanding Model Mappings in AxonHub
A model mapping is a bidirectional translation rule that intercepts the model field in LLM requests.
What Model Mappings Do
Each mapping consists of two fields:
from: The model name sent by the client (e.g.,gpt-4).to: The actual model identifier required by the downstream provider (e.g.,claude-3-opus).
When a request arrives, the inbound middleware applyModelMapping (implemented in internal/server/orchestrator/model_mapper.go) checks the active API-key profile for mappings. If a match exists, the middleware replaces req.Model with the to value before forwarding the request. After the provider responds, the outbound middleware restores the original from value so the client remains unaware of the translation.
Where Mappings Are Stored
Mappings can be defined in two locations, checked in this order:
- API-key profile level –
APIKeyProfile.ModelMappings - Channel level –
ChannelSettings.ModelMappings
If no mapping exists at the API-key level, the hub falls back to the channel's own mappings.
Configuring Model Mappings
Channel-Level Configuration
Define mappings in the ChannelSettings struct located in internal/objects/channel.go (lines 94-98):
type ChannelSettings struct {
// ...
// ModelMappings add model alias for the model in the channels.
// e.g. {"from": "deepseek-chat", "to": "deepseek/deepseek-chat"}
ModelMappings []ModelMapping `json:"modelMappings"`
// ...
}
API-Key Profile Configuration
The same structure applies to API-key profiles. You can update these via the GraphQL API defined in internal/server/gql/generated.go (lines 2584-2589).
Mapping Logic and Pattern Matching
The ModelMapper in internal/server/orchestrator/model_mapper.go handles the translation logic.
Supported Matching Patterns
The matchesMapping function supports three pattern types:
- Exact strings – Literal match (e.g.,
gpt-4matches onlygpt-4). - Wildcards – Asterisks match any sequence (e.g.,
gpt-*matchesgpt-4,gpt-3.5-turbo). - Regular expressions – Full regex support via the cached
xregexp.MatchStringengine.
The Mapping Algorithm
The core logic resides in applyModelMapping (lines 48-56):
func (m *ModelMapper) applyModelMapping(mappings []objects.ModelMapping, model string) string {
for _, mapping := range mappings {
if m.matchesMapping(mapping.From, model) {
return mapping.To // rewrite
}
}
return model // no match → keep original
}
If multiple mappings match, the first one in the slice wins.
Model Visibility Configuration
Control how models appear in listing endpoints with two boolean flags defined in ChannelSettings:
hideOriginalModels– Whentrue, the downstream (to) model names are hidden from the model list; only client-facing aliases (from) appear.hideMappedModels– Whentrue, only the provider's native model names are displayed, hiding the aliases.
These flags affect the model enumeration returned to UI and CLI clients.
Practical Configuration Examples
JSON Channel Definition
Configure a DeepSeek channel with model mappings via JSON:
{
"name": "deepseek-channel",
"type": "deepseek",
"settings": {
"extraModelPrefix": "deepseek",
"modelMappings": [
{ "from": "deepseek-chat", "to": "deepseek/deepseek-chat" },
{ "from": "deepseek-reasoner", "to": "deepseek/deepseek-reasoner" }
],
"hideOriginalModels": false,
"hideMappedModels": true
}
}
Store this in the database via the admin UI or import using axoncli.
GraphQL Mutation for API-Key Profiles
Update an API-key profile with regex-based mappings:
mutation {
updateAPIKeyProfile(
id: "api-key-uuid",
profile: {
name: "my-profile",
modelMappings: [
{ from: "gpt-4", to: "claude-3-opus" },
{ from: "gpt-.*", to: "claude-3-opus" }
]
}
) { id }
}
The GraphQL resolver for modelMappings is defined in internal/server/gql/generated.go.
Go SDK Programmatic Configuration
Create a channel with mappings using the internal objects package:
import "github.com/looplj/axonhub/internal/objects"
ch := objects.Channel{
Name: "deepseek-channel",
Type: objects.ChannelTypeDeepseekAnthropic,
Settings: objects.ChannelSettings{
ModelMappings: []objects.ModelMapping{
{From: "deepseek-chat", To: "deepseek/deepseek-chat"},
{From: "deepseek-reasoner", To: "deepseek/deepseek-reasoner"},
},
HideOriginalModels: false,
HideMappedModels: true,
},
}
Persist the channel using biz.ChannelService.CreateChannel.
Runtime Middleware Flow
The following simplified excerpts from internal/server/orchestrator/model_mapper.go demonstrate the actual request transformation:
Inbound request processing:
func (m *apiKeyModelMappingMiddleware) OnInboundLlmRequest(ctx context.Context, req *llm.Request) (*llm.Request, error) {
original := req.Model
// Rewrite if mapping exists
mapped := m.inbound.state.ModelMapper.MapModel(ctx, m.inbound.state.APIKey, original)
if mapped != original {
req.Model = mapped
log.Debug(ctx, "model rewritten", log.String("from", original), log.String("to", mapped))
}
// Store original for outbound restoration
m.RequestModel = original
return req, nil
}
Outbound response restoration:
func (m *apiKeyModelMappingMiddleware) OnOutboundLlmResponse(ctx context.Context, resp *llm.Response) (*llm.Response, error) {
// Restore client-side name
m.inbound.state.ModelMapper.ReplaceResponseModel(resp, m.RequestModel)
return resp, nil
}
Summary
- Model mappings translate client-facing model names to provider-specific identifiers using
fromandtofields. - Configure mappings in
ChannelSettings.ModelMappings(internal/objects/channel.go) orAPIKeyProfile.ModelMappingsfor per-key overrides. - The
ModelMapperininternal/server/orchestrator/model_mapper.gohandles inbound rewriting and outbound restoration viaapplyModelMappingandReplaceResponseModel. - Pattern matching supports exact strings, wildcards (
*), and full regular expressions. - Visibility flags
hideOriginalModelsandhideMappedModelscontrol which names appear in model listings.
Frequently Asked Questions
How does AxonHub decide which mapping to apply when multiple patterns match?
AxonHub evaluates mappings in the order they appear in the ModelMappings slice and applies the first match found. The applyModelMapping function iterates sequentially through the array, so you should place more specific patterns (exact matches) before broader wildcard or regex patterns to ensure correct prioritization.
Can I use regular expressions in the from field of a model mapping?
Yes, the matchesMapping function in internal/server/orchestrator/model_mapper.go supports full regular expressions in addition to exact strings and wildcards (*). The implementation uses a cached regex engine (xregexp.MatchString) to evaluate patterns, allowing complex matching rules like gpt-.* to match any model starting with gpt-.
What happens if I enable both hideOriginalModels and hideMappedModels?
These boolean flags in ChannelSettings control model list visibility for UI and CLI clients. If you enable both hideOriginalModels and hideMappedModels, the channel will effectively hide all model names from listings—both the client-side aliases (from) and the provider-native names (to). Typically, you should enable only one flag depending on whether you want users to see aliases or native model names.
Is it possible to configure model mappings via environment variables or only through the API?
Based on the source code in internal/objects/channel.go and the GraphQL schema in internal/server/gql/generated.go, model mappings are configured through the ChannelSettings struct or APIKeyProfile objects persisted in the database. While you can use the Go SDK (biz.ChannelService.CreateChannel) or the axoncli tool to import JSON/YAML configurations, the underlying storage mechanism is the database via these API interfaces rather than direct environment variable parsing.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →