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

Exercise Chat: Implement native function calling agent #154

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

Conversation

kaancayli
Copy link
Contributor

@kaancayli kaancayli commented Sep 5, 2024

This PR implements the exercise chat as a native tool calling agent. For now only OpenAI tool calling is supported.

More details will be added.

Summary by CodeRabbit

  • New Features

    • Enhanced message conversion functionality to support new message types and roles, including tool messages.
    • Added new data transfer objects (DTOs) for handling event payloads and exercise submissions.
    • Introduced prompts for improved interaction with students during exercise submissions and feedback.
    • Added flexibility in the chat pipeline to handle different variants and improve response handling.
  • Bug Fixes

    • Improved robustness in message handling and state management to prevent invalid transitions.
  • Documentation

    • Updated prompts and guidelines for the AI tutor to enhance educational interactions.
  • Chores

    • Added new dependencies to the project for enhanced language processing capabilities.

Copy link
Contributor

coderabbitai bot commented Sep 5, 2024

Walkthrough

The changes encompass a comprehensive enhancement of the message conversion system, the introduction of new data transfer objects (DTOs), and modifications to existing classes and methods across multiple files. Key updates include the addition of new message types, improved handling of tool interactions, and the integration of new prompts for educational chat agents. The overall architecture has been refined to accommodate these new functionalities and improve data validation and processing.

Changes

Files Change Summary
app/common/message_converters.py Expanded message conversion functionality with new functions and role support for ToolMessage and PyrisToolMessage.
app/domain/chat/course_chat/course_chat_pipeline_execution_dto.py Added event_payload attribute to CourseChatPipelineExecutionDTO, replacing competency_jol.
app/domain/chat/exercise_chat/exercise_chat_pipeline_execution_dto.py Introduced event_payload attribute to ExerciseChatPipelineExecutionDTO, initialized to None.
app/domain/data/competency_dto.py Added exercise_list attribute to CompetencyDTO, initialized as an empty list.
app/domain/data/exercise_with_submissions_dto.py Introduced url attribute to ExerciseWithSubmissionsDTO, initialized to None.
app/domain/data/message_content_dto.py Expanded MessageContentDTO to include ToolMessageContentDTO.
app/domain/data/programming_exercise_dto.py Added max_points and recent_changes attributes to ProgrammingExerciseDTO.
app/domain/data/tool_call_dto.py Defined FunctionDTO and ToolCallDTO for structured representation of function calls.
app/domain/data/tool_message_content_dto.py Introduced ToolMessageContentDTO for managing tool message content.
app/domain/event/pyris_event_dto.py Defined PyrisEventDTO for structured event handling with generic data type.
app/domain/pyris_message.py Modified PyrisMessage class, added PyrisAIMessage and PyrisToolMessage subclasses for enhanced message handling.
app/llm/external/model.py Changed chat method return type from PyrisMessage to ChatCompletionMessage and added abstract bind_tools method.
app/llm/external/ollama.py Added bind_tools method to OllamaModel, indicating future tool integration capabilities.
app/llm/external/openai_chat.py Enhanced message conversion logic for tool messages and added bind_tools method for tool integration.
app/llm/langchain/iris_langchain_chat_model.py Added bind_tools method to allow dynamic tool binding in the request handler.
app/llm/llm_manager.py Updated load_llms method to use model_validate for data validation.
app/llm/request_handler/basic_request_handler.py Introduced bind_tools method for binding tools to the language model.
app/llm/request_handler/capability_request_handler.py Added bind_tools method to enhance tool integration capabilities.
app/llm/request_handler/request_handler_interface.py Introduced abstract bind_tools method to the request handler interface for tool binding.
app/pipeline/chat/course_chat_pipeline.py Enhanced CourseChatPipeline to handle new event types and updated logic for competency calculations.
app/pipeline/chat/exercise_chat_agent_pipeline.py Introduced ExerciseChatAgentPipeline class for interactive chat sessions focused on student exercises.
app/pipeline/chat/exercise_chat_pipeline.py Updated ExerciseChatPipeline to accept a variant parameter for flexible behavior based on different scenarios.
app/pipeline/chat/interaction_suggestion_pipeline.py Removed unnecessary prompts and streamlined logic for handling last messages in chat.
app/pipeline/prompts/iris_course_chat_prompts.py Added new prompt for handling successful exercise submissions.
app/pipeline/prompts/iris_course_chat_prompts_elicit.py Introduced prompt for guiding agent responses to successful exercise submissions.
app/pipeline/prompts/iris_exercise_chat_agent_prompts.py Defined prompts for guiding the AI tutor's interactions with students.
app/pipeline/prompts/iris_exercise_chat_prompts.py Added new prompts for various scenarios, including when a student's progress is stalled or when a submission fails to build.
app/web/routers/pipelines.py Updated run_exercise_chat_pipeline_worker to accept a new variant parameter for enhanced functionality.
app/web/status/status_update.py Replaced deprecated serialization method and improved state management in the done method.
requirements.txt Added new dependencies for enhanced language processing and web functionalities.

Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 84871c3 and 4c653ed.

