Skip to content

Conversation

kalanyuz
Copy link
Contributor

With @TomeHirata 's #8692 that adds support for ResponseAPI, we can already actually use OpenAI's native tools like web_search or interpreter by initiating dspy.LM as the following:

        lm = LM(
            'openai/gpt-5',
            max_tokens=100_000,
            temperature=1.0,
            api_key=os.getenv("OPENAI_API_KEY"),
            model_type="responses",
            cache=False,
            text={
                "verbosity": "low"
            },
            tools=[{"type": "web_search_preview"}, {"type": "code_interpreter", "container": {"type": "auto"}}],
        )

        dspy.configure(lm=lm)

However, the current version of dspy would always fail, saying the response doesn't have the property type.

Upon debugging further, I found out that this is because base_lm undergoes litellm's wrapper function that transform each item in output as object. However they haven't added support for web_search_call or interpreter yet, therefore returning a simple dictionary. This causes the dspy to fail despite having the capability to return results.

This pull request refactors the _process_response method in dspy/clients/base_lm.py to add dict support so that dspy does not have to wait for litellm to add support for those objects.

@kalanyuz kalanyuz changed the title Support both dict and object response api's _process_response Support both dict and object for _process_response Aug 28, 2025
@okhat okhat requested review from Copilot and TomeHirata September 1, 2025 00:46
Copy link
Contributor

@Copilot 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 PR enables _process_response to handle both object attributes and dictionary keys when processing OpenAI's native tools like web_search and interpreter. The change addresses a compatibility issue where litellm returns dictionaries for some tool responses instead of objects, causing DSPy to fail when accessing the type property.

  • Adds a helper function to safely access both object attributes and dictionary keys
  • Updates response processing logic to work with both object and dictionary formats
  • Maintains backward compatibility with existing object-based responses

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@okhat
Copy link
Collaborator

okhat commented Sep 1, 2025

Thanks so much @kalanyuz !! Any chance some of the things in this PR should be (or even have been already) fixed upstream in LiteLLM instead?

@kalanyuz
Copy link
Contributor Author

kalanyuz commented Sep 3, 2025

@okhat I believe this issue is unique to dspy side due to its tight coupling with LiteLLM for it's usage tracking implementation. We can contribute to LiteLLM by adding more transformations but it'd probably be come a cat-and-mouse game where dspy would break every time a model provider decides to introduce a new object / features in the 'usage' object.

Would giving dspy the ability to fall back on basic dictionary make it more futureproof in this case?

@TomeHirata
Copy link
Collaborator

While I'd prefer to support this in litellm for the proper fix, I'm fine with the safely measure in dspy. Btw, should we convert the response into dict first so that we don't need to check the field and key every time?

@kalanyuz kalanyuz force-pushed the fix/dict_support_for_process_response branch from 9f1a2d1 to 4cfb5ae Compare September 5, 2025 11:25
@kalanyuz
Copy link
Contributor Author

kalanyuz commented Sep 5, 2025

@TomeHirata is this better?

return None
if isinstance(obj, dict):
return obj
if hasattr(obj, "model_dump"):
Copy link
Collaborator

Choose a reason for hiding this comment

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

can we do isinstance(obj, BaseModel) instead?

if hasattr(obj, "model_dump"):
return obj.model_dump()
if hasattr(obj, "__dict__") and not isinstance(obj, dict):
return {k: v for k, v in obj.__dict__.items() if not k.startswith("_")}
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is if not k.startswith("_") for?

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