Skip to content

Add ProTeGi Prompt Optimizer#57

Open
DanielDango wants to merge 27 commits intoardoco:mainfrom
DanielDango:feature/add-protegi-optimization
Open

Add ProTeGi Prompt Optimizer#57
DanielDango wants to merge 27 commits intoardoco:mainfrom
DanielDango:feature/add-protegi-optimization

Conversation

@DanielDango
Copy link
Collaborator

Adds the ProTeGi Prompt Optimizer by Pryzant et al. of their paper "Automatic Prompt Optimization with "Gradient Descent" and Beam Search".

@DanielDango DanielDango requested a review from dfuchss as a code owner January 22, 2026 11:53
Copilot AI review requested due to automatic review settings January 22, 2026 11:53
@DanielDango DanielDango changed the title Feature/add protegi optimization Add ProTeGi Prompt Optimizer Jan 22, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds the ProTeGi Prompt Optimizer, implementing an automatic prompt optimization approach based on gradient descent by Pryzant et al. (2023). The implementation transcribes the original Python code into the LiSSA framework and modifies the core optimization interface to return arrays of prompts representing all intermediate optimization states rather than just the final result.

Changes:

  • Adds AutomaticPromptOptimizer with gradient-based prompt refinement using textual gradients and beam search
  • Modifies PromptOptimizer interface to return String[] instead of String to capture all intermediate optimization states
  • Implements cache improvements including OpenAI model instance caching and configurable cache conflict resolution strategies

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/AutomaticPromptOptimizer.java New gradient-based optimizer implementation with textual gradients, synonym generation, and beam search
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/GradientOptimizerConfig.java Configuration record for gradient optimizer parameters
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/PromptOptimizer.java Interface changed to return String[] instead of String
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/IterativeOptimizer.java Updated to return array of prompts from all iterations
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/IterativeFeedbackOptimizer.java Updated to return array of prompts from all iterations
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/MockOptimizer.java Updated to return String[] instead of String
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/OptimizerFactory.java Added gradient optimizer case to factory method
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/classifier/ChatLanguageModelProvider.java Added singleton caching for OpenAI chat model instances
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/cache/CacheReplacementStrategy.java New enum defining strategies for cache conflict resolution
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/cache/RedisCache.java Integrated conflict resolution strategy
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/cache/CacheManager.java Updated to use new conflict resolution strategy
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/Statistics.java Added method to generate statistics for specific iteration counts
src/main/java/edu/kit/kastel/sdq/lissa/ratlr/Optimization.java Updated to handle array of prompts and generate statistics for each iteration
src/main/java/edu/kit/kastel/sdq/lissa/cli/command/OptimizeCommand.java Updated to handle array of prompts and evaluate all iterations
src/test/java/edu/kit/kastel/sdq/lissa/ratlr/e2e/Requirement2RequirementE2ETest.java Updated test to handle array return and select final prompt
src/test/resources/simplelogger.properties Changed log level from debug to info
example-configs/gradient-optimizer-config.json Example configuration file for gradient optimizer
src/test/resources/warc/WARC_gradient_gpt_gpt-4o-mini-2024-07-18_0.json Test configuration file
src/test/resources/expected/GradientOptimizerExpectation.txt Expected output for gradient optimizer test
src/test/resources/warc/cache/optimizerTests/gradient/*.json Cached test responses

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…d-protegi-optimization

# Conflicts:
#	src/main/java/edu/kit/kastel/sdq/lissa/cli/command/OptimizeCommand.java
#	src/main/java/edu/kit/kastel/sdq/lissa/ratlr/cache/CacheManager.java
#	src/main/java/edu/kit/kastel/sdq/lissa/ratlr/cache/RedisCache.java
#	src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/IterativeFeedbackOptimizer.java
#	src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/IterativeOptimizer.java
#	src/test/resources/simplelogger.properties
…n-module' into feature/add-protegi-optimization

# Conflicts:
#	src/main/java/edu/kit/kastel/sdq/lissa/cli/command/OptimizeCommand.java
#	src/main/java/edu/kit/kastel/sdq/lissa/ratlr/Optimization.java
#	src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/IterativeFeedbackOptimizer.java
#	src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/IterativeOptimizer.java
#	src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/OptimizerFactory.java
#	src/main/java/edu/kit/kastel/sdq/lissa/ratlr/promptoptimizer/PromptOptimizer.java
@dfuchss dfuchss changed the base branch from feature/add-prompt-optimization-module to main February 19, 2026 13:08
Copy link
Member

@dfuchss dfuchss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial review

import edu.kit.kastel.sdq.lissa.ratlr.promptoptimizer.samplestrategy.SampleStrategy;
import edu.kit.kastel.sdq.lissa.ratlr.promptoptimizer.samplestrategy.SamplerFactory;

public record GradientOptimizerConfig(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs. Also document what the different prompts are for

@Nullable ModuleConfiguration configuration,
Set<TraceLink> goldStandard,
Metric metric,
Evaluator evaluator) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nullable evaluator I guess

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Evaluator is not (independently) nullable currently. Same as with the metric and goldstandard. The config null check may be extended to include any parameter, as the other parameters may be null iff config is also null.

Copilot AI review requested due to automatic review settings March 5, 2026 11:12
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated 10 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +30 to +50
private @Nullable ChatModel delegate;

/**
* Creates a new LazyChatModel with the specified supplier for the delegate.
*
* @param delegateSupplier The supplier that provides the chat model delegate
*/
public LazyChatModel(Supplier<ChatModel> delegateSupplier) {
this.delegateSupplier = Objects.requireNonNull(delegateSupplier);
}

