Skip to content

Add work-around for tool_calls with granite models.#211

Merged
tisnik merged 1 commit intolightspeed-core:mainfrom
manstis:granite_tool_parser
Jul 9, 2025
Merged

Add work-around for tool_calls with granite models.#211
tisnik merged 1 commit intolightspeed-core:mainfrom
manstis:granite_tool_parser

Conversation

@manstis
Copy link
Contributor

@manstis manstis commented Jul 9, 2025

Description

During the integration of Ansible Lightspeed with vanilla llama-stack invocation of MCP Tool Calls was troublesum.

After much investigation and research by @TamiTakamiya an issue in llama-stack with IBM granite models was identified.

The fix in this PR was required in our prototype llama-stack work to optimise MCP Tool Calls with granite models.

We are contributing the same work-around to lightspeed-stack.

Type of change

  • Refactor
  • New feature
  • Bug fix
  • CVE fix
  • Optimization
  • Documentation Update
  • Configuration Update
  • Bump-up service version
  • Bump-up dependent library
  • Bump-up library or tool used for development (does not change the final image)
  • CI configuration change
  • Konflux configuration change
  • Unit tests improvement
  • Integration tests improvement
  • End to end tests improvement

Related Tickets & Documents

Checklist before requesting a review

  • I have performed a self-review of my code.
  • PR has passed all pre-merge test jobs.
  • If it is a core feature, I have added thorough tests.

Testing

  • Please provide detailed steps to perform tests related to this code change.
  • How were the fix/results from this change verified? Please provide relevant screenshots or results.

Summary by CodeRabbit

  • New Features
    • Improved agent capabilities by introducing specialized handling for tool calls in granite models.
  • Tests
    • Added unit tests to verify the behavior of the new tool parser for granite models.
    • Updated existing tests to align with enhanced agent initialization parameters.

@manstis manstis requested a review from umago July 9, 2025 11:54
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 9, 2025

Walkthrough

A new GraniteToolParser subclass is introduced to handle tool calls for granite models, with logic to select this parser based on the model ID. The agent creation process is updated to pass this parser when appropriate. Unit tests are added to validate the behavior of the new parser and its selection logic. Test updates reflect the new argument in mocked agent creation.

Changes

File(s) Change Summary
src/app/endpoints/streaming_query.py Imports GraniteToolParser and updates get_agent to provide a model-specific tool parser.
src/utils/types.py Adds GraniteToolParser subclass, get_tool_calls method, and get_parser static method.
tests/unit/utils/test_types.py Adds unit tests for GraniteToolParser and its static method get_parser.
tests/unit/app/endpoints/test_streaming_query.py Updates test calls to mocked AsyncAgent constructor to include tool_parser=None.

Poem

In code’s green field, a parser hops in,
Granite models now know where to begin.
With tests that ensure it’s working all right,
Tool calls are parsed, both day and night.
A rabbit’s delight, this update so neat,
Hopping through Python with nimble feet! 🐇


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d661a29 and 0596fbf.

📒 Files selected for processing (4)
  • src/app/endpoints/streaming_query.py (2 hunks)
  • src/utils/types.py (2 hunks)
  • tests/unit/app/endpoints/test_streaming_query.py (5 hunks)
  • tests/unit/utils/test_types.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/app/endpoints/streaming_query.py
  • tests/unit/utils/test_types.py
  • tests/unit/app/endpoints/test_streaming_query.py
  • src/utils/types.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-pr
  • GitHub Check: Pylinter
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@manstis manstis requested a review from tisnik July 9, 2025 11:54
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
tests/unit/utils/test_types.py (1)

40-48: Fix docstring and improve mock data

The docstring is incorrect and the mock data could be more realistic.

Apply this diff to fix the issues:

-    def test_get_tool_calls_from_completion_message_when_message_has_tool_calls(self):
-        """Test that get_tool_calls returns an empty array when CompletionMessage has no tool_calls."""
+    def test_get_tool_calls_from_completion_message_when_message_has_tool_calls(self):
+        """Test that get_tool_calls returns the tool_calls when CompletionMessage has tool_calls."""
         tool_parser = GraniteToolParser.get_parser("granite-3.3-8b-instruct")
         completion_message = Mock()
