This commit is contained in:
leokun
2026-06-30 10:38:52 +08:00
commit c083be5ec2
312 changed files with 146628 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
package modelchannel
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"net/url"
"strings"
)
const ChannelIDHexLength = 16
const (
OpenAIEndpointResponses = "/v1/responses"
OpenAIEndpointChatCompletions = "/v1/chat/completions"
)
func NormalizeBaseURL(raw string) (string, error) {
trimmed := strings.TrimSpace(raw)
if trimmed == "" {
return "", fmt.Errorf("模型适配器 baseURL 不能为空")
}
parsed, err := url.Parse(trimmed)
if err != nil {
return "", fmt.Errorf("模型适配器 baseURL 不是合法 URL")
}
if parsed.Scheme != "http" && parsed.Scheme != "https" {
return "", fmt.Errorf("模型适配器 baseURL 仅支持 http 或 https")
}
if strings.TrimSpace(parsed.Host) == "" {
return "", fmt.Errorf("模型适配器 baseURL 缺少主机名")
}
parsed.Scheme = strings.ToLower(strings.TrimSpace(parsed.Scheme))
parsed.Host = strings.ToLower(strings.TrimSpace(parsed.Host))
normalized := strings.TrimRight(parsed.String(), "/")
if normalized == "" {
normalized = parsed.String()
}
return normalized, nil
}
func NormalizeOpenAIEndpoint(providerType string, endpoint string) string {
if strings.TrimSpace(strings.ToLower(providerType)) != "openai" {
return ""
}
switch strings.ToLower(strings.TrimSpace(endpoint)) {
case "", OpenAIEndpointResponses:
return OpenAIEndpointResponses
case OpenAIEndpointChatCompletions:
return OpenAIEndpointChatCompletions
default:
return ""
}
}
func BuildLegacyChannelID(baseURL string, modelID string, apiKey string, name string) string {
return buildChannelID([]string{
strings.TrimSpace(baseURL),
strings.TrimSpace(modelID),
strings.TrimSpace(apiKey),
strings.TrimSpace(name),
})
}
func BuildChannelID(baseURL string, modelID string, apiKey string, name string, openAIEndpoint string) string {
endpoint := strings.TrimSpace(openAIEndpoint)
if endpoint == "" {
return BuildLegacyChannelID(baseURL, modelID, apiKey, name)
}
return buildChannelID([]string{
strings.TrimSpace(baseURL),
strings.TrimSpace(modelID),
strings.TrimSpace(apiKey),
strings.TrimSpace(name),
endpoint,
})
}
func buildChannelID(parts []string) string {
payload := strings.Join(parts, "\n")
hashBytes := sha256.Sum256([]byte(payload))
return hex.EncodeToString(hashBytes[:])[:ChannelIDHexLength]
}
+66
View File
@@ -0,0 +1,66 @@
package modelchannel
import "strings"
func IsMetaModelAlias(modelRef string) bool {
switch strings.ToLower(strings.TrimSpace(modelRef)) {
case "fast", "default", "auto":
return true
default:
return false
}
}
func ResolveAdapterIndex[T any](adapters []T, requestedModelRef string, id func(T) string, providerModelID func(T) string, legacyIDs ...func(T) string) (int, bool) {
if len(adapters) == 0 {
return -1, false
}
targetModelRef := strings.TrimSpace(requestedModelRef)
if targetModelRef == "" || IsMetaModelAlias(targetModelRef) {
targetModelRef = strings.TrimSpace(id(adapters[0]))
}
if targetModelRef == "" {
return -1, false
}
for index, adapter := range adapters {
if strings.TrimSpace(id(adapter)) == targetModelRef {
return index, true
}
}
legacyIndex := -1
for _, legacyID := range legacyIDs {
if legacyID == nil {
continue
}
for index, adapter := range adapters {
if strings.TrimSpace(legacyID(adapter)) != targetModelRef {
continue
}
if legacyIndex >= 0 && legacyIndex != index {
return -1, false
}
legacyIndex = index
}
}
if legacyIndex >= 0 {
return legacyIndex, true
}
fallbackIndex := -1
for index, adapter := range adapters {
if strings.TrimSpace(providerModelID(adapter)) != targetModelRef {
continue
}
if fallbackIndex >= 0 {
return -1, false
}
fallbackIndex = index
}
if fallbackIndex < 0 {
return -1, false
}
return fallbackIndex, true
}