Initial commit: Luminary AI Gateway
CI / docker (push) Successful in 2m4s

OpenAI-compatible AI gateway with Vue admin UI, multi-provider egress,
ingress key governance, monitoring, and security controls.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
renjue
2026-06-23 21:46:16 +08:00
commit 76ba500417
134 changed files with 18988 additions and 0 deletions
+176
View File
@@ -0,0 +1,176 @@
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 解析最终请求 URLoverride 可为相对 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)
}
}