Skip to content

fix: release the token counter when CompactionHook is closed - #12985

Merged
anakin87 merged 2 commits into
deepset-ai:mainfrom
nanhanq1:fix/compaction-hook-closes-token-counter
Sep 28, 2026
Merged

anakin87 merged 2 commits into
deepset-ai:mainfrom
nanhanq1:fix/compaction-hook-closes-token-counter

Conversation

@nanhanq1

Copy link
Copy Markdown

Related Issues

Proposed Changes:

CompactionHook owns two objects that can hold resources - the token_counter and the compactor - but only the compactor had a full lifecycle. warm_up() and warm_up_async() warmed up both, while close() and close_async() released only the compactor, and nothing else in haystack/ closes a token counter. A counter that had been warmed up was therefore never released.

OpenAITokenCounter is where this shows: warm_up() builds an openai.OpenAI client, and with it an HTTP connection pool, and its own close() drops it again. Agent.close() -> close_hooks() -> CompactionHook.close() never reached that close(), so the client outlived the hook that created it.

The hook now treats both resources the same way in all four lifecycle methods:

  • close() and close_async() release the token counter, then the compactor.
  • warm_up_async() and close_async() await a resource's warm_up_async / close_async when it defines one, and fall back to the sync method otherwise. That is the contract haystack/hooks/utils.py already uses for hooks; warm_up_async() previously called a token counter's sync warm_up() unconditionally.

The two getattr/hasattr pairs are factored into the private _warm_up_async / _close_async helpers 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_counter and test_lifecycle_also_closes_the_token_counter_async use 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_counter uses the real OpenAITokenCounter and asserts counter.client is built by warm_up() and dropped by close(). No network call is made - warm_up() only constructs the client.

Checks run locally:

  • A/B on the fix: with hooks.py reverted to main, 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, mypy and codespell clean 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 defines warm_up_async is now awaited rather than having its sync warm_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 the TokenCounter protocol - so ApproximateTokenCounter and TiktokenCounter, which have no close(), are unaffected.

Checklist

This PR was written with the help of an AI assistant. I have reviewed the changes and run the relevant tests locally.

@nanhanq1
nanhanq1 requested a review from a team as a code owner September 27, 2026 12:21
@nanhanq1
nanhanq1 requested review from anakin87 and removed request for a team September 27, 2026 12:21
@vercel

vercel Bot commented Sep 27, 2026

Copy link
Copy Markdown
Contributor

@nanhanq1 is attempting to deploy a commit to the deepset Team on Vercel.

A member of the Team first needs to authorize it.

@anakin87 anakin87 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you!

I simplified a bit the implementation

@anakin87
anakin87 enabled auto-merge (squash) September 28, 2026 09:38
@github-actions github-actions Bot added the type:documentation Improvements on the docs label Sep 28, 2026
@anakin87
anakin87 merged commit 693ec05 into deepset-ai:main Sep 28, 2026
22 of 23 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  haystack/hooks/compaction
  hooks.py 296
Project Total  

This report was generated by python-coverage-comment-action

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic:tests type:documentation Improvements on the docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CompactionHook.close() releases the compactor but never the token counter

2 participants