feat: implement AI gateway phase one runtime
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
const catalogProviderColumns = `
|
||||
id::text, provider_key, COALESCE(NULLIF(provider_code, ''), provider_key) AS provider_code,
|
||||
display_name, provider_type, COALESCE(icon_path, '') AS icon_path,
|
||||
COALESCE(source, '') AS source, capability_schema, default_rate_limit_policy,
|
||||
metadata, status, created_at, updated_at`
|
||||
|
||||
type CatalogProviderInput struct {
|
||||
ProviderKey string `json:"providerKey"`
|
||||
Code string `json:"code"`
|
||||
DisplayName string `json:"displayName"`
|
||||
ProviderType string `json:"providerType"`
|
||||
IconPath string `json:"iconPath"`
|
||||
Source string `json:"source"`
|
||||
CapabilitySchema map[string]any `json:"capabilitySchema"`
|
||||
DefaultRateLimitPolicy map[string]any `json:"defaultRateLimitPolicy"`
|
||||
Metadata map[string]any `json:"metadata"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type catalogProviderScanner interface {
|
||||
Scan(dest ...any) error
|
||||
}
|
||||
|
||||
func (s *Store) CreateCatalogProvider(ctx context.Context, input CatalogProviderInput) (CatalogProvider, error) {
|
||||
input = normalizeCatalogProviderInput(input)
|
||||
capabilitySchema, _ := json.Marshal(emptyObjectIfNil(input.CapabilitySchema))
|
||||
rateLimitPolicy, _ := json.Marshal(emptyObjectIfNil(input.DefaultRateLimitPolicy))
|
||||
metadata, _ := json.Marshal(emptyObjectIfNil(input.Metadata))
|
||||
|
||||
return scanCatalogProvider(s.pool.QueryRow(ctx, `
|
||||
INSERT INTO model_catalog_providers (
|
||||
provider_key, provider_code, display_name, provider_type, icon_path, source,
|
||||
capability_schema, default_rate_limit_policy, metadata, status
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, NULLIF($5, ''), $6, $7, $8, $9, $10)
|
||||
RETURNING `+catalogProviderColumns,
|
||||
input.ProviderKey,
|
||||
input.Code,
|
||||
input.DisplayName,
|
||||
input.ProviderType,
|
||||
input.IconPath,
|
||||
input.Source,
|
||||
capabilitySchema,
|
||||
rateLimitPolicy,
|
||||
metadata,
|
||||
input.Status,
|
||||
))
|
||||
}
|
||||
|
||||
func (s *Store) UpdateCatalogProvider(ctx context.Context, id string, input CatalogProviderInput) (CatalogProvider, error) {
|
||||
input = normalizeCatalogProviderInput(input)
|
||||
capabilitySchema, _ := json.Marshal(emptyObjectIfNil(input.CapabilitySchema))
|
||||
rateLimitPolicy, _ := json.Marshal(emptyObjectIfNil(input.DefaultRateLimitPolicy))
|
||||
metadata, _ := json.Marshal(emptyObjectIfNil(input.Metadata))
|
||||
|
||||
return scanCatalogProvider(s.pool.QueryRow(ctx, `
|
||||
UPDATE model_catalog_providers
|
||||
SET provider_key = $2,
|
||||
provider_code = $3,
|
||||
display_name = $4,
|
||||
provider_type = $5,
|
||||
icon_path = NULLIF($6, ''),
|
||||
source = $7,
|
||||
capability_schema = $8,
|
||||
default_rate_limit_policy = $9,
|
||||
metadata = $10,
|
||||
status = $11,
|
||||
updated_at = now()
|
||||
WHERE id = $1::uuid
|
||||
RETURNING `+catalogProviderColumns,
|
||||
id,
|
||||
input.ProviderKey,
|
||||
input.Code,
|
||||
input.DisplayName,
|
||||
input.ProviderType,
|
||||
input.IconPath,
|
||||
input.Source,
|
||||
capabilitySchema,
|
||||
rateLimitPolicy,
|
||||
metadata,
|
||||
input.Status,
|
||||
))
|
||||
}
|
||||
|
||||
func (s *Store) DeleteCatalogProvider(ctx context.Context, id string) error {
|
||||
result, err := s.pool.Exec(ctx, `DELETE FROM model_catalog_providers WHERE id = $1::uuid`, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.RowsAffected() == 0 {
|
||||
return pgx.ErrNoRows
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func scanCatalogProvider(scanner catalogProviderScanner) (CatalogProvider, error) {
|
||||
var item CatalogProvider
|
||||
var capabilitySchema []byte
|
||||
var rateLimitPolicy []byte
|
||||
var metadata []byte
|
||||
if err := scanner.Scan(
|
||||
&item.ID,
|
||||
&item.ProviderKey,
|
||||
&item.Code,
|
||||
&item.DisplayName,
|
||||
&item.ProviderType,
|
||||
&item.IconPath,
|
||||
&item.Source,
|
||||
&capabilitySchema,
|
||||
&rateLimitPolicy,
|
||||
&metadata,
|
||||
&item.Status,
|
||||
&item.CreatedAt,
|
||||
&item.UpdatedAt,
|
||||
); err != nil {
|
||||
return CatalogProvider{}, err
|
||||
}
|
||||
item.CapabilitySchema = decodeObject(capabilitySchema)
|
||||
item.DefaultRateLimitPolicy = decodeObject(rateLimitPolicy)
|
||||
item.Metadata = decodeObject(metadata)
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func normalizeCatalogProviderInput(input CatalogProviderInput) CatalogProviderInput {
|
||||
input.ProviderKey = strings.TrimSpace(input.ProviderKey)
|
||||
input.Code = strings.TrimSpace(input.Code)
|
||||
input.DisplayName = strings.TrimSpace(input.DisplayName)
|
||||
input.ProviderType = strings.TrimSpace(input.ProviderType)
|
||||
input.IconPath = strings.TrimSpace(input.IconPath)
|
||||
input.Source = strings.TrimSpace(input.Source)
|
||||
input.Status = strings.TrimSpace(input.Status)
|
||||
if input.Code == "" {
|
||||
input.Code = input.ProviderKey
|
||||
}
|
||||
if input.ProviderType == "" {
|
||||
input.ProviderType = "openai"
|
||||
}
|
||||
if input.Source == "" {
|
||||
input.Source = "gateway"
|
||||
}
|
||||
if input.Status == "" {
|
||||
input.Status = "active"
|
||||
}
|
||||
return input
|
||||
}
|
||||
Reference in New Issue
Block a user