Files
cloudpods/pkg/aiproxy/models/catalog_seed.go
Zexi Li 167682c39b feat(aiproxy): add Anthropic Messages API and migrate functional tests to Go (#25107)
Add /v1/messages handler with Anthropic-to-OpenAI translation, upstream
failover, and probe endpoints. Replace shell-based functional test scripts
with pkg/aiproxy/ft and climc test commands; consolidate documentation.
2026-07-07 20:13:14 +08:00

242 lines
7.2 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"context"
"fmt"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/aiproxy"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/mcclient"
)
// standardCatalogProviderKeys lists built-in provider_key values seeded at InitDB.
var standardCatalogProviderKeys = api.StandardCatalogProviderKeys
func standardProviderConfig(providerKey string) *api.SAiProviderConfig {
if u := api.DefaultPublicBaseURL(providerKey); u != "" {
return &api.SAiProviderConfig{BaseURL: u}
}
return nil
}
const placeholderCatalogModelKey = "default"
func catalogProviderId(providerKey string) string {
return providerKey
}
func catalogProviderExists(providerId string) (bool, error) {
cnt, err := AiProviderManager.RawQuery().Equals("id", providerId).CountWithError()
if err != nil {
return false, errors.Wrap(err, "count catalog ai_provider")
}
return cnt > 0, nil
}
func catalogModelExists(modelId string) (bool, error) {
cnt, err := AiModelManager.RawQuery().Equals("id", modelId).CountWithError()
if err != nil {
return false, errors.Wrap(err, "count catalog ai_model")
}
return cnt > 0, nil
}
func insertCatalogProvider(ctx context.Context, providerKey, description string, cfg *api.SAiProviderConfig) error {
providerId := catalogProviderId(providerKey)
exists, err := catalogProviderExists(providerId)
if err != nil {
return err
}
if exists {
return nil
}
prov := SAiProvider{}
prov.SetModelManager(AiProviderManager, &prov)
prov.Id = providerId
prov.Name = providerKey
prov.ProviderKey = providerKey
prov.Description = description
prov.Config = cfg
prov.SetEnabled(true)
prov.Status = apis.STATUS_AVAILABLE
prov.Progress = 100
if err := AiProviderManager.TableSpec().Insert(ctx, &prov); err != nil {
return errors.Wrapf(err, "insert ai_provider %s", providerKey)
}
return nil
}
func insertCatalogModel(ctx context.Context, providerId, providerKey, modelKey, description string) error {
modelId := catalogModelId(providerKey, modelKey)
exists, err := catalogModelExists(modelId)
if err != nil {
return err
}
if exists {
return nil
}
m := SAiModel{}
m.SetModelManager(AiModelManager, &m)
m.Id = modelId
m.Name = modelId
m.AiProviderId = providerId
m.ModelKey = modelKey
m.Description = description
m.SetEnabled(true)
m.Status = apis.STATUS_AVAILABLE
m.Progress = 100
if err := AiModelManager.TableSpec().Insert(ctx, &m); err != nil {
return errors.Wrapf(err, "insert ai_model %s/%s", providerKey, modelKey)
}
return nil
}
func ensureSeedModelsEntries(ctx context.Context, providerId, providerKey string, entries []catalogSeedModel) error {
if len(entries) == 0 {
return insertCatalogModel(ctx, providerId, providerKey, placeholderCatalogModelKey,
"Catalog seed placeholder; replace with concrete model_key values or use a provider with a built-in catalog.")
}
for i := range entries {
if err := insertCatalogModel(ctx, providerId, providerKey, entries[i].ModelKey, entries[i].Description); err != nil {
return err
}
}
return nil
}
func ensureSeedProvider(ctx context.Context, providerKey string) error {
providerKey = strings.TrimSpace(providerKey)
providerId := catalogProviderId(providerKey)
if err := insertCatalogProvider(ctx, providerKey,
fmt.Sprintf("Standard provider catalog entry: %s", providerKey),
standardProviderConfig(providerKey)); err != nil {
return err
}
return ensureSeedModelsEntries(ctx, providerId, providerKey, catalogSeedModelsForProvider(providerKey))
}
func providerModelExists(providerId, modelKey string) (bool, error) {
cnt, err := AiModelManager.Query().
Equals("ai_provider_id", providerId).
Equals("model_key", modelKey).
CountWithError()
if err != nil {
return false, errors.Wrap(err, "count ai_model for provider")
}
return cnt > 0, nil
}
func createUserProviderModel(
ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
prov *SAiProvider,
modelKey, description string,
) error {
if prov == nil || strings.TrimSpace(prov.Id) == "" {
return errors.Error("ai_provider is nil or has no id")
}
modelKey = strings.TrimSpace(modelKey)
if modelKey == "" {
return errors.Error("model_key is empty")
}
exists, err := providerModelExists(prov.Id, modelKey)
if err != nil {
return err
}
if exists {
return nil
}
dataDict := jsonutils.NewDict()
dataDict.Set("ai_provider_id", jsonutils.NewString(prov.Id))
dataDict.Set("model_key", jsonutils.NewString(modelKey))
dataDict.Set("enabled", jsonutils.JSONTrue)
dataDict.Set("generate_name", jsonutils.NewString(defaultAiModelName(prov.Name, modelKey)))
if desc := strings.TrimSpace(description); desc != "" {
dataDict.Set("description", jsonutils.NewString(desc))
}
if _, err := db.DoCreate(AiModelManager, ctx, userCred, nil, dataDict, ownerId); err != nil {
return errors.Wrapf(err, "create ai_model %q for provider %s", modelKey, prov.Id)
}
return nil
}
func createSelectedProviderModels(
ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
prov *SAiProvider,
modelKeys []string,
) error {
if prov == nil {
return nil
}
for _, modelKey := range modelKeys {
if err := createUserProviderModel(ctx, userCred, ownerId, prov, modelKey, ""); err != nil {
return err
}
}
return nil
}
// createCatalogModelsForUserProvider inserts built-in catalog model rows for a newly created public SaaS provider.
func createCatalogModelsForUserProvider(
ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
prov *SAiProvider,
) error {
if prov == nil {
return nil
}
pk := strings.TrimSpace(prov.ProviderKey)
if !api.HasDefaultPublicBaseURL(pk) {
return nil
}
entries := catalogSeedModelsForProvider(pk)
if len(entries) == 0 {
return createUserProviderModel(ctx, userCred, ownerId, prov, placeholderCatalogModelKey,
"Catalog seed placeholder; replace with concrete model_key values or use a provider with a built-in catalog.")
}
for i := range entries {
if err := createUserProviderModel(ctx, userCred, ownerId, prov, entries[i].ModelKey, entries[i].Description); err != nil {
return err
}
}
return nil
}
// SeedStandardCatalog inserts built-in ai_provider / ai_model catalog rows on first init only.
// Existing rows are left unchanged so user config survives service restarts.
func SeedStandardCatalog(ctx context.Context) error {
for _, pk := range standardCatalogProviderKeys {
if err := ensureSeedProvider(ctx, pk); err != nil {
return err
}
}
log.Infof("aiproxy: standard catalog seed completed (%d providers)", len(standardCatalogProviderKeys))
return nil
}