Biraboro's picture
Upload folder using huggingface_hub
9d614f1 verified
Raw History Blame Contribute Delete
5.84 kB
#!/usr/bin/env python3
"""RSS AI Engine - application entry point.
python RSS_AI_Engine/main.py --mode clean stream + clean data only
python RSS_AI_Engine/main.py --mode pretrain Stage-1 continual pre-training only
python RSS_AI_Engine/main.py --mode finetune Stage-2 rational SFT only
python RSS_AI_Engine/main.py --mode train_all purge -> fresh HF model -> clean -> pretrain -> finetune -> upload
python RSS_AI_Engine/main.py --mode serve load the trained model -> Telegram bot or CLI chat
Extras: --fresh-model --upload-only --skip-upload --limit 150|full --tier 1|2|3 --threads N
"""
from __future__ import annotations
import argparse
import importlib.util
import os
import signal
import subprocess
import sys
from datetime import datetime
from pathlib import Path
# Set native tokenizer/OpenMP limits BEFORE importing torch/transformers/tokenizers.
os.environ["TOKENIZERS_PARALLELISM"] = "false"
os.environ["OMP_NUM_THREADS"] = "2"
os.environ["MKL_NUM_THREADS"] = "2"
ROOT = Path(__file__).resolve().parent
ENV_TEMPLATE = """HF_TOKEN="hf_PUT_YOUR_TOKEN_HERE"
TELEGRAM_BOT_TOKEN=""
TELEGRAM_CHAT_ID=""
BASE_MODEL_REPO="Qwen/Qwen2.5-0.5B-Instruct"
OUTPUT_HF_REPO="Biraboro/rss-ai-trained"
SOURCE_REPOS='[{"repo": "Biraboro/Youtop-trenscprit", "limit": 150}]'
LOCAL_CHAT_FILE="./chat.jsonl"
JSON_PRETRAIN_QUOTA="0.15"
PRETRAIN_CONV_RATIO="0.25"
PURGE_OLD_MODEL_ON_START="true"
EXTRA_BLOCKED_EXTENSIONS=""
"""
# (import name, pip name)
REQUIRED = [("torch", "torch"), ("transformers", "transformers"), ("accelerate", "accelerate"),
("peft", "peft"), ("datasets", "datasets"), ("huggingface_hub", "huggingface_hub"),
("safetensors", "safetensors"), ("psutil", "psutil"), ("dotenv", "python-dotenv"),
("requests", "requests")]
def ensure_env_file() -> None:
env = ROOT / ".env"
if not env.exists():
env.write_text(ENV_TEMPLATE, encoding="utf-8")
print(f"[Setup] created {env} - put your HF_TOKEN (write access) in it before uploading")
def _pip(package: str) -> None:
base = [sys.executable, "-m", "pip", "install", "-q", package]
if subprocess.call(base) != 0:
# PEP 668 "externally managed" interpreters (Debian/Ubuntu system python) need this flag
subprocess.check_call(base + ["--break-system-packages"])
def _cuda_present() -> bool:
try:
import torch
return bool(torch.cuda.is_available())
except Exception:
return False
def ensure_dependencies() -> None:
missing = [pip for mod, pip in REQUIRED if importlib.util.find_spec(mod) is None]
for package in missing:
print(f"[Setup] installing {package} ...")
_pip(package)
if _cuda_present() and importlib.util.find_spec("bitsandbytes") is None:
print("[Setup] CUDA detected -> installing bitsandbytes (4-bit NF4)")
try:
_pip("bitsandbytes")
except Exception as exc:
print(f"[Setup] bitsandbytes install failed ({exc}); GPU tier will run without 4-bit")
if not missing:
print("[Setup] all dependencies present")
def parse_args(argv=None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description="RSS AI Engine", formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("--mode", choices=["clean", "pretrain", "finetune", "train_all", "serve"], default="serve",
help="clean | pretrain | finetune | train_all | serve (default: serve)")
parser.add_argument("--fresh-model", action="store_true",
help="purge old checkpoints/offload files and download the base model again")
parser.add_argument("--upload-only", action="store_true", help="only upload ./trained_model to OUTPUT_HF_REPO")
parser.add_argument("--skip-upload", action="store_true", help="train_all without the final upload")
parser.add_argument("--limit", default=None, help="override every repo limit: a number or 'full'")
parser.add_argument("--tier", type=int, choices=[1, 2, 3], default=None, help="force execution tier")
parser.add_argument("--threads", type=int, default=None, help="CPU threads for torch")
parser.add_argument("--no-install", action="store_true", help="do not auto-install missing dependencies")
return parser.parse_args(argv)
def _handle_sigterm(signum, _frame) -> None:
stamp = datetime.now().astimezone().isoformat(timespec="seconds")
print(f"\n[Runtime] received SIGTERM ({signum}) at {stamp}; "
"external supervisor/timeout or user termination is possible; sender is not identifiable here.",
flush=True)
raise SystemExit(128 + int(signum))
def main(argv=None) -> int:
try:
signal.signal(signal.SIGTERM, _handle_sigterm)
except (AttributeError, ValueError):
pass
args = parse_args(argv)
ensure_env_file()
if not args.no_install:
ensure_dependencies()
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
try:
import torch
torch.set_num_threads(2)
try:
torch.set_num_interop_threads(2)
except RuntimeError:
# Inter-op pool may already be initialised by an embedding host.
pass
print(f"[Runtime] torch threads={torch.get_num_threads()} interop={torch.get_num_interop_threads()}",
flush=True)
except ImportError:
pass
from Integration_Layer import engine_orchestrator as orchestrator
cfg = orchestrator.build_config(args)
try:
orchestrator.run(args.mode, cfg, args)
except FileNotFoundError as exc:
print(f"\n[RSS] {exc}")
return 2
except KeyboardInterrupt:
print("\n[RSS] interrupted")
return 130
return 0
if __name__ == "__main__":
sys.exit(main())