private ChatModel delegate() {
if (delegate != null) {
return delegate;
}
synchronized (this) {
if (delegate == null) {
delegate = delegateSupplier.get();
}
return delegate;
}
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LazyChatModel uses double-checked locking, but delegate is not volatile. Without safe publication, another thread can observe a partially constructed delegate instance when reading delegate outside the synchronized block. Make delegate volatile (or synchronize all reads / use an AtomicReference/Supplier memoization) to ensure thread-safety matches the class Javadoc.

Copilot uses AI. Check for mistakes.
…onfig.java to ProTeGiOptimizer(Config) for clarity

 - add doc section about the ProTeGi optimizer
 - add new "protegi" config alias but keep previous "gradient" for compatibility
@DanielDango DanielDango requested review from Copilot and dfuchss March 5, 2026 17:28
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@DanielDango DanielDango requested a review from Copilot March 5, 2026 17:58
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI review requested due to automatic review settings March 6, 2026 00:04
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 29 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +28 to +32
public record OptimizerConfiguration(
@JsonUnwrapped EvaluationConfiguration evaluationConfiguration,
@JsonProperty("prompt_optimizer") ModuleConfiguration promptOptimizer,
@JsonProperty("metric") ModuleConfiguration metric)
@JsonProperty(PROMPT_OPTIMIZER_FIELD) ModuleConfiguration promptOptimizer,
@JsonProperty("metric") ModuleConfiguration metric,
@JsonProperty("evaluator") ModuleConfiguration evaluator)
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making evaluator a mandatory field forces all existing optimizer configs to include an evaluator section; otherwise Jackson deserialization will fail. If backward compatibility is desired, consider making this field @Nullable and defaulting Selector.createSelector(null) to MockSelector (as it already does), and guard evaluator.finalizeForSerialization() accordingly.

Copilot uses AI. Check for mistakes.
…roTeGiOptimizer.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 6, 2026 00:21
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 29 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

evaluationConfiguration.serializeAndDestroyConfiguration();
promptOptimizer.finalizeForSerialization();
metric.finalizeForSerialization();
evaluator.finalizeForSerialization();
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serializeAndDestroyConfiguration() unconditionally calls evaluator.finalizeForSerialization(). If an optimization config omits the new evaluator section (e.g., older configs), evaluator will deserialize as null and this will throw a NullPointerException. Consider marking evaluator as @Nullable and null-checking here (defaulting to a mock selector), or enforce non-null by validation with a clearer error message.

Suggested change
evaluator.finalizeForSerialization();
if (evaluator != null) {
evaluator.finalizeForSerialization();
}

Copilot uses AI. Check for mistakes.
…iles for consistency

 - various additional small tweaks
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 6, 2026

* Checks if the classification does not match the ground truth.
* @return false if the classification is equal to the ground truth, true otherwise
*/
public boolean isIncorrect() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try to flip this. typically you would ask for "isCorrect"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is only used in this negated form, flipping it would mean always using !isCorrect(). How about renaming it to isWrong() instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants