fix: release the token counter when CompactionHook is closed - #12985
Merged
anakin87 merged 2 commits intoSep 28, 2026
Merged
Conversation
Contributor
|
@nanhanq1 is attempting to deploy a commit to the deepset Team on Vercel. A member of the Team first needs to authorize it. |
1 task done
anakin87
approved these changes
Sep 28, 2026
anakin87
left a comment
Member
There was a problem hiding this comment.
Thank you!
I simplified a bit the implementation
Contributor
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issues
Proposed Changes:
CompactionHookowns two objects that can hold resources - thetoken_counterand thecompactor- but only the compactor had a full lifecycle.warm_up()andwarm_up_async()warmed up both, whileclose()andclose_async()released only the compactor, and nothing else inhaystack/closes a token counter. A counter that had been warmed up was therefore never released.OpenAITokenCounteris where this shows:warm_up()builds anopenai.OpenAIclient, and with it an HTTP connection pool, and its ownclose()drops it again.Agent.close()->close_hooks()->CompactionHook.close()never reached thatclose(), so the client outlived the hook that created it.The hook now treats both resources the same way in all four lifecycle methods:
close()andclose_async()release the token counter, then the compactor.warm_up_async()andclose_async()await a resource'swarm_up_async/close_asyncwhen it defines one, and fall back to the sync method otherwise. That is the contracthaystack/hooks/utils.pyalready uses for hooks;warm_up_async()previously called a token counter's syncwarm_up()unconditionally.The two
getattr/hasattrpairs are factored into the private_warm_up_async/_close_asynchelpers so both resources go through the same path instead of repeating the pattern.How did you test it?
Three new unit tests in
test/hooks/compaction/test_hooks.py:test_lifecycle_also_closes_the_token_counterandtest_lifecycle_also_closes_the_token_counter_asyncuse a counter that records the lifecycle calls it receives, and assert it sees the same calls as the compactor does.test_lifecycle_closes_a_real_openai_token_counteruses the realOpenAITokenCounterand assertscounter.clientis built bywarm_up()and dropped byclose(). No network call is made -warm_up()only constructs the client.Checks run locally:
hooks.pyreverted tomain, exactly those three tests fail (3 failed, 38 passed); with the fix,41 passed.test/hooks+test/components/agents:682 passed, 5 skipped.ruff check,ruff format --check,mypyandcodespellclean on the changed files.Notes for the reviewer
The
warm_up_async()change is the one deliberate behaviour change beyond the reported leak: a token counter that defineswarm_up_asyncis now awaited rather than having its syncwarm_up()called. No counter in the repository defines one, so nothing changes for the built-in counters, and it makes the token counter match how the compactor is already handled. Happy to drop it if you would rather keep the diff to the close path.The token counter is duck-typed the same way the compactor already is - via
hasattr, not a change to theTokenCounterprotocol - soApproximateTokenCounterandTiktokenCounter, which have noclose(), are unaffected.Checklist
fix:.This PR was written with the help of an AI assistant. I have reviewed the changes and run the relevant tests locally.