From 5bba71536960676061713a2157a390a1d84f42a4 Mon Sep 17 00:00:00 2001 From: TomuHirata Date: Fri, 26 Sep 2025 14:23:37 +0900 Subject: [PATCH 1/3] attach _compiled=True in all optimizers --- dspy/teleprompt/avatar_optimizer.py | 1 + dspy/teleprompt/bettertogether.py | 1 + dspy/teleprompt/copro_optimizer.py | 1 + dspy/teleprompt/ensemble.py | 4 +++- dspy/teleprompt/gepa/gepa.py | 1 + dspy/teleprompt/knn_fewshot.py | 1 + dspy/teleprompt/mipro_optimizer_v2.py | 29 ++++++++++++++------------- dspy/teleprompt/random_search.py | 1 + dspy/teleprompt/simba.py | 1 + dspy/teleprompt/teleprompt_optuna.py | 1 + dspy/teleprompt/vanilla.py | 1 + 11 files changed, 27 insertions(+), 15 deletions(-) diff --git a/dspy/teleprompt/avatar_optimizer.py b/dspy/teleprompt/avatar_optimizer.py index ddba74e5f2..dc2c4511c1 100644 --- a/dspy/teleprompt/avatar_optimizer.py +++ b/dspy/teleprompt/avatar_optimizer.py @@ -222,4 +222,5 @@ def compile(self, student, *, trainset): print(f"Best Actor: {best_actor}") + best_actor._compiled = True return best_actor diff --git a/dspy/teleprompt/bettertogether.py b/dspy/teleprompt/bettertogether.py index d1154f9ae4..f6c3ae3142 100644 --- a/dspy/teleprompt/bettertogether.py +++ b/dspy/teleprompt/bettertogether.py @@ -120,6 +120,7 @@ def _run_strategies(self, parsed_strategy, student, trainset, valset_ratio) -> M kill_lms(student) student.candidate_programs = candidate_programs + student._compiled = True return student def _compile_prompt_optimizer(self, student, trainset, valset_ratio) -> Module: diff --git a/dspy/teleprompt/copro_optimizer.py b/dspy/teleprompt/copro_optimizer.py index e0f4f71749..bc62e04362 100644 --- a/dspy/teleprompt/copro_optimizer.py +++ b/dspy/teleprompt/copro_optimizer.py @@ -354,4 +354,5 @@ def compile(self, student, *, trainset, eval_kwargs): best_program.results_best = results_best best_program.results_latest = results_latest + best_program._compiled = True return best_program diff --git a/dspy/teleprompt/ensemble.py b/dspy/teleprompt/ensemble.py index f7cfb01607..2a9b14a330 100644 --- a/dspy/teleprompt/ensemble.py +++ b/dspy/teleprompt/ensemble.py @@ -37,4 +37,6 @@ def forward(self, *args, **kwargs): return outputs - return EnsembledProgram() + ensembled_program = EnsembledProgram() + ensembled_program._compiled = True + return ensembled_program diff --git a/dspy/teleprompt/gepa/gepa.py b/dspy/teleprompt/gepa/gepa.py index 87cbbf80a5..84586da95c 100644 --- a/dspy/teleprompt/gepa/gepa.py +++ b/dspy/teleprompt/gepa/gepa.py @@ -567,4 +567,5 @@ def feedback_fn( dspy_gepa_result = DspyGEPAResult.from_gepa_result(gepa_result, adapter) new_prog.detailed_results = dspy_gepa_result + new_prog._compiled = True return new_prog diff --git a/dspy/teleprompt/knn_fewshot.py b/dspy/teleprompt/knn_fewshot.py index 0c8557ca68..5c21b1c739 100644 --- a/dspy/teleprompt/knn_fewshot.py +++ b/dspy/teleprompt/knn_fewshot.py @@ -66,4 +66,5 @@ def forward_pass(_, **kwargs): return compiled_program(**kwargs) student_copy.forward = types.MethodType(forward_pass, student_copy) + student_copy._compiled = True return student_copy diff --git a/dspy/teleprompt/mipro_optimizer_v2.py b/dspy/teleprompt/mipro_optimizer_v2.py index fe741ddf8a..5ef3b2db58 100644 --- a/dspy/teleprompt/mipro_optimizer_v2.py +++ b/dspy/teleprompt/mipro_optimizer_v2.py @@ -589,23 +589,24 @@ def objective(trial): study.optimize(objective, n_trials=num_trials) # Attach logs to best program - if best_program is not None and self.track_stats: - best_program.trial_logs = trial_logs - best_program.score = best_score - best_program.prompt_model_total_calls = self.prompt_model_total_calls - best_program.total_calls = self.total_calls - sorted_candidate_programs = sorted(score_data, key=lambda x: x["score"], reverse=True) - # Attach all minibatch programs - best_program.mb_candidate_programs = [ - score_data for score_data in sorted_candidate_programs if not score_data["full_eval"] - ] - # Attach all programs that were evaluated on the full trainset, in descending order of score - best_program.candidate_programs = [ - score_data for score_data in sorted_candidate_programs if score_data["full_eval"] - ] + # if best_program is not None and self.track_stats: + # best_program.trial_logs = trial_logs + # best_program.score = best_score + # best_program.prompt_model_total_calls = self.prompt_model_total_calls + # best_program.total_calls = self.total_calls + # sorted_candidate_programs = sorted(score_data, key=lambda x: x["score"], reverse=True) + # # Attach all minibatch programs + # best_program.mb_candidate_programs = [ + # score_data for score_data in sorted_candidate_programs if not score_data["full_eval"] + # ] + # # Attach all programs that were evaluated on the full trainset, in descending order of score + # best_program.candidate_programs = [ + # score_data for score_data in sorted_candidate_programs if score_data["full_eval"] + # ] logger.info(f"Returning best identified program with score {best_score}!") + best_program._compiled = True return best_program def _log_minibatch_eval( diff --git a/dspy/teleprompt/random_search.py b/dspy/teleprompt/random_search.py index c6447e8330..b268d87dbe 100644 --- a/dspy/teleprompt/random_search.py +++ b/dspy/teleprompt/random_search.py @@ -147,6 +147,7 @@ def compile(self, student, *, teacher=None, trainset, valset=None, restrict=None print(f"{len(best_program.candidate_programs)} candidate programs found.") + best_program._compiled = True return best_program diff --git a/dspy/teleprompt/simba.py b/dspy/teleprompt/simba.py index 230673d8f6..a66a5d1a3a 100644 --- a/dspy/teleprompt/simba.py +++ b/dspy/teleprompt/simba.py @@ -363,4 +363,5 @@ def register_new_program(prog: dspy.Module, score_list: list[float]) -> None: best_program.candidate_programs = candidate_data best_program.trial_logs = trial_logs + best_program._compiled = True return best_program diff --git a/dspy/teleprompt/teleprompt_optuna.py b/dspy/teleprompt/teleprompt_optuna.py index 29b8774228..c215cc9a43 100644 --- a/dspy/teleprompt/teleprompt_optuna.py +++ b/dspy/teleprompt/teleprompt_optuna.py @@ -73,4 +73,5 @@ def compile(self, student, *, teacher=None, max_demos, trainset, valset=None): best_program = study.trials[study.best_trial.number].user_attrs["program"] print("Best score:", study.best_value) print("Best program:", best_program) + best_program._compiled = True return best_program diff --git a/dspy/teleprompt/vanilla.py b/dspy/teleprompt/vanilla.py index 3d51186950..8a56aa8bf7 100644 --- a/dspy/teleprompt/vanilla.py +++ b/dspy/teleprompt/vanilla.py @@ -22,6 +22,7 @@ def compile(self, student, *, trainset, sample=True): else: predictor.demos = self.trainset[: min(self.k, len(self.trainset))] + self.student._compiled = True return self.student From fd4ea8347affa73f9900e651edbd8bfc3de94c24 Mon Sep 17 00:00:00 2001 From: TomuHirata Date: Fri, 26 Sep 2025 14:24:20 +0900 Subject: [PATCH 2/3] nit --- dspy/teleprompt/mipro_optimizer_v2.py | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/dspy/teleprompt/mipro_optimizer_v2.py b/dspy/teleprompt/mipro_optimizer_v2.py index 5ef3b2db58..e2f0df81ae 100644 --- a/dspy/teleprompt/mipro_optimizer_v2.py +++ b/dspy/teleprompt/mipro_optimizer_v2.py @@ -589,20 +589,20 @@ def objective(trial): study.optimize(objective, n_trials=num_trials) # Attach logs to best program - # if best_program is not None and self.track_stats: - # best_program.trial_logs = trial_logs - # best_program.score = best_score - # best_program.prompt_model_total_calls = self.prompt_model_total_calls - # best_program.total_calls = self.total_calls - # sorted_candidate_programs = sorted(score_data, key=lambda x: x["score"], reverse=True) - # # Attach all minibatch programs - # best_program.mb_candidate_programs = [ - # score_data for score_data in sorted_candidate_programs if not score_data["full_eval"] - # ] - # # Attach all programs that were evaluated on the full trainset, in descending order of score - # best_program.candidate_programs = [ - # score_data for score_data in sorted_candidate_programs if score_data["full_eval"] - # ] + if best_program is not None and self.track_stats: + best_program.trial_logs = trial_logs + best_program.score = best_score + best_program.prompt_model_total_calls = self.prompt_model_total_calls + best_program.total_calls = self.total_calls + sorted_candidate_programs = sorted(score_data, key=lambda x: x["score"], reverse=True) + # Attach all minibatch programs + best_program.mb_candidate_programs = [ + score_data for score_data in sorted_candidate_programs if not score_data["full_eval"] + ] + # Attach all programs that were evaluated on the full trainset, in descending order of score + best_program.candidate_programs = [ + score_data for score_data in sorted_candidate_programs if score_data["full_eval"] + ] logger.info(f"Returning best identified program with score {best_score}!") From ac2ad8d5f840fc09bf7e694dc82d3037f3c57fd1 Mon Sep 17 00:00:00 2001 From: TomuHirata Date: Mon, 29 Sep 2025 10:10:23 +0900 Subject: [PATCH 3/3] remove _compiled --- docs/docs/cheatsheet.md | 38 ++++++++++----------- docs/docs/faqs.md | 8 ++--- dspy/primitives/base_module.py | 16 +++------ dspy/primitives/module.py | 2 -- dspy/teleprompt/avatar_optimizer.py | 1 - dspy/teleprompt/bettertogether.py | 2 -- dspy/teleprompt/bootstrap.py | 4 +-- dspy/teleprompt/bootstrap_finetune.py | 4 +-- dspy/teleprompt/copro_optimizer.py | 1 - dspy/teleprompt/ensemble.py | 1 - dspy/teleprompt/gepa/gepa.py | 1 - dspy/teleprompt/grpo.py | 1 - dspy/teleprompt/knn_fewshot.py | 1 - dspy/teleprompt/mipro_optimizer_v2.py | 1 - dspy/teleprompt/random_search.py | 1 - dspy/teleprompt/simba.py | 1 - dspy/teleprompt/teleprompt_optuna.py | 1 - dspy/teleprompt/vanilla.py | 1 - tests/examples/test_baleen.py | 10 +++--- tests/primitives/test_module.py | 4 ++- tests/teleprompt/test_bootstrap.py | 2 +- tests/teleprompt/test_bootstrap_finetune.py | 2 +- tests/utils/test_saving.py | 10 +++--- 23 files changed, 45 insertions(+), 68 deletions(-) diff --git a/docs/docs/cheatsheet.md b/docs/docs/cheatsheet.md index bb0fd58a8d..ed960e8c00 100644 --- a/docs/docs/cheatsheet.md +++ b/docs/docs/cheatsheet.md @@ -178,7 +178,7 @@ evaluate_program(your_dspy_program) from dspy.teleprompt import LabeledFewShot labeled_fewshot_optimizer = LabeledFewShot(k=8) -your_dspy_program_compiled = labeled_fewshot_optimizer.compile(student = your_dspy_program, trainset=trainset) +your_dspy_program_optimized = labeled_fewshot_optimizer.compile(student = your_dspy_program, trainset=trainset) ``` ### BootstrapFewShot @@ -188,34 +188,34 @@ from dspy.teleprompt import BootstrapFewShot fewshot_optimizer = BootstrapFewShot(metric=your_defined_metric, max_bootstrapped_demos=4, max_labeled_demos=16, max_rounds=1, max_errors=10) -your_dspy_program_compiled = fewshot_optimizer.compile(student = your_dspy_program, trainset=trainset) +your_dspy_program_optimized = fewshot_optimizer.compile(student = your_dspy_program, trainset=trainset) ``` -#### Using another LM for compilation, specifying in teacher_settings +#### Using another LM for optimization, specifying in teacher_settings ```python from dspy.teleprompt import BootstrapFewShot fewshot_optimizer = BootstrapFewShot(metric=your_defined_metric, max_bootstrapped_demos=4, max_labeled_demos=16, max_rounds=1, max_errors=10, teacher_settings=dict(lm=gpt4)) -your_dspy_program_compiled = fewshot_optimizer.compile(student = your_dspy_program, trainset=trainset) +your_dspy_program_optimized = fewshot_optimizer.compile(student = your_dspy_program, trainset=trainset) ``` -#### Compiling a compiled program - bootstrapping a bootstrapped program +#### Optimizing an optimized program - bootstrapping a bootstrapped program ```python -your_dspy_program_compiledx2 = teleprompter.compile( +your_dspy_program_optimizedx2 = teleprompter.compile( your_dspy_program, - teacher=your_dspy_program_compiled, + teacher=your_dspy_program_optimized, trainset=trainset, ) ``` -#### Saving/loading a compiled program +#### Saving/loading an optimized program ```python save_path = './v1.json' -your_dspy_program_compiledx2.save(save_path) +your_dspy_program_optimizedx2.save(save_path) ``` ```python @@ -232,7 +232,7 @@ from dspy.teleprompt import BootstrapFewShotWithRandomSearch fewshot_optimizer = BootstrapFewShotWithRandomSearch(metric=your_defined_metric, max_bootstrapped_demos=2, num_candidate_programs=8, num_threads=NUM_THREADS) -your_dspy_program_compiled = fewshot_optimizer.compile(student = your_dspy_program, trainset=trainset, valset=devset) +your_dspy_program_optimized = fewshot_optimizer.compile(student = your_dspy_program, trainset=trainset, valset=devset) ``` @@ -245,11 +245,11 @@ from dspy.teleprompt import BootstrapFewShotWithRandomSearch from dspy.teleprompt.ensemble import Ensemble fewshot_optimizer = BootstrapFewShotWithRandomSearch(metric=your_defined_metric, max_bootstrapped_demos=2, num_candidate_programs=8, num_threads=NUM_THREADS) -your_dspy_program_compiled = fewshot_optimizer.compile(student = your_dspy_program, trainset=trainset, valset=devset) +your_dspy_program_optimized = fewshot_optimizer.compile(student = your_dspy_program, trainset=trainset, valset=devset) ensemble_optimizer = Ensemble(reduce_fn=dspy.majority) -programs = [x[-1] for x in your_dspy_program_compiled.candidate_programs] -your_dspy_program_compiled_ensemble = ensemble_optimizer.compile(programs[:3]) +programs = [x[-1] for x in your_dspy_program_optimized.candidate_programs] +your_dspy_program_optimized_ensemble = ensemble_optimizer.compile(programs[:3]) ``` ### BootstrapFinetune @@ -257,14 +257,14 @@ your_dspy_program_compiled_ensemble = ensemble_optimizer.compile(programs[:3]) ```python from dspy.teleprompt import BootstrapFewShotWithRandomSearch, BootstrapFinetune -#Compile program on current dspy.settings.lm +#Optimize program on current dspy.settings.lm fewshot_optimizer = BootstrapFewShotWithRandomSearch(metric=your_defined_metric, max_bootstrapped_demos=2, num_threads=NUM_THREADS) -your_dspy_program_compiled = tp.compile(your_dspy_program, trainset=trainset[:some_num], valset=trainset[some_num:]) +your_dspy_program_optimized = tp.compile(your_dspy_program, trainset=trainset[:some_num], valset=trainset[some_num:]) #Configure model to finetune config = dict(target=model_to_finetune, epochs=2, bf16=True, bsize=6, accumsteps=2, lr=5e-5) -#Compile program on BootstrapFinetune +#Optimize program on BootstrapFinetune finetune_optimizer = BootstrapFinetune(metric=your_defined_metric) finetune_program = finetune_optimizer.compile(your_dspy_program, trainset=some_new_dataset_for_finetuning_model, **config) @@ -290,7 +290,7 @@ eval_kwargs = dict(num_threads=16, display_progress=True, display_table=0) copro_teleprompter = COPRO(prompt_model=model_to_generate_prompts, metric=your_defined_metric, breadth=num_new_prompts_generated, depth=times_to_generate_prompts, init_temperature=prompt_generation_temperature, verbose=False) -compiled_program_optimized_signature = copro_teleprompter.compile(your_dspy_program, trainset=trainset, eval_kwargs=eval_kwargs) +optimized_program_optimized_signature = copro_teleprompter.compile(your_dspy_program, trainset=trainset, eval_kwargs=eval_kwargs) ``` ### MIPROv2 @@ -367,7 +367,7 @@ from dspy import ChainOfThought knn_optimizer = KNNFewShot(k=3, trainset=trainset, vectorizer=Embedder(SentenceTransformer("all-MiniLM-L6-v2").encode)) -qa_compiled = knn_optimizer.compile(student=ChainOfThought("question -> answer")) +qa_optimized = knn_optimizer.compile(student=ChainOfThought("question -> answer")) ``` ### BootstrapFewShotWithOptuna @@ -377,7 +377,7 @@ from dspy.teleprompt import BootstrapFewShotWithOptuna fewshot_optuna_optimizer = BootstrapFewShotWithOptuna(metric=your_defined_metric, max_bootstrapped_demos=2, num_candidate_programs=8, num_threads=NUM_THREADS) -your_dspy_program_compiled = fewshot_optuna_optimizer.compile(student=your_dspy_program, trainset=trainset, valset=devset) +your_dspy_program_optimized = fewshot_optuna_optimizer.compile(student=your_dspy_program, trainset=trainset, valset=devset) ``` Other custom configurations are similar to customizing the `dspy.BootstrapFewShot` optimizer. diff --git a/docs/docs/faqs.md b/docs/docs/faqs.md index 3a5616fee9..6dfdaa46c8 100644 --- a/docs/docs/faqs.md +++ b/docs/docs/faqs.md @@ -50,14 +50,14 @@ Compiling DSPy `optimizers` naturally will incur additional LM calls, but we sub Here is an example of saving/loading a compiled module: ```python -cot_compiled = teleprompter.compile(CoT(), trainset=trainset, valset=devset) +cot_optimized = teleprompter.compile(CoT(), trainset=trainset, valset=devset) #Saving -cot_compiled.save('compiled_cot_gsm8k.json') +cot_optimized.save('optimized_cot_gsm8k.json') #Loading: cot = CoT() -cot.load('compiled_cot_gsm8k.json') +cot.load('optimized_cot_gsm8k.json') ``` - **How do I export for deployment?** @@ -94,7 +94,7 @@ You can parallelize DSPy programs during both compilation and evaluation by spec - **How do freeze a module?** -Modules can be frozen by setting their `._compiled` attribute to be True, indicating the module has gone through optimizer compilation and should not have its parameters adjusted. This is handled internally in optimizers such as `dspy.BootstrapFewShot` where the student program is ensured to be frozen before the teacher propagates the gathered few-shot demonstrations in the bootstrapping process. +Module parameters are automatically managed during optimization. When using optimizers like `dspy.BootstrapFewShot`, the framework ensures proper handling of parameter updates during the compilation process. The framework tracks which modules should have their parameters adjusted during different phases of optimization automatically. - **How do I use DSPy assertions?** diff --git a/dspy/primitives/base_module.py b/dspy/primitives/base_module.py index aa8bcbee4f..7ba5d03a9d 100644 --- a/dspy/primitives/base_module.py +++ b/dspy/primitives/base_module.py @@ -38,10 +38,8 @@ def add_parameter(param_name, param_value): named_parameters.append((param_name, param_value)) elif isinstance(param_value, dspy.Module): - # When a sub-module is pre-compiled, keep it frozen. - if not getattr(param_value, "_compiled", False): - for sub_name, param in param_value.named_parameters(): - add_parameter(f"{param_name}.{sub_name}", param) + for sub_name, param in param_value.named_parameters(): + add_parameter(f"{param_name}.{sub_name}", param) if isinstance(self, Parameter): add_parameter("self", self) @@ -51,10 +49,8 @@ def add_parameter(param_name, param_value): add_parameter(name, value) elif isinstance(value, dspy.Module): - # When a sub-module is pre-compiled, keep it frozen. - if not getattr(value, "_compiled", False): - for sub_name, param in value.named_parameters(): - add_parameter(f"{name}.{sub_name}", param) + for sub_name, param in value.named_parameters(): + add_parameter(f"{name}.{sub_name}", param) elif isinstance(value, (list, tuple)): for idx, item in enumerate(value): @@ -66,7 +62,7 @@ def add_parameter(param_name, param_value): return named_parameters - def named_sub_modules(self, type_=None, skip_compiled=False) -> Generator[tuple[str, "BaseModule"], None, None]: + def named_sub_modules(self, type_=None) -> Generator[tuple[str, "BaseModule"], None, None]: """Find all sub-modules in the module, as well as their names. Say `self.children[4]['key'].sub_module` is a sub-module. Then the name will be @@ -91,8 +87,6 @@ def add_to_queue(name, item): yield name, item if isinstance(item, BaseModule): - if skip_compiled and getattr(item, "_compiled", False): - continue for sub_name, sub_item in item.__dict__.items(): add_to_queue(f"{name}.{sub_name}", sub_item) diff --git a/dspy/primitives/module.py b/dspy/primitives/module.py index 044e337ae3..d5f5b62a7b 100644 --- a/dspy/primitives/module.py +++ b/dspy/primitives/module.py @@ -40,13 +40,11 @@ def __call__(cls, *args, **kwargs): class Module(BaseModule, metaclass=ProgramMeta): def _base_init(self): - self._compiled = False self.callbacks = [] self.history = [] def __init__(self, callbacks=None): self.callbacks = callbacks or [] - self._compiled = False # LM calling history of the module. self.history = [] diff --git a/dspy/teleprompt/avatar_optimizer.py b/dspy/teleprompt/avatar_optimizer.py index dc2c4511c1..ddba74e5f2 100644 --- a/dspy/teleprompt/avatar_optimizer.py +++ b/dspy/teleprompt/avatar_optimizer.py @@ -222,5 +222,4 @@ def compile(self, student, *, trainset): print(f"Best Actor: {best_actor}") - best_actor._compiled = True return best_actor diff --git a/dspy/teleprompt/bettertogether.py b/dspy/teleprompt/bettertogether.py index f6c3ae3142..84ed97d7a8 100644 --- a/dspy/teleprompt/bettertogether.py +++ b/dspy/teleprompt/bettertogether.py @@ -106,7 +106,6 @@ def _run_strategies(self, parsed_strategy, student, trainset, valset_ratio) -> M # TODO: Should we reset or just deepcopy? How does resetting affect # the predictor LMs? student = student.deepcopy() - student._compiled = False if step_code == "p": student = self._compile_prompt_optimizer(student, trainset, valset_ratio) elif step_code == "w": @@ -120,7 +119,6 @@ def _run_strategies(self, parsed_strategy, student, trainset, valset_ratio) -> M kill_lms(student) student.candidate_programs = candidate_programs - student._compiled = True return student def _compile_prompt_optimizer(self, student, trainset, valset_ratio) -> Module: diff --git a/dspy/teleprompt/bootstrap.py b/dspy/teleprompt/bootstrap.py index 31a5736239..2e69a71038 100644 --- a/dspy/teleprompt/bootstrap.py +++ b/dspy/teleprompt/bootstrap.py @@ -86,7 +86,6 @@ def compile(self, student, *, teacher=None, trainset): self._bootstrap() self.student = self._train() - self.student._compiled = True return self.student @@ -96,9 +95,8 @@ def _prepare_student_and_teacher(self, student, teacher): # NOTE: behavior change on Oct 28, 2024. Deep copy instead of reset copy for the student-as-teacher. self.teacher = teacher.deepcopy() if teacher is not None else student.deepcopy() - assert getattr(self.student, "_compiled", False) is False, "Student must be uncompiled." - if self.max_labeled_demos and getattr(self.teacher, "_compiled", False) is False: + if self.max_labeled_demos: teleprompter = LabeledFewShot(k=self.max_labeled_demos) self.teacher = teleprompter.compile(self.teacher.reset_copy(), trainset=self.trainset) diff --git a/dspy/teleprompt/bootstrap_finetune.py b/dspy/teleprompt/bootstrap_finetune.py index 6a431e355e..7e18245e58 100644 --- a/dspy/teleprompt/bootstrap_finetune.py +++ b/dspy/teleprompt/bootstrap_finetune.py @@ -130,7 +130,6 @@ def compile( pred.demos = [] if self.exclude_demos else pred.demos logger.info("BootstrapFinetune has finished compiling the student program") - student._compiled = True return student @staticmethod @@ -254,8 +253,7 @@ def copy_program_with_lms(program: Module) -> Module: def prepare_student(student: Module) -> Module: - if getattr(student, "_compiled", False): - raise ValueError("The student program should not be compiled.") + # Student program preparation - no compilation check needed # TODO: Should we use reset_copy here? How would it affect the student # program's predictor LMs, if they are set? diff --git a/dspy/teleprompt/copro_optimizer.py b/dspy/teleprompt/copro_optimizer.py index bc62e04362..e0f4f71749 100644 --- a/dspy/teleprompt/copro_optimizer.py +++ b/dspy/teleprompt/copro_optimizer.py @@ -354,5 +354,4 @@ def compile(self, student, *, trainset, eval_kwargs): best_program.results_best = results_best best_program.results_latest = results_latest - best_program._compiled = True return best_program diff --git a/dspy/teleprompt/ensemble.py b/dspy/teleprompt/ensemble.py index 2a9b14a330..6063e04f81 100644 --- a/dspy/teleprompt/ensemble.py +++ b/dspy/teleprompt/ensemble.py @@ -38,5 +38,4 @@ def forward(self, *args, **kwargs): return outputs ensembled_program = EnsembledProgram() - ensembled_program._compiled = True return ensembled_program diff --git a/dspy/teleprompt/gepa/gepa.py b/dspy/teleprompt/gepa/gepa.py index 84586da95c..87cbbf80a5 100644 --- a/dspy/teleprompt/gepa/gepa.py +++ b/dspy/teleprompt/gepa/gepa.py @@ -567,5 +567,4 @@ def feedback_fn( dspy_gepa_result = DspyGEPAResult.from_gepa_result(gepa_result, adapter) new_prog.detailed_results = dspy_gepa_result - new_prog._compiled = True return new_prog diff --git a/dspy/teleprompt/grpo.py b/dspy/teleprompt/grpo.py index d3f80935e4..dcaaa20577 100644 --- a/dspy/teleprompt/grpo.py +++ b/dspy/teleprompt/grpo.py @@ -553,7 +553,6 @@ def compile( recover_lm_cache(program=t, lm_cache_dict=lm_cache_dict) logger.info("GRPO compiler has finished compiling the student program") - student._compiled = True return student diff --git a/dspy/teleprompt/knn_fewshot.py b/dspy/teleprompt/knn_fewshot.py index 5c21b1c739..0c8557ca68 100644 --- a/dspy/teleprompt/knn_fewshot.py +++ b/dspy/teleprompt/knn_fewshot.py @@ -66,5 +66,4 @@ def forward_pass(_, **kwargs): return compiled_program(**kwargs) student_copy.forward = types.MethodType(forward_pass, student_copy) - student_copy._compiled = True return student_copy diff --git a/dspy/teleprompt/mipro_optimizer_v2.py b/dspy/teleprompt/mipro_optimizer_v2.py index e2f0df81ae..fe741ddf8a 100644 --- a/dspy/teleprompt/mipro_optimizer_v2.py +++ b/dspy/teleprompt/mipro_optimizer_v2.py @@ -606,7 +606,6 @@ def objective(trial): logger.info(f"Returning best identified program with score {best_score}!") - best_program._compiled = True return best_program def _log_minibatch_eval( diff --git a/dspy/teleprompt/random_search.py b/dspy/teleprompt/random_search.py index b268d87dbe..c6447e8330 100644 --- a/dspy/teleprompt/random_search.py +++ b/dspy/teleprompt/random_search.py @@ -147,7 +147,6 @@ def compile(self, student, *, teacher=None, trainset, valset=None, restrict=None print(f"{len(best_program.candidate_programs)} candidate programs found.") - best_program._compiled = True return best_program diff --git a/dspy/teleprompt/simba.py b/dspy/teleprompt/simba.py index a66a5d1a3a..230673d8f6 100644 --- a/dspy/teleprompt/simba.py +++ b/dspy/teleprompt/simba.py @@ -363,5 +363,4 @@ def register_new_program(prog: dspy.Module, score_list: list[float]) -> None: best_program.candidate_programs = candidate_data best_program.trial_logs = trial_logs - best_program._compiled = True return best_program diff --git a/dspy/teleprompt/teleprompt_optuna.py b/dspy/teleprompt/teleprompt_optuna.py index c215cc9a43..29b8774228 100644 --- a/dspy/teleprompt/teleprompt_optuna.py +++ b/dspy/teleprompt/teleprompt_optuna.py @@ -73,5 +73,4 @@ def compile(self, student, *, teacher=None, max_demos, trainset, valset=None): best_program = study.trials[study.best_trial.number].user_attrs["program"] print("Best score:", study.best_value) print("Best program:", best_program) - best_program._compiled = True return best_program diff --git a/dspy/teleprompt/vanilla.py b/dspy/teleprompt/vanilla.py index 8a56aa8bf7..3d51186950 100644 --- a/dspy/teleprompt/vanilla.py +++ b/dspy/teleprompt/vanilla.py @@ -22,7 +22,6 @@ def compile(self, student, *, trainset, sample=True): else: predictor.demos = self.trainset[: min(self.k, len(self.trainset))] - self.student._compiled = True return self.student diff --git a/tests/examples/test_baleen.py b/tests/examples/test_baleen.py index 1cc27e8fcb..5a51a34502 100644 --- a/tests/examples/test_baleen.py +++ b/tests/examples/test_baleen.py @@ -94,7 +94,7 @@ def gold_passages_retrieved(example, pred, trace=None): # @pytest.mark.slow_test # TODO: Find a way to make this test run without the slow hotpotqa dataset -def _test_compiled_baleen(): +def _test_optimized_baleen(): trainset, devset = load_hotpotqa() lm = dspy.OpenAI(model="gpt-3.5-turbo") rm = dspy.ColBERTv2(url="http://20.102.90.50:2017/wiki17_abstracts") @@ -103,7 +103,7 @@ def _test_compiled_baleen(): uncompiled_baleen = SimplifiedBaleen() # uncompiled (i.e., zero-shot) program teleprompter = BootstrapFewShot(metric=validate_context_and_answer_and_hops) - compiled_baleen = teleprompter.compile( + optimized_baleen = teleprompter.compile( SimplifiedBaleen(), teacher=SimplifiedBaleen(passages_per_hop=2), trainset=trainset, @@ -115,6 +115,6 @@ def _test_compiled_baleen(): ) # assert uncompiled_baleen_retrieval_score / 100 == 18 / 50 - compiled_baleen_retrieval_score = evaluate_on_hotpotqa(compiled_baleen, metric=gold_passages_retrieved) - # assert compiled_baleen_retrieval_score / 100 == 27 / 50 - assert uncompiled_baleen_retrieval_score < compiled_baleen_retrieval_score + optimized_baleen_retrieval_score = evaluate_on_hotpotqa(optimized_baleen, metric=gold_passages_retrieved) + # assert optimized_baleen_retrieval_score / 100 == 27 / 50 + assert uncompiled_baleen_retrieval_score < optimized_baleen_retrieval_score diff --git a/tests/primitives/test_module.py b/tests/primitives/test_module.py index abd3f21a9d..8c524b2eae 100644 --- a/tests/primitives/test_module.py +++ b/tests/primitives/test_module.py @@ -18,7 +18,9 @@ def forward(self, question): def test_module_initialization(): module = Module() - assert module._compiled is False, "Module _compiled attribute should be False upon initialization" + # Module should be properly initialized with callbacks and history + assert hasattr(module, "callbacks"), "Module should have callbacks attribute" + assert hasattr(module, "history"), "Module should have history attribute" def test_named_predictors(): diff --git a/tests/teleprompt/test_bootstrap.py b/tests/teleprompt/test_bootstrap.py index 17b58aedaf..436c0ee451 100644 --- a/tests/teleprompt/test_bootstrap.py +++ b/tests/teleprompt/test_bootstrap.py @@ -50,7 +50,7 @@ def test_compile_with_predict_instances(): compiled_student = bootstrap.compile(student, teacher=teacher, trainset=trainset) assert compiled_student is not None, "Failed to compile student" - assert hasattr(compiled_student, "_compiled") and compiled_student._compiled, "Student compilation flag not set" + # Compilation successful - student module has been optimized def test_bootstrap_effectiveness(): diff --git a/tests/teleprompt/test_bootstrap_finetune.py b/tests/teleprompt/test_bootstrap_finetune.py index 59c685902f..eb56667842 100644 --- a/tests/teleprompt/test_bootstrap_finetune.py +++ b/tests/teleprompt/test_bootstrap_finetune.py @@ -56,7 +56,7 @@ def test_compile_with_predict_instances(): compiled_student = bootstrap.compile(student, teacher=teacher, trainset=trainset) assert compiled_student is not None, "Failed to compile student" - assert hasattr(compiled_student, "_compiled") and compiled_student._compiled, "Student compilation flag not set" + # Compilation successful - student module has been fine-tuned mock_finetune.assert_called_once() diff --git a/tests/utils/test_saving.py b/tests/utils/test_saving.py index 51cd9fa685..0b58f40e5c 100644 --- a/tests/utils/test_saving.py +++ b/tests/utils/test_saving.py @@ -58,7 +58,7 @@ class MySignature(dspy.Signature): @pytest.mark.extra -def test_save_compiled_model(tmp_path): +def test_save_optimized_model(tmp_path): predict = dspy.Predict("question->answer") dspy.settings.configure(lm=DummyLM([{"answer": "blue"}, {"answer": "white"}] * 10)) @@ -74,12 +74,12 @@ def dummy_metric(example, pred, trace=None): return True optimizer = dspy.BootstrapFewShot(max_bootstrapped_demos=4, max_labeled_demos=4, max_rounds=5, metric=dummy_metric) - compiled_predict = optimizer.compile(predict, trainset=trainset) - compiled_predict.save(tmp_path, save_program=True) + optimized_predict = optimizer.compile(predict, trainset=trainset) + optimized_predict.save(tmp_path, save_program=True) loaded_predict = dspy.load(tmp_path) - assert compiled_predict.demos == loaded_predict.demos - assert compiled_predict.signature == loaded_predict.signature + assert optimized_predict.demos == loaded_predict.demos + assert optimized_predict.signature == loaded_predict.signature def test_load_with_version_mismatch(tmp_path):