feat: 更新后端平台模块、Compute引擎、前端组件及构建产物
- 更新 backend 平台 API、platform_store、compute_gateway sync - 更新 compute agent/engine/adapter 及 API - 更新 Docker 部署配置(app/compute) - 新增 frontend/src/utils/ 工具模块 - 新增 scripts/ops_diagnostics.py 运维诊断脚本 - 新增 docs/2026-07-23-development-summary.md 开发总结 - 重构 frontend/dist 构建产物(新 hash) - 更新前端多个视图组件及 API 模块 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -32,16 +32,26 @@ def _task_for_compute_job(job_id: str) -> dict[str, Any] | None:
|
|||||||
return next((task for task in get_platform_store().tasks() if task.get("compute_job_id") == job_id), None)
|
return next((task for task in get_platform_store().tasks() if task.get("compute_job_id") == job_id), None)
|
||||||
|
|
||||||
|
|
||||||
|
def _node_for_compute_job_record(job_id: str) -> dict[str, Any] | None:
|
||||||
|
store = get_platform_store()
|
||||||
|
try:
|
||||||
|
record = store.compute_job(job_id)
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
return next((node for node in store.compute_nodes() if node["id"] == record.get("node_id")), None)
|
||||||
|
|
||||||
|
|
||||||
async def _submit_fine_tune_task(store: Any, payload: dict[str, Any]) -> dict[str, Any]:
|
async def _submit_fine_tune_task(store: Any, payload: dict[str, Any]) -> dict[str, Any]:
|
||||||
task_id = str(payload.get("task_id") or payload.get("id") or "")
|
task_id = str(payload.get("task_id") or payload.get("id") or "")
|
||||||
if task_id and get_settings().compute_mode != "simulator":
|
if task_id and get_settings().compute_mode != "simulator":
|
||||||
try:
|
try:
|
||||||
preflight = await _fine_tune_preflight(store, task_id, payload, validate=True)
|
preflight = await _fine_tune_preflight(store, task_id, payload, validate=True, sync_resources=True)
|
||||||
except Exception as exc: # noqa: BLE001 - task has not entered running state yet
|
except Exception as exc: # noqa: BLE001 - task has not entered running state yet
|
||||||
raise RuntimeError(f"preflight failed: {exc}") from exc
|
raise RuntimeError(f"preflight failed: {exc}") from exc
|
||||||
if not preflight["valid"]:
|
if not preflight["valid"]:
|
||||||
errors = "; ".join(preflight.get("errors") or ["preflight failed"])
|
errors = "; ".join(preflight.get("errors") or ["preflight failed"])
|
||||||
raise RuntimeError(f"preflight failed: {errors}")
|
raise RuntimeError(f"preflight failed: {errors}")
|
||||||
|
payload = {**payload, "compute_node_id": preflight["node"]["id"]}
|
||||||
task = store.start_task(payload)
|
task = store.start_task(payload)
|
||||||
if get_settings().compute_mode == "simulator":
|
if get_settings().compute_mode == "simulator":
|
||||||
return task
|
return task
|
||||||
@@ -55,8 +65,20 @@ async def _fine_tune_preflight(
|
|||||||
task_id: str,
|
task_id: str,
|
||||||
payload: dict[str, Any] | None = None,
|
payload: dict[str, Any] | None = None,
|
||||||
validate: bool = True,
|
validate: bool = True,
|
||||||
|
sync_resources: bool = False,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
node, job_payload = store.prepare_compute_job_payload(task_id, payload or {})
|
node, job_payload = store.prepare_compute_job_payload(task_id, payload or {})
|
||||||
|
sync_results: list[dict[str, Any]] = []
|
||||||
|
sync_errors: list[str] = []
|
||||||
|
if sync_resources and get_settings().compute_mode != "simulator":
|
||||||
|
try:
|
||||||
|
sync_results = await _sync_training_dataset_to_compute_node(
|
||||||
|
store,
|
||||||
|
node,
|
||||||
|
str(job_payload.get("train_dataset_id") or ""),
|
||||||
|
)
|
||||||
|
except Exception as exc: # noqa: BLE001 - return as preflight error for page visibility
|
||||||
|
sync_errors.append(str(exc))
|
||||||
if get_settings().compute_mode == "simulator":
|
if get_settings().compute_mode == "simulator":
|
||||||
preview = {
|
preview = {
|
||||||
"valid": True,
|
"valid": True,
|
||||||
@@ -73,6 +95,7 @@ async def _fine_tune_preflight(
|
|||||||
client = ComputeNodeClient(node["api_base_url"])
|
client = ComputeNodeClient(node["api_base_url"])
|
||||||
preview = await (client.validate_job(job_payload) if validate else client.preview_job(job_payload))
|
preview = await (client.validate_job(job_payload) if validate else client.preview_job(job_payload))
|
||||||
errors = list(preview.get("errors") or [])
|
errors = list(preview.get("errors") or [])
|
||||||
|
errors.extend(sync_errors)
|
||||||
warnings = list(preview.get("warnings") or [])
|
warnings = list(preview.get("warnings") or [])
|
||||||
if not node.get("enabled"):
|
if not node.get("enabled"):
|
||||||
errors.append(f"compute node disabled: {node.get('code')}")
|
errors.append(f"compute node disabled: {node.get('code')}")
|
||||||
@@ -92,6 +115,7 @@ async def _fine_tune_preflight(
|
|||||||
},
|
},
|
||||||
"job_payload": job_payload,
|
"job_payload": job_payload,
|
||||||
"preview": preview,
|
"preview": preview,
|
||||||
|
"sync_results": sync_results,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -197,6 +221,21 @@ async def delete_trained_model(model_id: str, type: str = Query(default="merged"
|
|||||||
return ok({"deleted": model_id, "type": type})
|
return ok({"deleted": model_id, "type": type})
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/model-manage/trained-models/{model_id}/artifacts")
|
||||||
|
async def trained_model_artifacts(model_id: str) -> dict[str, Any]:
|
||||||
|
return ok(get_platform_store().model_artifacts(model_id))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/model-manage/trained-models/{model_id}/lineage")
|
||||||
|
async def trained_model_lineage(model_id: str) -> dict[str, Any]:
|
||||||
|
return ok(get_platform_store().model_lineage(model_id))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/model-manage/export-jobs")
|
||||||
|
async def model_export_jobs(trained_model_id: str | None = Query(default=None)) -> dict[str, Any]:
|
||||||
|
return ok(get_platform_store().model_export_jobs(trained_model_id))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/model-manage/name/{name}")
|
@router.get("/model-manage/name/{name}")
|
||||||
async def model_by_name(name: str) -> dict[str, Any]:
|
async def model_by_name(name: str) -> dict[str, Any]:
|
||||||
try:
|
try:
|
||||||
@@ -254,7 +293,51 @@ async def delete_model(model_id: str) -> dict[str, Any]:
|
|||||||
|
|
||||||
@router.post("/model-manage/merge")
|
@router.post("/model-manage/merge")
|
||||||
async def merge_model(payload: dict[str, Any] = Body(...)) -> dict[str, Any]:
|
async def merge_model(payload: dict[str, Any] = Body(...)) -> dict[str, Any]:
|
||||||
return ok({"job_id": f"merge_{uuid.uuid4().hex[:12]}", "status": "queued", **payload})
|
store = get_platform_store()
|
||||||
|
trained_model_id = str(payload.get("trained_model_id") or payload.get("model_id") or payload.get("model_name") or "")
|
||||||
|
trained_model = next(
|
||||||
|
(
|
||||||
|
item
|
||||||
|
for item in store.trained_models()
|
||||||
|
if trained_model_id and (item["id"] == trained_model_id or item["name"] == trained_model_id)
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
base_model_path = payload.get("base_model_path") or (trained_model and trained_model.get("base_model_path"))
|
||||||
|
adapter_path = payload.get("adapter_path") or payload.get("adapter_name_or_path") or (trained_model and trained_model.get("merged_path"))
|
||||||
|
if not base_model_path:
|
||||||
|
raise fail(400, "base_model_path is required")
|
||||||
|
if not adapter_path:
|
||||||
|
raise fail(400, "adapter_path is required")
|
||||||
|
node = store.schedule_node({**payload, "gpus": payload.get("gpus") or []})
|
||||||
|
health = node.get("health_detail") or {}
|
||||||
|
output_root = str(health.get("output_root") or f"{node['data_root'].rstrip('/')}/outputs")
|
||||||
|
output_name = str(payload.get("output_model_name") or payload.get("merged_model_name") or f"{trained_model_id or 'model'}-merged")
|
||||||
|
output_dir = str(payload.get("output_dir") or f"{output_root.rstrip('/')}/{output_name}")
|
||||||
|
job_payload = {
|
||||||
|
**payload,
|
||||||
|
"id": str(payload.get("job_id") or f"merge_{uuid.uuid4().hex[:12]}"),
|
||||||
|
"name": output_name,
|
||||||
|
"engine": "merge",
|
||||||
|
"base_model": base_model_path,
|
||||||
|
"model_name_or_path": base_model_path,
|
||||||
|
"adapter_name_or_path": adapter_path,
|
||||||
|
"output_dir": output_dir,
|
||||||
|
"template": payload.get("template", "qwen"),
|
||||||
|
"train_method": payload.get("train_method", "lora"),
|
||||||
|
"gpus": payload.get("gpus") or [],
|
||||||
|
"trained_model_id": trained_model["id"] if trained_model else trained_model_id,
|
||||||
|
"model_name": trained_model["name"] if trained_model else payload.get("model_name"),
|
||||||
|
}
|
||||||
|
if get_settings().compute_mode == "simulator":
|
||||||
|
job = {"id": job_payload["id"], "status": "queued", "progress": 10, "command": [], "output_dir": output_dir}
|
||||||
|
else:
|
||||||
|
client = ComputeNodeClient(node["api_base_url"])
|
||||||
|
preview = await client.validate_job(job_payload)
|
||||||
|
if not preview.get("valid", False):
|
||||||
|
raise fail(409, "; ".join(preview.get("errors") or ["merge preflight failed"]))
|
||||||
|
job = await client.create_job(job_payload)
|
||||||
|
return ok(store.record_model_merge_job(node, job_payload, job, trained_model["id"] if trained_model else trained_model_id))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/dataset-manage/preview/{file_id}")
|
@router.get("/dataset-manage/preview/{file_id}")
|
||||||
@@ -364,6 +447,47 @@ async def _sync_dataset_file_to_compute_nodes(
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
async def _sync_training_dataset_to_compute_node(
|
||||||
|
store: Any,
|
||||||
|
node: dict[str, Any],
|
||||||
|
dataset_id: str,
|
||||||
|
) -> list[dict[str, Any]]:
|
||||||
|
if not dataset_id:
|
||||||
|
raise RuntimeError("train_dataset_id is required")
|
||||||
|
files = store.training_dataset_files(dataset_id)
|
||||||
|
if not files:
|
||||||
|
raise RuntimeError(f"dataset has no uploaded file: {dataset_id}")
|
||||||
|
client = ComputeNodeClient(node["api_base_url"])
|
||||||
|
results: list[dict[str, Any]] = []
|
||||||
|
for item in files:
|
||||||
|
target_name = Path(str(item.get("name") or f"{item['id']}.jsonl")).name
|
||||||
|
result = await client.upload_file(
|
||||||
|
target_name,
|
||||||
|
str(item.get("content") or "").encode("utf-8"),
|
||||||
|
f"datasets/{dataset_id}/{target_name}",
|
||||||
|
resource_type="dataset",
|
||||||
|
resource_id=dataset_id,
|
||||||
|
)
|
||||||
|
store.upsert_resource_replica(
|
||||||
|
node["id"],
|
||||||
|
"dataset",
|
||||||
|
dataset_id,
|
||||||
|
str(result.get("local_path") or ""),
|
||||||
|
)
|
||||||
|
results.append(
|
||||||
|
{
|
||||||
|
"node_id": node["id"],
|
||||||
|
"node_code": node.get("code"),
|
||||||
|
"file_id": item.get("id"),
|
||||||
|
"name": target_name,
|
||||||
|
"local_path": result.get("local_path"),
|
||||||
|
"byte_size": result.get("byte_size"),
|
||||||
|
"checksum_sha256": result.get("checksum_sha256"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
@router.post("/dataset-manage/upload/{dataset_id}")
|
@router.post("/dataset-manage/upload/{dataset_id}")
|
||||||
async def upload_dataset_files(
|
async def upload_dataset_files(
|
||||||
dataset_id: str,
|
dataset_id: str,
|
||||||
@@ -543,6 +667,10 @@ async def fine_tune_logs(
|
|||||||
if node:
|
if node:
|
||||||
try:
|
try:
|
||||||
logs = await ComputeNodeClient(node["api_base_url"]).job_logs(task["compute_job_id"], tail_lines, offset, limit)
|
logs = await ComputeNodeClient(node["api_base_url"]).job_logs(task["compute_job_id"], tail_lines, offset, limit)
|
||||||
|
try:
|
||||||
|
store.record_training_log_metrics(task_id, str(logs.get("content") or ""))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
if task.get("status") in {"queued", "running", "failed", "stopped", "completed"}:
|
if task.get("status") in {"queued", "running", "failed", "stopped", "completed"}:
|
||||||
try:
|
try:
|
||||||
job = await ComputeNodeClient(node["api_base_url"]).get_job(task["compute_job_id"])
|
job = await ComputeNodeClient(node["api_base_url"]).get_job(task["compute_job_id"])
|
||||||
@@ -613,18 +741,36 @@ async def delete_fine_tune(task_id: str) -> dict[str, Any]:
|
|||||||
|
|
||||||
@router.get("/fine-tune/{task_id}/overview")
|
@router.get("/fine-tune/{task_id}/overview")
|
||||||
async def fine_tune_overview(task_id: str) -> dict[str, Any]:
|
async def fine_tune_overview(task_id: str) -> dict[str, Any]:
|
||||||
task = get_platform_store().task(task_id)
|
store = get_platform_store()
|
||||||
return ok({"task": task, "progress": get_platform_store().progress(task_id)})
|
task = store.task(task_id)
|
||||||
|
return ok(
|
||||||
|
{
|
||||||
|
"task": task,
|
||||||
|
"progress": store.progress(task_id),
|
||||||
|
"metrics": store.task_metrics(task_id),
|
||||||
|
"checkpoints": store.task_checkpoints(task_id),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/fine-tune/{task_id}/checkpoints")
|
@router.get("/fine-tune/{task_id}/checkpoints")
|
||||||
async def fine_tune_checkpoints(task_id: str) -> dict[str, Any]:
|
async def fine_tune_checkpoints(task_id: str) -> dict[str, Any]:
|
||||||
task = get_platform_store().task(task_id)
|
store = get_platform_store()
|
||||||
checkpoints = []
|
try:
|
||||||
for step in [50, 100, 150]:
|
store.task(task_id)
|
||||||
if task.get("progress", 0) >= min(100, step // 2):
|
except KeyError:
|
||||||
checkpoints.append({"step": step, "path": f"/data/yg-ft/outputs/{task['name']}/checkpoint-{step}"})
|
raise fail(404, "fine tune task not found")
|
||||||
return ok(checkpoints)
|
return ok(store.task_checkpoints(task_id))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/fine-tune/{task_id}/metrics")
|
||||||
|
async def fine_tune_metrics(task_id: str) -> dict[str, Any]:
|
||||||
|
store = get_platform_store()
|
||||||
|
try:
|
||||||
|
store.task(task_id)
|
||||||
|
except KeyError:
|
||||||
|
raise fail(404, "fine tune task not found")
|
||||||
|
return ok(store.task_metrics(task_id))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/model-eval")
|
@router.get("/model-eval")
|
||||||
@@ -909,6 +1055,142 @@ async def compute_node_replicas(node_id: str) -> dict[str, Any]:
|
|||||||
return ok(get_platform_store().replicas(node_id))
|
return ok(get_platform_store().replicas(node_id))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/compute/nodes/{node_id}/replicas/drift")
|
||||||
|
async def compute_node_replica_drift(node_id: str) -> dict[str, Any]:
|
||||||
|
store = get_platform_store()
|
||||||
|
node = next((item for item in store.compute_nodes() if item["id"] == node_id), None)
|
||||||
|
if not node:
|
||||||
|
raise fail(404, "compute node not found")
|
||||||
|
replicas = store.replicas(node_id)
|
||||||
|
if not replicas:
|
||||||
|
return ok({"node_id": node_id, "items": [], "drifted": 0})
|
||||||
|
paths = [
|
||||||
|
{
|
||||||
|
"name": replica["id"],
|
||||||
|
"path": replica["local_path"],
|
||||||
|
"type": "any",
|
||||||
|
"required": True,
|
||||||
|
}
|
||||||
|
for replica in replicas
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
result = await ComputeNodeClient(node["api_base_url"]).check_paths(paths)
|
||||||
|
except Exception as exc: # noqa: BLE001
|
||||||
|
raise fail(502, f"replica drift check failed: {exc}")
|
||||||
|
check_map = {str(item.get("name")): item for item in result.get("items") or []}
|
||||||
|
items = []
|
||||||
|
for replica in replicas:
|
||||||
|
check = check_map.get(replica["id"], {})
|
||||||
|
updated = store.update_resource_replica_check(
|
||||||
|
replica["id"],
|
||||||
|
bool(check.get("ok")),
|
||||||
|
int(check.get("byte_size") or replica.get("byte_size") or 0),
|
||||||
|
"" if check.get("ok") else f"path not available: {replica['local_path']}",
|
||||||
|
)
|
||||||
|
items.append({**updated, "check": check})
|
||||||
|
return ok({"node_id": node_id, "items": items, "drifted": len([item for item in items if item.get("sync_status") == "drifted"])})
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/compute/nodes/{node_id}/replicas/repair")
|
||||||
|
async def compute_node_replica_repair(node_id: str, payload: dict[str, Any] | None = Body(default=None)) -> dict[str, Any]:
|
||||||
|
store = get_platform_store()
|
||||||
|
node = next((item for item in store.compute_nodes() if item["id"] == node_id), None)
|
||||||
|
if not node:
|
||||||
|
raise fail(404, "compute node not found")
|
||||||
|
payload = payload or {}
|
||||||
|
replica_ids = payload.get("replica_ids") or [
|
||||||
|
item["id"] for item in store.replicas(node_id) if item.get("sync_status") in {"drifted", "failed", "repair_pending"}
|
||||||
|
]
|
||||||
|
updated = store.mark_resource_replica_repair_pending([str(item) for item in replica_ids])
|
||||||
|
sync_id = store.create_sync_job(
|
||||||
|
node_id,
|
||||||
|
{
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"resource_type": item.get("resource_type"),
|
||||||
|
"resource_id": item.get("resource_id"),
|
||||||
|
"replica_id": item.get("id"),
|
||||||
|
"target_path": item.get("local_path"),
|
||||||
|
}
|
||||||
|
for item in updated
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
client = ComputeNodeClient(node["api_base_url"])
|
||||||
|
repaired = []
|
||||||
|
failures = []
|
||||||
|
for replica in updated:
|
||||||
|
replica_id = str(replica["id"])
|
||||||
|
resource_type = str(replica.get("resource_type") or "")
|
||||||
|
resource_id = str(replica.get("resource_id") or "")
|
||||||
|
try:
|
||||||
|
if resource_type == "dataset":
|
||||||
|
files = store.training_dataset_files(resource_id)
|
||||||
|
if not files:
|
||||||
|
raise RuntimeError(f"dataset has no uploaded file: {resource_id}")
|
||||||
|
total_size = 0
|
||||||
|
checksum = ""
|
||||||
|
local_path = str(replica.get("local_path") or "")
|
||||||
|
for item in files:
|
||||||
|
filename = Path(str(item.get("name") or f"{item['id']}.jsonl")).name
|
||||||
|
result = await client.upload_file(
|
||||||
|
filename,
|
||||||
|
str(item.get("content") or "").encode("utf-8"),
|
||||||
|
f"datasets/{resource_id}/{filename}",
|
||||||
|
resource_type="dataset",
|
||||||
|
resource_id=resource_id,
|
||||||
|
)
|
||||||
|
total_size += int(result.get("byte_size") or 0)
|
||||||
|
checksum = str(result.get("checksum_sha256") or checksum)
|
||||||
|
local_path = str(result.get("local_path") or local_path)
|
||||||
|
repaired.append(store.update_resource_replica_sync_result(replica_id, True, local_path, total_size, checksum))
|
||||||
|
continue
|
||||||
|
|
||||||
|
source_path = ""
|
||||||
|
target_relative_path = ""
|
||||||
|
if resource_type == "model":
|
||||||
|
model = store.model(resource_id)
|
||||||
|
source_path = str(model.get("path") or "")
|
||||||
|
target_relative_path = f"models/{Path(source_path).name}" if source_path else ""
|
||||||
|
elif resource_type in {"trained_model", "model_artifact"}:
|
||||||
|
if resource_type == "trained_model":
|
||||||
|
artifacts = store.model_artifacts(resource_id)
|
||||||
|
artifact = next((item for item in artifacts if item.get("path")), None)
|
||||||
|
else:
|
||||||
|
artifact = store.model_artifact(resource_id)
|
||||||
|
source_path = str((artifact or {}).get("path") or replica.get("local_path") or "")
|
||||||
|
target_relative_path = f"outputs/{Path(source_path).name}" if source_path else ""
|
||||||
|
else:
|
||||||
|
source_path = str(payload.get("source_path") or replica.get("source_path") or replica.get("local_path") or "")
|
||||||
|
target_relative_path = str(payload.get("target_relative_path") or "")
|
||||||
|
|
||||||
|
if not source_path:
|
||||||
|
raise RuntimeError(f"authoritative source path not found for {resource_type}:{resource_id}")
|
||||||
|
result = await client.import_local_file(
|
||||||
|
{
|
||||||
|
"source_path": source_path,
|
||||||
|
"target_relative_path": target_relative_path,
|
||||||
|
"resource_type": resource_type,
|
||||||
|
"resource_id": resource_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
repaired.append(
|
||||||
|
store.update_resource_replica_sync_result(
|
||||||
|
replica_id,
|
||||||
|
True,
|
||||||
|
str(result.get("local_path") or replica.get("local_path") or ""),
|
||||||
|
int(result.get("byte_size") or 0),
|
||||||
|
str(result.get("checksum_sha256") or ""),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as exc: # noqa: BLE001 - collect all replica repair failures
|
||||||
|
error = str(exc)
|
||||||
|
failures.append({"replica_id": replica_id, "resource_type": resource_type, "resource_id": resource_id, "error": error})
|
||||||
|
repaired.append(store.update_resource_replica_sync_result(replica_id, False, None, int(replica.get("byte_size") or 0), "", error))
|
||||||
|
store.update_sync_job(sync_id, "failed" if failures else "completed", 100 if not failures else 99, completed=True)
|
||||||
|
return ok({"sync": store.sync_job(sync_id), "replicas": repaired, "failed": failures})
|
||||||
|
|
||||||
|
|
||||||
@router.get("/compute/nodes/{node_id}/engines")
|
@router.get("/compute/nodes/{node_id}/engines")
|
||||||
async def compute_node_engines(node_id: str) -> dict[str, Any]:
|
async def compute_node_engines(node_id: str) -> dict[str, Any]:
|
||||||
node = next((item for item in get_platform_store().compute_nodes() if item["id"] == node_id), None)
|
node = next((item for item in get_platform_store().compute_nodes() if item["id"] == node_id), None)
|
||||||
@@ -955,7 +1237,11 @@ async def compute_queue() -> dict[str, Any]:
|
|||||||
async def compute_job_detail(job_id: str) -> dict[str, Any]:
|
async def compute_job_detail(job_id: str) -> dict[str, Any]:
|
||||||
task = _task_for_compute_job(job_id)
|
task = _task_for_compute_job(job_id)
|
||||||
if not task:
|
if not task:
|
||||||
raise fail(404, "compute job not found")
|
node = _node_for_compute_job_record(job_id)
|
||||||
|
if not node:
|
||||||
|
raise fail(404, "compute job not found")
|
||||||
|
job = await ComputeNodeClient(node["api_base_url"]).get_job(job_id)
|
||||||
|
return ok(get_platform_store().sync_model_merge_job(job_id, job))
|
||||||
node = _node_for_task(task)
|
node = _node_for_task(task)
|
||||||
if not node:
|
if not node:
|
||||||
raise fail(404, "compute node not found")
|
raise fail(404, "compute node not found")
|
||||||
@@ -966,7 +1252,11 @@ async def compute_job_detail(job_id: str) -> dict[str, Any]:
|
|||||||
async def compute_job_stop(job_id: str) -> dict[str, Any]:
|
async def compute_job_stop(job_id: str) -> dict[str, Any]:
|
||||||
task = _task_for_compute_job(job_id)
|
task = _task_for_compute_job(job_id)
|
||||||
if not task:
|
if not task:
|
||||||
raise fail(404, "compute job not found")
|
node = _node_for_compute_job_record(job_id)
|
||||||
|
if not node:
|
||||||
|
raise fail(404, "compute job not found")
|
||||||
|
job = await ComputeNodeClient(node["api_base_url"]).stop_job(job_id)
|
||||||
|
return ok(get_platform_store().sync_model_merge_job(job_id, job))
|
||||||
node = _node_for_task(task)
|
node = _node_for_task(task)
|
||||||
if not node:
|
if not node:
|
||||||
raise fail(404, "compute node not found")
|
raise fail(404, "compute node not found")
|
||||||
@@ -984,7 +1274,10 @@ async def compute_job_logs(
|
|||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
task = _task_for_compute_job(job_id)
|
task = _task_for_compute_job(job_id)
|
||||||
if not task:
|
if not task:
|
||||||
raise fail(404, "compute job not found")
|
node = _node_for_compute_job_record(job_id)
|
||||||
|
if not node:
|
||||||
|
raise fail(404, "compute job not found")
|
||||||
|
return ok(await ComputeNodeClient(node["api_base_url"]).job_logs(job_id, tail_lines, offset, limit))
|
||||||
node = _node_for_task(task)
|
node = _node_for_task(task)
|
||||||
if not node:
|
if not node:
|
||||||
raise fail(404, "compute node not found")
|
raise fail(404, "compute node not found")
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,45 @@ CREATE TABLE IF NOT EXISTS trained_models (
|
|||||||
merged_path TEXT
|
merged_path TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS model_lineage (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
child_resource_type TEXT NOT NULL,
|
||||||
|
child_resource_id TEXT NOT NULL,
|
||||||
|
parent_resource_type TEXT NOT NULL,
|
||||||
|
parent_resource_id TEXT NOT NULL,
|
||||||
|
relation_type TEXT NOT NULL,
|
||||||
|
compute_job_id TEXT,
|
||||||
|
payload TEXT NOT NULL,
|
||||||
|
create_time TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS model_artifacts (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
model_id TEXT NOT NULL,
|
||||||
|
model_kind TEXT NOT NULL,
|
||||||
|
artifact_type TEXT NOT NULL,
|
||||||
|
path TEXT NOT NULL,
|
||||||
|
size_bytes BIGINT NOT NULL DEFAULT 0,
|
||||||
|
checksum_sha256 TEXT,
|
||||||
|
metadata TEXT NOT NULL,
|
||||||
|
compute_job_id TEXT,
|
||||||
|
create_time TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS model_export_jobs (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
trained_model_id TEXT,
|
||||||
|
compute_job_id TEXT NOT NULL,
|
||||||
|
node_id TEXT,
|
||||||
|
export_type TEXT NOT NULL,
|
||||||
|
quantization_bit INTEGER NOT NULL DEFAULT 0,
|
||||||
|
status TEXT NOT NULL,
|
||||||
|
output_dir TEXT,
|
||||||
|
payload TEXT NOT NULL,
|
||||||
|
create_time TEXT NOT NULL,
|
||||||
|
completed_at TEXT
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS datasets (
|
CREATE TABLE IF NOT EXISTS datasets (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
name TEXT NOT NULL UNIQUE,
|
name TEXT NOT NULL UNIQUE,
|
||||||
@@ -111,6 +150,62 @@ CREATE TABLE IF NOT EXISTS fine_tune_tasks (
|
|||||||
compute_job_id TEXT
|
compute_job_id TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS fine_tune_metrics (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
task_id TEXT NOT NULL REFERENCES fine_tune_tasks(id) ON DELETE CASCADE,
|
||||||
|
step INTEGER NOT NULL,
|
||||||
|
epoch DOUBLE PRECISION,
|
||||||
|
loss DOUBLE PRECISION,
|
||||||
|
grad_norm DOUBLE PRECISION,
|
||||||
|
learning_rate DOUBLE PRECISION,
|
||||||
|
raw TEXT NOT NULL,
|
||||||
|
create_time TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS fine_tune_checkpoints (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
task_id TEXT NOT NULL REFERENCES fine_tune_tasks(id) ON DELETE CASCADE,
|
||||||
|
step INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
path TEXT NOT NULL,
|
||||||
|
size_bytes BIGINT NOT NULL DEFAULT 0,
|
||||||
|
create_time TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS compute_jobs (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
task_id TEXT REFERENCES fine_tune_tasks(id) ON DELETE SET NULL,
|
||||||
|
node_id TEXT REFERENCES compute_nodes(id) ON DELETE SET NULL,
|
||||||
|
engine TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL,
|
||||||
|
command TEXT NOT NULL,
|
||||||
|
output_dir TEXT,
|
||||||
|
log_file TEXT,
|
||||||
|
payload TEXT NOT NULL,
|
||||||
|
create_time TEXT NOT NULL,
|
||||||
|
update_time TEXT NOT NULL,
|
||||||
|
completed_at TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS gpu_allocations (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
task_id TEXT REFERENCES fine_tune_tasks(id) ON DELETE CASCADE,
|
||||||
|
compute_job_id TEXT,
|
||||||
|
node_id TEXT REFERENCES compute_nodes(id) ON DELETE CASCADE,
|
||||||
|
gpu_index INTEGER NOT NULL,
|
||||||
|
status TEXT NOT NULL,
|
||||||
|
create_time TEXT NOT NULL,
|
||||||
|
released_at TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS scheduler_locks (
|
||||||
|
lock_key TEXT PRIMARY KEY,
|
||||||
|
owner TEXT NOT NULL,
|
||||||
|
expires_at TEXT NOT NULL,
|
||||||
|
create_time TEXT NOT NULL,
|
||||||
|
update_time TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS resource_replicas (
|
CREATE TABLE IF NOT EXISTS resource_replicas (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
node_id TEXT NOT NULL REFERENCES compute_nodes(id) ON DELETE CASCADE,
|
node_id TEXT NOT NULL REFERENCES compute_nodes(id) ON DELETE CASCADE,
|
||||||
@@ -119,6 +214,10 @@ CREATE TABLE IF NOT EXISTS resource_replicas (
|
|||||||
local_path TEXT NOT NULL,
|
local_path TEXT NOT NULL,
|
||||||
status TEXT NOT NULL,
|
status TEXT NOT NULL,
|
||||||
sync_status TEXT NOT NULL,
|
sync_status TEXT NOT NULL,
|
||||||
|
checksum_sha256 TEXT,
|
||||||
|
byte_size BIGINT NOT NULL DEFAULT 0,
|
||||||
|
last_checked_at TEXT,
|
||||||
|
last_error TEXT,
|
||||||
create_time TEXT NOT NULL
|
create_time TEXT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -160,6 +259,20 @@ CREATE TABLE IF NOT EXISTS compare_tasks (
|
|||||||
CREATE INDEX IF NOT EXISTS idx_fine_tune_status ON fine_tune_tasks(status);
|
CREATE INDEX IF NOT EXISTS idx_fine_tune_status ON fine_tune_tasks(status);
|
||||||
CREATE INDEX IF NOT EXISTS idx_fine_tune_compute_job ON fine_tune_tasks(compute_job_id);
|
CREATE INDEX IF NOT EXISTS idx_fine_tune_compute_job ON fine_tune_tasks(compute_job_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_fine_tune_compute_node_status ON fine_tune_tasks(compute_node_id, status);
|
CREATE INDEX IF NOT EXISTS idx_fine_tune_compute_node_status ON fine_tune_tasks(compute_node_id, status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_model_lineage_child ON model_lineage(child_resource_type, child_resource_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_model_lineage_parent ON model_lineage(parent_resource_type, parent_resource_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_model_artifacts_model ON model_artifacts(model_kind, model_id, artifact_type);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_model_export_jobs_model ON model_export_jobs(trained_model_id, create_time DESC);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_model_export_jobs_compute ON model_export_jobs(compute_job_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_fine_tune_metrics_task_step ON fine_tune_metrics(task_id, step);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_fine_tune_metrics_task_step_epoch ON fine_tune_metrics(task_id, step, epoch);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_fine_tune_checkpoints_task_step ON fine_tune_checkpoints(task_id, step);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_fine_tune_checkpoints_task_path ON fine_tune_checkpoints(task_id, path);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_compute_jobs_task ON compute_jobs(task_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_compute_jobs_node_status ON compute_jobs(node_id, status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_gpu_allocations_node_status ON gpu_allocations(node_id, status);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_gpu_allocations_active ON gpu_allocations(node_id, gpu_index) WHERE status IN ('allocated','running');
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_scheduler_locks_expires ON scheduler_locks(expires_at);
|
||||||
CREATE INDEX IF NOT EXISTS idx_dataset_files_dataset ON dataset_files(dataset_id);
|
CREATE INDEX IF NOT EXISTS idx_dataset_files_dataset ON dataset_files(dataset_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_gpus_node ON gpus(node_id);
|
CREATE INDEX IF NOT EXISTS idx_gpus_node ON gpus(node_id);
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_gpus_node_index ON gpus(node_id, gpu_index);
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_gpus_node_index ON gpus(node_id, gpu_index);
|
||||||
|
|||||||
@@ -20,8 +20,25 @@ async def poll_compute_jobs_once() -> dict[str, Any]:
|
|||||||
failed.append({"task_id": task["id"], "error": "compute node not found"})
|
failed.append({"task_id": task["id"], "error": "compute node not found"})
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
job = await ComputeNodeClient(node["api_base_url"]).get_job(task["compute_job_id"])
|
client = ComputeNodeClient(node["api_base_url"])
|
||||||
|
job = await client.get_job(task["compute_job_id"])
|
||||||
|
try:
|
||||||
|
logs = await client.job_logs(task["compute_job_id"], tail_lines=5000)
|
||||||
|
store.record_training_log_metrics(task["id"], str(logs.get("content") or ""))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
synced.append(store.apply_compute_job(task["id"], job))
|
synced.append(store.apply_compute_job(task["id"], job))
|
||||||
except Exception as exc: # noqa: BLE001 - keep polling other jobs
|
except Exception as exc: # noqa: BLE001 - keep polling other jobs
|
||||||
failed.append({"task_id": task["id"], "error": str(exc)})
|
failed.append({"task_id": task["id"], "error": str(exc)})
|
||||||
return {"synced": len(synced), "failed": failed, "items": synced}
|
standalone_synced: list[dict[str, Any]] = []
|
||||||
|
for record in store.active_standalone_compute_jobs():
|
||||||
|
node = next((item for item in store.compute_nodes() if item["id"] == record.get("node_id")), None)
|
||||||
|
if not node:
|
||||||
|
failed.append({"job_id": record["id"], "error": "compute node not found"})
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
job = await ComputeNodeClient(node["api_base_url"]).get_job(record["id"])
|
||||||
|
standalone_synced.append(store.sync_model_merge_job(record["id"], job))
|
||||||
|
except Exception as exc: # noqa: BLE001 - keep polling other jobs
|
||||||
|
failed.append({"job_id": record["id"], "error": str(exc)})
|
||||||
|
return {"synced": len(synced) + len(standalone_synced), "failed": failed, "items": synced, "standalone": standalone_synced}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import hashlib
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
@@ -127,6 +128,7 @@ class ProcessManager:
|
|||||||
|
|
||||||
def serialize(self, job: ManagedProcess) -> dict[str, Any]:
|
def serialize(self, job: ManagedProcess) -> dict[str, Any]:
|
||||||
code = job.process.poll() if job.process is not None else None
|
code = job.process.poll() if job.process is not None else None
|
||||||
|
checkpoints = self._collect_checkpoints(job.output_dir)
|
||||||
if job.status not in TERMINAL_STATUSES:
|
if job.status not in TERMINAL_STATUSES:
|
||||||
if job.process is None and job.pid is not None and not self._pid_alive(job.pid):
|
if job.process is None and job.pid is not None and not self._pid_alive(job.pid):
|
||||||
job.status = "failed"
|
job.status = "failed"
|
||||||
@@ -157,6 +159,7 @@ class ProcessManager:
|
|||||||
"output_dir": job.output_dir,
|
"output_dir": job.output_dir,
|
||||||
"log_file": str(job.log_path),
|
"log_file": str(job.log_path),
|
||||||
"artifacts": job.artifacts,
|
"artifacts": job.artifacts,
|
||||||
|
"checkpoints": checkpoints,
|
||||||
"return_code": code,
|
"return_code": code,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,15 +178,47 @@ class ProcessManager:
|
|||||||
artifacts: list[dict[str, Any]] = []
|
artifacts: list[dict[str, Any]] = []
|
||||||
for path in root.rglob("*"):
|
for path in root.rglob("*"):
|
||||||
if path.is_file():
|
if path.is_file():
|
||||||
|
digest = hashlib.sha256()
|
||||||
|
with path.open("rb") as handle:
|
||||||
|
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
||||||
|
digest.update(chunk)
|
||||||
|
size = path.stat().st_size
|
||||||
artifacts.append(
|
artifacts.append(
|
||||||
{
|
{
|
||||||
"path": str(path),
|
"path": str(path),
|
||||||
"name": path.name,
|
"name": path.name,
|
||||||
"size": path.stat().st_size,
|
"size": size,
|
||||||
|
"size_bytes": size,
|
||||||
|
"checksum_sha256": digest.hexdigest(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return artifacts[:200]
|
return artifacts[:200]
|
||||||
|
|
||||||
|
def _collect_checkpoints(self, output_dir: str) -> list[dict[str, Any]]:
|
||||||
|
root = Path(output_dir)
|
||||||
|
if not root.exists():
|
||||||
|
return []
|
||||||
|
checkpoints: list[dict[str, Any]] = []
|
||||||
|
for path in root.glob("checkpoint-*"):
|
||||||
|
if not path.is_dir():
|
||||||
|
continue
|
||||||
|
step = 0
|
||||||
|
try:
|
||||||
|
step = int(path.name.rsplit("-", 1)[-1])
|
||||||
|
except ValueError:
|
||||||
|
step = 0
|
||||||
|
size_bytes = sum(item.stat().st_size for item in path.rglob("*") if item.is_file())
|
||||||
|
checkpoints.append(
|
||||||
|
{
|
||||||
|
"step": step,
|
||||||
|
"name": path.name,
|
||||||
|
"path": str(path),
|
||||||
|
"size_bytes": size_bytes,
|
||||||
|
"create_time": path.stat().st_mtime,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return sorted(checkpoints, key=lambda item: (int(item.get("step") or 0), str(item.get("name") or "")))
|
||||||
|
|
||||||
def _save_registry(self) -> None:
|
def _save_registry(self) -> None:
|
||||||
items = []
|
items = []
|
||||||
for job in self.jobs.values():
|
for job in self.jobs.values():
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from fastapi import FastAPI, File, Form, HTTPException, Query, Request, UploadFi
|
|||||||
from fastapi.responses import FileResponse, JSONResponse
|
from fastapi.responses import FileResponse, JSONResponse
|
||||||
|
|
||||||
from compute.agent.process_manager import ProcessManager
|
from compute.agent.process_manager import ProcessManager
|
||||||
from compute.engines.llama_factory.adapter import build_command, parse_log_line
|
from compute.engines.llama_factory.adapter import build_command, parse_log_line, prepare_runtime_files
|
||||||
|
|
||||||
|
|
||||||
def create_app() -> FastAPI:
|
def create_app() -> FastAPI:
|
||||||
@@ -74,6 +74,48 @@ def create_app() -> FastAPI:
|
|||||||
return output.splitlines()[0][:120]
|
return output.splitlines()[0][:120]
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def torch_cuda_status() -> dict[str, Any]:
|
||||||
|
try:
|
||||||
|
import torch # type: ignore[import-not-found]
|
||||||
|
except Exception as exc: # noqa: BLE001 - keep health endpoint resilient
|
||||||
|
return {
|
||||||
|
"available": False,
|
||||||
|
"device_count": 0,
|
||||||
|
"torch_version": "",
|
||||||
|
"torch_cuda_version": "",
|
||||||
|
"error": f"torch import failed: {exc}",
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
available = bool(torch.cuda.is_available())
|
||||||
|
device_count = int(torch.cuda.device_count())
|
||||||
|
devices = []
|
||||||
|
for index in range(device_count):
|
||||||
|
props = torch.cuda.get_device_properties(index)
|
||||||
|
devices.append(
|
||||||
|
{
|
||||||
|
"index": index,
|
||||||
|
"name": props.name,
|
||||||
|
"memory_total_gb": round(props.total_memory / 1024 / 1024 / 1024, 2),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"available": available,
|
||||||
|
"device_count": device_count,
|
||||||
|
"torch_version": str(torch.__version__),
|
||||||
|
"torch_cuda_version": str(torch.version.cuda or ""),
|
||||||
|
"devices": devices,
|
||||||
|
"error": "" if available else "torch cuda is not available",
|
||||||
|
}
|
||||||
|
except Exception as exc: # noqa: BLE001 - expose CUDA initialization failures
|
||||||
|
return {
|
||||||
|
"available": False,
|
||||||
|
"device_count": 0,
|
||||||
|
"torch_version": str(getattr(torch, "__version__", "")),
|
||||||
|
"torch_cuda_version": str(getattr(torch.version, "cuda", "") or ""),
|
||||||
|
"devices": [],
|
||||||
|
"error": str(exc),
|
||||||
|
}
|
||||||
|
|
||||||
def _slice_log_content(
|
def _slice_log_content(
|
||||||
content: str,
|
content: str,
|
||||||
tail_lines: int | None = None,
|
tail_lines: int | None = None,
|
||||||
@@ -261,6 +303,35 @@ def create_app() -> FastAPI:
|
|||||||
)
|
)
|
||||||
return gpus
|
return gpus
|
||||||
|
|
||||||
|
def _validate_training_accelerator(payload: dict[str, Any]) -> tuple[list[str], list[str], dict[str, Any]]:
|
||||||
|
errors: list[str] = []
|
||||||
|
warnings: list[str] = []
|
||||||
|
if str(payload.get("engine") or payload.get("training_engine") or "llama_factory") == "smoke":
|
||||||
|
return errors, warnings, {}
|
||||||
|
requested_gpus = [int(item) for item in payload.get("gpus") or []]
|
||||||
|
if not requested_gpus:
|
||||||
|
warnings.append("no gpu selected; training will run on CPU")
|
||||||
|
return errors, warnings, {}
|
||||||
|
cuda = torch_cuda_status()
|
||||||
|
if not cuda.get("available"):
|
||||||
|
errors.append(f"torch cuda unavailable on compute node: {cuda.get('error') or 'unknown error'}")
|
||||||
|
device_count = int(cuda.get("device_count") or 0)
|
||||||
|
if device_count and max(requested_gpus) >= device_count:
|
||||||
|
errors.append(f"requested gpu index out of torch device range: requested={requested_gpus}, device_count={device_count}")
|
||||||
|
min_memory_gb = _float_env("MIN_TRAINING_GPU_MEMORY_GB", 4.0)
|
||||||
|
gpus = {int(item["gpu_index"]): item for item in gpu_resources() if "gpu_index" in item}
|
||||||
|
for gpu_index in requested_gpus:
|
||||||
|
gpu = gpus.get(gpu_index)
|
||||||
|
if not gpu:
|
||||||
|
errors.append(f"requested gpu not found by nvidia-smi: {gpu_index}")
|
||||||
|
continue
|
||||||
|
memory_total = float(gpu.get("memory_total_gb") or 0)
|
||||||
|
if memory_total and memory_total < min_memory_gb:
|
||||||
|
errors.append(
|
||||||
|
f"gpu {gpu_index} memory too small: {memory_total}GB < required {min_memory_gb}GB"
|
||||||
|
)
|
||||||
|
return errors, warnings, cuda
|
||||||
|
|
||||||
def _check_path_item(item: dict[str, Any]) -> dict[str, Any]:
|
def _check_path_item(item: dict[str, Any]) -> dict[str, Any]:
|
||||||
path = Path(str(item.get("path") or ""))
|
path = Path(str(item.get("path") or ""))
|
||||||
exists = path.exists()
|
exists = path.exists()
|
||||||
@@ -278,13 +349,32 @@ def create_app() -> FastAPI:
|
|||||||
"exists": exists,
|
"exists": exists,
|
||||||
"is_dir": path.is_dir() if exists else False,
|
"is_dir": path.is_dir() if exists else False,
|
||||||
"is_file": path.is_file() if exists else False,
|
"is_file": path.is_file() if exists else False,
|
||||||
|
"byte_size": sum(child.stat().st_size for child in path.rglob("*") if child.is_file()) if exists and path.is_dir() else path.stat().st_size if exists and path.is_file() else 0,
|
||||||
"ok": ok or not item.get("required", True),
|
"ok": ok or not item.get("required", True),
|
||||||
}
|
}
|
||||||
|
|
||||||
def _job_preview(payload: dict[str, Any], check_paths: bool) -> dict[str, Any]:
|
def _job_preview(payload: dict[str, Any], check_paths: bool) -> dict[str, Any]:
|
||||||
warnings: list[str] = []
|
warnings: list[str] = []
|
||||||
|
runtime_files: list[dict[str, str]] = []
|
||||||
|
command_payload = {**payload, "require_dataset_files": check_paths}
|
||||||
|
if check_paths:
|
||||||
|
try:
|
||||||
|
runtime_files = prepare_runtime_files(command_payload)
|
||||||
|
except OSError as exc:
|
||||||
|
return {
|
||||||
|
"valid": False,
|
||||||
|
"errors": [f"prepare runtime files failed: {exc}"],
|
||||||
|
"warnings": warnings,
|
||||||
|
"engine": str(payload.get("engine") or payload.get("training_engine") or "llama_factory"),
|
||||||
|
"command": [],
|
||||||
|
"command_text": "",
|
||||||
|
"work_dir": os.getenv("LLAMA_FACTORY_HOME", "/app/LLaMA-Factory"),
|
||||||
|
"env": {},
|
||||||
|
"runtime_files": [],
|
||||||
|
"path_checks": [],
|
||||||
|
}
|
||||||
try:
|
try:
|
||||||
command = build_command(payload, os.getenv("LLAMA_FACTORY_HOME", "/app/LLaMA-Factory"))
|
command = build_command(command_payload, os.getenv("LLAMA_FACTORY_HOME", "/app/LLaMA-Factory"))
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
return {
|
return {
|
||||||
"valid": False,
|
"valid": False,
|
||||||
@@ -295,23 +385,36 @@ def create_app() -> FastAPI:
|
|||||||
"command_text": "",
|
"command_text": "",
|
||||||
"work_dir": os.getenv("LLAMA_FACTORY_HOME", "/app/LLaMA-Factory"),
|
"work_dir": os.getenv("LLAMA_FACTORY_HOME", "/app/LLaMA-Factory"),
|
||||||
"env": {},
|
"env": {},
|
||||||
|
"runtime_files": runtime_files,
|
||||||
"path_checks": [],
|
"path_checks": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
errors: list[str] = []
|
errors: list[str] = []
|
||||||
engine = str(payload.get("engine") or payload.get("training_engine") or "llama_factory")
|
engine = str(payload.get("engine") or payload.get("training_engine") or "llama_factory")
|
||||||
path_checks: list[dict[str, Any]] = []
|
path_checks: list[dict[str, Any]] = []
|
||||||
|
accelerator: dict[str, Any] = {}
|
||||||
if check_paths and engine != "smoke":
|
if check_paths and engine != "smoke":
|
||||||
path_checks = [
|
path_checks = [
|
||||||
_check_path_item(
|
_check_path_item(
|
||||||
{
|
{
|
||||||
"name": "model_name_or_path",
|
"name": "model_name_or_path",
|
||||||
"path": payload.get("model_name_or_path") or payload.get("base_model") or "",
|
"path": payload.get("model_name_or_path") or payload.get("base_model") or payload.get("base_model_path") or "",
|
||||||
"type": "any",
|
"type": "any",
|
||||||
"required": True,
|
"required": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
if engine in {"merge", "export", "llama_factory_export"} and payload.get("adapter_name_or_path"):
|
||||||
|
path_checks.append(
|
||||||
|
_check_path_item(
|
||||||
|
{
|
||||||
|
"name": "adapter_name_or_path",
|
||||||
|
"path": payload.get("adapter_name_or_path"),
|
||||||
|
"type": "any",
|
||||||
|
"required": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
if payload.get("dataset_dir"):
|
if payload.get("dataset_dir"):
|
||||||
path_checks.append(
|
path_checks.append(
|
||||||
_check_path_item(
|
_check_path_item(
|
||||||
@@ -341,6 +444,10 @@ def create_app() -> FastAPI:
|
|||||||
errors.append(f"training command not found: {command.command[0]}")
|
errors.append(f"training command not found: {command.command[0]}")
|
||||||
if not Path(command.work_dir).exists():
|
if not Path(command.work_dir).exists():
|
||||||
errors.append(f"llama_factory_home not found: {command.work_dir}")
|
errors.append(f"llama_factory_home not found: {command.work_dir}")
|
||||||
|
if engine not in {"merge", "export", "llama_factory_export"}:
|
||||||
|
accelerator_errors, accelerator_warnings, accelerator = _validate_training_accelerator(payload)
|
||||||
|
errors.extend(accelerator_errors)
|
||||||
|
warnings.extend(accelerator_warnings)
|
||||||
elif engine == "smoke":
|
elif engine == "smoke":
|
||||||
warnings.append("smoke engine skips model and dataset path checks")
|
warnings.append("smoke engine skips model and dataset path checks")
|
||||||
|
|
||||||
@@ -353,6 +460,8 @@ def create_app() -> FastAPI:
|
|||||||
"command_text": " ".join(command.command),
|
"command_text": " ".join(command.command),
|
||||||
"work_dir": command.work_dir,
|
"work_dir": command.work_dir,
|
||||||
"env": command.env,
|
"env": command.env,
|
||||||
|
"runtime_files": runtime_files,
|
||||||
|
"accelerator": accelerator,
|
||||||
"path_checks": path_checks,
|
"path_checks": path_checks,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,6 +482,8 @@ def create_app() -> FastAPI:
|
|||||||
dataset_root = Path(os.getenv("YG_FT_DATASET_ROOT", str(data_root / "datasets")))
|
dataset_root = Path(os.getenv("YG_FT_DATASET_ROOT", str(data_root / "datasets")))
|
||||||
output_root = Path(os.getenv("YG_FT_OUTPUT_ROOT", str(data_root / "outputs")))
|
output_root = Path(os.getenv("YG_FT_OUTPUT_ROOT", str(data_root / "outputs")))
|
||||||
llama_factory_home = Path(os.getenv("LLAMA_FACTORY_HOME", "/app/LLaMA-Factory"))
|
llama_factory_home = Path(os.getenv("LLAMA_FACTORY_HOME", "/app/LLaMA-Factory"))
|
||||||
|
gpu_items = gpu_resources()
|
||||||
|
torch_cuda = torch_cuda_status()
|
||||||
return {
|
return {
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"api_version": "v1",
|
"api_version": "v1",
|
||||||
@@ -391,8 +502,10 @@ def create_app() -> FastAPI:
|
|||||||
"llama_factory_version": os.getenv("LLAMA_FACTORY_VERSION", ""),
|
"llama_factory_version": os.getenv("LLAMA_FACTORY_VERSION", ""),
|
||||||
"execution_mode": execution_mode(),
|
"execution_mode": execution_mode(),
|
||||||
"gpu_count": _int_env("COMPUTE_GPU_COUNT", 0),
|
"gpu_count": _int_env("COMPUTE_GPU_COUNT", 0),
|
||||||
|
"nvidia_gpu_count": len(gpu_items),
|
||||||
|
"torch_cuda": torch_cuda,
|
||||||
"gpu_discovery_endpoint": f"{route_prefix}/compute/resources/gpus",
|
"gpu_discovery_endpoint": f"{route_prefix}/compute/resources/gpus",
|
||||||
"capabilities": ["gpu_discovery", "llama_factory", "file_gateway", "job_polling"],
|
"capabilities": ["gpu_discovery", "torch_cuda_diagnostics", "llama_factory", "file_gateway", "job_polling"],
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get(f"{route_prefix}/v1/compute/jobs")
|
@app.get(f"{route_prefix}/v1/compute/jobs")
|
||||||
@@ -466,6 +579,11 @@ def create_app() -> FastAPI:
|
|||||||
|
|
||||||
@app.post(f"{route_prefix}/compute/jobs")
|
@app.post(f"{route_prefix}/compute/jobs")
|
||||||
async def create_job(payload: dict[str, Any]) -> dict[str, Any]:
|
async def create_job(payload: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
payload = {**payload, "require_dataset_files": True}
|
||||||
|
try:
|
||||||
|
prepare_runtime_files(payload)
|
||||||
|
except OSError as exc:
|
||||||
|
raise HTTPException(status_code=400, detail=f"prepare runtime files failed: {exc}")
|
||||||
try:
|
try:
|
||||||
command = build_command(payload, os.getenv("LLAMA_FACTORY_HOME", "/app/LLaMA-Factory"))
|
command = build_command(payload, os.getenv("LLAMA_FACTORY_HOME", "/app/LLaMA-Factory"))
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
import re
|
import re
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -13,6 +14,67 @@ class LlamaFactoryCommand:
|
|||||||
env: dict[str, str]
|
env: dict[str, str]
|
||||||
|
|
||||||
|
|
||||||
|
def _load_dataset_preview(path: Path) -> list[dict[str, Any]]:
|
||||||
|
if not path.exists():
|
||||||
|
return []
|
||||||
|
text = path.read_text(encoding="utf-8", errors="replace").strip()
|
||||||
|
if not text:
|
||||||
|
return []
|
||||||
|
if path.suffix.lower() == ".jsonl":
|
||||||
|
items: list[dict[str, Any]] = []
|
||||||
|
for line in text.splitlines()[:20]:
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
value = json.loads(line)
|
||||||
|
if isinstance(value, dict):
|
||||||
|
items.append(value)
|
||||||
|
return items
|
||||||
|
value = json.loads(text)
|
||||||
|
if isinstance(value, list):
|
||||||
|
return [item for item in value[:20] if isinstance(item, dict)]
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return [value]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_dataset_columns(config: dict[str, Any]) -> list[str]:
|
||||||
|
dataset_dir = config.get("dataset_dir")
|
||||||
|
dataset_info = config.get("dataset_info")
|
||||||
|
if not dataset_dir or not isinstance(dataset_info, dict):
|
||||||
|
return []
|
||||||
|
root = Path(str(dataset_dir))
|
||||||
|
errors: list[str] = []
|
||||||
|
for dataset_key, item in dataset_info.items():
|
||||||
|
if not isinstance(item, dict):
|
||||||
|
continue
|
||||||
|
file_name = item.get("file_name")
|
||||||
|
file_names = file_name if isinstance(file_name, list) else [file_name]
|
||||||
|
columns = item.get("columns") if isinstance(item.get("columns"), dict) else {}
|
||||||
|
required_columns = [str(value) for value in columns.values() if value]
|
||||||
|
for name in file_names:
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
path = root / str(name).lstrip("/\\")
|
||||||
|
if not path.exists():
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
preview_rows = _load_dataset_preview(path)
|
||||||
|
except Exception as exc: # noqa: BLE001 - expose malformed data as validation error
|
||||||
|
errors.append(f"dataset file parse failed: {path}: {exc}")
|
||||||
|
continue
|
||||||
|
if not preview_rows:
|
||||||
|
errors.append(f"dataset file has no valid object records: {path}")
|
||||||
|
continue
|
||||||
|
available = set().union(*(row.keys() for row in preview_rows))
|
||||||
|
missing = [column for column in required_columns if column not in available]
|
||||||
|
if missing:
|
||||||
|
errors.append(
|
||||||
|
f"dataset columns missing in {path.name} for {dataset_key}: {', '.join(sorted(set(missing)))}"
|
||||||
|
)
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
def validate_config(config: dict[str, Any]) -> list[str]:
|
def validate_config(config: dict[str, Any]) -> list[str]:
|
||||||
errors: list[str] = []
|
errors: list[str] = []
|
||||||
if not config.get("base_model") and not config.get("model_name_or_path"):
|
if not config.get("base_model") and not config.get("model_name_or_path"):
|
||||||
@@ -31,6 +93,24 @@ def validate_config(config: dict[str, Any]) -> list[str]:
|
|||||||
epochs = 0
|
epochs = 0
|
||||||
if epochs <= 0:
|
if epochs <= 0:
|
||||||
errors.append("n_epochs must be greater than zero")
|
errors.append("n_epochs must be greater than zero")
|
||||||
|
dataset_dir = config.get("dataset_dir")
|
||||||
|
dataset_info = config.get("dataset_info")
|
||||||
|
if config.get("require_dataset_files") and dataset_dir and isinstance(dataset_info, dict):
|
||||||
|
root = Path(str(dataset_dir))
|
||||||
|
for dataset_key, item in dataset_info.items():
|
||||||
|
if not isinstance(item, dict):
|
||||||
|
errors.append(f"dataset_info entry must be object: {dataset_key}")
|
||||||
|
continue
|
||||||
|
file_name = item.get("file_name")
|
||||||
|
file_names = file_name if isinstance(file_name, list) else [file_name]
|
||||||
|
for name in file_names:
|
||||||
|
if not name:
|
||||||
|
errors.append(f"dataset_info file_name is required: {dataset_key}")
|
||||||
|
continue
|
||||||
|
path = root / str(name).lstrip("/\\")
|
||||||
|
if not path.exists():
|
||||||
|
errors.append(f"dataset file not found: {path}")
|
||||||
|
errors.extend(_validate_dataset_columns(config))
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
|
||||||
@@ -42,12 +122,92 @@ def _optional_arg(config: dict[str, Any], command: list[str], option: str, *keys
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def _optional_bool_arg(config: dict[str, Any], command: list[str], option: str, *keys: str) -> None:
|
||||||
|
for key in keys:
|
||||||
|
value = config.get(key)
|
||||||
|
if value is True or str(value).lower() == "true":
|
||||||
|
command.extend([option, "true"])
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_stage(config: dict[str, Any]) -> str:
|
||||||
|
raw = str(config.get("stage") or config.get("train_type") or "sft").strip().lower()
|
||||||
|
return {
|
||||||
|
"sft": "sft",
|
||||||
|
"dpo": "dpo",
|
||||||
|
"cpt": "pt",
|
||||||
|
"pt": "pt",
|
||||||
|
"pretrain": "pt",
|
||||||
|
"rm": "rm",
|
||||||
|
"ppo": "ppo",
|
||||||
|
"kto": "kto",
|
||||||
|
}.get(raw, raw or "sft")
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_runtime_files(config: dict[str, Any]) -> list[dict[str, str]]:
|
||||||
|
dataset_dir = config.get("dataset_dir")
|
||||||
|
dataset_info = config.get("dataset_info")
|
||||||
|
if not dataset_dir or not isinstance(dataset_info, dict):
|
||||||
|
return []
|
||||||
|
root = Path(str(dataset_dir))
|
||||||
|
root.mkdir(parents=True, exist_ok=True)
|
||||||
|
path = root / "dataset_info.json"
|
||||||
|
existing: dict[str, Any] = {}
|
||||||
|
if path.exists():
|
||||||
|
try:
|
||||||
|
loaded = json.loads(path.read_text(encoding="utf-8"))
|
||||||
|
existing = loaded if isinstance(loaded, dict) else {}
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
existing = {}
|
||||||
|
existing.update(dataset_info)
|
||||||
|
path.write_text(json.dumps(existing, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||||
|
return [{"name": "dataset_info", "path": str(path)}]
|
||||||
|
|
||||||
|
|
||||||
def build_command(config: dict[str, Any], llama_factory_home: str = "/app/LLaMA-Factory") -> LlamaFactoryCommand:
|
def build_command(config: dict[str, Any], llama_factory_home: str = "/app/LLaMA-Factory") -> LlamaFactoryCommand:
|
||||||
|
engine = str(config.get("engine") or config.get("training_engine") or "llama_factory")
|
||||||
|
if engine in {"merge", "export", "llama_factory_export"}:
|
||||||
|
model_path = config.get("base_model") or config.get("model_name_or_path") or config.get("base_model_path")
|
||||||
|
adapter_path = config.get("adapter_name_or_path") or config.get("adapter_path") or config.get("lora_path")
|
||||||
|
output_dir = config.get("output_dir") or config.get("export_dir")
|
||||||
|
errors: list[str] = []
|
||||||
|
if not model_path:
|
||||||
|
errors.append("base_model or model_name_or_path is required")
|
||||||
|
if not adapter_path and engine == "merge":
|
||||||
|
errors.append("adapter_name_or_path or adapter_path is required")
|
||||||
|
if not output_dir:
|
||||||
|
errors.append("output_dir or export_dir is required")
|
||||||
|
if errors:
|
||||||
|
raise ValueError("; ".join(errors))
|
||||||
|
command = [
|
||||||
|
"llamafactory-cli",
|
||||||
|
"export",
|
||||||
|
"--model_name_or_path",
|
||||||
|
str(model_path),
|
||||||
|
"--template",
|
||||||
|
str(config.get("template", "qwen")),
|
||||||
|
"--finetuning_type",
|
||||||
|
str(config.get("train_method", config.get("finetuning_type", "lora"))),
|
||||||
|
"--export_dir",
|
||||||
|
str(output_dir),
|
||||||
|
"--export_size",
|
||||||
|
str(config.get("export_size", 2)),
|
||||||
|
"--export_device",
|
||||||
|
str(config.get("export_device", "cpu")),
|
||||||
|
"--export_legacy_format",
|
||||||
|
str(config.get("export_legacy_format", False)).lower(),
|
||||||
|
]
|
||||||
|
if adapter_path:
|
||||||
|
command.extend(["--adapter_name_or_path", str(adapter_path)])
|
||||||
|
quantization_bit = int(config.get("export_quantization_bit", config.get("quantization_bit", 0)) or 0)
|
||||||
|
if quantization_bit in {4, 8}:
|
||||||
|
command.extend(["--quantization_bit", str(quantization_bit)])
|
||||||
|
return LlamaFactoryCommand(command=command, work_dir=str(Path(llama_factory_home)), env={})
|
||||||
|
|
||||||
errors = validate_config(config)
|
errors = validate_config(config)
|
||||||
if errors:
|
if errors:
|
||||||
raise ValueError("; ".join(errors))
|
raise ValueError("; ".join(errors))
|
||||||
|
|
||||||
engine = str(config.get("engine") or config.get("training_engine") or "llama_factory")
|
|
||||||
if engine == "smoke":
|
if engine == "smoke":
|
||||||
output_dir = config.get("output_dir") or f"/data/yg-ft/outputs/{config.get('name', 'training-smoke')}"
|
output_dir = config.get("output_dir") or f"/data/yg-ft/outputs/{config.get('name', 'training-smoke')}"
|
||||||
script = (
|
script = (
|
||||||
@@ -75,7 +235,7 @@ def build_command(config: dict[str, Any], llama_factory_home: str = "/app/LLaMA-
|
|||||||
"llamafactory-cli",
|
"llamafactory-cli",
|
||||||
"train",
|
"train",
|
||||||
"--stage",
|
"--stage",
|
||||||
str(config.get("stage", "sft")).lower(),
|
_normalize_stage(config),
|
||||||
"--do_train",
|
"--do_train",
|
||||||
"true",
|
"true",
|
||||||
"--model_name_or_path",
|
"--model_name_or_path",
|
||||||
@@ -112,6 +272,12 @@ def build_command(config: dict[str, Any], llama_factory_home: str = "/app/LLaMA-
|
|||||||
_optional_arg(config, command, "--lora_rank", "lora_rank", "rank")
|
_optional_arg(config, command, "--lora_rank", "lora_rank", "rank")
|
||||||
_optional_arg(config, command, "--lora_alpha", "lora_alpha")
|
_optional_arg(config, command, "--lora_alpha", "lora_alpha")
|
||||||
_optional_arg(config, command, "--lora_dropout", "lora_dropout")
|
_optional_arg(config, command, "--lora_dropout", "lora_dropout")
|
||||||
|
_optional_arg(config, command, "--gradient_accumulation_steps", "gradient_accumulation_steps")
|
||||||
|
_optional_arg(config, command, "--val_size", "val_size")
|
||||||
|
_optional_arg(config, command, "--max_samples", "max_samples")
|
||||||
|
_optional_arg(config, command, "--preprocessing_num_workers", "preprocessing_num_workers")
|
||||||
|
_optional_bool_arg(config, command, "--fp16", "fp16")
|
||||||
|
_optional_bool_arg(config, command, "--bf16", "bf16")
|
||||||
quantization_bit = int(config.get("quantization_bit", 0) or 0)
|
quantization_bit = int(config.get("quantization_bit", 0) or 0)
|
||||||
if quantization_bit in {4, 8}:
|
if quantization_bit in {4, 8}:
|
||||||
command.extend(["--quantization_bit", str(quantization_bit)])
|
command.extend(["--quantization_bit", str(quantization_bit)])
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ services:
|
|||||||
image: ${BACKEND_API_IMAGE:-yg-ft-backend-api:latest}
|
image: ${BACKEND_API_IMAGE:-yg-ft-backend-api:latest}
|
||||||
container_name: yg-ft-backend-api
|
container_name: yg-ft-backend-api
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
|
||||||
condition: service_healthy
|
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
expose:
|
expose:
|
||||||
@@ -45,9 +43,9 @@ services:
|
|||||||
APP_NAME: ${APP_NAME:-YG Fine-Tune Platform API}
|
APP_NAME: ${APP_NAME:-YG Fine-Tune Platform API}
|
||||||
MODELTF_ROUTE_PREFIX: ${MODELTF_ROUTE_PREFIX:-/modelTF}
|
MODELTF_ROUTE_PREFIX: ${MODELTF_ROUTE_PREFIX:-/modelTF}
|
||||||
CORS_ALLOW_ORIGINS: ${CORS_ALLOW_ORIGINS:-http://localhost:16801,http://127.0.0.1:16801}
|
CORS_ALLOW_ORIGINS: ${CORS_ALLOW_ORIGINS:-http://localhost:16801,http://127.0.0.1:16801}
|
||||||
DATABASE_URL: ${DATABASE_URL:-postgresql+psycopg://yg_ft:change_me@postgres:5432/yg_ft}
|
DATABASE_URL: ${DATABASE_URL:-postgresql+psycopg://root:8811614287327Leo@www.caoxiaozhu.com:5432/yg_ft}
|
||||||
REDIS_URL: ${REDIS_URL:-redis://redis:6379/0}
|
REDIS_URL: ${REDIS_URL:-redis://redis:6379/0}
|
||||||
USE_BUILTIN_POSTGRES: ${USE_BUILTIN_POSTGRES:-true}
|
USE_BUILTIN_POSTGRES: ${USE_BUILTIN_POSTGRES:-false}
|
||||||
USE_BUILTIN_REDIS: ${USE_BUILTIN_REDIS:-true}
|
USE_BUILTIN_REDIS: ${USE_BUILTIN_REDIS:-true}
|
||||||
LOG_LEVEL: ${LOG_LEVEL:-INFO}
|
LOG_LEVEL: ${LOG_LEVEL:-INFO}
|
||||||
LOG_DIR: ${LOG_DIR:-/opt/yg-ft/logs/backend}
|
LOG_DIR: ${LOG_DIR:-/opt/yg-ft/logs/backend}
|
||||||
@@ -78,27 +76,29 @@ services:
|
|||||||
start_period: 20s
|
start_period: 20s
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
postgres:
|
# PostgreSQL uses the shared external database configured by DATABASE_URL in docker/app/.env.
|
||||||
image: postgres:16-alpine
|
# Keep this local service commented out unless development needs an isolated database again.
|
||||||
container_name: yg-ft-postgres
|
# postgres:
|
||||||
environment:
|
# image: postgres:16-alpine
|
||||||
POSTGRES_DB: ${POSTGRES_DB:-yg_ft}
|
# container_name: yg-ft-postgres
|
||||||
POSTGRES_USER: ${POSTGRES_USER:-yg_ft}
|
# environment:
|
||||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-change_me}
|
# POSTGRES_DB: ${POSTGRES_DB:-yg_ft}
|
||||||
PGDATA: /var/lib/postgresql/data/pgdata
|
# POSTGRES_USER: ${POSTGRES_USER:-yg_ft}
|
||||||
volumes:
|
# POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-change_me}
|
||||||
- postgres_data:/var/lib/postgresql/data
|
# PGDATA: /var/lib/postgresql/data/pgdata
|
||||||
- ../../backend/app/db/sql/001_platform_runtime.sql:/docker-entrypoint-initdb.d/001-platform-runtime.sql:ro
|
# volumes:
|
||||||
ports:
|
# - postgres_data:/var/lib/postgresql/data
|
||||||
- "${POSTGRES_PORT:-15432}:5432"
|
# - ../../backend/app/db/sql/001_platform_runtime.sql:/docker-entrypoint-initdb.d/001-platform-runtime.sql:ro
|
||||||
networks:
|
# ports:
|
||||||
- yg-ft-app
|
# - "${POSTGRES_PORT:-15432}:5432"
|
||||||
healthcheck:
|
# networks:
|
||||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
# - yg-ft-app
|
||||||
interval: 10s
|
# healthcheck:
|
||||||
timeout: 5s
|
# test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
||||||
retries: 5
|
# interval: 10s
|
||||||
restart: unless-stopped
|
# timeout: 5s
|
||||||
|
# retries: 5
|
||||||
|
# restart: unless-stopped
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
@@ -122,5 +122,5 @@ networks:
|
|||||||
name: yg-ft-app
|
name: yg-ft-app
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
# postgres_data:
|
||||||
redis_data:
|
redis_data:
|
||||||
|
|||||||
2
docker/compute/data/yg-ft/datasets/.gitignore
vendored
Normal file
2
docker/compute/data/yg-ft/datasets/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
2
docker/compute/data/yg-ft/logs/compute/.gitignore
vendored
Normal file
2
docker/compute/data/yg-ft/logs/compute/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
2
docker/compute/data/yg-ft/logs/training/.gitignore
vendored
Normal file
2
docker/compute/data/yg-ft/logs/training/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
2
docker/compute/data/yg-ft/models/.gitignore
vendored
Normal file
2
docker/compute/data/yg-ft/models/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
2
docker/compute/data/yg-ft/outputs/.gitignore
vendored
Normal file
2
docker/compute/data/yg-ft/outputs/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
@@ -24,6 +24,7 @@ services:
|
|||||||
COMPUTE_GPU_NAME: ${COMPUTE_GPU_NAME:-NVIDIA A800-SXM4-80GB}
|
COMPUTE_GPU_NAME: ${COMPUTE_GPU_NAME:-NVIDIA A800-SXM4-80GB}
|
||||||
COMPUTE_GPU_MEMORY_GB: ${COMPUTE_GPU_MEMORY_GB:-80}
|
COMPUTE_GPU_MEMORY_GB: ${COMPUTE_GPU_MEMORY_GB:-80}
|
||||||
COMPUTE_GPU_POWER_LIMIT_W: ${COMPUTE_GPU_POWER_LIMIT_W:-300}
|
COMPUTE_GPU_POWER_LIMIT_W: ${COMPUTE_GPU_POWER_LIMIT_W:-300}
|
||||||
|
MIN_TRAINING_GPU_MEMORY_GB: ${MIN_TRAINING_GPU_MEMORY_GB:-4}
|
||||||
LOG_DIR: ${LOG_DIR:-/opt/yg-ft/logs/compute}
|
LOG_DIR: ${LOG_DIR:-/opt/yg-ft/logs/compute}
|
||||||
CUDA_VISIBLE_DEVICES: ${CUDA_VISIBLE_DEVICES:-all}
|
CUDA_VISIBLE_DEVICES: ${CUDA_VISIBLE_DEVICES:-all}
|
||||||
NVIDIA_VISIBLE_DEVICES: ${NVIDIA_VISIBLE_DEVICES:-all}
|
NVIDIA_VISIBLE_DEVICES: ${NVIDIA_VISIBLE_DEVICES:-all}
|
||||||
|
|||||||
153
docs/2026-07-23-development-summary.md
Normal file
153
docs/2026-07-23-development-summary.md
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
# 2026-07-23 开发总结
|
||||||
|
|
||||||
|
本文档汇总 2026-07-23 当天围绕模型微调平台完成的系统开发内容。当前开发不再按临时 Demo 口径处理,以下能力均按后续可继续演进到生产系统的代码和数据结构推进。
|
||||||
|
|
||||||
|
## 1. 今日完成内容
|
||||||
|
|
||||||
|
### 1.1 模型训练最小闭环增强
|
||||||
|
|
||||||
|
| 功能点 | 作用 | 对应页面/模块 | 用户如何操作 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 训练任务创建前预检 | 在任务进入运行前校验模型、数据集、算力节点、路径和 LLaMA-Factory 参数,减少任务启动后才失败的情况 | 模型微调列表、创建模型微调、训练日志 | 在“模型微调”页面创建任务并提交,系统自动执行预检;失败时页面提示具体原因 |
|
||||||
|
| 训练数据同步到算力节点 | 将应用侧选择的数据集文件同步到目标 Compute 节点本地磁盘,保证 LLaMA-Factory 能读取到真实训练文件 | 创建模型微调、训练日志、算力节点/资源副本 | 用户选择数据集后提交训练任务,系统自动同步数据集到算力节点,无需手工进入容器复制 |
|
||||||
|
| 动态生成训练参数 | 根据用户在页面选择的训练数据、基座模型、训练方法、输出目录等动态生成 LLaMA-Factory 训练参数 | 创建模型微调、训练日志 | 用户在创建任务表单中选择模型和数据集,启动训练后可在训练日志中查看实际执行命令 |
|
||||||
|
| 训练日志和状态轮询 | 应用侧主动轮询 Compute API 获取训练状态、进度、日志、指标和 checkpoint | 训练日志详情页 | 用户进入训练日志页,可查看任务状态、日志输出、指标和 checkpoint 信息 |
|
||||||
|
| 训练失败信息回传 | 训练命令失败后将失败状态和日志内容回传应用侧,便于定位数据格式、模型路径或参数问题 | 模型微调列表、训练日志详情页 | 任务失败后,用户进入训练日志页查看失败日志和错误堆栈 |
|
||||||
|
|
||||||
|
### 1.2 B. 模型资产、训练与 LLaMA-Factory 任务能力
|
||||||
|
|
||||||
|
| 功能点 | 作用 | 对应页面/模块 | 用户如何操作 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 训练模型产物 artifact 展示 | 展示训练/合并产生的 Adapter、合并模型、量化模型等产物路径、大小、checksum | 模型管理 -> 训练模型列表 | 用户进入“模型管理”,切换到“训练模型”,展开某个模型行查看“模型产物” |
|
||||||
|
| 模型血缘 lineage 展示 | 记录和展示基座模型、训练模型、合并产物之间的来源关系 | 模型管理 -> 训练模型列表 | 用户展开训练模型行,在“模型血缘”区域查看父资源、子资源和对应 Compute Job |
|
||||||
|
| 模型导出任务 export job 状态展示 | 展示模型合并/导出的任务状态、输出目录和创建时间 | 模型管理 -> 训练模型列表 | 用户展开训练模型行,在“导出任务”区域查看导出任务状态 |
|
||||||
|
| export 完成后回填 checksum | Compute export 完成后扫描产物文件,回填大小和 `checksum_sha256`,为后续一致性校验和制品治理做准备 | 后端模型资产模块、模型管理页面 | 用户无需手工操作,任务完成后页面自动展示产物大小和 checksum |
|
||||||
|
| 合并模型任务接入 Compute | 模型合并不再只是生成模拟任务,而是通过 Compute 节点调用模型合并任务并记录导出任务 | 模型管理 -> 合并权重 | 用户在训练模型列表点击“合并权重”,提交后系统创建 Compute 合并任务 |
|
||||||
|
|
||||||
|
### 1.3 D. 算力平台、部署与运维能力
|
||||||
|
|
||||||
|
| 功能点 | 作用 | 对应页面/模块 | 用户如何操作 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 算力节点动态配置 | 支持维护 Compute API、File Gateway、权重、标签、启用状态、调度状态等信息 | 算力节点 -> 节点 | 用户进入“算力节点”,点击“新增节点”或“编辑”,填写节点地址和调度参数 |
|
||||||
|
| 节点连通性测试 | 验证应用服务器是否能访问 Compute API,并返回 GPU 发现结果和延迟 | 算力节点 -> 节点 | 用户在节点列表点击“测试”,查看连接成功或失败提示 |
|
||||||
|
| GPU 状态展示 | 展示每个算力节点的 GPU 利用率、显存、温度、功耗、进程信息 | 算力节点 -> GPU | 用户进入“算力节点”,切换到“GPU”页签查看 |
|
||||||
|
| 训练队列展示 | 展示 Compute 侧任务队列、状态、进度、节点和 GPU 分配 | 算力节点 -> 队列 | 用户切换到“队列”页签查看当前运行或等待任务 |
|
||||||
|
| 资源副本列表 | 展示数据集、模型、训练产物在算力节点本地磁盘上的副本路径和同步状态 | 算力节点 -> 资源副本 | 用户切换到“资源副本”页签,选择目标节点查看本地副本 |
|
||||||
|
| 资源副本漂移检测 | 检查副本路径是否仍存在、大小是否可读取,并将异常副本标记为漂移 | 算力节点 -> 资源副本 | 用户点击“漂移检测”,系统调用 Compute API 检查节点本地路径 |
|
||||||
|
| 资源副本 repair 重同步 | 对漂移、失败、待修复副本执行真实重同步,而不只是标记状态 | 算力节点 -> 资源副本 | 用户点击“修复副本”,系统从权威源重新上传或导入到目标算力节点 |
|
||||||
|
| 应用侧轮询模式 | 应用服务主动轮询 Compute API,避免需要 Compute 侧反向访问应用侧 | 后端 Compute Gateway、部署架构 | 用户无感知,部署时只需保证应用侧能访问各 Compute 节点 |
|
||||||
|
| 本地磁盘挂载约定 | 基座模型、训练数据、训练产物、训练日志均通过宿主机目录挂载到 Compute 容器 | docker/compute 部署 | 运维人员在启动 Compute 服务前准备并挂载 `docker/compute/data/yg-ft/*` 目录 |
|
||||||
|
|
||||||
|
### 1.4 前端状态中文化
|
||||||
|
|
||||||
|
| 功能点 | 作用 | 对应页面/模块 | 用户如何操作 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 统一状态字典 | 将 `pending`、`running`、`completed`、`failed`、`online`、`synced`、`drifted` 等状态统一展示为中文 | 通用前端组件 `ModelStatusTag`、状态工具 `status.ts` | 用户在各列表页看到中文状态,不再直接看到英文状态值 |
|
||||||
|
| 模型管理状态中文化 | 合并状态、导出任务状态、产物状态使用中文展示 | 模型管理 | 用户查看训练模型列表和展开详情时看到“未合并/合并中/已合并/已完成/失败”等中文 |
|
||||||
|
| 算力节点状态中文化 | 节点状态、GPU 状态、队列状态、副本同步状态使用中文展示 | 算力节点 | 用户查看“节点/GPU/队列/资源副本”时看到中文状态标签 |
|
||||||
|
| 推理/对比/评测/用户状态中文化 | 将推理任务、对比模型加载、评测任务、用户状态统一为中文标签 | 模型推理、模型对比、模型评测、用户设置 | 用户查看相关页面时看到“启动中/已就绪/已完成/启用”等中文状态 |
|
||||||
|
|
||||||
|
### 1.5 前端构建产物更新
|
||||||
|
|
||||||
|
| 功能点 | 作用 | 对应页面/模块 | 用户如何操作 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 刷新 `frontend/dist` | 保证 Docker 前端 nginx 容器可以直接加载最新页面代码 | 前端部署 | 用户重新访问前端服务即可看到最新页面 |
|
||||||
|
| 重启前后端服务验证 | 验证源码挂载和 dist 挂载方式下服务可正常加载最新代码 | docker/app、docker/compute | 开发人员重启 `backend-api`、`frontend`、`compute-api` 后验证页面和接口 |
|
||||||
|
|
||||||
|
## 2. 今日涉及的主要代码位置
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `backend/app/api/v1/endpoints/platform.py` | 新增/完善训练预检、资源同步、模型 artifact/lineage/export job 接口、资源副本漂移检测和 repair |
|
||||||
|
| `backend/app/db/platform_store.py` | 新增模型产物、血缘、导出任务、副本同步结果、artifact 查询等数据访问能力 |
|
||||||
|
| `compute/agent/process_manager.py` | export/artifact 扫描时补充文件大小和 checksum |
|
||||||
|
| `frontend/src/api/modules/model.ts` | 增加训练模型 artifact、lineage、export job API |
|
||||||
|
| `frontend/src/api/modules/compute.ts` | 增加副本漂移检测、repair API 和副本字段 |
|
||||||
|
| `frontend/src/views/model/ModelManageView.vue` | 增加训练模型展开详情:产物、血缘、导出任务 |
|
||||||
|
| `frontend/src/views/compute/ComputeNodesView.vue` | 增加资源副本页签、漂移检测、repair 操作和状态中文化 |
|
||||||
|
| `frontend/src/utils/status.ts` | 新增统一状态中文映射和标签类型映射 |
|
||||||
|
| `frontend/src/components/ModelStatusTag.vue` | 改为复用统一状态字典 |
|
||||||
|
| `frontend/src/components/DataTablePage.vue` | 增加表格展开事件支持 |
|
||||||
|
| `frontend/dist/*` | 前端生产构建产物已更新 |
|
||||||
|
|
||||||
|
## 3. 今日验证结果
|
||||||
|
|
||||||
|
| 验证项 | 结果 |
|
||||||
|
| --- | --- |
|
||||||
|
| Python 编译检查 | 通过:`python -m compileall backend compute scripts` |
|
||||||
|
| 前端生产构建 | 通过:`npm run build` |
|
||||||
|
| 前端入口 | 通过:`http://127.0.0.1:16801/` 返回 200 |
|
||||||
|
| 后端健康检查 | 通过:`/modelTF/health` 返回正常 |
|
||||||
|
| Compute 健康检查 | 通过:`/modelTF/health` 返回正常 |
|
||||||
|
| 模型导出任务接口 | 通过:`/modelTF/model-manage/export-jobs` 返回正常 |
|
||||||
|
| 算力节点列表接口 | 通过:`/modelTF/compute/nodes` 返回正常 |
|
||||||
|
| 资源副本漂移检测 | 通过:当前节点副本检测返回 `drifted: 0` |
|
||||||
|
|
||||||
|
## 4. 当前仍需注意的问题
|
||||||
|
|
||||||
|
| 问题 | 影响 | 建议 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 训练数据格式仍依赖 LLaMA-Factory 约定 | 如果用户上传的数据字段不符合模板要求,训练仍会失败 | 下一步增加数据集格式校验和模板转换预检 |
|
||||||
|
| 单机 MX350 显存较小 | 真实训练大模型时容易因显存不足失败 | 当前环境用于链路验证;真实训练应使用高显存 GPU 节点 |
|
||||||
|
| resource replica repair 对大型模型仍是同步调用 | 大模型重同步可能耗时较长 | 下一步将 repair 完整异步化,并展示 sync job 进度 |
|
||||||
|
| artifact checksum 目前在 Compute 扫描阶段计算 | 对超大目录递归扫描可能较慢 | 下一步支持分文件 checksum、manifest 文件和后台扫描 |
|
||||||
|
| 模型评测、模型推理仍未形成完整生产闭环 | 目前页面已有基础能力,但后端表结构、任务运行、日志、治理能力还需补齐 | 下一步将评测和推理纳入正式开发计划 |
|
||||||
|
|
||||||
|
## 5. 下一步开发计划
|
||||||
|
|
||||||
|
### 5.1 B. 模型资产、训练与 LLaMA-Factory 任务
|
||||||
|
|
||||||
|
| 优先级 | 开发任务 | 目标页面/模块 | 交付结果 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| P0 | 数据集格式预检 | 创建模型微调、数据集管理 | 提交训练前校验 Alpaca/ShareGPT/OpenAI Messages 等格式,提示缺失字段 |
|
||||||
|
| P0 | 训练参数可视化确认 | 创建模型微调 | 提交前展示最终 LLaMA-Factory 参数预览,减少参数不一致问题 |
|
||||||
|
| P0 | 训练任务失败诊断 | 训练日志详情 | 识别常见错误:数据字段缺失、模型路径不存在、显存不足、依赖缺失,并生成中文诊断 |
|
||||||
|
| P1 | checkpoint 管理 | 训练日志详情、模型管理 | 展示 checkpoint 列表、大小、路径、保留策略,支持标记最佳 checkpoint |
|
||||||
|
| P1 | 模型产物 manifest | 模型管理 | 为每个训练/导出产物生成 manifest,记录文件清单、大小、checksum、来源任务 |
|
||||||
|
| P1 | 合并/导出任务详情页 | 模型管理、训练日志 | 展示合并任务日志、状态、产物、失败原因 |
|
||||||
|
| P2 | 模型版本治理 | 模型管理 | 支持版本号、标签、发布状态、归档状态、审批状态 |
|
||||||
|
| P2 | 训练模板管理 | 系统设置或训练配置 | 将 LLaMA-Factory 模板、数据格式、默认超参做成可维护配置 |
|
||||||
|
|
||||||
|
### 5.2 D. 算力平台、部署与运维
|
||||||
|
|
||||||
|
| 优先级 | 开发任务 | 目标页面/模块 | 交付结果 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| P0 | repair 异步化 | 算力节点 -> 资源副本 | repair 创建 sync job 后后台执行,页面展示进度和失败原因 |
|
||||||
|
| P0 | 多算力节点调度策略 | 算力节点、创建模型微调 | 支持按标签、权重、空闲 GPU、显存要求选择节点 |
|
||||||
|
| P1 | 节点资源水位告警 | 算力节点、硬件监控 | 展示磁盘、GPU、显存、训练日志目录水位和告警状态 |
|
||||||
|
| P1 | 节点维护窗口 | 算力节点 | 支持维护中节点不再调度新任务,已有任务可继续或迁移 |
|
||||||
|
| P1 | 文件副本治理 | 算力节点 -> 资源副本 | 支持副本清理、重建、过期策略和跨节点一致性检查 |
|
||||||
|
| P2 | 部署健康巡检脚本 | 部署运维 | 一键检查 app、backend、redis、pg、compute、GPU、挂载目录、端口连通 |
|
||||||
|
| P2 | Compute Agent 插件标准 | 算力平台 | 抽象 LLaMA-Factory 接入规范,预留其他训练框架 |
|
||||||
|
|
||||||
|
### 5.3 模型评测页面开发计划
|
||||||
|
|
||||||
|
| 优先级 | 开发任务 | 目标页面/模块 | 交付结果 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| P0 | 评测任务表结构和接口补齐 | 模型评测列表、创建评测、评测详情 | 建立 `eval_tasks`、`eval_dimensions`、`eval_sample_results` 等运行表和接口 |
|
||||||
|
| P0 | 创建评测任务真实提交 | 创建评测 | 支持选择模型、数据集、评测维度、GPU/节点,提交后生成评测任务 |
|
||||||
|
| P0 | 评测任务运行闭环 | 评测详情 | 支持状态、进度、日志、样本级结果回传 |
|
||||||
|
| P1 | 评测维度管理 | 评测维度创建/编辑 | 支持规则、Prompt、评分器、权重、适用数据集配置 |
|
||||||
|
| P1 | 样本级评分展示 | 评测详情 | 展示每条样本的输入、模型输出、评分、原因、人工复核状态 |
|
||||||
|
| P1 | 综合报告生成 | 评测详情、排行榜 | 生成维度汇总、综合分、问题样本、改进建议 |
|
||||||
|
| P2 | 评测审批和审计 | 审批流、审计日志 | 评测任务创建、发布报告、删除报告纳入治理 |
|
||||||
|
|
||||||
|
### 5.4 模型推理页面开发计划
|
||||||
|
|
||||||
|
| 优先级 | 开发任务 | 目标页面/模块 | 交付结果 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| P0 | 推理任务表结构和接口补齐 | 模型推理列表、新建推理 | 建立 `inference_tasks`、`inference_task_models`、`chat_sessions`、`chat_messages` |
|
||||||
|
| P0 | 模型加载/卸载真实闭环 | 模型推理列表 | 支持选择训练产物加载推理服务,展示加载状态、端口、进程和错误 |
|
||||||
|
| P0 | 单模型对话持久化 | 模型对话 | 保存会话、消息、参数、响应耗时、token 统计 |
|
||||||
|
| P1 | 多模型对比任务 | 模型对比 | 支持多模型同时加载、同一问题并发请求、结果对比展示 |
|
||||||
|
| P1 | 推理资源管控 | 模型推理、算力节点 | 支持 GPU 选择、并发限制、空闲自动卸载、异常进程清理 |
|
||||||
|
| P1 | 推理日志和调用审计 | 日志、审计中心 | 记录加载、卸载、对话请求、失败原因、用户和租户信息 |
|
||||||
|
| P2 | 推理服务发布 | 模型管理、模型推理 | 支持将某个训练模型发布为内部推理服务,并配置访问权限 |
|
||||||
|
|
||||||
|
## 6. 建议的下一阶段顺序
|
||||||
|
|
||||||
|
1. 先完成 B+D 的训练稳定性增强:数据格式预检、训练参数预览、失败诊断、repair 异步化。
|
||||||
|
2. 再补齐模型评测的真实任务闭环:任务表、创建任务、运行状态、样本结果。
|
||||||
|
3. 然后补齐模型推理闭环:加载/卸载、对话持久化、多模型对比。
|
||||||
|
4. 最后统一治理能力:审批、审计、租户隔离、资源配额、保留策略和运维巡检。
|
||||||
|
|
||||||
@@ -1095,8 +1095,21 @@ GPU 响应字段:
|
|||||||
"engine": "llama_factory",
|
"engine": "llama_factory",
|
||||||
"base_model": "/data/yg-ft/models/Qwen2.5-7B",
|
"base_model": "/data/yg-ft/models/Qwen2.5-7B",
|
||||||
"model_name_or_path": "/data/yg-ft/models/Qwen2.5-7B",
|
"model_name_or_path": "/data/yg-ft/models/Qwen2.5-7B",
|
||||||
"dataset": "finance_train",
|
"train_dataset_id": "ds_finance_train",
|
||||||
"dataset_dir": "/data/yg-ft/datasets",
|
"dataset": "ygft_ds_finance_train",
|
||||||
|
"dataset_key": "ygft_ds_finance_train",
|
||||||
|
"dataset_dir": "/data/yg-ft/datasets/ds_finance_train",
|
||||||
|
"dataset_info": {
|
||||||
|
"ygft_ds_finance_train": {
|
||||||
|
"file_name": "train.jsonl",
|
||||||
|
"formatting": "alpaca",
|
||||||
|
"columns": {
|
||||||
|
"prompt": "instruction",
|
||||||
|
"query": "input",
|
||||||
|
"response": "output"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"output_dir": "/data/yg-ft/outputs/finance-sft-001",
|
"output_dir": "/data/yg-ft/outputs/finance-sft-001",
|
||||||
"template": "qwen",
|
"template": "qwen",
|
||||||
"train_method": "lora",
|
"train_method": "lora",
|
||||||
@@ -1108,6 +1121,16 @@ GPU 响应字段:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
数据集启动规则:
|
||||||
|
- 页面选择的是平台数据集 ID,后端提交 Compute Job 时会将其转换为 LLaMA-Factory 数据集 key。
|
||||||
|
- 单文件数据集使用 `--dataset ygft_{dataset_id}`;多文件数据集使用 `--dataset ygft_{dataset_id}_1,ygft_{dataset_id}_2`。
|
||||||
|
- `dataset_dir` 指向目标算力节点上的独立数据集目录 `/data/yg-ft/datasets/{dataset_id}`。
|
||||||
|
- Compute API 在 preflight 和启动训练前根据 `dataset_info` 生成 `{dataset_dir}/dataset_info.json`,避免 LLaMA-Factory 读取全局 `/data/yg-ft/datasets/dataset_info.json` 失败。
|
||||||
|
- `columns` 只声明训练文件实际存在的字段;`system`、`history` 等可选字段不能默认写入,否则样本缺少字段时 LLaMA-Factory 会在格式转换阶段报 `KeyError`。
|
||||||
|
- 正式启动前,应用侧会把当前数据集文件内容同步到被调度的算力节点,确保在线编辑/版本切换后的训练文件被使用。
|
||||||
|
- Preflight 会校验 `dataset_info.columns` 对应字段是否能在样本文件中找到,并校验 PyTorch CUDA 可用性、所选 GPU 是否存在、显存是否满足 `MIN_TRAINING_GPU_MEMORY_GB`。
|
||||||
|
- Compute 健康检查返回 `torch_cuda`,用于区分 `nvidia-smi` 可见但 PyTorch CUDA 初始化失败的环境问题。
|
||||||
|
|
||||||
应用侧轮询同步响应:
|
应用侧轮询同步响应:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -1330,3 +1353,38 @@ LLaMA-Factory 引擎声明:
|
|||||||
| 存储管理 | `/storage` | `GET /modelTF/quotas/usage`、`GET /modelTF/files/{id}/download-url`、`GET /modelTF/retention-policies`、`PUT /modelTF/retention-policies/{id}` | 磁盘占用、下载、留存 |
|
| 存储管理 | `/storage` | `GET /modelTF/quotas/usage`、`GET /modelTF/files/{id}/download-url`、`GET /modelTF/retention-policies`、`PUT /modelTF/retention-policies/{id}` | 磁盘占用、下载、留存 |
|
||||||
| 审计中心 | `/audit-logs`、`/login-logs`、`/download-logs` | `GET /modelTF/audit-logs`、`GET /modelTF/login-logs`、`GET /modelTF/download-logs` | 操作、登录、下载审计 |
|
| 审计中心 | `/audit-logs`、`/login-logs`、`/download-logs` | `GET /modelTF/audit-logs`、`GET /modelTF/login-logs`、`GET /modelTF/download-logs` | 操作、登录、下载审计 |
|
||||||
| 训练引擎管理 | `/training-engines` | `GET /modelTF/training-engines`、`GET /modelTF/training-engines/{id}`、`GET /modelTF/training-engines/{id}/schema`、`POST /modelTF/training-engines/{id}/health-check` | 引擎能力和健康 |
|
| 训练引擎管理 | `/training-engines` | `GET /modelTF/training-engines`、`GET /modelTF/training-engines/{id}`、`GET /modelTF/training-engines/{id}/schema`、`POST /modelTF/training-engines/{id}/health-check` | 引擎能力和健康 |
|
||||||
|
## P1/P2/P3 runtime implementation note
|
||||||
|
|
||||||
|
The current backend/compute implementation has connected the B+D training runtime features below:
|
||||||
|
|
||||||
|
| Page module | API | Runtime behavior |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Training detail / logs `/training-log/:id` | `GET /modelTF/fine-tune/{id}/overview` | Returns task progress, parsed training metrics and real checkpoint records. |
|
||||||
|
| Training detail / loss chart `/training-log/:id` | `GET /modelTF/fine-tune/{id}/metrics` | Reads `fine_tune_metrics`, populated from Compute log polling and log proxy access. |
|
||||||
|
| Training detail / checkpoint list `/training-log/:id` | `GET /modelTF/fine-tune/{id}/checkpoints` | Reads `fine_tune_checkpoints`, populated from Compute scanning `output_dir/checkpoint-*`. |
|
||||||
|
| Merge weights `/model-manage/merge` | `POST /modelTF/model-manage/merge` | Submits a real Compute job using `llamafactory-cli export`; records the job in `compute_jobs`; updates `trained_models.merging/merged/merged_path` when queried after completion. |
|
||||||
|
| Compute ops / job detail | `GET /modelTF/compute/jobs/{job_id}` | Supports both fine-tune jobs and model merge/export jobs recorded in `compute_jobs`. |
|
||||||
|
| Compute ops / job logs | `GET /modelTF/compute/jobs/{job_id}/logs` | Proxies logs from the assigned Compute node for training and merge/export jobs. |
|
||||||
|
|
||||||
|
Operational diagnostic script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
APP_BASE_URL=http://localhost:17861 \
|
||||||
|
COMPUTE_BASE_URL=http://localhost:19100 \
|
||||||
|
COMPUTE_SERVICE_TOKEN=change_me \
|
||||||
|
DATABASE_URL=postgresql+psycopg://user:password@host:5432/yg_ft \
|
||||||
|
python scripts/ops_diagnostics.py
|
||||||
|
```
|
||||||
|
## P2/P3 runtime extension note
|
||||||
|
|
||||||
|
This iteration extends the B+D runtime implementation with production-facing model asset governance and compute operations:
|
||||||
|
|
||||||
|
| Page module | API | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Trained model detail / artifacts | `GET /modelTF/model-manage/trained-models/{id}/artifacts` | Returns registered adapter, merged model and quantized/export artifacts from `model_artifacts`. |
|
||||||
|
| Trained model detail / lineage | `GET /modelTF/model-manage/trained-models/{id}/lineage` | Returns upstream/downstream relations from `model_lineage`, including base model to fine-tuned model and merge/export relations. |
|
||||||
|
| Merge/export task list | `GET /modelTF/model-manage/export-jobs?trained_model_id=xxx` | Returns model export and merge jobs from `model_export_jobs`. |
|
||||||
|
| Compute node replicas | `GET /modelTF/compute/nodes/{id}/replicas/drift` | Checks whether model/dataset/output replicas still exist on the compute node local disk and updates replica status. |
|
||||||
|
| Compute node replicas | `POST /modelTF/compute/nodes/{id}/replicas/repair` | Marks drifted replicas as `repair_pending` and creates a resource sync job for the operator/scheduler to process. |
|
||||||
|
|
||||||
|
The scheduler now uses the `scheduler_locks` table while starting training tasks. Node selection, task state update, resource sync job creation and GPU pre-allocation are written in one database transaction to reduce multi-worker GPU contention.
|
||||||
|
|||||||
@@ -701,7 +701,9 @@ YG_FT/
|
|||||||
职责:
|
职责:
|
||||||
|
|
||||||
- 生成训练 YAML 或 CLI。
|
- 生成训练 YAML 或 CLI。
|
||||||
- 生成 dataset_info。
|
- 根据平台数据集 ID 生成 LLaMA-Factory dataset key 和 `{dataset_dir}/dataset_info.json`。
|
||||||
|
- 单文件数据集使用一个 dataset key,多文件数据集拆分为多个 key 并通过逗号组合传入 `--dataset`。
|
||||||
|
- 正式启动前由应用侧同步当前数据集文件到目标算力节点,确保训练使用最新激活版本。
|
||||||
- 设置 `CUDA_VISIBLE_DEVICES`。
|
- 设置 `CUDA_VISIBLE_DEVICES`。
|
||||||
- 启动训练。
|
- 启动训练。
|
||||||
- 解析日志。
|
- 解析日志。
|
||||||
@@ -1116,3 +1118,10 @@ YG_FT/
|
|||||||
| P1 | 数据处理、评测、推理、模型服务治理 | 形成训练前后闭环 |
|
| P1 | 数据处理、评测、推理、模型服务治理 | 形成训练前后闭环 |
|
||||||
| P1 | 存储管理、checkpoint、审计中心 | 企业治理和运维收口 |
|
| P1 | 存储管理、checkpoint、审计中心 | 企业治理和运维收口 |
|
||||||
| P2 | 训练引擎管理、外部 API、OIDC/LDAP、成本核算、人工评测 | 扩展能力,第一版可预留 |
|
| P2 | 训练引擎管理、外部 API、OIDC/LDAP、成本核算、人工评测 | 扩展能力,第一版可预留 |
|
||||||
|
## P1/P2/P3 Development Status
|
||||||
|
|
||||||
|
| Priority | Scope | Implemented content | Remaining production hardening |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| P1 | Training runtime assets | Compute scans real `checkpoint-*` directories; backend persists checkpoints to `fine_tune_checkpoints`; backend parses log metrics into `fine_tune_metrics`; completed training auto-registers a trained model with its real output directory. | Add full TensorBoard event parsing and retention/cleanup execution policies. |
|
||||||
|
| P2 | Model merge/export | `/modelTF/model-manage/merge` submits `llamafactory-cli export` through Compute; merge/export jobs are recorded in `compute_jobs` and `model_export_jobs`; trained model artifacts are recorded in `model_artifacts`; base->trained and merge/export relations are recorded in `model_lineage`; APIs expose artifacts, lineage and export jobs. | Add frontend polling/status panels for export jobs and richer artifact checksum generation during long-running export. |
|
||||||
|
| P3 | Compute ops | `compute_jobs` and `gpu_allocations` are updated from runtime polling; training start uses `scheduler_locks` to serialize node selection and GPU pre-allocation; terminal task states release GPU allocations; resource replica drift check and repair-pending APIs are available; `scripts/ops_diagnostics.py` checks backend, PostgreSQL, Compute health, GPU discovery and Compute jobs. | Add dedicated scheduler worker, automatic replica repair execution from authoritative source metadata, and lock timeout alarms. |
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
import{d as C,bf as g,D as E,H as O,o as _,e as B,s as D,Z as $,w as A,c as I,aL as K,q as a,n as v,aa as r,g as q,bg as M,y as u,z as N,P as k}from"./index-Ds9AETjS.js";import{_ as R}from"./_plugin-vue_export-helper-DlAUqK2U.js";const V={class:"app-confirm-header"},z={class:"app-confirm-heading"},H={class:"app-confirm-icon","aria-hidden":"true"},L={class:"app-confirm-body"},P={class:"app-confirm-actions"},S=C({__name:"AppConfirmDialog",setup(j,{expose:x}){const c=u(!1),m=u(),p=u(),d=`app-confirm-title-${g()}`,y=`app-confirm-message-${g()}`,n=N({title:"请确认操作",message:"",confirmText:"确定",cancelText:"取消",tone:"warning",closeOnOverlay:!1});let o=null,i=null;function s(t){c.value=!1;const e=o;o=null,e==null||e(t)}function h(t){return o&&s(!1),Object.assign(n,{confirmText:"确定",cancelText:"取消",tone:"warning",closeOnOverlay:!1,...t}),c.value=!0,new Promise(e=>{o=e})}function w(){n.closeOnOverlay&&s(!1)}function T(t){var b;if(t.key==="Escape"){t.preventDefault(),s(!1);return}if(t.key!=="Tab")return;const e=Array.from(((b=m.value)==null?void 0:b.querySelectorAll("button:not([disabled])"))??[]),l=e[0],f=e[e.length-1];!l||!f||(t.shiftKey&&document.activeElement===l?(t.preventDefault(),f.focus()):!t.shiftKey&&document.activeElement===f&&(t.preventDefault(),l.focus()))}return E(c,async t=>{var e;if(t){i=document.activeElement instanceof HTMLElement?document.activeElement:null,await k(),(e=p.value)==null||e.focus();return}await k(),i==null||i.focus(),i=null}),O(()=>{o==null||o(!1),o=null}),x({open:h}),(t,e)=>(_(),B(M,{to:"body"},[D($,{name:"app-confirm"},{default:A(()=>[c.value?(_(),I("div",{key:0,class:"app-confirm-overlay",onMousedown:K(w,["self"])},[a("section",{ref_key:"dialogRef",ref:m,class:v(["app-confirm-dialog",`is-${n.tone}`]),role:"alertdialog","aria-modal":!0,"aria-labelledby":d,"aria-describedby":y,onKeydown:T},[a("header",V,[a("div",z,[a("span",H,[a("i",{class:v(n.tone==="primary"?"fa fa-question-circle":"fa fa-exclamation-triangle")},null,2)]),a("h2",{id:d},r(n.title),1)]),a("button",{class:"app-confirm-close",type:"button","aria-label":"关闭确认弹窗",onClick:e[0]||(e[0]=l=>s(!1))},[...e[3]||(e[3]=[a("i",{class:"fa fa-times","aria-hidden":"true"},null,-1)])])]),a("div",L,[a("p",{id:y},r(n.message),1)]),a("footer",P,[a("button",{ref_key:"cancelButtonRef",ref:p,class:"app-confirm-button is-cancel",type:"button",onClick:e[1]||(e[1]=l=>s(!1))},r(n.cancelText),513),a("button",{class:"app-confirm-button is-confirm",type:"button",onClick:e[2]||(e[2]=l=>s(!0))},r(n.confirmText),1)])],34)],32)):q("",!0)]),_:1})]))}}),G=R(S,[["__scopeId","data-v-398df98e"]]);export{G as A};
|
import{d as C,bf as g,D as E,H as O,o as _,e as B,s as D,Z as $,w as A,c as I,aL as K,q as a,n as v,aa as r,g as q,bg as M,y as u,z as N,P as k}from"./index-D5veDkuG.js";import{_ as R}from"./_plugin-vue_export-helper-DlAUqK2U.js";const V={class:"app-confirm-header"},z={class:"app-confirm-heading"},H={class:"app-confirm-icon","aria-hidden":"true"},L={class:"app-confirm-body"},P={class:"app-confirm-actions"},S=C({__name:"AppConfirmDialog",setup(j,{expose:x}){const c=u(!1),m=u(),p=u(),d=`app-confirm-title-${g()}`,y=`app-confirm-message-${g()}`,n=N({title:"请确认操作",message:"",confirmText:"确定",cancelText:"取消",tone:"warning",closeOnOverlay:!1});let o=null,i=null;function s(t){c.value=!1;const e=o;o=null,e==null||e(t)}function h(t){return o&&s(!1),Object.assign(n,{confirmText:"确定",cancelText:"取消",tone:"warning",closeOnOverlay:!1,...t}),c.value=!0,new Promise(e=>{o=e})}function w(){n.closeOnOverlay&&s(!1)}function T(t){var b;if(t.key==="Escape"){t.preventDefault(),s(!1);return}if(t.key!=="Tab")return;const e=Array.from(((b=m.value)==null?void 0:b.querySelectorAll("button:not([disabled])"))??[]),l=e[0],f=e[e.length-1];!l||!f||(t.shiftKey&&document.activeElement===l?(t.preventDefault(),f.focus()):!t.shiftKey&&document.activeElement===f&&(t.preventDefault(),l.focus()))}return E(c,async t=>{var e;if(t){i=document.activeElement instanceof HTMLElement?document.activeElement:null,await k(),(e=p.value)==null||e.focus();return}await k(),i==null||i.focus(),i=null}),O(()=>{o==null||o(!1),o=null}),x({open:h}),(t,e)=>(_(),B(M,{to:"body"},[D($,{name:"app-confirm"},{default:A(()=>[c.value?(_(),I("div",{key:0,class:"app-confirm-overlay",onMousedown:K(w,["self"])},[a("section",{ref_key:"dialogRef",ref:m,class:v(["app-confirm-dialog",`is-${n.tone}`]),role:"alertdialog","aria-modal":!0,"aria-labelledby":d,"aria-describedby":y,onKeydown:T},[a("header",V,[a("div",z,[a("span",H,[a("i",{class:v(n.tone==="primary"?"fa fa-question-circle":"fa fa-exclamation-triangle")},null,2)]),a("h2",{id:d},r(n.title),1)]),a("button",{class:"app-confirm-close",type:"button","aria-label":"关闭确认弹窗",onClick:e[0]||(e[0]=l=>s(!1))},[...e[3]||(e[3]=[a("i",{class:"fa fa-times","aria-hidden":"true"},null,-1)])])]),a("div",L,[a("p",{id:y},r(n.message),1)]),a("footer",P,[a("button",{ref_key:"cancelButtonRef",ref:p,class:"app-confirm-button is-cancel",type:"button",onClick:e[1]||(e[1]=l=>s(!1))},r(n.cancelText),513),a("button",{class:"app-confirm-button is-confirm",type:"button",onClick:e[2]||(e[2]=l=>s(!0))},r(n.confirmText),1)])],34)],32)):q("",!0)]),_:1})]))}}),G=R(S,[["__scopeId","data-v-398df98e"]]);export{G as A};
|
||||||
@@ -1 +0,0 @@
|
|||||||
import{a as N,E as B}from"./el-form-item-Bq0n_p5a.js";import{E as K}from"./index-h5FkzAP_.js";import{E as M}from"./index-BNsCyG4A.js";import{E as h}from"./index-ByhS8A1d.js";import{E as I}from"./el-divider-a924dciw.js";import{E as R}from"./el-slider-mZJHOhL2.js";import{d as F,G as z,e as V,w as l,ac as D,y as L,o as f,s as a,x as p,q as $,c as k,ad as j,aa as E,M as A,g as G,f as J,v as O,z as H,j as _,A as P}from"./index-Ds9AETjS.js";import"./el-popper-D1tByNLb.js";import"./el-tooltip-l0sNRNKZ.js";import"./el-input-number-Bg2C0S8w.js";/* empty css */import{P as Q}from"./PageCard-CV3p14-x.js";import{u as W}from"./usePolling-Bx2QCw4O.js";import{a as X}from"./compare-fmy9bbtE.js";import{_ as Y}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./castArray-BWI4UxBy.js";import"./_baseClone-CZxxnGCv.js";import"./raf-zQ00nuMI.js";import"./index-B0if-AHo.js";import"./index-BSbCLbaz.js";import"./debounce-D6mJVDYC.js";import"./toNumber-DrayciwT.js";import"./clamp-CyhADbdq.js";import"./index-sWKLi7bH.js";import"./index-2LOSO4Pw.js";import"./el-card-D86TXgMv.js";const Z={class:"model-list"},tt={key:0,class:"empty-hint"},et=F({__name:"CompareChatView",setup(ot){const b=D(),y=O(),x=b.params.id,n=L(null),e=H({systemPrompt:"",question:"",temperature:.7,topP:.9,topK:40,maxTokens:2048}),m=_(()=>{var s;if(!((s=n.value)!=null&&s.load_status))return[];try{return(typeof n.value.load_status=="string"?JSON.parse(n.value.load_status):n.value.load_status).loaded_models||[]}catch{return[]}}),T=_(()=>m.value.length>0&&m.value.every(s=>s.status==="ready"||s.status==="running")),g=_(()=>m.value.some(s=>s.status==="starting"));async function c(){try{n.value=await X(x)}catch{}}function C(){var u,i;if(!e.question.trim()){P.warning("请输入问题");return}if(g.value){P.warning("模型仍在启动中,请稍候");return}const s=new URLSearchParams({taskId:x,taskName:((u=n.value)==null?void 0:u.model_name)||((i=n.value)==null?void 0:i.name)||"",question:e.question,systemPrompt:e.systemPrompt,temperature:String(e.temperature),topP:String(e.topP),topK:String(e.topK),maxTokens:String(e.maxTokens)}),t=y.resolve(`/model-compare/result?${s.toString()}`).href;window.open(t,"_blank")}const{start:S}=W(c,5e3,{immediate:!1});return z(async()=>{await c(),S()}),(s,t)=>{const u=I,i=h,v=K,r=N,d=R,w=M,q=B;return f(),V(Q,{title:"模型对比配置"},{default:l(()=>[a(u,{"content-position":"left"},{default:l(()=>[...t[7]||(t[7]=[p("已启动模型",-1)])]),_:1}),$("div",Z,[(f(!0),k(A,null,j(m.value,(o,U)=>(f(),V(i,{key:U,type:o.status==="ready"||o.status==="running"?"success":o.status==="starting"?"warning":"danger",size:"large"},{default:l(()=>[p(E(o.model_name)+" ("+E(o.status)+") ",1)]),_:2},1032,["type"]))),128)),m.value.length?G("",!0):(f(),k("span",tt,"暂无已启动模型"))]),a(u,{"content-position":"left"},{default:l(()=>[...t[8]||(t[8]=[p("对话配置",-1)])]),_:1}),a(q,{"label-width":"120px",style:{"max-width":"700px"}},{default:l(()=>[a(r,{label:"系统提示词"},{default:l(()=>[a(v,{modelValue:e.systemPrompt,"onUpdate:modelValue":t[0]||(t[0]=o=>e.systemPrompt=o),type:"textarea",rows:3,placeholder:"可选"},null,8,["modelValue"])]),_:1}),a(r,{label:"问题"},{default:l(()=>[a(v,{modelValue:e.question,"onUpdate:modelValue":t[1]||(t[1]=o=>e.question=o),type:"textarea",rows:4,placeholder:"请输入要对比的问题"},null,8,["modelValue"])]),_:1}),a(r,{label:"Temperature"},{default:l(()=>[a(d,{modelValue:e.temperature,"onUpdate:modelValue":t[2]||(t[2]=o=>e.temperature=o),min:0,max:2,step:.1,"show-input":"",style:{"max-width":"500px"}},null,8,["modelValue"])]),_:1}),a(r,{label:"Top-p"},{default:l(()=>[a(d,{modelValue:e.topP,"onUpdate:modelValue":t[3]||(t[3]=o=>e.topP=o),min:0,max:1,step:.05,"show-input":"",style:{"max-width":"500px"}},null,8,["modelValue"])]),_:1}),a(r,{label:"Top-k"},{default:l(()=>[a(d,{modelValue:e.topK,"onUpdate:modelValue":t[4]||(t[4]=o=>e.topK=o),min:1,max:100,step:1,"show-input":"",style:{"max-width":"500px"}},null,8,["modelValue"])]),_:1}),a(r,{label:"Max Tokens"},{default:l(()=>[a(d,{modelValue:e.maxTokens,"onUpdate:modelValue":t[5]||(t[5]=o=>e.maxTokens=o),min:256,max:4096,step:128,"show-input":"",style:{"max-width":"500px"}},null,8,["modelValue"])]),_:1}),a(r,null,{default:l(()=>[a(w,{type:"primary",disabled:!T.value||g.value,onClick:C},{default:l(()=>[...t[9]||(t[9]=[p(" 开始对比 ",-1)])]),_:1},8,["disabled"]),a(w,{onClick:t[6]||(t[6]=o=>J(y).back())},{default:l(()=>[...t[10]||(t[10]=[p("返回",-1)])]),_:1})]),_:1})]),_:1})]),_:1})}}}),qt=Y(et,[["__scopeId","data-v-5da55d96"]]);export{qt as default};
|
|
||||||
1
frontend/dist/assets/CompareChatView-CyMT5mfJ.js
vendored
Normal file
1
frontend/dist/assets/CompareChatView-CyMT5mfJ.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{a as B,E as K}from"./el-form-item-rya6onmd.js";import{E as M}from"./index-DbBHj60R.js";import{E as h}from"./index-DxH5-_w6.js";import{E as I}from"./el-tag-C2d1Zfbn.js";import{E as R}from"./el-divider-CsS5KGIs.js";import{E as F}from"./el-slider-pAA0AhdE.js";import{d as L,G as z,e as V,w as s,ac as D,y as $,o as f,s as a,x as p,q as j,c as k,ad as A,aa as E,f as P,M as G,g as J,v as O,z as H,j as _,A as b}from"./index-D5veDkuG.js";import"./el-popper-DDJTxf27.js";import"./el-tooltip-l0sNRNKZ.js";import"./el-input-number-Cs1MXSA_.js";import{P as Q}from"./PageCard-DFGMy1ik.js";import{u as W}from"./usePolling-BV3KjihG.js";import{a as X}from"./compare-DQvbhHeE.js";import{a as Y}from"./status-Dl1fykxa.js";import{_ as Z}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./castArray-BDIbf8ih.js";import"./_baseClone-BuqHm105.js";import"./raf-Dsox5IbR.js";import"./index-DDY6aSpq.js";import"./index-BT1A2I5c.js";import"./debounce-D3zKL4rk.js";import"./toNumber-a8w_gs0u.js";import"./clamp-Ddg8_Yc2.js";import"./index-DmAaj_YO.js";import"./index-APLq0PKf.js";import"./el-card-DzhpMhms.js";const tt={class:"model-list"},et={key:0,class:"empty-hint"},ot=L({__name:"CompareChatView",setup(at){const T=D(),y=O(),x=T.params.id,n=$(null),e=H({systemPrompt:"",question:"",temperature:.7,topP:.9,topK:40,maxTokens:2048}),m=_(()=>{var l;if(!((l=n.value)!=null&&l.load_status))return[];try{return(typeof n.value.load_status=="string"?JSON.parse(n.value.load_status):n.value.load_status).loaded_models||[]}catch{return[]}}),C=_(()=>m.value.length>0&&m.value.every(l=>l.status==="ready"||l.status==="running")),g=_(()=>m.value.some(l=>l.status==="starting"));async function c(){try{n.value=await X(x)}catch{}}function S(){var u,i;if(!e.question.trim()){b.warning("请输入问题");return}if(g.value){b.warning("模型仍在启动中,请稍候");return}const l=new URLSearchParams({taskId:x,taskName:((u=n.value)==null?void 0:u.model_name)||((i=n.value)==null?void 0:i.name)||"",question:e.question,systemPrompt:e.systemPrompt,temperature:String(e.temperature),topP:String(e.topP),topK:String(e.topK),maxTokens:String(e.maxTokens)}),t=y.resolve(`/model-compare/result?${l.toString()}`).href;window.open(t,"_blank")}const{start:q}=W(c,5e3,{immediate:!1});return z(async()=>{await c(),q()}),(l,t)=>{const u=R,i=I,v=M,r=B,d=F,w=h,U=K;return f(),V(Q,{title:"模型对比配置"},{default:s(()=>[a(u,{"content-position":"left"},{default:s(()=>[...t[7]||(t[7]=[p("已启动模型",-1)])]),_:1}),j("div",tt,[(f(!0),k(G,null,A(m.value,(o,N)=>(f(),V(i,{key:N,type:o.status==="ready"||o.status==="running"?"success":o.status==="starting"?"warning":"danger",size:"large"},{default:s(()=>[p(E(o.model_name)+" ("+E(P(Y)(o.status))+") ",1)]),_:2},1032,["type"]))),128)),m.value.length?J("",!0):(f(),k("span",et,"暂无已启动模型"))]),a(u,{"content-position":"left"},{default:s(()=>[...t[8]||(t[8]=[p("对话配置",-1)])]),_:1}),a(U,{"label-width":"120px",style:{"max-width":"700px"}},{default:s(()=>[a(r,{label:"系统提示词"},{default:s(()=>[a(v,{modelValue:e.systemPrompt,"onUpdate:modelValue":t[0]||(t[0]=o=>e.systemPrompt=o),type:"textarea",rows:3,placeholder:"可选"},null,8,["modelValue"])]),_:1}),a(r,{label:"问题"},{default:s(()=>[a(v,{modelValue:e.question,"onUpdate:modelValue":t[1]||(t[1]=o=>e.question=o),type:"textarea",rows:4,placeholder:"请输入要对比的问题"},null,8,["modelValue"])]),_:1}),a(r,{label:"Temperature"},{default:s(()=>[a(d,{modelValue:e.temperature,"onUpdate:modelValue":t[2]||(t[2]=o=>e.temperature=o),min:0,max:2,step:.1,"show-input":"",style:{"max-width":"500px"}},null,8,["modelValue"])]),_:1}),a(r,{label:"Top-p"},{default:s(()=>[a(d,{modelValue:e.topP,"onUpdate:modelValue":t[3]||(t[3]=o=>e.topP=o),min:0,max:1,step:.05,"show-input":"",style:{"max-width":"500px"}},null,8,["modelValue"])]),_:1}),a(r,{label:"Top-k"},{default:s(()=>[a(d,{modelValue:e.topK,"onUpdate:modelValue":t[4]||(t[4]=o=>e.topK=o),min:1,max:100,step:1,"show-input":"",style:{"max-width":"500px"}},null,8,["modelValue"])]),_:1}),a(r,{label:"Max Tokens"},{default:s(()=>[a(d,{modelValue:e.maxTokens,"onUpdate:modelValue":t[5]||(t[5]=o=>e.maxTokens=o),min:256,max:4096,step:128,"show-input":"",style:{"max-width":"500px"}},null,8,["modelValue"])]),_:1}),a(r,null,{default:s(()=>[a(w,{type:"primary",disabled:!C.value||g.value,onClick:S},{default:s(()=>[...t[9]||(t[9]=[p(" 开始对比 ",-1)])]),_:1},8,["disabled"]),a(w,{onClick:t[6]||(t[6]=o=>P(y).back())},{default:s(()=>[...t[10]||(t[10]=[p("返回",-1)])]),_:1})]),_:1})]),_:1})]),_:1})}}}),Ut=Z(ot,[["__scopeId","data-v-9d25ca9d"]]);export{Ut as default};
|
||||||
@@ -1 +0,0 @@
|
|||||||
.model-list[data-v-5da55d96]{display:flex;flex-wrap:wrap;gap:12px;margin-bottom:12px}.empty-hint[data-v-5da55d96]{color:#909399}
|
|
||||||
1
frontend/dist/assets/CompareChatView-yGYtaxtI.css
vendored
Normal file
1
frontend/dist/assets/CompareChatView-yGYtaxtI.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.model-list[data-v-9d25ca9d]{display:flex;flex-wrap:wrap;gap:12px;margin-bottom:12px}.empty-hint[data-v-9d25ca9d]{color:#909399}
|
||||||
1
frontend/dist/assets/CompareResultView-CuqaCrX1.js
vendored
Normal file
1
frontend/dist/assets/CompareResultView-CuqaCrX1.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{E as D}from"./el-alert-DhjRMRnp.js";import{E as $}from"./index-DxH5-_w6.js";import{E as z}from"./el-tag-C2d1Zfbn.js";import{E as F}from"./el-card-DzhpMhms.js";import{d as O,G as U,H as J,c as d,q as i,aa as c,f as v,s as N,w as m,M as K,ad as A,ac as G,y as g,o as r,x as p,e as y,g as H}from"./index-D5veDkuG.js";import{_ as L}from"./MarkdownView.vue_vue_type_style_index_0_lang-DFYCH-49.js";import{a as W,c as j}from"./compare-DQvbhHeE.js";import{_ as Q}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./vnode-CYZHqsIy.js";const X={class:"compare-result"},Y={class:"result-header"},Z={class:"header-actions"},tt={class:"result-grid"},et={class:"card-header"},st={class:"model-name"},ot={key:0,class:"error-text"},at={key:1,class:"streaming-text"},nt={key:3,class:"loading-text"},lt={key:4,class:"result-stats"},rt=O({__name:"CompareResultView",setup(it){const u=G(),q=u.query.taskId,k=decodeURIComponent(u.query.question||""),h=decodeURIComponent(u.query.systemPrompt||""),E=Number(u.query.temperature||.7),P=Number(u.query.topP||.9),I=Number(u.query.topK||40),b=Number(u.query.maxTokens||2048),w=u.query.taskName,l=g([]),C=g(!1),x=g([]),f=new Set;async function B(){if(!C.value){C.value=!0;try{const s=await W(q);let t=[];s.load_status&&(t=(typeof s.load_status=="string"?JSON.parse(s.load_status):s.load_status).loaded_models||[]),x.value=t,l.value=t.map(o=>({name:o.model_name||"模型",content:"",displayContent:"",isTyping:!1,status:"loading"})),await Promise.all(t.map((o,e)=>S(o,e)))}catch{}}}async function S(s,t){const o=Date.now();try{const e=await V(j({port:s.port,model_name:s.model_name,messages:[...h?[{role:"system",content:h}]:[],{role:"user",content:k}],temperature:E,top_p:P,top_k:I,max_tokens:b}),3e5),a=(e==null?void 0:e.response)||(e==null?void 0:e.content)||(e==null?void 0:e.data)||JSON.stringify(e),_=(Date.now()-o)/1e3;l.value[t].content=a,l.value[t].status="done",l.value[t].stats={totalTime:_,charsPerSec:_>0?Number((a.length/_).toFixed(1)):0},M(t,a)}catch(e){l.value[t].content="推理失败: "+(e.message||""),l.value[t].status="error"}}async function V(s,t){let o=null;try{return await Promise.race([s,new Promise((e,a)=>{o=setTimeout(()=>a(new Error("推理超时")),t)})])}finally{o&&clearTimeout(o)}}function M(s,t){let o=0;l.value[s].isTyping=!0;const e=Math.max(2,Math.ceil(t.length/30)),a=setInterval(()=>{o+=e,l.value[s].displayContent=t.slice(0,o),o>=t.length&&(clearInterval(a),f.delete(a),l.value[s].displayContent=t,l.value[s].isTyping=!1)},50);f.add(a)}return U(B),J(()=>{f.forEach(clearInterval),f.clear()}),(s,t)=>{const o=$,e=D,a=z,_=F;return r(),d("div",X,[i("div",Y,[i("h2",null,"对比结果"+c(v(w)?` - ${v(w)}`:""),1),i("div",Z,[N(o,{onClick:t[0]||(t[0]=n=>s.$router.push("/model-inference"))},{default:m(()=>[...t[1]||(t[1]=[p("返回列表",-1)])]),_:1})])]),N(e,{type:"info",closable:!1,"show-icon":"",class:"question-box"},{title:m(()=>[t[2]||(t[2]=i("strong",null,"问题:",-1)),p(c(v(k)),1)]),_:1}),i("div",tt,[(r(!0),d(K,null,A(l.value,(n,R)=>(r(),y(_,{key:R,shadow:"hover",class:"result-card"},{header:m(()=>[i("div",et,[i("span",st,c(n.name),1),n.status==="loading"?(r(),y(a,{key:0,type:"warning",size:"small"},{default:m(()=>[...t[3]||(t[3]=[p("生成中...",-1)])]),_:1})):n.status==="done"?(r(),y(a,{key:1,type:"success",size:"small"},{default:m(()=>[...t[4]||(t[4]=[p("完成",-1)])]),_:1})):(r(),y(a,{key:2,type:"danger",size:"small"},{default:m(()=>[...t[5]||(t[5]=[p("失败",-1)])]),_:1}))])]),default:m(()=>{var T;return[n.status==="error"?(r(),d("div",ot,c(n.content),1)):n.isTyping?(r(),d("div",at,c(n.displayContent),1)):n.displayContent?(r(),y(L,{key:2,content:n.displayContent},null,8,["content"])):(r(),d("div",nt,[...t[6]||(t[6]=[i("i",{class:"fa fa-spinner fa-spin"},null,-1),p(" 正在生成回答... ",-1)])])),n.stats?(r(),d("div",lt,[i("span",null,"耗时 "+c((T=n.stats.totalTime)==null?void 0:T.toFixed(1))+"s",1),i("span",null,"速度 "+c(n.stats.charsPerSec)+" 字/秒",1)])):H("",!0)]}),_:2},1024))),128))])])}}}),gt=Q(rt,[["__scopeId","data-v-4c4e03d7"]]);export{gt as default};
|
||||||
@@ -1 +0,0 @@
|
|||||||
import{E as D}from"./el-alert-CoSdHkeK.js";import{E as $}from"./index-BNsCyG4A.js";import{E as z}from"./index-ByhS8A1d.js";import{E as F}from"./el-card-D86TXgMv.js";import{d as O,G as U,H as J,c as d,q as i,aa as c,f as v,s as N,w as m,M as K,ad as A,ac as G,y as g,o as r,x as p,e as y,g as H}from"./index-Ds9AETjS.js";/* empty css */import{_ as L}from"./MarkdownView.vue_vue_type_style_index_0_lang-CNORZ6uG.js";import{a as W,c as j}from"./compare-fmy9bbtE.js";import{_ as Q}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./vnode-CBiWHFw7.js";const X={class:"compare-result"},Y={class:"result-header"},Z={class:"header-actions"},tt={class:"result-grid"},et={class:"card-header"},st={class:"model-name"},ot={key:0,class:"error-text"},at={key:1,class:"streaming-text"},nt={key:3,class:"loading-text"},lt={key:4,class:"result-stats"},rt=O({__name:"CompareResultView",setup(it){const u=G(),q=u.query.taskId,k=decodeURIComponent(u.query.question||""),h=decodeURIComponent(u.query.systemPrompt||""),E=Number(u.query.temperature||.7),P=Number(u.query.topP||.9),I=Number(u.query.topK||40),b=Number(u.query.maxTokens||2048),w=u.query.taskName,l=g([]),C=g(!1),x=g([]),f=new Set;async function B(){if(!C.value){C.value=!0;try{const s=await W(q);let t=[];s.load_status&&(t=(typeof s.load_status=="string"?JSON.parse(s.load_status):s.load_status).loaded_models||[]),x.value=t,l.value=t.map(o=>({name:o.model_name||"模型",content:"",displayContent:"",isTyping:!1,status:"loading"})),await Promise.all(t.map((o,e)=>S(o,e)))}catch{}}}async function S(s,t){const o=Date.now();try{const e=await V(j({port:s.port,model_name:s.model_name,messages:[...h?[{role:"system",content:h}]:[],{role:"user",content:k}],temperature:E,top_p:P,top_k:I,max_tokens:b}),3e5),a=(e==null?void 0:e.response)||(e==null?void 0:e.content)||(e==null?void 0:e.data)||JSON.stringify(e),_=(Date.now()-o)/1e3;l.value[t].content=a,l.value[t].status="done",l.value[t].stats={totalTime:_,charsPerSec:_>0?Number((a.length/_).toFixed(1)):0},M(t,a)}catch(e){l.value[t].content="推理失败: "+(e.message||""),l.value[t].status="error"}}async function V(s,t){let o=null;try{return await Promise.race([s,new Promise((e,a)=>{o=setTimeout(()=>a(new Error("推理超时")),t)})])}finally{o&&clearTimeout(o)}}function M(s,t){let o=0;l.value[s].isTyping=!0;const e=Math.max(2,Math.ceil(t.length/30)),a=setInterval(()=>{o+=e,l.value[s].displayContent=t.slice(0,o),o>=t.length&&(clearInterval(a),f.delete(a),l.value[s].displayContent=t,l.value[s].isTyping=!1)},50);f.add(a)}return U(B),J(()=>{f.forEach(clearInterval),f.clear()}),(s,t)=>{const o=$,e=D,a=z,_=F;return r(),d("div",X,[i("div",Y,[i("h2",null,"对比结果"+c(v(w)?` - ${v(w)}`:""),1),i("div",Z,[N(o,{onClick:t[0]||(t[0]=n=>s.$router.push("/model-inference"))},{default:m(()=>[...t[1]||(t[1]=[p("返回列表",-1)])]),_:1})])]),N(e,{type:"info",closable:!1,"show-icon":"",class:"question-box"},{title:m(()=>[t[2]||(t[2]=i("strong",null,"问题:",-1)),p(c(v(k)),1)]),_:1}),i("div",tt,[(r(!0),d(K,null,A(l.value,(n,R)=>(r(),y(_,{key:R,shadow:"hover",class:"result-card"},{header:m(()=>[i("div",et,[i("span",st,c(n.name),1),n.status==="loading"?(r(),y(a,{key:0,type:"warning",size:"small"},{default:m(()=>[...t[3]||(t[3]=[p("生成中...",-1)])]),_:1})):n.status==="done"?(r(),y(a,{key:1,type:"success",size:"small"},{default:m(()=>[...t[4]||(t[4]=[p("完成",-1)])]),_:1})):(r(),y(a,{key:2,type:"danger",size:"small"},{default:m(()=>[...t[5]||(t[5]=[p("失败",-1)])]),_:1}))])]),default:m(()=>{var T;return[n.status==="error"?(r(),d("div",ot,c(n.content),1)):n.isTyping?(r(),d("div",at,c(n.displayContent),1)):n.displayContent?(r(),y(L,{key:2,content:n.displayContent},null,8,["content"])):(r(),d("div",nt,[...t[6]||(t[6]=[i("i",{class:"fa fa-spinner fa-spin"},null,-1),p(" 正在生成回答... ",-1)])])),n.stats?(r(),d("div",lt,[i("span",null,"耗时 "+c((T=n.stats.totalTime)==null?void 0:T.toFixed(1))+"s",1),i("span",null,"速度 "+c(n.stats.charsPerSec)+" 字/秒",1)])):H("",!0)]}),_:2},1024))),128))])])}}}),kt=Q(rt,[["__scopeId","data-v-4c4e03d7"]]);export{kt as default};
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
.compute-page[data-v-5ebf5cb5]{display:flex;flex-direction:column;gap:16px;min-height:0;height:100%;padding:24px;background:#fff}.compute-header[data-v-5ebf5cb5]{display:flex;justify-content:space-between;gap:16px;align-items:flex-start}.compute-header h1[data-v-5ebf5cb5]{margin:0;font-size:24px;font-weight:650;color:#111827}.compute-header p[data-v-5ebf5cb5]{margin:8px 0 0;color:#64748b}.header-actions[data-v-5ebf5cb5]{display:flex;align-items:center;gap:12px}.last-updated[data-v-5ebf5cb5],.muted[data-v-5ebf5cb5]{color:#64748b;font-size:12px}.last-updated[data-v-5ebf5cb5]{display:inline-block;min-width:92px;text-align:right}.summary-grid[data-v-5ebf5cb5]{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px}.summary-tile[data-v-5ebf5cb5]{border:1px solid #e5e7eb;border-radius:8px;padding:14px 16px;background:#f8fafc}.summary-tile span[data-v-5ebf5cb5]{display:block;color:#64748b;font-size:12px}.summary-tile strong[data-v-5ebf5cb5]{display:block;margin-top:8px;color:#111827;font-size:24px}.compute-tabs[data-v-5ebf5cb5]{flex:1;min-height:0}.compute-tabs[data-v-5ebf5cb5] .el-tabs__content{height:calc(100% - 56px)}.compute-tabs[data-v-5ebf5cb5] .el-tab-pane{height:100%}.mono[data-v-5ebf5cb5]{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}.replica-toolbar[data-v-5ebf5cb5]{display:flex;gap:12px;align-items:center;margin-bottom:12px}.replica-toolbar .el-select[data-v-5ebf5cb5]{width:280px}.node-form-grid[data-v-5ebf5cb5]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:12px}.node-form-grid[data-v-5ebf5cb5] .el-input-number,.node-form-grid[data-v-5ebf5cb5] .el-select{width:100%}@media(max-width:960px){.compute-header[data-v-5ebf5cb5],.header-actions[data-v-5ebf5cb5],.replica-toolbar[data-v-5ebf5cb5]{flex-direction:column;align-items:stretch}.summary-grid[data-v-5ebf5cb5]{grid-template-columns:repeat(2,minmax(0,1fr))}.node-form-grid[data-v-5ebf5cb5]{grid-template-columns:1fr}}
|
|
||||||
1
frontend/dist/assets/ComputeNodesView-DShEW0Q9.js
vendored
Normal file
1
frontend/dist/assets/ComputeNodesView-DShEW0Q9.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/ComputeNodesView-airm9KfK.css
vendored
Normal file
1
frontend/dist/assets/ComputeNodesView-airm9KfK.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.compute-page[data-v-6f93f55a]{display:flex;flex-direction:column;gap:16px;min-height:0;height:100%;padding:24px;background:#fff}.compute-header[data-v-6f93f55a]{display:flex;justify-content:space-between;gap:16px;align-items:flex-start}.compute-header h1[data-v-6f93f55a]{margin:0;font-size:24px;font-weight:650;color:#111827}.compute-header p[data-v-6f93f55a]{margin:8px 0 0;color:#64748b}.header-actions[data-v-6f93f55a],.replica-toolbar[data-v-6f93f55a]{display:flex;align-items:center;gap:12px}.last-updated[data-v-6f93f55a],.muted[data-v-6f93f55a]{color:#64748b;font-size:12px}.last-updated[data-v-6f93f55a]{min-width:92px;text-align:right}.summary-grid[data-v-6f93f55a]{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px}.summary-tile[data-v-6f93f55a]{border:1px solid #e5e7eb;border-radius:8px;padding:14px 16px;background:#f8fafc}.summary-tile span[data-v-6f93f55a]{display:block;color:#64748b;font-size:12px}.summary-tile strong[data-v-6f93f55a]{display:block;margin-top:8px;color:#111827;font-size:24px}.compute-tabs[data-v-6f93f55a]{flex:1;min-height:0}.compute-tabs[data-v-6f93f55a] .el-tabs__content{height:calc(100% - 56px)}.compute-tabs[data-v-6f93f55a] .el-tab-pane{height:100%}.mono[data-v-6f93f55a]{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}.replica-toolbar[data-v-6f93f55a]{margin-bottom:12px}.replica-toolbar .el-select[data-v-6f93f55a]{width:280px}.node-form-grid[data-v-6f93f55a]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:12px}.node-form-grid[data-v-6f93f55a] .el-input-number,.node-form-grid[data-v-6f93f55a] .el-select{width:100%}@media(max-width:960px){.compute-header[data-v-6f93f55a],.header-actions[data-v-6f93f55a],.replica-toolbar[data-v-6f93f55a]{flex-direction:column;align-items:stretch}.summary-grid[data-v-6f93f55a]{grid-template-columns:repeat(2,minmax(0,1fr))}.node-form-grid[data-v-6f93f55a]{grid-template-columns:1fr}}
|
||||||
File diff suppressed because one or more lines are too long
2
frontend/dist/assets/DashboardView-ZalvMwT0.js
vendored
Normal file
2
frontend/dist/assets/DashboardView-ZalvMwT0.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/DataConvertView-BPalOAiH.js
vendored
Normal file
1
frontend/dist/assets/DataConvertView-BPalOAiH.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{a as N,E as v}from"./el-form-item-rya6onmd.js";import{E as b}from"./el-popper-DDJTxf27.js";import{E as O}from"./index-DbBHj60R.js";import{E as S}from"./index-DxH5-_w6.js";import{E as g,a as E}from"./el-select-BQaRHu3V.js";import{d as J,e as V,w as e,o as w,q as t,s as l,x as i,z as x,A as C}from"./index-D5veDkuG.js";import"./el-tooltip-l0sNRNKZ.js";import"./el-scrollbar-GFOwBTUz.js";import"./el-tag-C2d1Zfbn.js";import{P as U}from"./PageCard-DFGMy1ik.js";import{_ as y}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./castArray-BDIbf8ih.js";import"./_baseClone-BuqHm105.js";import"./index-BT1A2I5c.js";import"./index-DmAaj_YO.js";import"./raf-Dsox5IbR.js";import"./index-DDY6aSpq.js";import"./vnode-CYZHqsIy.js";import"./index-DbuKXJ7V.js";import"./scroll-DswMXu1y.js";import"./clamp-Ddg8_Yc2.js";import"./toNumber-a8w_gs0u.js";import"./_baseIteratee-N-G7GeYE.js";import"./el-card-DzhpMhms.js";const F={class:"converter-panel"},L={class:"form-row"},T={class:"panel-footer"},B={class:"actions"},k=J({__name:"DataConvertView",setup(I){const a=x({outputName:"converted-data",encoding:"UTF-8"});function p(){C.info("当前仅完成界面设计,转换功能将在后续接入")}function d(){Object.assign(a,{outputName:"converted-data",encoding:"UTF-8"})}return(j,o)=>{const n=N,m=O,u=E,f=g,c=v,r=S,_=b;return w(),V(U,{class:"data-convert-page",title:"数据类型转换",subtitle:"将 JSON 文件转换为便于训练和评测使用的 JSONL 格式"},{default:e(()=>[t("div",F,[o[9]||(o[9]=t("div",{class:"panel-header"},[t("div",{class:"tool-icon","aria-hidden":"true"},[t("i",{class:"fa fa-exchange"})]),t("div",null,[t("h3",null,"JSON 转 JSONL"),t("p",null,"每条 JSON 数据将输出为 JSONL 文件中的一行记录")])],-1)),l(c,{class:"converter-form","label-position":"top"},{default:e(()=>[l(n,{label:"转换类型"},{default:e(()=>[...o[2]||(o[2]=[t("div",{class:"format-field","aria-label":"JSON 转 JSONL"},[t("span",null,"JSON"),t("i",{class:"fa fa-long-arrow-right","aria-hidden":"true"}),t("span",null,"JSONL")],-1)])]),_:1}),l(n,{label:"源文件",required:""},{default:e(()=>[t("button",{class:"upload-zone",type:"button",onClick:p},[...o[3]||(o[3]=[t("i",{class:"fa fa-cloud-upload","aria-hidden":"true"},null,-1),t("span",{class:"upload-content"},[t("strong",null,"点击选择或拖拽 JSON 文件到此处"),t("small",null,"仅支持 .json 格式,单文件不超过 200 MB")],-1),t("span",{class:"select-button"},"选择文件",-1)])])]),_:1}),t("div",L,[l(n,{label:"输出文件名"},{default:e(()=>[l(m,{modelValue:a.outputName,"onUpdate:modelValue":o[0]||(o[0]=s=>a.outputName=s)},{append:e(()=>[...o[4]||(o[4]=[i(".jsonl",-1)])]),_:1},8,["modelValue"])]),_:1}),l(n,{label:"字符编码"},{default:e(()=>[l(f,{modelValue:a.encoding,"onUpdate:modelValue":o[1]||(o[1]=s=>a.encoding=s),style:{width:"100%"}},{default:e(()=>[l(u,{label:"UTF-8",value:"UTF-8"})]),_:1},8,["modelValue"])]),_:1})]),o[5]||(o[5]=t("div",{class:"format-tip"},[t("i",{class:"fa fa-info-circle","aria-hidden":"true"}),t("span",null,"支持由 JSON 数组转换为 JSONL,每个数组元素输出为一行。")],-1))]),_:1}),t("div",T,[o[8]||(o[8]=t("span",{class:"prototype-label"},"当前为 UI 原型,暂不执行实际转换",-1)),t("div",B,[l(r,{onClick:d},{default:e(()=>[...o[6]||(o[6]=[i("重置",-1)])]),_:1}),l(_,{content:"转换功能将在后续开发中接入",placement:"top"},{default:e(()=>[t("span",null,[l(r,{type:"primary",disabled:""},{default:e(()=>[...o[7]||(o[7]=[i("开始转换",-1)])]),_:1})])]),_:1})])])])]),_:1})}}}),it=y(k,[["__scopeId","data-v-fcb69543"]]);export{it as default};
|
||||||
@@ -1 +0,0 @@
|
|||||||
import{a as N,E as v}from"./el-form-item-Bq0n_p5a.js";import{E as b}from"./el-popper-D1tByNLb.js";import{E as O}from"./index-h5FkzAP_.js";import{E as S}from"./index-BNsCyG4A.js";import{E as g,a as E}from"./el-select-BD70by95.js";import{d as J,e as V,w as e,o as w,q as t,s as l,x as i,z as x,A as C}from"./index-Ds9AETjS.js";import"./el-tooltip-l0sNRNKZ.js";import"./el-scrollbar-CF42LNzg.js";/* empty css */import{P as U}from"./PageCard-CV3p14-x.js";import{_ as y}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./castArray-BWI4UxBy.js";import"./_baseClone-CZxxnGCv.js";import"./index-BSbCLbaz.js";import"./index-sWKLi7bH.js";import"./raf-zQ00nuMI.js";import"./index-B0if-AHo.js";import"./vnode-CBiWHFw7.js";import"./index-CQlF36Lk.js";import"./index-ByhS8A1d.js";import"./scroll-DVmMv0d9.js";import"./clamp-CyhADbdq.js";import"./toNumber-DrayciwT.js";import"./_baseIteratee-82gB5Yov.js";import"./el-card-D86TXgMv.js";const F={class:"converter-panel"},L={class:"form-row"},T={class:"panel-footer"},B={class:"actions"},k=J({__name:"DataConvertView",setup(I){const a=x({outputName:"converted-data",encoding:"UTF-8"});function p(){C.info("当前仅完成界面设计,转换功能将在后续接入")}function d(){Object.assign(a,{outputName:"converted-data",encoding:"UTF-8"})}return(j,o)=>{const n=N,m=O,u=E,f=g,c=v,r=S,_=b;return w(),V(U,{class:"data-convert-page",title:"数据类型转换",subtitle:"将 JSON 文件转换为便于训练和评测使用的 JSONL 格式"},{default:e(()=>[t("div",F,[o[9]||(o[9]=t("div",{class:"panel-header"},[t("div",{class:"tool-icon","aria-hidden":"true"},[t("i",{class:"fa fa-exchange"})]),t("div",null,[t("h3",null,"JSON 转 JSONL"),t("p",null,"每条 JSON 数据将输出为 JSONL 文件中的一行记录")])],-1)),l(c,{class:"converter-form","label-position":"top"},{default:e(()=>[l(n,{label:"转换类型"},{default:e(()=>[...o[2]||(o[2]=[t("div",{class:"format-field","aria-label":"JSON 转 JSONL"},[t("span",null,"JSON"),t("i",{class:"fa fa-long-arrow-right","aria-hidden":"true"}),t("span",null,"JSONL")],-1)])]),_:1}),l(n,{label:"源文件",required:""},{default:e(()=>[t("button",{class:"upload-zone",type:"button",onClick:p},[...o[3]||(o[3]=[t("i",{class:"fa fa-cloud-upload","aria-hidden":"true"},null,-1),t("span",{class:"upload-content"},[t("strong",null,"点击选择或拖拽 JSON 文件到此处"),t("small",null,"仅支持 .json 格式,单文件不超过 200 MB")],-1),t("span",{class:"select-button"},"选择文件",-1)])])]),_:1}),t("div",L,[l(n,{label:"输出文件名"},{default:e(()=>[l(m,{modelValue:a.outputName,"onUpdate:modelValue":o[0]||(o[0]=s=>a.outputName=s)},{append:e(()=>[...o[4]||(o[4]=[i(".jsonl",-1)])]),_:1},8,["modelValue"])]),_:1}),l(n,{label:"字符编码"},{default:e(()=>[l(f,{modelValue:a.encoding,"onUpdate:modelValue":o[1]||(o[1]=s=>a.encoding=s),style:{width:"100%"}},{default:e(()=>[l(u,{label:"UTF-8",value:"UTF-8"})]),_:1},8,["modelValue"])]),_:1})]),o[5]||(o[5]=t("div",{class:"format-tip"},[t("i",{class:"fa fa-info-circle","aria-hidden":"true"}),t("span",null,"支持由 JSON 数组转换为 JSONL,每个数组元素输出为一行。")],-1))]),_:1}),t("div",T,[o[8]||(o[8]=t("span",{class:"prototype-label"},"当前为 UI 原型,暂不执行实际转换",-1)),t("div",B,[l(r,{onClick:d},{default:e(()=>[...o[6]||(o[6]=[i("重置",-1)])]),_:1}),l(_,{content:"转换功能将在后续开发中接入",placement:"top"},{default:e(()=>[t("span",null,[l(r,{type:"primary",disabled:""},{default:e(()=>[...o[7]||(o[7]=[i("开始转换",-1)])]),_:1})])]),_:1})])])])]),_:1})}}}),rt=y(k,[["__scopeId","data-v-fcb69543"]]);export{rt as default};
|
|
||||||
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/DataProcessDetailView-CgvCYCPl.js
vendored
Normal file
1
frontend/dist/assets/DataProcessDetailView-CgvCYCPl.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
import{E as b}from"./index-BNsCyG4A.js";import{E as k}from"./index-ByhS8A1d.js";import{E as x}from"./el-table-C7Zx0Bmo.js";import{d as v,c as m,s as e,w as a,y as D,o as l,q as p,x as i,e as C,aa as n,A as E,v as T}from"./index-Ds9AETjS.js";/* empty css */import"./el-checkbox-pV3o_8iy.js";import{D as z}from"./DataTablePage-DzEUb2tf.js";import{_ as B}from"./ModelStatusTag.vue_vue_type_script_setup_true_lang-CHgevs5i.js";import{_ as V}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./el-scrollbar-CF42LNzg.js";import"./index-BSbCLbaz.js";import"./el-popper-D1tByNLb.js";import"./index-sWKLi7bH.js";import"./_baseClone-CZxxnGCv.js";import"./_baseIteratee-82gB5Yov.js";import"./castArray-BWI4UxBy.js";import"./debounce-D6mJVDYC.js";import"./toNumber-DrayciwT.js";import"./raf-zQ00nuMI.js";import"./omit-BzI1MV_K.js";import"./index-h5FkzAP_.js";import"./index-B0if-AHo.js";import"./el-card-D86TXgMv.js";import"./el-pagination-C9R_oTJ1.js";import"./el-select-BD70by95.js";import"./vnode-CBiWHFw7.js";import"./index-CQlF36Lk.js";import"./scroll-DVmMv0d9.js";import"./clamp-CyhADbdq.js";import"./directive-B1cwmVOz.js";import"./index-DqJTjrY3.js";import"./validator-D1MwuZXj.js";import"./index-jBnYrPdD.js";import"./index-RtwaMutE.js";import"./el-tooltip-l0sNRNKZ.js";const L={class:"data-process-page",style:{height:"100%"}},N={key:1},I={class:"action-buttons"},P=v({__name:"DataProcessListView",setup(S){const u={structured:"结构化数据",unstructured:"非结构化数据",external:"外来数据源拉取"},d=T(),_=D([{id:183921,name:"客服问答数据清洗",status:"completed",process_type:"structured",source_dataset:"客服对话原始集",output_dataset:"客服对话清洗集",create_time:"2026-07-08 14:23:00"},{id:492015,name:"指令微调数据构造",status:"running",process_type:"unstructured",source_dataset:"通用语料库",output_dataset:"SFT 指令集",create_time:"2026-07-09 09:10:00"},{id:731948,name:"敏感信息脱敏处理",status:"pending",process_type:"structured",source_dataset:"用户反馈数据",create_time:"2026-07-09 16:45:00"},{id:582012,name:"多轮对话拼接",status:"failed",process_type:"structured",source_dataset:"单轮问答集",create_time:"2026-07-10 08:30:00"}]);function f(s){const r=s.id;d.push({name:"data-process-detail",params:{id:r}})}function g(s){E.info("删除功能开发中...")}function y(s){return s?new Date(s).toLocaleString("zh-CN",{hour12:!1}):"-"}return(s,r)=>{const o=x,h=k,c=b;return l(),m("div",L,[e(z,{title:"",data:_.value,searchable:"","search-fields":["name"],"create-text":"新建数据处理","create-to":"/data-process/create","row-key":"id","page-size":10},{columns:a(()=>[e(o,{label:"任务ID",prop:"id",align:"center",width:"100"}),e(o,{label:"任务名称",prop:"name",align:"center","show-overflow-tooltip":""}),e(o,{label:"任务状态",align:"center",width:"110"},{default:a(({row:t})=>[e(B,{status:t.status},null,8,["status"])]),_:1}),e(o,{label:"处理类型",align:"center",width:"140"},{default:a(({row:t})=>[t.process_type?(l(),C(h,{key:0,size:"small",type:"info",effect:"plain"},{default:a(()=>[i(n(u[t.process_type]||t.process_type),1)]),_:2},1024)):(l(),m("span",N,"-"))]),_:1}),e(o,{label:"源数据集",align:"center","show-overflow-tooltip":""},{default:a(({row:t})=>[i(n(t.source_dataset||"-"),1)]),_:1}),e(o,{label:"输出数据集",align:"center","show-overflow-tooltip":""},{default:a(({row:t})=>[i(n(t.output_dataset||"-"),1)]),_:1}),e(o,{label:"创建时间",align:"center",width:"190"},{default:a(({row:t})=>[i(n(y(t.create_time)),1)]),_:1})]),actions:a(({row:t})=>[p("div",I,[e(c,{type:"primary",link:"",size:"small",onClick:w=>f(t)},{default:a(()=>[...r[0]||(r[0]=[p("i",{class:"fa fa-file-text-o",style:{"margin-right":"4px"}},null,-1),i("详情 ",-1)])]),_:1},8,["onClick"]),e(c,{type:"danger",link:"",size:"small",onClick:w=>g(t)},{default:a(()=>[...r[1]||(r[1]=[p("i",{class:"fa fa-trash-o",style:{"margin-right":"4px"}},null,-1),i("删除 ",-1)])]),_:1},8,["onClick"])])]),_:1},8,["data"])])}}}),yt=V(P,[["__scopeId","data-v-c0da9336"]]);export{yt as default};
|
|
||||||
1
frontend/dist/assets/DataProcessListView-CE3lkVcz.js
vendored
Normal file
1
frontend/dist/assets/DataProcessListView-CE3lkVcz.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{E as b}from"./index-DxH5-_w6.js";import{E as k}from"./el-tag-C2d1Zfbn.js";import{E as x}from"./el-table-B6qMxzT4.js";import{d as v,c as m,s as e,w as a,y as D,o as l,q as p,x as i,e as C,aa as n,A as E,v as T}from"./index-D5veDkuG.js";import"./el-checkbox-D6iLoXkw.js";import{D as z}from"./DataTablePage-C2qtw8vm.js";import{_ as B}from"./ModelStatusTag.vue_vue_type_script_setup_true_lang-CDqn2Ul4.js";import{_ as V}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./el-scrollbar-GFOwBTUz.js";import"./index-BT1A2I5c.js";import"./el-popper-DDJTxf27.js";import"./index-DmAaj_YO.js";import"./_baseClone-BuqHm105.js";import"./_baseIteratee-N-G7GeYE.js";import"./castArray-BDIbf8ih.js";import"./debounce-D3zKL4rk.js";import"./toNumber-a8w_gs0u.js";import"./raf-Dsox5IbR.js";import"./omit-DxQEBv1Z.js";import"./index-DbBHj60R.js";import"./index-DDY6aSpq.js";import"./el-card-DzhpMhms.js";import"./el-pagination-DeOi3r5c.js";import"./el-select-BQaRHu3V.js";import"./vnode-CYZHqsIy.js";import"./index-DbuKXJ7V.js";import"./scroll-DswMXu1y.js";import"./clamp-Ddg8_Yc2.js";import"./directive-BBfPDwlQ.js";import"./index-BMWxqs3c.js";import"./validator-CddYkkyy.js";import"./index-fqcbYM03.js";import"./index-BNyn2v9n.js";import"./el-tooltip-l0sNRNKZ.js";import"./status-Dl1fykxa.js";const L={class:"data-process-page",style:{height:"100%"}},N={key:1},I={class:"action-buttons"},P=v({__name:"DataProcessListView",setup(S){const u={structured:"结构化数据",unstructured:"非结构化数据",external:"外来数据源拉取"},d=T(),_=D([{id:183921,name:"客服问答数据清洗",status:"completed",process_type:"structured",source_dataset:"客服对话原始集",output_dataset:"客服对话清洗集",create_time:"2026-07-08 14:23:00"},{id:492015,name:"指令微调数据构造",status:"running",process_type:"unstructured",source_dataset:"通用语料库",output_dataset:"SFT 指令集",create_time:"2026-07-09 09:10:00"},{id:731948,name:"敏感信息脱敏处理",status:"pending",process_type:"structured",source_dataset:"用户反馈数据",create_time:"2026-07-09 16:45:00"},{id:582012,name:"多轮对话拼接",status:"failed",process_type:"structured",source_dataset:"单轮问答集",create_time:"2026-07-10 08:30:00"}]);function f(s){const r=s.id;d.push({name:"data-process-detail",params:{id:r}})}function g(s){E.info("删除功能开发中...")}function y(s){return s?new Date(s).toLocaleString("zh-CN",{hour12:!1}):"-"}return(s,r)=>{const o=x,h=k,c=b;return l(),m("div",L,[e(z,{title:"",data:_.value,searchable:"","search-fields":["name"],"create-text":"新建数据处理","create-to":"/data-process/create","row-key":"id","page-size":10},{columns:a(()=>[e(o,{label:"任务ID",prop:"id",align:"center",width:"100"}),e(o,{label:"任务名称",prop:"name",align:"center","show-overflow-tooltip":""}),e(o,{label:"任务状态",align:"center",width:"110"},{default:a(({row:t})=>[e(B,{status:t.status},null,8,["status"])]),_:1}),e(o,{label:"处理类型",align:"center",width:"140"},{default:a(({row:t})=>[t.process_type?(l(),C(h,{key:0,size:"small",type:"info",effect:"plain"},{default:a(()=>[i(n(u[t.process_type]||t.process_type),1)]),_:2},1024)):(l(),m("span",N,"-"))]),_:1}),e(o,{label:"源数据集",align:"center","show-overflow-tooltip":""},{default:a(({row:t})=>[i(n(t.source_dataset||"-"),1)]),_:1}),e(o,{label:"输出数据集",align:"center","show-overflow-tooltip":""},{default:a(({row:t})=>[i(n(t.output_dataset||"-"),1)]),_:1}),e(o,{label:"创建时间",align:"center",width:"190"},{default:a(({row:t})=>[i(n(y(t.create_time)),1)]),_:1})]),actions:a(({row:t})=>[p("div",I,[e(c,{type:"primary",link:"",size:"small",onClick:w=>f(t)},{default:a(()=>[...r[0]||(r[0]=[p("i",{class:"fa fa-file-text-o",style:{"margin-right":"4px"}},null,-1),i("详情 ",-1)])]),_:1},8,["onClick"]),e(c,{type:"danger",link:"",size:"small",onClick:w=>g(t)},{default:a(()=>[...r[1]||(r[1]=[p("i",{class:"fa fa-trash-o",style:{"margin-right":"4px"}},null,-1),i("删除 ",-1)])]),_:1},8,["onClick"])])]),_:1},8,["data"])])}}}),yt=V(P,[["__scopeId","data-v-c0da9336"]]);export{yt as default};
|
||||||
1
frontend/dist/assets/DataTablePage-C2qtw8vm.js
vendored
Normal file
1
frontend/dist/assets/DataTablePage-C2qtw8vm.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
@charset "UTF-8";.data-table-page[data-v-e6e41965]{width:100%;height:100%;box-sizing:border-box;display:flex;flex-direction:column;min-height:0}.data-table-page .table-card[data-v-e6e41965]{margin-bottom:0;width:100%;flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden}.data-table-page .table-toolbar[data-v-e6e41965]{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;padding:0 12px;flex-wrap:wrap;gap:12px}.data-table-page .table-title[data-v-e6e41965]{font-size:18px;font-weight:600;color:#1e293b;margin:0;letter-spacing:-.3px}.data-table-page .toolbar-actions[data-v-e6e41965]{display:flex;align-items:center;gap:12px}.data-table-page .search-input[data-v-e6e41965]{width:280px}.data-table-page .search-input[data-v-e6e41965] .el-input__wrapper{border-radius:8px;box-shadow:0 0 0 1px #e2e8f0 inset;transition:all .2s}.data-table-page .search-input[data-v-e6e41965] .el-input__wrapper.is-focus{box-shadow:0 0 0 1px var(--primary-color) inset}.data-table-page .batch-bar[data-v-e6e41965]{display:flex;align-items:center;justify-content:space-between;background:#4f46e50d;border:1px solid rgba(79,70,229,.1);border-radius:8px;padding:10px 16px;margin:0 24px 16px}.data-table-page .batch-bar .batch-info[data-v-e6e41965]{font-size:13px;color:var(--primary-color)}.data-table-page .batch-bar .batch-info strong[data-v-e6e41965]{font-weight:600;margin:0 4px}.data-table-page .table-body[data-v-e6e41965]{flex:1;min-height:0}.data-table-page .table-pagination[data-v-e6e41965]{display:flex;justify-content:flex-end;align-items:center;padding:16px 24px;border-top:1px solid #f1f5f9}.data-table-page .table-pagination[data-v-e6e41965] .el-pagination{margin-top:0}.data-table-page[data-v-e6e41965] .el-card__body{display:flex;flex-direction:column;padding:12px 0 0;flex:1;min-height:0}.data-table-page[data-v-e6e41965] .el-table__inner-wrapper:before{display:none}.data-table-page[data-v-e6e41965] .el-table td.el-table__cell{padding:18px 0!important}.data-table-page[data-v-e6e41965] .el-table__cell .cell{line-height:22px;white-space:nowrap}.data-table-page[data-v-e6e41965] .el-table .action-buttons{display:flex;align-items:center;justify-content:center;flex-wrap:nowrap;gap:8px}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
import{E as M}from"./index-h5FkzAP_.js";import{E as Z}from"./index-BNsCyG4A.js";import{E as A}from"./el-card-D86TXgMv.js";import{E as G}from"./el-pagination-C9R_oTJ1.js";import{E as H,a as J}from"./el-table-C7Zx0Bmo.js";import{v as O}from"./directive-B1cwmVOz.js";import{E as b}from"./index-DqJTjrY3.js";import{d as Q,y as f,D as _,c as D,s as p,w as r,o as u,q as s,h as w,e as g,g as v,x as y,aa as k,Z as W,K as X,j as $,v as Y}from"./index-Ds9AETjS.js";import"./el-scrollbar-CF42LNzg.js";import"./el-popper-D1tByNLb.js";/* empty css */import"./el-select-BD70by95.js";import"./el-tooltip-l0sNRNKZ.js";import"./el-checkbox-pV3o_8iy.js";import{_ as ee}from"./_plugin-vue_export-helper-DlAUqK2U.js";const te={class:"data-table-page"},ae={class:"table-toolbar"},le={class:"table-title"},oe={class:"toolbar-actions"},se={key:0,class:"batch-bar"},ne={class:"batch-info"},re={class:"table-body"},ie={class:"table-pagination"},ce=Q({__name:"DataTablePage",props:{title:{},data:{},loading:{type:Boolean,default:!1},searchable:{type:Boolean,default:!1},searchFields:{},multiSelect:{type:Boolean,default:!1},createText:{},createTo:{},deleteFn:{},rowKey:{default:"id"},emptyText:{default:"暂无数据"},pageSize:{default:10},pageSizes:{default:()=>[10,20,50,100]}},emits:["create","refresh"],setup(l,{expose:F,emit:V}){const a=l,x=V,P=Y(),S=f(),o=f(new Set),d=f(""),m=f(1),h=f(a.pageSize);function z(t){return t[a.rowKey]??t.name??t.id}const T=$(()=>{var e;if(!d.value.trim()||!((e=a.searchFields)!=null&&e.length))return a.data;const t=d.value.toLowerCase().trim();return a.data.filter(i=>a.searchFields.some(n=>String(i[n]??"").toLowerCase().includes(t)))}),I=$(()=>{const t=(m.value-1)*h.value;return T.value.slice(t,t+h.value)});function K(t){o.value=new Set(t.map(z))}async function E(t,e="确定要删除这条记录吗?"){if(a.deleteFn)try{await b.confirm(e,"确认删除",{type:"warning",confirmButtonText:"确定",cancelButtonText:"取消"}),await a.deleteFn(t),x("refresh")}catch{}}async function N(){if(!(o.value.size===0||!a.deleteFn))try{await b.confirm(`确定要删除选中的 ${o.value.size} 条记录吗?`,"批量删除",{type:"warning"});const t=a.data.filter(n=>o.value.has(z(n)));let e=0,i=0;for(const n of t)try{await a.deleteFn(n),e++}catch{i++}o.value.clear(),x("refresh"),i===0?b.alert(`成功删除 ${e} 条记录`,"成功",{type:"success"}):b.alert(`成功 ${e} 条,失败 ${i} 条`,"部分失败",{type:"warning"})}catch{}}function C(){var t;o.value.clear(),(t=S.value)==null||t.clearSelection()}function L(){a.createTo?P.push(a.createTo):x("create")}return _(()=>a.data,()=>{var t;o.value.clear(),m.value=1,(t=S.value)==null||t.clearSelection()}),_(()=>d.value,()=>{m.value=1}),F({handleDelete:E,clearSelection:C}),(t,e)=>{const i=M,n=Z,B=H,R=J,U=G,j=A,q=O;return u(),D("div",te,[p(j,{shadow:"never",class:"table-card"},{default:r(()=>[s("div",ae,[w(t.$slots,"title",{},()=>[s("h2",le,k(l.title),1)],!0),s("div",oe,[l.searchable?(u(),g(i,{key:0,modelValue:d.value,"onUpdate:modelValue":e[0]||(e[0]=c=>d.value=c),placeholder:`搜索${l.title}...`,clearable:"",class:"search-input"},{prefix:r(()=>[...e[3]||(e[3]=[s("i",{class:"fa fa-search"},null,-1)])]),_:1},8,["modelValue","placeholder"])):v("",!0),l.createText?(u(),g(n,{key:1,type:"primary",onClick:L},{default:r(()=>[e[4]||(e[4]=s("i",{class:"fa fa-plus",style:{"margin-right":"4px"}},null,-1)),y(" "+k(l.createText),1)]),_:1})):v("",!0),w(t.$slots,"toolbar-extra",{},void 0,!0)])]),p(W,{name:"el-zoom-in-top"},{default:r(()=>[l.multiSelect&&o.value.size>0?(u(),D("div",se,[s("span",ne,[e[6]||(e[6]=y(" 已选择 ",-1)),s("strong",null,k(o.value.size),1),e[7]||(e[7]=y(" 项 ",-1)),p(n,{link:"",type:"primary",onClick:C},{default:r(()=>[...e[5]||(e[5]=[y("取消选择",-1)])]),_:1})]),p(n,{type:"danger",size:"small",onClick:N},{default:r(()=>[e[8]||(e[8]=s("i",{class:"fa fa-trash",style:{"margin-right":"4px"}},null,-1)),y(" 批量删除 ("+k(o.value.size)+") ",1)]),_:1})])):v("",!0)]),_:1}),s("div",re,[X((u(),g(R,{ref_key:"tableRef",ref:S,data:I.value,"row-key":l.rowKey,height:"100%",style:{width:"100%"},"empty-text":l.emptyText,onSelectionChange:K},{default:r(()=>[l.multiSelect?(u(),g(B,{key:0,type:"selection",width:"50",align:"center","reserve-selection":"",fixed:"left"})):v("",!0),w(t.$slots,"columns",{},void 0,!0),t.$slots.actions?(u(),g(B,{key:1,label:"操作",align:"center",width:"220",fixed:"right"},{default:r(({row:c})=>[w(t.$slots,"actions",{row:c,handleDelete:E},void 0,!0)]),_:3})):v("",!0)]),_:3},8,["data","row-key","empty-text"])),[[q,l.loading]])]),s("div",ie,[p(U,{"current-page":m.value,"onUpdate:currentPage":e[1]||(e[1]=c=>m.value=c),"page-size":h.value,"onUpdate:pageSize":e[2]||(e[2]=c=>h.value=c),background:"",layout:"total, sizes, prev, pager, next, jumper","page-sizes":l.pageSizes,total:T.value.length},null,8,["current-page","page-size","page-sizes","total"])])]),_:3})])}}}),Te=ee(ce,[["__scopeId","data-v-e6e41965"]]);export{Te as D};
|
|
||||||
1
frontend/dist/assets/DataTablePage-nV2v3ghG.css
vendored
Normal file
1
frontend/dist/assets/DataTablePage-nV2v3ghG.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@charset "UTF-8";.data-table-page[data-v-db8d3b6e]{width:100%;height:100%;box-sizing:border-box;display:flex;flex-direction:column;min-height:0}.data-table-page .table-card[data-v-db8d3b6e]{margin-bottom:0;width:100%;flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden}.data-table-page .table-toolbar[data-v-db8d3b6e]{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;padding:0 12px;flex-wrap:wrap;gap:12px}.data-table-page .table-title[data-v-db8d3b6e]{font-size:18px;font-weight:600;color:#1e293b;margin:0;letter-spacing:-.3px}.data-table-page .toolbar-actions[data-v-db8d3b6e]{display:flex;align-items:center;gap:12px}.data-table-page .search-input[data-v-db8d3b6e]{width:280px}.data-table-page .search-input[data-v-db8d3b6e] .el-input__wrapper{border-radius:8px;box-shadow:0 0 0 1px #e2e8f0 inset;transition:all .2s}.data-table-page .search-input[data-v-db8d3b6e] .el-input__wrapper.is-focus{box-shadow:0 0 0 1px var(--primary-color) inset}.data-table-page .batch-bar[data-v-db8d3b6e]{display:flex;align-items:center;justify-content:space-between;background:#4f46e50d;border:1px solid rgba(79,70,229,.1);border-radius:8px;padding:10px 16px;margin:0 24px 16px}.data-table-page .batch-bar .batch-info[data-v-db8d3b6e]{font-size:13px;color:var(--primary-color)}.data-table-page .batch-bar .batch-info strong[data-v-db8d3b6e]{font-weight:600;margin:0 4px}.data-table-page .table-body[data-v-db8d3b6e]{flex:1;min-height:0}.data-table-page .table-pagination[data-v-db8d3b6e]{display:flex;justify-content:flex-end;align-items:center;padding:16px 24px;border-top:1px solid #f1f5f9}.data-table-page .table-pagination[data-v-db8d3b6e] .el-pagination{margin-top:0}.data-table-page[data-v-db8d3b6e] .el-card__body{display:flex;flex-direction:column;padding:12px 0 0;flex:1;min-height:0}.data-table-page[data-v-db8d3b6e] .el-table__inner-wrapper:before{display:none}.data-table-page[data-v-db8d3b6e] .el-table td.el-table__cell{padding:18px 0!important}.data-table-page[data-v-db8d3b6e] .el-table__cell .cell{line-height:22px;white-space:nowrap}.data-table-page[data-v-db8d3b6e] .el-table .action-buttons{display:flex;align-items:center;justify-content:center;flex-wrap:nowrap;gap:8px}
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
import{E as z}from"./index-BNsCyG4A.js";import{E}from"./index-ByhS8A1d.js";import{E as T}from"./el-table-C7Zx0Bmo.js";import{d as L,G as A,e as k,w as e,y as f,j as V,o as y,q as r,s as l,x as s,g as $,aa as p,f as b,aY as B,aZ as N,n as w,v as P,A as S}from"./index-Ds9AETjS.js";/* empty css */import"./el-checkbox-pV3o_8iy.js";import{D as M}from"./DataTablePage-DzEUb2tf.js";import{g as R,d as G,a as I}from"./dataset-BWM6p-mU.js";import{_ as Y}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./el-scrollbar-CF42LNzg.js";import"./index-BSbCLbaz.js";import"./el-popper-D1tByNLb.js";import"./index-sWKLi7bH.js";import"./_baseClone-CZxxnGCv.js";import"./_baseIteratee-82gB5Yov.js";import"./castArray-BWI4UxBy.js";import"./debounce-D6mJVDYC.js";import"./toNumber-DrayciwT.js";import"./raf-zQ00nuMI.js";import"./omit-BzI1MV_K.js";import"./index-h5FkzAP_.js";import"./index-B0if-AHo.js";import"./el-card-D86TXgMv.js";import"./el-pagination-C9R_oTJ1.js";import"./el-select-BD70by95.js";import"./vnode-CBiWHFw7.js";import"./index-CQlF36Lk.js";import"./scroll-DVmMv0d9.js";import"./clamp-CyhADbdq.js";import"./directive-B1cwmVOz.js";import"./index-DqJTjrY3.js";import"./validator-D1MwuZXj.js";import"./index-jBnYrPdD.js";import"./index-RtwaMutE.js";import"./el-tooltip-l0sNRNKZ.js";const j={class:"capsule-tabs"},q={class:"action-buttons"},O=L({__name:"DatasetListView",setup(U){const C=P(),m=f(!1),c=f([]),n=f("upload"),D=V(()=>n.value==="task"?c.value.filter(i=>i.source==="task"):c.value.filter(i=>i.source!=="task"));async function d(){m.value=!0;try{c.value=await R()||[]}catch{}finally{m.value=!1}}async function _(i){await G(i.id),c.value=c.value.filter(a=>a.id!==i.id),await d(),S.success("删除成功")}function h(i){C.push(`/dataset/${i.id}/preview`)}function x(i){window.open(I(i.id),"_blank")}return A(d),(i,a)=>{const o=T,v=E,u=z;return y(),k(M,{title:"数据集管理",data:D.value,loading:m.value,searchable:"","search-fields":["name","description"],"create-text":n.value==="upload"?"上传数据集":"","create-to":"/dataset/create","delete-fn":_,"row-key":"id",onRefresh:d},{title:e(()=>[r("div",j,[r("button",{class:w(["capsule-tab-item",{active:n.value==="upload"}]),onClick:a[0]||(a[0]=t=>n.value="upload")}," 上传任务 ",2),r("button",{class:w(["capsule-tab-item",{active:n.value==="task"}]),onClick:a[1]||(a[1]=t=>n.value="task")}," 数据任务 ",2)])]),columns:e(()=>[n.value==="task"?(y(),k(o,{key:0,label:"任务ID",prop:"task_id",align:"center",width:"100"})):$("",!0),l(o,{label:"数据集名称",prop:"name",align:"center"}),l(o,{label:"数据类型",align:"center",width:"110"},{default:e(({row:t})=>[l(v,{type:"primary",size:"small"},{default:e(()=>[s(p(b(B)[String(t.type).toLowerCase()]||t.type||"-"),1)]),_:2},1024)]),_:1}),l(o,{label:"存储位置",align:"center",width:"110"},{default:e(({row:t})=>[l(v,{type:"success",size:"small"},{default:e(()=>[s(p(b(N)[t.storage_type]||t.storage_type||"-"),1)]),_:2},1024)]),_:1}),l(o,{label:"大小",align:"center",width:"100"},{default:e(({row:t})=>[s(p(t.size&&t.size!=="0 B"&&t.size!=="0"?t.size:"-"),1)]),_:1}),l(o,{label:"数据条数",align:"center",width:"100"},{default:e(({row:t})=>[s(p(t.count||0),1)]),_:1}),l(o,{label:"描述",align:"center","show-overflow-tooltip":""},{default:e(({row:t})=>[s(p(t.description||"-"),1)]),_:1}),l(o,{label:"创建时间",align:"center",width:"180"},{default:e(({row:t})=>[s(p(t.create_time?new Date(t.create_time).toLocaleString("zh-CN"):"-"),1)]),_:1})]),actions:e(({row:t})=>[r("div",q,[l(u,{type:"primary",link:"",size:"small",onClick:g=>h(t)},{default:e(()=>[...a[2]||(a[2]=[r("i",{class:"fa fa-eye",style:{"margin-right":"4px"}},null,-1),s("详情 ",-1)])]),_:1},8,["onClick"]),l(u,{type:"success",link:"",size:"small",onClick:g=>x(t)},{default:e(()=>[...a[3]||(a[3]=[r("i",{class:"fa fa-download",style:{"margin-right":"4px"}},null,-1),s("下载 ",-1)])]),_:1},8,["onClick"]),l(u,{type:"danger",link:"",size:"small",onClick:g=>_(t)},{default:e(()=>[...a[4]||(a[4]=[r("i",{class:"fa fa-trash-o",style:{"margin-right":"4px"}},null,-1),s("删除 ",-1)])]),_:1},8,["onClick"])])]),_:1},8,["data","loading","create-text"])}}}),Et=Y(O,[["__scopeId","data-v-e406ac6d"]]);export{Et as default};
|
|
||||||
1
frontend/dist/assets/DatasetListView-CwP8x_GJ.js
vendored
Normal file
1
frontend/dist/assets/DatasetListView-CwP8x_GJ.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{E as z}from"./index-DxH5-_w6.js";import{E}from"./el-tag-C2d1Zfbn.js";import{E as T}from"./el-table-B6qMxzT4.js";import{d as L,G as A,e as k,w as e,y as f,j as V,o as y,q as r,s as l,x as s,g as $,aa as p,f as b,aY as B,aZ as N,n as w,v as P,A as S}from"./index-D5veDkuG.js";import"./el-checkbox-D6iLoXkw.js";import{D as M}from"./DataTablePage-C2qtw8vm.js";import{g as R,d as G,a as I}from"./dataset-BvcQX9QF.js";import{_ as Y}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./el-scrollbar-GFOwBTUz.js";import"./index-BT1A2I5c.js";import"./el-popper-DDJTxf27.js";import"./index-DmAaj_YO.js";import"./_baseClone-BuqHm105.js";import"./_baseIteratee-N-G7GeYE.js";import"./castArray-BDIbf8ih.js";import"./debounce-D3zKL4rk.js";import"./toNumber-a8w_gs0u.js";import"./raf-Dsox5IbR.js";import"./omit-DxQEBv1Z.js";import"./index-DbBHj60R.js";import"./index-DDY6aSpq.js";import"./el-card-DzhpMhms.js";import"./el-pagination-DeOi3r5c.js";import"./el-select-BQaRHu3V.js";import"./vnode-CYZHqsIy.js";import"./index-DbuKXJ7V.js";import"./scroll-DswMXu1y.js";import"./clamp-Ddg8_Yc2.js";import"./directive-BBfPDwlQ.js";import"./index-BMWxqs3c.js";import"./validator-CddYkkyy.js";import"./index-fqcbYM03.js";import"./index-BNyn2v9n.js";import"./el-tooltip-l0sNRNKZ.js";const j={class:"capsule-tabs"},q={class:"action-buttons"},O=L({__name:"DatasetListView",setup(U){const C=P(),d=f(!1),c=f([]),n=f("upload"),D=V(()=>n.value==="task"?c.value.filter(i=>i.source==="task"):c.value.filter(i=>i.source!=="task"));async function m(){d.value=!0;try{c.value=await R()||[]}catch{}finally{d.value=!1}}async function _(i){await G(i.id),c.value=c.value.filter(a=>a.id!==i.id),await m(),S.success("删除成功")}function h(i){C.push(`/dataset/${i.id}/preview`)}function x(i){window.open(I(i.id),"_blank")}return A(m),(i,a)=>{const o=T,v=E,u=z;return y(),k(M,{title:"数据集管理",data:D.value,loading:d.value,searchable:"","search-fields":["name","description"],"create-text":n.value==="upload"?"上传数据集":"","create-to":"/dataset/create","delete-fn":_,"row-key":"id",onRefresh:m},{title:e(()=>[r("div",j,[r("button",{class:w(["capsule-tab-item",{active:n.value==="upload"}]),onClick:a[0]||(a[0]=t=>n.value="upload")}," 上传任务 ",2),r("button",{class:w(["capsule-tab-item",{active:n.value==="task"}]),onClick:a[1]||(a[1]=t=>n.value="task")}," 数据任务 ",2)])]),columns:e(()=>[n.value==="task"?(y(),k(o,{key:0,label:"任务ID",prop:"task_id",align:"center",width:"100"})):$("",!0),l(o,{label:"数据集名称",prop:"name",align:"center"}),l(o,{label:"数据类型",align:"center",width:"110"},{default:e(({row:t})=>[l(v,{type:"primary",size:"small"},{default:e(()=>[s(p(b(B)[String(t.type).toLowerCase()]||t.type||"-"),1)]),_:2},1024)]),_:1}),l(o,{label:"存储位置",align:"center",width:"110"},{default:e(({row:t})=>[l(v,{type:"success",size:"small"},{default:e(()=>[s(p(b(N)[t.storage_type]||t.storage_type||"-"),1)]),_:2},1024)]),_:1}),l(o,{label:"大小",align:"center",width:"100"},{default:e(({row:t})=>[s(p(t.size&&t.size!=="0 B"&&t.size!=="0"?t.size:"-"),1)]),_:1}),l(o,{label:"数据条数",align:"center",width:"100"},{default:e(({row:t})=>[s(p(t.count||0),1)]),_:1}),l(o,{label:"描述",align:"center","show-overflow-tooltip":""},{default:e(({row:t})=>[s(p(t.description||"-"),1)]),_:1}),l(o,{label:"创建时间",align:"center",width:"180"},{default:e(({row:t})=>[s(p(t.create_time?new Date(t.create_time).toLocaleString("zh-CN"):"-"),1)]),_:1})]),actions:e(({row:t})=>[r("div",q,[l(u,{type:"primary",link:"",size:"small",onClick:g=>h(t)},{default:e(()=>[...a[2]||(a[2]=[r("i",{class:"fa fa-eye",style:{"margin-right":"4px"}},null,-1),s("详情 ",-1)])]),_:1},8,["onClick"]),l(u,{type:"success",link:"",size:"small",onClick:g=>x(t)},{default:e(()=>[...a[3]||(a[3]=[r("i",{class:"fa fa-download",style:{"margin-right":"4px"}},null,-1),s("下载 ",-1)])]),_:1},8,["onClick"]),l(u,{type:"danger",link:"",size:"small",onClick:g=>_(t)},{default:e(()=>[...a[4]||(a[4]=[r("i",{class:"fa fa-trash-o",style:{"margin-right":"4px"}},null,-1),s("删除 ",-1)])]),_:1},8,["onClick"])])]),_:1},8,["data","loading","create-text"])}}}),zt=Y(O,[["__scopeId","data-v-e406ac6d"]]);export{zt as default};
|
||||||
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/DimensionCreateView-dON_Mrtf.js
vendored
Normal file
1
frontend/dist/assets/DimensionCreateView-dON_Mrtf.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{a as k,E as M}from"./el-form-item-rya6onmd.js";import{E as q}from"./index-DbBHj60R.js";import{E as R}from"./index-DxH5-_w6.js";import{d as S,G as B,e as F,w as i,j as y,y as _,z as I,o as O,s as r,f as m,aB as P,x as h,aa as j,A as x,v as A,ac as L}from"./index-D5veDkuG.js";import{P as N}from"./PageCard-DFGMy1ik.js";import{E as T,D as U}from"./DimensionFormFields-CNtVeJ2J.js";import{b as z,u as G,c as H}from"./eval-DOmZ1wrg.js";import{g as J}from"./model-Do_d1nlo.js";import"./castArray-BDIbf8ih.js";import"./_baseClone-BuqHm105.js";import"./raf-Dsox5IbR.js";import"./index-DDY6aSpq.js";import"./index-BT1A2I5c.js";import"./el-card-DzhpMhms.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./el-select-BQaRHu3V.js";import"./index-DmAaj_YO.js";import"./vnode-CYZHqsIy.js";import"./el-popper-DDJTxf27.js";import"./el-scrollbar-GFOwBTUz.js";import"./index-DbuKXJ7V.js";import"./el-tag-C2d1Zfbn.js";import"./scroll-DswMXu1y.js";import"./clamp-Ddg8_Yc2.js";import"./toNumber-a8w_gs0u.js";import"./_baseIteratee-N-G7GeYE.js";import"./el-checkbox-D6iLoXkw.js";import"./omit-DxQEBv1Z.js";import"./el-radio-group-HDdMmxmO.js";import"./el-input-number-Cs1MXSA_.js";import"./index-APLq0PKf.js";import"./el-slider-pAA0AhdE.js";import"./debounce-D3zKL4rk.js";import"./el-switch-DCvDahA2.js";import"./validator-CddYkkyy.js";import"./el-tooltip-l0sNRNKZ.js";/* empty css */const Se=S({__name:"DimensionCreateView",setup(K){const c=L(),f=A(),n=_(),p=_(!1),u=y(()=>!!c.params.id),s=y(()=>c.params.id),d=_([]);let e=I({name:"",type:"",description:"",eval_model:"",eval_method:"",eval_prompt:"",is_active:!0,is_default:!1,bleu_n:1,output_precision:3,score_min:0,score_max:5,pass_threshold:3});const b={name:[{required:!0,message:"请输入维度名称",trigger:"blur"},{max:50,message:"不超过 50 字符",trigger:"blur"}],type:[{required:!0,message:"请选择指标类型",trigger:"change"}],eval_model:[{required:!0,message:"请选择大模型",trigger:"change"}],eval_method:[{required:!0,message:"请选择评估方式",trigger:"change"}],eval_prompt:[{required:!0,message:"请填写评估 Prompt",trigger:"blur"}]};async function w(){var o,t;if(s.value)try{const a=await z(s.value);Object.assign(e,{name:a.name||"",type:a.type||"",description:a.description||"",eval_model:a.eval_model||"",eval_method:a.eval_method||(a.type?(t=(o=T[a.type])==null?void 0:o[0])==null?void 0:t.value:""),eval_prompt:a.eval_prompt||"",is_active:a.is_active!==!1,is_default:!!a.is_default,bleu_n:a.bleu_n??1,output_precision:a.output_precision??3,score_min:a.score_min??0,score_max:a.score_max??5,pass_threshold:a.pass_threshold??3})}catch{}}async function E(){try{const o=await J()||[];d.value=o.filter(t=>t.purpose==="evaluation")}catch{d.value=[]}}async function D(){n.value&&await n.value.validate(async o=>{if(o){p.value=!0;try{const t={name:e.name,type:e.type,description:e.description,eval_model:e.type==="text_similarity"?null:e.eval_model,eval_method:e.eval_method,eval_prompt:e.type==="text_similarity"?null:e.eval_prompt,is_active:e.is_active,is_default:e.is_default,create_time:new Date().toISOString()};e.type==="text_similarity"&&(t.bleu_n=e.bleu_n,t.output_precision=e.output_precision),e.type==="metric"&&(t.score_min=e.score_min,t.score_max=e.score_max,t.pass_threshold=e.pass_threshold),u.value&&s.value?(await G(s.value,t),x.success("更新成功")):(await H(t),x.success("创建成功")),f.push("/model-eval")}catch{}finally{p.value=!1}}})}function V(){f.back()}return B(()=>{E(),w()}),(o,t)=>{const a=q,v=k,g=R,C=M;return O(),F(N,{title:u.value?"编辑评测维度":"添加评测维度"},{default:i(()=>[r(C,{ref_key:"formRef",ref:n,model:m(e),rules:b,"label-width":"130px",style:{"max-width":"760px"}},{default:i(()=>[r(v,{label:"维度名称",prop:"name"},{default:i(()=>[r(a,{modelValue:m(e).name,"onUpdate:modelValue":t[0]||(t[0]=l=>m(e).name=l),placeholder:"请输入维度名称",maxlength:"50","show-word-limit":""},null,8,["modelValue"])]),_:1}),r(U,{modelValue:m(e),"onUpdate:modelValue":t[1]||(t[1]=l=>P(e)?e.value=l:e=l),"eval-models":d.value},null,8,["modelValue","eval-models"]),r(v,null,{default:i(()=>[r(g,{type:"primary",loading:p.value,onClick:D},{default:i(()=>[h(j(u.value?"保存":"创建"),1)]),_:1},8,["loading"]),r(g,{onClick:V},{default:i(()=>[...t[2]||(t[2]=[h("取消",-1)])]),_:1})]),_:1})]),_:1},8,["model"])]),_:1},8,["title"])}}});export{Se as default};
|
||||||
@@ -1 +0,0 @@
|
|||||||
import{a as k,E as M}from"./el-form-item-Bq0n_p5a.js";import{E as q}from"./index-h5FkzAP_.js";import{E as R}from"./index-BNsCyG4A.js";import{d as S,G as B,e as F,w as i,j as y,y as _,z as I,o as O,s as r,f as m,aB as P,x as h,aa as j,A as x,v as A,ac as L}from"./index-Ds9AETjS.js";import{P as N}from"./PageCard-CV3p14-x.js";import{E as T,D as U}from"./DimensionFormFields-Bvlt4Km3.js";import{b as z,u as G,c as H}from"./eval-RqHG_SzR.js";import{g as J}from"./model-cWd9CDtT.js";import"./castArray-BWI4UxBy.js";import"./_baseClone-CZxxnGCv.js";import"./raf-zQ00nuMI.js";import"./index-B0if-AHo.js";import"./index-BSbCLbaz.js";import"./el-card-D86TXgMv.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";import"./el-select-BD70by95.js";import"./index-sWKLi7bH.js";import"./vnode-CBiWHFw7.js";import"./el-popper-D1tByNLb.js";import"./el-scrollbar-CF42LNzg.js";import"./index-CQlF36Lk.js";import"./index-ByhS8A1d.js";import"./scroll-DVmMv0d9.js";import"./clamp-CyhADbdq.js";import"./toNumber-DrayciwT.js";import"./_baseIteratee-82gB5Yov.js";import"./el-checkbox-pV3o_8iy.js";import"./omit-BzI1MV_K.js";import"./el-radio-group-DxV9SE4w.js";import"./el-input-number-Bg2C0S8w.js";import"./index-2LOSO4Pw.js";import"./el-slider-mZJHOhL2.js";import"./debounce-D6mJVDYC.js";import"./el-switch-DflKYtBf.js";import"./validator-D1MwuZXj.js";import"./el-tooltip-l0sNRNKZ.js";/* empty css *//* empty css */const Be=S({__name:"DimensionCreateView",setup(K){const c=L(),f=A(),n=_(),p=_(!1),u=y(()=>!!c.params.id),s=y(()=>c.params.id),d=_([]);let e=I({name:"",type:"",description:"",eval_model:"",eval_method:"",eval_prompt:"",is_active:!0,is_default:!1,bleu_n:1,output_precision:3,score_min:0,score_max:5,pass_threshold:3});const b={name:[{required:!0,message:"请输入维度名称",trigger:"blur"},{max:50,message:"不超过 50 字符",trigger:"blur"}],type:[{required:!0,message:"请选择指标类型",trigger:"change"}],eval_model:[{required:!0,message:"请选择大模型",trigger:"change"}],eval_method:[{required:!0,message:"请选择评估方式",trigger:"change"}],eval_prompt:[{required:!0,message:"请填写评估 Prompt",trigger:"blur"}]};async function w(){var o,t;if(s.value)try{const a=await z(s.value);Object.assign(e,{name:a.name||"",type:a.type||"",description:a.description||"",eval_model:a.eval_model||"",eval_method:a.eval_method||(a.type?(t=(o=T[a.type])==null?void 0:o[0])==null?void 0:t.value:""),eval_prompt:a.eval_prompt||"",is_active:a.is_active!==!1,is_default:!!a.is_default,bleu_n:a.bleu_n??1,output_precision:a.output_precision??3,score_min:a.score_min??0,score_max:a.score_max??5,pass_threshold:a.pass_threshold??3})}catch{}}async function E(){try{const o=await J()||[];d.value=o.filter(t=>t.purpose==="evaluation")}catch{d.value=[]}}async function D(){n.value&&await n.value.validate(async o=>{if(o){p.value=!0;try{const t={name:e.name,type:e.type,description:e.description,eval_model:e.type==="text_similarity"?null:e.eval_model,eval_method:e.eval_method,eval_prompt:e.type==="text_similarity"?null:e.eval_prompt,is_active:e.is_active,is_default:e.is_default,create_time:new Date().toISOString()};e.type==="text_similarity"&&(t.bleu_n=e.bleu_n,t.output_precision=e.output_precision),e.type==="metric"&&(t.score_min=e.score_min,t.score_max=e.score_max,t.pass_threshold=e.pass_threshold),u.value&&s.value?(await G(s.value,t),x.success("更新成功")):(await H(t),x.success("创建成功")),f.push("/model-eval")}catch{}finally{p.value=!1}}})}function V(){f.back()}return B(()=>{E(),w()}),(o,t)=>{const a=q,v=k,g=R,C=M;return O(),F(N,{title:u.value?"编辑评测维度":"添加评测维度"},{default:i(()=>[r(C,{ref_key:"formRef",ref:n,model:m(e),rules:b,"label-width":"130px",style:{"max-width":"760px"}},{default:i(()=>[r(v,{label:"维度名称",prop:"name"},{default:i(()=>[r(a,{modelValue:m(e).name,"onUpdate:modelValue":t[0]||(t[0]=l=>m(e).name=l),placeholder:"请输入维度名称",maxlength:"50","show-word-limit":""},null,8,["modelValue"])]),_:1}),r(U,{modelValue:m(e),"onUpdate:modelValue":t[1]||(t[1]=l=>P(e)?e.value=l:e=l),"eval-models":d.value},null,8,["modelValue","eval-models"]),r(v,null,{default:i(()=>[r(g,{type:"primary",loading:p.value,onClick:D},{default:i(()=>[h(j(u.value?"保存":"创建"),1)]),_:1},8,["loading"]),r(g,{onClick:V},{default:i(()=>[...t[2]||(t[2]=[h("取消",-1)])]),_:1})]),_:1})]),_:1},8,["model"])]),_:1},8,["title"])}}});export{Be as default};
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/EvalCreateView-C_WvAqA8.js
vendored
Normal file
1
frontend/dist/assets/EvalCreateView-C_WvAqA8.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/EvalDetailView-BLvUTwZ-.js
vendored
Normal file
1
frontend/dist/assets/EvalDetailView-BLvUTwZ-.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/EvalView--2TVFV5G.css
vendored
1
frontend/dist/assets/EvalView--2TVFV5G.css
vendored
@@ -1 +0,0 @@
|
|||||||
@charset "UTF-8";.eval-page[data-v-78ab2fa4]{width:100%;height:100%;display:flex;flex-direction:column;min-height:0}.capsule-tabs[data-v-78ab2fa4]{display:flex;background:#f1f5f9;padding:3px;border-radius:8px;gap:2px;border:1px solid #e2e8f0}.capsule-tab-item[data-v-78ab2fa4]{border:0;background:transparent;padding:6px 20px;font-size:13px;font-weight:500;color:#64748b;cursor:pointer;border-radius:6px;transition:all .2s ease;outline:none}.capsule-tab-item[data-v-78ab2fa4]:hover{color:#1e293b}.capsule-tab-item.active[data-v-78ab2fa4]{background:#fff;color:#4f46e5;box-shadow:0 1px 3px #0000000f,0 1px 2px #0000000a;font-weight:600}
|
|
||||||
1
frontend/dist/assets/EvalView-CB5BSC8Y.css
vendored
Normal file
1
frontend/dist/assets/EvalView-CB5BSC8Y.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@charset "UTF-8";.eval-page[data-v-a44eaeff]{width:100%;height:100%;display:flex;flex-direction:column;min-height:0}.capsule-tabs[data-v-a44eaeff]{display:flex;background:#f1f5f9;padding:3px;border-radius:8px;gap:2px;border:1px solid #e2e8f0}.capsule-tab-item[data-v-a44eaeff]{border:0;background:transparent;padding:6px 20px;font-size:13px;font-weight:500;color:#64748b;cursor:pointer;border-radius:6px;transition:all .2s ease;outline:none}.capsule-tab-item[data-v-a44eaeff]:hover{color:#1e293b}.capsule-tab-item.active[data-v-a44eaeff]{background:#fff;color:#4f46e5;box-shadow:0 1px 3px #0000000f,0 1px 2px #0000000a;font-weight:600}
|
||||||
1
frontend/dist/assets/EvalView-CPiVytgX.js
vendored
Normal file
1
frontend/dist/assets/EvalView-CPiVytgX.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{E as V}from"./index-DxH5-_w6.js";import{E as $}from"./el-table-B6qMxzT4.js";import{d as B,G as D,c as T,e as _,w as o,g as C,y as p,o as v,s as a,q as i,x as f,aa as L,n as c,v as N,A as z}from"./index-D5veDkuG.js";import{E as M}from"./index-BMWxqs3c.js";import"./el-tag-C2d1Zfbn.js";import"./el-checkbox-D6iLoXkw.js";import{D as y}from"./DataTablePage-C2qtw8vm.js";import{_ as A}from"./ModelStatusTag.vue_vue_type_script_setup_true_lang-CDqn2Ul4.js";import{g as G,d as P}from"./eval-DOmZ1wrg.js";import{_ as R}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./el-scrollbar-GFOwBTUz.js";import"./index-BT1A2I5c.js";import"./el-popper-DDJTxf27.js";import"./index-DmAaj_YO.js";import"./_baseClone-BuqHm105.js";import"./_baseIteratee-N-G7GeYE.js";import"./castArray-BDIbf8ih.js";import"./debounce-D3zKL4rk.js";import"./toNumber-a8w_gs0u.js";import"./raf-Dsox5IbR.js";import"./validator-CddYkkyy.js";import"./index-fqcbYM03.js";import"./clamp-Ddg8_Yc2.js";import"./index-BNyn2v9n.js";import"./scroll-DswMXu1y.js";import"./index-DbBHj60R.js";import"./index-DDY6aSpq.js";import"./omit-DxQEBv1Z.js";import"./el-card-DzhpMhms.js";import"./el-pagination-DeOi3r5c.js";import"./el-select-BQaRHu3V.js";import"./vnode-CYZHqsIy.js";import"./index-DbuKXJ7V.js";import"./directive-BBfPDwlQ.js";import"./el-tooltip-l0sNRNKZ.js";import"./status-Dl1fykxa.js";const S={class:"eval-page"},q={class:"capsule-tabs"},I={class:"capsule-tabs"},Q=B({__name:"EvalView",setup(j){const b=N(),s=p("tasks"),d=p(!1),n=p([]),w=p([{rank:1,name:"GPT-4",score:92.5},{rank:2,name:"Claude-3",score:90.1},{rank:3,name:"Qwen-Max",score:85.3}]);async function u(){d.value=!0;try{n.value=await G()||[]}catch{n.value=[]}finally{d.value=!1}}async function k(r){await M.confirm("确定要删除这个评测任务吗?","确认删除",{type:"warning"}),await P(r.id),n.value=n.value.filter(e=>e.id!==r.id),z.success("删除成功"),await u()}function E(){b.push("/model-eval/create")}function m(r){return s.value===r}function x(r){b.push({name:"model-eval-detail",params:{id:r.id}})}return D(()=>{u()}),(r,e)=>{const l=$,g=V;return v(),T("div",S,[s.value==="tasks"?(v(),_(y,{key:0,title:"",data:n.value,loading:d.value,"delete-fn":k,"create-text":"创建评测任务",onCreate:E,"row-key":"id",onRefresh:u},{title:o(()=>[i("div",q,[i("button",{class:c(["capsule-tab-item",{active:m("tasks")}]),onClick:e[0]||(e[0]=t=>s.value="tasks")},"评测任务",2),i("button",{class:c(["capsule-tab-item",{active:m("leaderboard")}]),onClick:e[1]||(e[1]=t=>s.value="leaderboard")},"排行榜",2)])]),columns:o(()=>[a(l,{label:"任务名称",prop:"eval_task_name",align:"center"}),a(l,{label:"评测模型",prop:"model_name",align:"center"}),a(l,{label:"数据集",prop:"dataset",align:"center"}),a(l,{label:"指标",prop:"metric",align:"center"}),a(l,{label:"评分",prop:"score",width:"100",align:"center"}),a(l,{label:"状态",width:"100",align:"center"},{default:o(({row:t})=>[a(A,{status:t.status},null,8,["status"])]),_:1}),a(l,{label:"创建时间",align:"center",width:"180"},{default:o(({row:t})=>[f(L(t.create_time?new Date(t.create_time).toLocaleString("zh-CN"):"-"),1)]),_:1})]),actions:o(({row:t})=>[a(g,{link:"",type:"primary",size:"small",onClick:h=>x(t)},{default:o(()=>[...e[4]||(e[4]=[i("i",{class:"fa fa-file-text-o",style:{"margin-right":"4px"}},null,-1),f("详情 ",-1)])]),_:1},8,["onClick"]),a(g,{link:"",type:"danger",size:"small",onClick:h=>k(t)},{default:o(()=>[...e[5]||(e[5]=[i("i",{class:"fa fa-trash-o",style:{"margin-right":"4px"}},null,-1),f("删除 ",-1)])]),_:1},8,["onClick"])]),_:1},8,["data","loading"])):C("",!0),s.value==="leaderboard"?(v(),_(y,{key:1,title:"",data:w.value,"row-key":"rank"},{title:o(()=>[i("div",I,[i("button",{class:c(["capsule-tab-item",{active:m("tasks")}]),onClick:e[2]||(e[2]=t=>s.value="tasks")},"评测任务",2),i("button",{class:c(["capsule-tab-item",{active:m("leaderboard")}]),onClick:e[3]||(e[3]=t=>s.value="leaderboard")},"排行榜",2)])]),columns:o(()=>[a(l,{label:"排名",prop:"rank",width:"80",align:"center"}),a(l,{label:"模型名称",prop:"name",align:"center"}),a(l,{label:"综合评分",prop:"score",align:"center"})]),_:1},8,["data"])):C("",!0)])}}}),Ve=R(Q,[["__scopeId","data-v-a44eaeff"]]);export{Ve as default};
|
||||||
1
frontend/dist/assets/EvalView-Eldo4kFi.js
vendored
1
frontend/dist/assets/EvalView-Eldo4kFi.js
vendored
@@ -1 +0,0 @@
|
|||||||
import{E as V}from"./index-BNsCyG4A.js";import{E as B}from"./el-table-C7Zx0Bmo.js";import{d as D,G as T,c as $,e as _,w as o,g as C,y as m,o as v,s as a,q as i,x as b,aa as L,n as c,v as N,A as z}from"./index-Ds9AETjS.js";import{E as M}from"./index-DqJTjrY3.js";/* empty css */import"./el-checkbox-pV3o_8iy.js";import{D as y}from"./DataTablePage-DzEUb2tf.js";import{g as A,d as G}from"./eval-RqHG_SzR.js";import{_ as P}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./el-scrollbar-CF42LNzg.js";import"./index-BSbCLbaz.js";import"./el-popper-D1tByNLb.js";import"./index-sWKLi7bH.js";import"./_baseClone-CZxxnGCv.js";import"./_baseIteratee-82gB5Yov.js";import"./castArray-BWI4UxBy.js";import"./debounce-D6mJVDYC.js";import"./toNumber-DrayciwT.js";import"./raf-zQ00nuMI.js";import"./validator-D1MwuZXj.js";import"./index-jBnYrPdD.js";import"./clamp-CyhADbdq.js";import"./index-RtwaMutE.js";import"./scroll-DVmMv0d9.js";import"./index-h5FkzAP_.js";import"./index-B0if-AHo.js";import"./omit-BzI1MV_K.js";import"./el-card-D86TXgMv.js";import"./el-pagination-C9R_oTJ1.js";import"./el-select-BD70by95.js";import"./vnode-CBiWHFw7.js";import"./index-CQlF36Lk.js";import"./index-ByhS8A1d.js";import"./directive-B1cwmVOz.js";import"./el-tooltip-l0sNRNKZ.js";const R={class:"eval-page"},S={class:"capsule-tabs"},q={class:"capsule-tabs"},I=D({__name:"EvalView",setup(Q){const f=N(),r=m("tasks"),d=m(!1),n=m([]),w=m([{rank:1,name:"GPT-4",score:92.5},{rank:2,name:"Claude-3",score:90.1},{rank:3,name:"Qwen-Max",score:85.3}]);async function u(){d.value=!0;try{n.value=await A()||[]}catch{n.value=[]}finally{d.value=!1}}async function k(s){await M.confirm("确定要删除这个评测任务吗?","确认删除",{type:"warning"}),await G(s.id),n.value=n.value.filter(e=>e.id!==s.id),z.success("删除成功"),await u()}function E(){f.push("/model-eval/create")}function p(s){return r.value===s}function x(s){f.push({name:"model-eval-detail",params:{id:s.id}})}return T(()=>{u()}),(s,e)=>{const t=B,g=V;return v(),$("div",R,[r.value==="tasks"?(v(),_(y,{key:0,title:"",data:n.value,loading:d.value,"delete-fn":k,"create-text":"创建评测任务",onCreate:E,"row-key":"id",onRefresh:u},{title:o(()=>[i("div",S,[i("button",{class:c(["capsule-tab-item",{active:p("tasks")}]),onClick:e[0]||(e[0]=l=>r.value="tasks")},"评测任务",2),i("button",{class:c(["capsule-tab-item",{active:p("leaderboard")}]),onClick:e[1]||(e[1]=l=>r.value="leaderboard")},"排行榜",2)])]),columns:o(()=>[a(t,{label:"任务名称",prop:"eval_task_name",align:"center"}),a(t,{label:"评测模型",prop:"model_name",align:"center"}),a(t,{label:"数据集",prop:"dataset",align:"center"}),a(t,{label:"指标",prop:"metric",align:"center"}),a(t,{label:"评分",prop:"score",width:"100",align:"center"}),a(t,{label:"状态",prop:"status",width:"100",align:"center"}),a(t,{label:"创建时间",align:"center",width:"180"},{default:o(({row:l})=>[b(L(l.create_time?new Date(l.create_time).toLocaleString("zh-CN"):"-"),1)]),_:1})]),actions:o(({row:l})=>[a(g,{link:"",type:"primary",size:"small",onClick:h=>x(l)},{default:o(()=>[...e[4]||(e[4]=[i("i",{class:"fa fa-file-text-o",style:{"margin-right":"4px"}},null,-1),b("详情 ",-1)])]),_:1},8,["onClick"]),a(g,{link:"",type:"danger",size:"small",onClick:h=>k(l)},{default:o(()=>[...e[5]||(e[5]=[i("i",{class:"fa fa-trash-o",style:{"margin-right":"4px"}},null,-1),b("删除 ",-1)])]),_:1},8,["onClick"])]),_:1},8,["data","loading"])):C("",!0),r.value==="leaderboard"?(v(),_(y,{key:1,title:"",data:w.value,"row-key":"rank"},{title:o(()=>[i("div",q,[i("button",{class:c(["capsule-tab-item",{active:p("tasks")}]),onClick:e[2]||(e[2]=l=>r.value="tasks")},"评测任务",2),i("button",{class:c(["capsule-tab-item",{active:p("leaderboard")}]),onClick:e[3]||(e[3]=l=>r.value="leaderboard")},"排行榜",2)])]),columns:o(()=>[a(t,{label:"排名",prop:"rank",width:"80",align:"center"}),a(t,{label:"模型名称",prop:"name",align:"center"}),a(t,{label:"综合评分",prop:"score",align:"center"})]),_:1},8,["data"])):C("",!0)])}}}),xe=P(I,[["__scopeId","data-v-78ab2fa4"]]);export{xe as default};
|
|
||||||
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/FineTuneListView-BE5afp8L.js
vendored
Normal file
1
frontend/dist/assets/FineTuneListView-BE5afp8L.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
import{_ as s}from"./_plugin-vue_export-helper-DlAUqK2U.js";import{o as t,c,q as o}from"./index-Ds9AETjS.js";const n={},r={class:"simple-page"};function a(_,e){return t(),c("section",r,[...e[0]||(e[0]=[o("h1",null,"使用文档",-1),o("p",null,"第一版系统已接入后端、数据集、模型、微调任务、算力节点和训练日志主链路。",-1)])])}const p=s(n,[["render",a],["__scopeId","data-v-87551c13"]]);export{p as default};
|
import{_ as s}from"./_plugin-vue_export-helper-DlAUqK2U.js";import{o as t,c,q as o}from"./index-D5veDkuG.js";const n={},r={class:"simple-page"};function a(_,e){return t(),c("section",r,[...e[0]||(e[0]=[o("h1",null,"使用文档",-1),o("p",null,"第一版系统已接入后端、数据集、模型、微调任务、算力节点和训练日志主链路。",-1)])])}const p=s(n,[["render",a],["__scopeId","data-v-87551c13"]]);export{p as default};
|
||||||
1
frontend/dist/assets/HardwareView-BLjIy19_.js
vendored
Normal file
1
frontend/dist/assets/HardwareView-BLjIy19_.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
import{E as T}from"./el-alert-CoSdHkeK.js";import{a as A,E as D}from"./el-form-item-Bq0n_p5a.js";import{E as L}from"./index-h5FkzAP_.js";import{E as j}from"./index-BNsCyG4A.js";import{E as z,b as H,a as J}from"./el-select-BD70by95.js";import{E as K}from"./el-divider-a924dciw.js";import{d as Q,G as W,e as u,w as o,at as X,y as i,z as Y,o as s,s as a,x as b,c as v,ad as k,M as w,g as Z,j as p,A as B,v as ee}from"./index-Ds9AETjS.js";import"./el-scrollbar-CF42LNzg.js";import"./el-popper-D1tByNLb.js";/* empty css */import{P as te}from"./PageCard-CV3p14-x.js";import{g as le,a as ae}from"./model-cWd9CDtT.js";import"./vnode-CBiWHFw7.js";import"./castArray-BWI4UxBy.js";import"./_baseClone-CZxxnGCv.js";import"./raf-zQ00nuMI.js";import"./index-B0if-AHo.js";import"./index-BSbCLbaz.js";import"./index-sWKLi7bH.js";import"./index-CQlF36Lk.js";import"./index-ByhS8A1d.js";import"./scroll-DVmMv0d9.js";import"./clamp-CyhADbdq.js";import"./toNumber-DrayciwT.js";import"./_baseIteratee-82gB5Yov.js";import"./el-card-D86TXgMv.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";const Ie=Q({__name:"InferenceCreateView",setup(oe){const E=ee(),f=i(),c=i(!1),m=i(""),V=i([]),h=i([]),_=i([]),x=p(()=>V.value.map(t=>({key:`db-${t.id}`,id:t.id,name:t.name,source:"database",model_path:t.path||""}))),M=p(()=>h.value.map(t=>({key:`trained-${t.id}`,id:t.id,name:t.name,source:"trained",model_path:t.merged_path||t.base_model_path||"",merged:t.merged,merging:t.merging,disabled:t.merged===!1}))),G=p(()=>{const t={};for(const e of[...x.value,...M.value])t[e.key]=e;return t}),n=Y({name:"",description:"",model_key:"",gpu_id:0}),I={name:[{required:!0,message:"请输入推理名称",trigger:"blur"}],model_key:[{required:!0,message:"请选择模型",trigger:"change"}]},O=p(()=>G.value[n.model_key]);async function S(){f.value&&await f.value.validate(async t=>{if(!t)return;const e=O.value;if(!e){B.warning("请选择模型");return}c.value=!0,m.value="正在启动模型服务...";try{await new Promise(r=>setTimeout(r,1200)),B.success("模型已启动"),E.push({path:"/model-inference/chat/mock",query:{model:e.name}})}finally{c.value=!1,m.value=""}})}function $(){E.back()}async function q(){try{const[t,e,r]=await Promise.all([le(),ae(),X()]);V.value=t||[],h.value=(e==null?void 0:e.models)||[],_.value=(r==null?void 0:r.gpu)||[],_.value.length>0&&(n.gpu_id=0)}catch{}}return W(q),(t,e)=>{const r=L,d=A,F=K,g=J,C=H,P=z,N=T,U=j,R=D;return s(),u(te,{title:"新建推理"},{default:o(()=>[a(R,{ref_key:"formRef",ref:f,model:n,rules:I,"label-width":"100px"},{default:o(()=>[a(d,{label:"推理名称",prop:"name"},{default:o(()=>[a(r,{modelValue:n.name,"onUpdate:modelValue":e[0]||(e[0]=l=>n.name=l),placeholder:"请输入推理名称",maxlength:"50","show-word-limit":"",style:{"max-width":"400px"}},null,8,["modelValue"])]),_:1}),a(d,{label:"描述"},{default:o(()=>[a(r,{modelValue:n.description,"onUpdate:modelValue":e[1]||(e[1]=l=>n.description=l),type:"textarea",rows:2,maxlength:"200","show-word-limit":"",style:{"max-width":"400px"}},null,8,["modelValue"])]),_:1}),a(F,{"content-position":"left"},{default:o(()=>[...e[4]||(e[4]=[b("选择模型",-1)])]),_:1}),a(d,{label:"选择模型",prop:"model_key"},{default:o(()=>[a(P,{modelValue:n.model_key,"onUpdate:modelValue":e[2]||(e[2]=l=>n.model_key=l),placeholder:"请选择模型",filterable:"",style:{width:"400px"}},{default:o(()=>[a(C,{label:"本地模型"},{default:o(()=>[(s(!0),v(w,null,k(x.value,l=>(s(),u(g,{key:l.key,label:l.name,value:l.key},null,8,["label","value"]))),128))]),_:1}),a(C,{label:"已训练模型"},{default:o(()=>[(s(!0),v(w,null,k(M.value,l=>(s(),u(g,{key:l.key,label:l.name+(l.disabled?"(未合并)":""),value:l.key,disabled:l.disabled},null,8,["label","value","disabled"]))),128))]),_:1})]),_:1},8,["modelValue"])]),_:1}),a(d,{label:"GPU"},{default:o(()=>[a(P,{modelValue:n.gpu_id,"onUpdate:modelValue":e[3]||(e[3]=l=>n.gpu_id=l),style:{width:"400px"}},{default:o(()=>[(s(!0),v(w,null,k(_.value,(l,y)=>(s(),u(g,{key:y,label:`${l.name} (GPU${y})`,value:y},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1}),m.value?(s(),u(d,{key:0,label:"启动状态"},{default:o(()=>[a(N,{title:m.value,type:"info",closable:!1,"show-icon":""},null,8,["title"])]),_:1})):Z("",!0),a(d,null,{default:o(()=>[a(U,{type:"primary",loading:c.value,onClick:S},{default:o(()=>[...e[5]||(e[5]=[b("开始推理",-1)])]),_:1},8,["loading"]),a(U,{onClick:$},{default:o(()=>[...e[6]||(e[6]=[b("取消",-1)])]),_:1})]),_:1})]),_:1},8,["model"])]),_:1})}}});export{Ie as default};
|
|
||||||
1
frontend/dist/assets/InferenceCreateView-DEMDPZG-.js
vendored
Normal file
1
frontend/dist/assets/InferenceCreateView-DEMDPZG-.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{E as T}from"./el-alert-DhjRMRnp.js";import{a as A,E as D}from"./el-form-item-rya6onmd.js";import{E as L}from"./index-DbBHj60R.js";import{E as j}from"./index-DxH5-_w6.js";import{E as z,b as H,a as J}from"./el-select-BQaRHu3V.js";import{E as K}from"./el-divider-CsS5KGIs.js";import{d as Q,G as W,e as i,w as o,at as X,y as u,z as Y,o as s,s as a,x as b,c as v,ad as k,M as w,g as Z,j as p,A as B,v as ee}from"./index-D5veDkuG.js";import"./el-scrollbar-GFOwBTUz.js";import"./el-popper-DDJTxf27.js";import"./el-tag-C2d1Zfbn.js";import{P as te}from"./PageCard-DFGMy1ik.js";import{g as le,a as ae}from"./model-Do_d1nlo.js";import"./vnode-CYZHqsIy.js";import"./castArray-BDIbf8ih.js";import"./_baseClone-BuqHm105.js";import"./raf-Dsox5IbR.js";import"./index-DDY6aSpq.js";import"./index-BT1A2I5c.js";import"./index-DmAaj_YO.js";import"./index-DbuKXJ7V.js";import"./scroll-DswMXu1y.js";import"./clamp-Ddg8_Yc2.js";import"./toNumber-a8w_gs0u.js";import"./_baseIteratee-N-G7GeYE.js";import"./el-card-DzhpMhms.js";import"./_plugin-vue_export-helper-DlAUqK2U.js";const Ge=Q({__name:"InferenceCreateView",setup(oe){const E=ee(),f=u(),c=u(!1),m=u(""),V=u([]),h=u([]),_=u([]),x=p(()=>V.value.map(t=>({key:`db-${t.id}`,id:t.id,name:t.name,source:"database",model_path:t.path||""}))),M=p(()=>h.value.map(t=>({key:`trained-${t.id}`,id:t.id,name:t.name,source:"trained",model_path:t.merged_path||t.base_model_path||"",merged:t.merged,merging:t.merging,disabled:t.merged===!1}))),G=p(()=>{const t={};for(const e of[...x.value,...M.value])t[e.key]=e;return t}),n=Y({name:"",description:"",model_key:"",gpu_id:0}),I={name:[{required:!0,message:"请输入推理名称",trigger:"blur"}],model_key:[{required:!0,message:"请选择模型",trigger:"change"}]},O=p(()=>G.value[n.model_key]);async function S(){f.value&&await f.value.validate(async t=>{if(!t)return;const e=O.value;if(!e){B.warning("请选择模型");return}c.value=!0,m.value="正在启动模型服务...";try{await new Promise(r=>setTimeout(r,1200)),B.success("模型已启动"),E.push({path:"/model-inference/chat/mock",query:{model:e.name}})}finally{c.value=!1,m.value=""}})}function $(){E.back()}async function q(){try{const[t,e,r]=await Promise.all([le(),ae(),X()]);V.value=t||[],h.value=(e==null?void 0:e.models)||[],_.value=(r==null?void 0:r.gpu)||[],_.value.length>0&&(n.gpu_id=0)}catch{}}return W(q),(t,e)=>{const r=L,d=A,F=K,g=J,C=H,P=z,N=T,U=j,R=D;return s(),i(te,{title:"新建推理"},{default:o(()=>[a(R,{ref_key:"formRef",ref:f,model:n,rules:I,"label-width":"100px"},{default:o(()=>[a(d,{label:"推理名称",prop:"name"},{default:o(()=>[a(r,{modelValue:n.name,"onUpdate:modelValue":e[0]||(e[0]=l=>n.name=l),placeholder:"请输入推理名称",maxlength:"50","show-word-limit":"",style:{"max-width":"400px"}},null,8,["modelValue"])]),_:1}),a(d,{label:"描述"},{default:o(()=>[a(r,{modelValue:n.description,"onUpdate:modelValue":e[1]||(e[1]=l=>n.description=l),type:"textarea",rows:2,maxlength:"200","show-word-limit":"",style:{"max-width":"400px"}},null,8,["modelValue"])]),_:1}),a(F,{"content-position":"left"},{default:o(()=>[...e[4]||(e[4]=[b("选择模型",-1)])]),_:1}),a(d,{label:"选择模型",prop:"model_key"},{default:o(()=>[a(P,{modelValue:n.model_key,"onUpdate:modelValue":e[2]||(e[2]=l=>n.model_key=l),placeholder:"请选择模型",filterable:"",style:{width:"400px"}},{default:o(()=>[a(C,{label:"本地模型"},{default:o(()=>[(s(!0),v(w,null,k(x.value,l=>(s(),i(g,{key:l.key,label:l.name,value:l.key},null,8,["label","value"]))),128))]),_:1}),a(C,{label:"已训练模型"},{default:o(()=>[(s(!0),v(w,null,k(M.value,l=>(s(),i(g,{key:l.key,label:l.name+(l.disabled?"(未合并)":""),value:l.key,disabled:l.disabled},null,8,["label","value","disabled"]))),128))]),_:1})]),_:1},8,["modelValue"])]),_:1}),a(d,{label:"GPU"},{default:o(()=>[a(P,{modelValue:n.gpu_id,"onUpdate:modelValue":e[3]||(e[3]=l=>n.gpu_id=l),style:{width:"400px"}},{default:o(()=>[(s(!0),v(w,null,k(_.value,(l,y)=>(s(),i(g,{key:y,label:`${l.name} (GPU${y})`,value:y},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1}),m.value?(s(),i(d,{key:0,label:"启动状态"},{default:o(()=>[a(N,{title:m.value,type:"info",closable:!1,"show-icon":""},null,8,["title"])]),_:1})):Z("",!0),a(d,null,{default:o(()=>[a(U,{type:"primary",loading:c.value,onClick:S},{default:o(()=>[...e[5]||(e[5]=[b("开始推理",-1)])]),_:1},8,["loading"]),a(U,{onClick:$},{default:o(()=>[...e[6]||(e[6]=[b("取消",-1)])]),_:1})]),_:1})]),_:1},8,["model"])]),_:1})}}});export{Ge as default};
|
||||||
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/InferenceListView-DR5IRS2u.js
vendored
Normal file
1
frontend/dist/assets/InferenceListView-DR5IRS2u.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
import{E as $,a as q}from"./el-form-item-Bq0n_p5a.js";import{E as z}from"./index-h5FkzAP_.js";import{u as K,E as P}from"./index-BNsCyG4A.js";import{E as R}from"./el-checkbox-pV3o_8iy.js";import{b as T,i as U,d as N,u as A,a as D,o as u,c as k,e as h,w as t,r as M,f as w,E as j,g,n as E,h as V,j as b,k as C,l as G,m as H,p as J,q as n,s,t as O,v as Q,x as y,y as B,z as W,A as X}from"./index-Ds9AETjS.js";import{_ as S}from"./logo-CxXS7KxG.js";import{_ as Y}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./castArray-BWI4UxBy.js";import"./_baseClone-CZxxnGCv.js";import"./raf-zQ00nuMI.js";import"./index-B0if-AHo.js";import"./index-BSbCLbaz.js";import"./omit-BzI1MV_K.js";const Z=T({type:{type:String,values:["primary","success","warning","info","danger","default"],default:void 0},underline:{type:[Boolean,String],values:[!0,!1,"always","never","hover"],default:void 0},disabled:Boolean,href:{type:String,default:""},target:{type:String,default:"_self"},icon:{type:U}}),ee={click:l=>l instanceof MouseEvent},ae=["href","target"];var ne=N({name:"ElLink",__name:"link",props:Z,emits:ee,setup(l,{emit:m}){const o=l,i=m,r=A("link");K({scope:"el-link",from:"The underline option (boolean)",replacement:"'always' | 'hover' | 'never'",version:"3.0.0",ref:"https://element-plus.org/en-US/component/link.html#underline"},b(()=>C(o.underline)));const a=D("link"),c=b(()=>{var e;return[a.b(),a.m(o.type??((e=r.value)==null?void 0:e.type)??"default"),a.is("disabled",o.disabled),a.is("underline",d.value==="always"),a.is("hover-underline",d.value==="hover"&&!o.disabled)]}),d=b(()=>{var e;return C(o.underline)?o.underline?"hover":"never":o.underline??((e=r.value)==null?void 0:e.underline)??"hover"});function p(e){o.disabled||i("click",e)}return(e,f)=>(u(),k("a",{class:E(c.value),href:l.disabled||!l.href?void 0:l.href,target:l.disabled||!l.href?void 0:l.target,onClick:p},[l.icon?(u(),h(w(j),{key:0},{default:t(()=>[(u(),h(M(l.icon)))]),_:1})):g("v-if",!0),e.$slots.default?(u(),k("span",{key:1,class:E(w(a).e("inner"))},[V(e.$slots,"default")],2)):g("v-if",!0),e.$slots.icon?V(e.$slots,"icon",{key:2}):g("v-if",!0)],10,ae))}}),le=ne;const oe=G(le),se={class:"login-page"},te={class:"login-panel"},re={class:"login-form-wrap"},ie={class:"login-options"},de=N({__name:"LoginView",setup(l){const m=Q(),o=H(),i=B(),r=B(!1),a=W({username:"",password:""}),c={username:[{required:!0,message:"请输入账号",trigger:"blur"}],password:[{required:!0,message:"请输入密码",trigger:"blur"}]};async function d(){i.value&&await i.value.validate(async p=>{if(p){r.value=!0;try{await o.login(a.username,a.password),X.success("登录成功"),m.push("/dashboard")}catch{}finally{r.value=!1}}})}return(p,e)=>{const f=z,_=q,x=R,F=oe,I=P,L=$;return u(),k("div",se,[e[8]||(e[8]=J('<section class="login-visual" aria-labelledby="platform-title" data-v-713185bd><div class="login-visual-content" data-v-713185bd><div class="login-visual-copy" data-v-713185bd><h1 id="platform-title" data-v-713185bd>远光软件微调平台</h1><p data-v-713185bd>大模型微调、评测与推理的一体化工作台</p></div><div class="login-visual-footer" aria-label="平台核心能力" data-v-713185bd><span data-v-713185bd>数据准备</span><span aria-hidden="true" data-v-713185bd>·</span><span data-v-713185bd>模型训练</span><span aria-hidden="true" data-v-713185bd>·</span><span data-v-713185bd>效果评测</span></div></div></section>',1)),n("main",te,[e[6]||(e[6]=n("div",{class:"brand-lockup",role:"img","aria-label":"远光软件"},[n("span",{class:"brand-logo-crop brand-logo-crop-mark","aria-hidden":"true"},[n("img",{src:S,alt:""})]),n("span",{class:"brand-logo-crop brand-logo-crop-wordmark","aria-hidden":"true"},[n("img",{src:S,alt:""})])],-1)),n("div",re,[e[5]||(e[5]=n("div",{class:"login-heading"},[n("span",null,"账号登录"),n("h2",null,"欢迎回来"),n("p",null,"登录后继续使用微调平台")],-1)),s(L,{ref_key:"loginFormRef",ref:i,model:a,rules:c,"label-position":"top",size:"large",onKeyup:O(d,["enter"])},{default:t(()=>[s(_,{label:"账号",prop:"username"},{default:t(()=>[s(f,{modelValue:a.username,"onUpdate:modelValue":e[0]||(e[0]=v=>a.username=v),placeholder:"请输入账号",clearable:""},null,8,["modelValue"])]),_:1}),s(_,{label:"密码",prop:"password"},{default:t(()=>[s(f,{modelValue:a.password,"onUpdate:modelValue":e[1]||(e[1]=v=>a.password=v),type:"password",placeholder:"请输入密码","show-password":""},null,8,["modelValue"])]),_:1}),n("div",ie,[s(x,null,{default:t(()=>[...e[2]||(e[2]=[y("记住密码",-1)])]),_:1}),s(F,{type:"primary",underline:!1},{default:t(()=>[...e[3]||(e[3]=[y("忘记密码?",-1)])]),_:1})]),s(I,{type:"primary",class:"login-btn",loading:r.value,onClick:d},{default:t(()=>[...e[4]||(e[4]=[y(" 登录 ",-1)])]),_:1},8,["loading"])]),_:1},8,["model"])]),e[7]||(e[7]=n("footer",null,"© 2026 远光软件",-1))])])}}}),Ee=Y(de,[["__scopeId","data-v-713185bd"]]);export{Ee as default};
|
import{E as $,a as q}from"./el-form-item-rya6onmd.js";import{E as z}from"./index-DbBHj60R.js";import{u as K,E as P}from"./index-DxH5-_w6.js";import{E as R}from"./el-checkbox-D6iLoXkw.js";import{b as T,i as U,d as N,u as A,a as D,o as u,c as k,e as h,w as t,r as M,f as w,E as j,g,n as E,h as V,j as b,k as C,l as G,m as H,p as J,q as n,s,t as O,v as Q,x as y,y as B,z as W,A as X}from"./index-D5veDkuG.js";import{_ as S}from"./logo-CxXS7KxG.js";import{_ as Y}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./castArray-BDIbf8ih.js";import"./_baseClone-BuqHm105.js";import"./raf-Dsox5IbR.js";import"./index-DDY6aSpq.js";import"./index-BT1A2I5c.js";import"./omit-DxQEBv1Z.js";const Z=T({type:{type:String,values:["primary","success","warning","info","danger","default"],default:void 0},underline:{type:[Boolean,String],values:[!0,!1,"always","never","hover"],default:void 0},disabled:Boolean,href:{type:String,default:""},target:{type:String,default:"_self"},icon:{type:U}}),ee={click:l=>l instanceof MouseEvent},ae=["href","target"];var ne=N({name:"ElLink",__name:"link",props:Z,emits:ee,setup(l,{emit:m}){const o=l,i=m,r=A("link");K({scope:"el-link",from:"The underline option (boolean)",replacement:"'always' | 'hover' | 'never'",version:"3.0.0",ref:"https://element-plus.org/en-US/component/link.html#underline"},b(()=>C(o.underline)));const a=D("link"),c=b(()=>{var e;return[a.b(),a.m(o.type??((e=r.value)==null?void 0:e.type)??"default"),a.is("disabled",o.disabled),a.is("underline",d.value==="always"),a.is("hover-underline",d.value==="hover"&&!o.disabled)]}),d=b(()=>{var e;return C(o.underline)?o.underline?"hover":"never":o.underline??((e=r.value)==null?void 0:e.underline)??"hover"});function p(e){o.disabled||i("click",e)}return(e,f)=>(u(),k("a",{class:E(c.value),href:l.disabled||!l.href?void 0:l.href,target:l.disabled||!l.href?void 0:l.target,onClick:p},[l.icon?(u(),h(w(j),{key:0},{default:t(()=>[(u(),h(M(l.icon)))]),_:1})):g("v-if",!0),e.$slots.default?(u(),k("span",{key:1,class:E(w(a).e("inner"))},[V(e.$slots,"default")],2)):g("v-if",!0),e.$slots.icon?V(e.$slots,"icon",{key:2}):g("v-if",!0)],10,ae))}}),le=ne;const oe=G(le),se={class:"login-page"},te={class:"login-panel"},re={class:"login-form-wrap"},ie={class:"login-options"},de=N({__name:"LoginView",setup(l){const m=Q(),o=H(),i=B(),r=B(!1),a=W({username:"",password:""}),c={username:[{required:!0,message:"请输入账号",trigger:"blur"}],password:[{required:!0,message:"请输入密码",trigger:"blur"}]};async function d(){i.value&&await i.value.validate(async p=>{if(p){r.value=!0;try{await o.login(a.username,a.password),X.success("登录成功"),m.push("/dashboard")}catch{}finally{r.value=!1}}})}return(p,e)=>{const f=z,_=q,x=R,F=oe,I=P,L=$;return u(),k("div",se,[e[8]||(e[8]=J('<section class="login-visual" aria-labelledby="platform-title" data-v-713185bd><div class="login-visual-content" data-v-713185bd><div class="login-visual-copy" data-v-713185bd><h1 id="platform-title" data-v-713185bd>远光软件微调平台</h1><p data-v-713185bd>大模型微调、评测与推理的一体化工作台</p></div><div class="login-visual-footer" aria-label="平台核心能力" data-v-713185bd><span data-v-713185bd>数据准备</span><span aria-hidden="true" data-v-713185bd>·</span><span data-v-713185bd>模型训练</span><span aria-hidden="true" data-v-713185bd>·</span><span data-v-713185bd>效果评测</span></div></div></section>',1)),n("main",te,[e[6]||(e[6]=n("div",{class:"brand-lockup",role:"img","aria-label":"远光软件"},[n("span",{class:"brand-logo-crop brand-logo-crop-mark","aria-hidden":"true"},[n("img",{src:S,alt:""})]),n("span",{class:"brand-logo-crop brand-logo-crop-wordmark","aria-hidden":"true"},[n("img",{src:S,alt:""})])],-1)),n("div",re,[e[5]||(e[5]=n("div",{class:"login-heading"},[n("span",null,"账号登录"),n("h2",null,"欢迎回来"),n("p",null,"登录后继续使用微调平台")],-1)),s(L,{ref_key:"loginFormRef",ref:i,model:a,rules:c,"label-position":"top",size:"large",onKeyup:O(d,["enter"])},{default:t(()=>[s(_,{label:"账号",prop:"username"},{default:t(()=>[s(f,{modelValue:a.username,"onUpdate:modelValue":e[0]||(e[0]=v=>a.username=v),placeholder:"请输入账号",clearable:""},null,8,["modelValue"])]),_:1}),s(_,{label:"密码",prop:"password"},{default:t(()=>[s(f,{modelValue:a.password,"onUpdate:modelValue":e[1]||(e[1]=v=>a.password=v),type:"password",placeholder:"请输入密码","show-password":""},null,8,["modelValue"])]),_:1}),n("div",ie,[s(x,null,{default:t(()=>[...e[2]||(e[2]=[y("记住密码",-1)])]),_:1}),s(F,{type:"primary",underline:!1},{default:t(()=>[...e[3]||(e[3]=[y("忘记密码?",-1)])]),_:1})]),s(I,{type:"primary",class:"login-btn",loading:r.value,onClick:d},{default:t(()=>[...e[4]||(e[4]=[y(" 登录 ",-1)])]),_:1},8,["loading"])]),_:1},8,["model"])]),e[7]||(e[7]=n("footer",null,"© 2026 远光软件",-1))])])}}}),Ee=Y(de,[["__scopeId","data-v-713185bd"]]);export{Ee as default};
|
||||||
3
frontend/dist/assets/LogsView-CrOH_Cuj.js
vendored
Normal file
3
frontend/dist/assets/LogsView-CrOH_Cuj.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
frontend/dist/assets/LogsView-CxeOiep3.js
vendored
3
frontend/dist/assets/LogsView-CxeOiep3.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/MergeWeightsView-D-_UJ_G8.js
vendored
Normal file
1
frontend/dist/assets/MergeWeightsView-D-_UJ_G8.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{E as A}from"./el-alert-DhjRMRnp.js";import{a as N,E as T}from"./el-form-item-rya6onmd.js";import{E as B}from"./index-DbBHj60R.js";import{E as I}from"./index-DxH5-_w6.js";import{E as R}from"./el-tag-C2d1Zfbn.js";import{d as P,z as q,G as D,e as d,w as o,j as y,y as E,o as n,s as t,f as F,ar as U,x as m,aa as W,c as j,g as z,A as M,v as G,ac as H}from"./index-D5veDkuG.js";import{P as L}from"./PageCard-DFGMy1ik.js";import{a as O,m as S}from"./model-Do_d1nlo.js";import{_ as J}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./vnode-CYZHqsIy.js";import"./castArray-BDIbf8ih.js";import"./_baseClone-BuqHm105.js";import"./raf-Dsox5IbR.js";import"./index-DDY6aSpq.js";import"./index-BT1A2I5c.js";import"./el-card-DzhpMhms.js";const K={key:0,class:"model-status"},Q=P({__name:"MergeWeightsView",setup(X){const v=H(),b=G(),s=E(!1),u=y(()=>v.query.model||""),w=y(()=>v.query.method||"lora"),i=E([]),_=y(()=>i.value.find(l=>l.name===u.value)),a=q({model_name:u.value,train_method:w.value,base_model_path:""});async function V(){try{const l=await O();i.value=(l==null?void 0:l.models)||[];const e=i.value.find(p=>p.name===u.value);a.base_model_path=(e==null?void 0:e.base_model_path)||""}catch{}}async function k(){if(!a.model_name||!a.base_model_path){M.warning("缺少模型信息");return}s.value=!0;try{await S({model_name:a.model_name,train_method:a.train_method,base_model_path:a.base_model_path}),M.success("合并成功"),b.push("/model-manage")}catch{}finally{s.value=!1}}function x(){b.back()}return D(V),(l,e)=>{const p=A,f=B,r=N,h=I,C=T,c=R;return n(),d(L,{title:"合并权重"},{default:o(()=>[t(p,{type:"info",closable:!1,"show-icon":"",title:"将 LoRA 适配器权重合并到基座模型,合并后可直接用于推理部署。",style:{"margin-bottom":"20px"}}),t(C,{"label-width":"120px",style:{"max-width":"600px"}},{default:o(()=>[t(r,{label:"模型名称"},{default:o(()=>[t(f,{modelValue:a.model_name,"onUpdate:modelValue":e[0]||(e[0]=g=>a.model_name=g),disabled:""},null,8,["modelValue"])]),_:1}),t(r,{label:"训练方法"},{default:o(()=>[t(f,{"model-value":F(U)[a.train_method]||a.train_method,disabled:""},null,8,["model-value"])]),_:1}),t(r,{label:"基座模型路径"},{default:o(()=>[t(f,{modelValue:a.base_model_path,"onUpdate:modelValue":e[1]||(e[1]=g=>a.base_model_path=g),placeholder:"基座模型路径"},null,8,["modelValue"])]),_:1}),t(r,null,{default:o(()=>[t(h,{type:"primary",loading:s.value,onClick:k},{default:o(()=>[m(W(s.value?"合并中...":"开始合并"),1)]),_:1},8,["loading"]),t(h,{onClick:x},{default:o(()=>[...e[2]||(e[2]=[m("取消",-1)])]),_:1})]),_:1})]),_:1}),_.value?(n(),j("div",K,[_.value.merging?(n(),d(c,{key:0,type:"warning"},{default:o(()=>[...e[3]||(e[3]=[m("合并中",-1)])]),_:1})):_.value.merged?(n(),d(c,{key:1,type:"success"},{default:o(()=>[...e[4]||(e[4]=[m("已合并",-1)])]),_:1})):(n(),d(c,{key:2,type:"info"},{default:o(()=>[...e[5]||(e[5]=[m("未合并",-1)])]),_:1}))])):z("",!0)]),_:1})}}}),pe=J(Q,[["__scopeId","data-v-2e30aa99"]]);export{pe as default};
|
||||||
@@ -1 +0,0 @@
|
|||||||
import{E as A}from"./el-alert-CoSdHkeK.js";import{a as N,E as T}from"./el-form-item-Bq0n_p5a.js";import{E as B}from"./index-h5FkzAP_.js";import{E as I}from"./index-BNsCyG4A.js";import{E as R}from"./index-ByhS8A1d.js";import{d as P,z as q,G as D,e as d,w as o,j as y,y as E,o as n,s as t,f as F,ar as U,x as m,aa as W,c as j,g as z,A as M,v as G,ac as H}from"./index-Ds9AETjS.js";/* empty css */import{P as L}from"./PageCard-CV3p14-x.js";import{a as O,m as S}from"./model-cWd9CDtT.js";import{_ as J}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./vnode-CBiWHFw7.js";import"./castArray-BWI4UxBy.js";import"./_baseClone-CZxxnGCv.js";import"./raf-zQ00nuMI.js";import"./index-B0if-AHo.js";import"./index-BSbCLbaz.js";import"./el-card-D86TXgMv.js";const K={key:0,class:"model-status"},Q=P({__name:"MergeWeightsView",setup(X){const v=H(),b=G(),s=E(!1),u=y(()=>v.query.model||""),w=y(()=>v.query.method||"lora"),i=E([]),_=y(()=>i.value.find(l=>l.name===u.value)),a=q({model_name:u.value,train_method:w.value,base_model_path:""});async function V(){try{const l=await O();i.value=(l==null?void 0:l.models)||[];const e=i.value.find(p=>p.name===u.value);a.base_model_path=(e==null?void 0:e.base_model_path)||""}catch{}}async function k(){if(!a.model_name||!a.base_model_path){M.warning("缺少模型信息");return}s.value=!0;try{await S({model_name:a.model_name,train_method:a.train_method,base_model_path:a.base_model_path}),M.success("合并成功"),b.push("/model-manage")}catch{}finally{s.value=!1}}function x(){b.back()}return D(V),(l,e)=>{const p=A,f=B,r=N,h=I,C=T,c=R;return n(),d(L,{title:"合并权重"},{default:o(()=>[t(p,{type:"info",closable:!1,"show-icon":"",title:"将 LoRA 适配器权重合并到基座模型,合并后可直接用于推理部署。",style:{"margin-bottom":"20px"}}),t(C,{"label-width":"120px",style:{"max-width":"600px"}},{default:o(()=>[t(r,{label:"模型名称"},{default:o(()=>[t(f,{modelValue:a.model_name,"onUpdate:modelValue":e[0]||(e[0]=g=>a.model_name=g),disabled:""},null,8,["modelValue"])]),_:1}),t(r,{label:"训练方法"},{default:o(()=>[t(f,{"model-value":F(U)[a.train_method]||a.train_method,disabled:""},null,8,["model-value"])]),_:1}),t(r,{label:"基座模型路径"},{default:o(()=>[t(f,{modelValue:a.base_model_path,"onUpdate:modelValue":e[1]||(e[1]=g=>a.base_model_path=g),placeholder:"基座模型路径"},null,8,["modelValue"])]),_:1}),t(r,null,{default:o(()=>[t(h,{type:"primary",loading:s.value,onClick:k},{default:o(()=>[m(W(s.value?"合并中...":"开始合并"),1)]),_:1},8,["loading"]),t(h,{onClick:x},{default:o(()=>[...e[2]||(e[2]=[m("取消",-1)])]),_:1})]),_:1})]),_:1}),_.value?(n(),j("div",K,[_.value.merging?(n(),d(c,{key:0,type:"warning"},{default:o(()=>[...e[3]||(e[3]=[m("合并中",-1)])]),_:1})):_.value.merged?(n(),d(c,{key:1,type:"success"},{default:o(()=>[...e[4]||(e[4]=[m("已合并",-1)])]),_:1})):(n(),d(c,{key:2,type:"info"},{default:o(()=>[...e[5]||(e[5]=[m("未合并",-1)])]),_:1}))])):z("",!0)]),_:1})}}}),fe=J(Q,[["__scopeId","data-v-2e30aa99"]]);export{fe as default};
|
|
||||||
1
frontend/dist/assets/ModelCreateView-BceK1rRO.js
vendored
Normal file
1
frontend/dist/assets/ModelCreateView-BceK1rRO.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/ModelManageView-B6vWdbQY.css
vendored
Normal file
1
frontend/dist/assets/ModelManageView-B6vWdbQY.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.model-manage-page[data-v-cd3c82d5]{height:100%}.capsule-tabs[data-v-cd3c82d5]{display:flex;gap:2px;padding:3px;border:1px solid #e2e8f0;border-radius:8px;background:#f1f5f9}.capsule-tab-item[data-v-cd3c82d5]{border:0;border-radius:6px;background:transparent;padding:6px 20px;color:#64748b;font-size:13px;font-weight:500;cursor:pointer}.capsule-tab-item.active[data-v-cd3c82d5]{background:#fff;color:#4f46e5;box-shadow:0 1px 3px #0000000f;font-weight:600}.action-buttons[data-v-cd3c82d5]{display:flex;justify-content:center;gap:8px}.runtime-panel[data-v-cd3c82d5]{display:grid;gap:16px;padding:16px 24px;background:#f8fafc}.panel-title[data-v-cd3c82d5]{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;color:#1f2937}
|
||||||
1
frontend/dist/assets/ModelManageView-BIm0kKN1.js
vendored
Normal file
1
frontend/dist/assets/ModelManageView-BIm0kKN1.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
@charset "UTF-8";.capsule-tabs[data-v-4609d016]{display:flex;background:#f1f5f9;padding:3px;border-radius:8px;gap:2px;border:1px solid #e2e8f0}.capsule-tab-item[data-v-4609d016]{border:0;background:transparent;padding:6px 20px;font-size:13px;font-weight:500;color:#64748b;cursor:pointer;border-radius:6px;transition:all .2s ease;outline:none}.capsule-tab-item[data-v-4609d016]:hover{color:#1e293b}.capsule-tab-item.active[data-v-4609d016]{background:#fff;color:#4f46e5;box-shadow:0 1px 3px #0000000f,0 1px 2px #0000000a;font-weight:600}.action-buttons[data-v-4609d016]{display:flex;justify-content:center;gap:8px}
|
|
||||||
1
frontend/dist/assets/ModelStatusTag.vue_vue_type_script_setup_true_lang-CDqn2Ul4.js
vendored
Normal file
1
frontend/dist/assets/ModelStatusTag.vue_vue_type_script_setup_true_lang-CDqn2Ul4.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{E as o}from"./el-tag-C2d1Zfbn.js";import{d as p,e as n,w as r,j as c,o as l,x as u,aa as m}from"./index-D5veDkuG.js";import{s as _,a as i}from"./status-Dl1fykxa.js";const T=p({__name:"ModelStatusTag",props:{status:{}},setup(e){const t=e,a=c(()=>({text:i(t.status),type:_(t.status)}));return(f,g)=>{const s=o;return l(),n(s,{type:a.value.type,size:"small",effect:"light"},{default:r(()=>[u(m(a.value.text),1)]),_:1},8,["type"])}}});export{T as _};
|
||||||
@@ -1 +0,0 @@
|
|||||||
import{E as r}from"./index-ByhS8A1d.js";import{d as o,e as c,w as p,j as u,o as i,x as d,aa as l}from"./index-Ds9AETjS.js";/* empty css */const m=o({__name:"ModelStatusTag",props:{status:{}},setup(a){const s=a,t=u(()=>{const e=s.status||"";switch(e){case"running":case"ready":case"loaded":return{text:e==="ready"||e==="loaded"?"已就绪":"运行中",type:"success"};case"pending":return{text:"等待中",type:"info"};case"failed":return{text:"失败",type:"danger"};case"starting":case"loading":return{text:"加载中",type:"warning"};case"completed":return{text:"已完成",type:"success"};case"stopped":return{text:"已停止",type:"info"};default:return{text:e||"未知",type:"info"}}});return(e,f)=>{const n=r;return i(),c(n,{type:t.value.type,size:"small",effect:"light"},{default:p(()=>[d(l(t.value.text),1)]),_:1},8,["type"])}}});export{m as _};
|
|
||||||
@@ -1 +1 @@
|
|||||||
import{E as i}from"./el-card-D86TXgMv.js";import{d as n,e as u,w as p,o as a,c as s,h as o,g as r,q as d,aa as l}from"./index-Ds9AETjS.js";import{_}from"./_plugin-vue_export-helper-DlAUqK2U.js";const h={key:0,class:"page-card-header"},m={class:"page-card-title"},f={key:0,class:"page-card-subtitle"},g={key:0,class:"page-card-extra"},v={class:"page-card-body"},b=n({__name:"PageCard",props:{title:{},subtitle:{},bordered:{type:Boolean,default:!1}},setup(t){return(e,k)=>{const c=i;return a(),u(c,{shadow:"never",class:"page-card"},{default:p(()=>[t.title||e.$slots.header?(a(),s("div",h,[o(e.$slots,"header",{},()=>[d("div",null,[d("h2",m,l(t.title),1),t.subtitle?(a(),s("p",f,l(t.subtitle),1)):r("",!0)])],!0),e.$slots.extra?(a(),s("div",g,[o(e.$slots,"extra",{},void 0,!0)])):r("",!0)])):r("",!0),d("div",v,[o(e.$slots,"default",{},void 0,!0)])]),_:3})}}}),$=_(b,[["__scopeId","data-v-05c37e8a"]]);export{$ as P};
|
import{E as i}from"./el-card-DzhpMhms.js";import{d as n,e as u,w as p,o as a,c as s,h as o,g as r,q as d,aa as l}from"./index-D5veDkuG.js";import{_}from"./_plugin-vue_export-helper-DlAUqK2U.js";const h={key:0,class:"page-card-header"},m={class:"page-card-title"},f={key:0,class:"page-card-subtitle"},g={key:0,class:"page-card-extra"},v={class:"page-card-body"},b=n({__name:"PageCard",props:{title:{},subtitle:{},bordered:{type:Boolean,default:!1}},setup(t){return(e,k)=>{const c=i;return a(),u(c,{shadow:"never",class:"page-card"},{default:p(()=>[t.title||e.$slots.header?(a(),s("div",h,[o(e.$slots,"header",{},()=>[d("div",null,[d("h2",m,l(t.title),1),t.subtitle?(a(),s("p",f,l(t.subtitle),1)):r("",!0)])],!0),e.$slots.extra?(a(),s("div",g,[o(e.$slots,"extra",{},void 0,!0)])):r("",!0)])):r("",!0),d("div",v,[o(e.$slots,"default",{},void 0,!0)])]),_:3})}}}),$=_(b,[["__scopeId","data-v-05c37e8a"]]);export{$ as P};
|
||||||
@@ -1 +1 @@
|
|||||||
import{_ as o}from"./_plugin-vue_export-helper-DlAUqK2U.js";import{o as c,c as t,q as s}from"./index-Ds9AETjS.js";const n={},r={class:"simple-page"};function a(i,e){return c(),t("section",r,[...e[0]||(e[0]=[s("h1",null,"无权访问",-1),s("p",null,"当前账号没有访问该页面的权限,请联系管理员调整角色或页面权限。",-1)])])}const p=o(n,[["render",a],["__scopeId","data-v-cc370c43"]]);export{p as default};
|
import{_ as o}from"./_plugin-vue_export-helper-DlAUqK2U.js";import{o as c,c as t,q as s}from"./index-D5veDkuG.js";const n={},r={class:"simple-page"};function a(i,e){return c(),t("section",r,[...e[0]||(e[0]=[s("h1",null,"无权访问",-1),s("p",null,"当前账号没有访问该页面的权限,请联系管理员调整角色或页面权限。",-1)])])}const p=o(n,[["render",a],["__scopeId","data-v-cc370c43"]]);export{p as default};
|
||||||
@@ -1 +1 @@
|
|||||||
import{a as y,E as h}from"./el-form-item-Bq0n_p5a.js";import{E as B}from"./index-h5FkzAP_.js";import{E as R}from"./index-BNsCyG4A.js";import{d as S,G as I,e as N,w as l,j as g,z as O,o as m,s as o,q as v,c as x,ad as U,n as V,f as q,bx as F,M as L,x as b,aa as M,A as w,y as j,v as z,ac as D}from"./index-Ds9AETjS.js";import{P}from"./PageCard-CV3p14-x.js";import{u as A}from"./tools-s3Et6p-2.js";import{_ as G}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./castArray-BWI4UxBy.js";import"./_baseClone-CZxxnGCv.js";import"./raf-zQ00nuMI.js";import"./index-B0if-AHo.js";import"./index-BSbCLbaz.js";import"./el-card-D86TXgMv.js";const $={class:"icon-grid"},H=["onClick"],J=S({__name:"ToolCreateView",setup(K){const d=D(),p=z(),n=A(),i=j(),s=g(()=>d.params.id!=null),c=g(()=>s.value?n.getTool(d.params.id):void 0),e=O({id:"",name:"",description:"",url:"",icon:"fa-cog"}),C={name:[{required:!0,message:"请输入工具名称",trigger:"blur"},{max:30,message:"不超过 30 字符",trigger:"blur"}],url:[{required:!0,message:"请输入跳转地址",trigger:"blur"}]};function k(){i.value&&i.value.validate(f=>{f&&(s.value?(n.updateTool(e.id,{...e}),w.success("更新成功")):(n.addTool({...e,id:"custom_"+Date.now()}),w.success("添加成功")),p.push("/tools"))})}function E(){p.back()}return I(()=>{c.value&&Object.assign(e,c.value)}),(f,a)=>{const u=B,r=y,_=R,T=h;return m(),N(P,{title:s.value?"编辑自定义工具":"添加自定义工具"},{default:l(()=>[o(T,{ref_key:"formRef",ref:i,model:e,rules:C,"label-width":"100px",style:{"max-width":"600px"}},{default:l(()=>[o(r,{label:"工具名称",prop:"name"},{default:l(()=>[o(u,{modelValue:e.name,"onUpdate:modelValue":a[0]||(a[0]=t=>e.name=t),placeholder:"请输入工具名称",maxlength:"30","show-word-limit":""},null,8,["modelValue"])]),_:1}),o(r,{label:"工具描述"},{default:l(()=>[o(u,{modelValue:e.description,"onUpdate:modelValue":a[1]||(a[1]=t=>e.description=t),type:"textarea",rows:2,maxlength:"100","show-word-limit":""},null,8,["modelValue"])]),_:1}),o(r,{label:"跳转地址",prop:"url"},{default:l(()=>[o(u,{modelValue:e.url,"onUpdate:modelValue":a[2]||(a[2]=t=>e.url=t),placeholder:"相对路径或完整 URL"},null,8,["modelValue"])]),_:1}),o(r,{label:"图标"},{default:l(()=>[v("div",$,[(m(!0),x(L,null,U(q(F),t=>(m(),x("div",{key:t,class:V(["icon-option",{selected:e.icon===t}]),onClick:Q=>e.icon=t},[v("i",{class:V(["fa",t])},null,2)],10,H))),128))])]),_:1}),o(r,null,{default:l(()=>[o(_,{type:"primary",onClick:k},{default:l(()=>[b(M(s.value?"保存":"添加"),1)]),_:1}),o(_,{onClick:E},{default:l(()=>[...a[3]||(a[3]=[b("取消",-1)])]),_:1})]),_:1})]),_:1},8,["model"])]),_:1},8,["title"])}}}),ue=G(J,[["__scopeId","data-v-9116c5de"]]);export{ue as default};
|
import{a as y,E as h}from"./el-form-item-rya6onmd.js";import{E as B}from"./index-DbBHj60R.js";import{E as R}from"./index-DxH5-_w6.js";import{d as S,G as I,e as N,w as l,j as g,z as O,o as m,s as o,q as v,c as x,ad as U,n as V,f as q,bx as F,M as L,x as b,aa as M,A as w,y as j,v as z,ac as D}from"./index-D5veDkuG.js";import{P}from"./PageCard-DFGMy1ik.js";import{u as A}from"./tools-niY0Ru_Q.js";import{_ as G}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./castArray-BDIbf8ih.js";import"./_baseClone-BuqHm105.js";import"./raf-Dsox5IbR.js";import"./index-DDY6aSpq.js";import"./index-BT1A2I5c.js";import"./el-card-DzhpMhms.js";const $={class:"icon-grid"},H=["onClick"],J=S({__name:"ToolCreateView",setup(K){const d=D(),p=z(),n=A(),i=j(),s=g(()=>d.params.id!=null),c=g(()=>s.value?n.getTool(d.params.id):void 0),e=O({id:"",name:"",description:"",url:"",icon:"fa-cog"}),C={name:[{required:!0,message:"请输入工具名称",trigger:"blur"},{max:30,message:"不超过 30 字符",trigger:"blur"}],url:[{required:!0,message:"请输入跳转地址",trigger:"blur"}]};function k(){i.value&&i.value.validate(f=>{f&&(s.value?(n.updateTool(e.id,{...e}),w.success("更新成功")):(n.addTool({...e,id:"custom_"+Date.now()}),w.success("添加成功")),p.push("/tools"))})}function E(){p.back()}return I(()=>{c.value&&Object.assign(e,c.value)}),(f,a)=>{const u=B,r=y,_=R,T=h;return m(),N(P,{title:s.value?"编辑自定义工具":"添加自定义工具"},{default:l(()=>[o(T,{ref_key:"formRef",ref:i,model:e,rules:C,"label-width":"100px",style:{"max-width":"600px"}},{default:l(()=>[o(r,{label:"工具名称",prop:"name"},{default:l(()=>[o(u,{modelValue:e.name,"onUpdate:modelValue":a[0]||(a[0]=t=>e.name=t),placeholder:"请输入工具名称",maxlength:"30","show-word-limit":""},null,8,["modelValue"])]),_:1}),o(r,{label:"工具描述"},{default:l(()=>[o(u,{modelValue:e.description,"onUpdate:modelValue":a[1]||(a[1]=t=>e.description=t),type:"textarea",rows:2,maxlength:"100","show-word-limit":""},null,8,["modelValue"])]),_:1}),o(r,{label:"跳转地址",prop:"url"},{default:l(()=>[o(u,{modelValue:e.url,"onUpdate:modelValue":a[2]||(a[2]=t=>e.url=t),placeholder:"相对路径或完整 URL"},null,8,["modelValue"])]),_:1}),o(r,{label:"图标"},{default:l(()=>[v("div",$,[(m(!0),x(L,null,U(q(F),t=>(m(),x("div",{key:t,class:V(["icon-option",{selected:e.icon===t}]),onClick:Q=>e.icon=t},[v("i",{class:V(["fa",t])},null,2)],10,H))),128))])]),_:1}),o(r,null,{default:l(()=>[o(_,{type:"primary",onClick:k},{default:l(()=>[b(M(s.value?"保存":"添加"),1)]),_:1}),o(_,{onClick:E},{default:l(()=>[...a[3]||(a[3]=[b("取消",-1)])]),_:1})]),_:1})]),_:1},8,["model"])]),_:1},8,["title"])}}}),ue=G(J,[["__scopeId","data-v-9116c5de"]]);export{ue as default};
|
||||||
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/TrainingLogView-fWzPJkLG.js
vendored
Normal file
1
frontend/dist/assets/TrainingLogView-fWzPJkLG.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/UserCreateView-D_u7YOkp.js
vendored
Normal file
1
frontend/dist/assets/UserCreateView-D_u7YOkp.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{E as y,a as x}from"./el-form-item-rya6onmd.js";import{E as k}from"./index-DbBHj60R.js";import{E as U}from"./index-DxH5-_w6.js";import{E as C,a as B}from"./el-select-BQaRHu3V.js";import{a as F,E as I}from"./el-checkbox-D6iLoXkw.js";import{E as N,b as R}from"./el-radio-group-HDdMmxmO.js";import{d as G,c as _,q as M,s as o,w as l,o as f,x as r,M as O,ad as S,aa as q,f as z,v as A,y as D,z as L,db as T,A as $}from"./index-D5veDkuG.js";/* empty css */import"./el-scrollbar-GFOwBTUz.js";import"./el-popper-DDJTxf27.js";import"./el-tag-C2d1Zfbn.js";import{_ as j}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./castArray-BDIbf8ih.js";import"./_baseClone-BuqHm105.js";import"./raf-Dsox5IbR.js";import"./index-DDY6aSpq.js";import"./index-BT1A2I5c.js";import"./index-DmAaj_YO.js";import"./vnode-CYZHqsIy.js";import"./index-DbuKXJ7V.js";import"./scroll-DswMXu1y.js";import"./clamp-Ddg8_Yc2.js";import"./toNumber-a8w_gs0u.js";import"./_baseIteratee-N-G7GeYE.js";import"./omit-DxQEBv1Z.js";const H={class:"user-create"},J=G({__name:"UserCreateView",setup(K){const d=A(),n=D(!1),a=L({username:"",display_name:"",password:"platform123",role:"viewer",status:"active",permissions:["dashboard"]}),b=["dashboard","fine-tune","model-eval","model-inference","model-manage","dataset","data-process","data-convert","compute","hardware","logs","user-settings"];async function V(){n.value=!0;try{await T(a),$.success("用户已创建"),d.push("/user-settings")}finally{n.value=!1}}return(P,e)=>{const m=k,s=x,u=B,v=C,p=R,c=N,E=I,w=F,i=U,g=y;return f(),_("section",H,[e[11]||(e[11]=M("h1",null,"创建用户",-1)),o(g,{model:a,"label-width":"110px",class:"user-form"},{default:l(()=>[o(s,{label:"账号"},{default:l(()=>[o(m,{modelValue:a.username,"onUpdate:modelValue":e[0]||(e[0]=t=>a.username=t)},null,8,["modelValue"])]),_:1}),o(s,{label:"显示名称"},{default:l(()=>[o(m,{modelValue:a.display_name,"onUpdate:modelValue":e[1]||(e[1]=t=>a.display_name=t)},null,8,["modelValue"])]),_:1}),o(s,{label:"初始密码"},{default:l(()=>[o(m,{modelValue:a.password,"onUpdate:modelValue":e[2]||(e[2]=t=>a.password=t),type:"password","show-password":""},null,8,["modelValue"])]),_:1}),o(s,{label:"角色"},{default:l(()=>[o(v,{modelValue:a.role,"onUpdate:modelValue":e[3]||(e[3]=t=>a.role=t)},{default:l(()=>[o(u,{label:"管理员",value:"admin"}),o(u,{label:"操作员",value:"operator"}),o(u,{label:"观察员",value:"viewer"})]),_:1},8,["modelValue"])]),_:1}),o(s,{label:"状态"},{default:l(()=>[o(c,{modelValue:a.status,"onUpdate:modelValue":e[4]||(e[4]=t=>a.status=t)},{default:l(()=>[o(p,{value:"active"},{default:l(()=>[...e[7]||(e[7]=[r("启用",-1)])]),_:1}),o(p,{value:"disabled"},{default:l(()=>[...e[8]||(e[8]=[r("禁用",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),o(s,{label:"页面权限"},{default:l(()=>[o(w,{modelValue:a.permissions,"onUpdate:modelValue":e[5]||(e[5]=t=>a.permissions=t)},{default:l(()=>[(f(),_(O,null,S(b,t=>o(E,{key:t,value:t},{default:l(()=>[r(q(t),1)]),_:2},1032,["value"])),64))]),_:1},8,["modelValue"])]),_:1}),o(s,null,{default:l(()=>[o(i,{onClick:e[6]||(e[6]=t=>z(d).back())},{default:l(()=>[...e[9]||(e[9]=[r("返回",-1)])]),_:1}),o(i,{type:"primary",loading:n.value,onClick:V},{default:l(()=>[...e[10]||(e[10]=[r("保存",-1)])]),_:1},8,["loading"])]),_:1})]),_:1},8,["model"])])}}}),Ee=j(J,[["__scopeId","data-v-57777381"]]);export{Ee as default};
|
||||||
@@ -1 +0,0 @@
|
|||||||
import{E as y,a as x}from"./el-form-item-Bq0n_p5a.js";import{E as k}from"./index-h5FkzAP_.js";import{E as U}from"./index-BNsCyG4A.js";import{E as C,a as B}from"./el-select-BD70by95.js";import{a as F,E as I}from"./el-checkbox-pV3o_8iy.js";import{E as N,b as R}from"./el-radio-group-DxV9SE4w.js";import{d as G,c as _,q as M,s as o,w as l,o as f,x as r,M as O,ad as S,aa as q,f as z,v as A,y as D,z as L,db as T,A as $}from"./index-Ds9AETjS.js";/* empty css */import"./el-scrollbar-CF42LNzg.js";import"./el-popper-D1tByNLb.js";/* empty css */import{_ as j}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./castArray-BWI4UxBy.js";import"./_baseClone-CZxxnGCv.js";import"./raf-zQ00nuMI.js";import"./index-B0if-AHo.js";import"./index-BSbCLbaz.js";import"./index-sWKLi7bH.js";import"./vnode-CBiWHFw7.js";import"./index-CQlF36Lk.js";import"./index-ByhS8A1d.js";import"./scroll-DVmMv0d9.js";import"./clamp-CyhADbdq.js";import"./toNumber-DrayciwT.js";import"./_baseIteratee-82gB5Yov.js";import"./omit-BzI1MV_K.js";const H={class:"user-create"},J=G({__name:"UserCreateView",setup(K){const d=A(),n=D(!1),a=L({username:"",display_name:"",password:"platform123",role:"viewer",status:"active",permissions:["dashboard"]}),b=["dashboard","fine-tune","model-eval","model-inference","model-manage","dataset","data-process","data-convert","compute","hardware","logs","user-settings"];async function V(){n.value=!0;try{await T(a),$.success("用户已创建"),d.push("/user-settings")}finally{n.value=!1}}return(P,e)=>{const m=k,s=x,u=B,v=C,p=R,c=N,E=I,w=F,i=U,g=y;return f(),_("section",H,[e[11]||(e[11]=M("h1",null,"创建用户",-1)),o(g,{model:a,"label-width":"110px",class:"user-form"},{default:l(()=>[o(s,{label:"账号"},{default:l(()=>[o(m,{modelValue:a.username,"onUpdate:modelValue":e[0]||(e[0]=t=>a.username=t)},null,8,["modelValue"])]),_:1}),o(s,{label:"显示名称"},{default:l(()=>[o(m,{modelValue:a.display_name,"onUpdate:modelValue":e[1]||(e[1]=t=>a.display_name=t)},null,8,["modelValue"])]),_:1}),o(s,{label:"初始密码"},{default:l(()=>[o(m,{modelValue:a.password,"onUpdate:modelValue":e[2]||(e[2]=t=>a.password=t),type:"password","show-password":""},null,8,["modelValue"])]),_:1}),o(s,{label:"角色"},{default:l(()=>[o(v,{modelValue:a.role,"onUpdate:modelValue":e[3]||(e[3]=t=>a.role=t)},{default:l(()=>[o(u,{label:"管理员",value:"admin"}),o(u,{label:"操作员",value:"operator"}),o(u,{label:"观察员",value:"viewer"})]),_:1},8,["modelValue"])]),_:1}),o(s,{label:"状态"},{default:l(()=>[o(c,{modelValue:a.status,"onUpdate:modelValue":e[4]||(e[4]=t=>a.status=t)},{default:l(()=>[o(p,{value:"active"},{default:l(()=>[...e[7]||(e[7]=[r("启用",-1)])]),_:1}),o(p,{value:"disabled"},{default:l(()=>[...e[8]||(e[8]=[r("禁用",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),o(s,{label:"页面权限"},{default:l(()=>[o(w,{modelValue:a.permissions,"onUpdate:modelValue":e[5]||(e[5]=t=>a.permissions=t)},{default:l(()=>[(f(),_(O,null,S(b,t=>o(E,{key:t,value:t},{default:l(()=>[r(q(t),1)]),_:2},1032,["value"])),64))]),_:1},8,["modelValue"])]),_:1}),o(s,null,{default:l(()=>[o(i,{onClick:e[6]||(e[6]=t=>z(d).back())},{default:l(()=>[...e[9]||(e[9]=[r("返回",-1)])]),_:1}),o(i,{type:"primary",loading:n.value,onClick:V},{default:l(()=>[...e[10]||(e[10]=[r("保存",-1)])]),_:1},8,["loading"])]),_:1})]),_:1},8,["model"])])}}}),we=j(J,[["__scopeId","data-v-57777381"]]);export{we as default};
|
|
||||||
@@ -1 +1 @@
|
|||||||
import{_ as o}from"./_plugin-vue_export-helper-DlAUqK2U.js";import{o as t,c as r,q as s}from"./index-Ds9AETjS.js";const c={},n={class:"simple-page"};function a(_,e){return t(),r("section",n,[...e[0]||(e[0]=[s("h1",null,"权限设置",-1),s("p",null,"第一版已支持账号页面权限读取与保存,精细化项目/模型/数据集权限将在后续版本补齐。",-1)])])}const p=o(c,[["render",a],["__scopeId","data-v-608c6e7e"]]);export{p as default};
|
import{_ as o}from"./_plugin-vue_export-helper-DlAUqK2U.js";import{o as t,c as r,q as s}from"./index-D5veDkuG.js";const c={},n={class:"simple-page"};function a(_,e){return t(),r("section",n,[...e[0]||(e[0]=[s("h1",null,"权限设置",-1),s("p",null,"第一版已支持账号页面权限读取与保存,精细化项目/模型/数据集权限将在后续版本补齐。",-1)])])}const p=o(c,[["render",a],["__scopeId","data-v-608c6e7e"]]);export{p as default};
|
||||||
@@ -1 +0,0 @@
|
|||||||
import{E as v}from"./index-BNsCyG4A.js";import{a as w,E as b}from"./el-table-C7Zx0Bmo.js";import{v as g}from"./directive-B1cwmVOz.js";import{d as h,G as y,K as E,c as x,q as a,s as t,w as r,y as p,da as V,o as B,x as m,aa as C}from"./index-Ds9AETjS.js";import"./el-scrollbar-CF42LNzg.js";import"./el-popper-D1tByNLb.js";import"./el-tooltip-l0sNRNKZ.js";import"./el-checkbox-pV3o_8iy.js";/* empty css */import{_ as U}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./_baseClone-CZxxnGCv.js";import"./index-BSbCLbaz.js";import"./_baseIteratee-82gB5Yov.js";import"./castArray-BWI4UxBy.js";import"./debounce-D6mJVDYC.js";import"./toNumber-DrayciwT.js";import"./index-sWKLi7bH.js";import"./raf-zQ00nuMI.js";import"./omit-BzI1MV_K.js";const k={class:"user-settings"},N={class:"page-header"},S=h({__name:"UserSettingsView",setup(T){const s=p(!1),i=p([]);async function d(){s.value=!0;try{i.value=await V()}finally{s.value=!1}}return y(d),(u,e)=>{const _=v,o=b,c=w,f=g;return E((B(),x("section",k,[a("header",N,[e[2]||(e[2]=a("div",null,[a("h1",null,"用户设置"),a("p",null,"管理平台账号、角色状态和页面权限。")],-1)),t(_,{type:"primary",onClick:e[0]||(e[0]=l=>u.$router.push("/user-settings/create"))},{default:r(()=>[...e[1]||(e[1]=[m("创建用户",-1)])]),_:1})]),t(c,{data:i.value},{default:r(()=>[t(o,{prop:"username",label:"账号","min-width":"140"}),t(o,{prop:"display_name",label:"显示名称","min-width":"160"}),t(o,{prop:"role",label:"角色",width:"120"}),t(o,{prop:"status",label:"状态",width:"120"}),t(o,{label:"权限数",width:"120"},{default:r(({row:l})=>{var n;return[m(C(((n=l.permissions)==null?void 0:n.length)||0),1)]}),_:1}),t(o,{prop:"create_time",label:"创建时间","min-width":"180"})]),_:1},8,["data"])])),[[f,s.value]])}}}),X=U(S,[["__scopeId","data-v-16caa8c5"]]);export{X as default};
|
|
||||||
1
frontend/dist/assets/UserSettingsView-D3-QhDTj.js
vendored
Normal file
1
frontend/dist/assets/UserSettingsView-D3-QhDTj.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{E as w}from"./index-DxH5-_w6.js";import{E as y}from"./el-tag-C2d1Zfbn.js";import{a as h,E}from"./el-table-B6qMxzT4.js";import{v as T}from"./directive-BBfPDwlQ.js";import{d as x,G as V,K as B,c as C,q as r,s as t,w as o,y as m,da as U,o as k,x as i,f as u,aa as d}from"./index-D5veDkuG.js";import"./el-scrollbar-GFOwBTUz.js";import"./el-popper-DDJTxf27.js";import"./el-tooltip-l0sNRNKZ.js";import"./el-checkbox-D6iLoXkw.js";import{s as N,a as S}from"./status-Dl1fykxa.js";import{_ as D}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./_baseClone-BuqHm105.js";import"./index-BT1A2I5c.js";import"./_baseIteratee-N-G7GeYE.js";import"./castArray-BDIbf8ih.js";import"./debounce-D3zKL4rk.js";import"./toNumber-a8w_gs0u.js";import"./index-DmAaj_YO.js";import"./raf-Dsox5IbR.js";import"./omit-DxQEBv1Z.js";const L={class:"user-settings"},$={class:"page-header"},q=x({__name:"UserSettingsView",setup(z){const l=m(!1),n=m([]);async function _(){l.value=!0;try{n.value=await U()}finally{l.value=!1}}return V(_),(c,e)=>{const f=w,a=E,g=y,b=h,v=T;return B((k(),C("section",L,[r("header",$,[e[2]||(e[2]=r("div",null,[r("h1",null,"用户设置"),r("p",null,"管理平台账号、角色状态和页面权限。")],-1)),t(f,{type:"primary",onClick:e[0]||(e[0]=s=>c.$router.push("/user-settings/create"))},{default:o(()=>[...e[1]||(e[1]=[i("创建用户",-1)])]),_:1})]),t(b,{data:n.value},{default:o(()=>[t(a,{prop:"username",label:"账号","min-width":"140"}),t(a,{prop:"display_name",label:"显示名称","min-width":"160"}),t(a,{prop:"role",label:"角色",width:"120"}),t(a,{label:"状态",width:"120"},{default:o(({row:s})=>[t(g,{type:u(N)(s.status),size:"small"},{default:o(()=>[i(d(u(S)(s.status)),1)]),_:2},1032,["type"])]),_:1}),t(a,{label:"权限数",width:"120"},{default:o(({row:s})=>{var p;return[i(d(((p=s.permissions)==null?void 0:p.length)||0),1)]}),_:1}),t(a,{prop:"create_time",label:"创建时间","min-width":"180"})]),_:1},8,["data"])])),[[v,l.value]])}}}),st=D(q,[["__scopeId","data-v-3c7eb527"]]);export{st as default};
|
||||||
1
frontend/dist/assets/UserSettingsView-D5LgrnVq.css
vendored
Normal file
1
frontend/dist/assets/UserSettingsView-D5LgrnVq.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.user-settings[data-v-3c7eb527]{padding:24px}.page-header[data-v-3c7eb527]{display:flex;justify-content:space-between;gap:16px;margin-bottom:18px}.page-header h1[data-v-3c7eb527]{margin:0;font-size:24px}.page-header p[data-v-3c7eb527]{margin:8px 0 0;color:#64748b}
|
||||||
@@ -1 +0,0 @@
|
|||||||
.user-settings[data-v-16caa8c5]{padding:24px}.page-header[data-v-16caa8c5]{display:flex;justify-content:space-between;gap:16px;margin-bottom:18px}.page-header h1[data-v-16caa8c5]{margin:0;font-size:24px}.page-header p[data-v-16caa8c5]{margin:8px 0 0;color:#64748b}
|
|
||||||
@@ -1 +1 @@
|
|||||||
import{cs as T,ct as Q,cu as U,cv as B,cw as X,cx as Z,cy as M,cz as V,cA as D,cB as z,cj as k,cC as G,cD as ee,cE as m,ch as O,cF as K,cG as d,cH as N,cI as b,cJ as re,cK as te,ce as ne,cL as ae}from"./index-Ds9AETjS.js";var S=Object.create,oe=(function(){function e(){}return function(r){if(!T(r))return{};if(S)return S(r);e.prototype=r;var t=new e;return e.prototype=void 0,t}})();function se(e,r){var t=-1,a=e.length;for(r||(r=Array(a));++t<a;)r[t]=e[t];return r}function ce(e,r){for(var t=-1,a=e==null?0:e.length;++t<a&&r(e[t],t,e)!==!1;);return e}function y(e,r,t,a){var g=!t;t||(t={});for(var s=-1,o=r.length;++s<o;){var i=r[s],c=void 0;c===void 0&&(c=e[i]),g?Q(t,i,c):U(t,i,c)}return t}function ie(e){var r=[];if(e!=null)for(var t in Object(e))r.push(t);return r}var fe=Object.prototype,ue=fe.hasOwnProperty;function ge(e){if(!T(e))return ie(e);var r=B(e),t=[];for(var a in e)a=="constructor"&&(r||!ue.call(e,a))||t.push(a);return t}function A(e){return X(e)?Z(e,!0):ge(e)}function le(e,r){return e&&y(r,M(r),e)}function be(e,r){return e&&y(r,A(r),e)}var _=typeof exports=="object"&&exports&&!exports.nodeType&&exports,x=_&&typeof module=="object"&&module&&!module.nodeType&&module,ye=x&&x.exports===_,I=ye?V.Buffer:void 0,C=I?I.allocUnsafe:void 0;function pe(e,r){if(r)return e.slice();var t=e.length,a=C?C(t):new e.constructor(t);return e.copy(a),a}function Te(e,r){return y(e,D(e),r)}var de=Object.getOwnPropertySymbols,R=de?function(e){for(var r=[];e;)k(r,D(e)),e=G(e);return r}:z;function Ae(e,r){return y(e,R(e),r)}function je(e){return ee(e,A,R)}var he=Object.prototype,we=he.hasOwnProperty;function ve(e){var r=e.length,t=new e.constructor(r);return r&&typeof e[0]=="string"&&we.call(e,"index")&&(t.index=e.index,t.input=e.input),t}function j(e){var r=new e.constructor(e.byteLength);return new m(r).set(new m(e)),r}function $e(e,r){var t=r?j(e.buffer):e.buffer;return new e.constructor(t,e.byteOffset,e.byteLength)}var me=/\w*$/;function Oe(e){var r=new e.constructor(e.source,me.exec(e));return r.lastIndex=e.lastIndex,r}var E=O?O.prototype:void 0,F=E?E.valueOf:void 0;function Se(e){return F?Object(F.call(e)):{}}function xe(e,r){var t=r?j(e.buffer):e.buffer;return new e.constructor(t,e.byteOffset,e.length)}var Ie="[object Boolean]",Ce="[object Date]",Ee="[object Map]",Fe="[object Number]",Le="[object RegExp]",Pe="[object Set]",Ue="[object String]",Be="[object Symbol]",Me="[object ArrayBuffer]",De="[object DataView]",Ge="[object Float32Array]",Ke="[object Float64Array]",Ne="[object Int8Array]",_e="[object Int16Array]",Re="[object Int32Array]",He="[object Uint8Array]",Je="[object Uint8ClampedArray]",We="[object Uint16Array]",Ye="[object Uint32Array]";function qe(e,r,t){var a=e.constructor;switch(r){case Me:return j(e);case Ie:case Ce:return new a(+e);case De:return $e(e,t);case Ge:case Ke:case Ne:case _e:case Re:case He:case Je:case We:case Ye:return xe(e,t);case Ee:return new a;case Fe:case Ue:return new a(e);case Le:return Oe(e);case Pe:return new a;case Be:return Se(e)}}function Qe(e){return typeof e.constructor=="function"&&!B(e)?oe(G(e)):{}}var Xe="[object Map]";function Ze(e){return K(e)&&d(e)==Xe}var L=b&&b.isMap,Ve=L?N(L):Ze,ze="[object Set]";function ke(e){return K(e)&&d(e)==ze}var P=b&&b.isSet,er=P?N(P):ke,rr=1,tr=2,nr=4,H="[object Arguments]",ar="[object Array]",or="[object Boolean]",sr="[object Date]",cr="[object Error]",J="[object Function]",ir="[object GeneratorFunction]",fr="[object Map]",ur="[object Number]",W="[object Object]",gr="[object RegExp]",lr="[object Set]",br="[object String]",yr="[object Symbol]",pr="[object WeakMap]",Tr="[object ArrayBuffer]",dr="[object DataView]",Ar="[object Float32Array]",jr="[object Float64Array]",hr="[object Int8Array]",wr="[object Int16Array]",vr="[object Int32Array]",$r="[object Uint8Array]",mr="[object Uint8ClampedArray]",Or="[object Uint16Array]",Sr="[object Uint32Array]",n={};n[H]=n[ar]=n[Tr]=n[dr]=n[or]=n[sr]=n[Ar]=n[jr]=n[hr]=n[wr]=n[vr]=n[fr]=n[ur]=n[W]=n[gr]=n[lr]=n[br]=n[yr]=n[$r]=n[mr]=n[Or]=n[Sr]=!0;n[cr]=n[J]=n[pr]=!1;function p(e,r,t,a,g,s){var o,i=r&rr,c=r&tr,Y=r&nr;if(t&&(o=g?t(e,a,g,s):t(e)),o!==void 0)return o;if(!T(e))return e;var h=ne(e);if(h){if(o=ve(e),!i)return se(e,o)}else{var l=d(e),w=l==J||l==ir;if(re(e))return pe(e,i);if(l==W||l==H||w&&!g){if(o=c||w?{}:Qe(e),!i)return c?Ae(e,be(o,e)):Te(e,le(o,e))}else{if(!n[l])return g?e:{};o=qe(e,l,i)}}s||(s=new te);var v=s.get(e);if(v)return v;s.set(e,o),er(e)?e.forEach(function(f){o.add(p(f,r,t,f,e,s))}):Ve(e)&&e.forEach(function(f,u){o.set(u,p(f,r,t,u,e,s))});var q=Y?c?je:ae:c?A:M,$=h?void 0:q(e);return ce($||e,function(f,u){$&&(u=f,f=e[u]),U(o,u,p(f,r,t,u,e,s))}),o}export{se as a,p as b,y as c,pe as d,xe as e,je as g,Qe as i,A as k};
|
import{cs as T,ct as Q,cu as U,cv as B,cw as X,cx as Z,cy as M,cz as V,cA as D,cB as z,cj as k,cC as G,cD as ee,cE as m,ch as O,cF as K,cG as d,cH as N,cI as b,cJ as re,cK as te,ce as ne,cL as ae}from"./index-D5veDkuG.js";var S=Object.create,oe=(function(){function e(){}return function(r){if(!T(r))return{};if(S)return S(r);e.prototype=r;var t=new e;return e.prototype=void 0,t}})();function se(e,r){var t=-1,a=e.length;for(r||(r=Array(a));++t<a;)r[t]=e[t];return r}function ce(e,r){for(var t=-1,a=e==null?0:e.length;++t<a&&r(e[t],t,e)!==!1;);return e}function y(e,r,t,a){var g=!t;t||(t={});for(var s=-1,o=r.length;++s<o;){var i=r[s],c=void 0;c===void 0&&(c=e[i]),g?Q(t,i,c):U(t,i,c)}return t}function ie(e){var r=[];if(e!=null)for(var t in Object(e))r.push(t);return r}var fe=Object.prototype,ue=fe.hasOwnProperty;function ge(e){if(!T(e))return ie(e);var r=B(e),t=[];for(var a in e)a=="constructor"&&(r||!ue.call(e,a))||t.push(a);return t}function A(e){return X(e)?Z(e,!0):ge(e)}function le(e,r){return e&&y(r,M(r),e)}function be(e,r){return e&&y(r,A(r),e)}var _=typeof exports=="object"&&exports&&!exports.nodeType&&exports,x=_&&typeof module=="object"&&module&&!module.nodeType&&module,ye=x&&x.exports===_,I=ye?V.Buffer:void 0,C=I?I.allocUnsafe:void 0;function pe(e,r){if(r)return e.slice();var t=e.length,a=C?C(t):new e.constructor(t);return e.copy(a),a}function Te(e,r){return y(e,D(e),r)}var de=Object.getOwnPropertySymbols,R=de?function(e){for(var r=[];e;)k(r,D(e)),e=G(e);return r}:z;function Ae(e,r){return y(e,R(e),r)}function je(e){return ee(e,A,R)}var he=Object.prototype,we=he.hasOwnProperty;function ve(e){var r=e.length,t=new e.constructor(r);return r&&typeof e[0]=="string"&&we.call(e,"index")&&(t.index=e.index,t.input=e.input),t}function j(e){var r=new e.constructor(e.byteLength);return new m(r).set(new m(e)),r}function $e(e,r){var t=r?j(e.buffer):e.buffer;return new e.constructor(t,e.byteOffset,e.byteLength)}var me=/\w*$/;function Oe(e){var r=new e.constructor(e.source,me.exec(e));return r.lastIndex=e.lastIndex,r}var E=O?O.prototype:void 0,F=E?E.valueOf:void 0;function Se(e){return F?Object(F.call(e)):{}}function xe(e,r){var t=r?j(e.buffer):e.buffer;return new e.constructor(t,e.byteOffset,e.length)}var Ie="[object Boolean]",Ce="[object Date]",Ee="[object Map]",Fe="[object Number]",Le="[object RegExp]",Pe="[object Set]",Ue="[object String]",Be="[object Symbol]",Me="[object ArrayBuffer]",De="[object DataView]",Ge="[object Float32Array]",Ke="[object Float64Array]",Ne="[object Int8Array]",_e="[object Int16Array]",Re="[object Int32Array]",He="[object Uint8Array]",Je="[object Uint8ClampedArray]",We="[object Uint16Array]",Ye="[object Uint32Array]";function qe(e,r,t){var a=e.constructor;switch(r){case Me:return j(e);case Ie:case Ce:return new a(+e);case De:return $e(e,t);case Ge:case Ke:case Ne:case _e:case Re:case He:case Je:case We:case Ye:return xe(e,t);case Ee:return new a;case Fe:case Ue:return new a(e);case Le:return Oe(e);case Pe:return new a;case Be:return Se(e)}}function Qe(e){return typeof e.constructor=="function"&&!B(e)?oe(G(e)):{}}var Xe="[object Map]";function Ze(e){return K(e)&&d(e)==Xe}var L=b&&b.isMap,Ve=L?N(L):Ze,ze="[object Set]";function ke(e){return K(e)&&d(e)==ze}var P=b&&b.isSet,er=P?N(P):ke,rr=1,tr=2,nr=4,H="[object Arguments]",ar="[object Array]",or="[object Boolean]",sr="[object Date]",cr="[object Error]",J="[object Function]",ir="[object GeneratorFunction]",fr="[object Map]",ur="[object Number]",W="[object Object]",gr="[object RegExp]",lr="[object Set]",br="[object String]",yr="[object Symbol]",pr="[object WeakMap]",Tr="[object ArrayBuffer]",dr="[object DataView]",Ar="[object Float32Array]",jr="[object Float64Array]",hr="[object Int8Array]",wr="[object Int16Array]",vr="[object Int32Array]",$r="[object Uint8Array]",mr="[object Uint8ClampedArray]",Or="[object Uint16Array]",Sr="[object Uint32Array]",n={};n[H]=n[ar]=n[Tr]=n[dr]=n[or]=n[sr]=n[Ar]=n[jr]=n[hr]=n[wr]=n[vr]=n[fr]=n[ur]=n[W]=n[gr]=n[lr]=n[br]=n[yr]=n[$r]=n[mr]=n[Or]=n[Sr]=!0;n[cr]=n[J]=n[pr]=!1;function p(e,r,t,a,g,s){var o,i=r&rr,c=r&tr,Y=r&nr;if(t&&(o=g?t(e,a,g,s):t(e)),o!==void 0)return o;if(!T(e))return e;var h=ne(e);if(h){if(o=ve(e),!i)return se(e,o)}else{var l=d(e),w=l==J||l==ir;if(re(e))return pe(e,i);if(l==W||l==H||w&&!g){if(o=c||w?{}:Qe(e),!i)return c?Ae(e,be(o,e)):Te(e,le(o,e))}else{if(!n[l])return g?e:{};o=qe(e,l,i)}}s||(s=new te);var v=s.get(e);if(v)return v;s.set(e,o),er(e)?e.forEach(function(f){o.add(p(f,r,t,f,e,s))}):Ve(e)&&e.forEach(function(f,u){o.set(u,p(f,r,t,u,e,s))});var q=Y?c?je:ae:c?A:M,$=h?void 0:q(e);return ce($||e,function(f,u){$&&(u=f,f=e[u]),U(o,u,p(f,r,t,u,e,s))}),o}export{se as a,p as b,y as c,pe as d,xe as e,je as g,Qe as i,A as k};
|
||||||
@@ -1 +1 @@
|
|||||||
import{cK as O,d2 as a,cs as P,cy as p,d3 as A,cl as l,cU as M,co as _,ce as d}from"./index-Ds9AETjS.js";import{h as g,i as m}from"./index-BSbCLbaz.js";var C=1,I=2;function L(n,r,e,t){var i=e.length,u=i;if(n==null)return!u;for(n=Object(n);i--;){var f=e[i];if(f[2]?f[1]!==n[f[0]]:!(f[0]in n))return!1}for(;++i<u;){f=e[i];var s=f[0],o=n[s],y=f[1];if(f[2]){if(o===void 0&&!(s in n))return!1}else{var E=new O,c;if(!(c===void 0?a(y,o,C|I,t,E):c))return!1}}return!0}function R(n){return n===n&&!P(n)}function D(n){for(var r=p(n),e=r.length;e--;){var t=r[e],i=n[t];r[e]=[t,i,R(i)]}return r}function h(n,r){return function(e){return e==null?!1:e[n]===r&&(r!==void 0||n in Object(e))}}function G(n){var r=D(n);return r.length==1&&r[0][2]?h(r[0][0],r[0][1]):function(e){return e===n||L(e,n,r)}}var w=1,F=2;function b(n,r){return A(n)&&R(r)?h(l(n),r):function(e){var t=M(e,n);return t===void 0&&t===r?g(e,n):a(r,t,w|F)}}function K(n){return function(r){return r==null?void 0:r[n]}}function S(n){return function(r){return _(r,n)}}function U(n){return A(n)?K(l(n)):S(n)}function T(n){return typeof n=="function"?n:n==null?m:typeof n=="object"?d(n)?b(n[0],n[1]):G(n):U(n)}export{T as b};
|
import{cK as O,d2 as a,cs as P,cy as p,d3 as A,cl as l,cU as M,co as _,ce as d}from"./index-D5veDkuG.js";import{h as g,i as m}from"./index-BT1A2I5c.js";var C=1,I=2;function L(n,r,e,t){var i=e.length,u=i;if(n==null)return!u;for(n=Object(n);i--;){var f=e[i];if(f[2]?f[1]!==n[f[0]]:!(f[0]in n))return!1}for(;++i<u;){f=e[i];var s=f[0],o=n[s],y=f[1];if(f[2]){if(o===void 0&&!(s in n))return!1}else{var E=new O,c;if(!(c===void 0?a(y,o,C|I,t,E):c))return!1}}return!0}function R(n){return n===n&&!P(n)}function D(n){for(var r=p(n),e=r.length;e--;){var t=r[e],i=n[t];r[e]=[t,i,R(i)]}return r}function h(n,r){return function(e){return e==null?!1:e[n]===r&&(r!==void 0||n in Object(e))}}function G(n){var r=D(n);return r.length==1&&r[0][2]?h(r[0][0],r[0][1]):function(e){return e===n||L(e,n,r)}}var w=1,F=2;function b(n,r){return A(n)&&R(r)?h(l(n),r):function(e){var t=M(e,n);return t===void 0&&t===r?g(e,n):a(r,t,w|F)}}function K(n){return function(r){return r==null?void 0:r[n]}}function S(n){return function(r){return _(r,n)}}function U(n){return A(n)?K(l(n)):S(n)}function T(n){return typeof n=="function"?n:n==null?m:typeof n=="object"?d(n)?b(n[0],n[1]):G(n):U(n)}export{T as b};
|
||||||
@@ -1 +1 @@
|
|||||||
import{ce as a}from"./index-Ds9AETjS.js";function e(){if(!arguments.length)return[];var r=arguments[0];return a(r)?r:[r]}export{e as c};
|
import{ce as a}from"./index-D5veDkuG.js";function e(){if(!arguments.length)return[];var r=arguments[0];return a(r)?r:[r]}export{e as c};
|
||||||
@@ -1 +1 @@
|
|||||||
import{t as n}from"./toNumber-DrayciwT.js";function t(d,f,i){return d===d&&(i!==void 0&&(d=d<=i?d:i),f!==void 0&&(d=d>=f?d:f)),d}function c(d,f,i){return i===void 0&&(i=f,f=void 0),i!==void 0&&(i=n(i),i=i===i?i:0),f!==void 0&&(f=n(f),f=f===f?f:0),t(n(d),f,i)}export{c};
|
import{t as n}from"./toNumber-a8w_gs0u.js";function t(d,f,i){return d===d&&(i!==void 0&&(d=d<=i?d:i),f!==void 0&&(d=d>=f?d:f)),d}function c(d,f,i){return i===void 0&&(i=f,f=void 0),i!==void 0&&(i=n(i),i=i===i?i:0),f!==void 0&&(f=n(f),f=f===f?f:0),t(n(d),f,i)}export{c};
|
||||||
@@ -1 +1 @@
|
|||||||
import{aF as n,aG as t,aI as l}from"./index-Ds9AETjS.js";const p=()=>n("/model-compare"),i=e=>n(`/model-compare/${e}`),u=e=>l(`/model-compare/${e}`),h=e=>t("/model-compare/stop-by-pid",{pid:e}),g=e=>t(`/model-compare/${e}/load`),C=e=>t(`/model-compare/${e}/unload`),y=async e=>{const a=await t("/model-compare/stream-chat",e);return{ok:!0,body:{getReader:()=>{const o=(a==null?void 0:a.response)||"",r=new TextEncoder,s=[o.slice(0,o.length/3),o.slice(o.length/3,2*o.length/3),o.slice(2*o.length/3)];let c=0;return{read:async()=>c>=s.length?{done:!0,value:void 0}:{done:!1,value:r.encode(s[c++])}}}}}},v=e=>t("/model-compare/chat-with-port",e);export{i as a,y as b,v as c,u as d,p as g,g as l,h as s,C as u};
|
import{aF as n,aG as t,aI as l}from"./index-D5veDkuG.js";const p=()=>n("/model-compare"),i=e=>n(`/model-compare/${e}`),u=e=>l(`/model-compare/${e}`),h=e=>t("/model-compare/stop-by-pid",{pid:e}),g=e=>t(`/model-compare/${e}/load`),C=e=>t(`/model-compare/${e}/unload`),y=async e=>{const a=await t("/model-compare/stream-chat",e);return{ok:!0,body:{getReader:()=>{const o=(a==null?void 0:a.response)||"",r=new TextEncoder,s=[o.slice(0,o.length/3),o.slice(o.length/3,2*o.length/3),o.slice(2*o.length/3)];let c=0;return{read:async()=>c>=s.length?{done:!0,value:void 0}:{done:!1,value:r.encode(s[c++])}}}}}},v=e=>t("/model-compare/chat-with-port",e);export{i as a,y as b,v as c,u as d,p as g,g as l,h as s,C as u};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user