Skip to content

Commit 813fe08

Browse files
committed
Merge upstream/main (v6.9.27+ upstream updates)
Integrate 18 upstream commits from router-for-me/CLIProxyAPI main, including: - codex: backfill streaming response output, avoid repeated output patches - api: HEAD support for /healthz, custom Host header forwarding - auth: refresh backoff for ineffective token updates (router-for-me#2830) - executor: drop obsolete context-1m-2025-08-07 beta header (router-for-me#2866) - antigravity: strip billing header, cap max output tokens, session-affinity - chore: remove iFlow-related modules and GPT-5 stale registry entries - models: add Claude Opus 4.7 registry entry Conflict resolutions: - README.md / README_CN.md: kept Plus-specific minimal README. - README_JA.md: kept Plus deletion (837afff), drop upstream VisionCoder edits. - claude_executor.go: dropped public X-CPA-CLAUDE-1M header path (router-for-me#2866 fix), but preserved gitlab_duo_force_context_1m attribute for GitLab Duo's internal Anthropic gateway, which still relies on the 1M beta header. - conductor.go: kept Plus's shorter refreshFailureBackoff (1m) and the "preserve Authenticator-set NextRefreshAfter" philosophy, while adding upstream's new refreshIneffectiveBackoff (30s) guard against tight-looping when a refresh leaves the auth still needing refresh.
2 parents 210f7a6 + 8ced7a5 commit 813fe08

18 files changed

Lines changed: 957 additions & 123 deletions

assets/visioncoder.png

152 KB
Loading
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
package management
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
8+
"github.com/router-for-me/CLIProxyAPI/v6/internal/watcher/synthesizer"
9+
)
10+
11+
type geminiKeyWithAuthIndex struct {
12+
config.GeminiKey
13+
AuthIndex string `json:"auth-index,omitempty"`
14+
}
15+
16+
type claudeKeyWithAuthIndex struct {
17+
config.ClaudeKey
18+
AuthIndex string `json:"auth-index,omitempty"`
19+
}
20+
21+
type codexKeyWithAuthIndex struct {
22+
config.CodexKey
23+
AuthIndex string `json:"auth-index,omitempty"`
24+
}
25+
26+
type vertexCompatKeyWithAuthIndex struct {
27+
config.VertexCompatKey
28+
AuthIndex string `json:"auth-index,omitempty"`
29+
}
30+
31+
type openAICompatibilityAPIKeyWithAuthIndex struct {
32+
config.OpenAICompatibilityAPIKey
33+
AuthIndex string `json:"auth-index,omitempty"`
34+
}
35+
36+
type openAICompatibilityWithAuthIndex struct {
37+
Name string `json:"name"`
38+
Priority int `json:"priority,omitempty"`
39+
Prefix string `json:"prefix,omitempty"`
40+
BaseURL string `json:"base-url"`
41+
APIKeyEntries []openAICompatibilityAPIKeyWithAuthIndex `json:"api-key-entries,omitempty"`
42+
Models []config.OpenAICompatibilityModel `json:"models,omitempty"`
43+
Headers map[string]string `json:"headers,omitempty"`
44+
AuthIndex string `json:"auth-index,omitempty"`
45+
}
46+
47+
func (h *Handler) liveAuthIndexByID() map[string]string {
48+
out := map[string]string{}
49+
if h == nil {
50+
return out
51+
}
52+
h.mu.Lock()
53+
manager := h.authManager
54+
h.mu.Unlock()
55+
if manager == nil {
56+
return out
57+
}
58+
// authManager.List() returns clones, so EnsureIndex only affects these copies.
59+
for _, auth := range manager.List() {
60+
if auth == nil {
61+
continue
62+
}
63+
id := strings.TrimSpace(auth.ID)
64+
if id == "" {
65+
continue
66+
}
67+
idx := strings.TrimSpace(auth.Index)
68+
if idx == "" {
69+
idx = auth.EnsureIndex()
70+
}
71+
if idx == "" {
72+
continue
73+
}
74+
out[id] = idx
75+
}
76+
return out
77+
}
78+
79+
func (h *Handler) geminiKeysWithAuthIndex() []geminiKeyWithAuthIndex {
80+
if h == nil {
81+
return nil
82+
}
83+
liveIndexByID := h.liveAuthIndexByID()
84+
85+
h.mu.Lock()
86+
defer h.mu.Unlock()
87+
if h.cfg == nil {
88+
return nil
89+
}
90+
91+
idGen := synthesizer.NewStableIDGenerator()
92+
out := make([]geminiKeyWithAuthIndex, len(h.cfg.GeminiKey))
93+
for i := range h.cfg.GeminiKey {
94+
entry := h.cfg.GeminiKey[i]
95+
authIndex := ""
96+
if key := strings.TrimSpace(entry.APIKey); key != "" {
97+
id, _ := idGen.Next("gemini:apikey", key, entry.BaseURL)
98+
authIndex = liveIndexByID[id]
99+
}
100+
out[i] = geminiKeyWithAuthIndex{
101+
GeminiKey: entry,
102+
AuthIndex: authIndex,
103+
}
104+
}
105+
return out
106+
}
107+
108+
func (h *Handler) claudeKeysWithAuthIndex() []claudeKeyWithAuthIndex {
109+
if h == nil {
110+
return nil
111+
}
112+
liveIndexByID := h.liveAuthIndexByID()
113+
114+
h.mu.Lock()
115+
defer h.mu.Unlock()
116+
if h.cfg == nil {
117+
return nil
118+
}
119+
120+
idGen := synthesizer.NewStableIDGenerator()
121+
out := make([]claudeKeyWithAuthIndex, len(h.cfg.ClaudeKey))
122+
for i := range h.cfg.ClaudeKey {
123+
entry := h.cfg.ClaudeKey[i]
124+
authIndex := ""
125+
if key := strings.TrimSpace(entry.APIKey); key != "" {
126+
id, _ := idGen.Next("claude:apikey", key, entry.BaseURL)
127+
authIndex = liveIndexByID[id]
128+
}
129+
out[i] = claudeKeyWithAuthIndex{
130+
ClaudeKey: entry,
131+
AuthIndex: authIndex,
132+
}
133+
}
134+
return out
135+
}
136+
137+
func (h *Handler) codexKeysWithAuthIndex() []codexKeyWithAuthIndex {
138+
if h == nil {
139+
return nil
140+
}
141+
liveIndexByID := h.liveAuthIndexByID()
142+
143+
h.mu.Lock()
144+
defer h.mu.Unlock()
145+
if h.cfg == nil {
146+
return nil
147+
}
148+
149+
idGen := synthesizer.NewStableIDGenerator()
150+
out := make([]codexKeyWithAuthIndex, len(h.cfg.CodexKey))
151+
for i := range h.cfg.CodexKey {
152+
entry := h.cfg.CodexKey[i]
153+
authIndex := ""
154+
if key := strings.TrimSpace(entry.APIKey); key != "" {
155+
id, _ := idGen.Next("codex:apikey", key, entry.BaseURL)
156+
authIndex = liveIndexByID[id]
157+
}
158+
out[i] = codexKeyWithAuthIndex{
159+
CodexKey: entry,
160+
AuthIndex: authIndex,
161+
}
162+
}
163+
return out
164+
}
165+
166+
func (h *Handler) vertexCompatKeysWithAuthIndex() []vertexCompatKeyWithAuthIndex {
167+
if h == nil {
168+
return nil
169+
}
170+
liveIndexByID := h.liveAuthIndexByID()
171+
172+
h.mu.Lock()
173+
defer h.mu.Unlock()
174+
if h.cfg == nil {
175+
return nil
176+
}
177+
178+
idGen := synthesizer.NewStableIDGenerator()
179+
out := make([]vertexCompatKeyWithAuthIndex, len(h.cfg.VertexCompatAPIKey))
180+
for i := range h.cfg.VertexCompatAPIKey {
181+
entry := h.cfg.VertexCompatAPIKey[i]
182+
id, _ := idGen.Next("vertex:apikey", entry.APIKey, entry.BaseURL, entry.ProxyURL)
183+
authIndex := liveIndexByID[id]
184+
out[i] = vertexCompatKeyWithAuthIndex{
185+
VertexCompatKey: entry,
186+
AuthIndex: authIndex,
187+
}
188+
}
189+
return out
190+
}
191+
192+
func (h *Handler) openAICompatibilityWithAuthIndex() []openAICompatibilityWithAuthIndex {
193+
if h == nil {
194+
return nil
195+
}
196+
liveIndexByID := h.liveAuthIndexByID()
197+
198+
h.mu.Lock()
199+
defer h.mu.Unlock()
200+
if h.cfg == nil {
201+
return nil
202+
}
203+
204+
normalized := normalizedOpenAICompatibilityEntries(h.cfg.OpenAICompatibility)
205+
out := make([]openAICompatibilityWithAuthIndex, len(normalized))
206+
idGen := synthesizer.NewStableIDGenerator()
207+
for i := range normalized {
208+
entry := normalized[i]
209+
providerName := strings.ToLower(strings.TrimSpace(entry.Name))
210+
if providerName == "" {
211+
providerName = "openai-compatibility"
212+
}
213+
idKind := fmt.Sprintf("openai-compatibility:%s", providerName)
214+
215+
response := openAICompatibilityWithAuthIndex{
216+
Name: entry.Name,
217+
Priority: entry.Priority,
218+
Prefix: entry.Prefix,
219+
BaseURL: entry.BaseURL,
220+
Models: entry.Models,
221+
Headers: entry.Headers,
222+
AuthIndex: "",
223+
}
224+
if len(entry.APIKeyEntries) == 0 {
225+
id, _ := idGen.Next(idKind, entry.BaseURL)
226+
response.AuthIndex = liveIndexByID[id]
227+
} else {
228+
response.APIKeyEntries = make([]openAICompatibilityAPIKeyWithAuthIndex, len(entry.APIKeyEntries))
229+
for j := range entry.APIKeyEntries {
230+
apiKeyEntry := entry.APIKeyEntries[j]
231+
id, _ := idGen.Next(idKind, apiKeyEntry.APIKey, entry.BaseURL, apiKeyEntry.ProxyURL)
232+
response.APIKeyEntries[j] = openAICompatibilityAPIKeyWithAuthIndex{
233+
OpenAICompatibilityAPIKey: apiKeyEntry,
234+
AuthIndex: liveIndexByID[id],
235+
}
236+
}
237+
}
238+
out[i] = response
239+
}
240+
return out
241+
}

0 commit comments

Comments
 (0)