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

LLM: Add image recognition and image generation support #77

Closed
wants to merge 35 commits into from

Conversation

Hialus
Copy link
Member

@Hialus Hialus commented Mar 6, 2024

Motivation

In the future we want to support images in our LLM subsystem. This PR aims to introduce support for this.

Description

To add support for Images I added a new domain model PyrisImage to represent images in a unified way.
I also added a basic wrapper for OpenAIs Dall-E to showcase how images can be created.
I also added support to pass images to Ollama models via normal and chat completion.
I also added support for OpenAI GPT4-Vision models

@Hialus Hialus self-assigned this Mar 6, 2024
@Hialus Hialus marked this pull request as ready for review March 6, 2024 23:25
Copy link
Contributor

coderabbitai bot commented Mar 6, 2024

Warning

Rate Limit Exceeded

@yassinsws has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 5 minutes and 56 seconds before requesting another review.

How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.
Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.
Please see our FAQ for further information.

Commits Files that changed from the base of the PR and between 67d6bd0 and 2fe64ab.

Walkthrough

The recent update focuses on enhancing the application with image support in messages, expanding the domain model to include images, and improving external model interactions. It also establishes a robust content service layer for ingesting and retrieving lecture and repository data using Weaviate for vector database interactions. Chat pipelines have been refined to better handle exercise and lecture queries, with updated dependencies to support these new features.

Changes

File(s) Change Summary
app/domain/__init__.py, app/domain/iris_message.py,
app/domain/pyris_image.py, app/llm/external/model.py,
app/llm/external/ollama.py, app/llm/external/openai_chat.py
Integrated PyrisImage and IrisImage for supporting images in messages, modified external model interactions to handle image data, and introduced classes for image representation.
.env.example Added environment variables for Weaviate host and port configuration.
app/llm/external/openai_dalle.py Added OpenAIDalleWrapper class for image generation using OpenAI's DALL-E model.
app/content_service/Ingestion/...,
app/content_service/Retrieval/...
Established ingestion and retrieval classes for lectures and repositories using Weaviate, including abstract classes for generalized functionality.
app/content_service/get_lecture_from_artemis.py Implemented functionality to download lecture PDFs from Artemis.
app/pipeline/chat/... Refined chat pipelines for exercise and lecture queries, including separation of concerns and improved query handling.
app/pipeline/prompts/iris_tutor_chat_prompts.py Expanded prompts to support lecture guidance, emphasizing various educational values.
app/vector_database/... Introduced classes for managing schemas and connections to Weaviate Cloud Service.
requirements.txt Updated dependencies to include weaviate-client and PyMuPDF.

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.

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 as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • 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/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

coderabbitai[bot]
coderabbitai bot previously requested changes Mar 6, 2024
@Hialus Hialus added this to the 1.1.0 milestone Mar 15, 2024
… a httpx version >= 0.26 and ollama needs a version >= 0.25.2 and 0.26<,

Finished ingesting and retrieval classes for the lectures.
Added hybrid search instead of normal semantic search.
yassinsws and others added 9 commits March 18, 2024 01:24
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Comment on lines 15 to 41
class LectureChatPipeline(Pipeline):
"""Exercise chat pipeline that answers exercises related questions from students."""

llm: IrisLangchainChatModel
pipeline: Runnable
callback: TutorChatStatusCallback
prompt: ChatPromptTemplate
db: WeaviateClient

def __init__(self, callback: TutorChatStatusCallback, pipeline: Runnable, llm: IrisLangchainChatModel):
super().__init__(implementation_id="lecture_chat_pipeline")
self.llm = llm
self.callback = callback
self.pipeline = pipeline

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

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

def __call__(self, dto: TutorChatPipelineExecutionDTO, **kwargs):
"""
Runs the pipeline
:param kwargs: The keyword arguments
"""
pass
Copy link
Contributor

Choose a reason for hiding this comment

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

The LectureChatPipeline class is well-structured but lacks implementation in the __call__ method. Consider adding the implementation or a TODO comment to indicate that this functionality is a work in progress.

