Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions ai_eval/shortanswer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Short answers Xblock with AI evaluation."""

import chardet
import logging
import hashlib
import urllib.parse
Expand Down Expand Up @@ -128,6 +129,15 @@ def validate_field_data(self, validation, data):
)
)

try:
self._get_attachments(data.attachment_urls)
except Exception:
validation.add(
ValidationMessage(
ValidationMessage.ERROR, _("Error downloading attachments"),
)
)

def student_view(self, context=None):
"""
The primary view of the ShortAnswerAIEvalXBlock, shown to students
Expand Down Expand Up @@ -164,15 +174,17 @@ def student_view(self, context=None):

def _download_attachment(self, url):
with urllib.request.urlopen(url) as f:
return f.read().decode('utf-8')
data = f.read()
encoding = chardet.detect(data)['encoding']
return data.decode(encoding)

def _filename_for_url(self, url):
return urllib.parse.urlparse(url).path.split('/')[-1]

def _get_attachments(self):
def _get_attachments(self, attachment_urls):
pool = Pool(self.ATTACHMENT_PARALLEL_DOWNLOADS)
attachments = pool.map(self._download_attachment, self.attachment_urls)
filenames = map(self._filename_for_url, self.attachment_urls)
attachments = pool.map(self._download_attachment, attachment_urls)
filenames = map(self._filename_for_url, attachment_urls)
return zip(filenames, attachments)

@XBlock.json_handler
Expand All @@ -182,7 +194,7 @@ def get_response(self, data, suffix=""): # pylint: disable=unused-argument

attachments = []
attachment_hash_inputs = []
for filename, contents in self._get_attachments():
for filename, contents in self._get_attachments(self.attachment_urls):
# Build system prompt attachment section (HTML-like) as before
attachments.append(f"""
<attachment>
Expand Down
9 changes: 9 additions & 0 deletions ai_eval/tests/test_ai_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""
# pylint: disable=redefined-outer-name,protected-access

import urllib.request
from unittest.mock import Mock, patch

import pytest
Expand Down Expand Up @@ -159,6 +160,14 @@ def test_shortanswer_attachments(shortanswer_block_data):
assert "<contents>file contents &lt;&amp;&gt;</contents>" in prompt


def test_shortanswer_attachments_encoding(shortanswer_block_data):
"""Test attachments for ShortAnswerAIEvalXBlock."""
block = ShortAnswerAIEvalXBlock(ToyRuntime(), DictFieldData(shortanswer_block_data), None)
urllib.request.urlopen = Mock(return_value=io.BytesIO("á".encode('latin-1')))
contents = block._download_attachment("http://example.com/1.txt")
assert contents == "á"


def test_multiagent_block_finished():
"""Test the MultiAgentAIEvalXBlock for not allowing input after finished."""
data = {
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ def package_data(pkg, roots):
),
install_requires=[
"XBlock",
"celery",
"chardet",
"litellm>=0.14,<1.0",
],
entry_points={
Expand Down