chore: 忽略离线部署包,提交安全加固、数据库初始化与文档
- .gitignore: 忽略 docker/offline 离线部署包(镜像/运行时等大文件) - 安全加固: 新增 compute/api/security.py 及各端安全测试,补充 docs/security-hardening.md - 数据库: 新增完整初始化 SQL 与 docs/database-config.md - 数据转换与评测: 修复类型检查、增强校验并补充测试 - Docker 配置与环境变量更新 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -15,27 +15,58 @@ class LlamaFactoryCommand:
|
||||
|
||||
|
||||
def _load_dataset_preview(path: Path) -> list[dict[str, Any]]:
|
||||
"""Load a preview of JSON/JSONL records from a dataset file.
|
||||
|
||||
Content-sniffs instead of trusting the extension so that BOM-prefixed files,
|
||||
JSONL files containing a single JSON array, and mislabeled extensions all work.
|
||||
"""
|
||||
if not path.exists():
|
||||
return []
|
||||
text = path.read_text(encoding="utf-8", errors="replace").strip()
|
||||
text = path.read_text(encoding="utf-8-sig", 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)
|
||||
try:
|
||||
value = json.loads(text)
|
||||
except json.JSONDecodeError:
|
||||
value = None
|
||||
if isinstance(value, list):
|
||||
return [item for item in value[:20] if isinstance(item, dict)]
|
||||
if isinstance(value, dict):
|
||||
return [value]
|
||||
return []
|
||||
items: list[dict[str, Any]] = []
|
||||
for line in text.splitlines()[:20]:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
parsed = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if isinstance(parsed, list):
|
||||
items.extend(item for item in parsed[:20] if isinstance(item, dict))
|
||||
elif isinstance(parsed, dict):
|
||||
items.append(parsed)
|
||||
if len(items) >= 20:
|
||||
break
|
||||
return items[:20]
|
||||
|
||||
|
||||
def _required_columns_for(formatting: str, columns: dict[str, Any]) -> list[str]:
|
||||
"""Required data columns per dataset format.
|
||||
|
||||
Mirrors LLaMA-Factory's leniency: optional columns (e.g. ``input`` / ``query``
|
||||
in Alpaca) are never required, only fields the format structurally needs.
|
||||
"""
|
||||
fmt = str(formatting or "").lower()
|
||||
if fmt == "sharegpt":
|
||||
return [str(columns.get("messages") or "messages")]
|
||||
if fmt in {"dpo", "rm", "kto", "ppo"}:
|
||||
return [str(columns[key]) for key in ("chosen", "rejected") if columns.get(key)]
|
||||
if fmt in {"cpt", "pt", "pretrain"}:
|
||||
return [str(columns.get("prompt") or columns.get("text") or "text")]
|
||||
# alpaca family: prompt (instruction) + response (output) required,
|
||||
# query (input) / history are optional and common to omit in jsonl datasets.
|
||||
return [str(columns[key]) for key in ("prompt", "response") if columns.get(key)]
|
||||
|
||||
|
||||
def _validate_dataset_columns(config: dict[str, Any]) -> list[str]:
|
||||
@@ -51,7 +82,7 @@ def _validate_dataset_columns(config: dict[str, Any]) -> list[str]:
|
||||
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]
|
||||
required_columns = _required_columns_for(str(item.get("formatting") or ""), columns)
|
||||
for name in file_names:
|
||||
if not name:
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user