Skip to content

feat!: Track step_count, token_usage and tool_call_counts in Agent's State#11427

Merged
sjrl merged 7 commits into
v3from
add-more-to-state
May 29, 2026
Merged

feat!: Track step_count, token_usage and tool_call_counts in Agent's State#11427
sjrl merged 7 commits into
v3from
add-more-to-state

Conversation

@sjrl
Copy link
Copy Markdown
Contributor

@sjrl sjrl commented May 28, 2026

Related Issues

  • part of new features for Agent in v3

Proposed Changes:

Automatically add three new keys into Agent's state_schema: step_count, token_usage and tool_call_counts. These are managed by the Agent within State and are exposed as outputs from the Agent.

Besides this information being generally useful for downstream consumption, this is part of a larger goal to add callbacks to the Agent loop to allow tools to be triggered by the current status of the State object. See the Condition param section here https://github.com/deepset-ai/haystack-private/issues/329#issuecomment-4327141039 for more details on this idea.

How did you test it?

Added new tests

Notes for the reviewer

This is partially a breaking change because we now raise an error if a user tries to add a state_schema key that we are now reserving. I don't imagine this will cause many users problems, but I've added an upgrade section to the reno and added an entry to the Migration guide.

Checklist

  • I have read the contributors guidelines and the code of conduct.
  • I have updated the related issue with new insights and changes.
  • I have added unit tests and updated the docstrings.
  • I've used one of the conventional commit types for my PR title: fix:, feat:, build:, chore:, ci:, docs:, style:, refactor:, perf:, test: and added ! in case the PR includes breaking changes.
  • I have documented my code.
  • I have added a release note file, following the contributors guidelines.
  • I have run pre-commit hooks and fixed any issue.

@sjrl sjrl self-assigned this May 28, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
haystack-docs Ignored Ignored Preview May 29, 2026 8:49am

Request Review

@github-actions github-actions Bot added topic:tests type:documentation Improvements on the docs labels May 28, 2026
@sjrl sjrl marked this pull request as ready for review May 28, 2026 13:53
@sjrl sjrl requested review from a team as code owners May 28, 2026 13:53
@sjrl sjrl requested review from julian-risch and removed request for a team May 28, 2026 13:53
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  haystack/components/agents
  agent.py 116
  haystack/components/generators/chat
  llm.py
Project Total  

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

@sjrl sjrl added this to the 3.0 milestone May 29, 2026
Copy link
Copy Markdown
Member

@julian-risch julian-risch left a comment

Choose a reason for hiding this comment

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

Looks quite good to me already. Minor comments.

A dictionary with the following keys:
- "messages": List of all messages exchanged during the LLM's run.
- "last_message": The last message exchanged during the LLM's run.
- "token_usage": Token usage from the LLM call (e.g. prompt_tokens, completion_tokens). Empty if the
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.

What about adding this to the release note under enhancements? I suggest we do that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 22901c9

Comment thread haystack/components/agents/agent.py Outdated
A dictionary with the following keys:
- "messages": List of all messages exchanged during the agent's run.
- "last_message": The last message exchanged during the agent's run.
- "step_count": The number of steps the agent ran.
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.

I thought a bit about the naming of step_count and whether it's intuitive enough to users.
llm_call_count or iteration_count came to mind as alternatives but step_count seems best to me as long as we explain in documentation what we refer to as one step.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Expanded docs in 22901c9

Comment thread test/components/agents/test_agent.py Outdated
"http_client_kwargs": None,
},
},
"tools": [
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.

The changes here in test_from_dict_state_schema_none seem unrelated?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes that's true, I did this to help reduce line count in the tests. The tools are unnecessary for what the unit test is claiming to test and so it's wasted lines.

agent.run([ChatMessage.from_user("Hello")])
result = agent.run([ChatMessage.from_user("Hello")])
assert "Agent reached maximum agent steps" in caplog.text
assert result["step_count"] == 0
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.

When max_steps is exceeded the loop returns before incrementing the counter. Should we maybe increase the step_count before exiting to count the attempted steps?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually in this case the chat generator nor any tools where called, the whole loop is skipped since the max run steps is set to 0. If it were set to 1 then step_count would also be returned as 1.

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.

Ah, that's great. Thanks for the explanation!

**kwargs,
)
# Inherited Agent-internal bookkeeping that isn't useful at the LLM surface.
result.pop("step_count", None)
Copy link
Copy Markdown
Member

@julian-risch julian-risch May 29, 2026

Choose a reason for hiding this comment

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

If we add another key to the Agent's _INTERNAL_STATE_KEYS, we need to remember to also pop it here. Just a comment. No change request.

sjrl and others added 2 commits May 29, 2026 10:48
Co-authored-by: Julian Risch <julian.risch@deepset.ai>
@sjrl sjrl requested a review from julian-risch May 29, 2026 08:50
@sjrl sjrl merged commit 278c599 into v3 May 29, 2026
30 of 31 checks passed
@sjrl sjrl deleted the add-more-to-state branch May 29, 2026 09:07
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.

2 participants