alignmenter 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {alignmenter-0.1.0/src/alignmenter.egg-info → alignmenter-0.1.2}/PKG-INFO +44 -24
- {alignmenter-0.1.0 → alignmenter-0.1.2}/README.md +42 -23
- {alignmenter-0.1.0 → alignmenter-0.1.2}/pyproject.toml +2 -2
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/cli.py +103 -4
- {alignmenter-0.1.0 → alignmenter-0.1.2/src/alignmenter.egg-info}/PKG-INFO +44 -24
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter.egg-info/requires.txt +1 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_cli_helpers.py +72 -6
- alignmenter-0.1.2/tests/test_cli_run_config.py +46 -0
- alignmenter-0.1.0/tests/test_cli_run_config.py +0 -28
- {alignmenter-0.1.0 → alignmenter-0.1.2}/LICENSE +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/setup.cfg +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/__init__.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/calibration/__init__.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/calibration/analyze.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/calibration/bounds.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/calibration/diagnose.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/calibration/generate.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/calibration/label.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/calibration/optimize.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/calibration/sampling.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/calibration/validate.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/config.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/data/configs/demo_config.yaml +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/data/configs/judges/safety_prompt.txt +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/data/configs/persona/default.yaml +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/data/configs/run.yaml +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/data/configs/safety_keywords.yaml +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/data/datasets/demo_conversations.jsonl +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/judges/__init__.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/judges/authenticity_judge.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/judges/prompts.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/providers/__init__.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/providers/anthropic.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/providers/base.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/providers/classifiers.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/providers/embeddings.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/providers/judges.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/providers/local.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/providers/openai.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/reporting/__init__.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/reporting/html.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/reporting/json_out.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/run_config.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/runner.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/scorers/__init__.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/scorers/authenticity.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/scorers/safety.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/scorers/stability.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/scripts/__init__.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/scripts/bootstrap_dataset.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/scripts/calibrate_persona.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/scripts/run_openai_demo.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/scripts/sanitize_dataset.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/utils/__init__.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/utils/io.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/utils/tokens.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/utils/yaml.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter.egg-info/SOURCES.txt +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter.egg-info/dependency_links.txt +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter.egg-info/entry_points.txt +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter.egg-info/top_level.txt +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_authenticity_judge.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_calibrate_persona.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_cli_errors.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_cli_import.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_cli_init.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_config.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_judge_providers.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_offline_safety.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_persona_gpt.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_provider_local.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_provider_openai.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_providers.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_run_config_loader.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_run_openai_demo.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_runner.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_sampling.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_scorers.py +0 -0
- {alignmenter-0.1.0 → alignmenter-0.1.2}/tests/test_smoke.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: alignmenter
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Persona-aligned evaluation toolkit for auditing conversational AI authenticity, safety, and stability.
|
|
5
5
|
Author: Alignmenter
|
|
6
6
|
License: Apache License
|
|
@@ -236,6 +236,7 @@ Requires-Dist: tiktoken>=0.7.0
|
|
|
236
236
|
Requires-Dist: requests>=2.32.0
|
|
237
237
|
Provides-Extra: dev
|
|
238
238
|
Requires-Dist: pytest; extra == "dev"
|
|
239
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
239
240
|
Requires-Dist: ruff; extra == "dev"
|
|
240
241
|
Requires-Dist: build; extra == "dev"
|
|
241
242
|
Requires-Dist: twine; extra == "dev"
|
|
@@ -277,26 +278,23 @@ Unlike generic LLM evaluation frameworks, Alignmenter is purpose-built for **per
|
|
|
277
278
|
|
|
278
279
|
```bash
|
|
279
280
|
# Clone the repository
|
|
280
|
-
git clone https://github.com/
|
|
281
|
+
git clone https://github.com/justinGrosvenor/alignmenter.git
|
|
281
282
|
cd alignmenter
|
|
282
283
|
|
|
283
284
|
# Create virtual environment
|
|
284
285
|
python -m venv env
|
|
285
286
|
source env/bin/activate # On Windows: env\Scripts\activate
|
|
286
287
|
|
|
287
|
-
# Install with dev
|
|
288
|
-
pip install -e
|
|
289
|
-
|
|
290
|
-
# Optional: Install with offline safety classifier
|
|
291
|
-
pip install -e .[dev,safety]
|
|
288
|
+
# Install with dev + safety extras
|
|
289
|
+
pip install -e ./alignmenter[dev,safety]
|
|
292
290
|
```
|
|
293
291
|
|
|
294
292
|
### Install from PyPI
|
|
295
293
|
|
|
296
294
|
```bash
|
|
297
|
-
pip install alignmenter
|
|
295
|
+
pip install "alignmenter[safety]"
|
|
298
296
|
alignmenter init
|
|
299
|
-
alignmenter run --config configs/run.yaml --
|
|
297
|
+
alignmenter run --config configs/run.yaml --embedding sentence-transformer:all-MiniLM-L6-v2
|
|
300
298
|
```
|
|
301
299
|
|
|
302
300
|
> **Note**: The `safety` extra includes `transformers` for the offline safety classifier (ProtectAI/distilled-safety-roberta). Without it, Alignmenter falls back to a lightweight heuristic classifier. See [docs/offline_safety.md](https://github.com/justinGrosvenor/alignmenter/blob/main/docs/offline_safety.md) for details.
|
|
@@ -314,13 +312,16 @@ alignmenter run \
|
|
|
314
312
|
--persona configs/persona/default.yaml
|
|
315
313
|
|
|
316
314
|
# Reuse existing transcripts without hitting the provider
|
|
317
|
-
alignmenter run --config configs/run.yaml
|
|
315
|
+
alignmenter run --config configs/run.yaml
|
|
318
316
|
|
|
319
317
|
# View interactive HTML report
|
|
320
318
|
alignmenter report --last
|
|
321
319
|
|
|
322
320
|
# Sanitize a dataset in-place or to a new file
|
|
323
321
|
alignmenter dataset sanitize datasets/demo_conversations.jsonl --out datasets/demo_sanitized.jsonl
|
|
322
|
+
|
|
323
|
+
# Generate fresh transcripts (requires provider access)
|
|
324
|
+
alignmenter run --config configs/run.yaml --generate-transcripts
|
|
324
325
|
```
|
|
325
326
|
|
|
326
327
|
**Output:**
|
|
@@ -438,6 +439,14 @@ Quick links:
|
|
|
438
439
|
- **[LLM Judges](https://docs.alignmenter.com/guides/llm-judges/)** - Qualitative analysis
|
|
439
440
|
- **[Contributing](https://docs.alignmenter.com/contributing/)** - How to contribute
|
|
440
441
|
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## Case Studies
|
|
445
|
+
|
|
446
|
+
- **[Wendy's Twitter Voice](../docs/case-studies/wendys-twitter.md)** - End-to-end calibration example using the included case-study assets. *(Available when running from the source repo; not included in the PyPI wheel.)*
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
441
450
|
## Usage Examples
|
|
442
451
|
|
|
443
452
|
### Evaluate Multiple Models
|
|
@@ -490,11 +499,9 @@ alignmenter calibrate-persona \
|
|
|
490
499
|
|
|
491
500
|
```bash
|
|
492
501
|
# Remove PII before evaluation
|
|
493
|
-
alignmenter
|
|
494
|
-
--input prod_logs.jsonl \
|
|
502
|
+
alignmenter dataset sanitize prod_logs.jsonl \
|
|
495
503
|
--out datasets/sanitized.jsonl \
|
|
496
|
-
--
|
|
497
|
-
--scrub-patterns "email|phone|ssn"
|
|
504
|
+
--no-use-hashing
|
|
498
505
|
```
|
|
499
506
|
|
|
500
507
|
## Persona Configuration
|
|
@@ -646,13 +653,25 @@ make report-last
|
|
|
646
653
|
|
|
647
654
|
## Roadmap
|
|
648
655
|
|
|
649
|
-
|
|
650
|
-
-
|
|
651
|
-
-
|
|
652
|
-
-
|
|
653
|
-
-
|
|
654
|
-
-
|
|
655
|
-
-
|
|
656
|
+
### Completed ✅
|
|
657
|
+
- Three-dimensional scoring (authenticity, safety, stability)
|
|
658
|
+
- Multi-provider support (OpenAI, Anthropic, local models)
|
|
659
|
+
- HTML report cards with interactive charts
|
|
660
|
+
- Offline safety classifier (distilled-safety-roberta)
|
|
661
|
+
- LLM judges for qualitative analysis
|
|
662
|
+
- Budget guardrails and cost tracking
|
|
663
|
+
- PII sanitization tools
|
|
664
|
+
- Calibration workflow and diagnostics
|
|
665
|
+
|
|
666
|
+
### In Progress 🚧
|
|
667
|
+
- Multi-language support (non-English personas)
|
|
668
|
+
- Batch processing optimizations
|
|
669
|
+
- Additional embedding providers
|
|
670
|
+
|
|
671
|
+
### Future Considerations 💭
|
|
672
|
+
- Synthetic test case generation
|
|
673
|
+
- Custom metric plugins
|
|
674
|
+
- Advanced trait models (neural networks)
|
|
656
675
|
|
|
657
676
|
## Contributing
|
|
658
677
|
|
|
@@ -676,14 +695,15 @@ If you use Alignmenter in research, please cite:
|
|
|
676
695
|
@software{alignmenter2024,
|
|
677
696
|
title={Alignmenter: A Framework for Persona-Aligned Conversational AI Evaluation},
|
|
678
697
|
author={Alignmenter Contributors},
|
|
679
|
-
year={
|
|
680
|
-
url={https://github.com/
|
|
698
|
+
year={2025},
|
|
699
|
+
url={https://github.com/justinGrosvenor/alignmenter},
|
|
700
|
+
license={Apache-2.0}
|
|
681
701
|
}
|
|
682
702
|
```
|
|
683
703
|
|
|
684
704
|
## Support
|
|
685
705
|
|
|
686
|
-
- **Documentation**: [docs
|
|
706
|
+
- **Documentation**: [docs.alignmenter.com](https://docs.alignmenter.com)
|
|
687
707
|
- **Issues**: [GitHub Issues](https://github.com/justinGrosvenor/alignmenter/issues)
|
|
688
708
|
- **Discussions**: [GitHub Discussions](https://github.com/justinGrosvenor/alignmenter/discussions)
|
|
689
709
|
|
|
@@ -32,26 +32,23 @@ Unlike generic LLM evaluation frameworks, Alignmenter is purpose-built for **per
|
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
# Clone the repository
|
|
35
|
-
git clone https://github.com/
|
|
35
|
+
git clone https://github.com/justinGrosvenor/alignmenter.git
|
|
36
36
|
cd alignmenter
|
|
37
37
|
|
|
38
38
|
# Create virtual environment
|
|
39
39
|
python -m venv env
|
|
40
40
|
source env/bin/activate # On Windows: env\Scripts\activate
|
|
41
41
|
|
|
42
|
-
# Install with dev
|
|
43
|
-
pip install -e
|
|
44
|
-
|
|
45
|
-
# Optional: Install with offline safety classifier
|
|
46
|
-
pip install -e .[dev,safety]
|
|
42
|
+
# Install with dev + safety extras
|
|
43
|
+
pip install -e ./alignmenter[dev,safety]
|
|
47
44
|
```
|
|
48
45
|
|
|
49
46
|
### Install from PyPI
|
|
50
47
|
|
|
51
48
|
```bash
|
|
52
|
-
pip install alignmenter
|
|
49
|
+
pip install "alignmenter[safety]"
|
|
53
50
|
alignmenter init
|
|
54
|
-
alignmenter run --config configs/run.yaml --
|
|
51
|
+
alignmenter run --config configs/run.yaml --embedding sentence-transformer:all-MiniLM-L6-v2
|
|
55
52
|
```
|
|
56
53
|
|
|
57
54
|
> **Note**: The `safety` extra includes `transformers` for the offline safety classifier (ProtectAI/distilled-safety-roberta). Without it, Alignmenter falls back to a lightweight heuristic classifier. See [docs/offline_safety.md](https://github.com/justinGrosvenor/alignmenter/blob/main/docs/offline_safety.md) for details.
|
|
@@ -69,13 +66,16 @@ alignmenter run \
|
|
|
69
66
|
--persona configs/persona/default.yaml
|
|
70
67
|
|
|
71
68
|
# Reuse existing transcripts without hitting the provider
|
|
72
|
-
alignmenter run --config configs/run.yaml
|
|
69
|
+
alignmenter run --config configs/run.yaml
|
|
73
70
|
|
|
74
71
|
# View interactive HTML report
|
|
75
72
|
alignmenter report --last
|
|
76
73
|
|
|
77
74
|
# Sanitize a dataset in-place or to a new file
|
|
78
75
|
alignmenter dataset sanitize datasets/demo_conversations.jsonl --out datasets/demo_sanitized.jsonl
|
|
76
|
+
|
|
77
|
+
# Generate fresh transcripts (requires provider access)
|
|
78
|
+
alignmenter run --config configs/run.yaml --generate-transcripts
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
**Output:**
|
|
@@ -193,6 +193,14 @@ Quick links:
|
|
|
193
193
|
- **[LLM Judges](https://docs.alignmenter.com/guides/llm-judges/)** - Qualitative analysis
|
|
194
194
|
- **[Contributing](https://docs.alignmenter.com/contributing/)** - How to contribute
|
|
195
195
|
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Case Studies
|
|
199
|
+
|
|
200
|
+
- **[Wendy's Twitter Voice](../docs/case-studies/wendys-twitter.md)** - End-to-end calibration example using the included case-study assets. *(Available when running from the source repo; not included in the PyPI wheel.)*
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
196
204
|
## Usage Examples
|
|
197
205
|
|
|
198
206
|
### Evaluate Multiple Models
|
|
@@ -245,11 +253,9 @@ alignmenter calibrate-persona \
|
|
|
245
253
|
|
|
246
254
|
```bash
|
|
247
255
|
# Remove PII before evaluation
|
|
248
|
-
alignmenter
|
|
249
|
-
--input prod_logs.jsonl \
|
|
256
|
+
alignmenter dataset sanitize prod_logs.jsonl \
|
|
250
257
|
--out datasets/sanitized.jsonl \
|
|
251
|
-
--
|
|
252
|
-
--scrub-patterns "email|phone|ssn"
|
|
258
|
+
--no-use-hashing
|
|
253
259
|
```
|
|
254
260
|
|
|
255
261
|
## Persona Configuration
|
|
@@ -401,13 +407,25 @@ make report-last
|
|
|
401
407
|
|
|
402
408
|
## Roadmap
|
|
403
409
|
|
|
404
|
-
|
|
405
|
-
-
|
|
406
|
-
-
|
|
407
|
-
-
|
|
408
|
-
-
|
|
409
|
-
-
|
|
410
|
-
-
|
|
410
|
+
### Completed ✅
|
|
411
|
+
- Three-dimensional scoring (authenticity, safety, stability)
|
|
412
|
+
- Multi-provider support (OpenAI, Anthropic, local models)
|
|
413
|
+
- HTML report cards with interactive charts
|
|
414
|
+
- Offline safety classifier (distilled-safety-roberta)
|
|
415
|
+
- LLM judges for qualitative analysis
|
|
416
|
+
- Budget guardrails and cost tracking
|
|
417
|
+
- PII sanitization tools
|
|
418
|
+
- Calibration workflow and diagnostics
|
|
419
|
+
|
|
420
|
+
### In Progress 🚧
|
|
421
|
+
- Multi-language support (non-English personas)
|
|
422
|
+
- Batch processing optimizations
|
|
423
|
+
- Additional embedding providers
|
|
424
|
+
|
|
425
|
+
### Future Considerations 💭
|
|
426
|
+
- Synthetic test case generation
|
|
427
|
+
- Custom metric plugins
|
|
428
|
+
- Advanced trait models (neural networks)
|
|
411
429
|
|
|
412
430
|
## Contributing
|
|
413
431
|
|
|
@@ -431,14 +449,15 @@ If you use Alignmenter in research, please cite:
|
|
|
431
449
|
@software{alignmenter2024,
|
|
432
450
|
title={Alignmenter: A Framework for Persona-Aligned Conversational AI Evaluation},
|
|
433
451
|
author={Alignmenter Contributors},
|
|
434
|
-
year={
|
|
435
|
-
url={https://github.com/
|
|
452
|
+
year={2025},
|
|
453
|
+
url={https://github.com/justinGrosvenor/alignmenter},
|
|
454
|
+
license={Apache-2.0}
|
|
436
455
|
}
|
|
437
456
|
```
|
|
438
457
|
|
|
439
458
|
## Support
|
|
440
459
|
|
|
441
|
-
- **Documentation**: [docs
|
|
460
|
+
- **Documentation**: [docs.alignmenter.com](https://docs.alignmenter.com)
|
|
442
461
|
- **Issues**: [GitHub Issues](https://github.com/justinGrosvenor/alignmenter/issues)
|
|
443
462
|
- **Discussions**: [GitHub Discussions](https://github.com/justinGrosvenor/alignmenter/discussions)
|
|
444
463
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "alignmenter"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "Persona-aligned evaluation toolkit for auditing conversational AI authenticity, safety, and stability."
|
|
9
9
|
authors = [{name = "Alignmenter"}]
|
|
10
10
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
@@ -38,7 +38,7 @@ dependencies = [
|
|
|
38
38
|
]
|
|
39
39
|
|
|
40
40
|
[project.optional-dependencies]
|
|
41
|
-
dev = ["pytest", "ruff", "build", "twine"]
|
|
41
|
+
dev = ["pytest", "pytest-cov", "ruff", "build", "twine"]
|
|
42
42
|
safety = ["transformers>=4.30.0"]
|
|
43
43
|
|
|
44
44
|
[project.urls]
|
|
@@ -23,8 +23,10 @@ from alignmenter.providers.classifiers import load_safety_classifier
|
|
|
23
23
|
from alignmenter.providers.judges import load_judge_provider
|
|
24
24
|
from alignmenter.providers.openai import OpenAICustomGPTProvider
|
|
25
25
|
from alignmenter.run_config import load_run_options
|
|
26
|
-
from alignmenter.scripts.sanitize_dataset import sanitize_dataset_file
|
|
27
26
|
from alignmenter.runner import RunConfig, Runner
|
|
27
|
+
from alignmenter.scripts import bootstrap_dataset as bootstrap_dataset_script
|
|
28
|
+
from alignmenter.scripts import calibrate_persona as calibrate_persona_script
|
|
29
|
+
from alignmenter.scripts.sanitize_dataset import sanitize_dataset_file
|
|
28
30
|
from alignmenter.scorers.authenticity import AuthenticityScorer
|
|
29
31
|
from alignmenter.scorers.safety import SafetyScorer
|
|
30
32
|
from alignmenter.scorers.stability import StabilityScorer
|
|
@@ -513,9 +515,9 @@ def run(
|
|
|
513
515
|
judge: Optional[str] = typer.Option(None, help="Safety judge provider identifier (e.g. 'openai:gpt-4o-mini')."),
|
|
514
516
|
judge_budget: Optional[int] = typer.Option(None, help="Maximum LLM judge calls per run."),
|
|
515
517
|
generate_transcripts: bool = typer.Option(
|
|
516
|
-
|
|
517
|
-
"--generate
|
|
518
|
-
help="Call
|
|
518
|
+
False,
|
|
519
|
+
"--generate-transcripts",
|
|
520
|
+
help="Call providers to regenerate assistant turns before scoring (default reuses recorded transcripts).",
|
|
519
521
|
),
|
|
520
522
|
) -> None:
|
|
521
523
|
"""Execute an evaluation run."""
|
|
@@ -542,12 +544,25 @@ def run(
|
|
|
542
544
|
assistant_turns = _lazy_assistant_turn_counter(inputs.dataset_path)
|
|
543
545
|
_maybe_warn_about_cost(inputs, assistant_turns)
|
|
544
546
|
|
|
547
|
+
if not generate_transcripts:
|
|
548
|
+
typer.secho(
|
|
549
|
+
"Reusing recorded transcripts (pass --generate-transcripts to call providers).",
|
|
550
|
+
fg=typer.colors.BLUE,
|
|
551
|
+
)
|
|
552
|
+
|
|
545
553
|
regenerate, provider, compare_provider_obj = _initialise_providers(
|
|
546
554
|
inputs.model_identifier,
|
|
547
555
|
inputs.compare_identifier,
|
|
548
556
|
generate_transcripts,
|
|
549
557
|
)
|
|
550
558
|
|
|
559
|
+
if generate_transcripts and not regenerate:
|
|
560
|
+
typer.secho(
|
|
561
|
+
"✗ Unable to generate transcripts because provider initialization failed. Re-run without --generate-transcripts or configure API keys.",
|
|
562
|
+
fg=typer.colors.RED,
|
|
563
|
+
)
|
|
564
|
+
raise typer.Exit(code=1)
|
|
565
|
+
|
|
551
566
|
safety_classifier = load_safety_classifier(inputs.classifier_identifier)
|
|
552
567
|
judge_provider = _initialise_judge_provider(inputs.judge_identifier)
|
|
553
568
|
scorers, compare_scorers = _build_scorers_for_run(
|
|
@@ -614,6 +629,7 @@ def demo(
|
|
|
614
629
|
embedding=None,
|
|
615
630
|
judge=None,
|
|
616
631
|
judge_budget=None,
|
|
632
|
+
generate_transcripts=True,
|
|
617
633
|
)
|
|
618
634
|
|
|
619
635
|
|
|
@@ -939,6 +955,60 @@ def dataset_sanitize(
|
|
|
939
955
|
typer.echo(f" Output: {output_path}")
|
|
940
956
|
|
|
941
957
|
|
|
958
|
+
def _run_bootstrap_dataset(
|
|
959
|
+
source: Optional[Path],
|
|
960
|
+
out: Path,
|
|
961
|
+
sessions: int,
|
|
962
|
+
turns_per_session: int,
|
|
963
|
+
safety_trap_ratio: float,
|
|
964
|
+
brand_trap_ratio: float,
|
|
965
|
+
persona_id: str,
|
|
966
|
+
seed: int,
|
|
967
|
+
) -> None:
|
|
968
|
+
try:
|
|
969
|
+
bootstrap_dataset_script.bootstrap(
|
|
970
|
+
source=str(source) if source else None,
|
|
971
|
+
out=str(out),
|
|
972
|
+
sessions=sessions,
|
|
973
|
+
turns_per_session=turns_per_session,
|
|
974
|
+
safety_trap_ratio=safety_trap_ratio,
|
|
975
|
+
brand_trap_ratio=brand_trap_ratio,
|
|
976
|
+
persona_id=persona_id,
|
|
977
|
+
seed=seed,
|
|
978
|
+
)
|
|
979
|
+
except typer.Exit as exc:
|
|
980
|
+
raise exc
|
|
981
|
+
except Exception as exc: # noqa: BLE001 - surface friendly error
|
|
982
|
+
typer.secho(f"✗ Error: {exc}", fg=typer.colors.RED, err=True)
|
|
983
|
+
raise typer.Exit(1) from exc
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
@dataset_app.command("bootstrap")
|
|
987
|
+
@app.command("bootstrap-dataset")
|
|
988
|
+
def bootstrap_dataset_command(
|
|
989
|
+
source: Optional[Path] = typer.Option(None, "--source", help="Optional source dataset to augment."),
|
|
990
|
+
out: Path = typer.Option(..., "--out", help="Output JSONL path for generated data."),
|
|
991
|
+
sessions: int = typer.Option(10, "--sessions", help="Number of sessions to generate."),
|
|
992
|
+
turns_per_session: int = typer.Option(6, "--turns-per-session", help="Turns per session (user/assistant alternating)."),
|
|
993
|
+
safety_trap_ratio: float = typer.Option(0.2, "--safety-trap-ratio", help="Fraction of sessions with safety traps."),
|
|
994
|
+
brand_trap_ratio: float = typer.Option(0.2, "--brand-trap-ratio", help="Fraction of sessions with brand violations."),
|
|
995
|
+
persona_id: str = typer.Option("default_v1", "--persona-id", help="Persona identifier to annotate each turn with."),
|
|
996
|
+
seed: int = typer.Option(42, "--seed", help="Random seed for reproducibility."),
|
|
997
|
+
) -> None:
|
|
998
|
+
"""Generate a synthetic dataset with brand + safety traps."""
|
|
999
|
+
|
|
1000
|
+
_run_bootstrap_dataset(
|
|
1001
|
+
source=source,
|
|
1002
|
+
out=out,
|
|
1003
|
+
sessions=sessions,
|
|
1004
|
+
turns_per_session=turns_per_session,
|
|
1005
|
+
safety_trap_ratio=safety_trap_ratio,
|
|
1006
|
+
brand_trap_ratio=brand_trap_ratio,
|
|
1007
|
+
persona_id=persona_id,
|
|
1008
|
+
seed=seed,
|
|
1009
|
+
)
|
|
1010
|
+
|
|
1011
|
+
|
|
942
1012
|
# Calibration Commands
|
|
943
1013
|
|
|
944
1014
|
|
|
@@ -1168,6 +1238,35 @@ def analyze_scenarios(
|
|
|
1168
1238
|
raise typer.Exit(1)
|
|
1169
1239
|
|
|
1170
1240
|
|
|
1241
|
+
@app.command("calibrate-persona")
|
|
1242
|
+
def calibrate_persona_command(
|
|
1243
|
+
persona_path: Path = typer.Option(..., "--persona-path", help="Persona YAML containing the 'id' to calibrate."),
|
|
1244
|
+
dataset: Path = typer.Option(..., "--dataset", help="Labeled JSONL with persona_id + label fields."),
|
|
1245
|
+
out: Optional[Path] = typer.Option(None, "--out", help="Output path for generated .traits.json"),
|
|
1246
|
+
min_samples: int = typer.Option(25, "--min-samples", help="Minimum number of labeled examples required."),
|
|
1247
|
+
learning_rate: float = typer.Option(0.1, "--learning-rate", help="Learning rate for logistic regression."),
|
|
1248
|
+
epochs: int = typer.Option(300, "--epochs", help="Training epochs."),
|
|
1249
|
+
l2: float = typer.Option(0.0, "--l2", help="L2 regularization strength."),
|
|
1250
|
+
) -> None:
|
|
1251
|
+
"""Fit persona-specific trait weights from labeled data."""
|
|
1252
|
+
|
|
1253
|
+
try:
|
|
1254
|
+
calibrate_persona_script.calibrate(
|
|
1255
|
+
persona_path=str(persona_path),
|
|
1256
|
+
dataset=str(dataset),
|
|
1257
|
+
out=str(out) if out else None,
|
|
1258
|
+
min_samples=min_samples,
|
|
1259
|
+
learning_rate=learning_rate,
|
|
1260
|
+
epochs=epochs,
|
|
1261
|
+
l2=l2,
|
|
1262
|
+
)
|
|
1263
|
+
except typer.Exit as exc:
|
|
1264
|
+
raise exc
|
|
1265
|
+
except Exception as exc: # noqa: BLE001 - friendly surface
|
|
1266
|
+
typer.secho(f"✗ Error: {exc}", fg=typer.colors.RED, err=True)
|
|
1267
|
+
raise typer.Exit(1) from exc
|
|
1268
|
+
|
|
1269
|
+
|
|
1171
1270
|
def _load_env(path: Path) -> dict[str, str]:
|
|
1172
1271
|
if not path.exists():
|
|
1173
1272
|
return {}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: alignmenter
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Persona-aligned evaluation toolkit for auditing conversational AI authenticity, safety, and stability.
|
|
5
5
|
Author: Alignmenter
|
|
6
6
|
License: Apache License
|
|
@@ -236,6 +236,7 @@ Requires-Dist: tiktoken>=0.7.0
|
|
|
236
236
|
Requires-Dist: requests>=2.32.0
|
|
237
237
|
Provides-Extra: dev
|
|
238
238
|
Requires-Dist: pytest; extra == "dev"
|
|
239
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
239
240
|
Requires-Dist: ruff; extra == "dev"
|
|
240
241
|
Requires-Dist: build; extra == "dev"
|
|
241
242
|
Requires-Dist: twine; extra == "dev"
|
|
@@ -277,26 +278,23 @@ Unlike generic LLM evaluation frameworks, Alignmenter is purpose-built for **per
|
|
|
277
278
|
|
|
278
279
|
```bash
|
|
279
280
|
# Clone the repository
|
|
280
|
-
git clone https://github.com/
|
|
281
|
+
git clone https://github.com/justinGrosvenor/alignmenter.git
|
|
281
282
|
cd alignmenter
|
|
282
283
|
|
|
283
284
|
# Create virtual environment
|
|
284
285
|
python -m venv env
|
|
285
286
|
source env/bin/activate # On Windows: env\Scripts\activate
|
|
286
287
|
|
|
287
|
-
# Install with dev
|
|
288
|
-
pip install -e
|
|
289
|
-
|
|
290
|
-
# Optional: Install with offline safety classifier
|
|
291
|
-
pip install -e .[dev,safety]
|
|
288
|
+
# Install with dev + safety extras
|
|
289
|
+
pip install -e ./alignmenter[dev,safety]
|
|
292
290
|
```
|
|
293
291
|
|
|
294
292
|
### Install from PyPI
|
|
295
293
|
|
|
296
294
|
```bash
|
|
297
|
-
pip install alignmenter
|
|
295
|
+
pip install "alignmenter[safety]"
|
|
298
296
|
alignmenter init
|
|
299
|
-
alignmenter run --config configs/run.yaml --
|
|
297
|
+
alignmenter run --config configs/run.yaml --embedding sentence-transformer:all-MiniLM-L6-v2
|
|
300
298
|
```
|
|
301
299
|
|
|
302
300
|
> **Note**: The `safety` extra includes `transformers` for the offline safety classifier (ProtectAI/distilled-safety-roberta). Without it, Alignmenter falls back to a lightweight heuristic classifier. See [docs/offline_safety.md](https://github.com/justinGrosvenor/alignmenter/blob/main/docs/offline_safety.md) for details.
|
|
@@ -314,13 +312,16 @@ alignmenter run \
|
|
|
314
312
|
--persona configs/persona/default.yaml
|
|
315
313
|
|
|
316
314
|
# Reuse existing transcripts without hitting the provider
|
|
317
|
-
alignmenter run --config configs/run.yaml
|
|
315
|
+
alignmenter run --config configs/run.yaml
|
|
318
316
|
|
|
319
317
|
# View interactive HTML report
|
|
320
318
|
alignmenter report --last
|
|
321
319
|
|
|
322
320
|
# Sanitize a dataset in-place or to a new file
|
|
323
321
|
alignmenter dataset sanitize datasets/demo_conversations.jsonl --out datasets/demo_sanitized.jsonl
|
|
322
|
+
|
|
323
|
+
# Generate fresh transcripts (requires provider access)
|
|
324
|
+
alignmenter run --config configs/run.yaml --generate-transcripts
|
|
324
325
|
```
|
|
325
326
|
|
|
326
327
|
**Output:**
|
|
@@ -438,6 +439,14 @@ Quick links:
|
|
|
438
439
|
- **[LLM Judges](https://docs.alignmenter.com/guides/llm-judges/)** - Qualitative analysis
|
|
439
440
|
- **[Contributing](https://docs.alignmenter.com/contributing/)** - How to contribute
|
|
440
441
|
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## Case Studies
|
|
445
|
+
|
|
446
|
+
- **[Wendy's Twitter Voice](../docs/case-studies/wendys-twitter.md)** - End-to-end calibration example using the included case-study assets. *(Available when running from the source repo; not included in the PyPI wheel.)*
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
441
450
|
## Usage Examples
|
|
442
451
|
|
|
443
452
|
### Evaluate Multiple Models
|
|
@@ -490,11 +499,9 @@ alignmenter calibrate-persona \
|
|
|
490
499
|
|
|
491
500
|
```bash
|
|
492
501
|
# Remove PII before evaluation
|
|
493
|
-
alignmenter
|
|
494
|
-
--input prod_logs.jsonl \
|
|
502
|
+
alignmenter dataset sanitize prod_logs.jsonl \
|
|
495
503
|
--out datasets/sanitized.jsonl \
|
|
496
|
-
--
|
|
497
|
-
--scrub-patterns "email|phone|ssn"
|
|
504
|
+
--no-use-hashing
|
|
498
505
|
```
|
|
499
506
|
|
|
500
507
|
## Persona Configuration
|
|
@@ -646,13 +653,25 @@ make report-last
|
|
|
646
653
|
|
|
647
654
|
## Roadmap
|
|
648
655
|
|
|
649
|
-
|
|
650
|
-
-
|
|
651
|
-
-
|
|
652
|
-
-
|
|
653
|
-
-
|
|
654
|
-
-
|
|
655
|
-
-
|
|
656
|
+
### Completed ✅
|
|
657
|
+
- Three-dimensional scoring (authenticity, safety, stability)
|
|
658
|
+
- Multi-provider support (OpenAI, Anthropic, local models)
|
|
659
|
+
- HTML report cards with interactive charts
|
|
660
|
+
- Offline safety classifier (distilled-safety-roberta)
|
|
661
|
+
- LLM judges for qualitative analysis
|
|
662
|
+
- Budget guardrails and cost tracking
|
|
663
|
+
- PII sanitization tools
|
|
664
|
+
- Calibration workflow and diagnostics
|
|
665
|
+
|
|
666
|
+
### In Progress 🚧
|
|
667
|
+
- Multi-language support (non-English personas)
|
|
668
|
+
- Batch processing optimizations
|
|
669
|
+
- Additional embedding providers
|
|
670
|
+
|
|
671
|
+
### Future Considerations 💭
|
|
672
|
+
- Synthetic test case generation
|
|
673
|
+
- Custom metric plugins
|
|
674
|
+
- Advanced trait models (neural networks)
|
|
656
675
|
|
|
657
676
|
## Contributing
|
|
658
677
|
|
|
@@ -676,14 +695,15 @@ If you use Alignmenter in research, please cite:
|
|
|
676
695
|
@software{alignmenter2024,
|
|
677
696
|
title={Alignmenter: A Framework for Persona-Aligned Conversational AI Evaluation},
|
|
678
697
|
author={Alignmenter Contributors},
|
|
679
|
-
year={
|
|
680
|
-
url={https://github.com/
|
|
698
|
+
year={2025},
|
|
699
|
+
url={https://github.com/justinGrosvenor/alignmenter},
|
|
700
|
+
license={Apache-2.0}
|
|
681
701
|
}
|
|
682
702
|
```
|
|
683
703
|
|
|
684
704
|
## Support
|
|
685
705
|
|
|
686
|
-
- **Documentation**: [docs
|
|
706
|
+
- **Documentation**: [docs.alignmenter.com](https://docs.alignmenter.com)
|
|
687
707
|
- **Issues**: [GitHub Issues](https://github.com/justinGrosvenor/alignmenter/issues)
|
|
688
708
|
- **Discussions**: [GitHub Discussions](https://github.com/justinGrosvenor/alignmenter/discussions)
|
|
689
709
|
|
|
@@ -10,6 +10,9 @@ from typer.testing import CliRunner
|
|
|
10
10
|
from alignmenter import app
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
DATA_DIR = Path(__file__).resolve().parent / "data"
|
|
14
|
+
MINI_DATASET = DATA_DIR / "mini_cli_dataset.jsonl"
|
|
15
|
+
|
|
13
16
|
runner = CliRunner()
|
|
14
17
|
|
|
15
18
|
|
|
@@ -149,9 +152,75 @@ def test_dataset_sanitize_command(tmp_path: Path) -> None:
|
|
|
149
152
|
assert "[DRY RUN]" in result.output
|
|
150
153
|
|
|
151
154
|
|
|
155
|
+
def test_bootstrap_dataset_command(tmp_path: Path) -> None:
|
|
156
|
+
out_path = tmp_path / "generated.jsonl"
|
|
157
|
+
result = runner.invoke(
|
|
158
|
+
app,
|
|
159
|
+
[
|
|
160
|
+
"bootstrap-dataset",
|
|
161
|
+
"--out",
|
|
162
|
+
str(out_path),
|
|
163
|
+
"--sessions",
|
|
164
|
+
"2",
|
|
165
|
+
"--turns-per-session",
|
|
166
|
+
"2",
|
|
167
|
+
"--safety-trap-ratio",
|
|
168
|
+
"0.5",
|
|
169
|
+
"--brand-trap-ratio",
|
|
170
|
+
"0.0",
|
|
171
|
+
"--persona-id",
|
|
172
|
+
"demo_v1",
|
|
173
|
+
"--seed",
|
|
174
|
+
"7",
|
|
175
|
+
],
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
assert result.exit_code == 0, result.output
|
|
179
|
+
assert out_path.exists()
|
|
180
|
+
lines = out_path.read_text().strip().splitlines()
|
|
181
|
+
assert len(lines) == 4 # 2 sessions × 2 turns
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_calibrate_persona_command(tmp_path: Path) -> None:
|
|
185
|
+
persona_path = tmp_path / "persona.yaml"
|
|
186
|
+
persona_path.write_text("id: sample_persona\n", encoding="utf-8")
|
|
187
|
+
|
|
188
|
+
dataset_path = tmp_path / "annotations.jsonl"
|
|
189
|
+
dataset_path.write_text(
|
|
190
|
+
json.dumps({"text": "On brand reply", "label": 1, "persona_id": "sample_persona"})
|
|
191
|
+
+ "\n"
|
|
192
|
+
+ json.dumps({"text": "off brand hype", "label": 0, "persona_id": "sample_persona"})
|
|
193
|
+
+ "\n",
|
|
194
|
+
encoding="utf-8",
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
out_path = tmp_path / "persona.traits.json"
|
|
198
|
+
result = runner.invoke(
|
|
199
|
+
app,
|
|
200
|
+
[
|
|
201
|
+
"calibrate-persona",
|
|
202
|
+
"--persona-path",
|
|
203
|
+
str(persona_path),
|
|
204
|
+
"--dataset",
|
|
205
|
+
str(dataset_path),
|
|
206
|
+
"--out",
|
|
207
|
+
str(out_path),
|
|
208
|
+
"--min-samples",
|
|
209
|
+
"2",
|
|
210
|
+
"--epochs",
|
|
211
|
+
"10",
|
|
212
|
+
],
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
assert result.exit_code == 0, result.output
|
|
216
|
+
assert out_path.exists()
|
|
217
|
+
payload = json.loads(out_path.read_text())
|
|
218
|
+
assert "trait_model" in payload
|
|
219
|
+
|
|
220
|
+
|
|
152
221
|
def test_run_command(tmp_path: Path) -> None:
|
|
153
222
|
root = Path(__file__).resolve().parents[2] / "alignmenter"
|
|
154
|
-
dataset =
|
|
223
|
+
dataset = MINI_DATASET
|
|
155
224
|
persona = root / "configs" / "persona" / "default.yaml"
|
|
156
225
|
keywords = root / "configs" / "safety_keywords.yaml"
|
|
157
226
|
|
|
@@ -169,7 +238,6 @@ def test_run_command(tmp_path: Path) -> None:
|
|
|
169
238
|
str(keywords),
|
|
170
239
|
"--out",
|
|
171
240
|
str(tmp_path),
|
|
172
|
-
"--no-generate",
|
|
173
241
|
],
|
|
174
242
|
)
|
|
175
243
|
|
|
@@ -185,7 +253,7 @@ def test_run_command(tmp_path: Path) -> None:
|
|
|
185
253
|
|
|
186
254
|
def test_run_command_with_compare(tmp_path: Path) -> None:
|
|
187
255
|
root = Path(__file__).resolve().parents[2] / "alignmenter"
|
|
188
|
-
dataset =
|
|
256
|
+
dataset = MINI_DATASET
|
|
189
257
|
persona = root / "configs" / "persona" / "default.yaml"
|
|
190
258
|
keywords = root / "configs" / "safety_keywords.yaml"
|
|
191
259
|
|
|
@@ -205,7 +273,6 @@ def test_run_command_with_compare(tmp_path: Path) -> None:
|
|
|
205
273
|
str(keywords),
|
|
206
274
|
"--out",
|
|
207
275
|
str(tmp_path),
|
|
208
|
-
"--no-generate",
|
|
209
276
|
],
|
|
210
277
|
)
|
|
211
278
|
|
|
@@ -224,7 +291,7 @@ def test_run_command_threshold_failure(tmp_path: Path) -> None:
|
|
|
224
291
|
[
|
|
225
292
|
"run_id: threshold",
|
|
226
293
|
"model: openai:gpt-4o-mini",
|
|
227
|
-
f"dataset: {
|
|
294
|
+
f"dataset: {MINI_DATASET}",
|
|
228
295
|
f"persona: {root / 'configs' / 'persona' / 'default.yaml'}",
|
|
229
296
|
f"keywords: {root / 'configs' / 'safety_keywords.yaml'}",
|
|
230
297
|
"scorers:",
|
|
@@ -243,7 +310,6 @@ def test_run_command_threshold_failure(tmp_path: Path) -> None:
|
|
|
243
310
|
"run",
|
|
244
311
|
"--config",
|
|
245
312
|
str(config_path),
|
|
246
|
-
"--no-generate",
|
|
247
313
|
],
|
|
248
314
|
)
|
|
249
315
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Tests CLI integration with environment config defaults."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from typer.testing import CliRunner
|
|
8
|
+
|
|
9
|
+
from alignmenter import app
|
|
10
|
+
|
|
11
|
+
DATA_DIR = Path(__file__).resolve().parent / "data"
|
|
12
|
+
MINI_DATASET = DATA_DIR / "mini_cli_dataset.jsonl"
|
|
13
|
+
|
|
14
|
+
runner = CliRunner()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_cli_run_uses_run_config_file(tmp_path: Path) -> None:
|
|
18
|
+
root = Path(__file__).resolve().parents[2] / "alignmenter"
|
|
19
|
+
persona = root / "configs" / "persona" / "default.yaml"
|
|
20
|
+
keywords = root / "configs" / "safety_keywords.yaml"
|
|
21
|
+
|
|
22
|
+
config_path = tmp_path / "mini_config.yaml"
|
|
23
|
+
config_path.write_text(
|
|
24
|
+
"\n".join(
|
|
25
|
+
[
|
|
26
|
+
"run_id: cli-mini",
|
|
27
|
+
f"model: openai:gpt-4o-mini",
|
|
28
|
+
f"dataset: {MINI_DATASET}",
|
|
29
|
+
f"persona: {persona}",
|
|
30
|
+
f"keywords: {keywords}",
|
|
31
|
+
]
|
|
32
|
+
)
|
|
33
|
+
+ "\n",
|
|
34
|
+
encoding="utf-8",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
result = runner.invoke(
|
|
38
|
+
app,
|
|
39
|
+
[
|
|
40
|
+
"run",
|
|
41
|
+
"--config", str(config_path),
|
|
42
|
+
"--out", str(tmp_path),
|
|
43
|
+
],
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
assert result.exit_code == 0, result.output
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""Tests CLI integration with environment config defaults."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
from typer.testing import CliRunner
|
|
8
|
-
|
|
9
|
-
from alignmenter import app
|
|
10
|
-
|
|
11
|
-
runner = CliRunner()
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def test_cli_run_uses_run_config_file(tmp_path: Path) -> None:
|
|
15
|
-
root = Path(__file__).resolve().parents[2] / "alignmenter"
|
|
16
|
-
config_path = root / "configs" / "demo_config.yaml"
|
|
17
|
-
|
|
18
|
-
result = runner.invoke(
|
|
19
|
-
app,
|
|
20
|
-
[
|
|
21
|
-
"run",
|
|
22
|
-
"--config", str(config_path),
|
|
23
|
-
"--out", str(tmp_path),
|
|
24
|
-
"--no-generate",
|
|
25
|
-
],
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
assert result.exit_code == 0, result.output
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/data/configs/judges/safety_prompt.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alignmenter-0.1.0 → alignmenter-0.1.2}/src/alignmenter/data/datasets/demo_conversations.jsonl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|