mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-14 08:58:27 +08:00
Fix: Align part of Go provider APIs with Python APIs (#16823)
This commit is contained in:
@@ -81,9 +81,9 @@ func TestGoogleScholar_InvokeMergesNodeParams(t *testing.T) {
|
||||
|
||||
fake := &fakeGoogleScholarInvoker{}
|
||||
c := newGoogleScholarComponentWithInvoker(fake, map[string]any{
|
||||
"top_n": float64(20),
|
||||
"sort_by": "date",
|
||||
"patents": false,
|
||||
"top_n": float64(20),
|
||||
"sort_by": "date",
|
||||
"patents": false,
|
||||
})
|
||||
|
||||
_, err := c.Invoke(context.Background(), map[string]any{
|
||||
|
||||
@@ -834,7 +834,7 @@ Commands (User Mode):
|
||||
USE MODEL 'provider/instance/model'; - Set current model for chat
|
||||
CHAT 'message'; - Chat using current model
|
||||
CHAT 'provider/instance/model' 'message'; - Chat with specified model
|
||||
OPENAI_CHAT 'chat_id' 'message' [options] ; - OpenAI-compatible chat
|
||||
OPENAI_CHAT 'chat_id' 'message' [options] ; - OpenAI-compatible chat
|
||||
(run openai_chat -h for detailed options)
|
||||
CHAT COMPLETIONS 'question' [options] ; - Chat completions via /api/v1/chat/completions
|
||||
(run chat completions -h for detailed options)
|
||||
|
||||
@@ -123,6 +123,31 @@ func (dao *TenantModelDAO) GetModelsByInstanceID(instanceID string) ([]*entity.T
|
||||
return models, nil
|
||||
}
|
||||
|
||||
// DeleteByIDs deletes all models whose id is in the given list.
|
||||
// Mirrors Python's TenantModelService.delete_by_ids.
|
||||
func (dao *TenantModelDAO) DeleteByIDs(ids []string) (int64, error) {
|
||||
if len(ids) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
result := DB.Unscoped().Where("id IN ?", ids).Delete(&entity.TenantModel{})
|
||||
return result.RowsAffected, result.Error
|
||||
}
|
||||
|
||||
// UpdateByID updates a tenant model's model_type and extra by primary key.
|
||||
// Mirrors Python's TenantModelService.update_model.
|
||||
func (dao *TenantModelDAO) UpdateByID(id string, updates map[string]interface{}) error {
|
||||
return DB.Model(&entity.TenantModel{}).Where("id = ?", id).Updates(updates).Error
|
||||
}
|
||||
|
||||
// DeleteByInstanceIDs deletes all models whose instance_id is in the given list.
|
||||
func (dao *TenantModelDAO) DeleteByInstanceIDs(instanceIDs []string) (int64, error) {
|
||||
if len(instanceIDs) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
result := DB.Unscoped().Where("instance_id IN ?", instanceIDs).Delete(&entity.TenantModel{})
|
||||
return result.RowsAffected, result.Error
|
||||
}
|
||||
|
||||
// GetModelsByProviderIDsAndInstanceIDs returns TenantModel rows whose
|
||||
// provider_id is in providerIDs and instance_id is in instanceIDs.
|
||||
// Mirrors Python's
|
||||
|
||||
@@ -110,3 +110,15 @@ func (dao *TenantModelInstanceDAO) DeleteByProviderIDAndInstanceName(providerID,
|
||||
result := DB.Unscoped().Where("provider_id = ? and instance_name = ?", providerID, instanceName).Delete(&entity.TenantModelInstance{})
|
||||
return result.RowsAffected, result.Error
|
||||
}
|
||||
|
||||
// UpdateByID updates a tenant model instance by primary key.
|
||||
// Mirrors Python's TenantModelInstanceService.update_by_id.
|
||||
func (dao *TenantModelInstanceDAO) UpdateByID(id string, updates map[string]interface{}) error {
|
||||
return DB.Model(&entity.TenantModelInstance{}).Where("id = ?", id).Updates(updates).Error
|
||||
}
|
||||
|
||||
// DeleteByProviderID deletes all instances for the given provider.
|
||||
func (dao *TenantModelInstanceDAO) DeleteByProviderID(providerID string) (int64, error) {
|
||||
result := DB.Unscoped().Where("provider_id = ?", providerID).Delete(&entity.TenantModelInstance{})
|
||||
return result.RowsAffected, result.Error
|
||||
}
|
||||
|
||||
@@ -141,3 +141,11 @@ func ModelTypeFromStrings(types []string) ModelType {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ModelVerifyStatus mirrors Python's ModelVerifyStatusEnum
|
||||
// (common/constants.py).
|
||||
const (
|
||||
ModelVerifySuccess = "success"
|
||||
ModelVerifyFail = "fail"
|
||||
ModelVerifyUnknown = "unknown"
|
||||
)
|
||||
|
||||
@@ -175,10 +175,11 @@ func (h *ProviderHandler) ShowModel(c *gin.Context) {
|
||||
}
|
||||
|
||||
type CreateProviderInstanceRequest struct {
|
||||
InstanceName string `json:"instance_name" binding:"required"`
|
||||
APIKey string `json:"api_key"`
|
||||
BaseURL string `json:"base_url"`
|
||||
Region string `json:"region"`
|
||||
InstanceName string `json:"instance_name" binding:"required"`
|
||||
APIKey string `json:"api_key"`
|
||||
BaseURL string `json:"base_url"`
|
||||
Region string `json:"region"`
|
||||
ModelInfo []service.CreateInstanceModelInfo `json:"model_info"`
|
||||
}
|
||||
|
||||
func (h *ProviderHandler) CreateProviderInstance(c *gin.Context) {
|
||||
@@ -196,7 +197,20 @@ func (h *ProviderHandler) CreateProviderInstance(c *gin.Context) {
|
||||
|
||||
userID := c.GetString("user_id")
|
||||
|
||||
_, err := h.modelProviderService.CreateProviderInstance(providerName, req.InstanceName, req.APIKey, req.BaseURL, req.Region, userID)
|
||||
// If the request body only contains "instance_name", create a name-only
|
||||
// instance without API key validation or model creation.
|
||||
// Mirrors Python's provider_api.py:349 — set(data.keys()) == {"instance_name"}.
|
||||
if req.APIKey == "" && req.BaseURL == "" && req.Region == "" && len(req.ModelInfo) == 0 {
|
||||
code, err := h.modelProviderService.CreateNameOnlyProviderInstance(providerName, req.InstanceName, userID)
|
||||
if err != nil {
|
||||
common.ErrorWithCode(c, code, err.Error())
|
||||
return
|
||||
}
|
||||
common.SuccessWithMessage(c, "success")
|
||||
return
|
||||
}
|
||||
|
||||
_, err := h.modelProviderService.CreateProviderInstance(providerName, req.InstanceName, req.APIKey, req.BaseURL, req.Region, userID, req.ModelInfo)
|
||||
if err != nil {
|
||||
common.ErrorWithCode(c, common.CodeServerError, err.Error())
|
||||
return
|
||||
@@ -388,8 +402,12 @@ func (h *ProviderHandler) ShowTask(c *gin.Context) {
|
||||
}
|
||||
|
||||
type AlterProviderInstanceRequest struct {
|
||||
InstanceName string `json:"instance_name"`
|
||||
APIKey string `json:"api_key"`
|
||||
InstanceName string `json:"instance_name"`
|
||||
APIKey string `json:"api_key"`
|
||||
BaseURL string `json:"base_url"`
|
||||
Region string `json:"region"`
|
||||
ModelInfo []service.CreateInstanceModelInfo `json:"model_info"`
|
||||
Verify *bool `json:"verify"`
|
||||
}
|
||||
|
||||
func (h *ProviderHandler) AlterProviderInstance(c *gin.Context) {
|
||||
@@ -417,7 +435,12 @@ func (h *ProviderHandler) AlterProviderInstance(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
code, err := h.modelProviderService.AlterProviderInstance(userID, providerName, instanceName, req.InstanceName, req.APIKey)
|
||||
verify := true
|
||||
if req.Verify != nil {
|
||||
verify = *req.Verify
|
||||
}
|
||||
|
||||
code, err := h.modelProviderService.AlterProviderInstance(userID, providerName, instanceName, req.InstanceName, req.APIKey, req.BaseURL, req.Region, req.ModelInfo, verify)
|
||||
if err != nil {
|
||||
common.ErrorWithCode(c, code, err.Error())
|
||||
return
|
||||
|
||||
@@ -228,12 +228,35 @@ func (m *ModelProviderService) ListProvidersOfTenant(userID string) ([]map[strin
|
||||
return nil, common.CodeServerError, err
|
||||
}
|
||||
provider["name"] = providerName
|
||||
|
||||
// Set has_instance flag. Mirrors Python's:
|
||||
// provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, name)
|
||||
// has_instance = bool(provider_obj and TenantModelInstanceService.get_all_by_provider_id(provider_obj.id))
|
||||
provider["has_instance"] = m.providerHasInstance(tenantID, providerName)
|
||||
|
||||
result = append(result, provider)
|
||||
}
|
||||
|
||||
return result, common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
// providerHasInstance checks whether the given tenant's provider has any
|
||||
// configured instances. Mirrors the Python pattern:
|
||||
//
|
||||
// provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, name)
|
||||
// has_instance = bool(provider_obj and TenantModelInstanceService.get_all_by_provider_id(provider_obj.id))
|
||||
func (m *ModelProviderService) providerHasInstance(tenantID, providerName string) bool {
|
||||
providerObj, err := m.modelProviderDAO.GetByTenantIDAndProviderName(tenantID, providerName)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
instances, err := m.modelInstanceDAO.GetAllInstancesByProviderID(providerObj.ID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return len(instances) > 0
|
||||
}
|
||||
|
||||
// isExcludedTenantProvider returns true for system-pool names that the Python
|
||||
// implementation (api/apps/services/provider_api_service.py:108) intentionally
|
||||
// filters out when listing a tenant's providers.
|
||||
@@ -255,6 +278,30 @@ func (m *ModelProviderService) DeleteModelProvider(userID, providerName string)
|
||||
}
|
||||
tenantID := tenants[0].TenantID
|
||||
|
||||
// Find the provider first.
|
||||
provider, err := m.modelProviderDAO.GetByTenantIDAndProviderName(tenantID, providerName)
|
||||
if err != nil {
|
||||
return common.CodeNotFound, fmt.Errorf("provider %s not found", providerName)
|
||||
}
|
||||
|
||||
// Delete all models and instances under this provider.
|
||||
instances, err := m.modelInstanceDAO.GetAllInstancesByProviderID(provider.ID)
|
||||
if err != nil {
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
if len(instances) > 0 {
|
||||
instanceIDs := make([]string, len(instances))
|
||||
for i, inst := range instances {
|
||||
instanceIDs[i] = inst.ID
|
||||
}
|
||||
if _, err := m.modelDAO.DeleteByInstanceIDs(instanceIDs); err != nil {
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
if _, err := m.modelInstanceDAO.DeleteByProviderID(provider.ID); err != nil {
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = m.modelProviderDAO.DeleteByTenantIDAndProviderName(tenantID, providerName)
|
||||
if err != nil {
|
||||
return common.CodeServerError, err
|
||||
@@ -339,7 +386,16 @@ func (m *ModelProviderService) ListSupportedModels(providerName, instanceName, u
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *ModelProviderService) CreateProviderInstance(providerName, instanceName, apiKey, baseURL, region, userID string) (common.ErrorCode, error) {
|
||||
// CreateInstanceModelInfo mirrors Python's model_info dict in
|
||||
// create_provider_instance. See api/apps/services/provider_api_service.py:434.
|
||||
type CreateInstanceModelInfo struct {
|
||||
ModelName string `json:"model_name"`
|
||||
ModelTypes []string `json:"model_type"`
|
||||
MaxTokens int `json:"max_tokens"`
|
||||
Extra map[string]interface{} `json:"extra"`
|
||||
}
|
||||
|
||||
func (m *ModelProviderService) CreateProviderInstance(providerName, instanceName, apiKey, baseURL, region, userID string, modelInfo []CreateInstanceModelInfo) (common.ErrorCode, error) {
|
||||
providerName = strings.TrimSpace(providerName)
|
||||
providerName = strings.ToLower(providerName)
|
||||
|
||||
@@ -361,19 +417,28 @@ func (m *ModelProviderService) CreateProviderInstance(providerName, instanceName
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
|
||||
// Normalize api_key: VLLM with empty api_key defaults to "x".
|
||||
// Mirrors Python's _normalize_provider_api_key.
|
||||
if strings.EqualFold(providerName, "vllm") && apiKey == "" {
|
||||
apiKey = "x"
|
||||
}
|
||||
|
||||
// Verify the API key against the provider.
|
||||
// Mirrors Python's verify_api_key (provider_api_service.py:596).
|
||||
modelVerifyResult := m.verifyProviderAPIKey(providerName, apiKey, region, baseURL, modelInfo)
|
||||
|
||||
instanceID := utility.GenerateToken()
|
||||
|
||||
extra := make(map[string]string)
|
||||
extra["region"] = region
|
||||
extra["base_url"] = baseURL
|
||||
// convert extra to string
|
||||
extraByte, err := json.Marshal(extra)
|
||||
if err != nil {
|
||||
return common.CodeServerError, errors.New("fail to marshal extra")
|
||||
}
|
||||
extraStr := string(extraByte)
|
||||
|
||||
tenantModelProvider := &entity.TenantModelInstance{
|
||||
tenantModelInstance := &entity.TenantModelInstance{
|
||||
ID: instanceID,
|
||||
InstanceName: instanceName,
|
||||
ProviderID: provider.ID,
|
||||
@@ -381,14 +446,187 @@ func (m *ModelProviderService) CreateProviderInstance(providerName, instanceName
|
||||
Status: "active",
|
||||
Extra: extraStr,
|
||||
}
|
||||
err = m.modelInstanceDAO.Create(tenantModelProvider)
|
||||
|
||||
err = m.modelInstanceDAO.Create(tenantModelInstance)
|
||||
if err != nil {
|
||||
return common.CodeServerError, fmt.Errorf("fail to create model instance: %s", err.Error())
|
||||
}
|
||||
|
||||
// Add models with verify result in extra.
|
||||
if len(modelInfo) > 0 {
|
||||
for _, model := range modelInfo {
|
||||
if model.Extra == nil {
|
||||
model.Extra = make(map[string]interface{})
|
||||
}
|
||||
verifyStatus := modelVerifyResult[model.ModelName]
|
||||
if verifyStatus == "" {
|
||||
verifyStatus = entity.ModelVerifyUnknown
|
||||
}
|
||||
model.Extra["verify"] = verifyStatus
|
||||
if err := m.addModelToInstance(tenantID, providerName, instanceName, model); err != nil {
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
// CreateNameOnlyProviderInstance creates a provider instance with only a name,
|
||||
// skipping API key validation and model creation. Mirrors Python's
|
||||
// create_name_only_provider_instance in provider_api_service.py:536.
|
||||
func (m *ModelProviderService) CreateNameOnlyProviderInstance(providerName, instanceName, userID string) (common.ErrorCode, error) {
|
||||
providerName = strings.TrimSpace(providerName)
|
||||
providerName = strings.ToLower(providerName)
|
||||
|
||||
if instanceName == "default" {
|
||||
return common.CodeBadRequest, errors.New("instance name cannot be 'default'")
|
||||
}
|
||||
|
||||
tenants, err := m.userTenantDAO.GetByUserIDAndRole(userID, "owner")
|
||||
if err != nil {
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
if len(tenants) == 0 {
|
||||
return common.CodeNotFound, errors.New("user has no tenants")
|
||||
}
|
||||
tenantID := tenants[0].TenantID
|
||||
|
||||
provider, err := m.modelProviderDAO.GetByTenantIDAndProviderName(tenantID, providerName)
|
||||
if err != nil {
|
||||
return common.CodeNotFound, fmt.Errorf("provider '%s' does not exist", providerName)
|
||||
}
|
||||
|
||||
instanceID := utility.GenerateToken()
|
||||
|
||||
tenantModelInstance := &entity.TenantModelInstance{
|
||||
ID: instanceID,
|
||||
InstanceName: instanceName,
|
||||
ProviderID: provider.ID,
|
||||
APIKey: "",
|
||||
Status: "active",
|
||||
Extra: "{}",
|
||||
}
|
||||
err = m.modelInstanceDAO.Create(tenantModelInstance)
|
||||
if err != nil {
|
||||
return common.CodeServerError, fmt.Errorf("fail to create model instance: %s", err.Error())
|
||||
}
|
||||
|
||||
return common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
// verifyProviderAPIKey verifies the API key against the provider by calling
|
||||
// the driver's CheckConnection. It returns a map from model name to verify
|
||||
// status (success/fail/unknown). Mirrors Python's verify_api_key in
|
||||
// provider_api_service.py:596.
|
||||
func (m *ModelProviderService) verifyProviderAPIKey(providerName, apiKey, region, baseURL string, modelInfo []CreateInstanceModelInfo) map[string]string {
|
||||
result := make(map[string]string)
|
||||
|
||||
providerInfo := dao.GetModelProviderManager().FindProvider(providerName)
|
||||
if providerInfo == nil {
|
||||
// Provider not in system pool — mark all models as unknown.
|
||||
for _, model := range modelInfo {
|
||||
result[model.ModelName] = entity.ModelVerifyUnknown
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
apiKey = strings.TrimSpace(apiKey)
|
||||
region = strings.TrimSpace(region)
|
||||
baseURL = strings.TrimSpace(baseURL)
|
||||
if region == "" {
|
||||
region = "default"
|
||||
}
|
||||
|
||||
driver := providerInfo.ModelDriver
|
||||
if strings.EqualFold(providerInfo.Class, "local") {
|
||||
var err error
|
||||
driver, err = newModelDriverForBaseURL(driver, providerName, region, baseURL)
|
||||
if err != nil {
|
||||
for _, model := range modelInfo {
|
||||
result[model.ModelName] = entity.ModelVerifyFail
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
apiConfig := &modelModule.APIConfig{
|
||||
ApiKey: &apiKey,
|
||||
Region: ®ion,
|
||||
BaseURL: &baseURL,
|
||||
}
|
||||
|
||||
verifyErr := driver.CheckConnection(apiConfig)
|
||||
verifyStatus := entity.ModelVerifySuccess
|
||||
if verifyErr != nil {
|
||||
verifyStatus = entity.ModelVerifyFail
|
||||
}
|
||||
|
||||
for _, model := range modelInfo {
|
||||
result[model.ModelName] = verifyStatus
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// addModelToInstance creates a single model under the given provider instance.
|
||||
// Mirrors Python's add_model_to_instance in provider_api_service.py:969.
|
||||
func (m *ModelProviderService) addModelToInstance(tenantID, providerName, instanceName string, model CreateInstanceModelInfo) error {
|
||||
provider, err := m.modelProviderDAO.GetByTenantIDAndProviderName(tenantID, providerName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("no provider found for provider '%s'", providerName)
|
||||
}
|
||||
|
||||
instance, err := m.modelInstanceDAO.GetByProviderIDAndInstanceName(provider.ID, instanceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("no instance found for provider '%s' and instance '%s'", providerName, instanceName)
|
||||
}
|
||||
|
||||
// Check for duplicate model.
|
||||
_, err = m.modelDAO.GetModelByProviderIDAndInstanceIDAndModelName(provider.ID, instance.ID, model.ModelName)
|
||||
if err == nil {
|
||||
return fmt.Errorf("model '%s' already exists for provider '%s' and instance '%s'", model.ModelName, providerName, instanceName)
|
||||
}
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Compute model type bitmask.
|
||||
combinedType := entity.ModelType(0)
|
||||
for _, t := range model.ModelTypes {
|
||||
combinedType |= entity.ModelTypeFromString(t)
|
||||
}
|
||||
|
||||
// Build extra fields. Mirrors Python's extra_fields in add_model_to_instance.
|
||||
extraFields := map[string]interface{}{
|
||||
"max_tokens": model.MaxTokens,
|
||||
}
|
||||
if model.Extra != nil {
|
||||
for k, v := range model.Extra {
|
||||
extraFields[k] = v
|
||||
}
|
||||
}
|
||||
extraBytes, err := json.Marshal(extraFields)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fail to marshal extra: %s", err.Error())
|
||||
}
|
||||
|
||||
modelID := utility.GenerateToken()
|
||||
tenantModel := &entity.TenantModel{
|
||||
ID: modelID,
|
||||
ModelName: model.ModelName,
|
||||
ModelType: int(combinedType),
|
||||
ProviderID: provider.ID,
|
||||
InstanceID: instance.ID,
|
||||
Status: "active",
|
||||
Extra: string(extraBytes),
|
||||
}
|
||||
|
||||
if err := m.modelDAO.Create(tenantModel); err != nil {
|
||||
return fmt.Errorf("fail to create model '%s': %s", model.ModelName, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ModelProviderService) ListProviderInstances(providerName, userID string) ([]map[string]interface{}, common.ErrorCode, error) {
|
||||
providerName = strings.TrimSpace(providerName)
|
||||
providerName = strings.ToLower(providerName)
|
||||
@@ -999,7 +1237,173 @@ func (m *ModelProviderService) resolveModelListTenant(userID, ownerTenantID stri
|
||||
return tenant, common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
func (m *ModelProviderService) AlterProviderInstance(userID, providerName, instanceName, newInstanceName, apiKey string) (common.ErrorCode, error) {
|
||||
func (m *ModelProviderService) AlterProviderInstance(userID, providerName, instanceIDOrName, newInstanceName, apiKey, baseURL, region string, modelInfo []CreateInstanceModelInfo, verify bool) (common.ErrorCode, error) {
|
||||
providerName = strings.TrimSpace(providerName)
|
||||
providerName = strings.ToLower(providerName)
|
||||
|
||||
tenants, err := m.userTenantDAO.GetByUserIDAndRole(userID, "owner")
|
||||
if err != nil {
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
if len(tenants) == 0 {
|
||||
return common.CodeNotFound, errors.New("user has no tenants")
|
||||
}
|
||||
tenantID := tenants[0].TenantID
|
||||
|
||||
provider, err := m.modelProviderDAO.GetByTenantIDAndProviderName(tenantID, providerName)
|
||||
if err != nil {
|
||||
return common.CodeNotFound, fmt.Errorf("provider '%s' does not exist", providerName)
|
||||
}
|
||||
|
||||
// Find the instance — try by ID first, then by name.
|
||||
instance, err := m.modelInstanceDAO.GetByID(instanceIDOrName)
|
||||
if err != nil || instance.ProviderID != provider.ID {
|
||||
instance, err = m.modelInstanceDAO.GetByProviderIDAndInstanceName(provider.ID, instanceIDOrName)
|
||||
}
|
||||
if err != nil {
|
||||
return common.CodeNotFound, fmt.Errorf("no instance found for provider '%s' and instance '%s'", providerName, instanceIDOrName)
|
||||
}
|
||||
|
||||
// Normalize api_key: VLLM with empty api_key defaults to "x".
|
||||
if strings.EqualFold(providerName, "vllm") && apiKey == "" {
|
||||
apiKey = "x"
|
||||
}
|
||||
|
||||
// Verify API key if requested.
|
||||
modelVerifyResult := make(map[string]string)
|
||||
if verify {
|
||||
modelVerifyResult = m.verifyProviderAPIKey(providerName, apiKey, region, baseURL, modelInfo)
|
||||
}
|
||||
|
||||
// Update instance record.
|
||||
instanceUpdates := map[string]interface{}{
|
||||
"api_key": apiKey,
|
||||
}
|
||||
if newInstanceName != "" && newInstanceName != instance.InstanceName {
|
||||
instanceUpdates["instance_name"] = newInstanceName
|
||||
}
|
||||
|
||||
extraFields := make(map[string]interface{})
|
||||
if baseURL != "" {
|
||||
extraFields["base_url"] = baseURL
|
||||
}
|
||||
if region != "" {
|
||||
extraFields["region"] = region
|
||||
}
|
||||
// Preserve existing extra fields not overwritten.
|
||||
existingExtra := make(map[string]interface{})
|
||||
if instance.Extra != "" {
|
||||
if err := json.Unmarshal([]byte(instance.Extra), &existingExtra); err != nil {
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
}
|
||||
for k, v := range extraFields {
|
||||
existingExtra[k] = v
|
||||
}
|
||||
extraBytes, err := json.Marshal(existingExtra)
|
||||
if err != nil {
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
instanceUpdates["extra"] = string(extraBytes)
|
||||
if err := m.modelInstanceDAO.UpdateByID(instance.ID, instanceUpdates); err != nil {
|
||||
return common.CodeServerError, fmt.Errorf("fail to update instance: %s", err.Error())
|
||||
}
|
||||
|
||||
// Use the (possibly updated) instance_name for model operations.
|
||||
effectiveInstanceName := instance.InstanceName
|
||||
if newInstanceName != "" {
|
||||
effectiveInstanceName = newInstanceName
|
||||
}
|
||||
|
||||
// Upsert models: add new ones, update existing ones, remove ones no longer selected.
|
||||
existingModels, err := m.modelDAO.GetModelsByInstanceID(instance.ID)
|
||||
if err != nil {
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
existingModelMap := make(map[string]*entity.TenantModel)
|
||||
for _, mdl := range existingModels {
|
||||
existingModelMap[mdl.ModelName] = mdl
|
||||
}
|
||||
|
||||
// Delete models that are no longer in the submitted model_info.
|
||||
submittedModelNames := make(map[string]bool)
|
||||
if modelInfo != nil {
|
||||
for _, mdl := range modelInfo {
|
||||
if mdl.ModelName != "" {
|
||||
submittedModelNames[mdl.ModelName] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
var idsToRemove []string
|
||||
for name, mdl := range existingModelMap {
|
||||
if !submittedModelNames[name] {
|
||||
idsToRemove = append(idsToRemove, mdl.ID)
|
||||
}
|
||||
}
|
||||
if len(idsToRemove) > 0 {
|
||||
if _, err := m.modelDAO.DeleteByIDs(idsToRemove); err != nil {
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
}
|
||||
|
||||
// Add or update models from model_info.
|
||||
if modelInfo != nil {
|
||||
for _, mdl := range modelInfo {
|
||||
if mdl.ModelName == "" {
|
||||
continue
|
||||
}
|
||||
// Attach verify status.
|
||||
if verify {
|
||||
verifyStatus := modelVerifyResult[mdl.ModelName]
|
||||
if verifyStatus == "" {
|
||||
verifyStatus = entity.ModelVerifyUnknown
|
||||
}
|
||||
if mdl.Extra == nil {
|
||||
mdl.Extra = make(map[string]interface{})
|
||||
}
|
||||
mdl.Extra["verify"] = verifyStatus
|
||||
}
|
||||
|
||||
if existingMdl, exists := existingModelMap[mdl.ModelName]; exists {
|
||||
// Update existing model.
|
||||
updates := make(map[string]interface{})
|
||||
if len(mdl.ModelTypes) > 0 {
|
||||
combinedType := entity.ModelType(0)
|
||||
for _, t := range mdl.ModelTypes {
|
||||
combinedType |= entity.ModelTypeFromString(t)
|
||||
}
|
||||
if int(combinedType) != existingMdl.ModelType {
|
||||
updates["model_type"] = int(combinedType)
|
||||
}
|
||||
}
|
||||
mergedExtra := make(map[string]interface{})
|
||||
if existingMdl.Extra != "" {
|
||||
json.Unmarshal([]byte(existingMdl.Extra), &mergedExtra)
|
||||
}
|
||||
if mdl.Extra != nil {
|
||||
for k, v := range mdl.Extra {
|
||||
mergedExtra[k] = v
|
||||
}
|
||||
}
|
||||
if mdl.MaxTokens > 0 {
|
||||
mergedExtra["max_tokens"] = mdl.MaxTokens
|
||||
}
|
||||
extraBytes, _ := json.Marshal(mergedExtra)
|
||||
updates["extra"] = string(extraBytes)
|
||||
if len(updates) > 0 {
|
||||
if err := m.modelDAO.UpdateByID(existingMdl.ID, updates); err != nil {
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Add new model.
|
||||
if err := m.addModelToInstance(tenantID, providerName, effectiveInstanceName, mdl); err != nil {
|
||||
return common.CodeServerError, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2401,7 +2401,8 @@ Best for: Documents with flowing, contextually connected content — such as boo
|
||||
bGPTDescription:
|
||||
'Search scientific papers via BGPT and return structured evidence from full-text studies: methods, sample sizes, limitations, conflicts of interest, data availability, blind spots, and falsification prompts. Optional API key after the free tier.',
|
||||
bgptApiKey: 'API key',
|
||||
bgptApiKeyTip: 'Optional. Leave blank for the free tier (first 50 results).',
|
||||
bgptApiKeyTip:
|
||||
'Optional. Leave blank for the free tier (first 50 results).',
|
||||
bgptDaysBack: 'Days back',
|
||||
bgptDaysBackTip: 'Optional recency filter (e.g. 365 for the last year).',
|
||||
email: 'Email',
|
||||
|
||||
@@ -38,7 +38,9 @@ const CompilationForm = ({ node }: INextOperatorForm) => {
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<LargeModelFormField ownerTenantId={ownerTenantId}></LargeModelFormField>
|
||||
<LargeModelFormField
|
||||
ownerTenantId={ownerTenantId}
|
||||
></LargeModelFormField>
|
||||
<CompilationTemplateFormField name="compilation_template_group_ids"></CompilationTemplateFormField>
|
||||
<Output list={outputList}></Output>
|
||||
</FormWrapper>
|
||||
|
||||
@@ -29,7 +29,9 @@ export function LLMSelectForm() {
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<LargeModelFormFieldWithoutFilter ownerTenantId={data?.tenant_id}></LargeModelFormFieldWithoutFilter>
|
||||
<LargeModelFormFieldWithoutFilter
|
||||
ownerTenantId={data?.tenant_id}
|
||||
></LargeModelFormFieldWithoutFilter>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user