Would you like assistance in implementing the __call__ method?

Comment on lines 9 to 39
class VectorDatabase:
"""
Vector Database class
"""
def __init__(self):
"""weaviate_host = os.getenv("WEAVIATE_HOST")
weaviate_port = os.getenv("WEAVIATE_PORT")
assert weaviate_host, "WEAVIATE_HOST environment variable must be set"
assert weaviate_port, "WEAVIATE_PORT environment variable must be set"
assert (
weaviate_port.isdigit()
), "WEAVIATE_PORT environment variable must be an integer"
self._client = weaviate.connect_to_local(
host=weaviate_host, port=int(weaviate_port)
)"""
# Connect to the Weaviate Cloud Service until we set up a proper docker for this project
self.client = weaviate.connect_to_wcs(
cluster_url=os.getenv(
"https://try-repository-pipeline-99b1nlo4.weaviate.network"
), # Replace with your WCS URL
auth_credentials=weaviate.auth.AuthApiKey(
os.getenv("2IPqwB6mwGMIs92UJ3StB0Wovj0MquBxs9Ql")
), # Replace with your WCS key
)
print(self.client.is_ready())
self.repositories = init_repository_schema(self.client)
self.lectures = init_lecture_schema(self.client)

def __del__(self):
# Close the connection to Weaviate when the object is deleted
self.client.close()
Copy link
Contributor

Choose a reason for hiding this comment

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

The VectorDatabase class correctly manages Weaviate connections. However, the hardcoded WCS URL and API key pose security risks. Consider using environment variables for these values to enhance security and flexibility.

- cluster_url=os.getenv("https://try-repository-pipeline-99b1nlo4.weaviate.network"),
- auth_credentials=weaviate.auth.AuthApiKey(os.getenv("2IPqwB6mwGMIs92UJ3StB0Wovj0MquBxs9Ql")),
+ cluster_url=os.getenv("WEAVIATE_CLUSTER_URL"),
+ auth_credentials=weaviate.auth.AuthApiKey(os.getenv("WEAVIATE_API_KEY")),

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.

Suggested change
class VectorDatabase:
"""
Vector Database class
"""
def __init__(self):
"""weaviate_host = os.getenv("WEAVIATE_HOST")
weaviate_port = os.getenv("WEAVIATE_PORT")
assert weaviate_host, "WEAVIATE_HOST environment variable must be set"
assert weaviate_port, "WEAVIATE_PORT environment variable must be set"
assert (
weaviate_port.isdigit()
), "WEAVIATE_PORT environment variable must be an integer"
self._client = weaviate.connect_to_local(
host=weaviate_host, port=int(weaviate_port)
)"""
# Connect to the Weaviate Cloud Service until we set up a proper docker for this project
self.client = weaviate.connect_to_wcs(
cluster_url=os.getenv(
"https://try-repository-pipeline-99b1nlo4.weaviate.network"
), # Replace with your WCS URL
auth_credentials=weaviate.auth.AuthApiKey(
os.getenv("2IPqwB6mwGMIs92UJ3StB0Wovj0MquBxs9Ql")
), # Replace with your WCS key
)
print(self.client.is_ready())
self.repositories = init_repository_schema(self.client)
self.lectures = init_lecture_schema(self.client)
def __del__(self):
# Close the connection to Weaviate when the object is deleted
self.client.close()
class VectorDatabase:
"""
Vector Database class
"""
def __init__(self):
"""weaviate_host = os.getenv("WEAVIATE_HOST")
weaviate_port = os.getenv("WEAVIATE_PORT")
assert weaviate_host, "WEAVIATE_HOST environment variable must be set"
assert weaviate_port, "WEAVIATE_PORT environment variable must be set"
assert (
weaviate_port.isdigit()
), "WEAVIATE_PORT environment variable must be an integer"
self._client = weaviate.connect_to_local(
host=weaviate_host, port=int(weaviate_port)
)"""
# Connect to the Weaviate Cloud Service until we set up a proper docker for this project
self.client = weaviate.connect_to_wcs(
cluster_url=os.getenv(
"WEAVIATE_CLUSTER_URL"
), # Replace with your WCS URL
auth_credentials=weaviate.auth.AuthApiKey(
os.getenv("WEAVIATE_API_KEY")
), # Replace with your WCS key
)
print(self.client.is_ready())
self.repositories = init_repository_schema(self.client)
self.lectures = init_lecture_schema(self.client)
def __del__(self):
# Close the connection to Weaviate when the object is deleted
self.client.close()

