Thanks for your interest. This document covers everything you need to get set up, understand the codebase, and submit a contribution.
- Python 3.10 or higher
- Git
- Ollama (optional, for local model testing)
- A Groq API key (free at console.groq.com)
git clone https://github.com/RudraDudhat2509/diffprompt
cd diffprompt
pip install -e ".[dev]"The [dev] extra installs pytest, pytest-asyncio, and other dev dependencies defined in pyproject.toml.
export GROQ_API_KEY=your_key_herepytest tests/ -v
python examples/similarity_playground.py
python examples/ontology_inspect.pyAll three should pass without errors before you make any changes.
diffprompt/
diffprompt/
cli.py Entry point. Orchestrates the full pipeline.
models/
__init__.py All Pydantic data models. Start here to understand data flow.
cascade.py LLM client. Ollama first, Groq fallback.
core/
ontology.py Infers dimensions from prompt, tags test inputs.
generator.py Creates test cases across four taxonomy buckets.
embedder.py Local embeddings and similarity scoring.
runner.py Runs both prompts on all test cases concurrently.
judge.py LLM-as-judge; determines verdict per diff.
clusterer.py HDBSCAN clustering of failure modes.
slicer.py Behavioral slicing by input dimension.
scorer.py Regression score and key example selection.
output/
terminal.py Rich terminal renderer.
exporter.py JSON/HTML export.
tests/
test_embedder.py
test_generator.py
test_slicer.py
examples/
basic_diff.py
ontology_inspect.py
similarity_playground.py
Every piece of data in diffprompt is a typed Pydantic model. The pipeline looks like this:
prompt v1 + prompt v2
|
Ontology infers dimensions, tags every input
|
generator produces list[TestCase]
|
runner produces dict[test_id, RunResult] x2
|
embedder produces list[float] similarity scores
|
judge produces (Verdict, reason, confidence) per pair
|
DiffResult assembled from all of the above
|
clusterer groups DiffResults into Clusters
slicer groups DiffResults into SliceResults
scorer computes regression_score, selects KeyExamples
|
DiffReport final container; passed to output layer
|
terminal/exporter rendered output
If you want to understand where to make a change, follow the data type. Adding a new field to DiffResult? It lives in models/__init__.py, gets populated somewhere in the pipeline, and consumed in terminal.py or scorer.py.
- Add the format to
OutputFormatenum inmodels/__init__.py - Add the rendering logic in
output/exporter.py - Handle the new format in
cli.pywhere--outputis processed - Add an example to
examples/
- Add a new value to
TestCategoryinmodels/__init__.py - Add the generation prompt to
TAXONOMY_PROMPTSincore/generator.py - Add the distribution fraction to
DISTRIBUTIONincore/generator.py - Make sure fractions still sum to 1.0
- Add a new
call_<provider>()function inmodels/cascade.pyfollowing the same pattern ascall_groq() - Add it as a fallback in
call_cascade()after Groq - Add the relevant API key check and error message
- Document the new env variable in the README
The judge prompt is in core/judge.py as JUDGE_PROMPT. Any change here affects every verdict in the pipeline. When testing a new judge prompt, run examples/basic_diff.py with both the old and new version and compare the verdict distributions. A good judge prompt should produce roughly 30-50% neutral verdicts on similar prompts, not collapse everything into improvement or regression.
The ontology prompt is in core/ontology.py as INFER_PROMPT. Changes here affect what dimensions get inferred, which affects tagging, which affects slicing. Run examples/ontology_inspect.py after any change to verify the dimensions and tagging quality.
# All tests
pytest tests/ -v
# Single file
pytest tests/test_embedder.py -v
# With coverage
pytest tests/ --cov=diffprompt --cov-report=term-missingTests live in tests/. Each core module has a corresponding test file. Follow the existing patterns.
For async functions, use pytest-asyncio. Mark async tests with @pytest.mark.asyncio or set asyncio_mode = "auto" in pyproject.toml (already done).
For functions that make LLM calls, do not call real APIs in tests. Either mock the call or test the function's behavior with pre-built inputs that don't require LLM calls. See test_slicer.py for an example of building DiffResult objects directly without running the full pipeline.
Every public function in core/ should have at least:
- A happy path test with typical inputs
- An edge case test (empty input, single item, etc.)
- A type/shape test verifying the return value structure
- Type hints on every function signature
- Pydantic models for all data that crosses module boundaries
- Async for anything that makes network or LLM calls
- No print statements in library code. Use the
loggeror let the CLI handle output. - Private helpers prefixed with underscore:
_parse_judge_response,_compute_confidence - Constants in ALL_CAPS at the top of the file
- Keep functions short. If a function is longer than 40 lines, it probably does too much.
- Fork the repo and create a branch from
main - Make your changes
- Run
pytest tests/ -vand make sure everything passes - Run
python examples/basic_diff.pyend to end at least once - Update documentation if you changed any public API or added a feature
- Open a PR with a clear description of what changed and why
For significant changes (new features, architecture changes), open an issue first to discuss before writing code.
These are areas where contributions would have high impact:
Test generation quality. The generator sometimes returns malformed JSON or truncated arrays. Better retry logic and input validation would make the tool more reliable.
Tagging accuracy. The zero-shot label embedding approach works but mislabels edge cases. A better approach for specific prompt domains (code, medical, legal) would be valuable.
HTML output. output/exporter.py is currently a stub. A proper HTML report with a sortable table and diff view per example would make the tool more shareable.
More test coverage. core/runner.py, core/judge.py, core/clusterer.py, and core/scorer.py have no tests yet. Adding tests for these would significantly improve confidence in the pipeline.
Windows compatibility. The tool was developed on Linux/Mac. There are known issues with environment variable handling on Windows PowerShell. Fixes and documentation for Windows users are welcome.