feat: allow specifying a custom config file path (#557)

This commit is contained in:
Nathan Sapwell authored and GitHub committed 2026-11-03 17:53:18 +05:30
1 parent 4b3a1bd8fd
commit f4cf535403
3 files changed
+66 -2

No files matched your search

+3 -1
View File
@@ -85,7 +85,9 @@ Replace `Qwen/Qwen3-4B-Instruct-2507` with whatever model you want to process.
**By default, Heretic performs censorship removal (refusal suppression).**
If you want to do something else, choose one of the configuration files linked
above, or write your own. See [`config.default.toml`](/config.default.toml)
for available configuration options.
for available configuration options. Heretic loads the configuration from
`config.toml` in the working directory by default, or from the file path in the
`HERETIC_CONFIG_FILE` environment variable.
> [!IMPORTANT]
>
+11 -1
View File
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (C) 2025-2026 Philipp Emanuel Weidmann <pew@worldwidemann.com> + contributors
import os
from enum import Enum
from typing import Any, Literal, TypeAlias
@@ -190,6 +191,15 @@ class BenchmarkSpecification(BaseModel):
)
def _config_path() -> str:
path = os.environ.get("HERETIC_CONFIG_FILE", "config.toml")
if "HERETIC_CONFIG_FILE" in os.environ and not os.path.isfile(path):
raise SystemExit(f"heretic: configuration file {path} does not exist")
return path
class Settings(BaseSettings):
model: str = Field(description="Hugging Face model ID, or path to model on disk.")
@@ -590,7 +600,7 @@ class Settings(BaseSettings):
EnvSettingsSource(settings_cls, env_prefix="HERETIC_"),
dotenv_settings,
file_secret_settings,
TomlConfigSettingsSource(settings_cls, toml_file="config.toml"),
TomlConfigSettingsSource(settings_cls, toml_file=_config_path()),
)
@classmethod
+52
View File
@@ -134,6 +134,58 @@ class ModelValidateInitOnlyTests(unittest.TestCase):
self.assertEqual(settings.max_memory, {"0": "20GiB"})
class ConfigFileSelectionTests(unittest.TestCase):
def settings(
self, config_files: dict[str, str], heretic_config: str | None = None
) -> Settings:
with tempfile.TemporaryDirectory() as directory:
for name, content in config_files.items():
Path(directory, name).write_text(content)
environment = {
key: value
for key, value in os.environ.items()
if not key.startswith("HERETIC_")
}
if heretic_config is not None:
environment["HERETIC_CONFIG_FILE"] = heretic_config
previous_directory = os.getcwd()
os.chdir(directory)
try:
with (
patch.dict(os.environ, environment, clear=True),
patch("sys.argv", ["heretic"]),
):
return Settings(model="dummy")
finally:
os.chdir(previous_directory)
def test_environment_variable_selects_file(self) -> None:
settings = self.settings({"custom.toml": "n_trials = 42\n"}, "custom.toml")
self.assertEqual(settings.n_trials, 42)
def test_default_config_toml_still_loaded(self) -> None:
settings = self.settings({"config.toml": "n_trials = 42\n"})
self.assertEqual(settings.n_trials, 42)
def test_missing_default_config_file_is_silent(self) -> None:
settings = self.settings({})
self.assertEqual(settings.n_trials, Settings.model_fields["n_trials"].default)
def test_missing_configured_file_aborts(self) -> None:
with self.assertRaisesRegex(SystemExit, "does not exist"):
self.settings({}, "missing.toml")
def test_configured_default_file_missing_aborts(self) -> None:
# Unlike a missing implicit default, this counts as a configuration error.
with self.assertRaisesRegex(SystemExit, "does not exist"):
self.settings({}, "config.toml")
class SettingsUploadTagsTests(unittest.TestCase):
def test_default_upload_tags(self) -> None:
with patch("sys.argv", ["heretic"]):