76ba500417
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>
95 lines
3.0 KiB
Go
95 lines
3.0 KiB
Go
package openai
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/rose_cat707/luminary/internal/model"
|
|
"github.com/rose_cat707/luminary/internal/provider"
|
|
)
|
|
|
|
type Client struct {
|
|
http *provider.HTTPClientWrapper
|
|
}
|
|
|
|
func New() *Client {
|
|
return &Client{http: provider.NewHTTPClientWrapper()}
|
|
}
|
|
|
|
func (c *Client) Type() string { return "openai" }
|
|
|
|
func (c *Client) cfg(baseURL string) provider.ProviderConfig {
|
|
return provider.ProviderConfig{
|
|
Type: model.ProviderOpenAI,
|
|
Category: model.CategoryLLM,
|
|
BaseURL: provider.NormalizeBaseURL(baseURL, model.ProviderOpenAI),
|
|
}
|
|
}
|
|
|
|
func (c *Client) ListModels(ctx context.Context, apiKey, baseURL string) ([]provider.ModelInfo, error) {
|
|
return c.ListModelsWithConfig(ctx, apiKey, provider.ProviderConfig{
|
|
Type: model.ProviderOpenAI,
|
|
BaseURL: provider.NormalizeBaseURL(baseURL, model.ProviderOpenAI),
|
|
})
|
|
}
|
|
|
|
func (c *Client) ListModelsWithConfig(ctx context.Context, apiKey string, cfg provider.ProviderConfig) ([]provider.ModelInfo, error) {
|
|
cfg.Type = model.ProviderOpenAI
|
|
cfg.BaseURL = provider.NormalizeBaseURL(cfg.BaseURL, model.ProviderOpenAI)
|
|
url := cfg.ResolveEndpointURL(provider.EndpointListModels)
|
|
resp, err := provider.ForwardHTTP(ctx, c.http.Client, "GET", url, apiKey, cfg.Type, nil, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode >= 400 {
|
|
return nil, fmt.Errorf("list models: %s", string(resp.Body))
|
|
}
|
|
var out struct {
|
|
Data []struct {
|
|
ID string `json:"id"`
|
|
} `json:"data"`
|
|
}
|
|
if err := json.Unmarshal(resp.Body, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
models := make([]provider.ModelInfo, 0, len(out.Data))
|
|
for _, m := range out.Data {
|
|
models = append(models, provider.ModelInfo{ID: m.ID, DisplayName: m.ID})
|
|
}
|
|
return models, nil
|
|
}
|
|
|
|
func (c *Client) ChatCompletion(ctx context.Context, apiKey, baseURL string, req *provider.ChatRequest) (*provider.ChatResponse, error) {
|
|
return c.ForwardWithConfig(ctx, apiKey, provider.ProviderConfig{
|
|
Type: model.ProviderOpenAI,
|
|
BaseURL: provider.NormalizeBaseURL(baseURL, model.ProviderOpenAI),
|
|
}, provider.EndpointChatCompletion, req.Raw)
|
|
}
|
|
|
|
func (c *Client) ForwardWithConfig(ctx context.Context, apiKey string, cfg provider.ProviderConfig, endpoint provider.EndpointType, body []byte) (*provider.ChatResponse, error) {
|
|
cfg.Type = model.ProviderOpenAI
|
|
cfg.BaseURL = provider.NormalizeBaseURL(cfg.BaseURL, model.ProviderOpenAI)
|
|
url := cfg.ResolveEndpointURL(endpoint)
|
|
resp, err := provider.ForwardHTTP(ctx, c.http.Client, "POST", url, apiKey, cfg.Type, body, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &provider.ChatResponse{
|
|
StatusCode: resp.StatusCode,
|
|
Body: resp.Body,
|
|
TokensIn: resp.TokensIn,
|
|
TokensOut: resp.TokensOut,
|
|
}, nil
|
|
}
|
|
|
|
func (c *Client) HealthCheck(ctx context.Context, apiKey, baseURL string) error {
|
|
_, err := c.ListModels(ctx, apiKey, baseURL)
|
|
return err
|
|
}
|
|
|
|
func (c *Client) HealthCheckWithConfig(ctx context.Context, apiKey string, cfg provider.ProviderConfig) error {
|
|
_, err := c.ListModelsWithConfig(ctx, apiKey, cfg)
|
|
return err
|
|
}
|