Commit Graph
226 Commits
Author SHA1 Message Date
Philipp Emanuel Weidmann 753c9b6635 feat: make adjective describing the type of model produced configurable 2026-10-31 17:22:27 +05:30
Matt Van Horn d16e020116 fix: rename reproduce checksum file to SHA256SUMS.txt (#479)
* fix: rename reproduce checksum file to SHA256SUMS.txt

Hugging Face renders relative links without a file extension as
directory links (/tree/), so the SHA256SUMS link in the reproduce
README opened a 404. Giving the file a .txt extension keeps the link
relative while making it open as a file, like the other reproduce
assets. The verification instruction becomes sha256sum -c SHA256SUMS.txt.

Fixes #463

* test: remove reproduce folder tests
2026-10-31 14:57:28 +05:30
UnstableLlama 6e9e397933 feat: add config.nosyco.toml (#349) 2026-10-30 20:28:40 +05:30
Varun Balani a2e626fd03 perf(ara): compute closure outputs via low-rank update instead of dense weight (#499)
* perf(ara): compute closure outputs via low-rank update instead of dense weight

ARA's modify_model rebuilt the full dense effective weight (W_base + B@A)
and ran a full input @ W_eff.T on every L-BFGS closure evaluation, which
dominates the per-trial cost.

Since input @ W_base.T equals the captured module output (minus bias) and
is constant across closures, and the LoRA update is low-rank, the closure
output is input_output_base + (input @ A.T) @ B.T. This avoids both the
dense [d_out x d_in] weight materialization and the full dense matmul; only
the rank-r term is recomputed. With preserve_row_magnitudes, row
normalization becomes a per-output-row diagonal rescale applied to the
outputs, so the identity holds there too.

Algebraically exact (rel err ~1e-5); not bit-identical due to changed
float summation order. modify-step speedup ~1.4x (gemma-2-2b/H100) to
~3.2x (Qwen2.5-7B/H100), scaling with layer size; final-model Pareto
front is preserved.

* test: assert low-rank ARA closure equals the dense formulation

Adds an equivalence test that runs both the original dense closure
(build W_eff, multiply) and the low-rank reformulation on the same
inputs and asserts they match to float tolerance, with and without
preserve_row_magnitudes. This guards the reformulation's correctness
directly, rather than relying on bit-identical output hashes (which
necessarily change under any FLOP-reordering optimization).

* simplify: assume bias-free linear modules in ARA closure

The dense formulation already matches the raw weight product (input @
W.T) against the captured output, so a biased module was a pre-existing
apples-to-oranges comparison. Rather than reconstruct the bias-free base
from the captured output, assert the modules are bias-free (true for the
o_proj / down_proj modules ARA targets across essentially all current
LLMs). The captured output is then exactly input @ W_base.T, so the
closure uses it directly and the separate *_base variables are gone.
No behavior change for the bias-free modules ARA runs on.

* fix(ara): handle linear modules with a bias term

The captured module output is input @ W_base.T + bias, but ARA only modifies
the weight. Instead of asserting that no bias is present (which fails on
gpt-oss), subtract the bias to get the bias-free product, add the low-rank
update and apply the row-norm rescale to that, then add the bias back. This
also fixes the original dense closure, which compared input @ W_eff.T against
a bias-inclusive output for such models. Bias-free modules are unchanged.

Extend the equivalence test to cover modules with a bias, with and without
row normalization.

* test: update ARA output hashes for the low-rank closure

Only the two ARA tests (gpt-oss, seed-oss) change. Hashes taken from CI on
ubuntu-latest: gpt-oss is identical on Python 3.10-3.13; seed-oss has one hash
for 3.10-3.12 (SHA256SUMS.ci) and another for 3.13 (SHA256SUMS.ci2).

* fix(ara): compute the base product in float32 instead of reusing the captured output

The low-rank closure used the hook's captured module output as the constant
input @ W_base.T term. That output was computed in the model's dtype (e.g.
bfloat16), which is about 3e-3 relative off from the float32 product the
original dense closure used, so the optimizer saw a different objective.

Compute input @ W_base.T once per module from the float32 weight instead. This
matches the dense closure to float32 rounding (about 1e-6 relative on
gemma-3-270m) while keeping the low-rank speedup.

* test: update ARA output hashes for the float32 base product

* ci: update hash for deepseek-v4-ara test

* ci: update hash for deepseek-v4-ara test
2026-10-29 20:39:42 +05:30
Chris Leeandumran666 efb1be6e3f fix: support flattened hyper-connection residual streams (#525)
* fix: support flattened hyper-connection residual streams

Qwen4-Exp flattens its hc_count residual streams into the component
dimension, so its hidden states have shape (prompt, position, streams *
hidden_size) instead of the 4D layout handled by #519. Collapse the flattened
streams to their mean before stacking residuals.

* fix: collect Qwen4-Exp shared experts as ablation targets

Qwen4-Exp uses the singular mlp.shared_expert spelling, while DeepSeek and
GLM use the plural mlp.shared_experts spelling. Collect both.

Co-authored-by: umran666 <shaikumran666@gmail.com>

* test: update qwen3.5-moe hashes for shared experts

Collecting mlp.shared_expert.down_proj adds it to the abliterable modules
for Qwen3.5-MoE, so the merged model changes. Add the new Linux and
Python 3.10 CI hashes.

---------

Co-authored-by: umran666 <shaikumran666@gmail.com>
2026-10-28 22:13:40 +05:30
Vinay Umretheandbenthecarman d34dd9192d fix: Improve response prefix logic, handle default behaviors, and clarify documentation (#523)
* fix: Handle the default thinking behavior

Some chat-templates insert full CoT block when thinking is *disabled* by Default like Qwen3.5-2B. And some models by Default only insert the CoT initializer (somtimes possibly with whitespaces) like Qwen3.5-4B when thinking is *enabled*.

Co-authored-by: benthecarman <benthecarman@live.com>

* chore: Update CI test hashes

Removing $ from pattern_initializer broke the test model mistral-3, which was expected

* fix: Several fixes, docs update, cleanup

* chore: Update Windows test hash

very much expected, and looks good to me

* chore: Update CI test hashes

* chore: Update CI test hash (alternate)

good to see that it caught the third hash as well. Anyway, the GitHub CI remains terrible for such tests

* fix: minor change

* fix: Override the system prompt for the dummy_prompt case

This is correct for models which insert instructions about 'How to think' in the system prompt, which can cause false-positives when searching the dummy_prompt

* chore: Update CI test hashes

* chore: Update CI test hashes

* fix: minor change

* fix: Add alternate CI test hash

I'm not liking having a fourth hash btw

---------

Co-authored-by: benthecarman <benthecarman@live.com>
2026-10-26 23:43:56 +05:30
benthecarman 80f81c90be feat: support models with Hyper-Connections (#519)
DeepSeek V4 and GLM 5.3 have several residual streams per token instead
of one, so their hidden states have an extra dimension. Heretic crashed
computing refusal directions for GLM 5.3, and found no modules to
abliterate in DeepSeek V4.

Use the mean of the streams as the residual. The streams are mixed with
each other by a doubly stochastic matrix, so their mean only changes by
what the blocks add to it, like an ordinary residual.

Also abliterate DeepSeek V4's attention output (o_b_proj) and the shared
experts of MoE models. Both models fuse their routed experts into one
module, so the shared experts are the only MLP weights Heretic can edit.
2026-10-26 14:53:39 +05:30
dependabot[bot] 171bb98b8b build(deps): bump aiohttp from 3.14.3 to 3.14.4 (#510)
---
updated-dependencies:
- dependency-name: aiohttp
  dependency-version: 3.14.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-24 18:01:01 +05:30
dependabot[bot] 17ff311456 build(deps): bump setuptools from 84.0.0 to 84.1.0 (#524)
Bumps [setuptools](https://github.com/pypa/setuptools) from 84.0.0 to 84.1.0.
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/setuptools/compare/v84.0.0...v84.1.0)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-version: 84.1.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-24 13:52:42 +05:30
kaixin liu 07313bab52 fix: reject blank refusal keyword markers (#512)
* Reject blank refusal keyword markers

* fix: reject empty keyword marker lists

* test: expect validation error for empty keyword marker lists
2026-10-23 18:55:58 +05:30
Philipp Emanuel Weidmann 09271b6202 ci: add Python 3.14 and 3.15 to CI matrix (#511) 2026-10-20 23:52:02 +05:30
Ayush Vishwakarma bc077540a0 feat: make upload tags configurable (#506) 2026-10-20 22:12:06 +05:30
Varun Balaniandjayhemnani9910 1bdb5bcc5a fix: support models whose chat template rejects the system role (#498)
Models like Gemma 2 and its fine-tunes reject the system role in their
chat template. generate() always prepends a system message, so the
template raises 'System role not supported' and the dtype loop gives up,
making these models impossible to load.

Detect once (right after the tokenizer loads) whether the template
accepts a system role, by rendering a probe chat and checking a sentinel
system message survives in the output (this catches both templates that
raise and templates that silently drop the role). When the role isn't
supported, fold every system message into the following user message
(dropping empty system prompts), so a system role anywhere in the chat
is removed before rendering, not just a leading one. Templates that
support the system role are unaffected, so their rendered chats and
resulting weights are unchanged.

Supersedes #483.

Co-authored-by: jayhemnani9910 <jayhemnani9910@users.noreply.github.com>
2026-10-20 21:47:51 +05:30
Jay Hemnani eabd1d75fb fix: restore stored plugin settings as they are on reproduce and resume (#484)
* fix: restore stored plugin settings as they are on reproduce and resume

Settings.model_validate still reads config.toml, environment variables and
the command line, and merges plugin tables key by key. A [modifier.X] or
[scorer.X] key in the local config.toml that the stored settings don't have
was added to a --reproduce run or a resumed study, so a reproduction could
build a different model (hash mismatch).

* fix: restore every stored table, not just plugin tables

Stored values already win over config.toml, but pydantic-settings merges
tables key by key, so max_memory picked up local keys too, and a plugin
table that only exists in config.toml was added to the run. Restore each
stored table as it is and drop tables that weren't stored.

* fix: restore stored settings without reading other sources

Validate stored settings through StoredSettings, whose only source is
the values passed in, instead of letting config.toml, the environment
and the command line merge into them and overwriting afterwards. Fields
that are never stored (exclude=True) are copied from the settings this
run was started with.

* refactor: restore stored settings on top of the current ones

Validate the current settings' fields with the stored values laid over
them, instead of copying fields with exclude=True one by one. Fields
that are never stored keep the values this run was started with, and
plugin tables still come only from the stored values.

* fix: take only never-stored settings from the current run

* refactor: rename from_stored to model_validate_init_only
2026-10-19 20:07:50 +05:30
Hamed Shams 7f86616b61 fix: say which check found the Chain-of-Thought prefix (#489) 2026-10-19 19:45:58 +05:30
Philipp Emanuel Weidmann 3a5c864527 chore: add pull request template (#502) 2026-10-17 18:29:38 +05:30
Hamed Shams 66a952b49b fix: report a save or upload before re-ablating the model (#488) 2026-10-12 17:53:19 +05:30
Philipp Emanuel Weidmann 49b56e5af4 fix: skip non-metrics in benchmark output (#486)
Fixes #468
2026-10-07 18:16:53 +05:30
Philipp Emanuel Weidmann fd7c77e5c3 fix: silence warning spam from PyTorch's C++ logging (#485)
Fixes #467
2026-10-07 17:14:17 +05:30
Jay Hemnani 17f8dff09f fix: escape prompts and responses printed with print_responses (#482)
Text such as `[/INST]` in a prompt or response was parsed as Rich markup, which crashed the run with a MarkupError. Other bracketed text like `[bird migration]` was silently removed from the output.
2026-10-06 13:08:04 +05:30
benthecarman 8ae8526c9d fix: prevent empty layer range and too many neighbors in ARA (#478)
* Cap ARA's neighbor count at the number of prompts

The nearest neighbors are selected from the outputs for the good and
bad prompts, which have one row per prompt. With fewer than 15 prompts
in either dataset, the optimizer could pick a neighbor count larger
than that, and torch.topk would crash with "selected index k out of
range". Bounding the search range instead of clamping the value keeps
the optimizer from wasting trials on counts that would all behave the
same.

* Render chat templates with a fixed date

Some chat templates, including gpt-oss's, put the current date in the
system prompt. That makes the residuals, and with them the modified
model, depend on the day Heretic runs, so a model could not be
reproduced on a different day. It also made the gpt-oss test's hash
change daily once ARA started modifying the model.

Prompts used for computing residuals and for evaluation are now
rendered with a fixed date. Interactive chat keeps the real one.

* Prevent ARA from sampling an empty layer range

The start index was sampled from [0, L//2] and the exclusive end index
from [L//2, L], so both could land on L//2 and leave no layers to
modify, wasting the trial. Starting the end range at L//2 + 1 rules
this out while keeping the bounds fixed, as multivariate TPE requires.

On the 2-layer test models, every ARA trial had picked this empty
range, so the seed-oss and gpt-oss tests never ran the optimization.
They now modify layer 1, which changes their expected hashes. The
seed-oss output also differs between the CPUs of GitHub's runners,
because L-BFGS amplifies small floating-point differences, so it gets
CI hashes as well.
2026-10-06 12:40:51 +05:30
benthecarman 208c0ca35b fix: stop lora_A from carrying over between trials (#476)
* Re-initialize ARA's lora_A deterministically

ARA optimizes both LoRA matrices in place, but the model reset between
trials only zeroes lora_B. Each trial therefore started its
optimization from whatever lora_A the previous trial left behind.
Restoring a trial after the study re-optimized it from a different
starting point, so the exported model was not the one that earned the
trial's scores, and reproduction could not match hashes.

Re-initialize lora_A for every optimized module from a generator
seeded with the run seed. This makes the starting point independent of
trial history, the device and the global RNG state.

* Zero lora_A when resetting the model

The fast reset path only zeroed lora_B, and a fresh adapter gets lora_A
from PEFT's random init. Modules a trial doesn't modify therefore kept
whatever lora_A an earlier trial left, or a random one. The model is
unaffected since B is zero, but saved adapters differed byte-for-byte
depending on trial history, breaking hash verification for adapter
exports.

Zero lora_A as well, both on reset and right after applying LoRA.
2026-10-04 17:31:40 +05:30
Vinay Umrethe a09b4eae07 chore: add new Windows test hashes (#470) 2026-10-02 15:00:57 +05:30
Philipp Emanuel Weidmann c121a4ba41 chore: update dependencies (#469)
* chore: update dependencies

* chore: add new CI test hashes

* chore: add yet more CI test hashes
2026-10-02 14:49:47 +05:30
662e4ba27e feat: modifier plugins + ARA (#446)
* feat: add modifier base class

* feat: re-implement abliteration as a modifier plugin

* fix: adjust good/bad prompts hack to match tests

* feat: support dataset specifications containing multiple individual datasets

* feat: re-implement ARA as a modifier plugin

Arbitrary-Rank Ablation (ARA) (Weidmann 2026) was originally introduced by @p-e-w in #211

Co-authored-by: kabachuha <artemkhrapov2001@yandex.ru>
Co-authored-by: joninco <joninco@bullpoint.org>
Co-authored-by: Ashar <coder3101@users.noreply.github.com>

* feat: add tests for ARA

* feat: reduce default number of trials

* ci: remove broken "semantic-pull-request" workflow

* ci: add yet another alternative model hash

---------

Co-authored-by: kabachuha <artemkhrapov2001@yandex.ru>
Co-authored-by: joninco <joninco@bullpoint.org>
Co-authored-by: Ashar <coder3101@users.noreply.github.com>
2026-10-01 14:30:07 +05:30
Vinay Umrethe 71e6d5eb38 feat: add base model to the model card (#459) 2026-09-30 19:22:57 +05:30
Vinay Umrethe 3521f8648a feat: A better logic for detecting the thinking prefix in the response. (#423)
* fix: Check response prefix.

In case if the model adds <think> at the end of user prompt and only generates </think> end, then the detection goes wrong.

* fix: Handle whitespace in CoT, remove redundant checks

* fix: Type checker

* fix: try looking at tag positions, not text end

regex

* ruff, fix import sorting

* fix: rich markup

* fix: I missed other ones

* feat: Update SHA256SUMS file hashes in the tests.

A major change that affects reproducibility.

* fix: It is now sensible to also update the extra SHA256SUMS.ci2 file.

* fix: Only consider whitespace, no other text or instructions.

because mistral-3 as additional reasoning instructions in its chat template. And I suppose many other models can have it too.

* fix: Update windows hashes.

* fix: Update CI hashes too

* as always update case two of mistral-3 (ci2 hash)

* docs: update comment

* feat: Handle the edge case for models having additional instructions.

* fix: Update windows hash for mistral-3

* docs: remove a line from the comments

because I'm not sure about GPT-OSS models' thinking tags and it cannot be confirmed using an untrained tiny GPT-OSS model. And inference fallback would of course generate gibberish as the model cannot understand additional instructions about 'how to generate response and how to think' from the chat_template.

* fix: a few things.

* fix: Update hash for qwen3.5 after the whitespace fix for its response prefix.

* docs: Update comment

* fix: Update qwen3.5 hash for CI

* fix: Remove Case 2 which only serves tests

unnecessary

* fix: Hash

* fix: concern is valid enough, so we use a small text.

add a comment too

* docs: minor
2026-09-05 21:41:52 +05:30
Vinay Umrethe 515191b400 feat: support specifying a dataset's specific config/subset (#445)
* feat: Allow specifying a specific config/subset name for the datasets.

This would be useful for using a single dataset that has harmful/harmless prompt pairs in different languages stored in different configs/subsets.

* fix: setting config/subset value when loading the dataset.

* fix: minor changes
2026-09-05 18:41:08 +05:30
Philipp Emanuel Weidmann 95dda4c4db feat: add benchmark scorer (#444)
* fix: improve print output of scorers

* feat: add benchmark scorer
2026-09-03 17:49:46 +05:30
dependabot[bot] c7a44f0db7 build(deps): bump nltk from 3.10.0 to 3.10.3 (#442)
Bumps [nltk](https://github.com/nltk/nltk) from 3.10.0 to 3.10.3.
- [Release notes](https://github.com/nltk/nltk/releases)
- [Changelog](https://github.com/nltk/nltk/blob/develop/ChangeLog)
- [Commits](https://github.com/nltk/nltk/compare/v3.10.0...v3.10.3)

---
updated-dependencies:
- dependency-name: nltk
  dependency-version: 3.10.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-03 10:48:12 +05:30
Vinay Umrethe bedb94ef11 feat: Show a summary at end of tests (#425)
* feat: Show a summary at end of tests.

* fix: unnecessary
2026-08-17 22:27:44 +05:30
Philipp Emanuel Weidmann 638a583bd8 chore: bump version to 2.0.0.dev0 (#428) 2026-08-17 16:25:19 +05:30
Philipp Emanuel Weidmann 346d61673b chore: remove Gemini style guide (#427) 2026-08-17 16:19:58 +05:30
Vinay Umrethe 1a921522b2 docs: update model count in README (#424)
Update model count from 4000 to 5000 in README
2026-08-14 16:36:08 +05:30
dependabot[bot] 9069d3c754 build(deps): bump aiohttp from 3.14.1 to 3.14.3 (#422)
---
updated-dependencies:
- dependency-name: aiohttp
  dependency-version: 3.14.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-06 01:19:01 +05:30
dependabot[bot] 7675b90d64 build(deps): bump setuptools from 80.9.0 to 83.0.0 (#418)
Bumps [setuptools](https://github.com/pypa/setuptools) from 80.9.0 to 83.0.0.
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/setuptools/compare/v80.9.0...v83.0.0)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-version: 83.0.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 16:06:37 +05:30
dependabot[bot] 108fff813f build(deps): bump pillow from 12.2.0 to 12.3.0 (#416)
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.2.0 to 12.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/12.2.0...12.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 12.3.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 15:07:00 +05:30
dependabot[bot] 18eaf9533f build(deps): bump torch from 2.9.1 to 2.13.0 (#411)
Bumps [torch](https://github.com/pytorch/pytorch) from 2.9.1 to 2.13.0.
- [Release notes](https://github.com/pytorch/pytorch/releases)
- [Changelog](https://github.com/pytorch/pytorch/blob/main/RELEASE.md)
- [Commits](https://github.com/pytorch/pytorch/compare/v2.9.1...v2.13.0)

---
updated-dependencies:
- dependency-name: torch
  dependency-version: 2.12.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 23:37:59 +05:30
dependabot[bot] 20b7bf7869 build(deps): bump nltk from 3.9.4 to 3.10.0 (#415)
Bumps [nltk](https://github.com/nltk/nltk) from 3.9.4 to 3.10.0.
- [Release notes](https://github.com/nltk/nltk/releases)
- [Changelog](https://github.com/nltk/nltk/blob/develop/ChangeLog)
- [Commits](https://github.com/nltk/nltk/compare/3.9.4...v3.10.0)

---
updated-dependencies:
- dependency-name: nltk
  dependency-version: 3.10.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 16:35:01 +05:30
red40maxxerandmad-cat-lon 9b6d8a419e feat: add reproducible property to plugins (#414)
* feat: add `reproducible` property to plugins

* feat: hard stop reproducibility and actually check `reproducible` field in the evaluator

* chore: flip default

* fix: check if plugin is built-in in repro gate

* fix: ruff

* fix: kld should be reproducible

* feat: move reproducible field to scorer level

* chore: move reproducible field back to plugin level

---------

Co-authored-by: mad-cat-lon <113548315+mad-cat-lon@users.noreply.github.com>
2026-07-22 16:21:50 +05:30
nyxst4ckandnyxst4ck 44228c5c42 docs: quote pip extras install examples (#395)
* docs: quote pip extras install examples

* docs: quote research extra in analyzer messages

---------

Co-authored-by: nyxst4ck <289980115+nyxst4ck@users.noreply.github.com>
2026-07-17 13:24:20 +05:30
Vinay Umrethe 02ebf7a751 feat: Add alternate CI hash in the valid list (for qwen2.5) (#412)
* feat: Add alternate CI hash in the valid list (for qwen2.5)

* ruff
2026-07-16 18:58:48 +05:30
Andrew Barnes e7b783ed85 fix: validate scorer instance names in config (#407)
* fix: validate scorer instance names in config

* fix: run scorer config tests in CI

* fix: drop redundant UV_PYTHON env and quote unittest pattern

* fix: report precise scorer name validation errors
2026-07-15 20:28:10 +05:30
c8a254b825 feat: generic plugin system (#53)
* style: ruff

* feat(wip): populate metadata fields and allow plugins to declare what they need

* refactor: extract metadata logic to separate module

* style: placate ruff

* chore: use eos token for inferring finish reason with fallback

* fix: handle empty responses better

* style: ruff

* refactor: combine response text and metadata into single object

* refactor: clean up tagger and scorer usage

* style: ruff

* chore: remove is_refusal

* style: ruff import ordering

* feat: remove embeddings and generation traces

* feat: return all hidden states instead of just last ones

* chore: remove testing changes

* style: ruff format

* fix: mismatching stop reason identifier

* chore: update default config ordering

* chore: fix merge

* feat: allow external plugin imports

* feat: add good_residuals and bad_residuals to context metadata

* style: ruff

* chore: remove unnecessary allow extra

* chore: remove unnecessary system prompt and model name

* style: ruff

* perf: clear residuals from memory if plugin doesn't need them

* feat: support external filepaths and clean up import logic

* style: ruff

* refactor: consolidate tagger and scorer functionality into a single scorer plugin

* refactor: parent Plugin class for all plugins

* feat: support multiple scorer plugins

* refactor: type fixes

* style: satisfy ruff

* refactor: centralize scorer dataclasses

* refactor: rename MetricResult to Score

* feat: simplify plugin loading

* feat: split response metadata objects and access in evaluationContext

* style: ruff

* style: ruff

* chore: remove old tagger code

* refactor: scorer settings inherit directly from Pydantic

* refactor: move eval prompts and settings to CountRefusals and KLDivergence

* feat: move scorer config to top level and add support for scale factor

* fix: missing config for scorers

* style: ruff

* fix: scale type error

* docs: fix misleading docstring

* fix: clean up old fields

* refactor: use BaseModel for scorer settings

* chore: make scale default to 1 for safety

* refactor: get metadata dynamically through EvaluationContext

* refactor: rename CountRefusals to RefusalRate

* chore: remove unused kl_divergence config fields

* docs: restore missing comment

* refactor: remove unused code

* chore: specify settings and model field types

* refactor: rename to prompts

* refactor: move load_plugin to plugin

* style: ruff

* refactor: update optimization direction config to use StudyDirection directly

* fix: missing TypeVar

* fix: missing imports

* fix: use OptimizationDirection peoperly

* chore: remove names

* chore: remove unecessary future import

* chore: remove unused scorer imports

* refactor: objective should only return tuple of floats

* refactor: use dataclass for scorer config

* feat: support multiple instances of the same scorer

* style: ruff

* fix: nonexistent name attribute in scorer

* refactor: clear residuals and analyser

* docs: MetricResult -> Score

* fix: clean up default toml

* fix: missed renaming to RefusalRate

* chore: missing return ModuleType

* docs: add SPDX header

* docs: add SPDX header

* docs: add SPDX header

* chore: fix misleading field description leftover from old code

* chore: add newline

* chore: unused settings class

* fix: bad import

* refactor: rename ResponseText -> TextCompletion

* feat: simplify api

* refactor: rename to get_score

* feat: namespace scorer configs

* style: ruff

* fix: genericize readme intro

* chore: move init to scorer base class

* refactor: handle direction and scale outside scorer

* chore: use underscore for instance names

* fix: add scorer instance name to scores

* refactor: create structured api for scorers to access model

* refactor: rename plugin-specific Settings to PluginSettings

* feat: add instance name to plugin load logging

* style: ruff

* chore: allow extra fields for plugins

* fix: improve plugin loading logic

* chore: undo change fixed in master

* chore: remove old code

* docs: adjust docstring

* chore: cleanup import

* refactor: unnest plugin settings class and detect from type annotation

* refactor: use plain str instead of Response object with metadata

* refactor: move non evaluator-specific methods out

* refactor: use enum for StudyDirection

* refactor: no strings as type annotations

* chore: let evaluator blow up on error

* refactor: rename metrics to scores globally

* feat: separate cli and hf score displays and clean up readme logic

* fix: direction serialization ValidationError when restoring from save

* refactor: rename scorer start() to setup()

* style: ruff

* fix: remove external plugin test

* refactor: rename setup to init

* docs: formatting

* refactor: move scorers location in config

* docs: add comment describing return tensor shape

* style: ruff

* refactor: simplify scorer setting logic

* refactor: clarify plugin loading logic

* refactor: remove unnecessary hashing and inline import_module

* style: ruff

* fix: don't use classnames for readme

* refactor: don't expose heretic settings to scorer

* fix: adjust print responses logic and move to scorer config level

* refactor: separate baseline score computation

* refactor: rename hf_display to md_display

* style: ruff

* Update src/heretic/scorer.py

Co-authored-by: Philipp Emanuel Weidmann <pew@worldwidemann.com>

* Update src/heretic/scorer.py

Co-authored-by: Philipp Emanuel Weidmann <pew@worldwidemann.com>

* style: ruff

* fix: ty error

* refactor: bind Score names to parent Scorers as class property

* docs: fix doc

* docs: update comment

* style: remove changes

* chore: define default refusal markers

* style: ruff

* style: remove whitespace changes

* docs: tweak docs

* chore: cleanup from merge

* style: ruff

* fix: handle negative floating point kld

* style: formatting

* chore: remove unused code

* chore: ruff

* style: undo line removal

* style: update formatting and remove old comment

* docs: undo style change

* docs: update field description

* docs: tweak docstring

* chore: revert kld absolute value forcing

* style: ruff

* chore: cleanup

* docs: update header

* docs: update header

* refactor: remove unnecessary conditional imports

* fix: apply review omments on refusalrate

* refactor: move contract validation to plugin

* refactor: move Context to Plugin

* refactor: move init to plugin level

* refactor: move init() to plugin

* style: ruff

* docs: update SPDX header

* refactor: derive score name from scorer.score_name

* chore: no None option for baseline_score_displays

* fix: show CLI formatted metrics in trial selection

* fix: sort trials by scores

* chore: remove unnecessary from future import

* chore: remove scorer scale field

* refactor: import Context from plugin

* docs: add quote to direction

* refactor: move model_config to the end of the class

* refactor: use dataclass for consistency

* refactor: use BaseModel and store study direction as str

* docs: move docstring location

* refactor: combine scorer load and init

* refactor: use best_trials for single and multi-objective

* refactor: remove all .get()

* refactor: remove unused dataclass

* refactor: use ScorerEntry dataclass for improved code quality

* style: ruff

* chore: adapt reproducibility to plugin architecture

* chore: address PR comments

* chore: make `ScorerConfig` fields full `Field()`

* chore: address pr comments

* feat: bump to version 3 of reproduce json

* refactor: rename direction to optimization

* refactor: rename loop var

* feat: pin to dataset commit sha for reproducibility

* style: ruff

* feat: show metric as list instead of table

* chore: remove stale comment

* chore: resync with upstream

* fix: trial title formatting

* chore: single source of truth for optimization objective ordering

* feat: fail-fast when there are no optimization objectives

* chore: remove dead `verify_hashes`

* refactor: pair scores with baselines everywhere

* fix: bug

* chore: add recommendation to install heretic 1.4 for older reproduce files

* chore: adapt nohumor and noslop config files to new format

* refactor: rename refusals to residuals everywhere

* fix: merge issues

* fix: fix test configs

* Apply suggestion from @gemini-code-assist[bot]

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Apply suggestion from @gemini-code-assist[bot]

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Apply suggestion from @gemini-code-assist[bot]

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* style: ruff

* chore: validate `instance_name` early

* chore: add return type for `load_prompts`

* docs: comment typo

* docs: comments

* docs: comments

* chore: comments and spacing

* docs: comments

* Update src/heretic/evaluator.py

Co-authored-by: Vinay Umrethe <umrethevinay@gmail.com>

* refactor: rename `cli_display` to `rich_display`

* style: ruff

* fix: don't repro external plugins or local datasets

* test: adapt minicpm5 to scorer-based format

* test: adapt qwen2.5 to scorer based format

* chore: restore comment

* chore: address pr comments

* chore: remove stale `keyword_markers`

* chore: string

* style: ruff

* refactor: make KLD and keyword rate scorers default

---------

Co-authored-by: mad-cat-lon <113548315+mad-cat-lon@users.noreply.github.com>
Co-authored-by: Philipp Emanuel Weidmann <pew@worldwidemann.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Vinay Umrethe <umrethevinay@gmail.com>
2026-07-07 14:34:33 +05:30
Vinay Umrethe 7470dfd7af fix: use W_org matrix only where needed (#398)
* fix: minor change

use `W_org` matrix where needed...

* Update model.py

* Update model.py

* fix: Windows hash, remove BOM marker

* docs: Add info about test cases

* feat: Tests for row_normalization PRE & NONE

* feat: CI hash files for row_normalization PRE & NONE models

* feat: Documentation instructions about test suite

* add recommendation
2026-07-01 16:13:14 +05:30
dependabot[bot] 680c43e1bf build(deps): bump pydantic-settings from 2.13.1 to 2.14.2 (#397)
Bumps [pydantic-settings](https://github.com/pydantic/pydantic-settings) from 2.13.1 to 2.14.2.
- [Release notes](https://github.com/pydantic/pydantic-settings/releases)
- [Commits](https://github.com/pydantic/pydantic-settings/compare/v2.13.1...v2.14.2)

---
updated-dependencies:
- dependency-name: pydantic-settings
  dependency-version: 2.14.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-27 19:03:23 +05:30
Philipp Emanuel WeidmannandVinay Umrethe 0146b2760f feat: headless operation + end-to-end tests (#392)
* fix: remove notebook input shims

Closes #280

* feat: support headless operation (no interactive input)

* fix: prevent infinite loops

* feat: add end-to-end tests

* ci: run tests in CI

* ci: fix test output ordering

* fix: replace home-cooked `set_seed` function with Transformers builtin

* feat: print PyTorch config when running tests

* feat: print additional information

* experiment: try to standardize test environment

* fix: revert environment changes

* feat: support multiple valid hashes for each output file

* feat: add test output hashes for CI

* feat: add test output hashes for CI (alternative environment)

* feat: add hashes for Windows (#394)

* fix: Hash on windows

* trigger ci

* fix: prefer .yaml (used widely than .toml for model configs)

* use removeprefix

* docs: restore commet

* use removeprefix again

* tests: Add windows hash files for all test models

* trigger ci

* fix: minor cleanup

* clean merge mismatch

* remove unnecessary CRLF replace, now that we support more SUMS files

* fix: use binary mode for hashes everywhere

---------

Co-authored-by: Vinay Umrethe <umrethevinay@gmail.com>
2026-06-27 13:41:48 +05:30
UmranPros 3f68a0d4e5 fix: resolve UnicodeEncodeError on Windows during model evaluation (#389)
* fix: ensure utf-8 encoding for standard output and error to prevent UnicodeEncodeError on Windows

* fix: address bot review feedback

* refactor: deduplicate stream reconfiguration loop
2026-06-18 18:16:43 +05:30
Rocker Zhang 00185db9fc feat: let the optimizer disable MLP ablation via a 0 max_weight floor (#387)
* feat: let the optimizer disable MLP ablation via a 0 max_weight floor

The MLP max_weight lower bound was 0.8 for every component, so the optimizer
always applied at least 0.8x MLP ablation and could never turn it off, even
when ablating the MLP is pure collateral damage. Give the MLP a 0 lower bound
so the optimizer can disable it per model; attention keeps the 0.8 floor.

See #202.

* perf: skip the abliteration decomposition when the weight is 0

With a 0 max_weight the component's ablation is a no-op, and reset_model()
has already left the adapter at identity. Abort that layer/component before
the decomposition, which avoids the wasted work (and the degenerate
zero-matrix decomposition raised in review on #387).

* fix: clamp a negative MLP max_weight floor so 0 is reachable

A continuous suggest_float never samples exactly 0, so a 0 lower bound could
not actually disable the MLP. Use a small negative lower bound and clamp with
max(0, ...), which puts finite probability mass on exactly 0.
2026-06-18 13:44:45 +05:30
PetreandClaude 554a58aa0f fix: correct total trial count when adding additional trials (#385)
When a study is cancelled mid-way and the user selects 'Run additional
trials', settings.n_trials was incremented by n_additional_trials,
accumulating the original total into the new count. E.g. cancelling 200
trials at 30 and adding 10 gave n_trials=210 instead of 40, causing
'Running trial 31 of 210...' and planning 180 more trials instead of 10.

Fix by recalculating n_trials from actual completed trials + additional,
so the total reflects the new intended target, not the old one.

Fixes #379

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-17 14:58:40 +05:30