diff --git a/cmd/gateway_providers.go b/cmd/gateway_providers.go index 80702bfb..c4e365a9 100644 --- a/cmd/gateway_providers.go +++ b/cmd/gateway_providers.go @@ -41,7 +41,9 @@ func registerProviders(registry *providers.Registry, cfg *config.Config) { } if cfg.Providers.OpenRouter.APIKey != "" { - registry.Register(providers.NewOpenAIProvider("openrouter", cfg.Providers.OpenRouter.APIKey, "https://openrouter.ai/api/v1", "anthropic/claude-sonnet-4-5-20250929")) + orProv := providers.NewOpenAIProvider("openrouter", cfg.Providers.OpenRouter.APIKey, "https://openrouter.ai/api/v1", "anthropic/claude-sonnet-4-5-20250929") + orProv.WithSiteInfo("https://goclaw.sh", "GoClaw") + registry.Register(orProv) slog.Info("registered provider", "name", "openrouter") } @@ -404,6 +406,9 @@ func registerProvidersFromDB(registry *providers.Registry, provStore store.Provi if p.ProviderType == store.ProviderMiniMax { prov.WithChatPath("/text/chatcompletion_v2") } + if p.ProviderType == store.ProviderOpenRouter { + prov.WithSiteInfo("https://goclaw.sh", "GoClaw") + } registry.RegisterForTenant(p.TenantID, prov) } slog.Info("registered provider from DB", "name", p.Name) diff --git a/internal/providers/openai.go b/internal/providers/openai.go index fcd057f4..8f4f4a56 100644 --- a/internal/providers/openai.go +++ b/internal/providers/openai.go @@ -27,6 +27,8 @@ type OpenAIProvider struct { authPrefix string // auth header prefix, defaults to "Bearer " if empty defaultModel string providerType string // DB provider_type (e.g. "gemini_native", "openai", "minimax_native") + siteURL string // optional site URL for provider identification (e.g. OpenRouter HTTP-Referer) + siteTitle string // optional site title for provider identification (e.g. OpenRouter X-Title) client *http.Client retryConfig RetryConfig } @@ -114,6 +116,14 @@ func (p *OpenAIProvider) WithAuthPrefix(prefix string) *OpenAIProvider { return p } +// WithSiteInfo sets site identification headers sent with API requests. +// Used by OpenRouter for rankings (HTTP-Referer, X-Title). +func (p *OpenAIProvider) WithSiteInfo(url, title string) *OpenAIProvider { + p.siteURL = url + p.siteTitle = title + return p +} + func (p *OpenAIProvider) Name() string { return p.name } func (p *OpenAIProvider) DefaultModel() string { return p.defaultModel } func (p *OpenAIProvider) SupportsThinking() bool { return true } @@ -576,6 +586,13 @@ func (p *OpenAIProvider) doRequest(ctx context.Context, body any) (io.ReadCloser } httpReq.Header.Set("Authorization", prefix+p.apiKey) } + // OpenRouter identification headers for rankings/analytics + if p.siteURL != "" { + httpReq.Header.Set("HTTP-Referer", p.siteURL) + } + if p.siteTitle != "" { + httpReq.Header.Set("X-Title", p.siteTitle) + } resp, err := p.client.Do(httpReq) if err != nil {