mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
301 lines
8.6 KiB
Go
301 lines
8.6 KiB
Go
package synthesizer
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/watcher/diff"
|
|
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
|
)
|
|
|
|
// ConfigSynthesizer generates Auth entries from configuration API keys.
|
|
// It handles Gemini, Claude, Codex, OpenAI-compat, and Vertex-compat providers.
|
|
type ConfigSynthesizer struct{}
|
|
|
|
// NewConfigSynthesizer creates a new ConfigSynthesizer instance.
|
|
func NewConfigSynthesizer() *ConfigSynthesizer {
|
|
return &ConfigSynthesizer{}
|
|
}
|
|
|
|
// Synthesize generates Auth entries from config API keys.
|
|
func (s *ConfigSynthesizer) Synthesize(ctx *SynthesisContext) ([]*coreauth.Auth, error) {
|
|
out := make([]*coreauth.Auth, 0, 32)
|
|
if ctx == nil || ctx.Config == nil {
|
|
return out, nil
|
|
}
|
|
|
|
// Gemini API Keys
|
|
out = append(out, s.synthesizeGeminiKeys(ctx)...)
|
|
// Claude API Keys
|
|
out = append(out, s.synthesizeClaudeKeys(ctx)...)
|
|
// Codex API Keys
|
|
out = append(out, s.synthesizeCodexKeys(ctx)...)
|
|
// OpenAI-compat
|
|
out = append(out, s.synthesizeOpenAICompat(ctx)...)
|
|
// Vertex-compat
|
|
out = append(out, s.synthesizeVertexCompat(ctx)...)
|
|
|
|
return out, nil
|
|
}
|
|
|
|
// synthesizeGeminiKeys creates Auth entries for Gemini API keys.
|
|
func (s *ConfigSynthesizer) synthesizeGeminiKeys(ctx *SynthesisContext) []*coreauth.Auth {
|
|
cfg := ctx.Config
|
|
now := ctx.Now
|
|
idGen := ctx.IDGenerator
|
|
|
|
out := make([]*coreauth.Auth, 0, len(cfg.GeminiKey))
|
|
for i := range cfg.GeminiKey {
|
|
entry := cfg.GeminiKey[i]
|
|
key := strings.TrimSpace(entry.APIKey)
|
|
if key == "" {
|
|
continue
|
|
}
|
|
prefix := strings.TrimSpace(entry.Prefix)
|
|
base := strings.TrimSpace(entry.BaseURL)
|
|
proxyURL := strings.TrimSpace(entry.ProxyURL)
|
|
id, token := idGen.Next("gemini:apikey", key, base)
|
|
attrs := map[string]string{
|
|
"source": fmt.Sprintf("config:gemini[%s]", token),
|
|
"api_key": key,
|
|
}
|
|
if base != "" {
|
|
attrs["base_url"] = base
|
|
}
|
|
if hash := diff.ComputeGeminiModelsHash(entry.Models); hash != "" {
|
|
attrs["models_hash"] = hash
|
|
}
|
|
addConfigHeadersToAttrs(entry.Headers, attrs)
|
|
a := &coreauth.Auth{
|
|
ID: id,
|
|
Provider: "gemini",
|
|
Label: "gemini-apikey",
|
|
Prefix: prefix,
|
|
Status: coreauth.StatusActive,
|
|
ProxyURL: proxyURL,
|
|
Attributes: attrs,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
ApplyAuthExcludedModelsMeta(a, cfg, entry.ExcludedModels, "apikey")
|
|
out = append(out, a)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// synthesizeClaudeKeys creates Auth entries for Claude API keys.
|
|
func (s *ConfigSynthesizer) synthesizeClaudeKeys(ctx *SynthesisContext) []*coreauth.Auth {
|
|
cfg := ctx.Config
|
|
now := ctx.Now
|
|
idGen := ctx.IDGenerator
|
|
|
|
out := make([]*coreauth.Auth, 0, len(cfg.ClaudeKey))
|
|
for i := range cfg.ClaudeKey {
|
|
ck := cfg.ClaudeKey[i]
|
|
key := strings.TrimSpace(ck.APIKey)
|
|
if key == "" {
|
|
continue
|
|
}
|
|
prefix := strings.TrimSpace(ck.Prefix)
|
|
base := strings.TrimSpace(ck.BaseURL)
|
|
id, token := idGen.Next("claude:apikey", key, base)
|
|
attrs := map[string]string{
|
|
"source": fmt.Sprintf("config:claude[%s]", token),
|
|
"api_key": key,
|
|
}
|
|
if base != "" {
|
|
attrs["base_url"] = base
|
|
}
|
|
if hash := diff.ComputeClaudeModelsHash(ck.Models); hash != "" {
|
|
attrs["models_hash"] = hash
|
|
}
|
|
addConfigHeadersToAttrs(ck.Headers, attrs)
|
|
proxyURL := strings.TrimSpace(ck.ProxyURL)
|
|
a := &coreauth.Auth{
|
|
ID: id,
|
|
Provider: "claude",
|
|
Label: "claude-apikey",
|
|
Prefix: prefix,
|
|
Status: coreauth.StatusActive,
|
|
ProxyURL: proxyURL,
|
|
Attributes: attrs,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
ApplyAuthExcludedModelsMeta(a, cfg, ck.ExcludedModels, "apikey")
|
|
out = append(out, a)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// synthesizeCodexKeys creates Auth entries for Codex API keys.
|
|
func (s *ConfigSynthesizer) synthesizeCodexKeys(ctx *SynthesisContext) []*coreauth.Auth {
|
|
cfg := ctx.Config
|
|
now := ctx.Now
|
|
idGen := ctx.IDGenerator
|
|
|
|
out := make([]*coreauth.Auth, 0, len(cfg.CodexKey))
|
|
for i := range cfg.CodexKey {
|
|
ck := cfg.CodexKey[i]
|
|
key := strings.TrimSpace(ck.APIKey)
|
|
if key == "" {
|
|
continue
|
|
}
|
|
prefix := strings.TrimSpace(ck.Prefix)
|
|
id, token := idGen.Next("codex:apikey", key, ck.BaseURL)
|
|
attrs := map[string]string{
|
|
"source": fmt.Sprintf("config:codex[%s]", token),
|
|
"api_key": key,
|
|
}
|
|
if ck.BaseURL != "" {
|
|
attrs["base_url"] = ck.BaseURL
|
|
}
|
|
if hash := diff.ComputeCodexModelsHash(ck.Models); hash != "" {
|
|
attrs["models_hash"] = hash
|
|
}
|
|
addConfigHeadersToAttrs(ck.Headers, attrs)
|
|
proxyURL := strings.TrimSpace(ck.ProxyURL)
|
|
a := &coreauth.Auth{
|
|
ID: id,
|
|
Provider: "codex",
|
|
Label: "codex-apikey",
|
|
Prefix: prefix,
|
|
Status: coreauth.StatusActive,
|
|
ProxyURL: proxyURL,
|
|
Attributes: attrs,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
ApplyAuthExcludedModelsMeta(a, cfg, ck.ExcludedModels, "apikey")
|
|
out = append(out, a)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// synthesizeOpenAICompat creates Auth entries for OpenAI-compatible providers.
|
|
func (s *ConfigSynthesizer) synthesizeOpenAICompat(ctx *SynthesisContext) []*coreauth.Auth {
|
|
cfg := ctx.Config
|
|
now := ctx.Now
|
|
idGen := ctx.IDGenerator
|
|
|
|
out := make([]*coreauth.Auth, 0)
|
|
for i := range cfg.OpenAICompatibility {
|
|
compat := &cfg.OpenAICompatibility[i]
|
|
prefix := strings.TrimSpace(compat.Prefix)
|
|
providerName := strings.ToLower(strings.TrimSpace(compat.Name))
|
|
if providerName == "" {
|
|
providerName = "openai-compatibility"
|
|
}
|
|
base := strings.TrimSpace(compat.BaseURL)
|
|
|
|
// Handle new APIKeyEntries format (preferred)
|
|
createdEntries := 0
|
|
for j := range compat.APIKeyEntries {
|
|
entry := &compat.APIKeyEntries[j]
|
|
key := strings.TrimSpace(entry.APIKey)
|
|
proxyURL := strings.TrimSpace(entry.ProxyURL)
|
|
idKind := fmt.Sprintf("openai-compatibility:%s", providerName)
|
|
id, token := idGen.Next(idKind, key, base, proxyURL)
|
|
attrs := map[string]string{
|
|
"source": fmt.Sprintf("config:%s[%s]", providerName, token),
|
|
"base_url": base,
|
|
"compat_name": compat.Name,
|
|
"provider_key": providerName,
|
|
}
|
|
if key != "" {
|
|
attrs["api_key"] = key
|
|
}
|
|
if hash := diff.ComputeOpenAICompatModelsHash(compat.Models); hash != "" {
|
|
attrs["models_hash"] = hash
|
|
}
|
|
addConfigHeadersToAttrs(compat.Headers, attrs)
|
|
a := &coreauth.Auth{
|
|
ID: id,
|
|
Provider: providerName,
|
|
Label: compat.Name,
|
|
Prefix: prefix,
|
|
Status: coreauth.StatusActive,
|
|
ProxyURL: proxyURL,
|
|
Attributes: attrs,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
out = append(out, a)
|
|
createdEntries++
|
|
}
|
|
// Fallback: create entry without API key if no APIKeyEntries
|
|
if createdEntries == 0 {
|
|
idKind := fmt.Sprintf("openai-compatibility:%s", providerName)
|
|
id, token := idGen.Next(idKind, base)
|
|
attrs := map[string]string{
|
|
"source": fmt.Sprintf("config:%s[%s]", providerName, token),
|
|
"base_url": base,
|
|
"compat_name": compat.Name,
|
|
"provider_key": providerName,
|
|
}
|
|
if hash := diff.ComputeOpenAICompatModelsHash(compat.Models); hash != "" {
|
|
attrs["models_hash"] = hash
|
|
}
|
|
addConfigHeadersToAttrs(compat.Headers, attrs)
|
|
a := &coreauth.Auth{
|
|
ID: id,
|
|
Provider: providerName,
|
|
Label: compat.Name,
|
|
Prefix: prefix,
|
|
Status: coreauth.StatusActive,
|
|
Attributes: attrs,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
out = append(out, a)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// synthesizeVertexCompat creates Auth entries for Vertex-compatible providers.
|
|
func (s *ConfigSynthesizer) synthesizeVertexCompat(ctx *SynthesisContext) []*coreauth.Auth {
|
|
cfg := ctx.Config
|
|
now := ctx.Now
|
|
idGen := ctx.IDGenerator
|
|
|
|
out := make([]*coreauth.Auth, 0, len(cfg.VertexCompatAPIKey))
|
|
for i := range cfg.VertexCompatAPIKey {
|
|
compat := &cfg.VertexCompatAPIKey[i]
|
|
providerName := "vertex"
|
|
base := strings.TrimSpace(compat.BaseURL)
|
|
|
|
key := strings.TrimSpace(compat.APIKey)
|
|
prefix := strings.TrimSpace(compat.Prefix)
|
|
proxyURL := strings.TrimSpace(compat.ProxyURL)
|
|
idKind := "vertex:apikey"
|
|
id, token := idGen.Next(idKind, key, base, proxyURL)
|
|
attrs := map[string]string{
|
|
"source": fmt.Sprintf("config:vertex-apikey[%s]", token),
|
|
"base_url": base,
|
|
"provider_key": providerName,
|
|
}
|
|
if key != "" {
|
|
attrs["api_key"] = key
|
|
}
|
|
if hash := diff.ComputeVertexCompatModelsHash(compat.Models); hash != "" {
|
|
attrs["models_hash"] = hash
|
|
}
|
|
addConfigHeadersToAttrs(compat.Headers, attrs)
|
|
a := &coreauth.Auth{
|
|
ID: id,
|
|
Provider: providerName,
|
|
Label: "vertex-apikey",
|
|
Prefix: prefix,
|
|
Status: coreauth.StatusActive,
|
|
ProxyURL: proxyURL,
|
|
Attributes: attrs,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
ApplyAuthExcludedModelsMeta(a, cfg, nil, "apikey")
|
|
out = append(out, a)
|
|
}
|
|
return out
|
|
}
|