package provider import ( "strings" "github.com/rose_cat707/luminary/internal/model" ) // EndpointType 接口类型,对应入口 /v1/* 与上游 path 映射 type EndpointType string const ( EndpointListModels EndpointType = "list_models" EndpointTextCompletion EndpointType = "text_completion" EndpointChatCompletion EndpointType = "chat_completion" EndpointResponses EndpointType = "responses" EndpointEmbeddings EndpointType = "embeddings" EndpointRerank EndpointType = "rerank" EndpointSpeech EndpointType = "speech" EndpointTranscription EndpointType = "transcription" EndpointImageGeneration EndpointType = "image_generation" ) type EndpointMeta struct { Key EndpointType `json:"key"` Label string `json:"label"` Method string `json:"method"` IngressPath string `json:"ingress_path"` Categories []model.ProviderCategory `json:"categories"` } var AllEndpoints = []EndpointMeta{ {Key: EndpointListModels, Label: "List Models", Method: "GET", IngressPath: "/v1/models", Categories: []model.ProviderCategory{model.CategoryLLM, model.CategoryEmbedding, model.CategoryRerank}}, {Key: EndpointTextCompletion, Label: "Text Completion", Method: "POST", IngressPath: "/v1/completions", Categories: []model.ProviderCategory{model.CategoryLLM}}, {Key: EndpointChatCompletion, Label: "Chat Completion", Method: "POST", IngressPath: "/v1/chat/completions", Categories: []model.ProviderCategory{model.CategoryLLM}}, {Key: EndpointResponses, Label: "Responses", Method: "POST", IngressPath: "/v1/responses", Categories: []model.ProviderCategory{model.CategoryLLM}}, {Key: EndpointEmbeddings, Label: "Embeddings", Method: "POST", IngressPath: "/v1/embeddings", Categories: []model.ProviderCategory{model.CategoryEmbedding}}, {Key: EndpointRerank, Label: "Rerank", Method: "POST", IngressPath: "/v1/rerank", Categories: []model.ProviderCategory{model.CategoryRerank}}, {Key: EndpointSpeech, Label: "Speech", Method: "POST", IngressPath: "/v1/audio/speech", Categories: []model.ProviderCategory{model.CategorySpeech}}, {Key: EndpointTranscription, Label: "Transcription", Method: "POST", IngressPath: "/v1/audio/transcriptions", Categories: []model.ProviderCategory{model.CategoryAudio}}, {Key: EndpointImageGeneration, Label: "Image Generation", Method: "POST", IngressPath: "/v1/images/generations", Categories: []model.ProviderCategory{model.CategoryImage}}, } // DefaultPaths 各 Provider 类型的默认上游 path(相对 base_url) var DefaultPaths = map[model.ProviderType]map[EndpointType]string{ model.ProviderOpenAI: { EndpointListModels: "/models", EndpointTextCompletion: "/completions", EndpointChatCompletion: "/chat/completions", EndpointResponses: "/responses", EndpointEmbeddings: "/embeddings", EndpointRerank: "/rerank", EndpointSpeech: "/audio/speech", EndpointTranscription: "/audio/transcriptions", EndpointImageGeneration: "/images/generations", }, model.ProviderAnthropic: { EndpointListModels: "/models", EndpointChatCompletion: "/messages", }, model.ProviderCustom: { EndpointListModels: "/models", EndpointTextCompletion: "/completions", EndpointChatCompletion: "/chat/completions", EndpointResponses: "/responses", EndpointEmbeddings: "/embeddings", EndpointRerank: "/rerank", EndpointSpeech: "/audio/speech", EndpointTranscription: "/audio/transcriptions", EndpointImageGeneration: "/images/generations", }, model.ProviderAzureOpenAI: { EndpointListModels: "/models", EndpointChatCompletion: "/chat/completions", EndpointResponses: "/responses", EndpointEmbeddings: "/embeddings", EndpointRerank: "/rerank", }, model.ProviderOpenRouter: { EndpointListModels: "/models", EndpointChatCompletion: "/chat/completions", EndpointResponses: "/responses", EndpointEmbeddings: "/embeddings", EndpointRerank: "/rerank", }, model.ProviderOllama: { EndpointListModels: "/models", EndpointChatCompletion: "/chat/completions", EndpointEmbeddings: "/embeddings", EndpointRerank: "/rerank", }, model.ProviderComfyUI: {}, } func DefaultPath(providerType model.ProviderType, endpoint EndpointType) string { if m, ok := DefaultPaths[providerType]; ok { if p, ok := m[endpoint]; ok { return p } } if p, ok := DefaultPaths[model.ProviderOpenAI][endpoint]; ok { return p } return "" } // DefaultEndpointsForCategory 创建出口时默认启用的 endpoint keys func DefaultEndpointsForCategory(cat model.ProviderCategory) []string { switch cat { case model.CategoryEmbedding: return []string{string(EndpointListModels), string(EndpointEmbeddings)} case model.CategoryRerank: return []string{string(EndpointListModels), string(EndpointRerank)} case model.CategoryImage: return []string{} default: return []string{string(EndpointListModels), string(EndpointChatCompletion)} } } func EndpointsForCategory(cat model.ProviderCategory) []EndpointMeta { var out []EndpointMeta for _, e := range AllEndpoints { for _, c := range e.Categories { if c == cat { out = append(out, e) break } } } return out } // ResolveURL 解析最终请求 URL:override 可为相对 path 或完整 URL func ResolveURL(baseURL, override, defaultPath string) string { if override != "" { if strings.HasPrefix(override, "http://") || strings.HasPrefix(override, "https://") { return override } return joinURL(baseURL, override) } return joinURL(baseURL, defaultPath) } func joinURL(base, path string) string { base = strings.TrimRight(base, "/") if path == "" { return base } if !strings.HasPrefix(path, "/") { path = "/" + path } if base == "" { return path } return base + path } func CategoryLabel(cat model.ProviderCategory) string { switch cat { case model.CategoryLLM: return "语言模型" case model.CategoryEmbedding: return "向量嵌入" case model.CategoryRerank: return "重排序" case model.CategorySpeech: return "语音合成" case model.CategoryImage: return "图像生成" case model.CategoryAudio: return "音频转写" default: return string(cat) } }