-        tool_calls = [{"tool_name": "tool-1"}, {"tool_name": "tool-2"}]
+        tool_calls = [Mock(tool_name="tool-1"), Mock(tool_name="tool-2")]
         completion_message.tool_calls = tool_calls
         assert (
             tool_parser.get_tool_calls(completion_message) == tool_calls
         ), f"get_tool_calls should return {tool_calls}"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2cc5e93 and 3a00de6.

📒 Files selected for processing (3)
  • src/app/endpoints/streaming_query.py (2 hunks)
  • src/utils/types.py (2 hunks)
  • tests/unit/utils/test_types.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/app/endpoints/streaming_query.py (1)
src/utils/types.py (2)
  • GraniteToolParser (23-37)
  • get_parser (33-37)
tests/unit/utils/test_types.py (1)
src/utils/types.py (3)
  • GraniteToolParser (23-37)
  • get_parser (33-37)
  • get_tool_calls (26-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: unit_tests (3.12)
  • GitHub Check: build-pr
🔇 Additional comments (8)
src/utils/types.py (2)

3-7: LGTM: Imports are appropriate

The new imports are necessary for the GraniteToolParser implementation and follow proper typing conventions.


22-37: LGTM: Well-implemented workaround with good defensive programming

The implementation correctly:

  • References the GitHub issue for context
  • Handles null/empty inputs gracefully
  • Uses case-insensitive model ID matching
  • Provides a clean factory pattern via the static method

The defensive checks (if output_message and output_message.tool_calls and if model_id and model_id.lower().startswith("granite")) properly handle edge cases.

src/app/endpoints/streaming_query.py (2)

27-27: LGTM: Clean import addition

The import is properly placed and follows the existing import structure.


65-65: LGTM: Proper integration of the granite tool parser

The integration correctly passes the result of GraniteToolParser.get_parser(model_id) to the AsyncAgent constructor. Since the method returns None for non-granite models, this maintains backward compatibility while enabling the workaround for granite models.

tests/unit/utils/test_types.py (4)

1-6: LGTM: Proper test setup

The test module is well-structured with appropriate imports and clear organization.


9-24: LGTM: Good coverage of the get_parser static method

The tests properly verify that the parser is only returned for model IDs that start with "granite", covering both negative and positive cases.


26-29: LGTM: Proper null handling test

The test correctly verifies that get_tool_calls returns an empty list when passed None.


31-38: LGTM: Empty tool_calls test

The test correctly verifies the behavior when a completion message has an empty tool_calls list.

@manstis manstis force-pushed the granite_tool_parser branch from 3a00de6 to 61651bc Compare July 9, 2025 12:01
Copy link
Contributor

@tisnik tisnik left a comment

Choose a reason for hiding this comment

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

nit, will be probably repeated by Rabbit too ;)

@@ -1,5 +1,11 @@
"""Common types for the project."""

from typing import List, Optional
Copy link
Contributor

Choose a reason for hiding this comment

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

List->list

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, well, @tisnik this is interesting.

I originally had list as I know you prefer this over List however one of the CI checks failed saying the method signature of the get_tool_calls(..) didn't match that of the super class (that uses List)...

I've changed to list.. let's see what CI says 😄

Copy link
Contributor

Choose a reason for hiding this comment

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

and it's green :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, glad to see 👍

@manstis manstis force-pushed the granite_tool_parser branch from 61651bc to d661a29 Compare July 9, 2025 12:04
@manstis manstis force-pushed the granite_tool_parser branch from d661a29 to 0596fbf Compare July 9, 2025 12:06
Copy link
Contributor

@tisnik tisnik left a comment

Choose a reason for hiding this comment

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

LGTM, thank you

@tisnik tisnik merged commit 98e69de into lightspeed-core:main Jul 9, 2025
17 checks passed
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.

2 participants