-
Notifications
You must be signed in to change notification settings - Fork 4
Add prompt optimizers to LiSSA #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
711d142
7887f8d
4a7744a
75dddd1
565a80e
883ede1
cb89983
fb1de00
3e7facf
e31c6c1
c5fff3f
abd5a4f
317d3cc
63df5fb
a8d1d47
b09dea2
93f87e5
4929a71
a14496b
83b5b7b
6cbd159
f6f1895
bc44e6b
03d1d0d
f1da525
d0240e9
1dc90b1
e7bf61b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
|
|
||
| { | ||
| "cache_dir": "./cache/WARC", | ||
|
|
||
| "gold_standard_configuration": { | ||
| "path": "./datasets/req2req/WARC/answer.csv", | ||
| "hasHeader": "true" | ||
| }, | ||
|
|
||
| "source_artifact_provider" : { | ||
| "name" : "text", | ||
| "args" : { | ||
| "artifact_type" : "requirement", | ||
| "path" : "./datasets/req2req/WARC/high" | ||
| } | ||
| }, | ||
| "target_artifact_provider" : { | ||
| "name" : "text", | ||
| "args" : { | ||
| "artifact_type" : "requirement", | ||
| "path" : "./datasets/req2req/WARC/low" | ||
| } | ||
| }, | ||
| "source_preprocessor" : { | ||
| "name" : "artifact", | ||
| "args" : {} | ||
| }, | ||
| "target_preprocessor" : { | ||
| "name" : "artifact", | ||
| "args" : {} | ||
| }, | ||
| "embedding_creator" : { | ||
| "name" : "openai", | ||
| "args" : { | ||
| "model": "text-embedding-3-large" | ||
| } | ||
| }, | ||
| "source_store" : { | ||
| "name" : "custom", | ||
| "args" : {} | ||
| }, | ||
| "target_store" : { | ||
| "name" : "cosine_similarity", | ||
| "args" : { | ||
| "max_results" : "4" | ||
| } | ||
| }, | ||
| "metric" : { | ||
| "name" : "mock", | ||
| "args" : {} | ||
| }, | ||
| "evaluator" : { | ||
| "name" : "mock", | ||
| "args" : {} | ||
| }, | ||
| "prompt_optimizer": { | ||
| "name" : "simple_openai", | ||
| "args" : { | ||
| "prompt": "Question: Here are two parts of software development artifacts.\n\n {source_type}: '''{source_content}'''\n\n {target_type}: '''{target_content}'''\n Are they related?\n\n Answer with 'yes' or 'no'.", | ||
| "model": "gpt-4o-mini-2024-07-18" | ||
| } | ||
| }, | ||
| "classifier" : { | ||
| "name" : "simple_openai", | ||
| "args" : { | ||
| "model": "gpt-4o-mini-2024-07-18" | ||
| } | ||
| }, | ||
| "result_aggregator" : { | ||
| "name" : "any_connection", | ||
| "args" : {} | ||
| }, | ||
| "tracelinkid_postprocessor" : { | ||
| "name" : "identity", | ||
| "args" : {} | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -122,6 +122,7 @@ | |
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter-params</artifactId> | ||
| <version>${junit.version}</version> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? That should be managed by the parent. |
||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
|
|
@@ -174,6 +175,7 @@ | |
| <groupId>com.diffplug.spotless</groupId> | ||
| <artifactId>spotless-maven-plugin</artifactId> | ||
| <configuration combine.self="append"> | ||
| <lineEndings>UNIX</lineEndings> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catch. I will move that to the parent. |
||
| <java> | ||
| <toggleOffOn> | ||
| <off>@formatter:off</off> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| /* Licensed under MIT 2025-2026. */ | ||
| package edu.kit.kastel.sdq.lissa.cli.command; | ||
|
|
||
| import static edu.kit.kastel.sdq.lissa.cli.command.EvaluateCommand.loadConfigs; | ||
|
|
||
| import java.io.IOException; | ||
| import java.nio.file.Path; | ||
| import java.util.List; | ||
|
|
||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import edu.kit.kastel.sdq.lissa.ratlr.Evaluation; | ||
| import edu.kit.kastel.sdq.lissa.ratlr.Optimization; | ||
|
|
||
| import picocli.CommandLine; | ||
|
|
||
| /** | ||
| * Command implementation for optimizing prompts used in trace link analysis configurations. | ||
| * This command processes one or more optimization configuration files to run the prompt | ||
| * optimization pipeline, and optionally evaluates the optimized prompts using specified | ||
| * evaluation configuration files. | ||
| */ | ||
| @CommandLine.Command( | ||
| name = "optimize", | ||
| mixinStandardHelpOptions = true, | ||
| description = "Optimizes a prompt for usage in the pipeline") | ||
| public class OptimizeCommand implements Runnable { | ||
|
|
||
| private static final Logger LOGGER = LoggerFactory.getLogger(OptimizeCommand.class); | ||
|
|
||
| /** | ||
| * Array of optimization configuration file paths to be processed. | ||
| * If a path points to a directory, all files within that directory will be processed. | ||
| * This option is required to run the optimization command. | ||
| */ | ||
| @CommandLine.Option( | ||
| names = {"-c", "--configs"}, | ||
| arity = "1..*", | ||
| description = | ||
| "Specifies one or more config paths to be invoked by the pipeline iteratively. If the path points " | ||
| + "to a directory, all files inside are chosen to get invoked.") | ||
| private Path[] optimizationConfigs; | ||
|
|
||
| /** | ||
| * Array of evaluation configuration file paths to be processed. | ||
| * If a path points to a directory, all files within that directory will be processed. | ||
| * This option is optional; if not provided, no evaluation will be performed after optimization. | ||
| */ | ||
| @CommandLine.Option( | ||
| names = {"-e", "--eval"}, | ||
| arity = "0..*", | ||
| description = "Specifies optional evaluation config paths to be invoked by the pipeline iteratively. " | ||
| + "Each evaluation configuration will be used with each optimization config." | ||
| + "If the path points to a directory, all files inside are chosen to get invoked.") | ||
| private Path[] evaluationConfigs; | ||
|
|
||
| /** | ||
| * Runs the optimization and evaluation pipelines based on the provided configuration files. | ||
| * It first loads the optimization and evaluation configurations, then executes the evaluation | ||
| * pipeline for each evaluation configuration. This is the unoptimized baseline evaluation. <br> | ||
| * After that, it runs the optimization pipeline for | ||
| * each optimization configuration, and subsequently evaluates the optimized prompt using each | ||
| * evaluation configuration once more with the optimized prompt instead of the original one. | ||
| */ | ||
| @Override | ||
| public void run() { | ||
| List<Path> configsToOptimize = loadConfigs(optimizationConfigs); | ||
| List<Path> configsToEvaluate = loadConfigs(evaluationConfigs); | ||
| LOGGER.info( | ||
| "Found {} optimization config files and {} evaluation config files to invoke", | ||
| configsToOptimize.size(), | ||
| configsToEvaluate.size()); | ||
|
|
||
| for (Path evaluationConfig : configsToEvaluate) { | ||
| runEvaluation(evaluationConfig, ""); | ||
| } | ||
|
|
||
| for (Path optimizationConfig : configsToOptimize) { | ||
| LOGGER.info("Invoking the optimization pipeline with '{}'", optimizationConfig); | ||
| String optimizedPrompt = ""; | ||
| try { | ||
| var optimization = new Optimization(optimizationConfig); | ||
| optimizedPrompt = optimization.run(); | ||
| } catch (IOException e) { | ||
| LOGGER.warn( | ||
| "Optimization configuration '{}' threw an exception: {} \n Maybe the file does not exist?", | ||
| optimizationConfig, | ||
| e.getMessage()); | ||
| } | ||
| for (Path evaluationConfig : configsToEvaluate) { | ||
| runEvaluation(evaluationConfig, optimizedPrompt); | ||
| } | ||
| } | ||
|
Comment on lines
+79
to
+94
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move to own method to match the description in the JavaDoc :) |
||
| } | ||
|
|
||
| private static void runEvaluation(Path evaluationConfig, String optimizedPrompt) { | ||
| LOGGER.info("Invoking the evaluation pipeline with '{}'", evaluationConfig); | ||
| try { | ||
| var evaluation = new Evaluation(evaluationConfig, optimizedPrompt); | ||
| evaluation.run(); | ||
| } catch (IOException e) { | ||
| LOGGER.warn( | ||
| "Baseline evaluation configuration '{}' threw an exception: {} \n Maybe the file does not exist?", | ||
| evaluationConfig, | ||
| e.getMessage()); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,4 @@ | ||||||||||||||||||||||||||||||||||||||
| /* Licensed under MIT 2025. */ | ||||||||||||||||||||||||||||||||||||||
| /* Licensed under MIT 2025-2026. */ | ||||||||||||||||||||||||||||||||||||||
| package edu.kit.kastel.sdq.lissa.ratlr; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -17,6 +17,8 @@ | |||||||||||||||||||||||||||||||||||||
| import edu.kit.kastel.sdq.lissa.ratlr.cache.CacheManager; | ||||||||||||||||||||||||||||||||||||||
| import edu.kit.kastel.sdq.lissa.ratlr.classifier.Classifier; | ||||||||||||||||||||||||||||||||||||||
| import edu.kit.kastel.sdq.lissa.ratlr.configuration.Configuration; | ||||||||||||||||||||||||||||||||||||||
| import edu.kit.kastel.sdq.lissa.ratlr.configuration.ConfigurationBuilder; | ||||||||||||||||||||||||||||||||||||||
| import edu.kit.kastel.sdq.lissa.ratlr.configuration.ModuleConfiguration; | ||||||||||||||||||||||||||||||||||||||
| import edu.kit.kastel.sdq.lissa.ratlr.context.ContextStore; | ||||||||||||||||||||||||||||||||||||||
| import edu.kit.kastel.sdq.lissa.ratlr.elementstore.SourceElementStore; | ||||||||||||||||||||||||||||||||||||||
| import edu.kit.kastel.sdq.lissa.ratlr.elementstore.TargetElementStore; | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -110,7 +112,46 @@ public class Evaluation { | |||||||||||||||||||||||||||||||||||||
| public Evaluation(Path configFile) throws IOException { | ||||||||||||||||||||||||||||||||||||||
| this.configFile = Objects.requireNonNull(configFile); | ||||||||||||||||||||||||||||||||||||||
| configuration = new ObjectMapper().readValue(configFile.toFile(), Configuration.class); | ||||||||||||||||||||||||||||||||||||||
| setup(); | ||||||||||||||||||||||||||||||||||||||
| setup(""); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Creates a new evaluation instance with the specified configuration file. Overwrites the prompt used for classification. | ||||||||||||||||||||||||||||||||||||||
| * This constructor is only to be used by the class {@link Optimization}, as the resulting configuration will | ||||||||||||||||||||||||||||||||||||||
| * not include the prompt. | ||||||||||||||||||||||||||||||||||||||
| * This constructor: | ||||||||||||||||||||||||||||||||||||||
| * <ol> | ||||||||||||||||||||||||||||||||||||||
| * <li>Validates the configuration file path</li> | ||||||||||||||||||||||||||||||||||||||
| * <li>Loads and initializes the configuration</li> | ||||||||||||||||||||||||||||||||||||||
| * <li>Sets up all required components for the pipeline, sharing a {@link ContextStore}</li> | ||||||||||||||||||||||||||||||||||||||
| * </ol> | ||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||
| * @param configFile Path to the configuration file | ||||||||||||||||||||||||||||||||||||||
| * @param prompt The prompt to use for classification | ||||||||||||||||||||||||||||||||||||||
| * @throws IOException If there are issues reading the configuration file | ||||||||||||||||||||||||||||||||||||||
| * @throws NullPointerException If configFile is null | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| public Evaluation(Path configFile, String prompt) throws IOException { | ||||||||||||||||||||||||||||||||||||||
| this.configFile = Objects.requireNonNull(configFile); | ||||||||||||||||||||||||||||||||||||||
| configuration = new ObjectMapper().readValue(configFile.toFile(), Configuration.class); | ||||||||||||||||||||||||||||||||||||||
| setup(prompt); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Creates a new evaluation instance with the specified configuration object. | ||||||||||||||||||||||||||||||||||||||
| * This constructor: | ||||||||||||||||||||||||||||||||||||||
| * <ol> | ||||||||||||||||||||||||||||||||||||||
| * <li>Initializes the configuration</li> | ||||||||||||||||||||||||||||||||||||||
| * <li>Sets up all required components for the pipeline, sharing a {@link ContextStore}</li> | ||||||||||||||||||||||||||||||||||||||
| * </ol> | ||||||||||||||||||||||||||||||||||||||
| * @param config The configuration object | ||||||||||||||||||||||||||||||||||||||
| * @throws IOException If there are issues setting up the cache | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| public Evaluation(Configuration config) throws IOException { | ||||||||||||||||||||||||||||||||||||||
| this.configuration = config; | ||||||||||||||||||||||||||||||||||||||
| // TODO maybe dont? | ||||||||||||||||||||||||||||||||||||||
DanielDango marked this conversation as resolved.
Show resolved
Hide resolved
DanielDango marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+147
to
+152
|
||||||||||||||||||||||||||||||||||||||
| * @param config The configuration object | |
| * @throws IOException If there are issues setting up the cache | |
| */ | |
| public Evaluation(Configuration config) throws IOException { | |
| this.configuration = config; | |
| // TODO maybe dont? | |
| * <p> | |
| * Note: When using this constructor there is no associated configuration file on disk. | |
| * Consequently, {@link #configFile} is set to {@code null} by design and any code accessing | |
| * it must first check for {@code null}. | |
| * </p> | |
| * | |
| * @param config The configuration object | |
| * @throws IOException If there are issues setting up the cache | |
| */ | |
| public Evaluation(Configuration config) throws IOException { | |
| this.configuration = config; | |
| // No configuration file is associated with this instance; configFile remains null by design. |
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TODO comment on line 152 suggests uncertainty about setting configFile to null. This should either be resolved with a proper implementation or the TODO should be removed if null is the intended behavior. Consider clarifying whether configFile is optional for evaluation instances created from Configuration objects.
Uh oh!
There was an error while loading. Please reload this page.