Comment on lines 19 to 38
def _add_conversation_to_prompt(
chat_history: List[MessageDTO],
user_question: MessageDTO,
prompt: ChatPromptTemplate
):
"""
Adds the chat history and user question to the prompt
:param chat_history: The chat history
:param user_question: The user question
:return: The prompt with the chat history
"""
if chat_history is not None and len(chat_history) > 0:
chat_history_messages = [
message.convert_to_langchain_message() for message in chat_history
]
prompt += chat_history_messages
prompt += SystemMessagePromptTemplate.from_template(
"Now, consider the student's newest and latest input:"
)
prompt += user_question.convert_to_langchain_message()
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider refactoring the _add_conversation_to_prompt function for improved clarity and maintainability. Specifically, the way messages are added to the prompt could be streamlined. Here's a suggested refactor:

-        prompt += chat_history_messages
-        prompt += SystemMessagePromptTemplate.from_template(
-            "Now, consider the student's newest and latest input:"
-        )
+        prompt.add_messages(chat_history_messages)
+        prompt.add_message("Now, consider the student's newest and latest input:")

This assumes the ChatPromptTemplate and SystemMessagePromptTemplate have methods like add_messages and add_message for adding multiple messages or a single message, respectively. This change would make the code more readable and easier to maintain.


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.

Suggested change
def _add_conversation_to_prompt(
chat_history: List[MessageDTO],
user_question: MessageDTO,
prompt: ChatPromptTemplate
):
"""
Adds the chat history and user question to the prompt
:param chat_history: The chat history
:param user_question: The user question
:return: The prompt with the chat history
"""
if chat_history is not None and len(chat_history) > 0:
chat_history_messages = [
message.convert_to_langchain_message() for message in chat_history
]
prompt += chat_history_messages
prompt += SystemMessagePromptTemplate.from_template(
"Now, consider the student's newest and latest input:"
)
prompt += user_question.convert_to_langchain_message()
def _add_conversation_to_prompt(
chat_history: List[MessageDTO],
user_question: MessageDTO,
prompt: ChatPromptTemplate
):
"""
Adds the chat history and user question to the prompt
:param chat_history: The chat history
:param user_question: The user question
:return: The prompt with the chat history
"""
if chat_history is not None and len(chat_history) > 0:
chat_history_messages = [
message.convert_to_langchain_message() for message in chat_history
]
prompt.add_messages(chat_history_messages)
prompt.add_message("Now, consider the student's newest and latest input:")
prompt += user_question.convert_to_langchain_message()

@ls1intum ls1intum deleted a comment from coderabbitai bot Mar 29, 2024
@ls1intum ls1intum deleted a comment from coderabbitai bot Mar 29, 2024
Comment on lines 17 to 37
def add_conversation_to_prompt(
chat_history: List[MessageDTO],
user_question: MessageDTO,
prompt: ChatPromptTemplate,
):
"""
Adds the chat history and user question to the prompt
:param chat_history: The chat history
:param user_question: The user question
:return: The prompt with the chat history
"""
if chat_history is not None and len(chat_history) > 0:
chat_history_messages = [
message.convert_to_langchain_message() for message in chat_history
]
prompt += chat_history_messages
prompt += SystemMessagePromptTemplate.from_template(
"Now, consider the student's newest and latest input:"
)
prompt += user_question.convert_to_langchain_message()
return prompt
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider refactoring the add_conversation_to_prompt function for improved clarity and maintainability. Specifically, the way messages are added to the prompt could be streamlined to ensure compatibility and readability. Here's a suggested refactor:

