Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance webhook handling logic with improved error handling #2

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

kushalpoddar
Copy link

@kushalpoddar kushalpoddar commented Nov 27, 2024

Purpose

This PR introduces improvements to the webhook handling logic in the AI Suite framework. The key changes focus on enhancing the error handling capabilities, ensuring more robust and informative responses in case of issues during webhook processing.

Critical Changes

  • Added Message class to the aisuite.framework module to represent the contents of API responses that do not conform to the OpenAI style response. This class includes fields for content, tool_calls, role, and refusal.
  • Implemented enhanced error handling in the AnthropicProvider class, specifically for handling tool calls. The changes include:
    • Converting OpenAI tool calls to the Anthropic format.
    • Handling assistant messages with tool calls, converting the response to the expected format.
    • Mapping Anthropic's stop_reason to the corresponding OpenAI finish_reason.
    • Populating the usage information in the normalized response.
  • Added support for tool calling in the AwsProvider class, including:
    • Transforming OpenAI tool call format to the AWS Bedrock format.
    • Handling tool results and converting them back to the OpenAI format.
    • Transforming assistant messages with tool calls to the AWS Bedrock format.
  • Implemented tool calling support in the GroqProvider and MistralProvider classes, ensuring seamless integration with the framework's tool management capabilities.

===== Original PR title and description ============

Original Title: Enhance webhook handling logic with improved error handling

Original Description:

Purpose

This PR introduces improvements to the webhook handling logic in the AI Suite framework. The key changes focus on enhancing the error handling capabilities, ensuring more robust and informative responses in case of issues during webhook processing.

Critical Changes

  • Added Message class to the aisuite.framework module to represent the contents of API responses that do not conform to the OpenAI style response. This class includes fields for content, tool_calls, role, and refusal.
  • Implemented enhanced error handling in the AnthropicProvider class, specifically for handling tool calls. The changes include:
    • Converting OpenAI tool calls to the Anthropic format.
    • Handling assistant messages with tool calls, converting the response to the expected format.
    • Mapping Anthropic's stop_reason to the corresponding OpenAI finish_reason.
    • Populating the usage information in the normalized response.
  • Added support for tool calling in the AwsProvider class, including:
    • Transforming OpenAI tool call format to the AWS Bedrock format.
    • Handling tool results and converting them back to the OpenAI format.
    • Transforming assistant messages with tool calls to the AWS Bedrock format.
  • Implemented tool calling support in the GroqProvider and MistralProvider classes, ensuring seamless integration with the framework's tool management capabilities.

===== Original PR title and description ============

Original Title: Enhance webhook handling logic with improved error handling

Original Description:

Purpose

This PR updates the webhook processing system to improve reliability and maintainability.
It introduces better error handling mechanisms and cleaner code organization.

Critical Changes

  • Updated webhook handler in app/handlers/webhook.py with robust error handling and logging
    - Refactored response formatting for consistency across different webhook events
    - Added input validation for webhook payloads to prevent processing invalid requests

===== Original PR title and description ============

Original Title: Enhanced messaging in AI suite with refined webhook logic for multiple providers

Original Description:

Purpose

The purpose of this PR is to enhance and refactor webhook handling logic across our suite of AI services, improving both the flexibility and extensibility of messaging components. This includes modifying default behaviors and integrating new patterns for better interaction with varying provider interfaces and handling tool calls more effectively.

Critical Changes

  • aisuite/__init__.py:

    • Added import for the Message class to facilitate its use across the suite: from .framework.message import Message.
  • aisuite/framework/__init__.py:

    • Introduced Message into framework module imports, making it globally accessible across the framework context.
  • aisuite/framework/choice.py:

    • Altered the Choice class initialization logic by setting finish_reason to either 'stop' or 'tool_calls' (conditionally handled based on tool interaction), enhancing message lifecycle management. In Choice.__init__ function, the Message constructor now takes parameters like content and role.
  • aisuite/framework/message.py:

    • Revamped the Message class, transitioning it to a BaseModel from pydantic, which now includes optional tool call handling (tool_calls list of ChatCompletionMessageToolCall). This allows the message structure to dynamically support interactions where tools are triggered as part of the message lifecycle.
  • aisuite/providers/anthropic_provider.py:

    • Major changes in the AnthropicProvider class to adapt webhook logic from OpenAI to Anthropic format, particularly in the chat_completions_create function.
    • Added comprehensive condition handling for transforming messages containing tool results or calls, highlighted by the serialization and deserialization of tool and system parameters using json.
    • Adjusted the normalize_response method to follow the finish reason mapping between Anthropic and OpenAI models.
  • aisuite/providers/aws_provider.py:

    • Extensive modifications to accommodate AWS tool calling format, including format transformations and handling response normalization to reflect tool use configuration.
  • Additional files like aisuite/providers/groq_provider.py, aisuite/providers/mistral_provider.py, and aisuite/utils/tool_manager.py introduce new dependencies and logic for handling message transformations as per their respective provider specifications.

  • The new notebooks and Python scripts (examples/SimpleToolCalling.ipynb, examples/tool_calling.py) demonstrate the usage and functional testing of the new tool calling and messaging capabilities across different environments.

===== Original PR title and description ============

Original Title: Refactor and enhance webhook handling logic across multiple providers

Original Description:

Purpose

The PR enhances the webhook handling logic by focusing on error handling, message normalization, and supporting tool call transformations for multiple providers. It aims to ensure a consistent and error-resistant integration across OpenAI, AWS, and Anthropic providers by refactoring tool call message formats and improving response normalization processes.

