修改普通用户的数据类型转换在数据集管理看不见的问题
This commit is contained in:
@@ -17,6 +17,7 @@ import httpx
|
||||
from app.core.auth import filter_accessible_resource_ids, filter_accessible_resource_ids_batch, get_current_user, has_resource_access, is_admin
|
||||
from app.core.config import get_settings
|
||||
from app.core.audit import audit_log, AuditActions
|
||||
from app.core.op_log import op_log, OpModule, OpAction
|
||||
from app.db.platform_store import get_platform_store
|
||||
from app.modules.compute_gateway.client import ComputeNodeClient
|
||||
from app.modules.compute_gateway.sync import fetch_eval_result_content, poll_compute_jobs_once
|
||||
@@ -904,12 +905,11 @@ async def create_model(payload: dict[str, Any] = Body(...), current_user: dict =
|
||||
|
||||
@router.get("/model-manage/{model_id}")
|
||||
async def model_detail(model_id: str, current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
# 基座模型(配置模型)是平台共享资源,所有登录用户均可查看
|
||||
try:
|
||||
model = get_platform_store().model(model_id)
|
||||
except KeyError:
|
||||
raise fail(404, "model not found")
|
||||
if not has_resource_access("model", model_id, current_user, "read"):
|
||||
raise fail(403, "no permission to access this model")
|
||||
return ok(model)
|
||||
|
||||
|
||||
@@ -919,7 +919,10 @@ async def model_detail(model_id: str, current_user: dict = Depends(get_current_u
|
||||
target_type="model",
|
||||
detail_template="更新模型: {model_id}",
|
||||
)
|
||||
async def update_model(model_id: str, payload: dict[str, Any] = Body(...)) -> dict[str, Any]:
|
||||
async def update_model(model_id: str, payload: dict[str, Any] = Body(...), current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
# 基座模型(配置模型)只有管理员可以编辑
|
||||
if not is_admin(current_user):
|
||||
raise fail(403, "只有管理员可以修改模型配置")
|
||||
try:
|
||||
return ok(get_platform_store().update_model(model_id, payload))
|
||||
except KeyError:
|
||||
@@ -927,7 +930,10 @@ async def update_model(model_id: str, payload: dict[str, Any] = Body(...)) -> di
|
||||
|
||||
|
||||
@router.put("/model-manage/{model_id}/purpose")
|
||||
async def update_model_purpose(model_id: str, payload: dict[str, Any] = Body(...)) -> dict[str, Any]:
|
||||
async def update_model_purpose(model_id: str, payload: dict[str, Any] = Body(...), current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
# 基座模型(配置模型)只有管理员可以修改用途
|
||||
if not is_admin(current_user):
|
||||
raise fail(403, "只有管理员可以修改模型用途")
|
||||
try:
|
||||
return ok(get_platform_store().update_model(model_id, {"purpose": payload.get("purpose", "training")}))
|
||||
except KeyError:
|
||||
@@ -936,16 +942,15 @@ async def update_model_purpose(model_id: str, payload: dict[str, Any] = Body(...
|
||||
|
||||
@router.delete("/model-manage/{model_id}")
|
||||
async def delete_model(model_id: str, current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
if not has_resource_access("model", model_id, current_user, "delete"):
|
||||
raise fail(403, "no permission to delete this model")
|
||||
pending = _require_approval_or_admin("model", model_id, current_user, f"删除模型 {model_id}")
|
||||
if pending:
|
||||
return pending
|
||||
# 基座模型(配置模型)只有管理员可以删除
|
||||
if not is_admin(current_user):
|
||||
raise fail(403, "只有管理员可以删除模型配置")
|
||||
get_platform_store().delete_model(model_id)
|
||||
return ok({"deleted": model_id})
|
||||
|
||||
|
||||
@router.post("/model-manage/merge")
|
||||
@op_log(module=OpModule.MODEL_MANAGE, action=OpAction.MERGE, target_type="trained_model", target_name_param="trained_model_id")
|
||||
async def merge_model(payload: dict[str, Any] = Body(...), current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
store = get_platform_store()
|
||||
trained_model_id = str(payload.get("trained_model_id") or payload.get("model_id") or payload.get("model_name") or "")
|
||||
@@ -961,8 +966,7 @@ async def merge_model(payload: dict[str, Any] = Body(...), current_user: dict =
|
||||
raise fail(404, "trained model not found")
|
||||
if not has_resource_access("trained_model", trained_model["id"], current_user, "execute"):
|
||||
raise fail(403, "no permission to merge this trained model")
|
||||
if payload.get("base_model_id") and not has_resource_access("model", str(payload["base_model_id"]), current_user, "execute"):
|
||||
raise fail(403, "no permission to use merge base model")
|
||||
# 基座模型(配置模型)是平台共享资源,不需要 ACL 授权即可使用
|
||||
base_model_path = payload.get("base_model_path") or (trained_model and trained_model.get("base_model_path"))
|
||||
adapter_path = (
|
||||
payload.get("adapter_path")
|
||||
@@ -1282,6 +1286,7 @@ async def dataset_list(current_user: dict = Depends(get_current_user)) -> dict[s
|
||||
target_type="dataset",
|
||||
detail_template="创建数据集: {name}",
|
||||
)
|
||||
@op_log(module=OpModule.DATASET, action=OpAction.CREATE, target_type="dataset", target_name_param="name")
|
||||
async def create_dataset(payload: dict[str, Any] = Body(...), current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
payload.setdefault("created_by", current_user.get("id"))
|
||||
dataset = get_platform_store().create_dataset(payload)
|
||||
@@ -1318,6 +1323,7 @@ async def update_dataset(dataset_id: str, payload: dict[str, Any] = Body(...)) -
|
||||
target_type="dataset",
|
||||
detail_template="删除数据集: {dataset_id}",
|
||||
)
|
||||
@op_log(module=OpModule.DATASET, action=OpAction.DELETE, target_type="dataset", target_name_param="dataset_id")
|
||||
async def delete_dataset(dataset_id: str, current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
if not has_resource_access("dataset", dataset_id, current_user, "delete"):
|
||||
raise fail(403, "no permission to delete this dataset")
|
||||
@@ -1367,8 +1373,7 @@ async def create_fine_tune(payload: dict[str, Any] = Body(...), current_user: di
|
||||
if not is_admin(current_user):
|
||||
model_id = str(payload.get("base_model") or payload.get("base_model_id") or "")
|
||||
dataset_id = str(payload.get("train_dataset_id") or "")
|
||||
if model_id and not has_resource_access("model", model_id, current_user, "execute"):
|
||||
raise fail(403, "no permission to use this base model")
|
||||
# 基座模型(配置模型)是平台共享资源,不需要 ACL 授权
|
||||
if dataset_id and not has_resource_access("dataset", dataset_id, current_user, "execute"):
|
||||
raise fail(403, "no permission to use this dataset")
|
||||
try:
|
||||
@@ -1379,6 +1384,7 @@ async def create_fine_tune(payload: dict[str, Any] = Body(...), current_user: di
|
||||
|
||||
|
||||
@router.post("/fine-tune/start")
|
||||
@op_log(module=OpModule.FINE_TUNE, action=OpAction.START, target_type="fine_tune", target_name_param="name", detail_params=["task_id", "base_model", "train_dataset_id"])
|
||||
async def start_fine_tune(
|
||||
payload: dict[str, Any] = Body(...),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
@@ -1538,6 +1544,7 @@ async def update_fine_tune(task_id: str, payload: dict[str, Any] = Body(...), cu
|
||||
|
||||
|
||||
@router.post("/fine-tune/stop/{task_id}")
|
||||
@op_log(module=OpModule.FINE_TUNE, action=OpAction.STOP, target_type="fine_tune", target_name_param="task_id")
|
||||
async def stop_fine_tune(task_id: str, current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
store = get_platform_store()
|
||||
try:
|
||||
@@ -1584,6 +1591,7 @@ async def retry_fine_tune(task_id: str, payload: dict[str, Any] | None = Body(de
|
||||
|
||||
|
||||
@router.delete("/fine-tune/{task_id}")
|
||||
@op_log(module=OpModule.FINE_TUNE, action=OpAction.DELETE, target_type="fine_tune", target_name_param="task_id")
|
||||
async def delete_fine_tune(task_id: str, current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
if not has_resource_access("fine-tune", task_id, current_user, "delete"):
|
||||
raise fail(403, "no permission to delete this task")
|
||||
@@ -1665,6 +1673,7 @@ async def model_eval_detail(task_id: str, current_user: dict = Depends(get_curre
|
||||
|
||||
|
||||
@router.post("/model-eval/start")
|
||||
@op_log(module=OpModule.MODEL_EVAL, action=OpAction.START, target_type="eval_task", target_name_param="name", detail_params=["model_id", "dataset_id"])
|
||||
async def model_eval_start(payload: dict[str, Any] = Body(...), current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
"""Start an evaluation task: submit eval job to compute node."""
|
||||
store = get_platform_store()
|
||||
@@ -1878,11 +1887,7 @@ async def model_compare_list(current_user: dict = Depends(get_current_user)) ->
|
||||
async def model_compare_create(payload: dict[str, Any] = Body(...), current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
payload.setdefault("created_by", current_user.get("id"))
|
||||
model_ids = payload.get("model_ids") or payload.get("models") or []
|
||||
if not is_admin(current_user):
|
||||
for model_id in model_ids:
|
||||
if isinstance(model_id, dict): model_id = model_id.get("id") or model_id.get("model_id")
|
||||
if model_id and not has_resource_access("model", str(model_id), current_user, "execute"):
|
||||
raise fail(403, "no permission to use inference model")
|
||||
# 基座模型(配置模型)是平台共享资源,不需要 ACL 授权即可用于推理
|
||||
task = get_platform_store().create_compare_task(payload)
|
||||
return ok({"id": task["id"]})
|
||||
|
||||
@@ -1940,17 +1945,35 @@ async def _unload_from_compute_node(store: Any, task: dict[str, Any] | None = No
|
||||
|
||||
|
||||
@router.delete("/model-compare/{task_id}")
|
||||
@op_log(module=OpModule.INFERENCE, action=OpAction.DELETE, target_type="inference", target_name_param="task_id")
|
||||
async def model_compare_delete(task_id: str, current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
# 先删记录(快),再 best-effort 释放算力节点上的模型——删除绝不被卸载阻塞
|
||||
try:
|
||||
task = get_platform_store().compare_task(task_id)
|
||||
except KeyError:
|
||||
raise fail(404, "compare task not found")
|
||||
if not has_resource_access("compare", task_id, current_user, "delete"):
|
||||
raise fail(403, "no permission to delete inference task")
|
||||
pending = _require_approval_or_admin("compare", task_id, current_user, f"删除推理任务 {task_id}")
|
||||
if pending:
|
||||
return pending
|
||||
# 先判断是否为任务创建者本人:如果是,直接允许删除(不需要 ACL 授权也不需要审批)
|
||||
is_owner = False
|
||||
payload_obj = task
|
||||
if isinstance(payload_obj, dict):
|
||||
is_owner = (payload_obj.get("created_by") == current_user.get("id"))
|
||||
else:
|
||||
try:
|
||||
import json
|
||||
payload_str = str(payload_obj.get("payload", "{}") or "{}")
|
||||
payload_obj = json.loads(payload_str) if payload_str.startswith("{") else {}
|
||||
is_owner = (payload_obj.get("created_by") == current_user.get("id"))
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
# 管理员或任务创建者:直接删除
|
||||
# 其他用户:需要 ACL delete 权限 + 审批流程
|
||||
if not is_admin(current_user) and not is_owner:
|
||||
if not has_resource_access("compare", task_id, current_user, "delete"):
|
||||
raise fail(403, "no permission to delete inference task")
|
||||
pending = _require_approval_or_admin("compare", task_id, current_user, f"删除推理任务 {task_id}")
|
||||
if pending:
|
||||
return pending
|
||||
get_platform_store().delete_compare_task(task_id)
|
||||
try:
|
||||
await _unload_from_compute_node(get_platform_store(), task=task)
|
||||
@@ -2013,6 +2036,7 @@ def _invalidate_superseded_models(store: Any, task_id: str, loaded_models: list[
|
||||
|
||||
|
||||
@router.post("/model-compare/{task_id}/load")
|
||||
@op_log(module=OpModule.INFERENCE, action=OpAction.START, target_type="inference", target_name_param="task_id")
|
||||
async def model_compare_load(task_id: str, current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
"""异步派发模型加载到算力节点,立即返回。
|
||||
|
||||
@@ -2023,8 +2047,13 @@ async def model_compare_load(task_id: str, current_user: dict = Depends(get_curr
|
||||
try:
|
||||
store = get_platform_store()
|
||||
task = store.compare_task(task_id)
|
||||
if not has_resource_access("compare", task_id, current_user, "execute"):
|
||||
raise fail(403, "no permission to load inference task")
|
||||
# 先判断是否为任务创建者本人或管理员:如果是,直接允许操作
|
||||
is_owner = False
|
||||
if isinstance(task, dict):
|
||||
is_owner = (task.get("created_by") == current_user.get("id"))
|
||||
if not is_admin(current_user) and not is_owner:
|
||||
if not has_resource_access("compare", task_id, current_user, "execute"):
|
||||
raise fail(403, "no permission to load inference task")
|
||||
models = task.get("models") or []
|
||||
if isinstance(models, str):
|
||||
try:
|
||||
@@ -2231,8 +2260,9 @@ async def model_chat_local_status() -> dict[str, Any]:
|
||||
@router.post("/model-chat/trained/preload")
|
||||
async def model_chat_trained_preload(payload: dict[str, Any] = Body(...), current_user: dict = Depends(get_current_user)) -> dict[str, Any]:
|
||||
resource_id = str(payload.get("trained_model_id") or payload.get("model_id") or payload.get("resource_id") or "")
|
||||
if resource_id and not has_resource_access("trained_model", resource_id, current_user, "execute") and not has_resource_access("model", resource_id, current_user, "execute"):
|
||||
raise fail(403, "no permission to load this model")
|
||||
# 训练模型需要 ACL 授权;基座模型(配置模型)是平台共享资源,不需要 ACL
|
||||
if resource_id and not has_resource_access("trained_model", resource_id, current_user, "execute"):
|
||||
raise fail(403, "no permission to load this trained model")
|
||||
"""Load a trained model (base + adapter) on the compute node for inference."""
|
||||
model_path = (payload.get("model_name_or_path") or "").strip()
|
||||
if not model_path:
|
||||
@@ -2365,8 +2395,9 @@ async def archive_node_files(
|
||||
if not is_admin(current_user):
|
||||
model_id = str(payload.get("model_id") or "")
|
||||
dataset_id = str(payload.get("dataset_id") or "")
|
||||
if not model_id or not has_resource_access("model", model_id, current_user, "execute"):
|
||||
raise fail(403, "no permission to evaluate this model")
|
||||
# 基座模型(配置模型)是平台共享资源,不需要 ACL 授权
|
||||
if not model_id:
|
||||
raise fail(400, "model_id is required")
|
||||
if not dataset_id or not has_resource_access("dataset", dataset_id, current_user, "execute"):
|
||||
raise fail(403, "no permission to evaluate this dataset")
|
||||
node = next((item for item in store.compute_nodes() if item["id"] == node_id), None)
|
||||
|
||||
Reference in New Issue
Block a user