-        prompt += chat_history_messages
-        prompt += SystemMessagePromptTemplate.from_template(
-            "Now, consider the student's newest and latest input:"
-        )
+        prompt.add_messages(chat_history_messages)
+        prompt.add_message("Now, consider the student's newest and latest input:")

This assumes the ChatPromptTemplate and SystemMessagePromptTemplate have methods like add_messages and add_message for adding multiple messages or a single message, respectively. This change would make the code more readable and easier to maintain.


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.

Suggested change
def add_conversation_to_prompt(
chat_history: List[MessageDTO],
user_question: MessageDTO,
prompt: ChatPromptTemplate,
):
"""
Adds the chat history and user question to the prompt
:param chat_history: The chat history
:param user_question: The user question
:return: The prompt with the chat history
"""
if chat_history is not None and len(chat_history) > 0:
chat_history_messages = [
message.convert_to_langchain_message() for message in chat_history
]
prompt += chat_history_messages
prompt += SystemMessagePromptTemplate.from_template(
"Now, consider the student's newest and latest input:"
)
prompt += user_question.convert_to_langchain_message()
return prompt
def add_conversation_to_prompt(
chat_history: List[MessageDTO],
user_question: MessageDTO,
prompt: ChatPromptTemplate,
):
"""
Adds the chat history and user question to the prompt
:param chat_history: The chat history
:param user_question: The user question
:return: The prompt with the chat history
"""
if chat_history is not None and len(chat_history) > 0:
chat_history_messages = [
message.convert_to_langchain_message() for message in chat_history
]
prompt.add_messages(chat_history_messages)
prompt.add_message("Now, consider the student's newest and latest input:")
prompt += user_question.convert_to_langchain_message()
return prompt

Comment on lines +2 to +11
from .lecture_chat_pipeline import LectureChatPipeline
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
AIMessagePromptTemplate,
)
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import Runnable

from ...domain.data.build_log_entry import BuildLogEntryDTO
from ...domain.data.feedback_dto import FeedbackDTO
from ..prompts.iris_tutor_chat_prompts import (
iris_initial_system_prompt,
chat_history_system_prompt,
final_system_prompt,
guide_system_prompt,
)
from ...domain import TutorChatPipelineExecutionDTO
from ...domain.data.submission_dto import SubmissionDTO
from ...domain.data.message_dto import MessageDTO
from ...web.status.status_update import TutorChatStatusCallback
from .file_selector_pipeline import FileSelectorPipeline
from ...llm import BasicRequestHandler, CompletionArguments
from ...llm.langchain import IrisLangchainChatModel

from ...llm.langchain import IrisLangchainChatModel, IrisLangchainEmbeddingModel
from ..pipeline import Pipeline
from .exercise_chat_pipeline import ExerciseChatPipeline
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider organizing imports according to PEP 8 guidelines, which recommend grouping imports in the following order: standard library imports, related third-party imports, and local application/library specific imports, with a blank line between each group. This enhances readability and maintainability.

import logging
+from langchain_core.output_parsers import StrOutputParser
+from langchain_core.prompts import PromptTemplate
+from langchain_core.runnables import Runnable
from .lecture_chat_pipeline import LectureChatPipeline
-from langchain_core.output_parsers import StrOutputParser
-from langchain_core.prompts import PromptTemplate
-from langchain_core.runnables import Runnable
from ...domain import TutorChatPipelineExecutionDTO
from ...web.status.status_update import TutorChatStatusCallback
from ...llm import BasicRequestHandler, CompletionArguments
from ...llm.langchain import IrisLangchainChatModel, IrisLangchainEmbeddingModel
from ..pipeline import Pipeline
from .exercise_chat_pipeline import ExerciseChatPipeline

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.