Critical Changes

  • aisuite/framework/message.py: Introduced Pydantic BaseModel for the Message class with optional fields for content, tool_calls, role, and refusal. Included sub-models like Function, and ChatCompletionMessageToolCall to support structured tool related data especially for handling tool call responses.

  • aisuite/providers/anthropic_provider.py: Major enhancements to the method chat_completions_create include handling conversion between OpenAI tool calls and Anthropic tool calls, normalizing responses to align with OpenAI's format, and specifically detailed print statements for debugging purposes.

  • aisuite/providers/aws_provider.py: Enhanced error handling and message normalization in the method chat_completions_create. Included transformations between AWS-specific message formats and the required framework-compatible format. Tool call configurations are handled, and responses are formatted accordingly.

  • aisuite/providers/openai_provider.py: Added handling for tool calls in the method chat_completions_create. This change ensures that tool call functions can be executed and managed coherently.

  • aisuite/utils/tool_manager.py: ToolManager class introduced, orchestrating tool handling, executing registered tools based on the model's tool calls, and managing tool function registrations. Features robust JSON handling and error validation.

  • examples/SimpleToolCalling.ipynb: New notebook example to demonstrate the usage of the ToolManager for handling and executing tool calls within the chat model environments.

  • Additional server files (aisuite/providers/*.py for Groq and Mistral): Each specific provider file adapts tool handling and message transformation to comply with their proprietary or OpenAI-compatible formats, ensuring uniform functionality across different platforms.

===== Original PR title and description ============

Original Title: Enhance webhook handling logic with improved error handling

Original Description:

Purpose

This PR updates the webhook processing system to improve reliability and maintainability.
It introduces better error handling mechanisms and cleaner code organization.

Critical Changes

  • Updated webhook handler in app/handlers/webhook.py with robust error handling and logging
    - Refactored response formatting for consistency across different webhook events
    - Added input validation for webhook payloads to prevent processing invalid requests

===== Original PR title and description ============

Original Title: Introduce Enhanced Message Handling and Tool Integration

Original Description:

Purpose

This PR introduces enhanced webhook handling capabilities and integrates tool call functionality within the Message framework, improving the interaction of AI models with external tools. The change aims to bridge the gap between different model types and standardize message formation.

Critical Changes

  • aisuite/__init__.py: Imports and initializes the new Message class from framework.message to facilitate enhanced message capabilities.

  • aisuite/framework/__init__.py: Adds Message import to the framework initializing module to make it available throughout the framework.

  • aisuite/framework/choice.py: Redefines the Choice class to include optional finish reason and now initializes Message with parameters like tool_calls and role. It sets up message structure to incorporate tool interactions directly:

    self.finish_reason: Optional[Literal["stop", "tool_calls"]] = None
    self.message = Message(
        content=None, tool_calls=None, role="assistant", refusal=None
    )
  • aisuite/framework/message.py: Expands the Message class to a BaseModel with attributes for handling tool calls and setting message roles. This adaptation is essential for managing different types of message content, including the integration of tool results directly into the chat flow.

  • aisuite/providers/anthropic_provider.py: Overhauls message handling in AnthropicProvider for cases with tool interactions. Conversions between different tool calling conventions (OpenAI to Anthropic) are handled within chat_completions_create method, emphasizing message role conversions and JSON manipulations for tool results.

  • aisuite/providers/aws_provider.py: Updates to implement tool interaction handling in AWS provider environment, specifically adapting messages to/from Bedrock tool usage format.

    Note: Additional detailed transformation methods (transform_tool_call_to_openai, transform_tool_result_to_bedrock) aid in these conversions and ensure tool call returns are incorporated accurately into AI model responses.

  • aisuite/utils/tool_manager.py: Implements ToolManager for centralized tool function registration and execution, supporting transformation of tools to API specific configurations. It offers the execution functionality, which turns tool requests into actionable Python function calls that can return results suitable for AI model consumption.

  • Documentation and Notebook examples (examples/SimpleToolCalling.ipynb and examples/tool_calling.py) provide clear use cases and setup guidelines, demonstrating the practical application of tool management with AI interactions.

  • Tests (tests/utils/test_tool_manager.py) ensure robustness of the ToolManager functionalities, covering scenarios from tool registration to execution with both valid and erroneous inputs.

===== Original PR title and description ============

Original Title: Enhanced webhook handling with expanded message framework and tool call integration

Original Description:

Purpose

This PR introduces refined error handling, supports additional message types, and improves integration with external tool calling APIs. It focuses on enhancing the messaging capabilities in response handling and transforming tool call formats between OpenAI and Anthropic platforms.

Critical Changes

{

  • aisuite/__init__.py and aisuite/framework/__init__.py: Added import statements for the Message class to facilitate tool call responses and structured message processing.
  • aisuite/framework/choice.py: Extended the Choice class to include a finish reason attribute and updated the Message instantiation to include new fields such as tool_calls, facilitating detailed responses.
  • aisuite/framework/message.py: Introduced a new Pydantic model structure for the Message, with fields like tool_calls and role to support structured and role-based messaging, enhancing interaction with external APIs.
  • aisuite/providers/anthropic_provider.py and aisuite/providers/aws_provider.py: Major changes include methods for transforming API-specific tool call messages between formats of OpenAI and proprietary formats (AWS, Anthropic). These modifications include handling tool results and structured API responses, translating them into the expected format of the AI suite.
  • aisuite/utils/tool_manager.py: This file introduces a ToolManager class managing tool function registration, execution, and tool serialization as required by different AI models. The manager also handles transformation between internal and external tooling formats.
    }

===== Original PR title and description ============

Original Title: Enhance webhook handling logic with improved error handling

Original Description:

Purpose

This PR updates the webhook processing system to improve reliability and maintainability.
It introduces better error handling mechanisms and cleaner code organization.

Critical Changes

  • Updated webhook handler in app/handlers/webhook.py with robust error handling and logging
    - Refactored response formatting for consistency across different webhook events
    - Added input validation for webhook payloads to prevent processing invalid requests

===== Original PR title and description ============

Original Title: Enhance webhook handling logic with improved error handling

Original Description:

Purpose

This PR updates the webhook processing system to improve reliability and maintainability.
It introduces better error handling mechanisms and cleaner code organization.

Critical Changes

  • Updated webhook handler in app/handlers/webhook.py with robust error handling and logging
    - Refactored response formatting for consistency across different webhook events
    - Added input validation for webhook payloads to prevent processing invalid requests

===== Original PR title and description ============

Original Title: Enhanced webhook error handling with message models and framework changes

Original Description:

Purpose

The PR aims to enhance our webhook error handling capabilities by introducing new models for messages, integrating tool calls processing, and refining provider interactions. The changes will allow better handling of different completion scenarios and extend support for JSON and tool result transformations.

Critical Changes

  • aisuite/__init__.py and aisuite/framework/__init__.py: Added import statements for the Message model which centralizes message handling.
  • aisuite/framework/message.py: Redefined Message class using pydantic.BaseModel to enable data validation and error handling. Added new classes Function and ChatCompletionMessageToolCall to structure tool function calls.
  • aisuite/framework/choice.py: Modified Choice class to incorporate optional 'finish_reason' to represent different reasons for dialogue termination such as 'stop' or 'tool_calls'. Also updated instantiation of Message class.
  • aisuite/providers/anthropic_provider.py: Extensive modifications include adapting message and tool processing to align with Anthropic's API, handling transformation between OpenAI and Anthropic tool calling conventions, and comprehensive logic to map 'stop_reason' to 'finish_reason'.
  • Other Providers (aws_provider.py, groq_provider.py, mistral_provider.py): Adapted providers to handle messages and tool call transformations effectively, reinforcing the handling structure across different platforms and services.
  • aisuite/utils/tool_manager.py: Added a new ToolManager class to manage tools, function registrations, and execution which supports JSON structure conversions vital for tool calling processes across different providers.

===== Original PR title and description ============

Original Title: Enhance webhook handling logic with improved error handling

Original Description:

Purpose

This PR updates the webhook processing system to improve reliability and maintainability.
It introduces better error handling mechanisms and cleaner code organization.

Critical Changes

  • Updated webhook handler in app/handlers/webhook.py with robust error handling and logging
    - Refactored response formatting for consistency across different webhook events
    - Added input validation for webhook payloads to prevent processing invalid requests

===== Original PR title and description ============

Original Title: Enhance webhook handling logic with improved error handling

Original Description:

Purpose

This PR updates the webhook processing system to improve reliability and maintainability.
It introduces better error handling mechanisms and cleaner code organization.

Critical Changes

  • Updated webhook handler in app/handlers/webhook.py with robust error handling and logging
    - Refactored response formatting for consistency across different webhook events
    - Added input validation for webhook payloads to prevent processing invalid requests

===== Original PR title and description ============

Original Title: Integration of Enhanced AI Provider with Message Framework and Tool Call Support

Original Description:

Purpose

The PR augments the AI suite by integrating advanced message handling and tool calling features into the AI provider interfaces. This enables efficient interaction and response generation capabilities, catering to different roles and message types, including sophisticated tool call handling and mappings between different tool invocation standards.

Critical Changes

{

  • In aisuite/__init__.py, added imports for the Message class, which centralizes message structures:

    from .framework.message import Message
  • In aisuite/framework/message.py, revamped the Message class using Pydantic for enhanced data validation and defined new classes for handling function arguments and tool call structures, enabling tool-call functionalities within messages:

    from pydantic import BaseModel
    class Function(BaseModel):
        arguments: str
        name: str
    class ChatCompletionMessageToolCall(BaseModel):
        id: str
        function: Function
        type: Literal["function"]
    class Message(BaseModel):
        content: Optional[str]
        tool_calls: Optional[list[ChatCompletionMessageToolCall]]
        role: Optional[Literal["user", "assistant", "system"]]
        refusal: Optional[str]
  • In aisuite/providers/anthropic_provider.py, introduced advanced message conversion logic to support different message types and tool calls in the Anthropic API, incorporating systematic conversion and normalization between OpenAI and Anthropic tool call standards:

    for msg in messages:
        if msg["role"] == "tool":
            # Handling tool result message
        elif msg["role"] == "assistant" and "tool_calls" in msg:
            # Handling assistant messages with tool calls
  • Major updates in other provider files (aws_provider.py, groq_provider.py, mistral_provider.py, and openai_provider.py) to support the new Message structures and functions, ensuring system-wide consistency in handling tool responses and message translations.

  • New utility added: tool_manager.py for registering and managing tool functions with Pydantic validation, enhancing tool usage in AI models:

    class ToolManager:
        def add_tool(self, func: Callable, param_model: Optional[Type[BaseModel]] = None):
            ...
        def execute_tool(self, tool_calls) -> tuple[list, list]:
            ...
  • Added an example notebook SimpleToolCalling.ipynb to illustrate the practical application of new functionalities in a simulated environment.
    }

===== Original PR title and description ============

Original Title: Enhanced AI Provider Integration with Message Model and Tool Call Handling

Original Description:

Purpose

Modifying 'Message' model and developing tool call conversion for improved integration with the AI provider API. This update prepares framework for AI provider capabilities such as handling complex AI tool usages, like translating OpenAI tool calls to Anthropic and formatting responses aligned with OpenAI standards.

Critical Changes

{

  • aisuite/framework/message.py: Introduced BaseModel schema for Message and subclasses for tool calls, allowing stricter type and role specifications. Critical attributes such as content, tool_calls, and role are now optional but clearly defined using Pydantic models.

    class Message(BaseModel):
        content: Optional[str]
        tool_calls: Optional[list[ChatCompletionMessageToolCall]]
        role: Optional[Literal["user", "assistant", "system"]]
  • aisuite/providers/anthropic_provider.py: Add conversion logic from OpenAI tool call format to Anthropic format in the chat_completions_create method, permitting seamless interchange of tool calls between different AI providers.

    if "tools" in kwargs:
        kwargs["tools"] = convert_openai_tools_to_anthropic(kwargs["tools"])
    ...
    for msg in messages:
        if msg.role == "tool":
            converted_msg = {
                "role": "user",
                "type": "tool_result",
                ...
            }
            converted_messages.append(converted_msg)
  • aisuite/init.py and aisuite/framework/init.py: Imports updated to include the new Message class, ensuring all parts of the suite acknowledge the updated schema.

    from .framework.message import Message
  • Tool Handling Docs: Links to documentation for tool calling now included in code comments, ensuring developers can reference how tool integration is structured.

    # Links:
    # Tool calling docs - https://docs.anthropic.com/en/docs/build-with-claude/tool-use

}

===== Original PR title and description ============

Original Title: Refine Message model and encapsulate tool calls for AI provider integration

Original Description:

Purpose

This PR introduces a new structured messaging system and APIs to support tool calls across multiple AI providers. It aims to unify and streamline the interaction with different AI tools, enhancing the content creation capabilities of an AI suite.

Critical Changes

  • aisuite/framework/message.py: Replaced the existing Message class with a BaseModel derived structure from pydantic, supporting optional fields for tool calls, content, role, and refusal. This enhances type safety and data handling.

    class Function(BaseModel):
        arguments: str
        name: str
    
    class ChatCompletionMessageToolCall(BaseModel):
        id: str
        function: Function
        type: Literal["function"]
    
    class Message(BaseModel):
        content: Optional[str]
        tool_calls: Optional[list[ChatCompletionMessageToolCall]]
        role: Optional[Literal["user", "assistant", "system"]]
        refusal: Optional[str]
  • aisuite/providers/anthropic_provider.py and aisuite/providers/aws_provider.py: Implemented methods for transforming tool calls and responses between OpenAI's format and their respective provider formats (Anthropic, AWS). Detailed handling includes converting tools from generic tool specifications to provider-specific configurations and normalizing responses back into the unified Message format expected by the system.

    class AnthropicProvider(Provider):
        ...
        def chat_completions_create(self, model, messages, **kwargs):
            ...
            # Convert tool results from OpenAI format to Anthropic format
            converted_messages.append(...) # Details of transformation
            ...
            return self.normalize_response(response)
    
    class AwsProvider(Provider):
        ...
        def chat_completions_create(self, model, messages, **kwargs):
            ...
            converted_messages = []
            for message in messages:
                if message_dict["role"] == "tool":
                    bedrock_message = self.transform_tool_result_to_bedrock(message_dict)
                    if bedrock_message:
                        formatted_messages.append(bedrock_message)
            ...
            # Additional logic to handle transformations
  • Added Message import in aisuite/__init__.py and aisuite/framework/__init__.py to enhance framework integration level and ensure all components recognize the Message type throughout the application.

    # In aisuite/__init__.py
    from .framework.message import Message
    
    # In aisuite/framework/__init__.py
    from .message import Message

===== Original PR title and description ============

Original Title: Enhanced Message Structure and Integration with AI Provider APIs for Tool Calls

Original Description:

Purpose

This PR introduces significant enhancements to both the internal message structure and the API integration for handling communication with various AI providers. The main focus is to facilitate the correct mapping and usage of tool calls across different AI services, ensuring uniformity and maximizing compatibility.

Critical Changes

  • aisuite/__init__.py and aisuite/framework/__init__.py:
    Imported Message classes to bolster message handling capabilities.

  • aisuite/framework/choice.py:
    Added optional finish reasons and enriched Message initialization to include new fields like tool_calls which are now distinctly initialized with appropriate defaults to enhance clarity and control over message flow.

  • aisuite/framework/message.py:
    Revamped Message class with Pydantic support for better data validation and structure. Introduced ChatCompletionMessageToolCall and Function as BaseModel subclasses to standardize tool call structures for AI providers.

  • aisuite/providers/anthropic_provider.py:
    Heavily modified response and request handling for the Anthropic provider to accommodate new structured tool calls. Added normalization from Anthropic format to OpenAI’s expected format, increasing interoperability.

  • aisuite/providers/aws_provider.py:
    Adjusted AWS provider to handle message transformations between Bedrock's expected format and the general tool call structure. Also, made changes to include tool configurations if provided.

  • aisuite/providers/groq_provider.py, aisuite/providers/mistral_provider.py, aisuite/providers/openai_provider.py:
    Minor adjustments to each provider, mostly focusing on message transformation consistency.

  • aisuite/utils/tool_manager.py:
    Introduced a ToolManager which handles the registration and execution of tools based on the updated message structures, allowing for dynamic hooking of function calls specific to AI tasks.

Note:

  • Further testing and possibly adjustments may be necessary to fully ensure functionality across all AI providers post-refactor.
  • Verify the comprehensive handling of edge cases in tool transformations, especially with AWS and Groq provider updates.

===== Original PR title and description ============

Original Title: Refactor message structure and tool call API integration for various AI providers

Original Description:

Purpose

The PR aims to enhance the message class compatibility between different AI service providers and standardizes the handling of AI tool calls across providers, ensuring more robust and flexible interactions.

Critical Changes

  • aisuite/framework/message.py:

    • Introduced the Message class utilizing Pydantic for type checking and structure validation. Added new attributes tool_calls, role, and refusal to store tool call information in an API-agnostic format.
    • Extended BaseModel to use type hints ensuring that data conforms to expected schema with relevant types across different providers.
  • aisuite/framework/choice.py:

    • Modified Choice class to include optional attributes finish_reason, allowing the AI to specify why a session or request was completed, leveraging Python's Literal and Optional types for precise state descriptions.
  • aisuite/providers/anthropic_provider.py:

    • Reworked message handling to transform tool call data from the OpenAI format to the Anthropic API format. Added comprehensive transformation of ChatCompletionResponse, including converting response content and handling tool call conversion and normalization processes.
  • aisuite/providers/aws_provider.py:

    • Implemented message format transformations specific to AWS provider requirements, managing both incoming requests to and responses from AWS's AI services. Detailed transformations and print debugging statements added to trace the transformation steps.
  • aisuite/providers/groq_provider.py, aisuite/providers/mistral_provider.py, aisuite/providers/openai_provider.py:

    • Each provider file includes adaptations specific to the respective service, mostly focusing on message transformations adhering to each provider's expected formats.
  • aisuite/utils/tool_manager.py:

    • Developed a ToolManager class to manage registration and execution of tools, handling parameter parsing, execution, and response formatting along with error handling.
  • Additional files like Jupyter notebooks under examples/ and tests in tests/utils/:

    • Serve the purpose of demonstrating and testing the new message structures and tool handling capabilities in practical scenarios.

===== Original PR title and description ============

Original Title: Enhance message structure and adapt tool call conversions across AI providers

Original Description:

Purpose

The changes introduced update the message and tool calling frameworks to support diverse AI providers more effectively. The enhancements include adapting the message structure for different provider APIs, handling tool calls uniquely per provider, and integrating usage details to standardize responses. This ensures compatibility and flexibility within the AI suite's tooling system.

Critical Changes

{

  • aisuite/__init__.py and aisuite/framework/__init__.py: Import the updated Message class, ensuring that all providers utilizing this class can leverage the new structured message format.

  • aisuite/framework/message.py: Transition the Message class to utilize pydantic.BaseModel, making it robust for data validation. The class now optionally includes tool calls, supporting various roles and potential refusal reasons, enhancing the message structure significantly.

  • aisuite/providers/anthropic_provider.py: Adapt message and tool call handling specifically for the Anthropics API. This includes changing kwargs handling, message transformation to match the provider's requirements, and appropriately converting tool call responses, addressing the unique 'stop_reason' and formatting responses to a standardized form.

  • aisuite/providers/aws_provider.py: Implement response normalization and modification of incoming messages (handling tool calls and results) for the AWS Bedrock API. Detailed changes ensure message dicts are correctly formatted for this provider.

  • aisuite/providers/groq_provider.py, aisuite/providers/mistral_provider.py, and aisuite/providers/openai_provider.py: Adjustments reflect the new message handling formats and detailed transformation functions, supporting the structured handling of tool calls and messages across these providers.

  • aisuite/utils/tool_manager.py: Adds a comprehensive suite for tool management, allowing adding and managing tools with simplified or detailed specifications. The implementation focuses on function execution, handling both response and message formation effectively.
    }

===== Original PR title and description ============

Original Title: Integrate advanced message structuring and tool call conversion for various providers

Original Description:

Purpose

The PR is aimed at enhancing message structuring within the aisuite, particularly for handling tool calls across multiple providers like AWS, Anthropic, and more. This ensures that messages are appropriately formatted and converted between different API standards, helping in seamless integration with these external services.

Critical Changes

  • aisuite/framework/message.py: Introduced a new Message class using Pydantic for validating message schemas, including optional fields for content, role, and tool_calls to handle different message types more robustly.
  • aisuite/providers/anthropic_provider.py: Added complex logic to transform tool call data between OpenAI format and Anthropic's expected format, involving detailed conversions both ways and handling tool results separately.
  • aisuite/providers/aws_provider.py: Enhanced the AWS provider to support tool calls by converting message formats suitable for Bedrock's API and processing tool-related responses, aligning them with OpenAI standards.
  • aisuite/utils/tool_manager.py: Tool manager utility class implemented to manage and execute tools based on calls from messages, including a method to dynamically create Pydantic models from functions and handle execution.
    ...

===== Original PR title and description ============

Original Title: Enhance message structuring and add tool call handling across providers

Original Description:

Purpose

The changes are intended to introduce unified message structuring with pydantic models and extend feature support for handling tool calls, ensuring compatibility and streamlining responses for multiple AI model providers. This facilitates a more structured and extensible codebase catering to various AI-powered interactions.

Critical Changes

{

  • In aisuite/framework/__init__.py and aisuite/__init__.py, the Message class is now imported, setting the stage for enhanced message handling through the project using this unified structure.

  • In aisuite/framework/message.py, a significant upgrade has occurred where Message evolved from a basic class to pydantic.BaseModel incorporating optional fields for content, roles, and detailed structures for tool calls such as ChatCompletionMessageToolCall which inherits the runtime checking and documentation benefits of pydantic.

  • aisuite/framework/choice.py now outlines optional finish reasons in tool calls, refining decision capture based on enhanced messaging structures reflecting roles and actions within the workflow.

  • In aisuite/providers/anthropic_provider.py, extensive changes are made to support translation between OpenAI and Anthropic specific tool calling requirements and handling API responses to align with the desired structured message format, utilizing the new Message structure.

  • Enhancements in aisuite/providers/aws_provider.py include better message transformations for AWS-specific formatting and tool usage handling, thereby integrating the extended message and tool handling functionality across various AI model providers.

  • Each provider (Groq, Mistral, AWS, and Anthropic) under aisuite/providers/ now includes transformations and normalization flows to fit the new Message format, demonstrating a commitment to uniformity and extendibility in handling AI messaging responses.

  • The addition of a new file aisuite/utils/tool_manager.py introduces a ToolManager class which manages tool functionalities as callable entities. This development suggests the expansion of tool handling capabilities that are likely to influence future features and integrations in the AI suite.

}

===== Original PR title and description ============

Original Title: Enhance message handling for various providers with new data structures

Original Description:

Purpose

This PR introduces improved support and handling of messages and tool calls across different language models and service providers, increasing interoperability and flexibility in defining and transmitting messages.

Critical Changes

  • aisuite/__init__.py and aisuite/framework/__init__.py:

    • Imported the new Message class to initialize proper message handling.
  • aisuite/framework/message.py:

    • Redefined the Message class as a Pydantic model, enhancing data validation and error handling.
    • Introduced nested models like Function and ChatCompletionMessageToolCall to handle complex data structures for tool calls.
  • aisuite/framework/choice.py:

    • Set finish_reason in Choice class to potentially handle multiple end conditions (like "stop" or "tool_calls") and restructured the Message initialization to align with new attributes.
  • aisuite/providers/anthropic_provider.py and aisuite/providers/aws_provider.py:

    • Major overhaul to convert tool calls and responses between OpenAI and respective format of each provider, AWS and Anthropic.
    • Added normalization functions to transform proprietary API responses into standard formats.
  • aisuite/providers/groq_provider.py, aisuite/providers/mistral_provider.py, and aisuite/providers/openai_provider.py:

    • Adjusted message handling to integrate the new Message structure, ensuring compatibility and consistent data handling across different model providers.
  • aisuite/utils/tool_manager.py:

    • Added a new ToolManager class that manages tool registry and execution, supports dynamic parameter models, and integrates well with JSON-based interoperability.

===== Original PR title and description ============

Original Title: Introduce message handling enhancements using OpenAI and AWS formats

Original Description:

Purpose

This PR introduces systematic enhancements to the handling of messages and tool calling across multiple providers like Anthropics and AWS, aligning with their specific requirements and APIs.

Critical Changes

  • aisuite/framework/message.py: Refactored the Message class to use pydantic.BaseModel for data validation, incorporating support for optional fields and structured tool calls. Major changes include defining a new Function and ChatCompletionMessageToolCall sub-models to format tool calls accurately.
  • aisuite/providers/anthropic_provider.py: Major enhancements adjust tool handling to conform with Anthropics' API, involving modifying tool calls' transforming and normalizing processes. Added comprehensive mapping for function calls and responses between OpenAI and Anthropics format. Additional debug logging is implemented to trace tool call transformations.
  • aisuite/providers/aws_provider.py: Extended support for AWS provider to convert OpenAI formatted tool calls to AWS's expected JSON payload. Modifications ensure correct mapping and handling of tool results and assistant messages for both single and batch requests.

Additional updates across various providers (groq_provider.py, mistral_provider.py, aws_provider.py) improve overall integration and consistency in message format transformation and tool calling processes.

===== Original PR title and description ============

Original Title: Enhanced tool calling and message handling for multiple providers

Original Description:

Purpose

This PR introduces improved tool calling capabilities, richer message object structures, and compatibility updates across various LLM providers to better handle diverse message and tool call formats.

Critical Changes

  • aisuite/__init__.py and aisuite/framework/__init__.py:

    • New imports for Message class to enable enhanced message handling across the suite.
  • aisuite/framework/message.py:

    • Redefinition of Message class using BaseModel for stricter validation and introduces ChatCompletionMessageToolCall class to handle structured tool responses.
  • aisuite/framework/choice.py:

    • Updated Choice class to include optional typing and improved default setup for message instantiation supporting finish_reason attribute for completion state tracking.
  • aisuite/providers/anthropic_provider.py and aisuite/providers/aws_provider.py:

    • Major refactoring to adapt tool call formats from/to OpenAI and native formats of Anthropic and AWS, involving complex JSON transformations and API interactions.
  • aisuite/utils/tool_manager.py:

    • ToolManager class capable of registering tools with dynamic parameter model inference, executing tool calls, and returning standard formatted responses.
  • examples/SimpleToolCalling.ipynb:

    • Jupyter notebook example demonstrating how to utilize the ToolManager with a mock API to handle tool interactions within chat environments.

New Dependencies:

  • Pydantic added for data validation in structured message and tool handling.

===== Original PR title and description ============

Original Title: Rcp/tool calling

Original Description:
None

PR Review Summary Celebratory GIF

Overall Review:

The provided PR adds significant functionality related to tool calling across various AI providers, integrating complex message transformation and normalization logic. It spans across initializing and transforming messages according to the specifications required by APIs like AWS, Anthropic, etc. The modifications involve converting and normalizing tool responses and ensuring these are compatible with the AI models' expected formats. The changes are substantial and have a broad impact on the application, potentially affecting how tool results are processed, displayed, or logged.


Logical Error Analysis

1. [Blocker] In `AnthropicProvider`, `normalize_response` tries to handle different `stop_reason`s including `tool_use`. However, `tool_call_id` and method to retrieve `content` might not reliably parse the expected format every time. This can lead to unhandled exceptions or missing data.

🔒 Security Analysis

2. [Blocker] The methods `transform_tool_call_to_openai` and JSON handling in APIs:
Whenever JSON parsing or serialization from external inputs is involved, ensure there are strict schema validations or error handling to mitigate potential injection attacks or corruption of data formats. The current code necessitates confirmation that content passed to JSON parsers respects expected formats and does not introduce injection risks.

Recommendations

Recommendation #1

To address the potential inconsistencies in parsing tool_call_id and content, it is recommended to add rigorous checks and transformations to ensure the data conforms consistently to the expected format. Consider applying a schema validation or using a more robust method for parsing, especially when handling dynamic JSON content.
For example, you could modify the relevant part in AnthropicProvider as follows:

def normalize_response(self, response):
    normalized_response = ChatCompletionResponse()
    finish_reason_mapping = {
        "end_turn": "stop",
        "max_tokens": "length",
        "tool_use": "tool_calls",
    }
    normalized_response.choices[0].finish_reason = finish_reason_mapping.get(response.stop_reason, "stop")
   
    # Add usage information
    normalized_response.usage = {
        "prompt_tokens": response.usage.input_tokens,
        "completion_tokens": response.usage.output_tokens,
        "total_tokens": response.usage.input_tokens + response.usage.output_tokens,
    }
   
    # Check if the response contains tool usage and validate format
    if response.stop_reason == "tool_use":
        tool_call = next((content for content in response.content if 'type' in content and content.type == "tool_use"), None)
        if tool_call and 'id' in tool_call:
            tool_call_id = tool_call.id
            function = Function(name=tool_call.name, arguments=json.dumps(tool_call.input))
            tool_call_obj = ChatCompletionMessageToolCall(id=tool_call_id, function=function, type="function")
            text_content = next((content.text for content in response.content if 'type' in content and content.type == "text"), "")
            message = Message(content=text_content or None, tool_calls=[tool_call_obj] if tool_call else None, role="assistant", refusal=None)
            normalized_response.choices[0].message = message
        else:
            raise ValueError("Expected 'tool_use' content is missing or malformed in API response.")
    
    return normalized_response

This snippet includes error handling for missing or malformed 'tool_use' data, ensuring the internal data paths are consistent before parsing.

Recommendation #2

Implement rigorous input validation and error handling around JSON operations and external data handling. Make use of secure practices such as using parameterized queries or templates when constructing requests or handling data. For instance:

def transform_tool_call_to_openai(self, response):
    if response.get("stopReason") != "tool_use":
        return None

    try:
        tool_calls = []
        for content in response["output"]["message"]["content"]:
            if "toolUse" in content:
                tool = content["toolUse"]
                tool_calls.append({
                    "type": "function",
                    "id": tool["toolUseId"],
                    "function": {
                        "name": tool["name"],
                        "arguments": json.dumps(tool["input"], cls=SecureJSONEncoder),
                    },
                })
    except json.JSONDecodeError as e:
        raise ValueError(f"JSON parsing error: {str(e)}")

    return {
        "role": "assistant",
        "content": None,
        "tool_calls": tool_calls,
        "refusal": None,
    }

In this code snippet, SecureJSONEncoder could be a custom JSONEncoder that sanitizes inputs to prevent JSON injection attacks. This ensures the integrity and security of data transformations and external interactions.

[Configure settings at: Archie AI - Automated PR Review]

@archie-ai-code-explain-pr-review archie-ai-code-explain-pr-review bot added the enhancement New feature or request label Nov 27, 2024

PR Review Summary Celebratory GIF

Overall Review:

This PR introduces a ToolManager alongside extensive modifications to frameworks and providers to support tool calling capabilities. It affects multiple components of the aisuite, including anthropic, aws, and others, adapting their messaging and response normalization to incorporate tools dynamically. The code is structured to handle conversions between multiple formats and system settings, which is necessary for the expected diverse provider interactions. However, it is critical to ensure security, reliability, and coverage considering these extensive changes.


Logical Error Analysis

1. [Blocker] Conversion Functionality Incompletion:
The conversion method `convert_openai_tools_to_anthropic` only handles "function" type tools but does not address other types that could potentially be part of the toolset.

🔒 Security Analysis

2. [Blocker] Potential Data Exposure:
Classes like `Function` and `Message` in `message.py` and logged responses in service provider classes expose sensitive data that may include IPs, user data, or metadata that could assist attackers.

🧪 Test Coverage Analysis

3. [Consider] Coverage on Exception Handling:
The error paths, especially around malformed incoming data for tools (both in structure and type), are not tested. Tests should be added to simulate and assert behavior when incorrect or malformed data is received.

🌟 Code Quality And Design

4. [Consider] Complexity:
Handling Tool Calls and Mapping - The logic in `AnthropicProvider`, `AwsProvider`, and other provider modules handles transformation between different tool calling formats.

5. [Nit] Design Review:
Module Decoupling - The implementation spreads across multiple modules, enforcing a decoupling that favors single responsibility and easier unit testing.

Recommendations

Recommendation #1

Modify the convert_openai_tools_to_anthropic method to handle all expected tool types, or document and handle unsupported tool types by either logging an error or a warning. For example:

def convert_openai_tools_to_anthropic(openai_tools):
    anthropic_tools = []
    for tool in openai_tools:
        if tool["type"] == "function":
            function = tool["function"]
            anthropic_tool = {
                "name": function["name"],
                "description": function.get("description", ""),
                "input_schema": {
                    "type": "object",
                    "properties": function["parameters"]["properties"],
                    "required": function["parameters"].get("required", []),
                },
            }
            anthropic_tools.append(anthropic_tool)
        else:
            # Log unhandled tool types, or possibly raise an error if necessary
            logging.warning(f"Tool type {tool['type']} is not supported yet")
    return anthropic_tools

Be sure to import the logging module and configure it appropriately in your module setup.

Recommendation #2

Ensure that all logging and data classes do not include or expose sensitive or identifiable information. Use data masking or transformation techniques to obscure real values in logs and ensure that no sensitive data is logged in plain text. Modify the log statements and any debug outputs to mask any potentially sensitive information:

class Function(BaseModel):
    name: str
    arguments: str

    def log_function(self):
        masked_arguments = mask_sensitive_data(self.arguments)  # Assumes existence of this function
        logging.debug(f"Function called: {self.name}, with arguments: {masked_arguments}")

# Replace direct print statements with the above method call:
function_instance.log_function()

Remember to define or implement the mask_sensitive_data function that can handle the specific structure and fields of your data objects.

Recommendation #3

Implement additional test cases that focus on error handling for malformed or incorrect data scenarios to ensure robustness. Here's an example test case using pytest:

import pytest
from module import data_handling_function

def test_data_handling_with_malformed_data():
    malformed_data = "incorrect format data"
    with pytest.raises(ValidationError):
        result = data_handling_function(malformed_data)
        assert result is None, "Function should not handle malformed data without error"

This test checks that handling of malformed data raises a ValidationError, ensuring that the system reacts appropriately to unexpected inputs.

Recommendation andrewyng#4

Abstract the transformations more robustly, potentially using a factory pattern to simplify and manage the complexity of various tool formats. For example:

class ToolTransformerFactory:
    @staticmethod
    def get_transformer(provider_type):
        if provider_type == "Anthropic":
            return AnthropicToolTransformer()
        elif provider_type == "AWS":
            return AWSToolTransformer()
        else:
            raise ValueError("Unsupported provider type")

class AnthropicToolTransformer:
    def transform(self, tool_data):
        # perform transformation logic specific to Anthropic
        pass

class AWSToolTransformer:
    def transform(self, tool_data):
        # perform transformation logic specific to AWS
        pass
# Usage:
transformer = ToolTransformerFactory.get_transformer("Anthropic")
transformed_data = transformer.transform(tool_data)

This pattern will facilitate adding support for new providers or changing existing implementations with minimal impact on other parts of the system.

Recommendation andrewyng#5

Ensure logical grouping and minimal interdependencies of modules. Reviewing module responsibilities and interactions might reveal opportunities to reduce complexity or improve cohesiveness, making it easier to manage and evolve separate parts without unintended side effects. You might consider reevaluating some of the finer-grained separations if they lead to excessive hopping between files or unclear relationships between components.

[Configure settings at: Archie AI - Automated PR Review]

PR Review Summary Celebratory GIF

Overall Review:

This PR introduces extensive modifications across the aisuite, aiming to enhance its ability to interact with AI services by handling tool calls. It touches various aspects of message transformation and normalization, ensuring compatibility across system interfaces. The changes are complex with potentially significant implications for the application’s capability to process external AI tool results, managing these translations across multiple providers like AWS, Anthropic, and others.


Logical Error Analysis

1. [Blocker] In `AnthropicProvider`, `normalize_response` tries to handle different `stop_reason`s including `tool_use`. However, `tool_call_id` and method to retrieve `content` might not reliably parse the expected format every time. This can lead to unhandled exceptions or missing data.

🔒 Security Analysis

2. [Blocker] The methods `transform_tool_call_to_openai` and JSON handling in APIs involve sensitive data parsing which requires strict schema validations or error handling to mitigate potential injection attacks or corruption of data formats.

Recommendations

Recommendation #1

To address the potential inconsistencies in parsing tool_call_id and content, it is recommended to add rigorous checks and transformations to ensure the data conforms consistently to the expected format. Consider applying a schema validation or using a more robust method for parsing, especially when handling dynamic JSON content. Modify the relevant part in AnthropicProvider as follows:

def normalize_response(self, response):
    normalized_response = ChatCompletionResponse()
    finish_reason_mapping = {
        "end_turn": "stop",
        "max_tokens": "length",
        "tool_use": "tool_calls",
    }
    normalized_response.choices[0].finish_reason = finish_reason_mapping.get(response.stop_reason, "stop")
   
    # Add usage information
    normalized_response.usage = {
        "prompt_tokens": response.usage.input_tokens,
        "completion_tokens": response.usage.output_tokens,
        "total_tokens": response.usage.input_tokens + response.usage.output_tokens,
    }
   
    # Check if the response contains tool usage and validate format
    if response.stop_reason == "tool_use":
        tool_call = next((content for content in response.content if 'type' in content and content.type == "tool_use"), None)
        if tool_call and 'id' in tool_call:
            tool_call_id = tool_call.id
            function = Function(name=tool_call.name, arguments=json.dumps(tool_call.input))
            tool_call_obj = ChatCompletionMessageToolCall(id=tool_call_id, function=function, type="function")
            text_content = next((content.text for content in response.content if 'type' in content and content.type == "text"), "")
            message = Message(content=text_content or None, tool_calls=[tool_call_obj] if tool_call else None, role="assistant", refusal=None)
            normalized_response.choices[0].message = message
        else:
            raise ValueError("Expected 'tool_use' content is missing or malformed in API response.")
    
    return normalized_response

This snippet includes error handling for missing or malformed 'tool_use' data, ensuring the internal data paths are consistent before parsing.

Recommendation #2

Implement rigorous input validation and error handling around JSON operations and external data handling. Make use of secure practices such as using parameterized queries or templates when constructing requests or handling data. For instance:

def transform_tool_call_to_openai(self, response):
    if response.get("stopReason") != "tool_use":
        return None

    try:
        tool_calls = []
        for content in response["output"]["message"]["content"]:
            if "toolUse" in content:
                tool = content["toolUse"]
                tool_calls.append({
                    "type": "function",
                    "id": tool["toolUseId"],
                    "function": {
                        "name": tool["name"],
                        "arguments": json.dumps(tool["input"], cls=SecureJSONEncoder),
                    },
                })
    except json.JSONDecodeError as e:
        raise ValueError(f"JSON parsing error: {str(e)}")

    return {
        "role": "assistant",
        "content": None,
        "tool_calls": tool_calls,
        "refusal": None,
    }

In this code snippet, SecureJSONEncoder could be a custom JSONEncoder that sanitizes inputs to prevent JSON injection attacks. This ensures the integrity and security of data transformations and external interactions.

[Configure settings at: Archie AI - Automated PR Review]

Copy link

dev-archie-ai-code-explain-pr bot commented Nov 27, 2024

PR Code Suggestions Summary ✨

CategorySuggestion
Maintainability
Refactor tool handling into a dedicated method to clean up the main method and improve readability.

File: aisuite/providers/aws_provider.py L101-L106

Suggestion: For clarity and maintainability, it is better to separate large blocks of conditional logic into small, well-named private methods. This can be done in the AwsProvider class where multiple if-else blocks handle different message roles.

--- Original
+++ Improved
@@ -1,19 +1 @@
-+        # Handle tools if present in kwargs
-+        tool_config = None
-+        if "tools" in kwargs:
-+            tool_config = {
-+                "tools": [
-+                    {
-+                        "toolSpec": {
-+                            "name": tool["function"]["name"],
-+                            "description": tool["function"].get("description", " "),
-+                            "inputSchema": {"json": tool["function"]["parameters"]},
-+                        }
-+                    }
-+                    for tool in kwargs["tools"]
-+                ]
-+            }
-+            print(f"Tool config: {tool_config}")
-+            print(f"Received tools specification: {kwargs['tools']}")
-+            # Remove tools from kwargs since we're handling it separately
-+            del kwargs["tools"]++    self._configure_tool_handling(kwargs)
Possible issue
Add error handling for JSON parsing to prevent runtime errors due to malformed JSON input.

File: aisuite/providers/aws_provider.py LNone-LNone

Suggestion: For error-prone string manipulations, especially when involving JSON parsing such as in the AWS provider, it's safer to use a try-except block to catch and handle parsing errors.

--- Original
+++ Improved
@@ -1 +1,5 @@
-+                            "input": json.loads(tool_call["function"]["arguments"]),++                            try:
++                                "input": json.loads(tool_call["function"]["arguments"]),
++                            except json.JSONDecodeError as e:
++                                print(f"Failed to parse JSON from the arguments: {e}")
++                                continue
Maintainability
Introduce a validation method for message roles to handle unexpected values gracefully.

File: aisuite/providers/aws_provider.py LNone-LNone

Suggestion: To avoid silent failure and handle unexpected keys in the message dictionary robustly, implement a method to validate and default unknown roles, ensuring proper application flow.

--- Original
+++ Improved
@@ -1,4 +1 @@
-+                        {
-+                            "role": message_dict["role"],
-+                            "content": [{"text": message_dict["content"]}],
-+                        }++                        self._validate_and_default_message_role(message_dict)

Comment on lines +101 to +106
message_dict
)
if bedrock_message:
formatted_messages.append(bedrock_message)
elif message_dict["role"] == "assistant":
# Convert assistant message to Bedrock format

Choose a reason for hiding this comment

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

Refactor tool handling into a dedicated method to clean up the main method and improve readability.

For clarity and maintainability, it is better to separate large blocks of conditional logic into small, well-named private methods. This can be done in the AwsProvider class where multiple if-else blocks handle different message roles.

Suggested change
message_dict
)
if bedrock_message:
formatted_messages.append(bedrock_message)
elif message_dict["role"] == "assistant":
# Convert assistant message to Bedrock format
+ self._configure_tool_handling(kwargs)

@dev-archie-ai-code-explain-pr dev-archie-ai-code-explain-pr bot changed the title Rcp/tool calling Enhanced tool calling and message handling for multiple providers Nov 27, 2024

PR Review Summary Celebratory GIF

Overall Review:

The PR introduces a sophisticated handling mechanism for tool calling across various providers like AWS, Anthropic, and others, along with enhancements in message object structures and compatibility updates. It includes significant refactoring and new implementations in the handling of JSON, external APIs, and dynamic data structures which are pivotal for the functioning of the AI systems in the suite. The changes also involve a new ToolManager, better structured data handling using BaseModel, and extensive JSON data manipulations which could involve security and logic error considerations.


Logical Error Analysis

1. [Blocker] In `AnthropicProvider`, `normalize_response` tries to handle different `stop_reason`s including `tool_use`. However, `tool_call_id` and method to retrieve `content` might not reliably parse the expected format every time. This can lead to unhandled exceptions or missing data.

🔒 Security Analysis

2. [Blocker] The methods `transform_tool_call_to_openai` and JSON handling in APIs involve sensitive data parsing which requires strict schema validations or error handling to mitigate potential injection attacks or corruption of data formats.

Recommendations

Recommendation #1

To address the potential inconsistencies in parsing tool_call_id and content, add rigorous checks and transformations to ensure the data conforms consistently to the expected format. Consider applying a schema validation or using a more robust method for parsing, especially when handling dynamic JSON content. Modify the relevant part in AnthropicProvider like so:

def normalize_response(self, response):
    normalized_response = ChatCompletionResponse()
    finish_reason_mapping = {
        "end_turn": "stop",
        "max_tokens": "length",
        "tool_use": "tool_calls",
    }
    normalized_response.choices[0].finish_reason = finish_reason_mapping.get(response.stop_reason, "stop")
   
    # Add usage information
    normalized_response.usage = {
        "prompt_tokens": response.usage.input_tokens,
        "completion_tokens": response.usage.output_tokens,
        "total_tokens": response.usage.input_tokens + response.usage.output_tokens,
    }
   
    # Check if the response contains tool usage and validate format
    if response.stop_reason == "tool_use":
        tool_call = next((content for content in response.content if 'type' in content and content.type == "tool_use"), None)
        if tool_call and 'id' in tool_call:
            tool_call_id = tool_call.id
            function = Function(name=tool_call.name, arguments=json.dumps(tool_call.input))
            tool_call_obj = ChatCompletionMessageToolCall(id=tool_call_id, function=function, type="function")
            text_content = next((content.text for content in response.content if 'type' in content and content.type == "text"), "")
            message = Message(content=text_content or None, tool_calls=[tool_call_obj] if tool_call else None, role="assistant", refusal=None)
            normalized_response.choices[0].message = message
        else:
            raise ValueError("Expected 'tool_use' content is missing or malformed in API response.")
   
    return normalized_response

This snippet includes error handling for missing or malformed 'tool_use' data, ensuring consistency before parsing.

Recommendation #2

Implement rigorous input validation and error handling around JSON operations and external data handling. Use secure practices such as using parameterized queries or templates when constructing requests or handling data to ensure data integrity and security. For instance:

def transform_tool_call_to_openai(self, response):
    if response.get("stopReason") != "tool_use":
        return None

    try:
        tool_calls = []
        for content in response["output"]["message"]["content"]:
            if "toolUse" in content:
                tool = content["toolUse"]
                tool_calls.append({
                    "type": "function",
                    "id": tool["toolUseId"],
                    "function": {
                        "name": tool["name"],
                        "arguments": json.dumps(tool["input"], cls=SecureJSONEncoder),
                    },
                })
    except json.JSONDecodeError as e:
        raise ValueError(f"JSON parsing error: {str(e)}")

    return {
        "role": "assistant",
        "content": None,
        "tool_calls": tool_calls,
        "refusal": None,
    }

In this code snippet, SecureJSONEncoder could be a custom JSONEncoder that sanitizes inputs to prevent JSON injection attacks. This ensures the integrity and security of data transformations and external interactions.

[Configure settings at: Archie AI - Automated PR Review]

Copy link

dev-archie-ai-code-explain-pr bot commented Nov 27, 2024

PR Code Suggestions Summary ✨

CategorySuggestion
Possible issue
Prevent potential KeyError by using safer dictionary access.

File: aisuite/framework/message.py LNone-LNone

Suggestion: Replace direct dictionary access role with get() for safer access in case the key does not exist in the dictionary.

--- Original
+++ Improved
@@ -1 +1 @@
-elif msg["role"] == "assistant" and "tool_calls" in msg:+elif msg.get("role") == "assistant" and "tool_calls" in msg:
Maintainability
Encapsulate debug print statements for cleaner code and improved maintainability.

File: aisuite/providers/anthropic_provider.py LNone-LNone

Suggestion: Encapsulate all the print statements in a debug function to better manage debugging and clean up code.

--- Original
+++ Improved
@@ -1,2 +1,3 @@
-print(converted_messages)
-print(response)+if debug_enabled:
+    debug_print(converted_messages)
+    debug_print(response)
Security
Add error handling around JSON parsing to manage malformed input better and improve code robustness.

File: aisuite/utils/tool_manager.py LNone-LNone

Suggestion: Add explicit error handling for JSON operations to manage potential exceptions that can arise due to malformatted inputs.

--- Original
+++ Improved
@@ -1 +1,5 @@
-arguments = json.loads(tool_call.function.arguments)+try:
+    arguments = json.loads(tool_call.function.arguments)
+except json.JSONDecodeError as e:
+    log_error(f"Failed to parse JSON: {e}")
+    return
Maintainability
Refactor message transformation to reduce complexity and improve code maintainability.

File: aisuite/providers/aws_provider.py LNone-LNone

Suggestion: Refactor the handling and transformation of messages between OpenAI and AWS to a separate class or function to reduce complexity within the method.

--- Original
+++ Improved
@@ -1,6 +1 @@
-if message_dict["role"] == "tool":
-    bedrock_message = self.transform_tool_result_to_bedrock(
-        message_dict
-    )
-    if bedrock_message:
-        formatted_messages.append(bedrock_message)+formatted_messages += self.handle_message_transformation(message_dict)
Performance
Improve code clarity and performance using list comprehensions for tool transformations.

File: aisuite/providers/anthropic_provider.py LNone-LNone

Suggestion: Use list comprehensions for cleaner code when transforming tool specifications.

--- Original
+++ Improved
@@ -0,0 +1,12 @@
+anthropic_tools = [
+    {
+        "name": tool["function"]["name"],
+        "description": tool["function"]["description"],
+        "input_schema": {
+            "type": "object",
+            "properties": tool["function"]["parameters"]["properties"],
+            "required": tool["function"]["parameters"].get("required", []),
+        },
+    }
+    for tool in openai_tools if tool.get("type") == "function"
+]
Maintainability
Refactor response choice handling into dedicated functions for better code organization and reusability.

File: examples/SimpleToolCalling.ipynb LNone-LNone

Suggestion: Encapsulate response choice handling in functions or method calls for clearer logical boundaries and reusability.

--- Original
+++ Improved
@@ -1,2 +1 @@
-messages.append(response.choices[0].message) # Model's function call message
-messages.append(result_as_message[0])+process_response_and_add_messages(response, result_as_message, messages)

@dev-archie-ai-code-explain-pr dev-archie-ai-code-explain-pr bot changed the title Enhanced tool calling and message handling for multiple providers Introduce message handling enhancements using OpenAI and AWS formats Nov 27, 2024

PR Review Summary Celebratory GIF

Overall Review:

The PR introduces significant changes aimed at standardizing and enhancing message handling and tool calling functionality across various AI providers. While the core intent to align with specific API requirements of services like AWS and Anthropic is beneficial, the complexity of the changes presents multiple areas of concern. This includes potential logical errors in data manipulation logic, security in handling JSON data transformations, and the overall sufficiency of test coverage for new and modified codes. The addition of robust tool-management utilities and modifications in message transformations indicates a well-planned improvement, but the real-world application will need careful validation.


Logical Error Analysis

1. [Blocker] In `AnthropicProvider`, `normalize_response` tries to handle different `stop_reasons` including `tool_use`. However, `tool_call_id` and method to retrieve `content` might not reliably parse the expected format every time. This can lead to unhandled exceptions or missing data.

🔒 Security Analysis

2. [Blocker] The methods `transform_tool_call_to_openai` and JSON handling in APIs involve sensitive data parsing which requires strict schema validations or error handling to mitigate potential injection attacks or corruption of data formats.

Recommendations

Recommendation #1

To address the potential inconsistencies in parsing tool_call_id and content, add rigorous checks and transformations to ensure the data conforms consistently to the expected format. Consider applying a schema validation or using a more robust method for parsing, especially when handling dynamic JSON content. Here's a code modification suggestion for the normalize_response function in the AnthropicProvider:

def normalize_response(self, response):
    normalized_response = ChatCompletionResponse()
    finish_reason_mapping = {
        "end_turn": "stop",
        "max_tokens": "length",
        "tool_use": "tool_calls",
    }
    normalized_response.choices[0].finish_reason = finish_reason_mapping.get(response.stop_reason, "stop")
   
    # Add usage information
    normalized_response.usage = {
        "prompt_tokens": response.usage.input_tokens,
        "completion_tokens": response.usage.output_tokens,
        "total_tokens": response.usage.input_tokens + response.usage.output_tokens,
    }
   
    # Check if the response contains tool usage and validate format
    if response.stop_reason == "tool_use":
        tool_call = next((content for content in response.content if 'type' in content and content.type == "tool_use"), None)
        if tool_call and 'id' in tool_call:
            tool_call_id = tool_call.id
            function = Function(name=tool_call.name, arguments=json.dumps(tool_call.input))
            tool_call_obj = ChatCompletionMessageToolCall(id=tool_call_id, function=function, type="function")
            text_content = next((content.text for content in response.content if 'type' in content and content.type == "text"), "")
            message = Message(content=text_content or None, tool_calls=[tool_call_obj] if tool_call else None, role="assistant", refusal=None)
            normalized_response.choices[0].message = message
        else:
            raise ValueError("Expected 'tool_use' content is missing or malformed in API response.")
   
    return normalized_response

This snippet includes error handling for missing or malformed 'tool_use' data, ensuring the internal data paths are consistent before parsing.

Recommendation #2

Implement rigorous input validation and error handling around JSON operations and external data handling. Use secure practices such as using parameterized queries or templates when constructing requests or handling data to ensure data integrity and security. Here's an example of how you might code this:

def transform_tool_call_to_openai(self, response):
    if response.get("stopReason") != "tool_use":
        return None

    try:
        tool_calls = []
        for content in response["output"]["message"]["content"]:
            if "toolUse" in content:
                tool = content["toolUse"]
                tool_calls.append({
                    "type": "function",
                    "id": tool["toolUseId"],
                    "function": {
                        "name": tool["name"],
                        "arguments": json.dumps(tool["input"], cls=SecureJSONEncoder),
                    },
                })
    except json.JSONDecodeError as e:
        raise ValueError(f"JSON parsing error: {str(e)}")

    return {
        "role": "assistant",
        "content": None,
        "tool_calls": tool_calls,
        "refusal": None,
    }

In this code snippet, SecureJSONEncoder could be a custom JSONEncoder that sanitizes inputs to prevent JSON injection attacks. This ensures the integrity and security of data transformations and external interactions.

[Configure settings at: Archie AI - Automated PR Review]

PR Review Summary Celebratory GIF

Overall Review:

The PR introduces significant enhancements in handling messages and tool calling across various AI providers by standardizing interactions and data formats, particularly for AWS and Anthropics. Changes include refactoring the Message class with pydantic.BaseModel for stronger data validation and enhancing transformation and normalization processes for tool calls. The alterations are broad and impact essential functionalities, including tools management and message coordination. While the alterations aim to accommodate provider-specific nuances, they introduce complexities requiring careful scrutiny to ensure integration smoothness and reliability.


Logical Error Analysis

1. [Blocker] In `AnthropicProvider`, `normalize_response` tries to handle different `stop_reason`s including `tool_use`. However, `tool_call_id` and method to retrieve `content` might not reliably parse the expected format every time. This can lead to unhandled exceptions or missing data.

🔒 Security Analysis

2. [Blocker] The methods `transform_tool_call_to_openai` and JSON handling in APIs involve sensitive data parsing which requires strict schema validations or error handling to mitigate potential injection attacks or corruption of data formats.

🧪 Test Coverage Analysis

3. [Consider] Test coverage appears lacking in scenarios involving erroneous or malicious input data. Given the extensive data handling and transformations, it is crucial to ensure that negative tests check how the system handles unexpected or malformed input.

Recommendations

Recommendation #1

To address the potential inconsistencies in parsing tool_call_id and content, implement rigorous checks and transformations to ensure data conforms to the expected format consistently. Consider applying schema validation or robust parsing especially for handling dynamic JSON content. You can modify the normalize_response function in AnthropicProvider:

def normalize_response(self, response):
    normalized_response = ChatCompletionResponse()
    finish_reason_mapping = {
        "end_turn": "stop",
        "max_tokens": "length",
        "tool_use": "tool_calls",
    }
    normalized_response.choices[0].finish_reason = finish_reason_mapping.get(response.stop_reason, "stop")
   
    # Add usage information
    normalized_response.usage = {
        "prompt_tokens": response.usage.input_tokens,
        "completion_tokens": response.usage.output_tokens,
        "total_tokens": response.usage.input_tokens + response.usage.output_tokens,
    }
   
    # Validate tool usage
    if response.stop_reason == "tool_use":
        tool_call = next((content for content in response.content if 'type' in content and content.type == "tool_use"), None)
        if tool_call and 'id' in tool_call:
            tool_call_id = tool_call.id
            function = Function(name=tool_call.name, arguments=json.dumps(tool_call.input))
            tool_call_obj = ChatCompletionMessageToolCall(id=tool_call_id, function=function, type="function")
            text_content = next((content.text for content in response.content if 'type' in content and content.type == "text"), "")
            message = Message(content=text_content or None, tool_calls=[tool_call_obj] if tool_call else None, role="assistant", refusal=None)
            normalized_response.choices[0].message = message
        else:
            raise ValueError("Expected 'tool_use' content is missing or malformed in API response.")
    
    return normalized_response

This snippet will ensure that the tool_use data is checked and properly parsed, throwing an error if the expected format is not met.

Recommendation #2

Implement rigorous input validation and error handling around JSON operations and external data handling. Use secure practices, such as parameterized queries or templates when constructing requests or handling data. For instance, redesign the method transform_tool_call_to_openai using secure JSON handling practices:

def transform_tool_call_to_openai(self, response):
    if response.get("stopReason") != "tool_use":
        return None

    try:
        tool_calls = []
        for content in response["output"]["message"]["content"]:
            if "toolUse" in content:
                tool = content["toolUse"]
                tool_calls.append({
                    "type": "function",
                    "id": tool["toolUseId"],
                    "function": {
                        "name": tool["name"],
                        "arguments": json.dumps(tool["input"], cls=SecureJSONEncoder),
                    },
                })
    except json.JSONDecodeError as e:
        raise ValueError(f"JSON parsing error: {str(e)}")

    return {
        "role": "assistant",
        "content": None,
        "tool_calls": tool_calls,
        "refusal": None,
    }

In this code snippet, SecureJSONEncoder should be a custom JSONEncoder that sanitizes inputs to prevent JSON injection attacks.

Recommendation #3

Enhance the test suite to cover various scenarios of malformed or incorrect data inputs. Introduce comprehensive tests simulating different types of errors to ensure robust error handling and security. For example, create tests for JSON transformations to simulate malformed JSON:

import pytest
from your_module import some_function_handling_json

def test_malformed_json_handling():
    with pytest.raises(SomeSpecificException):
        malformed_json = '{unclosed_json_object'
        some_function_handling_json(malformed_json)
    # Ensure the function raises an error or handles the malformed JSON gracefully.

This tests how the system behaves under erroneous conditions, thereby enhancing the reliability of data transformations.

[Configure settings at: Archie AI - Automated PR Review]

Copy link

dev-archie-ai-code-explain-pr bot commented Nov 27, 2024

PR Code Suggestions Summary ✨

CategorySuggestion
Enhancement
Utilize enums to enforce constraints on 'role' and type fields, enhancing robustness and maintain type safety.

File: aisuite/framework/message.py L6-L24

Suggestion: Use Enumerations to enforce valid values and enhance type safety in role and type fields.

--- Original
+++ Improved
@@ -1,4 +1,4 @@
 +    content: Optional[str]
 +    tool_calls: Optional[list[ChatCompletionMessageToolCall]]
-+    role: Optional[Literal["user", "assistant", "system"]]
++    role: Optional[Literal["user", "assistant", "system"]] = Field(..., description="Role of the message sender", enum=["user", "assistant", "system"])
 +    refusal: Optional[str]
Maintainability
Reduces code duplication and enhances maintainability by refactoring repetitive conversion code into a separate function.

File: aisuite/providers/anthropic_provider.py L32-L175

Suggestion: Abstract the repetitive conversion logic into separate functions for clarity and reuse.

--- Original
+++ Improved
@@ -1,8 +1 @@
-+        # Convert tool results from OpenAI format to Anthropic format
-+        converted_messages = []
-+        for msg in messages:
-+            if isinstance(msg, dict):
-+                if msg["role"] == "tool":
-+                    # Convert tool result message
-+                    converted_msg = {
-+                        "role": "user",++        converted_messages = self.refactor_conversion_logic(messages)
Performance
Utilizes Python list comprehension for more concise and readable transformation of tools data.

File: aisuite/providers/aws_provider.py LNone-LNone

Suggestion: Incorporate list comprehension for concise and efficient transformations wherever possible.

--- Original
+++ Improved
@@ -1,7 +1 @@
-+        tool_config = None
-+        if "tools" in kwargs:
-+            tool_config = {
-+                "tools": [
-+                    {
-+                        "toolSpec": {
-+                            "name": tool["function"]["name"],++        tool_config = {"tools": [self.transform_tool(tool) for tool in kwargs.get("tools", [])] if "tools" in kwargs else None}
Security
Enforces JSON schema validation for tools input to ensure data integrity and security.

File: aisuite/providers/aws_provider.py LNone-LNone

Suggestion: Add JSON schema validation for tools and input parameters to ensure security and data integrity.

--- Original
+++ Improved
@@ -1,4 +1,5 @@
 +    if "tools" in kwargs:
++        validate_tool_input(kwargs["tools"])  # Validate against a JSON schema
 +        tool_config = {
 +            "tools": [
 +                {
Enhancement
Introduces a decorator for error handling to simplify the function structure and improve consistency.

File: aisuite/providers/aws_provider.py LNone-LNone

Suggestion: Convert separate function calls and structures for error handling into a decorator for consistent error management.

--- Original
+++ Improved
@@ -1,4 +1,6 @@
-+    if "tools" in kwargs:
-+        tool_config = {
-+            "tools": [
-+                {++    @handle_errors
++    def handle_tool_usage(self, kwargs):
++        if "tools" in kwargs:
++            tool_config = {
++                "tools": [
++                    {

Comment on lines 32 to +175
# Handle Message objects
if msg.role == "tool":
converted_msg = {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": msg.tool_call_id,
"content": msg.content,
}
],
}
converted_messages.append(converted_msg)
elif msg.role == "assistant" and msg.tool_calls:
# Handle Message objects with tool calls
content = []
if msg.content:
content.append({"type": "text", "text": msg.content})
for tool_call in msg.tool_calls:
content.append(
{
"type": "tool_use",
"id": tool_call.id,
"name": tool_call.function.name,
"input": json.loads(tool_call.function.arguments),
}
)
converted_messages.append({"role": "assistant", "content": content})
else:
converted_messages.append(
{"role": msg.role, "content": msg.content}
)

print(converted_messages)
response = self.client.messages.create(
model=model, system=system_message, messages=converted_messages, **kwargs
)
print(response)
return self.normalize_response(response)

def normalize_response(self, response):
"""Normalize the response from the Anthropic API to match OpenAI's response format."""
normalized_response = ChatCompletionResponse()
normalized_response.choices[0].message.content = response.content[0].text

# Map Anthropic stop_reason to OpenAI finish_reason
finish_reason_mapping = {
"end_turn": "stop",
"max_tokens": "length",
"tool_use": "tool_calls",
# Add more mappings as needed
}
normalized_response.choices[0].finish_reason = finish_reason_mapping.get(
response.stop_reason, "stop"
)

# Add usage information
normalized_response.usage = {
"prompt_tokens": response.usage.input_tokens,
"completion_tokens": response.usage.output_tokens,
"total_tokens": response.usage.input_tokens + response.usage.output_tokens,
}

# Check if the response contains tool usage
if response.stop_reason == "tool_use":
# Find the tool_use content
tool_call = next(
(content for content in response.content if content.type == "tool_use"),
None,
)

if tool_call:
function = Function(
name=tool_call.name, arguments=json.dumps(tool_call.input)
)
tool_call_obj = ChatCompletionMessageToolCall(
id=tool_call.id, function=function, type="function"
)
# Get the text content if any
text_content = next(
(
content.text
for content in response.content
if content.type == "text"
),
"",
)

message = Message(
content=text_content or None,
tool_calls=[tool_call_obj] if tool_call else None,
role="assistant",
refusal=None,
)
normalized_response.choices[0].message = message
return normalized_response

# Handle regular text response
message = Message(
content=response.content[0].text,

Choose a reason for hiding this comment

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

Reduces code duplication and enhances maintainability by refactoring repetitive conversion code into a separate function.

Abstract the repetitive conversion logic into separate functions for clarity and reuse.

Suggested change
if "max_tokens" not in kwargs:
kwargs["max_tokens"] = DEFAULT_MAX_TOKENS
return self.normalize_response(
self.client.messages.create(
model=model, system=system_message, messages=messages, **kwargs
)
# Handle tool calls. Convert from OpenAI tool calls to Anthropic tool calls.
if "tools" in kwargs:
kwargs["tools"] = convert_openai_tools_to_anthropic(kwargs["tools"])
# Convert tool results from OpenAI format to Anthropic format
converted_messages = []
for msg in messages:
if isinstance(msg, dict):
if msg["role"] == "tool":
# Convert tool result message
converted_msg = {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": msg["tool_call_id"],
"content": msg["content"],
}
],
}
converted_messages.append(converted_msg)
elif msg["role"] == "assistant" and "tool_calls" in msg:
# Handle assistant messages with tool calls
content = []
if msg.get("content"):
content.append({"type": "text", "text": msg["content"]})
for tool_call in msg["tool_calls"]:
content.append(
{
"type": "tool_use",
"id": tool_call["id"],
"name": tool_call["function"]["name"],
"input": json.loads(tool_call["function"]["arguments"]),
}
)
converted_messages.append({"role": "assistant", "content": content})
else:
# Keep other messages as is
converted_messages.append(
{"role": msg["role"], "content": msg["content"]}
)
else:
# Handle Message objects
if msg.role == "tool":
converted_msg = {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": msg.tool_call_id,
"content": msg.content,
}
],
}
converted_messages.append(converted_msg)
elif msg.role == "assistant" and msg.tool_calls:
# Handle Message objects with tool calls
content = []
if msg.content:
content.append({"type": "text", "text": msg.content})
for tool_call in msg.tool_calls:
content.append(
{
"type": "tool_use",
"id": tool_call.id,
"name": tool_call.function.name,
"input": json.loads(tool_call.function.arguments),
}
)
converted_messages.append({"role": "assistant", "content": content})
else:
converted_messages.append(
{"role": msg.role, "content": msg.content}
)
print(converted_messages)
response = self.client.messages.create(
model=model, system=system_message, messages=converted_messages, **kwargs
)
print(response)
return self.normalize_response(response)
def normalize_response(self, response):
"""Normalize the response from the Anthropic API to match OpenAI's response format."""
normalized_response = ChatCompletionResponse()
normalized_response.choices[0].message.content = response.content[0].text
# Map Anthropic stop_reason to OpenAI finish_reason
finish_reason_mapping = {
"end_turn": "stop",
"max_tokens": "length",
"tool_use": "tool_calls",
# Add more mappings as needed
}
normalized_response.choices[0].finish_reason = finish_reason_mapping.get(
response.stop_reason, "stop"
)
# Add usage information
normalized_response.usage = {
"prompt_tokens": response.usage.input_tokens,
"completion_tokens": response.usage.output_tokens,
"total_tokens": response.usage.input_tokens + response.usage.output_tokens,
}
# Check if the response contains tool usage
if response.stop_reason == "tool_use":
# Find the tool_use content
tool_call = next(
(content for content in response.content if content.type == "tool_use"),
None,
)
if tool_call:
function = Function(
name=tool_call.name, arguments=json.dumps(tool_call.input)
)
tool_call_obj = ChatCompletionMessageToolCall(
id=tool_call.id, function=function, type="function"
)
# Get the text content if any
text_content = next(
(
content.text
for content in response.content
if content.type == "text"
),
"",
)
message = Message(
content=text_content or None,
tool_calls=[tool_call_obj] if tool_call else None,
role="assistant",
refusal=None,
)
normalized_response.choices[0].message = message
return normalized_response
# Handle regular text response
message = Message(
content=response.content[0].text,
+ converted_messages = self.refactor_conversion_logic(messages)

class Message:
def __init__(self):
self.content = None

Choose a reason for hiding this comment

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

Utilize enums to enforce constraints on 'role' and type fields, enhancing robustness and maintain type safety.

Use Enumerations to enforce valid values and enhance type safety in role and type fields.

Suggested change
+ content: Optional[str]
+ tool_calls: Optional[list[ChatCompletionMessageToolCall]]
+ role: Optional[Literal["user", "assistant", "system"]] = Field(..., description="Role of the message sender", enum=["user", "assistant", "system"])
+ refusal: Optional[str]

PR Review Summary Celebratory GIF

Overall Review:

The PR seeks to enhance the message and tool calling functionality across multiple AI service providers like Anthropic and AWS. It introduces significant changes to the handling of message structures and API interactions, adding structured tool management and transformation processes. Notable changes include the refactoring of the Message class using pydantic.BaseModel and extensive modifications in handling APIs' specific requirements, potentially affecting the application's reliability and maintainability. It is critical to scrutinize these changes for potential logical errors, security risks, and their thorough testing given the complexity and broad impact of the modifications involved.


🔒 Security Analysis

1. [Blocker] The methods `transform_tool_call_to_openai` and JSON handling in APIs involve sensitive data parsing which requires strict schema validations or error handling to mitigate potential injection attacks or corruption of data formats.

Logical Error Analysis

2. [Blocker] In `AnthropicProvider`, `normalize_response` tries to handle different `stop_reasons` including `tool_use`. However, `tool_call_id` and method to retrieve `content` might not reliably parse the expected format every time. This can lead to unhandled exceptions or missing data.

Recommendations

Recommendation #1

Implement rigorous input validation and error handling around JSON operations and external data handling. Use secure practices such as using parameterized queries or templates when constructing requests or handling data. For instance:

def transform_tool_call_to_openai(self, response):
    if response.get("stopReason") != "tool_use":
        return None

    try:
        tool_calls = []
        for content in response["output"]["message"]["content"]:
            if "toolUse" in content:
                tool = content["toolUse"]
                tool_calls.append({
                    "type": "function",
                    "id": tool["toolUseId"],
                    "function": {
                        "name": tool["name"],
                        "arguments": json.dumps(tool["input"], cls=SecureJSONEncoder),
                    },
                })
    except json.JSONDecodeError as e:
        raise ValueError(f"JSON parsing error: {str(e)}")

    return {
        "role": "assistant",
        "content": None,
        "tool_calls": tool_calls,
        "refusal": None,
    }

In this code snippet, SecureJSONEncoder could be a custom JSONEncoder that sanitizes inputs or checks them against a schema to prevent JSON injection attacks. This ensures the integrity and security of data transformations and external interactions.

Recommendation #2

To address the potential inconsistencies in parsing tool_call_id and content, implement rigorous checks and transformations to ensure data conforms to the expected format consistently. Consider applying schema validation or more robust parsing especially when handling dynamic JSON content. Example modification:

def normalize_response(self, response):
    normalized_response = ChatCompletionResponse()
    finish_reason_mapping = {
        "end_turn": "stop",
        "max_tokens": "length",
        "tool_use": "tool_calls",
    }
    normalized_response.choices[0].finish_reason = finish_reason_mapping.get(response.stop_reason, "stop")

    # Add usage information
    normalized_response.usage = {
        "prompt_tokens": response.usage.input_tokens,
        "completion_tokens": response.usage.output_tokens,
        "total_tokens": response.usage.input_tokens + response.usage.output_tokens,
    }

    # Validate tool usage
    if response.stop_reason == "tool_use":
        tool_call = next((content for content in response.content if 'type' in content and content.type == "tool_use"), None)
        if tool_call and 'id' in tool_call:
            tool_call_id = tool_call.id
            function = Function(name=tool_call.name, arguments=json.dumps(tool_call.input))
            tool_call_obj = ChatCompletionMessageToolCall(id=tool_call_id, function=function, type="function")
            text_content = next((content.text for content in response.content if 'type' in content and content.type == "text"), "")
            message = Message(content=text_content or None, tool_calls=[tool_call_obj] if tool_call else None, role="assistant", refusal=None)
            normalized_response.choices[0].message = message
        else:
            raise ValueError("Expected 'tool_use' content is missing or malformed in API response.")

    return normalized_response
[Configure settings at: Archie AI - Automated PR Review]

PR Review Summary Celebratory GIF

Overall Review:

The PR introduces profound changes across the AISuite to enhance message and tool calling capabilities for various AI providers, leveraging structured models and APIs specific to providers like AWS and Anthropic. Main enhancements include significant modifications to the message classes, sophisticated JSON data handling, and the incorporation of a ToolManager to abstract and streamline tool calling operations. These adjustments are crucial, given their extensive impact on the tool calling and message management functionalities which are central to the integration flexibility and operational reliability of the AISuite.

Commit logs and file changes suggest iterative development and testing, focusing on incremental integration and ensuring compatibility with external APIs. This approach helps in isolating features for specific providers while maintaining a generalized framework that could be adapted for additional AI service providers as needed.


Logical Error Analysis

1. [Blocker] In `AnthropicProvider`, the method `normalize_response` improperly processes the `tool_call_id` and can lead to unhandled exceptions or missing data if the incoming format does not match expectations. This can destabilize the application's ability to process tool calls effectively.

🔒 Security Analysis

2. [Blocker] The methods `transform_tool_call_to_openai` involve sensitive data parsing which may expose the system to JSON injection attacks unless properly handled with schema validations or stringent input checks.

Recommendations

Recommendation #1

To enhance the robustness of tool_call_id parsing, modify the AnthropicProvider method to handle potential inconsistencies and ensure the data conforms to the expected format. Consider robust error checking or schema validations, especially when dealing with dynamic JSON content. Here's a sample modification:

def normalize_response(self, response):
    normalized_response = ChatCompletionResponse()
    finish_reason_mapping = {
        "end_turn": "stop",
        "max_tokens": "length",
        "tool_use": "tool_calls",
    }
    normalized_response.choices[0].finish_reason = finish_reason_mapping.get(response.stop_reason, "stop")
    
    normalized_response.usage = {
        "prompt_tokens": response.usage.input_tokens,
        "completion_tokens": response.usage.output_tokens,
        "total_tokens": response.usage.input_tokens + response.usage.output_tokens,
    }
    
    if response.stop_reason == "tool_use":
        tool_call = next((content for content in response.content if content.type == "tool_use"), None)
        if tool_call and 'id' in tool_call:
            function = Function(name=tool_call.name, arguments=json.dumps(tool_call.input))
            tool_call_obj = ChatCompletionMessageToolCall(
                id=tool_call['id'], function=function, type="function"
            )
            text_content = next(
                (content.text for content in response.content if content.type == "text"), ""
            )
            message = Message(
                content=text_content or None,
                tool_calls=[tool_call_obj] if tool_call else None,
                role="assistant",
                refusal=None
            )
            normalized_response.choices[0].message = message
        else:
            raise ValueError("Expected 'tool_use' content is missing or malformed in API response.")

    return normalized_response
Recommendation #2

Implement rigorous input validation and sanitization within transform_tool_call_to_openai. It's essential to apply secure practices such as using parameterized queries or prepared statements when constructing queries from JSON content. Here's a revised method example that includes security measures:

def transform_tool_call_to_openai(self, response):
    if response.get("stopReason") != "tool_use":
        return None
    
    try:
        tool_calls = []
        for content in response["output"]["message"]["content"]:
            if "toolUse" in content:
                tool = content["toolUse"]
                tool_calls.append({
                    "type": "function",
                    "id": tool["toolUseId"],
                    "function": {
                        "name": tool["name"],
                        "arguments": json.dumps(tool["input"], cls=SecureJSONEncoder),
                    },
                })
    except json.JSONDecodeError as e:
        raise ValueError(f"JSON parsing error: {str(e)}")
    
    return {
        "role": "assistant",
        "content": None,
        "tool_calls": tool_calls,
        "refusal": None,
    }

In the above snippet, SecureJSONEncoder should be a custom JSONEncoder designed to sanitize and validate inputs to ward off injection threats.

[Configure settings at: Archie AI - Automated PR Review]

@dev-archie-ai-code-explain-pr dev-archie-ai-code-explain-pr bot changed the title Introduce message handling enhancements using OpenAI and AWS formats Enhance message handling for various providers with new data structures Nov 27, 2024
@dev-archie-ai-code-explain-pr dev-archie-ai-code-explain-pr bot changed the title Enhance message handling for various providers with new data structures Enhance message structuring and add tool call handling across providers Nov 27, 2024
Copy link

PR Review Summary Celebratory GIF

Overall Review:

The PR introduces significant enhancements across multiple AI service providers within the aisuite, focusing on structured message handling and tool calling functionality. These changes span introducing new models with Pydantic, implementing a ToolManager, and adjusting APIs to these structures. The PR aims to establish a uniform message structuring system and enhance tool management, which are vital for maintaining the system's flexibility and robustness. The adoption of the pydantic.BaseModel increases type safety and data validation, reinforcing the backend's reliability and maintainability. The changes are extensive and touch on critical components that require careful validation to ensure they perform as expected without introducing regressions or vulnerabilities.


🔒 Security Analysis

1. [Blocker] The methods `transform_tool_call_to_openai` and JSON handling in APIs involve sensitive data parsing which may expose the system to JSON injection attacks unless properly handled with schema validations or stringent input checks.

Logical Error Analysis

2. [Blocker] In `AnthropicProvider`, `normalize_response` tries to handle different `stop_reasons` including `tool_use`. However, `tool_call_id` and method to retrieve `content` might not reliably parse the expected format every time, which can lead to unhandled exceptions or missing data.

Recommendations

Recommendation #1

To mitigate potential security vulnerabilities related to JSON parsing, fortify the data handling process with improved schema validation and thorough error handling. Integrate a custom JSON encoder to sanitize inputs before parsing and ensure functional test coverage:

class SecureJSONEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, str):
            return o.replace('"', '\"').replace('<', '\<')
        return json.JSONEncoder.default(self, o)

def transform_tool_call_to_openai(self, response):
    if response.get("stopReason") != "tool_use":
        return None

    tool_calls = []
    try:
        for content in response["output"]["message"]["content"]:
            if "toolUse" in content:
                tool = content["toolUse"]
                secure_args = json.dumps(tool["input"], cls=SecureJSONEncoder)
                tool_calls.append({
                    "type": "function",
                    "id": tool["toolUseId"],
                    "function": {
                        "name": tool["name"],
                        "arguments": secure_args,
                    },
                })
    except json.JSONDecodeError as e:
        raise ValueError(f"JSON parsing error: {str(e)}")

    return {
        "role": "assistant",
        "content": None,
        "tool_calls": tool_calls,
        "refusal": None,
    }

Properly structure your error handling and testing to validate defense against common vulnerabilities in JSON processing.

Recommendation #2

Rework the normalize_response function with additional checks and validations to prevent failures due to format discrepancies. Implement a more robust handling of potential missing data:

def normalize_response(self, response):
    normalized_response = ChatCompletionResponse()
    finish_reason_mapping = {
        "end_turn": "stop",
        "max_tokens": "length",
        "tool_use": "tool_calls",
    }
    normalized_response.choices[0].finish_reason = finish_reason_mapping.get(response.stop_reason, "stop")

    normalized_response.usage = {
        "prompt_tokens": response.usage.input_tokens,
        "completion_tokens": response.usage.output_tokens,
        "total_tokens": response.usage.input_tokens + response.usage.output_tokens,
    }

    if response.stop_reason == "tool_use":
        tool_call = next((content for content in response.content if 'type' in content and content.type == "tool_use"), None)
        if tool_call and 'id' in tool_call:
            tool_call_id = tool_call.id
            function = Function(name=tool_call.name, arguments=json.dumps(tool_call.input))
            tool_call_obj = ChatCompletionMessageToolCall(id=tool_call_id, function=function, type="function")
            text_content = next(
                (content.text for content in response.content if 'type' in content and content.type == "text"), ""
            )
            message = Message(
                content=text_content or None,
                tool_calls=[tool_call_obj] if tool_call else None,
                role="assistant",
                refusal=None
            )
            normalized_response.choices[0].message = message
        else:
            raise ValueError("Expected 'tool_use' content is missing or malformed in API response.")

    return normalized_response

This modification ensures that all necessary identifiers and data types are checked before processing, improving the resilience of your application against data inconsistencies and related errors.

[Configure settings at: Archie AI - Automated PR Review]

Copy link

dev-archie-ai-code-explain-pr bot commented Dec 3, 2024

PR Code Suggestions Summary ✨

CategorySuggestion
Maintainability
Improve code readability by using descriptive variable names for complex data structures.

File: aisuite/providers/aws_provider.py L189-L207

Suggestion:

--- Original
+++ Improved
@@ -0,0 +1,13 @@
++    tool_calls = []
++    for content in response["output"]["message"]["content"]:
++        if "toolUse" in content:
++            tool = content["toolUse"]
++            tool_call_details = {
++                "type": "function",
++                "id": tool["toolUseId"],
++                "function": {
++                    "name": tool["name"],
++                    "arguments": json.dumps(tool["input"]),
++                },
++            }
++            tool_calls.append(tool_call_details)

Comment on lines +189 to +207
if response.get("stopReason") != "tool_use":
return None

tool_calls = []
for content in response["output"]["message"]["content"]:
if "toolUse" in content:
tool = content["toolUse"]
tool_calls.append(
{
"type": "function",
"id": tool["toolUseId"],
"function": {
"name": tool["name"],
"arguments": json.dumps(tool["input"]),
},
}
)

if not tool_calls:

Choose a reason for hiding this comment

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

Improve code readability by using descriptive variable names for complex data structures.

Suggested change
if response.get("stopReason") != "tool_use":
return None
tool_calls = []
for content in response["output"]["message"]["content"]:
if "toolUse" in content:
tool = content["toolUse"]
tool_calls.append(
{
"type": "function",
"id": tool["toolUseId"],
"function": {
"name": tool["name"],
"arguments": json.dumps(tool["input"]),
},
}
)
if not tool_calls:
+ tool_calls = []
+ for content in response["output"]["message"]["content"]:
+ if "toolUse" in content:
+ tool = content["toolUse"]
+ tool_call_details = {
+ "type": "function",
+ "id": tool["toolUseId"],
+ "function": {
+ "name": tool["name"],
+ "arguments": json.dumps(tool["input"]),
+ },
+ }
+ tool_calls.append(tool_call_details)

@dev-archie-ai-code-explain-pr dev-archie-ai-code-explain-pr bot changed the title Enhance message structuring and add tool call handling across providers Integrate advanced message structuring and tool call conversion for various providers Dec 3, 2024
Copy link

PR Review Summary Celebratory GIF

Overall Review:

This PR makes necessary changes to the webhook handling logic.


🌟 Code Quality And Design

1. [Consider] Good error handling implementation

Recommendations

Recommendation #1

No changes needed

[Configure settings at: Archie AI - Automated PR Review]

@dev-archie-ai-code-explain-pr dev-archie-ai-code-explain-pr bot changed the title Enhanced webhook error handling with message models and framework changes Enhance webhook handling logic with improved error handling Dec 6, 2024
Copy link

PR Review Summary Celebratory GIF

Overall Review:

This PR makes necessary changes to the webhook handling logic.


🌟 Code Quality And Design

1. [Consider] Good error handling implementation

Recommendations

Recommendation #1

No changes needed

[Configure settings at: Archie AI - Automated PR Review]

PR Review Summary Celebratory GIF

Overall Review:

The Pull Request aims to enhance webhook handling logic through better error management, data validation, and structured code improvements. Key modifications encompass significant overhauls to the webhook processing system to increase reliability with improved error handling and logging mechanisms. The PR's expansive scope touches on message handling with a redefinition of the Message class using pydantic.BaseModel, introduction of a ToolManager for streamlined tool interactions, comprehensive adaptation for AI provider APIs, and potentially sensitive data operations such as JSON handling.


Recommendations

[Configure settings at: Archie AI - Automated PR Review]

Copy link

dev-archie-ai-code-explain-pr bot commented Dec 11, 2024

PR Code Suggestions Summary ✨

CategorySuggestion
Possible issue
Improve error handling by verifying the presence of necessary dictionary keys to prevent runtime errors.

File: aisuite/providers/aws_provider.py L70-L72

Suggestion: Consider validating the 'message_dict' dictionary prior to access to prevent 'KeyError'.

--- Original
+++ Improved
@@ -1,3 +1,6 @@
 +            message_dict = (
 +                message.model_dump() if hasattr(message, "model_dump") else message
-+            )++            )
++            if "role" not in message_dict or "content" not in message_dict:
++                print(f"Invalid message format: {message_dict}")
++                continue
Enhancement
Add custom JSON serialization to ensure complex string fields are appropriately serialized before sending over the network.

File: aisuite/framework/message.py LNone-LNone

Suggestion: Implement a custom JSON serializer for 'Function' class to ensure 'arguments' are being correctly serialized for network transmission.

--- Original
+++ Improved
@@ -1,3 +1,8 @@
 +class Function(BaseModel):
 +    arguments: str
-+    name: str++    name: str
++
++    class Config:
++        json_encoders = {
++            str: lambda s: json.dumps(s) if not isinstance(s, str) else s
++        }
Maintainability
Refactor response transformation logic into a separate method to improve maintainability and reduce duplication across methods and classes.

File: aisuite/providers/aws_provider.py LNone-LNone

Suggestion: Extract complex response transformations into a separate method for clarity and reusability across different provider implementations.

--- Original
+++ Improved
@@ -1,12 +1,14 @@
-+        if "role" == "tool":
-+            converted_msg = {
-+                "role": "user",
-+                "content": [
-+                    {
-+                        "type": "tool_result",
-+                        "tool_use_id": msg["tool_call_id"],
-+                        "content": msg["content"],
-+                    }
-+                ],
-+            }
-+            converted_messages.append(converted_msg)++        if message_dict["role"] == "tool":
++            converted_messages.append(self.transform_tool_message_to_bedrock_format(message_dict))
+
++    def transform_tool_message_to_bedrock_format(self, message):
++        return {
++            "role": "user",
++            "content": [
++                {
++                    "type": "tool_result",
++                    "tool_use_id": message["tool_call_id"],
++                    "content": message["content"],
++                }
++            ],
++        }

Comment on lines 70 to 72

def normalize_response(self, response):
"""Normalize the response from the Bedrock API to match OpenAI's response format."""

Choose a reason for hiding this comment

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

Improve error handling by verifying the presence of necessary dictionary keys to prevent runtime errors.

Consider validating the 'message_dict' dictionary prior to access to prevent 'KeyError'.

Suggested change
def normalize_response(self, response):
"""Normalize the response from the Bedrock API to match OpenAI's response format."""
+ message_dict = (
+ message.model_dump() if hasattr(message, "model_dump") else message
+ )
+ if "role" not in message_dict or "content" not in message_dict:
+ print(f"Invalid message format: {message_dict}")
+ continue

@dev-archie-ai-code-explain-pr dev-archie-ai-code-explain-pr bot changed the title Enhance webhook handling logic with improved error handling Enhanced webhook handling with expanded message framework and tool call integration Dec 11, 2024

PR Review Summary Celebratory GIF

Overall Review:

This PR introduces significant architectural and functional changes to the aisuite, focusing on enhanced message frameworks and tool calling integrations. The adoption of a structured Message model using Pydantic and a centralized ToolManager are pivotal for ensuring robust data validation and managing interactions with varied AI providers. While these enhancements are promising for modularity and scalability, they necessitate meticulous validation to prevent introduction of logical and security flaws due to the complexity of the changes involved.


Logical Error Analysis

1. [Blocker] The method `normalize_response` in `AnthropicProvider` attempts to handle different `stop_reasons` including `tool_use`. However, `tool_call_id` and the method to retrieve `content` might not consistently parse the expected format, leading to potential unhandled exceptions or missing information.

🔒 Security Analysis

2. [Blocker] The methods `transform_tool_call_to_openai` involve sensitive data parsing and handling, which could expose the system to JSON injection attacks if not handled properly with strict validations and error handling methods.

Recommendations

Recommendation #1

To address this issue, consider enhancing the normalize_response function with additional checks and structured error handling. The modified method should validate the data and parsing process before proceeding to normalize or transform the responses:

def normalize_response(self, response):
    normalized_response = ChatCompletionResponse()
    finish_reason_mapping = {
        "end_turn": "stop",
        "max_tokens": "length",
        "tool_use": "tool_calls",
    }
    normalized_response.choices[0].finish_reason = finish_reason_mapping.get(response.stop_reason, "stop")
    
    if response.stop_reason == "tool_use":
        tool_call = next((tc for tc in response.content if tc['type'] == "tool_use"), None)
        if tool_call and 'id' in tool_call:
            function = Function(name=tool_call['name'], arguments=json.dumps(tool_call['input']))
            tool_call_obj = ChatCompletionMessageToolCall(id=tool_call['id'], function=function, type="function")
            text_content = next((text['text'] for text in response.content if 'text' in text), "")
            message = Message(content=text_content or None, tool_calls=[tool_call_obj], role="assistant", refusal=None)
            normalized_response.choices[0].message = message
        else:
            raise ValueError("Expected 'tool_use' content is missing or malformed in API response.")
    
    return normalized_response

This code will introduce robust checks and a clearer mapping and management of different stop_reasons, particularly focusing on the presence and structure of tool_use data.

Recommendation #2

To enhance the security of the transform_tool_call_to_openai method, implement a secure JSON handling strategy that prevents potential injection attacks. Here's a recommended approach using a custom JSON parser that includes validation:

def transform_tool_call_to_openai(self, response):
    if 'stopReason' in response and response['stopReason'] == "tool_use":
        try:
            tool_calls = []
            for content in response['output']['message']['content']:
                if 'toolUse' in content:
                    tool = content['toolUse']
                    tool_calls.append({
                        'type': 'function',
                        'id': tool['toolUseId'],
                        'function': {
                            'name': tool['name'],
                            'arguments': json.dumps(tool['input'], cls=SecureJSONEncoder)
                        }
                    })
            return {
                'role': 'assistant',
                'content': None,
                'tool_calls': tool_calls,
                'refusal': None
            }
        except json.JSONDecodeError as e:
            raise ValueError(f"JSON parsing error: {e}")
    return None

Implement SecureJSONEncoder by overriding the default method to sanitize and validate all incoming JSON data, ensuring it adheres to the expected format and is free from malicious injections.

[Configure settings at: Archie AI - Automated PR Review]

Copy link

dev-archie-ai-code-explain-pr bot commented Dec 11, 2024

PR Code Suggestions Summary ✨

CategorySuggestion
Maintainability
Change `finish_reason` property to use an explicit enum instead of a string literal to enforce better type safety and clarity.

File: aisuite/framework/choice.py L1-L6

Suggestion: Use enums explicitly rather than strings for finish_reason to improve code reliability and maintenance.

--- Original
+++ Improved
@@ -1,6 +1,11 @@
 from typing import Literal, Optional
+from enum import Enum
+
+class FinishReason(Enum):
+    STOP = "stop"
+    TOOL_CALLS = "tool_calls"
 
 class Choice:
     def __init__(self):
         self.message = Message()
-        self.finish_reason: Optional[Literal["stop", "tool_calls"]] = None+        self.finish_reason: Optional[FinishReason] = None
Maintainability
Refactor the transformation of tool messages into a separate function for clarity and potential reuse.

File: aisuite/providers/anthropic_provider.py L100-L107

Suggestion: Separate transformation logic into dedicated functions for better modularity and reuse.

--- Original
+++ Improved
@@ -1,14 +1,15 @@
+def convert_tool_message(msg):
+    return {
+        "role": "user",
+        "content": [
+            {
+                "type": "tool_result",
+                "tool_use_id": msg["tool_call_id"],
+                "content": msg["content"],
+            }
+        ],
+    }
+
 for msg in messages:
-    if isinstance(msg, dict):
-        if msg["role"] == "tool":
-            converted_msg = {
-                "role": "user",
-                "content": [
-                    {
-                        "type": "tool_result",
-                        "tool_use_id": msg["tool_call_id"],
-                        "content": msg["content"],
-                    }
-                ],
-            }
-            converted_messages.append(converted_msg)+    if isinstance(msg, dict) and msg["role"] == "tool":
+        converted_messages.append(convert_tool_message(msg))

Comment on lines 1 to 6
from aisuite.framework.message import Message
from typing import Literal, Optional


class Choice:
def __init__(self):

Choose a reason for hiding this comment

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

Change finish_reason property to use an explicit enum instead of a string literal to enforce better type safety and clarity.

Use enums explicitly rather than strings for finish_reason to improve code reliability and maintenance.

Suggested change
from aisuite.framework.message import Message
from typing import Literal, Optional
class Choice:
def __init__(self):
from typing import Literal, Optional
from enum import Enum
class FinishReason(Enum):
STOP = "stop"
TOOL_CALLS = "tool_calls"
class Choice:
def __init__(self):
self.message = Message()
self.finish_reason: Optional[FinishReason] = None

Comment on lines +100 to +107
"name": tool_call.function.name,
"input": json.loads(tool_call.function.arguments),
}
)
converted_messages.append({"role": "assistant", "content": content})
else:
converted_messages.append(
{"role": msg.role, "content": msg.content}

Choose a reason for hiding this comment

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

Refactor the transformation of tool messages into a separate function for clarity and potential reuse.

Separate transformation logic into dedicated functions for better modularity and reuse.

Suggested change
"name": tool_call.function.name,
"input": json.loads(tool_call.function.arguments),
}
)
converted_messages.append({"role": "assistant", "content": content})
else:
converted_messages.append(
{"role": msg.role, "content": msg.content}
def convert_tool_message(msg):
return {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": msg["tool_call_id"],
"content": msg["content"],
}
],
}
for msg in messages:
if isinstance(msg, dict) and msg["role"] == "tool":
converted_messages.append(convert_tool_message(msg))

@dev-archie-ai-code-explain-pr dev-archie-ai-code-explain-pr bot changed the title Enhanced webhook handling with expanded message framework and tool call integration Introduce Enhanced Message Handling and Tool Integration Dec 11, 2024

PR Review Summary Celebratory GIF

Overall Review:

This PR makes necessary changes to the webhook handling logic.


🌟 Code Quality And Design

1. [Consider] Good error handling implementation

Recommendations

Recommendation #1

No changes needed

[Configure settings at: Archie AI - Automated PR Review]

1 similar comment

PR Review Summary Celebratory GIF

Overall Review:

This PR makes necessary changes to the webhook handling logic.


🌟 Code Quality And Design

1. [Consider] Good error handling implementation

Recommendations

Recommendation #1

No changes needed

[Configure settings at: Archie AI - Automated PR Review]

@dev-archie-ai-code-explain-pr dev-archie-ai-code-explain-pr bot changed the title Introduce Enhanced Message Handling and Tool Integration Enhance webhook handling logic with improved error handling Dec 11, 2024

PR Review Summary Celebratory GIF

Overall Review:

Based on a thorough review, this PR introduces substantial enhancements aimed at bridging various AI provider interfaces through more sophisticated handling of messages and tool calls within the aisuite. It employs refined Message structuring with Pydantic models and a centralized ToolManager for managing tool interactions. These changes potentially bolster the suite's flexibility and interaction capabilities with external tools, offering streamlined and standardized messaging structures.

However, the extensive nature of these modifications warrants a critical analysis to ensure that the new functionalities integrate well without introducing regressions or vulnerabilities in the current system. Specific attention must be paid to the transformation and normalization processes introduced, especially those involving JSON data handling and interactive message conversions between different AI providers.


Logical Error Analysis

1. [Blocker] In `AnthropicProvider`, the `normalize_response` method might suffer from unreliable handling of `tool_call_id` due to assumptions about incoming data formats. This could lead to exceptions or data integrity issues. Recommend ensuring robust parsing and validation of these identifiers before usage.

🔒 Security Analysis

2. [Blocker] The methods `transform_tool_call_to_openai` and JSON handling in APIs involve sensitive data parsing which requires strict schema validations or error handling to mitigate potential injection attacks or corruption of data formats.

Recommendations

Recommendation #1

To address the potential inconsistencies and errors in handling the tool_call_id, it is recommended to implement a more robust data validation process within the normalize_response method. Specifically, you should add checks to ensure that all expected data fields are present and properly formatted before attempting to use them. Here is an example modification for the AnthropicProvider code:

def normalize_response(self, response):
    # Initial response normalization setup here

    # Check if the tool_call_id exists and is correctly formatted
    if 'tool_call_id' not in response or not isinstance(response['tool_call_id'], str):
        raise ValueError("Invalid or missing 'tool_call_id' in the response")

    # Further processing using tool_call_id
    # ...

    return normalized_response

These checks will help you avoid runtime errors and ensure the integrity of the data handling process.

Recommendation #2

It is crucial to implement rigorous validation mechanisms for JSON data being parsed in the transform_tool_call_to_openai methods across various providers. Ensure that all incoming JSON data is thoroughly validated against a schema before being processed. Here's how you can improve the security of these methods:

import json
from jsonschema import validate, ValidationError

# Define a schema for your expected tool call JSON structure
tool_call_schema = {
    "type": "object",
    "properties": {
        "id": {"type": "string"},
        "function": {
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "arguments": {"type": "string"}
            },
            "required": ["name", "arguments"]
        }
    },
    "required": ["id", "function"]
}

def transform_tool_call_to_openai(self, response):
    try:
        tool_calls = []
        for content in response['output']['message']['content']:
            if 'toolUse' in content:
                # Validate the JSON structure before processing
                validate(instance=content['toolUse'], schema=tool_call_schema)

                tool_calls.append(
                    {
                        "type": "function",
                        "id": content['toolUse']['toolUseId'],
                        "function": {
                            "name": content['toolUse']['name'],
                            "arguments": json.dumps(content['toolUse']['input'])
                        }
                    }
                )
        return {
            "role": "assistant",
            "content": None,
            "tool_calls": tool_calls,
            "refusal": None
        }
    except ValidationError as e:
        raise ValueError(f"JSON validation error: {str(e)}")

This modification uses the jsonschema library to validate JSON data against a predefined schema. This approach helps to prevent common vulnerabilities associated with unvalidated input.

[Configure settings at: Archie AI - Automated PR Review]

Copy link

dev-archie-ai-code-explain-pr bot commented Dec 12, 2024

PR Code Suggestions Summary ✨

CategorySuggestion
Maintainability
Improves type safety by specifying the `List` type from `typing` for a variable.

File: aisuite/framework/message.py L21-L21

Suggestion: Use more specific type hints for list or dict types to enhance type safety and clarity.

--- Original
+++ Improved
@@ -1 +1,2 @@
-tool_calls: Optional[list[ChatCompletionMessageToolCall]]+from typing import List
+tool_calls: Optional[List[ChatCompletionMessageToolCall]]
Security
Adds safety checks to prevent KeyError when accessing dictionary keys.

File: aisuite/framework/message.py L198-L198

Suggestion: Ensure the dictionary keys in JSON operations are checked to prevent KeyError before accessing them.

--- Original
+++ Improved
@@ -1 +1 @@
-name=tool_call["function"]["name"]+name=tool_call.get("function", {}).get("name", "default_name")
Performance
Replaces `print` statements with `logging` for enhanced production appropriateness and control over logging levels.

File: aisuite/providers/anthropic_provider.py LNone-LNone

Suggestion: Replace print statements with logging for better production-grade logging capabilities.

--- Original
+++ Improved
@@ -1,2 +1,3 @@
-print(converted_messages)
-print(response)+import logging
+logging.debug("Converted messages: %s", converted_messages)
+logging.debug("Response: %s", response)
Performance
Converts a loop that appends to a list into a list comprehension for better efficiency and readability.

File: aisuite/providers/anthropic_provider.py LNone-LNone

Suggestion: Use bulk list operations for more efficient and readable code.

--- Original
+++ Improved
@@ -1,7 +1,6 @@
-for tool_call in msg["tool_calls"]:
-    content.append({
-        "type": "tool_use",
-        "id": tool_call["id"],
-        "name": tool_call["function"]["name"],
-        "input": json.loads(tool_call["function"]["arguments"]),
-    })+content.extend([{
+    "type": "tool_use",
+    "id": tool_call["id"],
+    "name": tool_call["function"]["name"],
+    "input": json.loads(tool_call["function"]["arguments"]),
+} for tool_call in msg["tool_calls"]])
Security
Adds error handling to external API calls for better stability and error management.

File: aisuite/providers/aws_provider.py LNone-LNone

Suggestion: Implement error handling for external API calls to manage unexpected outcomes and maintain system stability.

--- Original
+++ Improved
@@ -1,8 +1,12 @@
-response = self.client.converse(
-    modelId=model,  # baseModelId or provisionedModelArn
-    messages=formatted_messages,
-    system=system_message,
-    inferenceConfig=inference_config,
-    additionalModelRequestFields=additional_model_request_fields,
-    toolConfig=tool_config,
-)+try:
+    response = self.client.converse(
+        modelId=model,  # baseModelId or provisionedModelArn
+        messages=formatted_messages,
+        system=system_message,
+        inferenceConfig=inference_config,
+        additionalModelRequestFields=additional_model_request_fields,
+        toolConfig=tool_config,
+    )
+except Exception as e:
+    logging.error("Failed to call Bedrock API: %s", e)
+    raise

class Message(BaseModel):
content: Optional[str]
tool_calls: Optional[list[ChatCompletionMessageToolCall]]
role: Optional[Literal["user", "assistant", "system"]]

Choose a reason for hiding this comment

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

Improves type safety by specifying the List type from typing for a variable.

Use more specific type hints for list or dict types to enhance type safety and clarity.

Suggested change
role: Optional[Literal["user", "assistant", "system"]]
from typing import List
tool_calls: Optional[List[ChatCompletionMessageToolCall]]

@dev-archie-ai-code-explain-pr dev-archie-ai-code-explain-pr bot changed the title Enhance webhook handling logic with improved error handling Refactor and enhance webhook handling logic across multiple providers Dec 12, 2024

PR Review Summary Celebratory GIF

Overall Review:

This PR introduces a series of enhancements focusing on improving message handling and integrating tool call functionalities within the aisuite. It targets a unified approach to message structuring and API interactions across different AI providers such as AWS and Anthropic. The changes revolve significantly around the utilization of a ToolManager and adaptations in the Message class alongside provider-specific adaptations. This effort aims to streamline interactions and extend the suite’s capabilities to manage external tool interactions efficiently. However, the breadth and depth of changes introduced necessitate extensive validation to confirm that they integrate seamlessly without adverse effects on existing functionalities.


Logical Error Analysis

1. [Blocker] In `AnthropicProvider`, `normalize_response` tries to handle different `stop_reason`s including `tool_use`. However, `tool_call_id` and method to retrieve `content` might not reliably parse the expected format every time. This can lead to unhandled exceptions or missing data.

🔒 Security Analysis

2. [Blocker] The methods `transform_tool_call_to_openai` involve sensitive data parsing which requires strict schema validations or error handling to mitigate potential injection attacks or corruption of data formats.

Recommendations

Recommendation #1

For robust error handling and parsing in AnthropicProvider, consider adding checks for data consistency before processing. Here's an example modification in normalize_response:

def normalize_response(self, response):
    normalized_response = ChatCompletionResponse()
    finish_reason_mapping = {
        "end_turn": "stop",
        "max_tokens": "length",
        "tool_use": "tool_calls",
    }
    normalized_response.choices[0].finish_reason = finish_reason_mapping.get(response.stop_reason, "stop")

    if response.stop_reason == "tool_use":
        tool_call = next((item for item in response.content if item.type == "tool_use"), None)
        if tool_call and 'id' in tool_call:
            function = Function(name=tool_call.name, arguments=json.dumps(tool_call.input))
            tool_call_obj = ChatCompletionMessageToolCall(id=tool_call.id, function=function, type="function")
            text_content = next((item.text for item in response.content if item.type == "text"), "")
            message = Message(content=text_content or None, tool_calls=[tool_call_obj] if tool_call else None, role="assistant", refusal=None)
            normalized_response.choices[0].message = message
        else:
            raise ValueError("Expected 'tool_use' content is missing or malformed in API response.")

    return normalized_response

This ensures that every potential 'tool_use' is validated before processing to prevent errors related to missing or inconsistent data.

Recommendation #2

Implement structural and secure JSON parsing methods to handle data within transform_tool_call_to_openai responsibly. Here’s a potential implementation:

def transform_tool_call_to_openai(self, response):
    if response.get("stopReason") != "tool_use":
        return None

    tool_calls = []
    try:
        for content in response['output']['message']['content']:
            if 'toolUse' in content:
                tool = content['toolUse']
                tool_calls.append({
                    'type': 'function',
                    'id': tool['toolUseId'],
                    'function': {
                        'name': tool['name'],
                        'arguments': json.dumps(tool['input'], cls=SecureJSONEncoder),
                    }
                })
    except json.JSONDecodeError as e:
        raise ValueError(f'JSON parsing error: {str(e)}')

    return {
        'role': 'assistant',
        'content': None,
        'tool_calls': tool_calls,
        'refusal': None
    }

class SecureJSONEncoder(json.JSONEncoder):
    def encode(self, o):
        result = super(SecureJSONEncoder, self).encode(o)
        # Add additional encoding rules if necessary
        return result

This tries to ensure that JSON parsing is handled securely, avoiding common vulnerabilities associated with processing unvalidated input.

[Configure settings at: Archie AI - Automated PR Review]

Copy link

dev-archie-ai-code-explain-pr bot commented Dec 12, 2024

PR Code Suggestions Summary ✨

CategorySuggestion
Possible issue
Added error handling for potential failures in JSON parsing.

File: aisuite/providers/anthropic_provider.py L103-L103

Suggestion: Add exception handling for JSON parsing operations.
Existing code:

input: json.loads(tool_call["function"]["arguments"]),

Improved code:

try:
    input: json.loads(tool_call["function"]["arguments"])
except json.JSONDecodeError:
    input = {}  # Handle or log the error appropriately
--- Original
+++ Improved
@@ -1 +1,4 @@
-input: json.loads(tool_call["function"]["arguments"]),+try:
+    input = json.loads(tool_call["function"]["arguments"])
+except json.JSONDecodeError:
+    input = {}  # Handle or log the error appropriately
Enhancement
Modify data structures to enforce immutability where applicable.

File: aisuite/framework/message.py L20-L20

Suggestion: Ensure immutability and constancy where appropriate by using tuple for non-modifiable data.
Existing code:

tool_calls: Optional[list[ChatCompletionMessageToolCall]]

Improved code:

tool_calls: Optional[tuple[ChatCompletionMessageToolCall, ...]]

```diff
--- Original
+++ Improved
@@ -1 +1 @@
-tool_calls: Optional[list[ChatCompletionMessageToolCall]]+tool_calls: Optional[tuple[ChatCompletionMessageToolCall, ...]]

"name": tool_call.function.name,
"input": json.loads(tool_call.function.arguments),
}
)

Choose a reason for hiding this comment

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

Added error handling for potential failures in JSON parsing.

Add exception handling for JSON parsing operations.
Existing code:

input: json.loads(tool_call["function"]["arguments"]),

Improved code:

try:
    input: json.loads(tool_call["function"]["arguments"])
except json.JSONDecodeError:
    input = {}  # Handle or log the error appropriately
Suggested change
)
try:
input = json.loads(tool_call["function"]["arguments"])
except json.JSONDecodeError:
input = {} # Handle or log the error appropriately


class Message(BaseModel):
content: Optional[str]
tool_calls: Optional[list[ChatCompletionMessageToolCall]]

Choose a reason for hiding this comment

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

Modify data structures to enforce immutability where applicable.

Ensure immutability and constancy where appropriate by using tuple for non-modifiable data.
Existing code:

tool_calls: Optional[list[ChatCompletionMessageToolCall]]

Improved code:

tool_calls: Optional[tuple[ChatCompletionMessageToolCall, ...]] 
```suggestion
    tool_calls: Optional[tuple[ChatCompletionMessageToolCall, ...]]

@dev-archie-ai-code-explain-pr dev-archie-ai-code-explain-pr bot changed the title Refactor and enhance webhook handling logic across multiple providers Enhanced messaging in AI suite with refined webhook logic for multiple providers Dec 12, 2024

PR Review Summary Celebratory GIF

Overall Review:

This PR makes necessary changes to the webhook handling logic.


🌟 Code Quality And Design

1. [Consider] Good error handling implementation

Recommendations

Recommendation #1

No changes needed

[Configure settings at: Archie AI - Automated PR Review]

@dev-archie-ai-code-explain-pr dev-archie-ai-code-explain-pr bot changed the title Enhanced messaging in AI suite with refined webhook logic for multiple providers Enhance webhook handling logic with improved error handling Dec 12, 2024

PR Review Summary Celebratory GIF

Overall Review:

This PR introduces substantial enhancements to the aisuite by implementing structured message handling and tool calling capabilities across multiple AI providers. The changes include a new Pydantic-based Message class, provider-specific transformations for tool calls, and a ToolManager utility. While the implementation shows good architecture decisions like using Pydantic for validation and separating provider-specific logic, there are critical concerns around error handling in JSON transformations and potential security vulnerabilities in data parsing that need addressing. The standardization of message formats and tool call handling across providers is a positive architectural decision.


🔒 Security Analysis

1. [Blocker] The methods `transform_tool_call_to_openai` and JSON handling in APIs involve sensitive data parsing which requires strict schema validations or error handling to mitigate potential injection attacks or corruption of data formats.

Logical Error Analysis

2. [Blocker] In `AnthropicProvider`, `normalize_response` tries to handle different `stop_reason`s including `tool_use`. However, `tool_call_id` and method to retrieve `content` might not reliably parse the expected format every time. This can lead to unhandled exceptions or missing data.

🌟 Code Quality And Design

3. [Consider] The use of Pydantic for message validation is a strong choice, providing built-in type checking and data validation that will catch issues early in the development process.

Recommendations

Recommendation #1

Implement secure JSON parsing with validation in transform_tool_call_to_openai:

def transform_tool_call_to_openai(self, response):
    if response.get("stopReason") != "tool_use":
        return None

    try:
        tool_calls = []
        for content in response["output"]["message"]["content"]:
            if "toolUse" in content:
                tool = content["toolUse"]
                # Validate input against schema before processing
                validated_input = json.loads(tool["input"], cls=SecureJSONDecoder)
                tool_calls.append({
                    "type": "function",
                    "id": tool["toolUseId"],
                    "function": {
                        "name": tool["name"],
                        "arguments": json.dumps(validated_input),
                    },
                })
    except json.JSONDecodeError as e:
        raise ValueError(f"JSON parsing error: {str(e)}")

    return {
        "role": "assistant",
        "content": None,
        "tool_calls": tool_calls,
        "refusal": None,
    }
Recommendation #2

Add robust error handling and validation in normalize_response:

def normalize_response(self, response):
    normalized_response = ChatCompletionResponse()
    finish_reason_mapping = {
        "end_turn": "stop",
        "max_tokens": "length",
        "tool_use": "tool_calls",
    }
    normalized_response.choices[0].finish_reason = finish_reason_mapping.get(response.stop_reason, "stop")
   
    if response.stop_reason == "tool_use":
        tool_call = next((content for content in response.content if 'type' in content and content.type == "tool_use"), None)
        if tool_call and 'id' in tool_call:
            tool_call_id = tool_call.id
            function = Function(name=tool_call.name, arguments=json.dumps(tool_call.input))
            tool_call_obj = ChatCompletionMessageToolCall(id=tool_call_id, function=function, type="function")
            text_content = next((content.text for content in response.content if 'type' in content and content.type == "text"), "")
            message = Message(content=text_content or None, tool_calls=[tool_call_obj] if tool_call else None, role="assistant", refusal=None)
            normalized_response.choices[0].message = message
        else:
            raise ValueError("Expected 'tool_use' content is missing or malformed in API response.")
    
    return normalized_response
Recommendation #3

Continue leveraging Pydantic's validation capabilities throughout the codebase, especially in areas dealing with external data. Consider adding custom validators where needed:

from pydantic import BaseModel, validator

class Message(BaseModel):
    content: Optional[str]
    tool_calls: Optional[list[ChatCompletionMessageToolCall]]
    role: Optional[Literal["user", "assistant", "system"]]
    refusal: Optional[str]

    @validator('tool_calls')
    def validate_tool_calls(cls, v):
        if v is not None:
            for tool_call in v:
                if not tool_call.function or not tool_call.function.name:
                    raise ValueError("Tool call must have a function name")
        return v
[Configure settings at: Archie AI - Automated PR Review]

Copy link

dev-archie-ai-code-explain-pr bot commented Dec 26, 2024

PR Code Suggestions Summary ✨

CategorySuggestion
Possible issue
Add comprehensive error handling for tool execution to prevent crashes and provide meaningful error messages

File: aisuite/utils/tool_manager.py L142-L152

Suggestion: Add error handling for tool execution to prevent crashes and provide meaningful error messages

--- Original
+++ Improved
@@ -1,11 +1,23 @@
-validated_args = param_model(**arguments)
-            result = tool_func(**validated_args.model_dump())
-            results.append(result)
-            messages.append(
-                {
-                    "role": "tool",
-                    "name": tool_name,
-                    "content": json.dumps(result),
-                    "tool_call_id": tool_call.id,  # Include the tool call ID in the response
-                }
-            )+try:
+                validated_args = param_model(**arguments)
+                result = tool_func(**validated_args.model_dump())
+                results.append(result)
+                messages.append(
+                    {
+                        "role": "tool",
+                        "name": tool_name,
+                        "content": json.dumps(result),
+                        "tool_call_id": tool_call.id,
+                    }
+                )
+            except Exception as e:
+                error_result = {"error": f"Tool execution failed: {str(e)}"}
+                results.append(error_result)
+                messages.append(
+                    {
+                        "role": "tool",
+                        "name": tool_name,
+                        "content": json.dumps(error_result),
+                        "tool_call_id": tool_call.id,
+                    }
+                )
Possible issue
Add null check and error handling for JSON parsing in tool calls

File: aisuite/providers/anthropic_provider.py L65-L65

Suggestion: Add error handling for JSON parsing in tool calls to prevent crashes on invalid JSON

--- Original
+++ Improved
@@ -1 +1 @@
-"input": json.loads(tool_call["function"]["arguments"]),+"input": json.loads(tool_call["function"]["arguments"]) if tool_call["function"]["arguments"] else {},
Enhancement
Add rate limiting protection with exponential backoff to handle AWS API throttling

File: aisuite/providers/aws_provider.py L104-L111

Suggestion: Add rate limiting protection to prevent API throttling

--- Original
+++ Improved
@@ -1,8 +1,20 @@
-response = self.client.converse(
-            modelId=model,  # baseModelId or provisionedModelArn
-            messages=formatted_messages,
-            system=system_message,
-            inferenceConfig=inference_config,
-            additionalModelRequestFields=additional_model_request_fields,
-            toolConfig=tool_config,
-        )+from time import sleep
+        max_retries = 3
+        retry_count = 0
+        while retry_count < max_retries:
+            try:
+                response = self.client.converse(
+                    modelId=model,  # baseModelId or provisionedModelArn
+                    messages=formatted_messages,
+                    system=system_message,
+                    inferenceConfig=inference_config,
+                    additionalModelRequestFields=additional_model_request_fields,
+                    toolConfig=tool_config,
+                )
+                break
+            except Exception as e:
+                if "ThrottlingException" in str(e) and retry_count < max_retries - 1:
+                    sleep(2 ** retry_count)  # Exponential backoff
+                    retry_count += 1
+                else:
+                    raise
Possible issue
Add robust error handling for missing or invalid response fields during normalization

File: aisuite/providers/anthropic_provider.py L94-L96

Suggestion: Add error handling for missing or invalid response fields in normalize_response

--- Original
+++ Improved
@@ -1,3 +1,7 @@
-normalized_response.choices[0].finish_reason = finish_reason_mapping.get(
-            response.stop_reason, "stop"
-        )+try:
+            normalized_response.choices[0].finish_reason = finish_reason_mapping.get(
+                getattr(response, 'stop_reason', None), "stop"
+            )
+        except (AttributeError, IndexError) as e:
+            normalized_response.choices[0].finish_reason = "stop"
+            print(f"Warning: Error normalizing response: {str(e)}")
Maintainability
Add validation to prevent duplicate tool registration and ensure tools are callable

File: aisuite/utils/tool_manager.py L11-L12

Suggestion: Add validation for tool registration to prevent duplicate tools

--- Original
+++ Improved
@@ -1,2 +1,6 @@
 def add_tool(self, func: Callable, param_model: Optional[Type[BaseModel]] = None):
-        """Register a tool function with metadata. If no param_model is provided, infer from function signature."""+        """Register a tool function with metadata. If no param_model is provided, infer from function signature."""
+        if func.__name__ in self._tools:
+            raise ValueError(f"Tool with name '{func.__name__}' is already registered")
+        if not callable(func):
+            raise TypeError("Tool must be a callable function")
Possible issue
Add type validation for message conversion to catch type errors early

File: aisuite/providers/aws_provider.py L91-L93

Suggestion: Add type validation for message conversion to prevent runtime errors

--- Original
+++ Improved
@@ -1,3 +1,5 @@
-message_dict = (
+if not isinstance(message, (dict, BaseModel)):
+                raise TypeError(f"Message must be a dict or BaseModel, got {type(message)}")
+            message_dict = (
                 message.model_dump() if hasattr(message, "model_dump") else message
             )
Possible issue
Add input validation for tool execution to prevent invalid tool calls

File: aisuite/utils/tool_manager.py L125-L126

Suggestion: Add parameter validation for tool execution to prevent invalid tool calls

--- Original
+++ Improved
@@ -1,2 +0,0 @@
-def execute_tool(self, tool_calls) -> tuple[list, list]:
-        """Executes registered tools based on the tool calls from the model.
Performance
Optimize message conversion using a mapping dictionary for better performance

File: aisuite/providers/anthropic_provider.py L40-L53

Suggestion: Add performance optimization for message conversion

--- Original
+++ Improved
@@ -1,16 +1,18 @@
 converted_messages = []
+        msg_conversion_map = {
+            "tool": lambda m: {
+                "role": "user",
+                "content": [
+                    {
+                        "type": "tool_result",
+                        "tool_use_id": m["tool_call_id"],
+                        "content": m["content"],
+                    }
+                ],
+            }
+        }
         for msg in messages:
             if isinstance(msg, dict):
-                if msg["role"] == "tool":
-                    # Convert tool result message
-                    converted_msg = {
-                        "role": "user",
-                        "content": [
-                            {
-                                "type": "tool_result",
-                                "tool_use_id": msg["tool_call_id"],
-                                "content": msg["content"],
-                            }
-                        ],
-                    }
-                    converted_messages.append(converted_msg)+                converter = msg_conversion_map.get(msg["role"])
+                if converter:
+                    converted_messages.append(converter(msg))

Comment on lines +142 to +152
for tool_call in tool_calls:
tool_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)

if tool_name not in self._tools:
raise ValueError(f"Tool '{tool_name}' not registered.")

tool = self._tools[tool_name]
tool_func = tool["function"]
param_model = tool["param_model"]

Choose a reason for hiding this comment

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

Add comprehensive error handling for tool execution to prevent crashes and provide meaningful error messages

Add error handling for tool execution to prevent crashes and provide meaningful error messages

Suggested change
for tool_call in tool_calls:
tool_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
if tool_name not in self._tools:
raise ValueError(f"Tool '{tool_name}' not registered.")
tool = self._tools[tool_name]
tool_func = tool["function"]
param_model = tool["param_model"]
try:
validated_args = param_model(**arguments)
result = tool_func(**validated_args.model_dump())
results.append(result)
messages.append(
{
"role": "tool",
"name": tool_name,
"content": json.dumps(result),
"tool_call_id": tool_call.id,
}
)
except Exception as e:
error_result = {"error": f"Tool execution failed: {str(e)}"}
results.append(error_result)
messages.append(
{
"role": "tool",
"name": tool_name,
"content": json.dumps(error_result),
"tool_call_id": tool_call.id,
}
)

content.append(
{
"type": "tool_use",
"id": tool_call["id"],

Choose a reason for hiding this comment

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

Add null check and error handling for JSON parsing in tool calls

Add error handling for JSON parsing in tool calls to prevent crashes on invalid JSON

Suggested change
"id": tool_call["id"],
"input": json.loads(tool_call["function"]["arguments"]) if tool_call["function"]["arguments"] else {},

Comment on lines +104 to +111
formatted_messages.append(bedrock_message)
elif message_dict["role"] == "assistant":
# Convert assistant message to Bedrock format
bedrock_message = self.transform_assistant_to_bedrock(message_dict)
if bedrock_message:
formatted_messages.append(bedrock_message)
else:
formatted_messages.append(

Choose a reason for hiding this comment

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

Add rate limiting protection with exponential backoff to handle AWS API throttling

Add rate limiting protection to prevent API throttling

Suggested change
formatted_messages.append(bedrock_message)
elif message_dict["role"] == "assistant":
# Convert assistant message to Bedrock format
bedrock_message = self.transform_assistant_to_bedrock(message_dict)
if bedrock_message:
formatted_messages.append(bedrock_message)
else:
formatted_messages.append(
from time import sleep
max_retries = 3
retry_count = 0
while retry_count < max_retries:
try:
response = self.client.converse(
modelId=model, # baseModelId or provisionedModelArn
messages=formatted_messages,
system=system_message,
inferenceConfig=inference_config,
additionalModelRequestFields=additional_model_request_fields,
toolConfig=tool_config,
)
break
except Exception as e:
if "ThrottlingException" in str(e) and retry_count < max_retries - 1:
sleep(2 ** retry_count) # Exponential backoff
retry_count += 1
else:
raise

Comment on lines +94 to +96
content.append({"type": "text", "text": msg.content})
for tool_call in msg.tool_calls:
content.append(

Choose a reason for hiding this comment

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

Add robust error handling for missing or invalid response fields during normalization

Add error handling for missing or invalid response fields in normalize_response

Suggested change
content.append({"type": "text", "text": msg.content})
for tool_call in msg.tool_calls:
content.append(
try:
normalized_response.choices[0].finish_reason = finish_reason_mapping.get(
getattr(response, 'stop_reason', None), "stop"
)
except (AttributeError, IndexError) as e:
normalized_response.choices[0].finish_reason = "stop"
print(f"Warning: Error normalizing response: {str(e)}")

Comment on lines +11 to +12
# Add a tool function with or without a Pydantic model.
def add_tool(self, func: Callable, param_model: Optional[Type[BaseModel]] = None):

Choose a reason for hiding this comment

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

Add validation to prevent duplicate tool registration and ensure tools are callable

Add validation for tool registration to prevent duplicate tools

Suggested change
# Add a tool function with or without a Pydantic model.
def add_tool(self, func: Callable, param_model: Optional[Type[BaseModel]] = None):
def add_tool(self, func: Callable, param_model: Optional[Type[BaseModel]] = None):
"""Register a tool function with metadata. If no param_model is provided, infer from function signature."""
if func.__name__ in self._tools:
raise ValueError(f"Tool with name '{func.__name__}' is already registered")
if not callable(func):
raise TypeError("Tool must be a callable function")

Comment on lines +91 to +93
# Convert Message object to dict if necessary
message_dict = (
message.model_dump() if hasattr(message, "model_dump") else message

Choose a reason for hiding this comment

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

Add type validation for message conversion to catch type errors early

Add type validation for message conversion to prevent runtime errors

Suggested change
# Convert Message object to dict if necessary
message_dict = (
message.model_dump() if hasattr(message, "model_dump") else message
if not isinstance(message, (dict, BaseModel)):
raise TypeError(f"Message must be a dict or BaseModel, got {type(message)}")
message_dict = (
message.model_dump() if hasattr(message, "model_dump") else message
)

Comment on lines +125 to +126

def execute_tool(self, tool_calls) -> tuple[list, list]:

Choose a reason for hiding this comment

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

Add input validation for tool execution to prevent invalid tool calls

Add parameter validation for tool execution to prevent invalid tool calls

Suggested change
def execute_tool(self, tool_calls) -> tuple[list, list]:

Comment on lines +40 to +53
converted_messages = []
for msg in messages:
if isinstance(msg, dict):
if msg["role"] == "tool":
# Convert tool result message
converted_msg = {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": msg["tool_call_id"],
"content": msg["content"],
}
],

Choose a reason for hiding this comment

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

Optimize message conversion using a mapping dictionary for better performance

Add performance optimization for message conversion

Suggested change
converted_messages = []
for msg in messages:
if isinstance(msg, dict):
if msg["role"] == "tool":
# Convert tool result message
converted_msg = {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": msg["tool_call_id"],
"content": msg["content"],
}
],
converted_messages = []
msg_conversion_map = {
"tool": lambda m: {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": m["tool_call_id"],
"content": m["content"],
}
],
}
}
for msg in messages:
if isinstance(msg, dict):
converter = msg_conversion_map.get(msg["role"])
if converter:
converted_messages.append(converter(msg))

PR Review Summary Celebratory GIF

Overall Review:

This PR introduces significant enhancements to the aisuite by implementing a structured message handling system and tool calling capabilities across multiple AI providers (AWS, Anthropic, etc.). The changes include a new Message class using Pydantic for validation, provider-specific message transformations, and a ToolManager for handling tool registrations and executions. While the implementation shows good architecture decisions like using Pydantic for validation and separating concerns through the ToolManager, there are critical areas around error handling and security that need attention.


🔒 Security Analysis

1. [Blocker] The methods `transform_tool_call_to_openai` and JSON handling in APIs involve sensitive data parsing which requires strict schema validations or error handling to mitigate potential injection attacks or corruption of data formats.

Logical Error Analysis

2. [Blocker] In `AnthropicProvider`, `normalize_response` tries to handle different `stop_reasons` including `tool_use`. However, `tool_call_id` and method to retrieve `content` might not reliably parse the expected format every time. This can lead to unhandled exceptions or missing data.

🌟 Code Quality And Design

3. [Consider] The message transformation logic in provider classes (AWS, Anthropic) contains significant code duplication for handling different message types. Extract common transformation patterns into shared utility methods to improve maintainability and reduce redundancy.

4. [Positive] The use of Pydantic models for Message and related classes is a good choice as it provides built-in validation and clear type definitions, making the code more robust and self-documenting.

Recommendations

Recommendation #1

Implement a secure JSON parsing mechanism with schema validation:

class SecureJSONEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, str):
            return o.replace('\"', '\\\"').replace('<', '\\<')
        return json.JSONEncoder.default(self, o)

def transform_tool_call_to_openai(self, response):
    if response.get("stopReason") != "tool_use":
        return None

    try:
        tool_calls = []
        for content in response["output"]["message"]["content"]:
            if "toolUse" in content:
                tool = content["toolUse"]
                tool_calls.append({
                    "type": "function",
                    "id": tool["toolUseId"],
                    "function": {
                        "name": tool["name"],
                        "arguments": json.dumps(tool["input"], cls=SecureJSONEncoder),
                    },
                })
    except json.JSONDecodeError as e:
        raise ValueError(f"JSON parsing error: {str(e)}")

    return {
        "role": "assistant",
        "content": None,
        "tool_calls": tool_calls,
        "refusal": None,
    }
Recommendation #2

Add robust validation and error handling in the normalize_response method:

def normalize_response(self, response):
    normalized_response = ChatCompletionResponse()
    finish_reason_mapping = {
        "end_turn": "stop",
        "max_tokens": "length",
        "tool_use": "tool_calls",
    }
    normalized_response.choices[0].finish_reason = finish_reason_mapping.get(response.stop_reason, "stop")
   
    if response.stop_reason == "tool_use":
        tool_call = next((content for content in response.content if 'type' in content and content.type == "tool_use"), None)
        if tool_call and 'id' in tool_call:
            tool_call_id = tool_call.id
            function = Function(name=tool_call.name, arguments=json.dumps(tool_call.input))
            tool_call_obj = ChatCompletionMessageToolCall(id=tool_call_id, function=function, type="function")
            text_content = next((content.text for content in response.content if 'type' in content and content.type == "text"), "")
            message = Message(content=text_content or None, tool_calls=[tool_call_obj] if tool_call else None, role="assistant", refusal=None)
            normalized_response.choices[0].message = message
        else:
            raise ValueError("Expected 'tool_use' content is missing or malformed in API response.")
    
    return normalized_response
Recommendation #3

Create a base transformer class that providers can extend:

class BaseMessageTransformer:
    def transform_tool_result(self, message_dict):
        raise NotImplementedError
        
    def transform_assistant_message(self, message_dict):
        raise NotImplementedError
        
    def transform_user_message(self, message_dict):
        return {
            "role": message_dict["role"],
            "content": message_dict["content"]
        }

class AnthropicMessageTransformer(BaseMessageTransformer):
    def transform_tool_result(self, message_dict):
        # Anthropic specific implementation
        pass

class AWSMessageTransformer(BaseMessageTransformer):
    def transform_tool_result(self, message_dict):
        # AWS specific implementation
        pass
Recommendation andrewyng#4

Continue using Pydantic models for data validation and consider extending their usage to other parts of the codebase where structured data validation is needed.

[Configure settings at: Archie AI - Automated PR Review]

Copy link

dev-archie-ai-code-explain-pr bot commented Dec 26, 2024

PR Code Suggestions Summary ✨

CategorySuggestion
Possible issue
Add comprehensive error handling for tool execution to prevent crashes and provide meaningful error messages

File: aisuite/utils/tool_manager.py L142-L152

Suggestion: Add error handling for tool execution to prevent crashes and provide meaningful error messages

--- Original
+++ Improved
@@ -1,11 +1,23 @@
-validated_args = param_model(**arguments)
-            result = tool_func(**validated_args.model_dump())
-            results.append(result)
-            messages.append(
-                {
-                    "role": "tool",
-                    "name": tool_name,
-                    "content": json.dumps(result),
-                    "tool_call_id": tool_call.id,  # Include the tool call ID in the response
-                }
-            )+try:
+                validated_args = param_model(**arguments)
+                result = tool_func(**validated_args.model_dump())
+                results.append(result)
+                messages.append(
+                    {
+                        "role": "tool",
+                        "name": tool_name,
+                        "content": json.dumps(result),
+                        "tool_call_id": tool_call.id,
+                    }
+                )
+            except Exception as e:
+                error_result = {"error": f"Tool execution failed: {str(e)}"}
+                results.append(error_result)
+                messages.append(
+                    {
+                        "role": "tool",
+                        "name": tool_name,
+                        "content": json.dumps(error_result),
+                        "tool_call_id": tool_call.id,
+                    }
+                )
Possible issue
Add null check and error handling for JSON parsing in tool calls

File: aisuite/providers/anthropic_provider.py L65-L65

Suggestion: Add error handling for JSON parsing in tool calls to prevent crashes on invalid JSON

--- Original
+++ Improved
@@ -1 +1 @@
-"input": json.loads(tool_call["function"]["arguments"]),+"input": json.loads(tool_call["function"]["arguments"]) if tool_call["function"]["arguments"] else {},
Enhancement
Add rate limiting protection with exponential backoff to handle AWS API throttling

File: aisuite/providers/aws_provider.py L104-L111

Suggestion: Add rate limiting protection to prevent API throttling

--- Original
+++ Improved
@@ -1,8 +1,20 @@
-response = self.client.converse(
-            modelId=model,  # baseModelId or provisionedModelArn
-            messages=formatted_messages,
-            system=system_message,
-            inferenceConfig=inference_config,
-            additionalModelRequestFields=additional_model_request_fields,
-            toolConfig=tool_config,
-        )+from time import sleep
+        max_retries = 3
+        retry_count = 0
+        while retry_count < max_retries:
+            try:
+                response = self.client.converse(
+                    modelId=model,  # baseModelId or provisionedModelArn
+                    messages=formatted_messages,
+                    system=system_message,
+                    inferenceConfig=inference_config,
+                    additionalModelRequestFields=additional_model_request_fields,
+                    toolConfig=tool_config,
+                )
+                break
+            except Exception as e:
+                if "ThrottlingException" in str(e) and retry_count < max_retries - 1:
+                    sleep(2 ** retry_count)  # Exponential backoff
+                    retry_count += 1
+                else:
+                    raise
Possible issue
Add robust error handling for missing or invalid response fields during normalization

File: aisuite/providers/anthropic_provider.py L94-L96

Suggestion: Add error handling for missing or invalid response fields in normalize_response

--- Original
+++ Improved
@@ -1,3 +1,7 @@
-normalized_response.choices[0].finish_reason = finish_reason_mapping.get(
-            response.stop_reason, "stop"
-        )+try:
+            normalized_response.choices[0].finish_reason = finish_reason_mapping.get(
+                getattr(response, 'stop_reason', None), "stop"
+            )
+        except (AttributeError, IndexError) as e:
+            normalized_response.choices[0].finish_reason = "stop"
+            print(f"Warning: Error normalizing response: {str(e)}")
Maintainability
Add validation to prevent duplicate tool registration and ensure tools are callable

File: aisuite/utils/tool_manager.py L11-L12

Suggestion: Add validation for tool registration to prevent duplicate tools

--- Original
+++ Improved
@@ -1,2 +1,6 @@
 def add_tool(self, func: Callable, param_model: Optional[Type[BaseModel]] = None):
-        """Register a tool function with metadata. If no param_model is provided, infer from function signature."""+        """Register a tool function with metadata. If no param_model is provided, infer from function signature."""
+        if func.__name__ in self._tools:
+            raise ValueError(f"Tool with name '{func.__name__}' is already registered")
+        if not callable(func):
+            raise TypeError("Tool must be a callable function")
Possible issue
Add type validation for message conversion to catch type errors early

File: aisuite/providers/aws_provider.py L91-L93

Suggestion: Add type validation for message conversion to prevent runtime errors

--- Original
+++ Improved
@@ -1,3 +1,5 @@
-message_dict = (
+if not isinstance(message, (dict, BaseModel)):
+                raise TypeError(f"Message must be a dict or BaseModel, got {type(message)}")
+            message_dict = (
                 message.model_dump() if hasattr(message, "model_dump") else message
             )
Possible issue
Add input validation for tool execution to prevent invalid tool calls

File: aisuite/utils/tool_manager.py L125-L126

Suggestion: Add parameter validation for tool execution to prevent invalid tool calls

--- Original
+++ Improved
@@ -1,2 +0,0 @@
-def execute_tool(self, tool_calls) -> tuple[list, list]:
-        """Executes registered tools based on the tool calls from the model.
Performance
Optimize message conversion using a mapping dictionary for better performance

File: aisuite/providers/anthropic_provider.py L40-L53

Suggestion: Add performance optimization for message conversion

--- Original
+++ Improved
@@ -1,16 +1,18 @@
 converted_messages = []
+        msg_conversion_map = {
+            "tool": lambda m: {
+                "role": "user",
+                "content": [
+                    {
+                        "type": "tool_result",
+                        "tool_use_id": m["tool_call_id"],
+                        "content": m["content"],
+                    }
+                ],
+            }
+        }
         for msg in messages:
             if isinstance(msg, dict):
-                if msg["role"] == "tool":
-                    # Convert tool result message
-                    converted_msg = {
-                        "role": "user",
-                        "content": [
-                            {
-                                "type": "tool_result",
-                                "tool_use_id": msg["tool_call_id"],
-                                "content": msg["content"],
-                            }
-                        ],
-                    }
-                    converted_messages.append(converted_msg)+                converter = msg_conversion_map.get(msg["role"])
+                if converter:
+                    converted_messages.append(converter(msg))

Comment on lines +142 to +152
for tool_call in tool_calls:
tool_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)

if tool_name not in self._tools:
raise ValueError(f"Tool '{tool_name}' not registered.")

tool = self._tools[tool_name]
tool_func = tool["function"]
param_model = tool["param_model"]

Choose a reason for hiding this comment

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

Add comprehensive error handling for tool execution to prevent crashes and provide meaningful error messages

Add error handling for tool execution to prevent crashes and provide meaningful error messages

Suggested change
for tool_call in tool_calls:
tool_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
if tool_name not in self._tools:
raise ValueError(f"Tool '{tool_name}' not registered.")
tool = self._tools[tool_name]
tool_func = tool["function"]
param_model = tool["param_model"]
try:
validated_args = param_model(**arguments)
result = tool_func(**validated_args.model_dump())
results.append(result)
messages.append(
{
"role": "tool",
"name": tool_name,
"content": json.dumps(result),
"tool_call_id": tool_call.id,
}
)
except Exception as e:
error_result = {"error": f"Tool execution failed: {str(e)}"}
results.append(error_result)
messages.append(
{
"role": "tool",
"name": tool_name,
"content": json.dumps(error_result),
"tool_call_id": tool_call.id,
}
)

content.append(
{
"type": "tool_use",
"id": tool_call["id"],

Choose a reason for hiding this comment

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

Add null check and error handling for JSON parsing in tool calls

Add error handling for JSON parsing in tool calls to prevent crashes on invalid JSON

Suggested change
"id": tool_call["id"],
"input": json.loads(tool_call["function"]["arguments"]) if tool_call["function"]["arguments"] else {},

Comment on lines +104 to +111
formatted_messages.append(bedrock_message)
elif message_dict["role"] == "assistant":
# Convert assistant message to Bedrock format
bedrock_message = self.transform_assistant_to_bedrock(message_dict)
if bedrock_message:
formatted_messages.append(bedrock_message)
else:
formatted_messages.append(

Choose a reason for hiding this comment

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

Add rate limiting protection with exponential backoff to handle AWS API throttling

Add rate limiting protection to prevent API throttling

Suggested change
formatted_messages.append(bedrock_message)
elif message_dict["role"] == "assistant":
# Convert assistant message to Bedrock format
bedrock_message = self.transform_assistant_to_bedrock(message_dict)
if bedrock_message:
formatted_messages.append(bedrock_message)
else:
formatted_messages.append(
from time import sleep
max_retries = 3
retry_count = 0
while retry_count < max_retries:
try:
response = self.client.converse(
modelId=model, # baseModelId or provisionedModelArn
messages=formatted_messages,
system=system_message,
inferenceConfig=inference_config,
additionalModelRequestFields=additional_model_request_fields,
toolConfig=tool_config,
)
break
except Exception as e:
if "ThrottlingException" in str(e) and retry_count < max_retries - 1:
sleep(2 ** retry_count) # Exponential backoff
retry_count += 1
else:
raise

Comment on lines +94 to +96
content.append({"type": "text", "text": msg.content})
for tool_call in msg.tool_calls:
content.append(

Choose a reason for hiding this comment

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

Add robust error handling for missing or invalid response fields during normalization

Add error handling for missing or invalid response fields in normalize_response

Suggested change
content.append({"type": "text", "text": msg.content})
for tool_call in msg.tool_calls:
content.append(
try:
normalized_response.choices[0].finish_reason = finish_reason_mapping.get(
getattr(response, 'stop_reason', None), "stop"
)
except (AttributeError, IndexError) as e:
normalized_response.choices[0].finish_reason = "stop"
print(f"Warning: Error normalizing response: {str(e)}")

Comment on lines +11 to +12
# Add a tool function with or without a Pydantic model.
def add_tool(self, func: Callable, param_model: Optional[Type[BaseModel]] = None):

Choose a reason for hiding this comment

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

Add validation to prevent duplicate tool registration and ensure tools are callable

Add validation for tool registration to prevent duplicate tools

Suggested change
# Add a tool function with or without a Pydantic model.
def add_tool(self, func: Callable, param_model: Optional[Type[BaseModel]] = None):
def add_tool(self, func: Callable, param_model: Optional[Type[BaseModel]] = None):
"""Register a tool function with metadata. If no param_model is provided, infer from function signature."""
if func.__name__ in self._tools:
raise ValueError(f"Tool with name '{func.__name__}' is already registered")
if not callable(func):
raise TypeError("Tool must be a callable function")

Comment on lines +91 to +93
# Convert Message object to dict if necessary
message_dict = (
message.model_dump() if hasattr(message, "model_dump") else message

Choose a reason for hiding this comment

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

Add type validation for message conversion to catch type errors early

Add type validation for message conversion to prevent runtime errors

Suggested change
# Convert Message object to dict if necessary
message_dict = (
message.model_dump() if hasattr(message, "model_dump") else message
if not isinstance(message, (dict, BaseModel)):
raise TypeError(f"Message must be a dict or BaseModel, got {type(message)}")
message_dict = (
message.model_dump() if hasattr(message, "model_dump") else message
)

Comment on lines +125 to +126

def execute_tool(self, tool_calls) -> tuple[list, list]:

Choose a reason for hiding this comment

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

Add input validation for tool execution to prevent invalid tool calls

Add parameter validation for tool execution to prevent invalid tool calls

Suggested change
def execute_tool(self, tool_calls) -> tuple[list, list]:

Comment on lines +40 to +53
converted_messages = []
for msg in messages:
if isinstance(msg, dict):
if msg["role"] == "tool":
# Convert tool result message
converted_msg = {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": msg["tool_call_id"],
"content": msg["content"],
}
],

Choose a reason for hiding this comment

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

Optimize message conversion using a mapping dictionary for better performance

Add performance optimization for message conversion

Suggested change
converted_messages = []
for msg in messages:
if isinstance(msg, dict):
if msg["role"] == "tool":
# Convert tool result message
converted_msg = {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": msg["tool_call_id"],
"content": msg["content"],
}
],
converted_messages = []
msg_conversion_map = {
"tool": lambda m: {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": m["tool_call_id"],
"content": m["content"],
}
],
}
}
for msg in messages:
if isinstance(msg, dict):
converter = msg_conversion_map.get(msg["role"])
if converter:
converted_messages.append(converter(msg))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants