mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 10:18:42 +00:00
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:
1 parent
5987349b0d
commit
2801f0a978
2 files changed
+23
-1
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user