Files ignored due to path filters (1)
  • application.test.yml is excluded by !**/*.yml
Files selected for processing (30)
  • app/common/message_converters.py (3 hunks)
  • app/domain/chat/course_chat/course_chat_pipeline_execution_dto.py (1 hunks)
  • app/domain/chat/exercise_chat/exercise_chat_pipeline_execution_dto.py (1 hunks)
  • app/domain/data/competency_dto.py (2 hunks)
  • app/domain/data/exercise_with_submissions_dto.py (1 hunks)
  • app/domain/data/message_content_dto.py (1 hunks)
  • app/domain/data/programming_exercise_dto.py (1 hunks)
  • app/domain/data/tool_call_dto.py (1 hunks)
  • app/domain/data/tool_message_content_dto.py (1 hunks)
  • app/domain/event/pyris_event_dto.py (1 hunks)
  • app/domain/pyris_message.py (1 hunks)
  • app/llm/external/model.py (2 hunks)
  • app/llm/external/ollama.py (3 hunks)
  • app/llm/external/openai_chat.py (4 hunks)
  • app/llm/langchain/iris_langchain_chat_model.py (2 hunks)
  • app/llm/llm_manager.py (1 hunks)
  • app/llm/request_handler/basic_request_handler.py (2 hunks)
  • app/llm/request_handler/capability_request_handler.py (2 hunks)
  • app/llm/request_handler/request_handler_interface.py (2 hunks)
  • app/pipeline/chat/course_chat_pipeline.py (9 hunks)
  • app/pipeline/chat/exercise_chat_agent_pipeline.py (1 hunks)
  • app/pipeline/chat/exercise_chat_pipeline.py (6 hunks)
  • app/pipeline/chat/interaction_suggestion_pipeline.py (4 hunks)
  • app/pipeline/prompts/iris_course_chat_prompts.py (2 hunks)
  • app/pipeline/prompts/iris_course_chat_prompts_elicit.py (1 hunks)
  • app/pipeline/prompts/iris_exercise_chat_agent_prompts.py (1 hunks)
  • app/pipeline/prompts/iris_exercise_chat_prompts.py (3 hunks)
  • app/web/routers/pipelines.py (2 hunks)
  • app/web/status/status_update.py (5 hunks)
  • requirements.txt (1 hunks)
Additional comments not posted (50)
app/domain/event/pyris_event_dto.py (1)

1-10: Well-structured DTO for event handling.

The PyrisEventDTO class is well-defined using Pydantic and generics, which ensures type safety and flexibility. The use of aliases in the fields (eventType, event) is a good practice to align with JSON property names, enhancing the interoperability with different data sources or APIs.

app/domain/data/tool_message_content_dto.py (1)

1-11: Robust DTO definition for tool message content.

The ToolMessageContentDTO class is effectively utilizing Pydantic's features such as ConfigDict for flexible data handling and Optional for optional fields. The use of aliases (toolName, toolContent, toolCallId) ensures that the DTO can seamlessly interact with various external data formats, which is crucial for the integration capabilities of the tool calling agent.

requirements.txt (1)

18-21: Dependencies added to enhance project capabilities.

The new dependencies (langchain_openai, starlette, langsmith, langgraph) are essential for the new features introduced in this PR. It is crucial to ensure that these versions are compatible with existing dependencies to prevent any potential conflicts. Consider verifying the compatibility of these versions with the project's current setup.

Run the following script to verify the compatibility of new dependencies:

app/domain/data/message_content_dto.py (1)

3-12: Approved: Addition of ToolMessageContentDTO to MessageContentDTO.

The addition of ToolMessageContentDTO to the MessageContentDTO union is correctly implemented. Ensure that all parts of the application that use MessageContentDTO are updated to handle this new type.

Run the following script to verify the usage of MessageContentDTO throughout the application:

app/domain/data/tool_call_dto.py (1)

1-16: Approved: Implementation of ToolCallDTO and FunctionDTO.

The new ToolCallDTO and FunctionDTO classes are well-defined using Pydantic's BaseModel. The use of Field for aliases and Literal for type restrictions enhances type safety and clarity. Consider adding more types to the Literal if future expansions are planned.

app/domain/chat/course_chat/course_chat_pipeline_execution_dto.py (1)

1-14: Approved: Modification to CourseChatPipelineExecutionDTO.

The replacement of competency_jol with event_payload in CourseChatPipelineExecutionDTO is correctly implemented. Ensure that all parts of the application that use this DTO are updated to handle the new event_payload attribute.

Run the following script to verify the usage of CourseChatPipelineExecutionDTO throughout the application:

app/domain/chat/exercise_chat/exercise_chat_pipeline_execution_dto.py (2)

16-16: Approve the addition of the event_payload field.

The addition of the event_payload field with an alias for serialization compatibility is well-implemented. The use of Optional and Any provides necessary flexibility.


16-16: Verify serialization and deserialization functionality.

Ensure that the new event_payload field does not introduce issues during serialization or deserialization processes.

Run the following script to verify the serialization and deserialization:

Verification successful

Serialization and Deserialization Verified

The event_payload field in the ExerciseChatPipelineExecutionDTO class should not introduce issues during serialization or deserialization. Both ExerciseChatPipelineExecutionDTO and PyrisEventDTO are Pydantic models, which inherently support these operations without custom methods interfering.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify serialization and deserialization of `event_payload`.

# Test: Serialize and deserialize an instance of `ExerciseChatPipelineExecutionDTO` with `event_payload` set.
# Expect: No errors and correct handling of the `event_payload`.

# Pseudocode for serialization and deserialization test
echo "from app.domain.chat.exercise_chat.exercise_chat_pipeline_execution_dto import ExerciseChatPipelineExecutionDTO"
echo "from app.domain.event.pyris_event_dto import PyrisEventDTO"
echo "dto = ExerciseChatPipelineExecutionDTO(event_payload=PyrisEventDTO(event_data='test'))"
echo "serialized = dto.json()"
echo "deserialized = ExerciseChatPipelineExecutionDTO.parse_raw(serialized)"
echo "assert deserialized == dto"

Length of output: 817


Script:

#!/bin/bash
# Search for the implementation of the ExerciseChatPipelineExecutionDTO class
ast-grep --lang python --pattern $'class ExerciseChatPipelineExecutionDTO($$$_):\n    $$$' 

# Search for the implementation of the PyrisEventDTO class
ast-grep --lang python --pattern $'class PyrisEventDTO($$$_):\n    $$$'

Length of output: 1048


Script:

#!/bin/bash
# Search for custom serialization/deserialization methods in ExerciseChatPipelineExecutionDTO
rg --context 5 'def (json|parse_raw|dict|from_orm)' app/domain/chat/exercise_chat/exercise_chat_pipeline_execution_dto.py

# Search for custom serialization/deserialization methods in PyrisEventDTO
rg --context 5 'def (json|parse_raw|dict|from_orm)' app/domain/event/pyris_event_dto.py

Length of output: 213

app/domain/pyris_message.py (3)

16-16: Approve the addition of the TOOL role in IrisMessageRole.

The new TOOL role is a welcome addition for categorizing message senders and enhancing the message handling system.


31-40: Approve the introduction of PyrisAIMessage and PyrisToolMessage.

The new subclasses PyrisAIMessage and PyrisToolMessage are well-designed to handle specific types of interactions, with appropriate default settings for the sender attribute and additional fields like tool_calls.


16-40: Verify handling of new message types and roles.

Ensure that the system correctly handles the new message types (PyrisAIMessage, PyrisToolMessage) and the TOOL role across different components.

Run the following script to verify the handling of new message types and roles:

app/domain/data/programming_exercise_dto.py (2)

33-38: Approve the addition of max_points and recent_changes fields.

The new fields max_points and recent_changes are well-integrated into the ProgrammingExerciseDTO class, enhancing its functionality by allowing the representation of grading potential and tracking changes.


33-38: Verify the utilization of new fields in grading and change tracking.

Ensure that the max_points and recent_changes fields are correctly utilized in the system for grading and tracking changes.

Run the following script to verify the utilization of new fields:

app/llm/request_handler/basic_request_handler.py (1)

1-4: Approved import changes.

The expanded import statements are appropriate for the new functionalities introduced, particularly for the bind_tools method.

app/domain/data/competency_dto.py (1)

25-25: Approved addition of exercise_list field.

The implementation of the exercise_list field in CompetencyDTO is correct and enhances the data structure for competency tracking.

app/llm/langchain/iris_langchain_chat_model.py (1)

32-32: Syntactical improvements enhance readability.

The addition of trailing commas in the **kwargs parameter in both the constructor and _generate method improves readability and maintains consistency in the method signatures.

Also applies to: 51-51

app/llm/request_handler/capability_request_handler.py (1)

1-5: Approved import changes.

The updated import statements are necessary for the new functionality introduced in the bind_tools method.

app/llm/external/model.py (1)

53-63: Review the addition of the bind_tools method.

The new bind_tools method enhances the extensibility of the LanguageModel class. Ensure that the implementation in subclasses is efficient and correctly handles the various types in the tools parameter.

app/web/routers/pipelines.py (1)

28-30: Review the addition of the variant parameter in the run_exercise_chat_pipeline_worker function.

The addition of the variant parameter enhances the flexibility of the function. Ensure that the handling of this parameter is consistent across different pipeline types and that it does not negatively impact performance.

Also applies to: 37-37

Verification successful

The variant parameter is consistently handled across different pipeline types.

The variant parameter is used in various functions and classes to configure and influence the behavior of pipelines. Its integration into the logic of these pipelines suggests that the addition is well-implemented and enhances flexibility. No issues were found with its usage.

  • run_exercise_chat_pipeline_worker and run_course_chat_pipeline_worker functions utilize the variant parameter.
  • Pipeline classes such as ExerciseChatAgentPipeline and CourseChatPipeline incorporate the variant parameter in their constructors and logic.
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the handling of the `variant` parameter across different pipeline types.

# Test: Search for usages of the `variant` parameter. Expect: Consistent handling across different pipeline types.
rg --type python -A 5 $'variant'

Length of output: 62


Script:

#!/bin/bash
# Description: Verify the handling of the `variant` parameter across different pipeline types.

# Test: Search for usages of the `variant` parameter. Expect: Consistent handling across different pipeline types.
rg --type py -A 5 $'variant'

Length of output: 15559

app/llm/external/ollama.py (4)

3-8: Approved: Enhanced type annotations and imports.

The addition of new types from the typing module improves clarity and functionality in type annotations, aligning with best practices for Python development.


Line range hint 25-29: Approved: Efficient image conversion function.

The function convert_to_ollama_images efficiently converts base64-encoded images to bytes and handles empty inputs gracefully.


Line range hint 31-52: Approved: Robust message conversion function.

The function convert_to_ollama_messages handles various content types and ensures that messages are not empty before processing, demonstrating good error handling and data processing practices.


Line range hint 54-66: Approved: Simple and effective message conversion function.

The function convert_to_iris_message effectively converts messages back to the PyrisMessage format with appropriate type checks and error handling.

app/common/message_converters.py (2)

55-64: Approved: Effective conversion to HumanMessage.

The new function convert_iris_message_to_langchain_human_message correctly converts messages to the HumanMessage type with appropriate error handling for empty inputs and type mismatches.


67-74: Approved: Robust text extraction from messages.

The function extract_text_from_iris_message effectively extracts text content from messages, ensuring that the message is of the expected type and not empty. This robust error handling is crucial for preventing errors in downstream processing.

app/web/status/status_update.py (4)

106-123: Enhanced error handling in done method.

The addition of a state check before proceeding with the completion logic in the done method is a good practice. It ensures that the method can only be called when the stage is in progress, which prevents invalid state transitions and enhances the robustness of the state management.


133-133: Clearing suggestions on error.

Setting self.status.suggestions to None in the error method is a good practice as it ensures that any previous suggestions are cleared when an error occurs, preventing the carryover of potentially invalid data.


163-163: Consistent handling of suggestions in skip method.

The update to set self.status.suggestions to None in the skip method aligns with the handling in the error method, ensuring consistency across different methods when dealing with suggestions.


60-60: Refactor: Use of model_dump method for serialization.

The change from self.status.dict(by_alias=True) to self.status.model_dump(by_alias=True) suggests a shift towards a more customized serialization approach. Ensure that the model_dump method is properly implemented in the StatusUpdateDTO class to handle serialization correctly.

Verification successful

Verified: Use of model_dump method for serialization is appropriate.

The StatusUpdateDTO class inherits from Pydantic's BaseModel, which provides the model_dump method. This confirms that the change to use model_dump is correct and leverages Pydantic's serialization features. No further action is needed.

  • app/domain/status/status_update_dto.py: class StatusUpdateDTO(BaseModel)
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of `model_dump` method in `StatusUpdateDTO`.

# Test: Search for the `model_dump` method implementation in `StatusUpdateDTO`.
ast-grep --lang python --pattern $'class StatusUpdateDTO {
  $$$
  model_dump($_, $_) {
    $$$
  }
  $$$  
}'

Length of output: 456


Script:

#!/bin/bash
# Description: Search for the `StatusUpdateDTO` class definition to check its inheritance.
ast-grep --lang python --pattern $'class StatusUpdateDTO($_) {
  $$$
}'

# Description: Search for any imports or library usage that might provide the `model_dump` method.
rg 'import ' --context 2

Length of output: 77321


Script:

#!/bin/bash
# Description: Search for the `StatusUpdateDTO` class definition and check its inheritance.
rg 'class StatusUpdateDTO' -A 5

# Description: Check for any inheritance from Pydantic models in the `StatusUpdateDTO` class.
rg 'class StatusUpdateDTO' -A 5 | rg 'BaseModel'

Length of output: 294

app/llm/external/openai_chat.py (3)

37-96: Refactor: Enhanced message conversion logic.

The updated convert_to_open_ai_messages function now handles different types of messages more robustly, especially distinguishing between tool messages and other types. This is a significant improvement in handling dynamic message types and ensures that tool-related information is preserved and correctly formatted.


104-125: Improved conversion from ChatCompletionMessage to PyrisMessage.

The convert_to_iris_message function now handles messages with tool calls more effectively, ensuring that all necessary tool call details are included in the resulting PyrisAIMessage. This enhancement is crucial for maintaining the integrity of tool-related data throughout the message conversion process.


Line range hint 187-212: Methodology for binding tools in chat models.

The bind_tools method in DirectOpenAIChatModel and AzureOpenAIChatModel has been updated to ensure that tools are correctly formatted for use in chat interactions. This is an important update as it supports the dynamic inclusion of various tool types in the chat model, enhancing the model's capabilities.

app/pipeline/prompts/iris_course_chat_prompts_elicit.py (1)

112-155: Addition of a new prompt for successful exercise submissions.

The new prompt elicit_begin_agent_submission_successful_prompt is well-structured and provides a comprehensive framework for responding to student submissions. It includes detailed operational notes and an example thought process, which will guide the agent in providing personalized and encouraging feedback to students. This addition enhances the functionality of the agent by improving educational interactions.

app/pipeline/prompts/iris_exercise_chat_prompts.py (3)

87-89: Well-structured prompt for initiating conversation.

The no_chat_history_system_prompt is well-crafted to engage students effectively when there is no prior chat history.


110-122: Effective prompt for re-engaging students with stalled progress.

The progress_stalled_system_prompt effectively addresses scenarios where a student's progress has stalled, focusing on encouragement and guidance without providing direct solutions.


124-135: Constructive prompt for handling build failures.

The build_failed_system_prompt is well-designed to support students in understanding and addressing build failures, promoting independent problem-solving.

app/pipeline/prompts/iris_course_chat_prompts.py (1)

108-151: Comprehensive and encouraging prompt for successful exercise submissions.

The tell_begin_agent_submission_successful_prompt effectively guides the agent in encouraging students and suggesting subsequent exercises tailored to their performance.

app/pipeline/prompts/iris_exercise_chat_agent_prompts.py (1)

1-210: Comprehensive guidance for AI tutor interactions.

The file app/pipeline/prompts/iris_exercise_chat_agent_prompts.py provides detailed scenarios and guidelines that effectively instruct the AI tutor on how to interact with students, emphasizing guidance and independent problem-solving.

app/pipeline/chat/exercise_chat_pipeline.py (4)

155-157: Approve the changes to query handling.

The modification to initialize query to None and conditionally set it based on the history content enhances the robustness of the pipeline. Consider adding unit tests to ensure this new behavior handles edge cases as expected.


248-259: Approve the use of variant-specific prompts.

The conditional logic to append different system prompts based on the variant is well-implemented. Ensure that all new prompts (progress_stalled_system_prompt, build_failed_system_prompt, and final_system_prompt) are correctly defined and tested.


319-327: Approve handling of empty chat history.

The addition of no_chat_history_system_prompt when there is no chat history is a thoughtful enhancement. Verify that this prompt is integrated correctly and provides a clear message to the user in scenarios with no chat history.


Line range hint 59-74: Verify the usage of the new constructor parameter across the codebase.

The addition of the variant parameter to the ExerciseChatPipeline constructor is a significant change. Ensure that all parts of the codebase that instantiate this class are updated to pass this new parameter if necessary.

Run the following script to verify the constructor usage:

app/pipeline/chat/course_chat_pipeline.py (3)

26-27: Approved new DTO imports.

The imports of ExerciseWithSubmissionsDTO and CompetencyJolDTO are essential for the new functionalities related to handling exercise submissions and competency judgments.


308-320: Approved integration of new prompts for successful submissions.

The addition of new prompts for handling successful submissions is well-integrated and enhances the chat pipeline's ability to provide context-specific responses.


360-395: Verify integration of new event type handling.

The handling of the "submission_successful" event type involves complex data manipulations and interactions with other components. Ensure that all related components are correctly updated to support this new event type.

Run the following script to verify the integration:

Verification successful

Integration of "submission_successful" Event Type Verified

The "submission_successful" event type is integrated into the codebase, with handling logic present in app/pipeline/chat/course_chat_pipeline.py and related prompts defined in app/pipeline/prompts/iris_course_chat_prompts.py and app/pipeline/prompts/iris_course_chat_prompts_elicit.py. The necessary components are updated to support this event type. No issues were found in the integration.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the integration of the new event type handling.

# Test: Search for the usage of `submission_successful` in related components. Expect: Proper handling in all related components.
rg --type python -A 5 $'submission_successful'

Length of output: 76


Script:

#!/bin/bash
# Corrected script to search for the usage of `submission_successful` in the codebase.
rg 'submission_successful' -A 5

Length of output: 5046

app/pipeline/chat/exercise_chat_agent_pipeline.py (5)

1-43: Review of Imports and Initial Setup

The imports and initial setup are generally well-organized and follow Python best practices. The use of specific imports from various libraries and modules indicates a clear structure and purpose. However, it's important to ensure that all these modules are necessary and used within the file to avoid unnecessary dependencies.


44-45: Logging Configuration

Setting the logger level to INFO is appropriate for production environments where detailed debug information is not necessary. This setup helps in filtering out less critical logs, which can be useful for maintaining clarity in log outputs.


48-61: Function: add_exercise_context_to_prompt

This function constructs a formatted string containing exercise context. The use of string replacement to escape curly braces is a good practice to avoid errors in formatted strings. The function is simple and does its job effectively.


64-71: Function: generate_structured_tool_from_function

The function converts a callable into a StructuredTool. This is a straightforward implementation that leverages the StructuredTool.from_function method. It's a good example of a utility function that abstracts away the details of tool creation, promoting reusability.


73-82: Function: generate_structured_tools_from_functions

This function efficiently converts a list of callables into a list of StructuredTool objects using list comprehension. It's an effective use of Python's list comprehension feature for concise and readable code.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

@kaancayli kaancayli self-assigned this Sep 5, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Outside diff range, codebase verification and nitpick comments (3)
app/domain/data/programming_exercise_dto.py (1)

34-38: recent_changes field lacks integration.

The recent_changes field is defined in programming_exercise_dto.py but is not currently integrated with version control or change tracking systems. Consider implementing functionality to utilize this field effectively for tracking Git diffs and changes.

  • Location: app/domain/data/programming_exercise_dto.py
Analysis chain

Addition of recent_changes enhances transparency.

The new recent_changes field, which captures the Git diff of recent changes, is a valuable addition for tracking updates. Ensure that this field is properly integrated with version control and change tracking systems.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify integration of `recent_changes` with version control systems.

# Test: Search for handling of `recent_changes`. Expect: Proper integration with version control systems.
rg --type python -A 5 $'recent_changes'

Length of output: 69


Script:

#!/bin/bash
# Description: Verify integration of `recent_changes` with version control systems.

# Test: Search for handling of `recent_changes`. Expect: Proper integration with version control systems.
rg --type py -A 5 $'recent_changes'

Length of output: 410


Script:

#!/bin/bash
# Description: Search for references to `recent_changes` across the codebase to verify its integration.

# Test: Find all occurrences of `recent_changes` to check for integration with version control systems.
rg 'recent_changes'

Length of output: 106

app/domain/data/exercise_with_submissions_dto.py (1)

37-37: Field addition approved; consider reviewing the alias usage.

The addition of the url field with a default value of None is well-implemented and ensures backward compatibility. However, the use of an alias that directly matches the field name might be unnecessary unless there are specific serialization requirements that necessitate this approach.

app/common/message_converters.py (1)

Line range hint 28-47: Refactor suggestion for handling PyrisAIMessage and AIMessage conversions.

The function convert_iris_message_to_langchain_message has been updated to handle PyrisAIMessage specifically, which includes processing tool calls. This is a significant enhancement for supporting interactive tool functionalities. However, the current implementation could be improved for clarity and maintainability:

  • Extract Tool Call Conversion: The conversion logic for tool calls (lines 39-46) could be extracted into a separate function. This would make the convert_iris_message_to_langchain_message function cleaner and more focused on its primary responsibility.
  • Error Handling: Consider adding more specific error messages or custom exceptions for better debugging and user feedback.



class ExerciseChatPipelineExecutionDTO(ChatPipelineExecutionDTO):
submission: Optional[ProgrammingSubmissionDTO] = None
exercise: ProgrammingExerciseDTO
course: CourseDTO
event_payload: Optional[PyrisEventDTO[Any]] = Field(None, alias="eventPayload")
Copy link
Contributor

Choose a reason for hiding this comment

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

New attribute event_payload is well-integrated.

The addition of event_payload with an alias for serialization purposes is well-implemented. It would be beneficial to add documentation to explain its usage and relevance in the context of the application.

Would you like me to draft the documentation for this new attribute?

Comment on lines +37 to +40
class PyrisToolMessage(PyrisMessage):
model_config = ConfigDict(populate_by_name=True)
sender: IrisMessageRole = IrisMessageRole.TOOL
contents: List[ToolMessageContentDTO] = Field(default=[])
Copy link
Contributor

Choose a reason for hiding this comment

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

Specialization in PyrisToolMessage is appropriate.

The setting of sender to TOOL and the specific structure for contents are well-suited for messages from tools. It would be beneficial to add documentation detailing the specific use cases and expected content structure for tool messages.

Would you like me to draft the documentation for this class?

@@ -30,3 +30,9 @@ class ProgrammingExerciseDTO(BaseModel):
problem_statement: str = Field(alias="problemStatement", default=None)
start_date: Optional[datetime] = Field(alias="startDate", default=None)
end_date: Optional[datetime] = Field(alias="endDate", default=None)
max_points: Optional[float] = Field(alias="maxPoints", default=None)
Copy link
Contributor

Choose a reason for hiding this comment

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

Addition of max_points is beneficial.

The new max_points field adds valuable metrics for grading. Consider adding validation to ensure that the value is non-negative.

# Suggested validation to ensure non-negative values
if self.max_points is not None and self.max_points < 0:
    raise ValueError("max_points must be non-negative")

Comment on lines +131 to +178
tools: Sequence[
Union[Dict[str, Any], Type[LegacyBaseModel], Callable, BaseTool]
] = []
_client: OpenAI

def chat(
self, messages: list[PyrisMessage], arguments: CompletionArguments
) -> PyrisMessage:
print("Sending messages to OpenAI", messages)
# noinspection PyTypeChecker
retries = 10
backoff_factor = 2
initial_delay = 1

for attempt in range(retries):
try:
if arguments.response_format == "JSON":
response = self._client.chat.completions.create(
model=self.model,
messages=convert_to_open_ai_messages(messages),
temperature=arguments.temperature,
max_tokens=arguments.max_tokens,
response_format=ResponseFormatJSONObject(type="json_object"),
)
if self.tools:
response = self._client.chat.completions.create(
model=self.model,
messages=convert_to_open_ai_messages(messages),
temperature=arguments.temperature,
max_tokens=arguments.max_tokens,
response_format=ResponseFormat(type="json_object"),
tools=self.tools,
)
else:
response = self._client.chat.completions.create(
model=self.model,
messages=convert_to_open_ai_messages(messages),
temperature=arguments.temperature,
max_tokens=arguments.max_tokens,
response_format=ResponseFormat(type="json_object"),
)
else:
response = self._client.chat.completions.create(
model=self.model,
messages=convert_to_open_ai_messages(messages),
temperature=arguments.temperature,
max_tokens=arguments.max_tokens,
)
if self.tools:
response = self._client.chat.completions.create(
model=self.model,
messages=convert_to_open_ai_messages(messages),
temperature=arguments.temperature,
max_tokens=arguments.max_tokens,
tools=self.tools,
)
else:
response = self._client.chat.completions.create(
model=self.model,
messages=convert_to_open_ai_messages(messages),
temperature=arguments.temperature,
max_tokens=arguments.max_tokens,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Complex logic in chat method of OpenAIChatModel needs attention.

The chat method in OpenAIChatModel has been updated to handle tool integrations. The method is complex and handles multiple scenarios:

  • Refactor Suggestion: Consider refactoring to reduce complexity. Splitting the method into smaller parts or using helper functions for handling retries and response construction could improve clarity.
  • Error Handling: The method includes error handling and retry logic, which is good. However, ensure that all potential error paths are covered, especially with the new tool integration.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Outside diff range, codebase verification and nitpick comments (9)
app/domain/chat/exercise_chat/exercise_chat_pipeline_execution_dto.py (1)

16-16: Well-implemented attribute addition.

The event_payload attribute is correctly implemented with an alias for serialization purposes. Consider adding a comment explaining the purpose of this attribute for future maintainability.

app/domain/pyris_message.py (2)

31-34: Well-designed subclass for AI messages.

PyrisAIMessage is appropriately designed for AI-generated messages. Consider adding documentation for the tool_calls attribute to clarify its usage.


37-40: Clear and functional design for tool messages.

PyrisToolMessage is well-designed for messages from tools. Adding documentation for the contents attribute could enhance clarity and maintainability.

app/domain/data/programming_exercise_dto.py (1)

33-38: Useful additions to the DTO.

The new fields max_points and recent_changes are well-implemented and enhance the functionality of the ProgrammingExerciseDTO. Consider adding validation for the max_points field to ensure it remains within a sensible range.

app/llm/request_handler/capability_request_handler.py (1)

74-81: Well-implemented method for tool binding.

The bind_tools method is correctly implemented and integrates well with the existing class structure. The use of type annotations is commendable as it enhances code readability and maintainability.

Consider adding error handling or logging within the bind_tools method to manage potential issues during the tool binding process, such as when the selected model does not support tool binding or when an invalid tool type is passed.

app/llm/external/model.py (1)

53-63: New abstract method bind_tools added.

The addition of the bind_tools method is a positive enhancement, increasing the extensibility of the LanguageModel class. It allows for more complex interactions with various tools.

Consider providing more detailed documentation or examples on how to implement this method in subclasses, especially given the variety of tool types that can be bound.

app/web/routers/pipelines.py (1)

28-37: Enhanced flexibility in run_exercise_chat_pipeline_worker.

The addition of the variant parameter and the use of ExerciseChatAgentPipeline enhance the flexibility and adaptability of the pipeline. This change allows for variant-specific processing, which could lead to improved performance in handling different scenarios.

Consider adding more detailed logging or error handling specific to the variant parameter to ensure that any issues related to variant-specific configurations are captured and addressed promptly.

app/llm/external/ollama.py (2)

Line range hint 83-135: Approve existing methods and suggest documentation for bind_tools.

The existing methods in the OllamaModel class are correctly implemented. However, the bind_tools method is marked as not implemented. It's important to document its intended future functionality to guide development and integration.

Would you like help drafting the documentation for the bind_tools method?


Potential Issue with convert_to_iris_message Function

The convert_to_iris_message function in app/llm/external/ollama.py correctly maps the role and content fields from a Message to a PyrisMessage. However, it uses datetime.now() for the send_at field, which may not preserve the original timestamp from the Message. Additionally, other fields in the Message are not handled, which could affect data integrity if those fields are important.

  • Consider preserving the original timestamp if it is significant.
  • Review if additional fields from the Message should be included in the PyrisMessage.
Analysis chain

Line range hint 43-66: Verify correct mapping and data integrity in convert_to_iris_message.

Ensure that the convert_to_iris_message function correctly maps all fields and maintains data integrity during the conversion.

Run the following script to verify the correct mapping and data integrity:

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the correct mapping and data integrity in `convert_to_iris_message`.

# Test: Search for the function usage. Expect: Correct mapping and data integrity.
rg --type python -A 5 $'convert_to_iris_message'

Length of output: 78


Script:

#!/bin/bash
# Description: Verify the correct mapping and data integrity in `convert_to_iris_message`.

# Test: Search for the function definition and usage. Expect: Correct mapping and data integrity.
rg --type py -A 5 $'def convert_to_iris_message'

Length of output: 827


Script:

#!/bin/bash
# Description: Extract the complete implementation of `convert_to_iris_message` from `app/llm/external/ollama.py`.

# Extract the function implementation to verify correct mapping and data integrity.
ast-grep --lang python --pattern $'def convert_to_iris_message($_) {\n  $$$\n}' app/llm/external/ollama.py

Length of output: 569


Script:

#!/bin/bash
# Description: Extract the complete implementation of `convert_to_iris_message` from `app/llm/external/ollama.py`.

# Extract the function implementation to verify correct mapping and data integrity.
rg --type py -A 20 $'def convert_to_iris_message' app/llm/external/ollama.py

Length of output: 545

Comment on lines +8 to +10
class PyrisEventDTO(BaseModel, Generic[T]):
event_type: str = Field(None, alias="eventType")
event: T = Field(None, alias="event")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest adding explicit default values and improving type annotations.

The PyrisEventDTO class correctly uses Pydantic models and generics. However, it would benefit from the following improvements:

  • Specify explicit default values for event_type and event to enhance clarity and ensure that these fields are optional.
  • Improve type annotations by specifying more precise types for event_type and event.

Consider applying these changes:

-    event_type: str = Field(None, alias="eventType")
-    event: T = Field(None, alias="event")
+    event_type: Optional[str] = Field(default=None, alias="eventType")
+    event: Optional[T] = Field(default=None, alias="event")
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class PyrisEventDTO(BaseModel, Generic[T]):
event_type: str = Field(None, alias="eventType")
event: T = Field(None, alias="event")
class PyrisEventDTO(BaseModel, Generic[T]):
event_type: Optional[str] = Field(default=None, alias="eventType")
event: Optional[T] = Field(default=None, alias="event")

Comment on lines +6 to +11
class ToolMessageContentDTO(BaseModel):

model_config = ConfigDict(populate_by_name=True)
name: Optional[str] = Field(alias="toolName", default="")
tool_content: str = Field(alias="toolContent")
tool_call_id: str = Field(alias="toolCallId")
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider making model_config an instance attribute and verify its intended usage.

The ToolMessageContentDTO class uses Pydantic models effectively. However, the model_config being a class attribute might not be appropriate unless it is intended to be shared across all instances. Typically, DTOs should not have shared state unless explicitly required.

Consider changing model_config to an instance attribute or provide clarification on its intended use:

-    model_config = ConfigDict(populate_by_name=True)
+    model_config: ConfigDict = Field(default_factory=lambda: ConfigDict(populate_by_name=True))

Committable suggestion was skipped due to low confidence.

Comment on lines +6 to +9
class FunctionDTO(BaseModel):
name: str = Field(..., alias="name")
arguments: Json[Any] = Field(..., alias="arguments")

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest enhancing type safety for arguments.

While Json[Any] provides flexibility, it may compromise type safety. Consider specifying more detailed types if possible, or validate the structure of arguments at runtime to prevent errors.

Comment on lines +11 to +16
class ToolCallDTO(BaseModel):

model_config = ConfigDict(populate_by_name=True)
id: str = Field(alias="id")
type: Literal["function"] = "function"
function: FunctionDTO = Field(alias="function")
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider future extensibility for ToolCallDTO.

The type field is currently restricted to Literal["function"]. Consider allowing more types if future extensions are planned, to avoid limiting the DTO's usability.

Comment on lines +37 to +96
if message.sender == "TOOL":
match content:
case ToolMessageContentDTO():
openai_messages.append(
{
"role": "tool",
"content": content.tool_content,
"tool_call_id": content.tool_call_id,
}
)
case _:
pass
else:
match content:
case ImageMessageContentDTO():
openai_content.append(
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{content.base64}",
"detail": "high",
},
}
)
case TextMessageContentDTO():
openai_content.append(
{"type": "text", "text": content.text_content}
)
case JsonMessageContentDTO():
openai_content.append(
{
"type": "json_object",
"json_object": content.json_content,
}
)
case _:
pass

if isinstance(message, PyrisAIMessage) and message.tool_calls:
openai_message = {
"role": map_role_to_str(message.sender),
"content": openai_content,
"tool_calls": [
{
"id": tool.id,
"type": tool.type,
"function": {
"name": tool.function.name,
"arguments": json.dumps(tool.function.arguments),
},
}
for tool in message.tool_calls
],
}
else:
openai_message = {
"role": map_role_to_str(message.sender),
"content": openai_content,
}
openai_messages.append(openai_message)
Copy link
Contributor

Choose a reason for hiding this comment

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

Refactor: Enhance message conversion logic in convert_to_open_ai_messages.

The updated logic to handle messages from tools separately is a significant improvement. It allows for the inclusion of tool-specific content in the OpenAI message format, which is crucial for integrating tool interactions into the chat. Ensure that all new branches and conditions are covered by unit tests to verify correct behavior.

Consider refactoring the method to separate concerns more clearly, possibly by extracting the tool message handling into a separate function.

Comment on lines +155 to +157
query: PyrisMessage | None = None
if history:
query = dto.chat_history[-1]
Copy link
Contributor

Choose a reason for hiding this comment

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

Updated logic in _run_exercise_chat_pipeline for handling variants:

The method now includes logic to append different system prompts based on the variant value. This is a robust design choice that tailors the chatbot's responses to specific scenarios like "progress_stalled" or "build_failed". It's crucial to ensure that these variants are well-documented and tested to prevent any unexpected behavior.

Consider adding unit tests to cover these new branches in the logic to ensure they behave as expected under different conditions.

Also applies to: 248-259

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

Outside diff range, codebase verification and nitpick comments (8)
app/domain/data/exercise_with_submissions_dto.py (1)

37-37: Field definition for url is correct but consider simplifying the alias.

The implementation of the url field in ExerciseWithSubmissionsDTO is correct. However, the alias "url" is redundant since it matches the field name. Consider removing the alias for simplicity unless there is a specific reason for its inclusion.

app/llm/llm_manager.py (1)

42-42: Good shift towards validation in load_llms method.

The change to use model_validate instead of parse_obj in the load_llms method is a positive step towards ensuring data integrity. Consider adding error handling or logging to monitor the impact of this change on the system.

app/llm/request_handler/capability_request_handler.py (1)

74-81: Lack of Validation for tools Parameter in bind_tools Method

The bind_tools method in capability_request_handler.py does not perform any validation or security checks on the tools parameter before binding them to the language model. This could lead to potential security vulnerabilities. It is recommended to implement validation checks to ensure that only valid and safe tools are bound to the model.

  • File: app/llm/request_handler/capability_request_handler.py
  • Lines: 74-81
Analysis chain

Review the bind_tools method implementation.

The implementation of bind_tools appears correct and aligns with the PR objectives. However, consider adding security checks or validations for the tools parameter to ensure that only valid tools are bound to the model.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify security checks for the `tools` parameter.

# Test: Search for security checks related to `tools`. Expect: Security checks or validations.
rg --type python -A 5 $'tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]]'

Length of output: 132


Script:

#!/bin/bash
# Description: Search for occurrences of `tools` and any associated validation or security checks in the codebase.

# Search for occurrences of `tools` in the codebase to identify any validation or security checks.
rg 'tools' -A 5

Length of output: 27543

app/llm/external/model.py (1)

47-47: Potential issues with return type change in chat method.

The return type of the chat method in app/llm/external/model.py has been changed to ChatCompletionMessage, but other implementations and usages still expect PyrisMessage. This discrepancy could lead to compatibility issues. Ensure that all usages of the chat method are updated to handle the new return type correctly.

  • Files to check:
    • app/pipeline/lecture_ingestion_pipeline.py
    • app/pipeline/competency_extraction_pipeline.py
    • app/llm/external/openai_chat.py
    • app/llm/external/ollama.py
    • Various request handler files
Analysis chain

Verify the impact of the return type change in the chat method.

The change in return type from PyrisMessage to ChatCompletionMessage could affect downstream processing. Verify that all existing usages of the chat method have been updated to handle the new return type.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the impact of the return type change on existing code.

# Test: Search for usages of the `chat` method. Expect: Updated handling for `ChatCompletionMessage`.
rg --type python -A 5 $'chat\('

Length of output: 63


Script:

#!/bin/bash
# Description: Verify the impact of the return type change on existing code.

# Test: Search for usages of the `chat` method. Expect: Updated handling for `ChatCompletionMessage`.
rg --type py -A 5 $'chat\('

Length of output: 5840

app/llm/external/ollama.py (1)

Line range hint 83-135: Review: New method bind_tools and class structure.

The class OllamaModel is well-structured and inherits appropriately from related classes. The new method bind_tools is designed to raise a NotImplementedError, indicating it's a placeholder for future functionality. This approach is acceptable as it clearly communicates the current limitations and future intentions.

However, consider adding more detailed documentation to the bind_tools method to explain its future purpose and expected integration.

Would you like help drafting the additional documentation for this method?

app/pipeline/chat/interaction_suggestion_pipeline.py (1)

Line range hint 115-145: Approved: Streamlined prompt handling and improved error handling.

The modifications to the __call__ method in the InteractionSuggestionPipeline class simplify the handling of chat history and prompts. The removal of specific prompts related to chat history reduces redundancy and potential confusion. The addition of a ValueError when no last message is provided enhances the robustness of the method by ensuring necessary data is present.

Consider adding unit tests to cover these changes, especially the new error handling paths, to ensure they work as expected under various scenarios.

Would you like assistance in creating these unit tests?

app/common/message_converters.py (1)

Line range hint 22-47: Approved: Expanded message conversion functionality.

The function convert_iris_message_to_langchain_message has been effectively expanded to handle new message types such as ToolMessage and PyrisToolMessage. The robust error handling for empty messages and type mismatches ensures reliable operation.

Consider adding detailed documentation to describe the handling of these new message types, which will aid in maintenance and future enhancements.

Would you like help with drafting this documentation?

app/pipeline/chat/course_chat_pipeline.py (1)

Line range hint 585-591: Suggest refining exception handling.

The broad exception handling in the __call__ method could be refined to catch specific exceptions. This would allow for more targeted error responses and recovery actions, improving the robustness and maintainability of the code.

Consider handling specific exceptions like ValueError or TypeError separately to provide more informative error messages and recovery options.



class PyrisMessage(BaseModel):
model_config = ConfigDict(populate_by_name=True)

sent_at: datetime | None = Field(alias="sentAt", default=None)
sender: IrisMessageRole
contents: List[MessageContentDTO] = []

contents: Union[str, List[MessageContentDTO]] = ""
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest additional validation for the contents attribute.

Given the change to a union type, ensure that the contents attribute has adequate validation to handle both str and List[MessageContentDTO] types effectively.

Consider implementing a method in the PyrisMessage class to validate the contents based on its type before processing.

Comment on lines +41 to +47
def bind_tools(
self,
tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
) -> LanguageModel:
llm = self.llm_manager.get_llm_by_id(self.model_id)
llm.bind_tools(tools)
return llm
Copy link
Contributor

Choose a reason for hiding this comment

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

Approve the bind_tools method with a suggestion.

The implementation of bind_tools is correct and aligns with the described functionality. However, it would benefit from a detailed docstring explaining the parameters and the expected behavior.

Consider adding a detailed docstring to the bind_tools method:

def bind_tools(
    self,
    tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
) -> LanguageModel:
    """
    Binds a sequence of tools to the language model.

    Args:
        tools (Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]]): A sequence of tools to be bound.

    Returns:
        LanguageModel: The language model with tools bound.
    """
    llm = self.llm_manager.get_llm_by_id(self.model_id)
    llm.bind_tools(tools)
    return llm

Comment on lines +46 to +52
@abstractmethod
def bind_tools(
self,
tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
) -> LanguageModel:
"""Bind tools"""
raise NotImplementedError
Copy link
Contributor

Choose a reason for hiding this comment

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

Approve the addition of bind_tools method with a suggestion.

The addition of the bind_tools method to the RequestHandler interface is crucial for ensuring consistency across implementations. It would benefit from a detailed docstring explaining the parameters and the expected behavior.

Consider adding a detailed docstring to the bind_tools method:

def bind_tools(
    self,
    tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
) -> LanguageModel:
    """
    Abstract method to bind tools to the language model.

    Args:
        tools (Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]]): A sequence of tools to be bound.

    Returns:
        LanguageModel: The language model with tools bound.
    """
    raise NotImplementedError

Comment on lines +38 to +44
def bind_tools(
self,
tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
**kwargs: Any,
) -> Runnable[LanguageModelInput, BaseMessage]:
self.request_handler.bind_tools(tools)
return self
Copy link
Contributor

Choose a reason for hiding this comment

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

New method bind_tools enhances flexibility.

The addition of the bind_tools method is a positive enhancement, allowing dynamic integration of various tools. Consider adding unit tests to verify the method's functionality with different types of tools.

Would you like me to help with creating these unit tests?

Comment on lines +77 to +89
def convert_langchain_tool_calls_to_iris_tool_calls(
tool_calls: List[ToolCall],
) -> List[ToolCallDTO]:
return [
ToolCallDTO(
function=FunctionDTO(
name=tc["name"],
arguments=json.dumps(tc["args"]),
),
id=tc["id"],
)
for tc in tool_calls
]
Copy link
Contributor

Choose a reason for hiding this comment

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

Approved: Effective tool call conversion with a note on JSON serialization.

The function convert_langchain_tool_calls_to_iris_tool_calls effectively converts tool calls for integration, using JSON serialization for arguments. Ensure that this serialization process is thoroughly tested to maintain data integrity and prevent issues in downstream processing.

Would you like assistance in setting up tests for the JSON serialization process?

Comment on lines +270 to +275
@tool
def lecture_content_retrieval() -> str:
"""
Retrieve content from indexed lecture slides.
This will run a RAG retrieval based on the chat history on the indexed lecture slides and return the most
relevant paragraphs.
This will run a RAG retrieval based on the chat history on the indexed lecture slides and return the
most relevant paragraphs.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest performance review for lecture content retrieval.

The method now incorporates chat history in retrieving lecture content, which could introduce performance bottlenecks depending on the size of the history and the efficiency of the retrieval algorithm.

Consider reviewing the performance of this method, especially under conditions of extensive chat histories, and explore potential optimizations.

@@ -121,6 +125,7 @@
:param dto: The pipeline execution data transfer object
:param kwargs: The keyword arguments
"""
print(dto.model_dump_json(indent=4))
Copy link
Contributor

Choose a reason for hiding this comment

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

Replace print statement with logging for production readiness.

Using print for debugging is common in development, but it's recommended to use logger.debug or logger.info for better control over logging levels and outputs in production environments.

Consider replacing the print statement with a logging statement:

- print(dto.model_dump_json(indent=4))
+ logger.debug(dto.model_dump_json(indent=4))
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
print(dto.model_dump_json(indent=4))
logger.debug(dto.model_dump_json(indent=4))

Comment on lines +110 to +152
class ExerciseChatAgentPipeline(Pipeline):
"""Exercise chat agent pipeline that answers exercises related questions from students."""

llm: IrisLangchainChatModel
pipeline: Runnable
callback: ExerciseChatStatusCallback
suggestion_pipeline: InteractionSuggestionPipeline
code_feedback_pipeline: CodeFeedbackPipeline
prompt: ChatPromptTemplate
variant: str

def __init__(self, callback: ExerciseChatStatusCallback, variant: str = "default"):
super().__init__(implementation_id="exercise_chat_pipeline")
# Set the langchain chat model
completion_args = CompletionArguments(temperature=0.5, max_tokens=2000)
self.llm = IrisLangchainChatModel(
request_handler=CapabilityRequestHandler(
requirements=RequirementList(
gpt_version_equivalent=4.5,
),
),
completion_args=completion_args,
)
self.variant = variant
self.callback = callback

# Create the pipelines
self.db = VectorDatabase()
self.suggestion_pipeline = InteractionSuggestionPipeline(variant="exercise")
self.retriever = LectureRetrieval(self.db.client)
self.reranker_pipeline = RerankerPipeline()
self.code_feedback_pipeline = CodeFeedbackPipeline()
self.pipeline = self.llm | JsonOutputParser()
self.citation_pipeline = CitationPipeline()

def __repr__(self):
return f"{self.__class__.__name__}(llm={self.llm})"

def __str__(self):
return f"{self.__class__.__name__}(llm={self.llm})"

@traceable(name="Exercise Chat Agent Pipeline")
def __call__(self, dto: ExerciseChatPipelineExecutionDTO):
Copy link
Contributor

Choose a reason for hiding this comment

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

Class: ExerciseChatAgentPipeline

The class is well-structured and encapsulates the functionality of an exercise chat agent pipeline. The use of dependency injection for components like VectorDatabase, LectureRetrieval, and various pipelines is a good practice. However, the class is quite large and handles multiple responsibilities, which might violate the Single Responsibility Principle (SRP).

Consider breaking down this class into smaller components or services that handle specific aspects of the pipeline, such as message handling, tool execution, and response generation, to improve modularity and maintainability.

Comment on lines +84 to +107
def convert_chat_history_to_str(chat_history: List[PyrisMessage]) -> str:
"""
Converts the chat history to a string
:param chat_history: The chat history
:return: The chat history as a string
"""

def map_message_role(role: IrisMessageRole) -> str:
if role == IrisMessageRole.SYSTEM:
return "System"
elif role == IrisMessageRole.ASSISTANT:
return "AI Tutor"
elif role == IrisMessageRole.USER:
return "Student"
else:
return "Unknown"

return "\n\n".join(
[
f"{map_message_role(message.sender)} {"" if not message.sent_at else f"at {message.sent_at.strftime(
"%Y-%m-%d %H:%M:%S")}"}: {message.contents[0].text_content}"
for message in chat_history
]
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Function: convert_chat_history_to_str

This function converts a list of chat messages into a formatted string. The nested function map_message_role is used to convert message roles into human-readable formats, which is a clean way to handle conditional logic. However, the function could be improved by handling potential exceptions that might occur during date formatting or when accessing message contents.

Consider adding error handling to manage cases where message.sent_at might be None or message.contents might be empty, which could lead to exceptions.

- f"{map_message_role(message.sender)} {"" if not message.sent_at else f"at {message.sent_at.strftime(
+ f"{map_message_role(message.sender)} {"" if not message.sent_at else f"at {message.sent_at.strftime('%Y-%m-%d %H:%M:%S') if message.sent_at else 'Unknown date'}"}: {message.contents[0].text_content if message.contents else 'No content'}"

Committable suggestion was skipped due to low confidence.

Comment on lines +151 to +591
HumanMessage(
"Consider the student's newest and latest input:"
),
convert_iris_message_to_langchain_human_message(query),
("placeholder", "{agent_scratchpad}"),
]
)
else:
self.prompt = ChatPromptTemplate.from_messages(
[
SystemMessage(
initial_prompt_with_date
+ "\n"
+ add_exercise_context_to_prompt(
exercise_title,
problem_statement,
programming_language,
)
+ agent_prompt
+ "\n"
+ format_reminder_prompt,
),
("placeholder", "{agent_scratchpad}"),
]
)

tools = generate_structured_tools_from_functions(
[
get_submission_details,
get_additional_exercise_details,
get_build_logs_analysis_tool,
get_feedbacks,
repository_files,
file_lookup,
]
)
agent = create_tool_calling_agent(
llm=self.llm, tools=tools, prompt=self.prompt
)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=False)
out = None
self.callback.in_progress()
invocation_result = agent_executor.invoke(params)
if invocation_result.get("output", None):
out = invocation_result["output"]

try:
self.callback.in_progress("Refining response...")
self.prompt = ChatPromptTemplate.from_messages(
[
SystemMessagePromptTemplate.from_template(guide_system_prompt),
]
)

guide_response = (self.prompt | self.llm | StrOutputParser()).invoke(
{
"response": out,
}
)
if "!ok!" in guide_response:
print("Response is ok and not rewritten!!!")
else:
out = guide_response
print("Response is rewritten.")

self.callback.done("Response created", final_result=out)
except Exception as e:
logger.error(
"An error occurred while running the course chat interaction suggestion pipeline",
exc_info=e,
)
traceback.print_exc()
self.callback.error("Error in refining response")
try:
if out:
suggestion_dto = InteractionSuggestionPipelineExecutionDTO()
suggestion_dto.chat_history = dto.chat_history
suggestion_dto.last_message = out
suggestions = self.suggestion_pipeline(suggestion_dto)
self.callback.done(final_result=None, suggestions=suggestions)
else:
# This should never happen but whatever
self.callback.skip(
"Skipping suggestion generation as no output was generated."
)
except Exception as e:
logger.error(
"An error occurred while running the course chat interaction suggestion pipeline",
exc_info=e,
)
traceback.print_exc()
self.callback.error("Generating interaction suggestions failed.")
except Exception as e:
logger.error(
"An error occurred while running the course chat pipeline", exc_info=e
)
traceback.print_exc()
self.callback.error(
"An error occurred while running the course chat pipeline."
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Method: __call__

This method orchestrates the execution of the chat agent pipeline. It is complex and handles multiple steps including logging, error handling, and invoking various tools. The method could benefit from further decomposition into smaller methods to improve readability and maintainability.

Refactor the __call__ method by extracting parts of its functionality into separate methods, such as handle_query, setup_prompt, and execute_tools, to make the code cleaner and easier to manage.

- def __call__(self, dto: ExerciseChatPipelineExecutionDTO):
+ def handle_query(self, dto):
+     # Logic to handle query extraction and validation
+
+ def setup_prompt(self, dto, query):
+     # Logic to setup the chat prompt based on the query and dto
+
+ def execute_tools(self, dto, params):
+     # Logic to execute tools and handle outputs
+
+ def __call__(self, dto: ExerciseChatPipelineExecutionDTO):
+     query = self.handle_query(dto)
+     self.setup_prompt(dto, query)
+     params = {}
+     self.execute_tools(dto, params)

Committable suggestion was skipped due to low confidence.

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

Successfully merging this pull request may close these issues.

2 participants