Suggested change
from .lecture_chat_pipeline import LectureChatPipeline
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
AIMessagePromptTemplate,
)
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import Runnable
from ...domain.data.build_log_entry import BuildLogEntryDTO
from ...domain.data.feedback_dto import FeedbackDTO
from ..prompts.iris_tutor_chat_prompts import (
iris_initial_system_prompt,
chat_history_system_prompt,
final_system_prompt,
guide_system_prompt,
)
from ...domain import TutorChatPipelineExecutionDTO
from ...domain.data.submission_dto import SubmissionDTO
from ...domain.data.message_dto import MessageDTO
from ...web.status.status_update import TutorChatStatusCallback
from .file_selector_pipeline import FileSelectorPipeline
from ...llm import BasicRequestHandler, CompletionArguments
from ...llm.langchain import IrisLangchainChatModel
from ...llm.langchain import IrisLangchainChatModel, IrisLangchainEmbeddingModel
from ..pipeline import Pipeline
from .exercise_chat_pipeline import ExerciseChatPipeline
import logging
from .lecture_chat_pipeline import LectureChatPipeline
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import Runnable
from ...domain import TutorChatPipelineExecutionDTO
from ...web.status.status_update import TutorChatStatusCallback
from ...llm import BasicRequestHandler, CompletionArguments
from ...llm.langchain import IrisLangchainChatModel, IrisLangchainEmbeddingModel
from ..pipeline import Pipeline
from .exercise_chat_pipeline import ExerciseChatPipeline

Comment on lines +60 to +82
:param user_question: The user question
:return: The prompt with the chat history
"""
if chat_history is not None and len(chat_history) > 0:
chat_history_messages = [
message.convert_to_langchain_message() for message in chat_history
]
self.prompt += chat_history_messages
self.prompt += SystemMessagePromptTemplate.from_template(
"Now, consider the student's newest and latest input:"
Classification:"""
)
self.prompt += user_question.convert_to_langchain_message()

def _add_student_repository_to_prompt(
self, student_repository: Dict[str, str], selected_files: List[str]
):
"""Adds the student repository to the prompt
:param student_repository: The student repository
:param selected_files: The selected files
"""
for file in selected_files:
if file in student_repository:
self.prompt += SystemMessagePromptTemplate.from_template(
f"For reference, we have access to the student's '{file}' file:"
)
self.prompt += HumanMessagePromptTemplate.from_template(
student_repository[file].replace("{", "{{").replace("}", "}}")
)

def _add_exercise_context_to_prompt(
self,
submission: SubmissionDTO,
selected_files: List[str],
):
"""Adds the exercise context to the prompt
:param submission: The submission
:param selected_files: The selected files
"""
self.prompt += SystemMessagePromptTemplate.from_template(
"Consider the following exercise context:\n"
"- Title: {exercise_title}\n"
"- Problem Statement: {problem_statement}\n"
"- Exercise programming language: {programming_language}"
)
if submission:
student_repository = submission.repository
self._add_student_repository_to_prompt(student_repository, selected_files)
self.prompt += SystemMessagePromptTemplate.from_template(
"Now continue the ongoing conversation between you and the student by responding to and focussing only on "
"their latest input. Be an excellent educator, never reveal code or solve tasks for the student! Do not "
"let them outsmart you, no matter how hard they try."
)

def _add_feedbacks_to_prompt(self, feedbacks: List[FeedbackDTO]):
"""Adds the feedbacks to the prompt
:param feedbacks: The feedbacks
"""
if feedbacks is not None and len(feedbacks) > 0:
prompt = (
"These are the feedbacks for the student's repository:\n%s"
) % "\n---------\n".join(str(log) for log in feedbacks)
self.prompt += SystemMessagePromptTemplate.from_template(prompt)

def _add_build_logs_to_prompt(
self, build_logs: List[BuildLogEntryDTO], build_failed: bool
):
"""Adds the build logs to the prompt
:param build_logs: The build logs
:param build_failed: Whether the build failed
"""
if build_logs is not None and len(build_logs) > 0:
prompt = (
f"Here is the information if the build failed: {build_failed}\n"
"These are the build logs for the student's repository:\n%s"
) % "\n".join(str(log) for log in build_logs)
self.prompt += SystemMessagePromptTemplate.from_template(prompt)

