OpenHands icon indicating copy to clipboard operation
OpenHands copied to clipboard

Support for condensation pipelines

Open csmith49 opened this issue 8 months ago • 2 comments

What problem or use case are you trying to solve?

There are situations where it's handy to have multiple condensers simultaneously: the BrowserOutputCondenser is great for reducing costs when the agent is browsing, and the LLMSummarizingCondenser is great at reducing costs for long-running tasks.

But there's no condenser that's great for long-running tasks when the agent is also browsing.

Describe the UX or technical implementation you have in mind

We could write a new condenser that combines the behavior of the BrowserOutputCondenser and the LLMSummarizingCondenser. That's likely to lead to all condenser behavior centralizing to a single, difficult-to-maintain implementation. The alternative proposed here is to make it so that individual condensers can be "composed" by the agent.

The central task in developing a condenser pipeline is handling the composition. Condensers act via the function:

condense: View -> View | CondensationAction

Some condensers, like BrowserOutputCondenser, will never emit a CondensationAction, which makes their inclusion trivial: given two functions View -> View I can always make a new function View -> View by composing them.

(This might warrant a new base class for them to inherit from to make that behavior clear.)

When we have a condenser that can emit either a View or a CondensationAction, we don't have any other way to handle the CondensationAction except for passing it back to the agent -- no downstream condenser can do anything with that information.

So the proposal: add a new Condenser implementation called CondenserPipeline that is parameterized by a list of condensers and whose condense function acts something like:

def condense(self, view: View) -> View | CondensationAction:
    result: View | CondensationAction = view
    for condenser in self.condensers:
        result = condenser.condense(result)
        if isinstance(result, CondensationAction):
            break
    return result

Configuration might be tricky (as evidenced by #7793), and we'll want to be careful about tuning a condenser pipeline -- intersectional effects likely mean we'll want to re-run experiments to identify the appropriate parameterization for the entire pipeline.

Additional context

If you find this feature request or enhancement useful, make sure to add a 👍 to the issue

csmith49 avatar Apr 14 '25 20:04 csmith49