mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
feat: 导入上游模型时自动匹配或创建 GlobalModel
- 导入模型时按名称精确匹配已有的 GlobalModel - 如果没有匹配到则自动创建新的 GlobalModel - 导入完成后清除 /v1/models 缓存使模型参与路由
This commit is contained in:
@@ -339,29 +339,29 @@ async def import_models_from_upstream(
|
|||||||
"""
|
"""
|
||||||
从上游提供商导入模型
|
从上游提供商导入模型
|
||||||
|
|
||||||
从上游提供商导入模型列表。导入的模型作为独立的 ProviderModel 存储,
|
从上游提供商导入模型列表。自动匹配已有的 GlobalModel,如果不存在则自动创建。
|
||||||
不会自动创建 GlobalModel。后续需要手动关联 GlobalModel 才能参与路由。
|
|
||||||
|
|
||||||
**流程说明**:
|
**流程说明**:
|
||||||
1. 检查模型是否已存在于当前 Provider(按 provider_model_name 匹配)
|
1. 检查模型是否已存在于当前 Provider(按 provider_model_name 匹配)
|
||||||
2. 创建新的 ProviderModel(global_model_id = NULL)
|
2. 尝试按名称精确匹配已有的 GlobalModel
|
||||||
3. 支持设置价格覆盖(tiered_pricing, price_per_request)
|
3. 如果没有匹配到,自动创建新的 GlobalModel
|
||||||
|
4. 创建 Model 记录并关联到 GlobalModel
|
||||||
|
|
||||||
**路径参数**:
|
**路径参数**:
|
||||||
- `provider_id`: 提供商 ID
|
- `provider_id`: 提供商 ID
|
||||||
|
|
||||||
**请求体字段**:
|
**请求体字段**:
|
||||||
- `model_ids`: 模型 ID 数组(必填,每个 ID 长度 1-100 字符)
|
- `model_ids`: 模型 ID 数组(必填,每个 ID 长度 1-100 字符)
|
||||||
- `tiered_pricing`: 可选的阶梯计费配置(应用于所有导入的模型)
|
- `tiered_pricing`: 可选的阶梯计费配置(应用于所有导入的模型和新创建的 GlobalModel)
|
||||||
- `price_per_request`: 可选的按次计费价格(应用于所有导入的模型)
|
- `price_per_request`: 可选的按次计费价格(应用于所有导入的模型和新创建的 GlobalModel)
|
||||||
|
|
||||||
**返回字段**:
|
**返回字段**:
|
||||||
- `success`: 成功导入的模型数组,每项包含:
|
- `success`: 成功导入的模型数组,每项包含:
|
||||||
- `model_id`: 模型 ID
|
- `model_id`: 模型 ID
|
||||||
- `provider_model_id`: 提供商模型 ID
|
- `provider_model_id`: 提供商模型 ID
|
||||||
- `global_model_id`: 全局模型 ID(如果已关联)
|
- `global_model_id`: 全局模型 ID
|
||||||
- `global_model_name`: 全局模型名称(如果已关联)
|
- `global_model_name`: 全局模型名称
|
||||||
- `created_global_model`: 是否新创建了全局模型(始终为 false)
|
- `created_global_model`: 是否新创建了全局模型
|
||||||
- `errors`: 失败的模型数组,每项包含:
|
- `errors`: 失败的模型数组,每项包含:
|
||||||
- `model_id`: 模型 ID
|
- `model_id`: 模型 ID
|
||||||
- `error`: 错误信息
|
- `error`: 错误信息
|
||||||
@@ -657,7 +657,7 @@ class AdminBatchAssignModelsToProviderAdapter(AdminApiAdapter):
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class AdminImportFromUpstreamAdapter(AdminApiAdapter):
|
class AdminImportFromUpstreamAdapter(AdminApiAdapter):
|
||||||
"""从上游提供商导入模型(不创建 GlobalModel,作为独立 ProviderModel)"""
|
"""从上游提供商导入模型(自动匹配或创建 GlobalModel)"""
|
||||||
|
|
||||||
provider_id: str
|
provider_id: str
|
||||||
payload: ImportFromUpstreamRequest
|
payload: ImportFromUpstreamRequest
|
||||||
@@ -682,6 +682,17 @@ class AdminImportFromUpstreamAdapter(AdminApiAdapter):
|
|||||||
):
|
):
|
||||||
price_per_request = self.payload.price_per_request
|
price_per_request = self.payload.price_per_request
|
||||||
|
|
||||||
|
# 默认价格配置(用于自动创建的 GlobalModel)
|
||||||
|
default_pricing = {
|
||||||
|
"tiers": [
|
||||||
|
{
|
||||||
|
"up_to": None,
|
||||||
|
"input_price_per_1m": 0.0,
|
||||||
|
"output_price_per_1m": 0.0,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
for model_id in self.payload.model_ids:
|
for model_id in self.payload.model_ids:
|
||||||
# 输入验证:检查 model_id 长度
|
# 输入验证:检查 model_id 长度
|
||||||
if not model_id or len(model_id) > 100:
|
if not model_id or len(model_id) > 100:
|
||||||
@@ -726,10 +737,33 @@ class AdminImportFromUpstreamAdapter(AdminApiAdapter):
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 2. 创建新的 Model 记录(不关联 GlobalModel)
|
# 2. 尝试匹配已有的 GlobalModel(按名称精确匹配)
|
||||||
|
global_model = (
|
||||||
|
db.query(GlobalModel).filter(GlobalModel.name == model_id).first()
|
||||||
|
)
|
||||||
|
created_global_model = False
|
||||||
|
|
||||||
|
# 3. 如果没有匹配到,自动创建新的 GlobalModel
|
||||||
|
if not global_model:
|
||||||
|
global_model = GlobalModel(
|
||||||
|
name=model_id,
|
||||||
|
display_name=model_id,
|
||||||
|
default_tiered_pricing=tiered_pricing or default_pricing,
|
||||||
|
default_price_per_request=price_per_request,
|
||||||
|
is_active=True,
|
||||||
|
)
|
||||||
|
db.add(global_model)
|
||||||
|
db.flush()
|
||||||
|
created_global_model = True
|
||||||
|
logger.info(
|
||||||
|
f"Auto-created GlobalModel: {model_id} for provider {provider.name} "
|
||||||
|
f"by {context.user.username}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 4. 创建新的 Model 记录(关联到 GlobalModel)
|
||||||
new_model = Model(
|
new_model = Model(
|
||||||
provider_id=self.provider_id,
|
provider_id=self.provider_id,
|
||||||
global_model_id=None, # 独立模型,不关联 GlobalModel
|
global_model_id=global_model.id,
|
||||||
provider_model_name=model_id,
|
provider_model_name=model_id,
|
||||||
is_active=True,
|
is_active=True,
|
||||||
tiered_pricing=tiered_pricing,
|
tiered_pricing=tiered_pricing,
|
||||||
@@ -743,14 +777,15 @@ class AdminImportFromUpstreamAdapter(AdminApiAdapter):
|
|||||||
success.append(
|
success.append(
|
||||||
ImportFromUpstreamSuccessItem(
|
ImportFromUpstreamSuccessItem(
|
||||||
model_id=model_id,
|
model_id=model_id,
|
||||||
global_model_id="", # 未关联
|
global_model_id=global_model.id,
|
||||||
global_model_name="", # 未关联
|
global_model_name=global_model.name,
|
||||||
provider_model_id=new_model.id,
|
provider_model_id=new_model.id,
|
||||||
created_global_model=False,
|
created_global_model=created_global_model,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Created independent ProviderModel: {model_id} for provider {provider.name}"
|
f"Imported model: {model_id} -> GlobalModel: {global_model.name} "
|
||||||
|
f"(created={created_global_model}) for provider {provider.name}"
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# 回滚到 savepoint
|
# 回滚到 savepoint
|
||||||
@@ -762,9 +797,11 @@ class AdminImportFromUpstreamAdapter(AdminApiAdapter):
|
|||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Imported {len(success)} independent models to provider {provider.name} by {context.user.username}"
|
f"Imported {len(success)} models to provider {provider.name} by {context.user.username}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 不需要清除 /v1/models 缓存,因为独立模型不参与路由
|
# 清除 /v1/models 列表缓存(导入的模型现在参与路由)
|
||||||
|
if success:
|
||||||
|
await invalidate_models_list_cache()
|
||||||
|
|
||||||
return ImportFromUpstreamResponse(success=success, errors=errors)
|
return ImportFromUpstreamResponse(success=success, errors=errors)
|
||||||
|
|||||||
Reference in New Issue
Block a user