Skip to content

Conversation

@MervinPraison
Copy link
Owner

@MervinPraison MervinPraison commented Jul 8, 2025

User description

After the OpenAI refactoring in PR #766, tool calls from streaming responses are ToolCall dataclass objects while non-streaming responses return OpenAI API objects. This fix handles both cases properly to prevent the "'dict' object has no attribute 'name'" error.


PR Type

Bug fix


Description

  • Fix tool call handling for both ToolCall dataclass and OpenAI objects

  • Prevent "'dict' object has no attribute 'name'" error

  • Add type checking for proper attribute access


Changes diagram

flowchart LR
  A["Tool Call Detection"] --> B["Type Check: ToolCall vs OpenAI"]
  B --> C["Dictionary Access for ToolCall"]
  B --> D["Attribute Access for OpenAI"]
  C --> E["Execute Tool Function"]
  D --> E
Loading

Changes walkthrough 📝

Relevant files
Bug fix
agent.py
Fix tool call object type handling                                             

src/praisonai-agents/praisonaiagents/agent/agent.py

  • Add type checking for ToolCall dataclass vs OpenAI objects
  • Use dictionary access for ToolCall dataclass attributes
  • Use attribute access for OpenAI object attributes
  • Handle tool_call_id with fallback for both object types
  • +15/-5   

    Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • Summary by CodeRabbit

    • New Features
      • Improved compatibility for tool call handling, supporting both OpenAI-style objects and a new format.
    • Bug Fixes
      • Enhanced robustness in processing tool calls, reducing potential errors when different tool call formats are used.

    After the OpenAI refactoring in PR #766, tool calls from streaming responses
    are ToolCall dataclass objects while non-streaming responses return OpenAI
    API objects. This fix handles both cases properly to prevent the "'dict' 
    object has no attribute 'name'" error.
    
    Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
    @coderabbitai
    Copy link
    Contributor

    coderabbitai bot commented Jul 8, 2025

    Caution

    Review failed

    The pull request is closed.

    Walkthrough

    The update introduces support for handling tool calls represented as either OpenAI-style objects or as a new ToolCall dataclass within the _chat_completion method. Conditional logic is added to access function names, arguments, and IDs appropriately based on the tool call's representation, without altering the overall control flow.

    Changes

    File(s) Change Summary
    src/praisonai-agents/praisonaiagents/agent/agent.py Modified _chat_completion to support both OpenAI-style and ToolCall dataclass tool call formats.

    Sequence Diagram(s)

    sequenceDiagram
        participant Agent
        participant ToolCall (object/dict)
        participant ToolExecutor
    
        Agent->>ToolCall: Iterate tool_calls
        alt ToolCall is dataclass
            Agent->>ToolCall: Access function["name"], function["arguments"]
        else ToolCall is OpenAI-style object
            Agent->>ToolCall: Access function.name, function.arguments
        end
        Agent->>ToolExecutor: Execute tool with extracted name and arguments
        Agent->>ToolCall: Access ID (attribute or key) for continuation logic
    
    Loading

    Possibly related PRs

    • Fix redundant tool invocation #594: Both PRs address tool call processing, with this PR focusing on internal compatibility for multiple tool call formats and the related PR removing redundant tool call handling in a different method.

    Suggested labels

    Review effort 2/5, codex

    Poem

    A rabbit hops through code anew,
    Supporting tool calls, not just a few.
    Whether object or dataclass,
    The agent now lets both types pass.
    With nimble paws and careful sight,
    It makes sure every tool call's right!
    🐇✨


    📜 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 bc5e89c and 0ebcedd.

    📒 Files selected for processing (1)
    • src/praisonai-agents/praisonaiagents/agent/agent.py (2 hunks)
    ✨ Finishing Touches
    • 📝 Generate Docstrings

    Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

    ❤️ Share
    🪧 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.

    @MervinPraison MervinPraison merged commit 109cf3b into main Jul 8, 2025
    8 of 9 checks passed
    @qodo-merge-pro
    Copy link

    qodo-merge-pro bot commented Jul 8, 2025

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Code Duplication

    The same type checking and attribute extraction logic is duplicated in two separate loops. This violates DRY principle and makes maintenance harder. Consider extracting this logic into a helper method.

        if isinstance(tool_call, ToolCall):
            function_name = tool_call.function["name"]
            arguments = json.loads(tool_call.function["arguments"])
        else:
            function_name = tool_call.function.name
            arguments = json.loads(tool_call.function.arguments)
    
        if self.verbose:
            display_tool_call(f"Agent {self.name} is calling function '{function_name}' with arguments: {arguments}")
    
        tool_result = self.execute_tool(function_name, arguments)
        results_str = json.dumps(tool_result) if tool_result else "Function returned an empty output"
    
        if self.verbose:
            display_tool_call(f"Function '{function_name}' returned: {results_str}")
    
        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id if hasattr(tool_call, 'id') else tool_call['id'],
            "content": results_str
        })
    
    # Check if we should continue (for tools like sequential thinking)
    should_continue = False
    for tool_call in tool_calls:
        # Handle both ToolCall dataclass and OpenAI object
        if isinstance(tool_call, ToolCall):
            function_name = tool_call.function["name"]
            arguments = json.loads(tool_call.function["arguments"])
        else:
            function_name = tool_call.function.name
            arguments = json.loads(tool_call.function.arguments)
    Missing Import

    The code uses isinstance(tool_call, ToolCall) but there's no visible import for the ToolCall class. This could cause a NameError at runtime if the import is missing.

    if isinstance(tool_call, ToolCall):
        function_name = tool_call.function["name"]

    Copy link
    Contributor

    @gemini-code-assist gemini-code-assist bot left a comment

    Choose a reason for hiding this comment

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

    Summary of Changes

    Hello @MervinPraison, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

    This pull request resolves a critical bug in the agent's tool execution mechanism by introducing robust type handling for tool call objects. It ensures that the system can correctly interpret and process tool calls, regardless of whether they originate from streaming or non-streaming OpenAI API responses, thereby preventing attribute errors and enhancing the reliability of the agent's operations.

    Highlights

    • Bug Fix: Addressed a runtime error ('dict' object has no attribute 'name') that occurred when processing tool calls, specifically due to inconsistencies in object types returned by the OpenAI API (ToolCall dataclass for streaming vs. OpenAI object for non-streaming responses).
    • Type Handling: Implemented conditional logic within the _chat_completion method to correctly parse function_name, arguments, and tool_call_id by checking if the tool_call object is a ToolCall dataclass or a standard OpenAI API object.
    • Code Robustness: Ensured that the agent's tool execution logic is robust to variations in the structure of tool call objects received from the OpenAI API, improving overall stability.
    Using Gemini Code Assist

    The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

    Invoking Gemini

    You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

    Feature Command Description
    Code Review /gemini review Performs a code review for the current pull request in its current state.
    Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
    Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
    Help /gemini help Displays a list of available commands.

    Customization

    To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

    Limitations & Feedback

    Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

    You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

    Footnotes

    1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

    @qodo-merge-pro
    Copy link

    qodo-merge-pro bot commented Jul 8, 2025

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    Possible issue
    Add safe fallback for tool ID

    The fallback logic assumes tool_call['id'] exists when hasattr(tool_call, 'id')
    is False, but this could raise a KeyError. Add proper error handling or
    validation to ensure the ID exists in both cases.

    src/praisonai-agents/praisonaiagents/agent/agent.py [1275]

    -"tool_call_id": tool_call.id if hasattr(tool_call, 'id') else tool_call['id'],
    +"tool_call_id": getattr(tool_call, 'id', tool_call.get('id')) if hasattr(tool_call, 'id') else tool_call.get('id'),
    • Apply / Chat
    Suggestion importance[1-10]: 7

    __

    Why: The suggestion correctly identifies a potential KeyError by using direct dictionary access (tool_call['id']) and proposes using a safer access method, which prevents a potential runtime error.

    Medium
    General
    Extract duplicated logic into helper method

    Extract the duplicated type checking logic into a helper method to avoid code
    repetition. The same pattern is repeated three times in the code, which violates
    the DRY principle and makes maintenance harder.

    src/praisonai-agents/praisonaiagents/agent/agent.py [1256-1262]

    -# Handle both ToolCall dataclass and OpenAI object
    -if isinstance(tool_call, ToolCall):
    -    function_name = tool_call.function["name"]
    -    arguments = json.loads(tool_call.function["arguments"])
    -else:
    -    function_name = tool_call.function.name
    -    arguments = json.loads(tool_call.function.arguments)
    +def _extract_tool_call_info(self, tool_call):
    +    if isinstance(tool_call, ToolCall):
    +        return tool_call.function["name"], json.loads(tool_call.function["arguments"])
    +    else:
    +        return tool_call.function.name, json.loads(tool_call.function.arguments)
     
    +# Usage:
    +function_name, arguments = self._extract_tool_call_info(tool_call)
    +
    • Apply / Chat
    Suggestion importance[1-10]: 6

    __

    Why: The suggestion correctly identifies duplicated code for handling tool_call objects and proposes a helper method, which improves code quality and maintainability by adhering to the DRY principle.

    Low
    • More

    Copy link
    Contributor

    @gemini-code-assist gemini-code-assist bot left a comment

    Choose a reason for hiding this comment

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

    Code Review

    This pull request correctly handles different types of tool call objects, preventing potential AttributeError exceptions. The changes look correct. I have included suggestions to improve maintainability and robustness.

    Comment on lines +1257 to +1262
    if isinstance(tool_call, ToolCall):
    function_name = tool_call.function["name"]
    arguments = json.loads(tool_call.function["arguments"])
    else:
    function_name = tool_call.function.name
    arguments = json.loads(tool_call.function.arguments)
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    medium

    This logic for handling different tool_call types is duplicated on lines 1283-1288. Consider refactoring to reduce code duplication and improve maintainability.

    messages.append({
    "role": "tool",
    "tool_call_id": tool_call.id,
    "tool_call_id": tool_call.id if hasattr(tool_call, 'id') else tool_call['id'],
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    medium

    For robustness, use isinstance to check the type of tool_call directly, instead of hasattr, before accessing its attributes.

    Suggested change
    "tool_call_id": tool_call.id if hasattr(tool_call, 'id') else tool_call['id'],
    "tool_call_id": tool_call['id'] if isinstance(tool_call, dict) else tool_call.id,

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

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    2 participants