Skip to content

Commit 61ddacc

Browse files
authored
Merge pull request #58 from finitearth/Feature/API-Enhancements
Feature/api enhancements
2 parents ab458fc + 6dcf75d commit 61ddacc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1020
-624
lines changed

.coverage

0 Bytes
Binary file not shown.

.pre-commit-config.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ repos:
1414
rev: 6.0.0
1515
hooks:
1616
- id: flake8
17+
- repo: https://github.com/astral-sh/ruff-pre-commit
18+
rev: v0.14.6
19+
hooks:
20+
- id: ruff-check
21+
args: [ --fix ]
1722
- repo: https://github.com/pycqa/isort
1823
rev: 5.12.0
1924
hooks:

README.md

Lines changed: 52 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
![promptolution](https://github.com/user-attachments/assets/84c050bd-61a1-4f2e-bc4e-874d9b4a69af)
21

32
![Coverage](https://img.shields.io/badge/Coverage-91%25-brightgreen)
43
[![CI](https://github.com/finitearth/promptolution/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/finitearth/promptolution/actions/workflows/ci.yml)
@@ -7,104 +6,93 @@
76
![Python Versions](https://img.shields.io/badge/Python%20Versions-≥3.10-blue)
87
[![Getting Started](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/finitearth/promptolution/blob/main/tutorials/getting_started.ipynb)
98

10-
Promptolution is a library that provides a modular and extensible framework for implementing prompt tuning for single tasks and larger experiments. It offers a user-friendly interface to assemble the core components for various prompt optimization tasks.
9+
![promptolution](https://github.com/user-attachments/assets/84c050bd-61a1-4f2e-bc4e-874d9b4a69af)
10+
11+
<p align="center">
12+
<img height="60" alt="lmu_logo" src="https://github.com/user-attachments/assets/5aecd0d6-fc2d-48b2-b395-d1877578a3c5" />
13+
<img height="60" alt="mcml" src="https://github.com/user-attachments/assets/d9f3b18e-a5ec-4c3f-b449-e57cb977f483" />
14+
<img height="60" alt="ellis_logo" src="https://github.com/user-attachments/assets/60654a27-0f8f-4624-a1d5-5122f2632bec" />
15+
<img height="60" alt="uni_freiburg_color" src="https://github.com/user-attachments/assets/f5eabbd2-ae6a-497b-857b-71958ed77335" />
16+
<img height="60" alt="tum_logo" src="https://github.com/user-attachments/assets/982ec2f0-ec14-4dc2-8d75-bfae09d4fa73" />
17+
</p>
18+
19+
## 🚀 What is Promptolution?
1120

12-
This project was developed by [Timo Heiß](https://www.linkedin.com/in/timo-heiss/), [Moritz Schlager](https://www.linkedin.com/in/moritz-schlager/) and [Tom Zehle](https://www.linkedin.com/in/tom-zehle/) as part of a study program at LMU Munich.
21+
**Promptolution** is a unified, modular framework for prompt optimization built for researchers and advanced practitioners who want full control over their experimental setup. Unlike end-to-end application frameworks with high abstraction, promptolution focuses exclusively on the optimization stage, providing a clean, transparent, and extensible API. It allows for simple prompt optimization for one task up to large-scale reproducible benchmark experiments.
22+
23+
<img width="808" height="356" alt="promptolution_framework" src="https://github.com/user-attachments/assets/e3d05493-30e3-4464-b0d6-1d3e3085f575" />
24+
25+
### Key Features
1326

14-
## Installation
27+
* Implementation of many current prompt optimizers out of the box.
28+
* Unified LLM backend supporting API-based models, Local LLMs, and vLLM clusters.
29+
* Built-in response caching to save costs and parallelized inference for speed.
30+
* Detailed logging and token usage tracking for granular post-hoc analysis.
1531

16-
Use pip to install our library:
32+
Have a look at our [Release Notes](https://finitearth.github.io/promptolution/release-notes/) for the latest updates to promptolution.
33+
34+
## 📦 Installation
1735

1836
```
1937
pip install promptolution[api]
2038
```
2139

22-
If you want to run your prompt optimization locally, either via transformers or vLLM, consider running:
40+
Local inference via vLLM or transformers:
2341

2442
```
2543
pip install promptolution[vllm,transformers]
2644
```
2745

28-
Alternatively, clone the repository, run
46+
From source:
2947

3048
```
49+
git clone https://github.com/finitearth/promptolution.git
50+
cd promptolution
3151
poetry install
3252
```
3353

34-
to install the necessary dependencies. You might need to install [pipx](https://pipx.pypa.io/stable/installation/) and [poetry](https://python-poetry.org/docs/) first.
35-
36-
## Usage
37-
38-
To get started right away, take a look at our [getting started notebook](https://github.com/finitearth/promptolution/blob/main/tutorials/getting_started.ipynb) and our [other demos and tutorials](https://github.com/finitearth/promptolution/blob/main/tutorials).
39-
For more details, a comprehensive **documentation** with API reference is availabe at https://finitearth.github.io/promptolution/.
54+
## 🔧 Quickstart
4055

41-
### Featured Optimizers
56+
Start with the **Getting Started tutorial**:
57+
[https://github.com/finitearth/promptolution/blob/main/tutorials/getting_started.ipynb](https://github.com/finitearth/promptolution/blob/main/tutorials/getting_started.ipynb)
4258

43-
| **Name** | **Paper** | **init prompts** | **Exploration** | **Costs** | **Parallelizable** | **Utilizes Fewshot Examples** |
44-
| :-----------: | :----------------------------------------------: | :--------------: | :-------------: | :-------: | :-------------------: | :---------------------------: |
45-
| `CAPO` | [Zehle et al.](https://arxiv.org/abs/2504.16005) | _required_ | 👍 | 💲 |||
46-
| `EvoPromptDE` | [Guo et al.](https://arxiv.org/abs/2309.08532) | _required_ | 👍 | 💲💲 |||
47-
| `EvoPromptGA` | [Guo et al.](https://arxiv.org/abs/2309.08532) | _required_ | 👍 | 💲💲 |||
48-
| `OPRO` | [Yang et al.](https://arxiv.org/abs/2309.03409) | _optional_ | 👎 | 💲💲 |||
59+
Full docs:
60+
[https://finitearth.github.io/promptolution/](https://finitearth.github.io/promptolution/)
4961

50-
### Core Components
51-
52-
- `Task`: Encapsulates initial prompts, dataset features, targets, and evaluation methods.
53-
- `Predictor`: Implements the prediction logic, interfacing between the `Task` and `LLM` components.
54-
- `LLM`: Unifies the process of obtaining responses from language models, whether locally hosted or accessed via API.
55-
- `Optimizer`: Implements prompt optimization algorithms, utilizing the other components during the optimization process.
56-
57-
### Key Features
5862

59-
- Modular and object-oriented design
60-
- Extensible architecture
61-
- Easy-to-use interface for assembling experiments
62-
- Parallelized LLM requests for improved efficiency
63-
- Integration with langchain for standardized LLM API calls
64-
- Detailed logging and callback system for optimization analysis
63+
## 🧠 Featured Optimizers
6564

66-
## Changelog
65+
| **Name** | **Paper** | **Init prompts** | **Exploration** | **Costs** | **Parallelizable** | **Few-shot** |
66+
| ---- | ---- | ---- |---- |---- | ----|---- |
67+
| `CAPO` | [Zehle et al., 2025](https://arxiv.org/abs/2504.16005) | required | 👍 | 💲 |||
68+
| `EvoPromptDE` | [Guo et al., 2023](https://arxiv.org/abs/2309.08532) | required | 👍 | 💲💲 |||
69+
| `EvoPromptGA` | [Guo et al., 2023](https://arxiv.org/abs/2309.08532) | required | 👍 | 💲💲 |||
70+
| `OPRO` | [Yang et al., 2023](https://arxiv.org/abs/2309.03409) | optional | 👎 | 💲💲 |||
6771

68-
Release notes for each version of the library can be found [here](https://finitearth.github.io/promptolution/release-notes/)
72+
## 🏗 Components
6973

70-
## Contributing
74+
* **`Task`** – Manages the dataset, evaluation metrics, and subsampling.
75+
* **`Predictor`** – Defines how to extract the answer from the model's response.
76+
* **`LLM`** – A unified interface handling inference, token counting, and concurrency.
77+
* **`Optimizer`** – The core component that implements the algorithms that refine prompts.
78+
* **`ExperimentConfig`** – A configuration abstraction to streamline and parametrize large-scale scientific experiments.
7179

72-
The first step to contributing is to open an issue describing the bug, feature, or enhancements. Ensure the issue is clearly described, assigned, and properly tagged. All work should be linked to an open issue.
80+
## 🤝 Contributing
7381

74-
### Code Style and Linting
82+
Open an issue → create a branch → PR → CI → review → merge.
83+
Branch naming: `feature/...`, `fix/...`, `chore/...`, `refactor/...`.
7584

76-
We use Black for code formatting, Flake8 for linting, pydocstyle for docstring conventions (Google format), and isort to sort imports. All these checks are enforced via pre-commit hooks, which automatically run on every commit. Install the pre-commit hooks to ensure that all checks run automatically:
85+
Please ensure to use pre-commit, which assists with keeping the code quality high:
7786

7887
```
7988
pre-commit install
80-
```
81-
82-
To run all checks manually:
83-
84-
```
8589
pre-commit run --all-files
8690
```
87-
88-
### Branch Protection and Merging Guidelines
89-
90-
- The main branch is protected. No direct commits are allowed for non-administrators.
91-
- Rebase your branch on main before opening a pull request.
92-
- All contributions must be made on dedicated branches linked to specific issues.
93-
- Name the branch according to {prefix}/{description} with one of the prefixes fix, feature, chore, or refactor.
94-
- A pull request must have at least one approval from a code owner before it can be merged into main.
95-
- CI checks must pass before a pull request can be merged.
96-
- New releases will only be created by code owners.
97-
98-
### Testing
99-
100-
We use pytest to run tests, and coverage to track code coverage. Tests automatically run on pull requests and pushes to the main branch, but please ensure they also pass locally before pushing!
101-
To run the tests with coverage locally, use the following commands or your IDE's test runner:
91+
We encourage every contributor to also write tests, that automatically check if the implementation works as expected:
10292

10393
```
10494
poetry run python -m coverage run -m pytest
105-
```
106-
107-
To see the coverage report run:
108-
```
10995
poetry run python -m coverage report
11096
```
97+
98+
Developed by **Timo Heiß**, **Moritz Schlager**, and **Tom Zehle** (LMU Munich, MCML, ELLIS, TUM, Uni Freiburg).

docs/index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,6 @@ Or clone our GitHub repository:
2929
- [Optimizers](api/optimizers.md)
3030
- [Predictors](api/predictors.md)
3131
- [Tasks](api/tasks.md)
32-
- [Callbacks](api/callbacks.md)
33-
- [Config](api/config.md)
32+
- [Helpers](api/helpers.md)
33+
- [Utils](api/utils.md)
34+
- [Exemplar Selectors](api/examplar_selectors.md)

docs/release-notes/v2.2.0.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## Release v2.2.0
2+
### What's changed
3+
4+
#### Added features:
5+
* Extended interface of APILLM allowing to pass kwargs to the API
6+
* Improve asynchronous parallelization of LLM calls shortening inference times
7+
* Introduced a `Prompt` class to encapsulate instructions and few-shot examples
8+
9+
#### Further changes:
10+
* Improved error handling
11+
* Improved task-description infusion mechanism for meta-prompts
12+
13+
**Full Changelog**: [here](https://github.com/finitearth/promptolution/compare/2.1.0...v2.2.0)

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ nav:
4747
- Home: index.md
4848
- Release Notes:
4949
- Overview: release-notes.md
50+
- v2.2.0: release-notes/v2.2.0.md
5051
- v2.1.0: release-notes/v2.1.0.md
5152
- v2.0.1: release-notes/v2.0.1.md
5253
- v2.0.0: release-notes/v2.0.0.md

promptolution/exemplar_selectors/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@
22

33
from promptolution.exemplar_selectors.random_search_selector import RandomSearchSelector
44
from promptolution.exemplar_selectors.random_selector import RandomSelector
5+
6+
__all__ = [
7+
"RandomSelector",
8+
"RandomSearchSelector",
9+
]

promptolution/exemplar_selectors/base_exemplar_selector.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
from typing import TYPE_CHECKING, Optional
77

8+
from promptolution.utils.prompt import Prompt
9+
810
if TYPE_CHECKING: # pragma: no cover
911
from promptolution.predictors.base_predictor import BasePredictor
1012
from promptolution.tasks.base_task import BaseTask
@@ -33,11 +35,11 @@ def __init__(self, task: "BaseTask", predictor: "BasePredictor", config: Optiona
3335
config.apply_to(self)
3436

3537
@abstractmethod
36-
def select_exemplars(self, prompt: str, n_examples: int = 5) -> str:
38+
def select_exemplars(self, prompt: Prompt, n_examples: int = 5) -> Prompt:
3739
"""Select exemplars based on the given prompt.
3840
3941
Args:
40-
prompt (str): The input prompt to base the exemplar selection on.
42+
prompt (Prompt): The input prompt to base the exemplar selection on.
4143
n_examples (int, optional): The number of exemplars to select. Defaults to 5.
4244
4345
Returns:

promptolution/exemplar_selectors/random_search_selector.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Random search exemplar selector."""
22

33
from promptolution.exemplar_selectors.base_exemplar_selector import BaseExemplarSelector
4+
from promptolution.utils.prompt import Prompt
45

56

67
class RandomSearchSelector(BaseExemplarSelector):
@@ -10,7 +11,7 @@ class RandomSearchSelector(BaseExemplarSelector):
1011
evaluates their performance, and selects the best performing set.
1112
"""
1213

13-
def select_exemplars(self, prompt: str, n_trials: int = 5) -> str:
14+
def select_exemplars(self, prompt: Prompt, n_trials: int = 5) -> Prompt:
1415
"""Select exemplars using a random search strategy.
1516
1617
This method generates multiple sets of random examples, evaluates their performance
@@ -21,7 +22,7 @@ def select_exemplars(self, prompt: str, n_trials: int = 5) -> str:
2122
n_trials (int, optional): The number of random trials to perform. Defaults to 5.
2223
2324
Returns:
24-
str: The best performing prompt, which includes the original prompt and the selected exemplars.
25+
Prompt: The best performing prompt, which includes the original prompt and the selected exemplars.
2526
"""
2627
best_score = 0.0
2728
best_prompt = prompt
@@ -30,7 +31,7 @@ def select_exemplars(self, prompt: str, n_trials: int = 5) -> str:
3031
_, seq = self.task.evaluate(
3132
prompt, self.predictor, eval_strategy="subsample", return_seq=True, return_agg_scores=False
3233
)
33-
prompt_with_examples = "\n\n".join([prompt] + [seq[0][0]]) + "\n\n"
34+
prompt_with_examples = Prompt(prompt.instruction, [seq[0][0]])
3435
# evaluate prompts as few shot prompt
3536
score = self.task.evaluate(prompt_with_examples, self.predictor, eval_strategy="subsample")[0]
3637
if score > best_score:

promptolution/exemplar_selectors/random_selector.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import TYPE_CHECKING, List, Optional
66

77
from promptolution.exemplar_selectors.base_exemplar_selector import BaseExemplarSelector
8+
from promptolution.utils.prompt import Prompt
89

910
if TYPE_CHECKING: # pragma: no cover
1011
from promptolution.predictors.base_predictor import BasePredictor
@@ -37,18 +38,18 @@ def __init__(
3738
self.desired_score = desired_score
3839
super().__init__(task, predictor, config)
3940

40-
def select_exemplars(self, prompt: str, n_examples: int = 5) -> str:
41+
def select_exemplars(self, prompt: Prompt, n_examples: int = 5) -> Prompt:
4142
"""Select exemplars using a random selection strategy.
4243
4344
This method generates random examples and selects those that are evaluated as correct
4445
(score == self.desired_score) until the desired number of exemplars is reached.
4546
4647
Args:
47-
prompt (str): The input prompt to base the exemplar selection on.
48+
prompt (Prompt): The input prompt to base the exemplar selection on.
4849
n_examples (int, optional): The number of exemplars to select. Defaults to 5.
4950
5051
Returns:
51-
str: A new prompt that includes the original prompt and the selected exemplars.
52+
Prompt: A new prompt that includes the original prompt and the selected exemplars.
5253
"""
5354
examples: List[str] = []
5455
while len(examples) < n_examples:
@@ -59,4 +60,4 @@ def select_exemplars(self, prompt: str, n_examples: int = 5) -> str:
5960
seq = seqs[0][0]
6061
if score == self.desired_score:
6162
examples.append(seq)
62-
return "\n\n".join([prompt] + examples) + "\n\n"
63+
return Prompt(prompt.instruction, examples)

0 commit comments

Comments
 (0)