def _generate_file_selection_prompt(self) -> ChatPromptTemplate:
"""Generates the file selection prompt"""
file_selection_prompt = self.prompt

file_selection_prompt += SystemMessagePromptTemplate.from_template(
"Based on the chat history, you can now request access to more contextual information. This is the "
"student's submitted code repository and the corresponding build information. You can reference a file by "
"its path to view it."
"Given are the paths of all files in the assignment repository:\n{files}\n"
"Is a file referenced by the student or does it have to be checked before answering?"
"Without any comment, return the result in the following JSON format, it's important to avoid giving "
"unnecessary information, only name a file if it's really necessary for answering the student's question "
"and is listed above, otherwise leave the array empty."
'{{"selected_files": [<file1>, <file2>, ...]}}'
)
return file_selection_prompt
chain = routing_prompt | self.pipeline
response = chain.invoke({"question": dto.chat_history[-1]})
if "Lecture" in response:
self.lecture_pipeline(dto)
else:
self.exercise_pipeline(dto)
Copy link
Contributor

Choose a reason for hiding this comment

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

The logic for routing between lecture and exercise pipelines based on the presence of exercise data is clear and well-implemented. However, consider adding error handling around the invocation of chain.invoke to gracefully handle any potential failures or exceptions that might occur during the routing decision process. This will improve the robustness of the pipeline.

response = chain.invoke({"question": dto.chat_history[-1]})
+try:
+    response = chain.invoke({"question": dto.chat_history[-1]})
+except Exception as e:
+    logger.error(f"Error during routing decision: {e}")
+    # Handle the error appropriately, possibly with a fallback or error message to the user

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.

