feat(settings): add explicit settings dir override for sandboxed runs

Add LORA_MANAGER_SETTINGS_DIR env var and standalone --settings-path to pin
the settings location (settings.json, cache/, wildcards/, backups/, logs/,
stats/) to an arbitrary directory. The override takes precedence over
portable mode and the platform user config dir, and skips legacy migration,
so sandboxed dev/E2E runs no longer need to write settings.json in the repo
root or collide with the real instance.

standalone.py pre-scans argv for --settings-path at import time because the
settings location is resolved before main() parses arguments. SettingsManager
portable-switch migration is a no-op while the directory is pinned.

Update the lora-manager-e2e skill (prefer --settings-path sandboxing;
start_server.py passes it through) and the lora-manager-runtime-context
skill (document precedence; inspect script honors the override).
This commit is contained in:
Will Miao
2026-08-27 00:03:50 +08:00
parent 1d3bcdfe47
commit 574dfbbe55
10 changed files with 383 additions and 30 deletions
+47 -1
View File
@@ -8,12 +8,36 @@ from typing import Any, cast
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from py.middleware.cache_middleware import cache_control
from py.middleware.error_middleware import api_json_error
from py.utils.settings_paths import ensure_settings_file
from py.utils.settings_paths import SETTINGS_DIR_ENV, ensure_settings_file
# Set environment variable to indicate standalone mode
os.environ["LORA_MANAGER_STANDALONE"] = "1"
def _apply_settings_dir_from_argv(argv=None):
"""Apply ``--settings-path`` from argv before any settings resolution runs.
Standalone resolves the settings location at import time (session logging and
the settings manager run before ``main()`` parses arguments), so pre-scan
argv and publish the explicit directory through ``LORA_MANAGER_SETTINGS_DIR``,
which ``py.utils.settings_paths`` honors in both standalone and plugin modes.
Args:
argv: Argument list to scan; defaults to ``sys.argv[1:]``.
"""
args = list(sys.argv[1:] if argv is None else argv)
for index, arg in enumerate(args):
if arg == "--settings-path" and index + 1 < len(args):
os.environ[SETTINGS_DIR_ENV] = args[index + 1]
return
if arg.startswith("--settings-path="):
os.environ[SETTINGS_DIR_ENV] = arg.split("=", 1)[1]
return
_apply_settings_dir_from_argv()
# Create mock modules for py/nodes directory - add this before any other imports
def mock_nodes_directory():
"""Create mock modules for all Python files in the py/nodes directory"""
@@ -395,6 +419,16 @@ def parse_args():
# help="Additional paths to LoRA model directories (optional if settings.json has paths)")
# parser.add_argument("--checkpoints", type=str, nargs="+",
# help="Additional paths to checkpoint model directories (optional if settings.json has paths)")
parser.add_argument(
"--settings-path",
type=str,
default=None,
metavar="DIR",
help="Explicit settings directory: settings.json, cache/, wildcards/, "
"backups/, logs/, stats/ all live under this directory. Overrides portable "
"mode and the default user config dir. Equivalent to the "
"LORA_MANAGER_SETTINGS_DIR environment variable.",
)
parser.add_argument(
"--log-level",
type=str,
@@ -414,6 +448,18 @@ async def main():
"""Main entry point for standalone mode"""
args = parse_args()
# Normalize and validate the explicit settings directory (the pre-import
# argv scan already applied it; re-derive so --settings-path wins over any
# pre-existing LORA_MANAGER_SETTINGS_DIR and is canonicalized the same way).
if args.settings_path:
settings_dir = os.path.abspath(os.path.expanduser(args.settings_path))
if os.path.exists(settings_dir) and not os.path.isdir(settings_dir):
logger.error(
"--settings-path '%s' exists but is not a directory.", settings_dir
)
return
os.environ[SETTINGS_DIR_ENV] = settings_dir
# Set log level (verbose flag overrides to DEBUG)
log_level = "DEBUG" if args.verbose else args.log_level
logging.getLogger().setLevel(getattr(logging, log_level))