stanfordnlp / dspy

DSPy: The framework for programming—not prompting—foundation models
https://dspy-docs.vercel.app/
MIT License
14.9k stars 1.15k forks source link

Typed predictors initiated with a range make assert_transform_module crash #882

Open ko0stik opened 3 months ago

ko0stik commented 3 months ago

Hi there, first, thanks a lot for this very cool library!

Code to reproduce the issue:

import functools
from logging import Logger
from typing import List, Optional
from dspy import Module, Suggest, assert_transform_module, backtrack_handler, Predict
from dspy.functional import TypedChainOfThought

class Sandbox(Module):
    def __init__(self, logger: Logger = Logger("Sandbox")):
        super().__init__()
        self.logger = logger

    def forward(self, context: List[str] = []):
        transformed_module_with_array_nested_predictors = assert_transform_module(
            TransformedModule(),
            functools.partial(backtrack_handler, max_backtracks=2))
        transformed_module_with_array_nested_predictors([])

class TransformedModule(Module):
    def __init__(self, logger: Logger = Logger("TransformedModule")):
        super().__init__()
        self.logger = logger
        self.array_predictor = [TypedChainOfThought("question -> answer") for _ in range(3)]

    def forward(self, context: List[str] = []):
        self.array_predictor[0](question="What is the capital of the world?")

Trace:

module
    module.map_named_predictors(dspy.retry.Retry)
  File "/Users/X/Library/Caches/pypoetry/virtualenvs/rag-jAwKM_yQ-py3.11/lib/python3.11/site-packages/dspy/primitives/program.py", line 47, in map_named_predictors
    set_attribute_by_name(self, name, func(predictor))
  File "/Users/X/Library/Caches/pypoetry/virtualenvs/rag-jAwKM_yQ-py3.11/lib/python3.11/site-packages/dspy/primitives/program.py", line 88, in set_attribute_by_name
    sub_obj = getattr(obj, module_name)
              ^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'TransformedModule' object has no attribute 'array_predictor[0]'. Did you mean: 'array_predictor'?

It works as it should with non typed arrays of predictors or typed predictors alone

My dirty workaround:

# __init__
        [setattr(self, f"finalize_sub_section_{i}", TypedChainOfThought(GenerateFinalisedSubsection)) for i in range(max_subsections)]

# forward
for i, sub_section in enumerate(sub_sections[:self.max_subsections]):
            # p = self.finalize_sub_section[i](
            p = getattr(self, f"finalize_sub_section_{i}")(context=...
arnavsinghvi11 commented 3 months ago

Hi @ko0stik , thanks for raising this. Assertions at the time were experimental and not adapted for TypedPredictors just yet (as per the note on nested patterns here). Will keep this open for patching. Tagging @Shangyint for any additional comments.