Suggested change
# Lecture or Exercise query ?
if dto.exercise is None:
# Execute lecture content pipeline
self.lecture_pipeline(dto)
else:
self.callback.skip("No submission found")
# Add the exercise context to the prompt
self._add_exercise_context_to_prompt(
submission,
selected_files,
)
routing_prompt = PromptTemplate.from_template(
"""Given the user question below, classify it as either being about `Lecture` or
`Exercise`.
self.callback.in_progress("Generating response...")
Do not respond with more than one word.
# Add the final message to the prompt and run the pipeline
self.prompt += SystemMessagePromptTemplate.from_template(final_system_prompt)
prompt_val = self.prompt.format_messages(
exercise_title=exercise_title,
problem_statement=problem_statement,
programming_language=programming_language,
)
self.prompt = ChatPromptTemplate.from_messages(prompt_val)
try:
response_draft = (self.prompt | self.pipeline).invoke({})
self.prompt += AIMessagePromptTemplate.from_template(f"{response_draft}")
self.prompt += SystemMessagePromptTemplate.from_template(
guide_system_prompt
)
response = (self.prompt | self.pipeline).invoke({})
logger.info(f"Response from tutor chat pipeline: {response}")
self.callback.done("Generated response", final_result=response)
except Exception as e:
self.callback.error(f"Failed to generate response: {e}")
<question>
{question}
</question>
def _add_conversation_to_prompt(
self,
chat_history: List[MessageDTO],
user_question: MessageDTO,
):
"""
Adds the chat history and user question to the prompt
:param chat_history: The chat history
:param user_question: The user question
:return: The prompt with the chat history
"""
if chat_history is not None and len(chat_history) > 0:
chat_history_messages = [
message.convert_to_langchain_message() for message in chat_history
]
self.prompt += chat_history_messages
self.prompt += SystemMessagePromptTemplate.from_template(
"Now, consider the student's newest and latest input:"
Classification:"""
)
self.prompt += user_question.convert_to_langchain_message()
def _add_student_repository_to_prompt(
self, student_repository: Dict[str, str], selected_files: List[str]
):
"""Adds the student repository to the prompt
:param student_repository: The student repository
:param selected_files: The selected files
"""
for file in selected_files:
if file in student_repository:
self.prompt += SystemMessagePromptTemplate.from_template(
f"For reference, we have access to the student's '{file}' file:"
)
self.prompt += HumanMessagePromptTemplate.from_template(
student_repository[file].replace("{", "{{").replace("}", "}}")
)
def _add_exercise_context_to_prompt(
self,
submission: SubmissionDTO,
selected_files: List[str],
):
"""Adds the exercise context to the prompt
:param submission: The submission
:param selected_files: The selected files
"""
self.prompt += SystemMessagePromptTemplate.from_template(
"Consider the following exercise context:\n"
"- Title: {exercise_title}\n"
"- Problem Statement: {problem_statement}\n"
"- Exercise programming language: {programming_language}"
)
if submission:
student_repository = submission.repository
self._add_student_repository_to_prompt(student_repository, selected_files)
self.prompt += SystemMessagePromptTemplate.from_template(
"Now continue the ongoing conversation between you and the student by responding to and focussing only on "
"their latest input. Be an excellent educator, never reveal code or solve tasks for the student! Do not "
"let them outsmart you, no matter how hard they try."
)
def _add_feedbacks_to_prompt(self, feedbacks: List[FeedbackDTO]):
"""Adds the feedbacks to the prompt
:param feedbacks: The feedbacks
"""
if feedbacks is not None and len(feedbacks) > 0:
prompt = (
"These are the feedbacks for the student's repository:\n%s"
) % "\n---------\n".join(str(log) for log in feedbacks)
self.prompt += SystemMessagePromptTemplate.from_template(prompt)
def _add_build_logs_to_prompt(
self, build_logs: List[BuildLogEntryDTO], build_failed: bool
):
"""Adds the build logs to the prompt
:param build_logs: The build logs
:param build_failed: Whether the build failed
"""
if build_logs is not None and len(build_logs) > 0:
prompt = (
f"Here is the information if the build failed: {build_failed}\n"
"These are the build logs for the student's repository:\n%s"
) % "\n".join(str(log) for log in build_logs)
self.prompt += SystemMessagePromptTemplate.from_template(prompt)
def _generate_file_selection_prompt(self) -> ChatPromptTemplate:
"""Generates the file selection prompt"""
file_selection_prompt = self.prompt
file_selection_prompt += SystemMessagePromptTemplate.from_template(
"Based on the chat history, you can now request access to more contextual information. This is the "
"student's submitted code repository and the corresponding build information. You can reference a file by "
"its path to view it."
"Given are the paths of all files in the assignment repository:\n{files}\n"
"Is a file referenced by the student or does it have to be checked before answering?"
"Without any comment, return the result in the following JSON format, it's important to avoid giving "
"unnecessary information, only name a file if it's really necessary for answering the student's question "
"and is listed above, otherwise leave the array empty."
'{{"selected_files": [<file1>, <file2>, ...]}}'
)
return file_selection_prompt
chain = routing_prompt | self.pipeline
response = chain.invoke({"question": dto.chat_history[-1]})
if "Lecture" in response:
self.lecture_pipeline(dto)
else:
self.exercise_pipeline(dto)
# Lecture or Exercise query ?
if dto.exercise is None:
# Execute lecture content pipeline
self.lecture_pipeline(dto)
else:
routing_prompt = PromptTemplate.from_template(
"""Given the user question below, classify it as either being about `Lecture` or
`Exercise`.
Do not respond with more than one word.
<question>
{question}
</question>
Classification:"""
)
chain = routing_prompt | self.pipeline
try:
response = chain.invoke({"question": dto.chat_history[-1]})
except Exception as e:
logger.error(f"Error during routing decision: {e}")
# Handle the error appropriately, possibly with a fallback or error message to the user
if "Lecture" in response:
self.lecture_pipeline(dto)
else:
self.exercise_pipeline(dto)

@yassinsws yassinsws assigned yassinsws and unassigned Hialus Mar 31, 2024
@yassinsws yassinsws closed this Mar 31, 2024
@yassinsws yassinsws deleted the feature/llm/image-support branch March 31, 2024 20:24
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.

3 participants