feat(providers): add OpenRouter identification headers (#705)

* fix(ci): skip CI condition in semantic-release for main branch

go-semantic-release auto-detects the default branch from GitHub API
(which is dev), but releases are cut from main. The CI condition
rejects runs on non-default branches. Use --no-ci to bypass this
check since the workflow already gates on push to main.

* docs: document CI/CD pipelines, release flow, and v2.66.0 changelog

- CLAUDE.md: add CI/CD & Releases section with workflow table, tag
  patterns, Docker variants, beta/desktop release commands
- CONTRIBUTING.md: expand Releases section with standard (auto),
  beta (manual tag), and desktop release workflows
- docs/17-changelog.md: add v2.66.0 entry covering IDOR fix, BytePlus
  provider, per-agent grants, beta pipeline, and CI fixes

* fix(telegram): handle group-to-supergroup migration seamlessly

When a Telegram group upgrades to a supergroup, the chat ID changes and
all existing references become stale. This caused send failures (400),
orphaned sessions, and required manual re-pairing.

Add dual-path migration handling:
- Proactive: intercept inbound MigrateToChatID before isServiceMessage
- Reactive: detect 400 + MigrateToChatID on send, migrate DB, retry

DB migration updates in a single transaction (scoped by tenant + channel):
- paired_devices: sender_id, chat_id
- sessions: session_key, user_id
- channel_contacts: sender_id
- channel_pending_messages: history_key

Also invalidates in-memory caches (approvedGroups, pairingReplySent,
groupHistory) and handles media sends via migration retry in Send().

* feat(providers): add OpenRouter identification headers (#704)

Add HTTP-Referer and X-Title headers to OpenRouter API requests
for rankings and analytics visibility on openrouter.ai.

---------

Co-authored-by: viettranx <viettranx@gmail.com>
This commit is contained in:
Duy /zuey/andviettranx authored and GitHub committed 2026-04-05 18:51:17 +07:00
1 parent 5987349b0d
commit 2801f0a978
2 files changed
+23 -1

No files matched your search

+6 -1
View File
@@ -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)
+17
View File
@@ -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 {