feat: unify provider id or name routing (#16336)

This commit is contained in:
buua436
2026-06-25 13:04:21 +08:00
committed by GitHub
parent d0fc75f1bb
commit 479a9a715e
3 changed files with 281 additions and 178 deletions

View File

@@ -120,9 +120,9 @@ async def add_provider(tenant_id: str = None):
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>", methods=["GET"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>", methods=["GET"]) # noqa: F821
@login_required
def show_provider(provider_name: str):
def show_provider(provider_id_or_name: str):
"""
Show provider details.
---
@@ -132,10 +132,10 @@ def show_provider(provider_name: str):
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: header
name: Authorization
type: string
@@ -148,7 +148,7 @@ def show_provider(provider_name: str):
type: object
"""
try:
success, result = provider_api_service.show_provider(provider_name)
success, result = provider_api_service.show_provider(provider_id_or_name)
if success:
return get_result(data=result)
else:
@@ -158,10 +158,10 @@ def show_provider(provider_name: str):
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>", methods=["DELETE"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>", methods=["DELETE"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
def delete_provider(tenant_id: str = None, provider_name: str = None):
def delete_provider(tenant_id: str = None, provider_id_or_name: str = None):
"""
Delete a provider and all its models for the tenant.
---
@@ -171,10 +171,10 @@ def delete_provider(tenant_id: str = None, provider_name: str = None):
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: header
name: Authorization
type: string
@@ -187,7 +187,7 @@ def delete_provider(tenant_id: str = None, provider_name: str = None):
type: object
"""
try:
success, msg = provider_api_service.delete_provider(tenant_id, provider_name)
success, msg = provider_api_service.delete_provider(tenant_id, provider_id_or_name)
if success:
return get_result(message=msg)
else:
@@ -197,9 +197,9 @@ def delete_provider(tenant_id: str = None, provider_name: str = None):
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>/models", methods=["GET"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>/models", methods=["GET"]) # noqa: F821
@login_required
async def list_provider_models(provider_name: str):
async def list_provider_models(provider_id_or_name: str):
"""
List models for a provider.
---
@@ -209,10 +209,10 @@ async def list_provider_models(provider_name: str):
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: header
name: Authorization
type: string
@@ -232,7 +232,7 @@ async def list_provider_models(provider_name: str):
try:
api_key = request.args.get("api_key")
base_url = request.args.get("base_url")
success, result = await provider_api_service.list_provider_models(provider_name, api_key, base_url)
success, result = await provider_api_service.list_provider_models(provider_id_or_name, api_key, base_url)
if success:
return get_result(data=result)
else:
@@ -242,9 +242,9 @@ async def list_provider_models(provider_name: str):
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>/models/<path:model_name>", methods=["GET"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>/models/<path:model_name>", methods=["GET"]) # noqa: F821
@login_required
def show_provider_model(provider_name: str, model_name: str):
def show_provider_model(provider_id_or_name: str, model_name: str):
"""
Show a specific model for a provider.
---
@@ -254,10 +254,10 @@ def show_provider_model(provider_name: str, model_name: str):
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: path
name: model_name
type: string
@@ -275,7 +275,7 @@ def show_provider_model(provider_name: str, model_name: str):
type: object
"""
try:
success, result = provider_api_service.show_provider_model(provider_name, model_name)
success, result = provider_api_service.show_provider_model(provider_id_or_name, model_name)
if success:
return get_result(data=result)
else:
@@ -285,10 +285,10 @@ def show_provider_model(provider_name: str, model_name: str):
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>/instances", methods=["POST"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>/instances", methods=["POST"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
async def create_provider_instance(tenant_id: str = None, provider_name: str = None):
async def create_provider_instance(tenant_id: str = None, provider_id_or_name: str = None):
"""
Create a provider instance.
---
@@ -298,10 +298,10 @@ async def create_provider_instance(tenant_id: str = None, provider_name: str = N
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: header
name: Authorization
type: string
@@ -346,7 +346,7 @@ async def create_provider_instance(tenant_id: str = None, provider_name: str = N
model_info = data.get("model_info", [])
try:
success, msg = await provider_api_service.create_provider_instance(tenant_id, provider_name, instance_name, api_key, base_url, region, model_info)
success, msg = await provider_api_service.create_provider_instance(tenant_id, provider_id_or_name, instance_name, api_key, base_url, region, model_info)
if success:
return get_result(message=msg)
else:
@@ -356,9 +356,9 @@ async def create_provider_instance(tenant_id: str = None, provider_name: str = N
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>/connection", methods=["POST"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>/connection", methods=["POST"]) # noqa: F821
@login_required
async def verify_provider_api_key(provider_name: str = None):
async def verify_provider_api_key(provider_id_or_name: str = None):
"""
Verify api key.
---
@@ -368,10 +368,10 @@ async def verify_provider_api_key(provider_name: str = None):
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: header
name: Authorization
type: string
@@ -414,7 +414,7 @@ async def verify_provider_api_key(provider_name: str = None):
model_info = data.get("model_info", [])
try:
success, msg = await provider_api_service.verify_api_key(provider_name, api_key, base_url, region, model_info)
success, msg = await provider_api_service.verify_api_key(provider_id_or_name, api_key, base_url, region, model_info)
if success:
return get_result(message=msg)
else:
@@ -424,10 +424,10 @@ async def verify_provider_api_key(provider_name: str = None):
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>/instances", methods=["GET"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>/instances", methods=["GET"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
def list_provider_instances(tenant_id: str = None, provider_name: str = None):
def list_provider_instances(tenant_id: str = None, provider_id_or_name: str = None):
"""
List provider instances.
---
@@ -437,10 +437,10 @@ def list_provider_instances(tenant_id: str = None, provider_name: str = None):
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: header
name: Authorization
type: string
@@ -458,7 +458,7 @@ def list_provider_instances(tenant_id: str = None, provider_name: str = None):
type: object
"""
try:
success, result = provider_api_service.list_provider_instances(tenant_id, provider_name)
success, result = provider_api_service.list_provider_instances(tenant_id, provider_id_or_name)
if success:
return get_result(data=result)
else:
@@ -468,10 +468,10 @@ def list_provider_instances(tenant_id: str = None, provider_name: str = None):
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>/instances/<instance_name>", methods=["GET"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>/instances/<instance_id_or_name>", methods=["GET"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
def show_provider_instance(tenant_id: str = None, provider_name: str = None, instance_name: str = None):
def show_provider_instance(tenant_id: str = None, provider_id_or_name: str = None, instance_id_or_name: str = None):
"""
Show a provider instance.
---
@@ -481,15 +481,15 @@ def show_provider_instance(tenant_id: str = None, provider_name: str = None, ins
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: path
name: instance_name
name: instance_id_or_name
type: string
required: true
description: Instance name.
description: Instance ID or name.
- in: header
name: Authorization
type: string
@@ -502,7 +502,7 @@ def show_provider_instance(tenant_id: str = None, provider_name: str = None, ins
type: object
"""
try:
success, result = provider_api_service.show_provider_instance(tenant_id, provider_name, instance_name)
success, result = provider_api_service.show_provider_instance(tenant_id, provider_id_or_name, instance_id_or_name)
if success:
return get_result(data=result)
else:
@@ -512,10 +512,10 @@ def show_provider_instance(tenant_id: str = None, provider_name: str = None, ins
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>/instances", methods=["DELETE"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>/instances", methods=["DELETE"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
async def drop_provider_instances(tenant_id: str = None, provider_name: str = None):
async def drop_provider_instances(tenant_id: str = None, provider_id_or_name: str = None):
"""
Drop provider instances.
---
@@ -525,10 +525,10 @@ async def drop_provider_instances(tenant_id: str = None, provider_name: str = No
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: header
name: Authorization
type: string
@@ -547,7 +547,7 @@ async def drop_provider_instances(tenant_id: str = None, provider_name: str = No
type: array
items:
type: string
description: List of instance names to drop.
description: List of instance IDs or names to drop.
responses:
200:
description: Instances dropped successfully.
@@ -563,7 +563,7 @@ async def drop_provider_instances(tenant_id: str = None, provider_name: str = No
return get_error_argument_result(message="instances is required")
try:
success, msg = provider_api_service.drop_provider_instances(tenant_id, provider_name, instances)
success, msg = provider_api_service.drop_provider_instances(tenant_id, provider_id_or_name, instances)
if success:
return get_result(message=msg)
else:
@@ -573,10 +573,10 @@ async def drop_provider_instances(tenant_id: str = None, provider_name: str = No
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>/instances/<instance_name>/models", methods=["GET"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>/instances/<instance_id_or_name>/models", methods=["GET"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
def list_instance_models(tenant_id: str = None, provider_name: str = None, instance_name: str = None):
def list_instance_models(tenant_id: str = None, provider_id_or_name: str = None, instance_id_or_name: str = None):
"""
List models for a provider instance.
---
@@ -586,15 +586,15 @@ def list_instance_models(tenant_id: str = None, provider_name: str = None, insta
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: path
name: instance_name
name: instance_id_or_name
type: string
required: true
description: Instance name.
description: Instance ID or name.
- in: query
name: supported
type: string
@@ -619,7 +619,7 @@ def list_instance_models(tenant_id: str = None, provider_name: str = None, insta
supported_only = request.args.get("supported", "").lower() == "true"
try:
success, result = provider_api_service.list_instance_models(
tenant_id, provider_name, instance_name, supported_only
tenant_id, provider_id_or_name, instance_id_or_name, supported_only
)
if success:
return get_result(data=result)
@@ -630,10 +630,10 @@ def list_instance_models(tenant_id: str = None, provider_name: str = None, insta
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>/instances/<instance_name>/models", methods=["PUT"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>/instances/<instance_id_or_name>/models", methods=["PUT"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
async def update_instance_models(tenant_id: str, provider_name: str, instance_name: str):
async def update_instance_models(tenant_id: str, provider_id_or_name: str, instance_id_or_name: str):
"""
Batch update model_type for models in instance.
---
@@ -643,15 +643,15 @@ async def update_instance_models(tenant_id: str, provider_name: str, instance_na
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: path
name: instance_name
name: instance_id_or_name
type: string
required: true
description: Instance name.
description: Instance ID or name.
- in: header
name: Authorization
type: string
@@ -680,7 +680,9 @@ async def update_instance_models(tenant_id: str, provider_name: str, instance_na
model_name = data["model_name"]
model_type = data["model_type"]
try:
success, msg = provider_api_service.update_instance_models(tenant_id, provider_name, instance_name, model_name, model_type)
success, msg = provider_api_service.update_instance_models(
tenant_id, provider_id_or_name, instance_id_or_name, model_name, model_type
)
if success:
return get_result(message=msg)
else:
@@ -690,10 +692,10 @@ async def update_instance_models(tenant_id: str, provider_name: str, instance_na
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>/instances/<instance_name>/models", methods=["POST"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>/instances/<instance_id_or_name>/models", methods=["POST"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
async def add_model_to_instance(tenant_id: str, provider_name: str, instance_name: str):
async def add_model_to_instance(tenant_id: str, provider_id_or_name: str, instance_id_or_name: str):
"""
Add a model to an instance.
---
@@ -703,15 +705,15 @@ async def add_model_to_instance(tenant_id: str, provider_name: str, instance_nam
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: path
name: instance_name
name: instance_id_or_name
type: string
required: true
description: Instance name.
description: Instance ID or name.
- in: header
name: Authorization
type: string
@@ -754,7 +756,7 @@ async def add_model_to_instance(tenant_id: str, provider_name: str, instance_nam
try:
success, result = provider_api_service.add_model_to_instance(
tenant_id, provider_name, instance_name, model_name, model_type, max_tokens, extra
tenant_id, provider_id_or_name, instance_id_or_name, model_name, model_type, max_tokens, extra
)
if success:
return get_result(message=result)
@@ -765,10 +767,10 @@ async def add_model_to_instance(tenant_id: str, provider_name: str, instance_nam
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>/instances/<instance_name>/models/<path:model_name>", methods=["PATCH"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>/instances/<instance_id_or_name>/models/<path:model_name>", methods=["PATCH"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
async def enable_or_disable_model(tenant_id: str = None, provider_name: str = None, instance_name: str = None, model_name: str = None):
async def enable_or_disable_model(tenant_id: str = None, provider_id_or_name: str = None, instance_id_or_name: str = None, model_name: str = None):
"""
Enable or disable a model.
---
@@ -778,15 +780,15 @@ async def enable_or_disable_model(tenant_id: str = None, provider_name: str = No
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: path
name: instance_name
name: instance_id_or_name
type: string
required: true
description: Instance name.
description: Instance ID or name.
- in: path
name: model_name
type: string
@@ -825,7 +827,9 @@ async def enable_or_disable_model(tenant_id: str = None, provider_name: str = No
return get_error_argument_result(message="status must be 'active' or 'inactive'")
try:
success, msg = provider_api_service.update_model_status(tenant_id, provider_name, instance_name, model_name, status)
success, msg = provider_api_service.update_model_status(
tenant_id, provider_id_or_name, instance_id_or_name, model_name, status
)
if success:
return get_result(message=msg)
else:
@@ -835,10 +839,10 @@ async def enable_or_disable_model(tenant_id: str = None, provider_name: str = No
return get_error_data_result(message="Internal server error")
@manager.route("/providers/<provider_name>/instances/<instance_name>/models/<path:model_name>", methods=["POST"]) # noqa: F821
@manager.route("/providers/<provider_id_or_name>/instances/<instance_id_or_name>/models/<path:model_name>", methods=["POST"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
async def chat_to_model(tenant_id: str = None, provider_name: str = None, instance_name: str = None, model_name: str = None):
async def chat_to_model(tenant_id: str = None, provider_id_or_name: str = None, instance_id_or_name: str = None, model_name: str = None):
"""
Chat to a model.
---
@@ -848,15 +852,15 @@ async def chat_to_model(tenant_id: str = None, provider_name: str = None, instan
- ApiKeyAuth: []
parameters:
- in: path
name: provider_name
name: provider_id_or_name
type: string
required: true
description: Provider name.
description: Provider ID or name.
- in: path
name: instance_name
name: instance_id_or_name
type: string
required: true
description: Instance name.
description: Instance ID or name.
- in: path
name: model_name
type: string
@@ -901,7 +905,7 @@ async def chat_to_model(tenant_id: str = None, provider_name: str = None, instan
try:
success, result = await provider_api_service.chat_to_model(
tenant_id, provider_name, instance_name, model_name, message, stream, thinking
tenant_id, provider_id_or_name, instance_id_or_name, model_name, message, stream, thinking
)
if not success:
return get_error_data_result(message=result)

View File

@@ -157,37 +157,42 @@ def add_provider(tenant_id: str, provider_name: str):
return True, "success"
def delete_provider(tenant_id: str, provider_name: str):
def delete_provider(tenant_id: str, provider_id_or_name: str):
"""
Delete all instances and models for a provider.
:param tenant_id: tenant ID
:param provider_name: provider/factory name
:param provider_id_or_name: provider ID or provider/factory name
:return: (success, result_or_error_message)
"""
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name)
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_id(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"Provider {provider_name} not found"
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"Provider {provider_id_or_name} not found"
instance_objs = TenantModelInstanceService.get_all_by_provider_id(provider_obj.id)
if not instance_objs:
return False, f"No instances found for provider {provider_name}"
instance_ids = [instance_obj.id for instance_obj in instance_objs]
delete_models_by_instance_ids(instance_ids)
delete_instances_by_provider_ids([provider_obj.id])
TenantModelProviderService.delete_by_tenant_id_and_provider_name(tenant_id, provider_name)
if instance_objs:
instance_ids = [instance_obj.id for instance_obj in instance_objs]
delete_models_by_instance_ids(instance_ids)
delete_instances_by_provider_ids([provider_obj.id])
TenantModelProviderService.delete_by_tenant_id_and_provider_name(tenant_id, provider_obj.provider_name)
return True, "success"
def show_provider(provider_name: str):
def show_provider(provider_id_or_name: str):
"""
Show provider details from LLMFactories.
:param provider_name: provider/factory name
:param provider_id_or_name: provider/factory ID or name
:return: (success, result_or_error_message)
"""
provider_obj = None
if provider_id_or_name:
_, provider_obj = TenantModelProviderService.get_by_id(provider_id_or_name)
provider_name = provider_obj.provider_name if provider_obj else provider_id_or_name
fac_list = [f for f in FACTORY_LLM_INFOS if f["name"]==provider_name]
if not fac_list:
return False, f"Provider '{provider_name}' not found"
return False, f"Provider '{provider_id_or_name}' not found"
factory_info = fac_list[0]
return True, {
"base_url": {
@@ -198,18 +203,22 @@ def show_provider(provider_name: str):
}
async def list_provider_models(provider_name: str, api_key: str = None, base_url: str = None):
async def list_provider_models(provider_id_or_name: str, api_key: str = None, base_url: str = None):
"""
List all models for a provider from the LLM dictionary.
:param provider_name: provider/factory name
:param provider_id_or_name: provider ID or provider/factory name
:param api_key: api key
:param base_url: base url
:return: (success, result_or_error_message)
"""
factory_info = [f for f in FACTORY_LLM_INFOS if f["name"]==provider_name]
provider_obj = None
if provider_id_or_name:
_, provider_obj = TenantModelProviderService.get_by_id(provider_id_or_name)
provider_name = provider_obj.provider_name if provider_obj else provider_id_or_name
factory_info = [f for f in FACTORY_LLM_INFOS if f["name"] == provider_name]
if not factory_info:
return False, f"Provider '{provider_name}' not found"
return False, f"Provider '{provider_id_or_name}' not found"
static_llms = [{
"name": _factory_llm_name(llm),
"max_tokens": llm["max_tokens"],
@@ -230,7 +239,7 @@ async def list_provider_models(provider_name: str, api_key: str = None, base_url
remote_models = await ModelMeta[provider_name](api_key, model_base_url).get_model_list()
if not static_llms and not remote_models:
return False, f"No models found for provider '{provider_name}'"
return False, f"No models found for provider '{provider_id_or_name}'"
# Merge static and remote models, preferring remote_models on name conflicts
merged = {m["name"]: m for m in static_llms}
@@ -241,20 +250,24 @@ async def list_provider_models(provider_name: str, api_key: str = None, base_url
return True, models
def show_provider_model(provider_name: str, model_name: str):
def show_provider_model(provider_id_or_name: str, model_name: str):
"""
Show a specific model for a provider.
:param provider_name: provider/factory name
:param provider_id_or_name: provider/factory ID or name
:param model_name: model name
:return: (success, result_or_error_message)
"""
provider_obj = None
if provider_id_or_name:
_, provider_obj = TenantModelProviderService.get_by_id(provider_id_or_name)
provider_name = provider_obj.provider_name if provider_obj else provider_id_or_name
factory_info = [f for f in FACTORY_LLM_INFOS if f["name"] == provider_name]
if not factory_info:
return False, f"Provider '{provider_name}' not found"
return False, f"Provider '{provider_id_or_name}' not found"
llms = factory_info[0]["llm"]
if not llms:
return False, f"No models found for provider '{provider_name}'"
return False, f"No models found for provider '{provider_id_or_name}'"
target_llm = [llm for llm in llms if _factory_llm_name(llm) == model_name]
if not target_llm:
return False, f"Model '{model_name}' not found"
@@ -269,7 +282,7 @@ def show_provider_model(provider_name: str, model_name: str):
}
async def create_provider_instance(tenant_id: str, provider_name: str, instance_name: str, api_key: str|dict, base_url: str, region: str, model_info: list[dict]=None):
async def create_provider_instance(tenant_id: str, provider_id_or_name: str, instance_name: str, api_key: str|dict, base_url: str, region: str, model_info: list[dict]=None):
"""
Create a provider instance.
@@ -277,7 +290,7 @@ async def create_provider_instance(tenant_id: str, provider_name: str, instance_
model all records under a factory share the same API key configuration.
:param tenant_id: tenant ID
:param provider_name: provider/factory name
:param provider_id_or_name: provider/factory ID or name
:param instance_name: instance name (used as a logical identifier)
:param api_key: API key
:param base_url: base url
@@ -293,8 +306,16 @@ async def create_provider_instance(tenant_id: str, provider_name: str, instance_
}]
:return: (success, result_or_error_message)
"""
if not provider_name:
return False, "Provider name is required"
if not provider_id_or_name:
return False, "Provider ID or name is required"
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_id(tenant_id, provider_id_or_name)
if not provider_obj:
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"Provider '{provider_id_or_name}' does not exist"
provider_name = provider_obj.provider_name
base_url = _normalize_provider_base_url(provider_name, base_url)
@@ -306,10 +327,6 @@ async def create_provider_instance(tenant_id: str, provider_name: str, instance_
if provider_name not in allowed_factories:
return False, f"Provider '{provider_name}' is not allowed"
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name)
if not provider_obj:
return False, f"Provider '{provider_name}' does not exist"
api_key_str = ""
if api_key:
api_key_str = api_key if isinstance(api_key, str) else json.dumps(api_key)
@@ -335,17 +352,19 @@ async def create_provider_instance(tenant_id: str, provider_name: str, instance_
return True, "success"
def list_provider_instances(tenant_id: str, provider_name: str):
def list_provider_instances(tenant_id: str, provider_id_or_name: str):
"""
List provider instances for a tenant.
:param tenant_id: tenant ID
:param provider_name: provider/factory name
:param provider_id_or_name: provider/factory ID or name
:return: (success, result_or_error_message)
"""
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name)
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_id(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_name}'"
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_id_or_name}'"
provider_id = provider_obj.id
instance_objs = TenantModelInstanceService.get_all_by_provider_id(provider_id)
if not instance_objs:
@@ -364,11 +383,11 @@ def list_provider_instances(tenant_id: str, provider_name: str):
return True, instances
async def verify_api_key(provider_name: str, api_key: str|dict, base_url: str=None, region: str=None, model_info: list[dict]=None):
async def verify_api_key(provider_id_or_name: str, api_key: str|dict, base_url: str=None, region: str=None, model_info: list[dict]=None):
"""
Verify API key for a provider.
:param provider_name: provider/factory name
:param provider_id_or_name: provider/factory ID or name
:param api_key: API key
:param base_url: base url
:param region: region
@@ -383,8 +402,13 @@ async def verify_api_key(provider_name: str, api_key: str|dict, base_url: str=No
}]
:return: (success, result_or_error_message)
"""
if not provider_name:
return False, "Provider name is required"
if not provider_id_or_name:
return False, "Provider ID or name is required"
provider_obj = None
if provider_id_or_name:
_, provider_obj = TenantModelProviderService.get_by_id(provider_id_or_name)
provider_name = provider_obj.provider_name if provider_obj else provider_id_or_name
base_url = _normalize_provider_base_url(provider_name, base_url)
@@ -395,18 +419,18 @@ async def verify_api_key(provider_name: str, api_key: str|dict, base_url: str=No
factory_info = [f for f in FACTORY_LLM_INFOS if f["name"] == target_factory_name]
if not factory_info:
return False, f"Provider '{provider_name}' not found"
return False, f"Provider '{provider_id_or_name}' not found"
factory_llms = factory_info[0]["llm"]
if not factory_llms:
if not model_info:
return False, f"No models found for provider '{provider_name}'"
return False, f"No models found for provider '{provider_id_or_name}'"
factory_llms = [{
"model_type": _type,
"llm_name": model.get("model_name", ""),
} for model in model_info if model for _type in model.get("model_type", []) ]
if not factory_llms:
return False, f"No valid models found for provider '{provider_name}'"
return False, f"No valid models found for provider '{provider_id_or_name}'"
# test if api key works
chat_passed, embd_passed, rerank_passed, ocr_passed, tts_passed = False, False, False, False, False
@@ -535,22 +559,30 @@ async def verify_api_key(provider_name: str, api_key: str|dict, base_url: str=No
return success, "success" if success else msg
def show_provider_instance(tenant_id: str, provider_name: str, instance_name: str):
def show_provider_instance(tenant_id: str, provider_id_or_name: str, instance_id_or_name: str):
"""
Show a specific provider instance.
:param tenant_id: tenant ID
:param provider_name: provider/factory name
:param instance_name: instance name
:param provider_id_or_name: provider/factory ID or name
:param instance_id_or_name: instance ID or name
:return: (success, result_or_error_message)
"""
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name)
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_id(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_name}'"
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_id_or_name}'"
provider_id = provider_obj.id
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_id, instance_name)
instance_obj = None
if instance_id_or_name:
_, instance_obj = TenantModelInstanceService.get_by_id(instance_id_or_name)
if instance_obj and instance_obj.provider_id != provider_id:
instance_obj = None
if not instance_obj:
return False, f"No instance found for provider '{provider_name}' and instance '{instance_name}'"
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_id, instance_id_or_name)
if not instance_obj:
return False, f"No instance found for provider '{provider_id_or_name}' and instance '{instance_id_or_name}'"
extra_fields = json.loads(instance_obj.extra) if instance_obj.extra else {}
return True, {
@@ -562,30 +594,38 @@ def show_provider_instance(tenant_id: str, provider_name: str, instance_name: st
}
def drop_provider_instances(tenant_id: str, provider_name: str, instance_names: list):
def drop_provider_instances(tenant_id: str, provider_id_or_name: str, instance_id_or_names: list):
"""
Drop provider instances.
for the specified models/instances.
:param tenant_id: tenant ID
:param provider_name: provider/factory name
:param instance_names: list of instance names to drop
:param provider_id_or_name: provider/factory ID or name
:param instance_id_or_names: list of instance IDs or names to drop
:return: (success, result_or_error_message)
"""
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name)
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_id(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_name}'"
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_id_or_name}'"
provider_id = provider_obj.id
not_exist_instances = []
instance_ids = []
for instance_name in instance_names:
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_id, instance_name)
for instance_id_or_name in instance_id_or_names:
instance_obj = None
if instance_id_or_name:
_, instance_obj = TenantModelInstanceService.get_by_id(instance_id_or_name)
if instance_obj and instance_obj.provider_id != provider_id:
instance_obj = None
if not instance_obj:
not_exist_instances.append(instance_name)
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_id, instance_id_or_name)
if not instance_obj:
not_exist_instances.append(instance_id_or_name)
continue
instance_ids.append(instance_obj.id)
if not_exist_instances:
return False, f"No instance found for provider '{provider_name}' and instance '{not_exist_instances}'"
return False, f"No instance found for provider '{provider_id_or_name}' and instance '{not_exist_instances}'"
delete_models_by_instance_ids(instance_ids)
TenantModelInstanceService.delete_by_ids(instance_ids)
return True, None
@@ -642,7 +682,7 @@ def _hybrid_get_instance_models(provider_name: str, instance_id: str):
return True, models
def list_instance_models(tenant_id: str, provider_name: str, instance_name: str, supported_only: bool = False):
def list_instance_models(tenant_id: str, provider_id_or_name: str, instance_id_or_name: str, supported_only: bool = False):
"""
List models for a provider instance.
@@ -652,52 +692,68 @@ def list_instance_models(tenant_id: str, provider_name: str, instance_name: str,
- Models present in tenant_model table are marked "inactive", others "active".
:param tenant_id: tenant ID
:param provider_name: provider/factory name
:param instance_name: instance name
:param provider_id_or_name: provider/factory ID or name
:param instance_id_or_name: instance ID or name
:param supported_only: if True, only list supported models (from LLM dictionary)
:return: (success, result_or_error_message)
"""
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name)
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_id(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_name}'"
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_id_or_name}'"
if supported_only:
# List all models supported by this provider from the LLM dictionary
factory_info = [f for f in FACTORY_LLM_INFOS if f["name"] == provider_name]
factory_info = [f for f in FACTORY_LLM_INFOS if f["name"] == provider_obj.provider_name]
if not factory_info:
return False, f"Provider '{provider_name}' not found"
return False, f"Provider '{provider_id_or_name}' not found"
llms = factory_info[0].get("llm", [])
models = [{"name": llm["llm_name"]} for llm in llms]
models.sort(key=lambda x: x["name"])
return True, models
# Get instance
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_name)
instance_obj = None
if instance_id_or_name:
_, instance_obj = TenantModelInstanceService.get_by_id(instance_id_or_name)
if instance_obj and instance_obj.provider_id != provider_obj.id:
instance_obj = None
if not instance_obj:
return False, f"No instance found for provider '{provider_name}' and instance '{instance_name}'"
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_id_or_name)
if not instance_obj:
return False, f"No instance found for provider '{provider_id_or_name}' and instance '{instance_id_or_name}'"
return _hybrid_get_instance_models(provider_name, instance_obj.id)
return _hybrid_get_instance_models(provider_obj.provider_name, instance_obj.id)
def update_instance_models(tenant_id: str, provider_name: str, instance_name: str, model_names: list, model_types: list):
def update_instance_models(tenant_id: str, provider_id_or_name: str, instance_id_or_name: str, model_names: list, model_types: list):
if not model_names or not model_types:
return False, "model_name and model_type are required"
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name)
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_id(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_name}'"
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_name)
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_id_or_name}'"
instance_obj = None
if instance_id_or_name:
_, instance_obj = TenantModelInstanceService.get_by_id(instance_id_or_name)
if instance_obj and instance_obj.provider_id != provider_obj.id:
instance_obj = None
if not instance_obj:
return False, f"No instance found for provider '{provider_name}' and instance '{instance_name}'"
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_id_or_name)
if not instance_obj:
return False, f"No instance found for provider '{provider_id_or_name}' and instance '{instance_id_or_name}'"
found, models = _hybrid_get_instance_models(provider_name, instance_obj.id)
found, models = _hybrid_get_instance_models(provider_obj.provider_name, instance_obj.id)
if not found:
return False, models
model_info_map = {model["name"]: model for model in models}
not_exist_models = set(model_names) - set(model_info_map.keys())
if not_exist_models:
return False, f"Models {not_exist_models} not found for provider '{provider_name}' and instance '{instance_name}'"
return False, f"Models {not_exist_models} not found for provider '{provider_id_or_name}' and instance '{instance_id_or_name}'"
for model_name in model_names:
model_info = model_info_map.get(model_name, {})
TenantModelService.upsert_model_type(
@@ -712,19 +768,27 @@ def update_instance_models(tenant_id: str, provider_name: str, instance_name: st
return True, "success"
def add_model_to_instance(tenant_id: str, provider_name: str, instance_name: str, model_name: str, model_type: str|list[str], max_tokens: int=8192, extra: dict=None):
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name)
def add_model_to_instance(tenant_id: str, provider_id_or_name: str, instance_id_or_name: str, model_name: str, model_type: str|list[str], max_tokens: int=8192, extra: dict=None):
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_id(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_name}'"
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_name)
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_id_or_name}'"
instance_obj = None
if instance_id_or_name:
_, instance_obj = TenantModelInstanceService.get_by_id(instance_id_or_name)
if instance_obj and instance_obj.provider_id != provider_obj.id:
instance_obj = None
if not instance_obj:
return False, f"No instance found for provider '{provider_name}' and instance '{instance_name}'"
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_id_or_name)
if not instance_obj:
return False, f"No instance found for provider '{provider_id_or_name}' and instance '{instance_id_or_name}'"
model_obj = TenantModelService.get_by_provider_id_and_instance_id_and_model_name(provider_obj.id, instance_obj.id, model_name)
if model_obj:
return False, f"Model '{model_name}' already exists for provider '{provider_name}' and instance '{instance_name}'"
factory_info = [f for f in FACTORY_LLM_INFOS if f["name"] == provider_name]
return False, f"Model '{model_name}' already exists for provider '{provider_id_or_name}' and instance '{instance_id_or_name}'"
factory_info = [f for f in FACTORY_LLM_INFOS if f["name"] == provider_obj.provider_name]
if not factory_info:
return False, f"Provider '{provider_name}' not found"
return False, f"Provider '{provider_id_or_name}' not found"
llms = factory_info[0].get("llm", [])
if isinstance(model_type, str):
model_type = [model_type]
@@ -747,7 +811,7 @@ def add_model_to_instance(tenant_id: str, provider_name: str, instance_name: str
return True, "success"
def update_model_status(tenant_id: str, provider_name: str, instance_name: str, model_name: str, status: str):
def update_model_status(tenant_id: str, provider_id_or_name: str, instance_id_or_name: str, model_name: str, status: str):
"""
Enable or disable a model for a provider instance.
@@ -757,8 +821,8 @@ def update_model_status(tenant_id: str, provider_name: str, instance_name: str,
- status="inactive": create a record with status="inactive".
:param tenant_id: tenant ID
:param provider_name: provider/factory name
:param instance_name: instance name
:param provider_id_or_name: provider/factory ID or name
:param instance_id_or_name: instance ID or name
:param model_name: model name
:param status: "active" or "inactive" (ActiveStatusEnum values)
:return: (success, result_or_error_message)
@@ -767,14 +831,22 @@ def update_model_status(tenant_id: str, provider_name: str, instance_name: str,
return False, f"status must be '{ActiveStatusEnum.ACTIVE.value}' or '{ActiveStatusEnum.INACTIVE.value}'"
# Check if provider exists for this tenant
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name)
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_id(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_name}'"
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_id_or_name}'"
# Check if instance exists
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_name)
instance_obj = None
if instance_id_or_name:
_, instance_obj = TenantModelInstanceService.get_by_id(instance_id_or_name)
if instance_obj and instance_obj.provider_id != provider_obj.id:
instance_obj = None
if not instance_obj:
return False, f"No instance found for provider '{provider_name}' and instance '{instance_name}'"
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_id_or_name)
if not instance_obj:
return False, f"No instance found for provider '{provider_id_or_name}' and instance '{instance_id_or_name}'"
# Check if model record already exists in tenant_model table
model_obj_list = TenantModelService.get_by_provider_id_and_instance_id_and_model_name(
@@ -791,13 +863,13 @@ def update_model_status(tenant_id: str, provider_name: str, instance_name: str,
return True, None
# status is "inactive" — create a record with inactive status
# Look up model schema from FACTORY_LLM_INFOS
factory_info = [f for f in FACTORY_LLM_INFOS if f["name"] == provider_name]
factory_info = [f for f in FACTORY_LLM_INFOS if f["name"] == provider_obj.provider_name]
if not factory_info:
return False, f"Provider '{provider_name}' not found"
return False, f"Provider '{provider_id_or_name}' not found"
llms = factory_info[0].get("llm", [])
target_llm = [llm for llm in llms if llm["llm_name"] == model_name]
if not target_llm:
return False, f"provider {provider_name} model {model_name} not found"
return False, f"provider {provider_obj.provider_name} model {model_name} not found"
for model_type in _factory_model_types(target_llm[0]):
TenantModelService.insert(
@@ -813,13 +885,13 @@ def update_model_status(tenant_id: str, provider_name: str, instance_name: str,
return True, None
async def chat_to_model(tenant_id: str, provider_name: str, instance_name: str, model_name: str, message: str, stream: bool = False, thinking: bool = False):
async def chat_to_model(tenant_id: str, provider_id_or_name: str, instance_id_or_name: str, model_name: str, message: str, stream: bool = False, thinking: bool = False):
"""
Chat to a model.
:param tenant_id: tenant ID
:param provider_name: provider/factory name
:param instance_name: instance name
:param provider_id_or_name: provider/factory ID or name
:param instance_id_or_name: instance ID or name
:param model_name: model name
:param message: chat message
:param stream: whether to stream the response
@@ -828,6 +900,25 @@ async def chat_to_model(tenant_id: str, provider_name: str, instance_name: str,
"""
from api.db.services.llm_service import LLMBundle
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_id(tenant_id, provider_id_or_name)
if not provider_obj:
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_id_or_name)
if not provider_obj:
return False, f"No provider found for provider '{provider_id_or_name}'"
instance_obj = None
if instance_id_or_name:
_, instance_obj = TenantModelInstanceService.get_by_id(instance_id_or_name)
if instance_obj and instance_obj.provider_id != provider_obj.id:
instance_obj = None
if not instance_obj:
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_id_or_name)
if not instance_obj:
return False, f"No instance found for provider '{provider_id_or_name}' and instance '{instance_id_or_name}'"
provider_name = provider_obj.provider_name
instance_name = instance_obj.instance_name
# Get model config
composite_name = f"{model_name}@{instance_name}@{provider_name}"
try:

View File

@@ -28,6 +28,14 @@ class TenantModelProviderService(CommonService):
cls.model.provider_name == provider_name,
)
@classmethod
@DB.connection_context()
def get_by_tenant_id_and_provider_id(cls, tenant_id, provider_id):
return cls.model.get_or_none(
cls.model.tenant_id == tenant_id,
cls.model.id == provider_id,
)
@classmethod
@DB.connection_context()
def get_by_tenant_id(cls, tenant_id):