From 70e7d654112257a25a6c6d10cfa1990c255b9cd9 Mon Sep 17 00:00:00 2001 From: muhammad-ammar Date: Fri, 8 Mar 2024 17:12:19 +0500 Subject: [PATCH] feat: add utils to communicate with openai --- .../apps/ai_curation/api/serializers.py | 2 +- enterprise_catalog/apps/ai_curation/errors.py | 17 ++ .../apps/ai_curation/openai_client.py | 112 +++++++++ .../apps/ai_curation/tests/test_utils.py | 232 +++++++++++++++++- .../apps/ai_curation/tests/test_views.py | 9 + enterprise_catalog/apps/ai_curation/utils.py | 78 ++++++ enterprise_catalog/settings/base.py | 10 + requirements/base.in | 1 + requirements/base.txt | 60 ++++- requirements/constraints.txt | 2 + requirements/dev.txt | 105 ++++++-- requirements/django.txt | 2 +- requirements/doc.txt | 89 +++++-- requirements/pip-tools.txt | 12 +- requirements/pip.txt | 2 +- requirements/production.txt | 74 +++++- requirements/quality.txt | 76 +++++- requirements/test.txt | 87 +++++-- requirements/validation.txt | 97 ++++++-- 19 files changed, 970 insertions(+), 97 deletions(-) create mode 100644 enterprise_catalog/apps/ai_curation/errors.py create mode 100644 enterprise_catalog/apps/ai_curation/openai_client.py diff --git a/enterprise_catalog/apps/ai_curation/api/serializers.py b/enterprise_catalog/apps/ai_curation/api/serializers.py index 17924bb4e..95db21f90 100644 --- a/enterprise_catalog/apps/ai_curation/api/serializers.py +++ b/enterprise_catalog/apps/ai_curation/api/serializers.py @@ -8,5 +8,5 @@ class AICurationSerializer(serializers.Serializer): # pylint: disable=abstract- """ Serializer for AI Curation. """ - query = serializers.CharField() + query = serializers.CharField(max_length=300) catalog_id = serializers.UUIDField() diff --git a/enterprise_catalog/apps/ai_curation/errors.py b/enterprise_catalog/apps/ai_curation/errors.py new file mode 100644 index 000000000..ad0a00e1c --- /dev/null +++ b/enterprise_catalog/apps/ai_curation/errors.py @@ -0,0 +1,17 @@ +""" +AI curation errors +""" + +USER_MESSAGE = "Something went wrong. Please wait a minute and try again. If the issue persists, please reach out to your contact at edX." # pylint: disable=line-too-long + + +class AICurationError(Exception): + def __init__(self, message=USER_MESSAGE, dev_message=None, status_code=None): + super().__init__(message) + self.message = message + self.dev_message = dev_message or message + self.status_code = status_code + + +class InvalidJSONResponseError(AICurationError): + """Invalid JSON response received""" diff --git a/enterprise_catalog/apps/ai_curation/openai_client.py b/enterprise_catalog/apps/ai_curation/openai_client.py new file mode 100644 index 000000000..33b9b44d1 --- /dev/null +++ b/enterprise_catalog/apps/ai_curation/openai_client.py @@ -0,0 +1,112 @@ +import functools +import logging + +import backoff +import simplejson +from django.conf import settings +from openai import ( + APIConnectionError, + APIError, + APITimeoutError, + InternalServerError, + OpenAI, + RateLimitError, +) + +from enterprise_catalog.apps.ai_curation.errors import ( + AICurationError, + InvalidJSONResponseError, +) + + +LOGGER = logging.getLogger(__name__) + +client = OpenAI(api_key=settings.OPENAI_API_KEY) + + +def api_error_handler(func): + """ + Decorator that activates when the API continues to raise persistent errors even after retries. + + Raises a custom exception with the following attributes: + - message (str): A user-friendly message + - dev_message (str): The actual error message returned by the API + - status_code (int): The actual error code returned by the API + """ + @functools.wraps(func) + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except (APIError, AICurationError) as ex: + LOGGER.exception('[AI_CURATION] API Error: Prompt: [%s]', kwargs.get('messages')) + # status_code attribute is not available for all exceptions, such as APIConnectionError and APITimeoutError + status_code = getattr(ex, 'status_code', None) + message = getattr(ex, 'message', None) + raise AICurationError(dev_message=message, status_code=status_code) from ex + return wrapper + + +@api_error_handler +@backoff.on_exception( + backoff.expo, + (APIConnectionError, APITimeoutError, InternalServerError, RateLimitError, InvalidJSONResponseError), + max_tries=3, +) +def chat_completions( + messages, + response_format='json', + response_type=list, + model="gpt-4", + temperature=0.3, + max_tokens=500, +): + """ + Get a response from the chat.completions endpoint + + Args: + messages (list): List of messages to send to the chat.completions endpoint + response_format (str): Format of the response. Can be 'json' or 'text' + response_type (any): Expected type of the response. For now we only expect `list` + model (str): Model to use for the completion + temperature (number): Make model output more focused and deterministic + max_tokens (int): Maximum number of tokens that can be generated in the chat completion + + Returns: + list: The response from the chat.completions endpoint + + Throws: + AICurationError: Raise an exception with the below attributes + - message (str): A user-friendly message + - dev_message (str): The actual error message returned by the API + - status_code (int): The actual error code returned by the API + """ + LOGGER.info('[AI_CURATION] [CHAT_COMPLETIONS] Prompt: [%s]', messages) + response = client.chat.completions.create( + messages=messages, + model=model, + temperature=temperature, + max_tokens=max_tokens, + ) + LOGGER.info('[AI_CURATION] [CHAT_COMPLETIONS] Response: [%s]', response) + response_content = response.choices[0].message.content + + if response_format == 'json': + try: + json_response = simplejson.loads(response_content) + if isinstance(json_response, response_type): + return json_response + LOGGER.error( + '[AI_CURATION] JSON response received but response type is incorrect: Prompt: [%s], Response: [%s]', + messages, + response + ) + raise InvalidJSONResponseError('Invalid response type received from chatgpt') + except simplejson.errors.JSONDecodeError as ex: + LOGGER.error( + '[AI_CURATION] Invalid JSON response received from chatgpt: Prompt: [%s], Response: [%s]', + messages, + response + ) + raise InvalidJSONResponseError('Invalid JSON response received from chatgpt') from ex + + return response_content diff --git a/enterprise_catalog/apps/ai_curation/tests/test_utils.py b/enterprise_catalog/apps/ai_curation/tests/test_utils.py index 9d623d72a..152ea61ee 100644 --- a/enterprise_catalog/apps/ai_curation/tests/test_utils.py +++ b/enterprise_catalog/apps/ai_curation/tests/test_utils.py @@ -1,12 +1,28 @@ """ -Tests for the views of the ai_curation app. +Tests for ai_curation app utils. """ +import json +import logging from unittest import mock +from unittest.mock import MagicMock, patch +import httpx +from django.conf import settings from django.test import TestCase +from openai import APIConnectionError +from enterprise_catalog.apps.ai_curation.errors import AICurationError from enterprise_catalog.apps.ai_curation.utils import ( + chat_completions, fetch_catalog_metadata_from_algolia, + get_filtered_subjects, + get_keywords_to_prose, + get_query_keywords, +) + + +CHAT_COMPLETIONS_API_KEYWARGS = dict( + model='gpt-4', temperature=0.3, max_tokens=500 ) @@ -50,3 +66,217 @@ def test_fetch_catalog_metadata_from_algolia(self, mock_algolia_client): self.assertEqual(sorted(subjects), ["Business & Management", "Computer Science", "Data Analysis & Statistics", "Economics & Finance", "Electronics", "Engineering", "Philosophy & Ethics"]) + + +class TestChatCompletionUtils(TestCase): + @patch('enterprise_catalog.apps.ai_curation.utils.LOGGER') + @patch('enterprise_catalog.apps.ai_curation.openai_client.client.chat.completions.create') + def test_get_filtered_subjects(self, mock_create, mock_logger): + """ + Test that get_filtered_subjects returns the correct filtered subjects + """ + mock_create.return_value = MagicMock( + choices=[MagicMock(message=MagicMock(content=json.dumps(['subject1', 'subject2'])))] + ) + subjects = ['subject1', 'subject2', 'subject3', 'subject4'] + query = 'test query' + expected_content = settings.AI_CURATION_FILTER_SUBJECTS_PROMPT.format(query=query, subjects=subjects) + + result = get_filtered_subjects(query, subjects) + + mock_create.assert_called_once_with( + messages=[{'role': 'system', 'content': expected_content}], **CHAT_COMPLETIONS_API_KEYWARGS + ) + mock_logger.info.assert_has_calls( + [ + mock.call( + '[AI_CURATION] Filtering subjects. Prompt: [%s]', + [{'role': 'system', 'content': expected_content}] + ), + mock.call('[AI_CURATION] Filtering subjects. Response: [%s]', ['subject1', 'subject2']) + ] + ) + assert result == ['subject1', 'subject2'] + + @patch('enterprise_catalog.apps.ai_curation.openai_client.LOGGER') + @patch('enterprise_catalog.apps.ai_curation.openai_client.client.chat.completions.create') + def test_invalid_json(self, mock_create, mock_logger): + """ + Test that correct exception is raised if chat.completions.create send an invalid json + """ + mock_create.return_value = MagicMock(choices=[MagicMock(message=MagicMock(content='non json response'))]) + + messages = [ + { + 'role': 'system', + 'content': 'I am a prompt' + } + ] + with self.assertRaises(AICurationError): + chat_completions(messages) + + assert mock_create.call_count == 3 + assert mock_logger.error.called + mock_logger.error.assert_has_calls([ + mock.call( + '[AI_CURATION] Invalid JSON response received from chatgpt: Prompt: [%s], Response: [%s]', + [{'role': 'system', 'content': 'I am a prompt'}], + mock.ANY + ), + mock.call( + '[AI_CURATION] Invalid JSON response received from chatgpt: Prompt: [%s], Response: [%s]', + [{'role': 'system', 'content': 'I am a prompt'}], + mock.ANY + ), + mock.call( + '[AI_CURATION] Invalid JSON response received from chatgpt: Prompt: [%s], Response: [%s]', + [{'role': 'system', 'content': 'I am a prompt'}], + mock.ANY + ) + ]) + + @patch('enterprise_catalog.apps.ai_curation.openai_client.LOGGER') + @patch('enterprise_catalog.apps.ai_curation.openai_client.client.chat.completions.create') + def test_valid_json_with_wrong_type(self, mock_create, mock_logger): + """ + Test that correct exception is raised if chat.completions.create send a valid json but wrong type + """ + mock_create.return_value = MagicMock(choices=[MagicMock(message=MagicMock(content='{"a": 1}'))]) + + messages = [ + { + 'role': 'system', + 'content': 'I am a prompt' + } + ] + with self.assertRaises(AICurationError): + chat_completions(messages) + + assert mock_create.call_count == 3 + assert mock_logger.error.called + mock_logger.error.assert_has_calls([ + mock.call( + '[AI_CURATION] JSON response received but response type is incorrect: Prompt: [%s], Response: [%s]', + [{'role': 'system', 'content': 'I am a prompt'}], + mock.ANY + ), + mock.call( + '[AI_CURATION] JSON response received but response type is incorrect: Prompt: [%s], Response: [%s]', + [{'role': 'system', 'content': 'I am a prompt'}], + mock.ANY + ), + mock.call( + '[AI_CURATION] JSON response received but response type is incorrect: Prompt: [%s], Response: [%s]', + [{'role': 'system', 'content': 'I am a prompt'}], + mock.ANY + ) + ]) + + @patch('enterprise_catalog.apps.ai_curation.utils.LOGGER') + @patch('enterprise_catalog.apps.ai_curation.openai_client.client.chat.completions.create') + def test_get_query_keywords(self, mock_create, mock_logger): + """ + Test that get_query_keywords returns the correct keywords + """ + mock_create.return_value = MagicMock( + choices=[MagicMock(message=MagicMock(content=json.dumps(['keyword1', 'keyword2'])))] + ) + query = 'test query' + expected_content = settings.AI_CURATION_QUERY_TO_KEYWORDS_PROMPT.format(query=query) + + result = get_query_keywords(query) + + mock_create.assert_called_once_with( + messages=[{'role': 'system', 'content': expected_content}], **CHAT_COMPLETIONS_API_KEYWARGS + ) + mock_logger.info.assert_has_calls( + [ + mock.call( + '[AI_CURATION] Generating keywords. Prompt: [%s]', + [{'role': 'system', 'content': expected_content}] + ), + mock.call('[AI_CURATION] Generating keywords. Response: [%s]', ['keyword1', 'keyword2']) + ] + ) + assert result == ['keyword1', 'keyword2'] + + @patch('enterprise_catalog.apps.ai_curation.utils.LOGGER') + @patch('enterprise_catalog.apps.ai_curation.openai_client.client.chat.completions.create') + @patch('enterprise_catalog.apps.ai_curation.utils.get_query_keywords') + def test_get_keywords_to_prose(self, mock_get_query_keywords, mock_create, mock_logger): + """ + Test that get_keywords_to_prose returns the correct prose + """ + mock_get_query_keywords.return_value = ['keyword1', 'keyword2'] + mock_create.return_value = MagicMock( + choices=[MagicMock(message=MagicMock(content=json.dumps(['I am a prose'])))] + ) + query = 'test query' + keywords = ['keyword1', 'keyword2'] + expected_content = settings.AI_CURATION_KEYWORDS_TO_PROSE_PROMPT.format(query=query, keywords=keywords) + + result = get_keywords_to_prose(query) + + mock_create.assert_called_once_with( + messages=[{'role': 'system', 'content': expected_content}], **CHAT_COMPLETIONS_API_KEYWARGS + ) + mock_logger.info.assert_has_calls( + [ + mock.call( + '[AI_CURATION] Generating prose from keywords. Prompt: [%s]', + [{'role': 'system', 'content': expected_content}] + ), + mock.call('[AI_CURATION] Generating prose from keywords. Response: [%s]', ['I am a prose']) + ] + ) + assert result == 'I am a prose' + + @patch('enterprise_catalog.apps.ai_curation.openai_client.LOGGER') + @patch('enterprise_catalog.apps.ai_curation.openai_client.client.chat.completions.create') + def test_chat_completions_retries(self, mock_create, mock_logger): + """ + Test that retries work as expected for chat_completions + """ + mock_create.side_effect = APIConnectionError(request=httpx.Request("GET", "https://api.example.com")) + messages = [ + { + 'role': 'system', + 'content': 'I am a prompt' + } + ] + with self.assertRaises(AICurationError): + backoff_logger = logging.getLogger('backoff') + with mock.patch.multiple(backoff_logger, info=mock.DEFAULT, error=mock.DEFAULT) as mock_backoff_logger: + chat_completions(messages=messages) + + assert mock_create.call_count == 3 + assert mock_backoff_logger['info'].call_count == 2 + mock_backoff_logger['info'].assert_has_calls( + [ + mock.call( + 'Backing off %s(...) for %.1fs (%s)', + 'chat_completions', + mock.ANY, + 'openai.APIConnectionError: Connection error.' + ), + mock.call( + 'Backing off %s(...) for %.1fs (%s)', + 'chat_completions', + mock.ANY, + 'openai.APIConnectionError: Connection error.' + ) + ] + ) + assert mock_backoff_logger['error'].call_count == 1 + mock_backoff_logger['error'].assert_has_calls( + [ + mock.call( + 'Giving up %s(...) after %d tries (%s)', + 'chat_completions', + 3, + 'openai.APIConnectionError: Connection error.' + ) + ] + ) + assert mock_logger.exception.called + mock_logger.exception.assert_has_calls([mock.call('[AI_CURATION] API Error: Prompt: [%s]', messages)]) diff --git a/enterprise_catalog/apps/ai_curation/tests/test_views.py b/enterprise_catalog/apps/ai_curation/tests/test_views.py index 3c4944e59..8cc56d3b2 100644 --- a/enterprise_catalog/apps/ai_curation/tests/test_views.py +++ b/enterprise_catalog/apps/ai_curation/tests/test_views.py @@ -55,3 +55,12 @@ def test_post(self, mock_trigger_ai_curations): self.assertEqual(response.data['status'], AICurationStatus.PENDING) mock_trigger_ai_curations.delay.assert_called_once() + + def test_post_with_query(self): + """ + Verify that the api returns error if query length is greater than 300 characters + """ + data = {'query': 'a' * 301, 'catalog_id': str(uuid4())} + response = self.client.post(self.url, data) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.json(), {'query': ['Ensure this field has no more than 300 characters.']}) diff --git a/enterprise_catalog/apps/ai_curation/utils.py b/enterprise_catalog/apps/ai_curation/utils.py index efb5d41f4..b5c8cafe9 100644 --- a/enterprise_catalog/apps/ai_curation/utils.py +++ b/enterprise_catalog/apps/ai_curation/utils.py @@ -3,6 +3,9 @@ """ from logging import getLogger +from django.conf import settings + +from enterprise_catalog.apps.ai_curation.openai_client import chat_completions from enterprise_catalog.apps.catalog.algolia_utils import ( get_initialized_algolia_client, ) @@ -46,3 +49,78 @@ def fetch_catalog_metadata_from_algolia(enterprise_catalog_query_title): page = algolia_client.algolia_index.search('', search_options) return ocm_courses, exec_ed_courses, programs, list(subjects) + + +def get_filtered_subjects(query, subjects): + """ + Find the top 2-3 most relevant subjects based on the query + + Args: + query (str): Search query given by the user + subjects (list): The list of available course subjects + + Returns: + list: Top 2-3 most relevant subjects + """ + content = settings.AI_CURATION_FILTER_SUBJECTS_PROMPT.format(query=query, subjects=subjects) + messages = [ + { + 'role': 'system', + 'content': content + } + ] + LOGGER.info('[AI_CURATION] Filtering subjects. Prompt: [%s]', messages) + filtered_subjects = chat_completions(messages=messages) + LOGGER.info('[AI_CURATION] Filtering subjects. Response: [%s]', filtered_subjects) + return filtered_subjects + + +def get_query_keywords(query): + """ + Generate a list of 4-8 single word keywords to transform the query into relevant subjects and skills + + Args: + query (str): Search query given by the user + + Returns: + list: 4-8 single word keywords + """ + content = settings.AI_CURATION_QUERY_TO_KEYWORDS_PROMPT.format(query=query) + messages = [ + { + 'role': 'system', + 'content': content + } + ] + LOGGER.info('[AI_CURATION] Generating keywords. Prompt: [%s]', messages) + keywords = chat_completions(messages=messages) + LOGGER.info('[AI_CURATION] Generating keywords. Response: [%s]', keywords) + return keywords + + +def get_keywords_to_prose(query): + """ + Get an expanded version of the query, roughly 100 words in length, stuffed with the keywords + + Args: + query (str): Search query given by the user + + Returns: + str: Expanded version of the query + """ + keywords = get_query_keywords(query) + content = settings.AI_CURATION_KEYWORDS_TO_PROSE_PROMPT.format(query=query, keywords=keywords) + messages = [ + { + 'role': 'system', + 'content': content + } + ] + LOGGER.info('[AI_CURATION] Generating prose from keywords. Prompt: [%s]', messages) + keywords_to_prose = chat_completions(messages=messages) + LOGGER.info('[AI_CURATION] Generating prose from keywords. Response: [%s]', keywords_to_prose) + # keywords_to_prose will always be a list - empty or with a valid prose + if keywords_to_prose: + return keywords_to_prose[0] + + return '' diff --git a/enterprise_catalog/settings/base.py b/enterprise_catalog/settings/base.py index 05ed146d1..752662903 100644 --- a/enterprise_catalog/settings/base.py +++ b/enterprise_catalog/settings/base.py @@ -437,3 +437,13 @@ 'VERSION': '1.0.0', 'SERVE_INCLUDE_SCHEMA': False, } + +############################################## AI CURATION ############################################## + +AI_CURATION_FILTER_SUBJECTS_PROMPT = 'I am a filter subjects prompt. query: {query} subjects: {subjects}' +AI_CURATION_QUERY_TO_KEYWORDS_PROMPT = 'I am a query to keywords prompt. query: {query}' +AI_CURATION_KEYWORDS_TO_PROSE_PROMPT = 'I am a keywords to prose prompt. query: {query} keywords: {keywords}' + +OPENAI_API_KEY = 'I am key' + +############################################## AI CURATION ############################################## diff --git a/requirements/base.in b/requirements/base.in index aa14a4bd7..cd497e693 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -38,3 +38,4 @@ social-auth-app-django xlsxwriter django-clearcache django-log-request-id +openai diff --git a/requirements/base.txt b/requirements/base.txt index 884bbdeda..7825f2932 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -10,6 +10,12 @@ amqp==5.2.0 # via kombu analytics-python==1.4.post1 # via -r requirements/base.in +annotated-types==0.6.0 + # via pydantic +anyio==4.3.0 + # via + # httpx + # openai asgiref==3.7.2 # via # django @@ -35,7 +41,10 @@ celery==5.3.6 # django-celery-results # edx-celeryutils certifi==2024.2.2 - # via requests + # via + # httpcore + # httpx + # requests cffi==1.16.0 # via # cryptography @@ -67,7 +76,9 @@ defusedxml==0.7.1 # djangorestframework-xml # python3-openid # social-auth-core -django==4.2.10 +distro==1.9.0 + # via openai +django==4.2.11 # via # -c requirements/constraints.txt # -r requirements/base.in @@ -146,7 +157,7 @@ edx-celeryutils==1.2.5 # via -r requirements/base.in edx-django-release-util==1.3.0 # via -r requirements/base.in -edx-django-utils==5.10.1 +edx-django-utils==5.11.0 # via # -r requirements/base.in # django-config-models @@ -165,9 +176,20 @@ edx-rest-api-client==5.6.1 # via -r requirements/base.in edx-toggles==5.1.1 # via -r requirements/base.in +exceptiongroup==1.2.0 + # via anyio +h11==0.14.0 + # via httpcore +httpcore==1.0.4 + # via httpx +httpx==0.27.0 + # via openai idna==3.6 - # via requests -importlib-resources==6.1.2 + # via + # anyio + # httpx + # requests +importlib-resources==6.3.0 # via # jsonschema # jsonschema-specifications @@ -191,12 +213,16 @@ monotonic==1.6 # via analytics-python mysqlclient==2.2.4 # via -r requirements/base.in -newrelic==9.7.0 +newrelic==9.7.1 # via edx-django-utils oauthlib==3.2.2 # via # requests-oauthlib # social-auth-core +openai==1.13.3 + # via + # -c requirements/constraints.txt + # -r requirements/base.in pbr==6.0.0 # via stevedore pkgutil-resolve-name==1.3.10 @@ -209,6 +235,10 @@ psutil==5.9.8 # via edx-django-utils pycparser==2.21 # via cffi +pydantic==2.6.4 + # via openai +pydantic-core==2.16.3 + # via pydantic pyjwt[crypto]==2.8.0 # via # drf-jwt @@ -222,7 +252,7 @@ pymongo==3.13.0 # via edx-opaque-keys pynacl==1.5.0 # via edx-django-utils -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via # analytics-python # celery @@ -256,7 +286,7 @@ requests==2.31.0 # requests-oauthlib # slumber # social-auth-core -requests-oauthlib==1.3.1 +requests-oauthlib==1.4.0 # via social-auth-core rpds-py==0.18.0 # via @@ -277,6 +307,11 @@ six==1.16.0 # python-dateutil slumber==0.7.1 # via edx-rest-api-client +sniffio==1.3.1 + # via + # anyio + # httpx + # openai social-auth-app-django==5.4.0 # via # -r requirements/base.in @@ -294,12 +329,19 @@ stevedore==5.2.0 # edx-opaque-keys text-unidecode==1.3 # via python-slugify +tqdm==4.66.2 + # via openai typing-extensions==4.10.0 # via + # annotated-types + # anyio # asgiref # drf-spectacular # edx-opaque-keys # kombu + # openai + # pydantic + # pydantic-core tzdata==2024.1 # via # backports-zoneinfo @@ -317,7 +359,7 @@ wcwidth==0.2.13 # via prompt-toolkit xlsxwriter==3.2.0 # via -r requirements/base.in -zipp==3.17.0 +zipp==3.18.0 # via # -r requirements/base.in # importlib-resources diff --git a/requirements/constraints.txt b/requirements/constraints.txt index e71605da0..822279be8 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -38,3 +38,5 @@ edx-lint<5.3 pylint<2.15 astroid<2.12 +# To avoid any breaking changes +openai<=1.13.3 diff --git a/requirements/dev.txt b/requirements/dev.txt index e7f78d0ff..20ac28af5 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -17,6 +17,17 @@ analytics-python==1.4.post1 # via # -r requirements/quality.txt # -r requirements/test.txt +annotated-types==0.6.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # pydantic +anyio==4.3.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # httpx + # openai asgiref==3.7.2 # via # -r requirements/quality.txt @@ -54,11 +65,11 @@ billiard==4.2.0 # -r requirements/quality.txt # -r requirements/test.txt # celery -build==1.0.3 +build==1.1.1 # via # -r requirements/pip-tools.txt # pip-tools -cachetools==5.3.2 +cachetools==5.3.3 # via # -r requirements/test.txt # tox @@ -73,6 +84,8 @@ certifi==2024.2.2 # via # -r requirements/quality.txt # -r requirements/test.txt + # httpcore + # httpx # requests cffi==1.16.0 # via @@ -143,7 +156,7 @@ cryptography==42.0.5 # -r requirements/test.txt # pyjwt # social-auth-core -ddt==1.7.1 +ddt==1.7.2 # via # -r requirements/dev.in # -r requirements/test.txt @@ -167,7 +180,12 @@ distlib==0.3.8 # via # -r requirements/test.txt # virtualenv -django==4.2.10 +distro==1.9.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # openai +django==4.2.11 # via # -c requirements/constraints.txt # -r requirements/quality.txt @@ -286,7 +304,7 @@ edx-django-release-util==1.3.0 # via # -r requirements/quality.txt # -r requirements/test.txt -edx-django-utils==5.10.1 +edx-django-utils==5.11.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -325,11 +343,13 @@ edx-toggles==5.1.1 # -r requirements/test.txt exceptiongroup==1.2.0 # via + # -r requirements/quality.txt # -r requirements/test.txt + # anyio # pytest factory-boy==3.3.0 # via -r requirements/test.txt -faker==23.2.1 +faker==24.1.0 # via # -r requirements/test.txt # factory-boy @@ -340,17 +360,34 @@ filelock==3.13.1 # virtualenv gunicorn==21.2.0 # via -r requirements/dev.in +h11==0.14.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # httpcore +httpcore==1.0.4 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # httpx +httpx==0.27.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # openai idna==3.6 # via # -r requirements/quality.txt # -r requirements/test.txt + # anyio + # httpx # requests -importlib-metadata==7.0.1 +importlib-metadata==7.0.2 # via # -r requirements/pip-tools.txt # build # inflect -importlib-resources==6.1.2 +importlib-resources==6.3.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -434,7 +471,7 @@ mysqlclient==2.2.4 # via # -r requirements/quality.txt # -r requirements/test.txt -newrelic==9.7.0 +newrelic==9.7.1 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -445,7 +482,12 @@ oauthlib==3.2.2 # -r requirements/test.txt # requests-oauthlib # social-auth-core -packaging==23.2 +openai==1.13.3 + # via + # -c requirements/constraints.txt + # -r requirements/quality.txt + # -r requirements/test.txt +packaging==24.0 # via # -r requirements/pip-tools.txt # -r requirements/test.txt @@ -461,7 +503,7 @@ pbr==6.0.0 # -r requirements/quality.txt # -r requirements/test.txt # stevedore -pip-tools==7.4.0 +pip-tools==7.4.1 # via -r requirements/pip-tools.txt pkgutil-resolve-name==1.3.10 # via @@ -505,6 +547,16 @@ pycparser==2.21 # -r requirements/quality.txt # -r requirements/test.txt # cffi +pydantic==2.6.4 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # openai +pydantic-core==2.16.3 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # pydantic pydocstyle==6.3.0 # via -r requirements/quality.txt pygments==2.17.2 @@ -566,7 +618,7 @@ pyproject-hooks==1.0.0 # -r requirements/pip-tools.txt # build # pip-tools -pytest==8.0.2 +pytest==8.1.1 # via # -r requirements/test.txt # pytest-cov @@ -575,7 +627,7 @@ pytest-cov==4.1.0 # via -r requirements/test.txt pytest-django==4.8.0 # via -r requirements/test.txt -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -631,7 +683,7 @@ requests==2.31.0 # responses # slumber # social-auth-core -requests-oauthlib==1.3.1 +requests-oauthlib==1.4.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -674,6 +726,13 @@ slumber==0.7.1 # -r requirements/quality.txt # -r requirements/test.txt # edx-rest-api-client +sniffio==1.3.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # anyio + # httpx + # openai snowballstemmer==2.2.0 # via # -r requirements/quality.txt @@ -720,23 +779,33 @@ tomli==2.0.1 # pyproject-hooks # pytest # tox -tomlkit==0.12.3 +tomlkit==0.12.4 # via # -r requirements/quality.txt # -r requirements/test.txt # pylint -tox==4.13.0 +tox==4.14.1 # via -r requirements/test.txt +tqdm==4.66.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # openai typing-extensions==4.10.0 # via # -r requirements/quality.txt # -r requirements/test.txt + # annotated-types + # anyio # asgiref # astroid # drf-spectacular # edx-opaque-keys # faker # kombu + # openai + # pydantic + # pydantic-core # pylint tzdata==2024.1 # via @@ -771,7 +840,7 @@ wcwidth==0.2.13 # -r requirements/quality.txt # -r requirements/test.txt # prompt-toolkit -wheel==0.42.0 +wheel==0.43.0 # via # -r requirements/pip-tools.txt # pip-tools @@ -784,7 +853,7 @@ xlsxwriter==3.2.0 # via # -r requirements/quality.txt # -r requirements/test.txt -zipp==3.17.0 +zipp==3.18.0 # via # -r requirements/pip-tools.txt # -r requirements/quality.txt diff --git a/requirements/django.txt b/requirements/django.txt index 1facfe28b..db03776fe 100644 --- a/requirements/django.txt +++ b/requirements/django.txt @@ -1 +1 @@ -django==4.2.10 +django==4.2.11 diff --git a/requirements/doc.txt b/requirements/doc.txt index 8a3cf6bc2..1414605f3 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -16,6 +16,15 @@ amqp==5.2.0 # kombu analytics-python==1.4.post1 # via -r requirements/test.txt +annotated-types==0.6.0 + # via + # -r requirements/test.txt + # pydantic +anyio==4.3.0 + # via + # -r requirements/test.txt + # httpx + # openai asgiref==3.7.2 # via # -r requirements/test.txt @@ -53,7 +62,7 @@ billiard==4.2.0 # via # -r requirements/test.txt # celery -cachetools==5.3.2 +cachetools==5.3.3 # via # -r requirements/test.txt # tox @@ -66,6 +75,8 @@ celery==5.3.6 certifi==2024.2.2 # via # -r requirements/test.txt + # httpcore + # httpx # requests cffi==1.16.0 # via @@ -125,7 +136,7 @@ cryptography==42.0.5 # -r requirements/test.txt # pyjwt # social-auth-core -ddt==1.7.1 +ddt==1.7.2 # via -r requirements/test.txt defusedxml==0.7.1 # via @@ -141,7 +152,11 @@ distlib==0.3.8 # via # -r requirements/test.txt # virtualenv -django==4.2.10 +distro==1.9.0 + # via + # -r requirements/test.txt + # openai +django==4.2.11 # via # -c requirements/constraints.txt # -r requirements/test.txt @@ -234,7 +249,7 @@ edx-celeryutils==1.2.5 # via -r requirements/test.txt edx-django-release-util==1.3.0 # via -r requirements/test.txt -edx-django-utils==5.10.1 +edx-django-utils==5.11.0 # via # -r requirements/test.txt # django-config-models @@ -262,10 +277,11 @@ edx-toggles==5.1.1 exceptiongroup==1.2.0 # via # -r requirements/test.txt + # anyio # pytest factory-boy==3.3.0 # via -r requirements/test.txt -faker==23.2.1 +faker==24.1.0 # via # -r requirements/test.txt # factory-boy @@ -274,15 +290,29 @@ filelock==3.13.1 # -r requirements/test.txt # tox # virtualenv +h11==0.14.0 + # via + # -r requirements/test.txt + # httpcore +httpcore==1.0.4 + # via + # -r requirements/test.txt + # httpx +httpx==0.27.0 + # via + # -r requirements/test.txt + # openai idna==3.6 # via # -r requirements/test.txt + # anyio + # httpx # requests imagesize==1.4.1 # via sphinx -importlib-metadata==7.0.1 +importlib-metadata==7.0.2 # via sphinx -importlib-resources==6.1.2 +importlib-resources==6.3.0 # via # -r requirements/test.txt # jsonschema @@ -340,7 +370,7 @@ monotonic==1.6 # analytics-python mysqlclient==2.2.4 # via -r requirements/test.txt -newrelic==9.7.0 +newrelic==9.7.1 # via # -r requirements/test.txt # edx-django-utils @@ -351,7 +381,11 @@ oauthlib==3.2.2 # -r requirements/test.txt # requests-oauthlib # social-auth-core -packaging==23.2 +openai==1.13.3 + # via + # -c requirements/constraints.txt + # -r requirements/test.txt +packaging==24.0 # via # -r requirements/test.txt # pydata-sphinx-theme @@ -394,6 +428,14 @@ pycparser==2.21 # via # -r requirements/test.txt # cffi +pydantic==2.6.4 + # via + # -r requirements/test.txt + # openai +pydantic-core==2.16.3 + # via + # -r requirements/test.txt + # pydantic pydata-sphinx-theme==0.14.4 # via sphinx-book-theme pygments==2.17.2 @@ -446,7 +488,7 @@ pyproject-api==1.6.1 # via # -r requirements/test.txt # tox -pytest==8.0.2 +pytest==8.1.1 # via # -r requirements/test.txt # pytest-cov @@ -455,7 +497,7 @@ pytest-cov==4.1.0 # via -r requirements/test.txt pytest-django==4.8.0 # via -r requirements/test.txt -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via # -r requirements/test.txt # analytics-python @@ -481,7 +523,7 @@ pyyaml==6.0.1 # drf-spectacular # edx-django-release-util # responses -readme-renderer==42.0 +readme-renderer==43.0 # via -r requirements/doc.in redis==3.5.3 # via @@ -504,7 +546,7 @@ requests==2.31.0 # slumber # social-auth-core # sphinx -requests-oauthlib==1.3.1 +requests-oauthlib==1.4.0 # via # -r requirements/test.txt # social-auth-core @@ -538,6 +580,12 @@ slumber==0.7.1 # via # -r requirements/test.txt # edx-rest-api-client +sniffio==1.3.1 + # via + # -r requirements/test.txt + # anyio + # httpx + # openai snowballstemmer==2.2.0 # via sphinx social-auth-app-django==5.4.0 @@ -594,21 +642,30 @@ tomli==2.0.1 # pyproject-api # pytest # tox -tomlkit==0.12.3 +tomlkit==0.12.4 # via # -r requirements/test.txt # pylint -tox==4.13.0 +tox==4.14.1 # via -r requirements/test.txt +tqdm==4.66.2 + # via + # -r requirements/test.txt + # openai typing-extensions==4.10.0 # via # -r requirements/test.txt + # annotated-types + # anyio # asgiref # astroid # drf-spectacular # edx-opaque-keys # faker # kombu + # openai + # pydantic + # pydantic-core # pydata-sphinx-theme # pylint tzdata==2024.1 @@ -645,7 +702,7 @@ wrapt==1.16.0 # astroid xlsxwriter==3.2.0 # via -r requirements/test.txt -zipp==3.17.0 +zipp==3.18.0 # via # -r requirements/test.txt # importlib-metadata diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt index 44c48d996..266ab28ec 100644 --- a/requirements/pip-tools.txt +++ b/requirements/pip-tools.txt @@ -4,15 +4,15 @@ # # make upgrade # -build==1.0.3 +build==1.1.1 # via pip-tools click==8.1.7 # via pip-tools -importlib-metadata==7.0.1 +importlib-metadata==7.0.2 # via build -packaging==23.2 +packaging==24.0 # via build -pip-tools==7.4.0 +pip-tools==7.4.1 # via -r requirements/pip-tools.in pyproject-hooks==1.0.0 # via @@ -23,9 +23,9 @@ tomli==2.0.1 # build # pip-tools # pyproject-hooks -wheel==0.42.0 +wheel==0.43.0 # via pip-tools -zipp==3.17.0 +zipp==3.18.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements/pip.txt b/requirements/pip.txt index 66656035b..0094cc686 100644 --- a/requirements/pip.txt +++ b/requirements/pip.txt @@ -4,7 +4,7 @@ # # make upgrade # -wheel==0.42.0 +wheel==0.43.0 # via -r requirements/pip.in # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements/production.txt b/requirements/production.txt index ac1f1c4b1..6a8fbd2dd 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -12,6 +12,15 @@ amqp==5.2.0 # kombu analytics-python==1.4.post1 # via -r requirements/base.txt +annotated-types==0.6.0 + # via + # -r requirements/base.txt + # pydantic +anyio==4.3.0 + # via + # -r requirements/base.txt + # httpx + # openai asgiref==3.7.2 # via # -r requirements/base.txt @@ -45,6 +54,8 @@ celery==5.3.6 certifi==2024.2.2 # via # -r requirements/base.txt + # httpcore + # httpx # requests cffi==1.16.0 # via @@ -91,7 +102,11 @@ defusedxml==0.7.1 # djangorestframework-xml # python3-openid # social-auth-core -django==4.2.10 +distro==1.9.0 + # via + # -r requirements/base.txt + # openai +django==4.2.11 # via # -r requirements/base.txt # django-celery-results @@ -170,7 +185,7 @@ edx-celeryutils==1.2.5 # via -r requirements/base.txt edx-django-release-util==1.3.0 # via -r requirements/base.txt -edx-django-utils==5.10.1 +edx-django-utils==5.11.0 # via # -r requirements/base.txt # django-config-models @@ -191,17 +206,35 @@ edx-rest-api-client==5.6.1 # via -r requirements/base.txt edx-toggles==5.1.1 # via -r requirements/base.txt +exceptiongroup==1.2.0 + # via + # -r requirements/base.txt + # anyio gevent==24.2.1 # via -r requirements/production.in greenlet==3.0.3 # via gevent gunicorn==21.2.0 # via -r requirements/production.in +h11==0.14.0 + # via + # -r requirements/base.txt + # httpcore +httpcore==1.0.4 + # via + # -r requirements/base.txt + # httpx +httpx==0.27.0 + # via + # -r requirements/base.txt + # openai idna==3.6 # via # -r requirements/base.txt + # anyio + # httpx # requests -importlib-resources==6.1.2 +importlib-resources==6.3.0 # via # -r requirements/base.txt # jsonschema @@ -242,7 +275,7 @@ monotonic==1.6 # analytics-python mysqlclient==2.2.4 # via -r requirements/base.txt -newrelic==9.7.0 +newrelic==9.7.1 # via # -r requirements/base.txt # edx-django-utils @@ -251,7 +284,9 @@ oauthlib==3.2.2 # -r requirements/base.txt # requests-oauthlib # social-auth-core -packaging==23.2 +openai==1.13.3 + # via -r requirements/base.txt +packaging==24.0 # via gunicorn pbr==6.0.0 # via @@ -277,6 +312,14 @@ pycparser==2.21 # via # -r requirements/base.txt # cffi +pydantic==2.6.4 + # via + # -r requirements/base.txt + # openai +pydantic-core==2.16.3 + # via + # -r requirements/base.txt + # pydantic pyjwt[crypto]==2.8.0 # via # -r requirements/base.txt @@ -295,7 +338,7 @@ pynacl==1.5.0 # via # -r requirements/base.txt # edx-django-utils -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via # -r requirements/base.txt # analytics-python @@ -338,7 +381,7 @@ requests==2.31.0 # requests-oauthlib # slumber # social-auth-core -requests-oauthlib==1.3.1 +requests-oauthlib==1.4.0 # via # -r requirements/base.txt # social-auth-core @@ -367,6 +410,12 @@ slumber==0.7.1 # via # -r requirements/base.txt # edx-rest-api-client +sniffio==1.3.1 + # via + # -r requirements/base.txt + # anyio + # httpx + # openai social-auth-app-django==5.4.0 # via # -r requirements/base.txt @@ -390,13 +439,22 @@ text-unidecode==1.3 # via # -r requirements/base.txt # python-slugify +tqdm==4.66.2 + # via + # -r requirements/base.txt + # openai typing-extensions==4.10.0 # via # -r requirements/base.txt + # annotated-types + # anyio # asgiref # drf-spectacular # edx-opaque-keys # kombu + # openai + # pydantic + # pydantic-core tzdata==2024.1 # via # -r requirements/base.txt @@ -422,7 +480,7 @@ wcwidth==0.2.13 # prompt-toolkit xlsxwriter==3.2.0 # via -r requirements/base.txt -zipp==3.17.0 +zipp==3.18.0 # via # -r requirements/base.txt # importlib-resources diff --git a/requirements/quality.txt b/requirements/quality.txt index 3dc3d6cba..8cd661f37 100644 --- a/requirements/quality.txt +++ b/requirements/quality.txt @@ -12,6 +12,15 @@ amqp==5.2.0 # kombu analytics-python==1.4.post1 # via -r requirements/base.txt +annotated-types==0.6.0 + # via + # -r requirements/base.txt + # pydantic +anyio==4.3.0 + # via + # -r requirements/base.txt + # httpx + # openai asgiref==3.7.2 # via # -r requirements/base.txt @@ -51,6 +60,8 @@ celery==5.3.6 certifi==2024.2.2 # via # -r requirements/base.txt + # httpcore + # httpx # requests cffi==1.16.0 # via @@ -104,7 +115,11 @@ defusedxml==0.7.1 # social-auth-core dill==0.3.8 # via pylint -django==4.2.10 +distro==1.9.0 + # via + # -r requirements/base.txt + # openai +django==4.2.11 # via # -c requirements/constraints.txt # -r requirements/base.txt @@ -186,7 +201,7 @@ edx-celeryutils==1.2.5 # via -r requirements/base.txt edx-django-release-util==1.3.0 # via -r requirements/base.txt -edx-django-utils==5.10.1 +edx-django-utils==5.11.0 # via # -r requirements/base.txt # django-config-models @@ -211,11 +226,29 @@ edx-rest-api-client==5.6.1 # via -r requirements/base.txt edx-toggles==5.1.1 # via -r requirements/base.txt +exceptiongroup==1.2.0 + # via + # -r requirements/base.txt + # anyio +h11==0.14.0 + # via + # -r requirements/base.txt + # httpcore +httpcore==1.0.4 + # via + # -r requirements/base.txt + # httpx +httpx==0.27.0 + # via + # -r requirements/base.txt + # openai idna==3.6 # via # -r requirements/base.txt + # anyio + # httpx # requests -importlib-resources==6.1.2 +importlib-resources==6.3.0 # via # -r requirements/base.txt # jsonschema @@ -264,7 +297,7 @@ monotonic==1.6 # analytics-python mysqlclient==2.2.4 # via -r requirements/base.txt -newrelic==9.7.0 +newrelic==9.7.1 # via # -r requirements/base.txt # edx-django-utils @@ -273,6 +306,10 @@ oauthlib==3.2.2 # -r requirements/base.txt # requests-oauthlib # social-auth-core +openai==1.13.3 + # via + # -c requirements/constraints.txt + # -r requirements/base.txt pbr==6.0.0 # via # -r requirements/base.txt @@ -301,6 +338,14 @@ pycparser==2.21 # via # -r requirements/base.txt # cffi +pydantic==2.6.4 + # via + # -r requirements/base.txt + # openai +pydantic-core==2.16.3 + # via + # -r requirements/base.txt + # pydantic pydocstyle==6.3.0 # via -r requirements/quality.in pyjwt[crypto]==2.8.0 @@ -336,7 +381,7 @@ pynacl==1.5.0 # via # -r requirements/base.txt # edx-django-utils -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via # -r requirements/base.txt # analytics-python @@ -378,7 +423,7 @@ requests==2.31.0 # requests-oauthlib # slumber # social-auth-core -requests-oauthlib==1.3.1 +requests-oauthlib==1.4.0 # via # -r requirements/base.txt # social-auth-core @@ -408,6 +453,12 @@ slumber==0.7.1 # via # -r requirements/base.txt # edx-rest-api-client +sniffio==1.3.1 + # via + # -r requirements/base.txt + # anyio + # httpx + # openai snowballstemmer==2.2.0 # via pydocstyle social-auth-app-django==5.4.0 @@ -435,16 +486,25 @@ text-unidecode==1.3 # python-slugify tomli==2.0.1 # via pylint -tomlkit==0.12.3 +tomlkit==0.12.4 # via pylint +tqdm==4.66.2 + # via + # -r requirements/base.txt + # openai typing-extensions==4.10.0 # via # -r requirements/base.txt + # annotated-types + # anyio # asgiref # astroid # drf-spectacular # edx-opaque-keys # kombu + # openai + # pydantic + # pydantic-core # pylint tzdata==2024.1 # via @@ -473,7 +533,7 @@ wrapt==1.16.0 # via astroid xlsxwriter==3.2.0 # via -r requirements/base.txt -zipp==3.17.0 +zipp==3.18.0 # via # -r requirements/base.txt # importlib-resources diff --git a/requirements/test.txt b/requirements/test.txt index 1fead2b90..85f44f5b6 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -12,6 +12,15 @@ amqp==5.2.0 # kombu analytics-python==1.4.post1 # via -r requirements/base.txt +annotated-types==0.6.0 + # via + # -r requirements/base.txt + # pydantic +anyio==4.3.0 + # via + # -r requirements/base.txt + # httpx + # openai asgiref==3.7.2 # via # -r requirements/base.txt @@ -42,7 +51,7 @@ billiard==4.2.0 # via # -r requirements/base.txt # celery -cachetools==5.3.2 +cachetools==5.3.3 # via tox celery==5.3.6 # via @@ -53,6 +62,8 @@ celery==5.3.6 certifi==2024.2.2 # via # -r requirements/base.txt + # httpcore + # httpx # requests cffi==1.16.0 # via @@ -107,7 +118,7 @@ cryptography==42.0.5 # -r requirements/base.txt # pyjwt # social-auth-core -ddt==1.7.1 +ddt==1.7.2 # via -r requirements/test.in defusedxml==0.7.1 # via @@ -119,6 +130,10 @@ dill==0.3.8 # via pylint distlib==0.3.8 # via virtualenv +distro==1.9.0 + # via + # -r requirements/base.txt + # openai # via # -c requirements/constraints.txt # -r requirements/base.txt @@ -202,7 +217,7 @@ edx-celeryutils==1.2.5 # via -r requirements/base.txt edx-django-release-util==1.3.0 # via -r requirements/base.txt -edx-django-utils==5.10.1 +edx-django-utils==5.11.0 # via # -r requirements/base.txt # django-config-models @@ -228,20 +243,37 @@ edx-rest-api-client==5.6.1 edx-toggles==5.1.1 # via -r requirements/base.txt exceptiongroup==1.2.0 - # via pytest + # via + # -r requirements/base.txt + # anyio + # pytest factory-boy==3.3.0 # via -r requirements/test.in -faker==23.2.1 +faker==24.1.0 # via factory-boy filelock==3.13.1 # via # tox # virtualenv +h11==0.14.0 + # via + # -r requirements/base.txt + # httpcore +httpcore==1.0.4 + # via + # -r requirements/base.txt + # httpx +httpx==0.27.0 + # via + # -r requirements/base.txt + # openai idna==3.6 # via # -r requirements/base.txt + # anyio + # httpx # requests -importlib-resources==6.1.2 +importlib-resources==6.3.0 # via # -r requirements/base.txt # jsonschema @@ -290,7 +322,7 @@ monotonic==1.6 # analytics-python mysqlclient==2.2.4 # via -r requirements/base.txt -newrelic==9.7.0 +newrelic==9.7.1 # via # -r requirements/base.txt # edx-django-utils @@ -299,7 +331,11 @@ oauthlib==3.2.2 # -r requirements/base.txt # requests-oauthlib # social-auth-core -packaging==23.2 +openai==1.13.3 + # via + # -c requirements/constraints.txt + # -r requirements/base.txt +packaging==24.0 # via # pyproject-api # pytest @@ -337,6 +373,14 @@ pycparser==2.21 # via # -r requirements/base.txt # cffi +pydantic==2.6.4 + # via + # -r requirements/base.txt + # openai +pydantic-core==2.16.3 + # via + # -r requirements/base.txt + # pydantic pyjwt[crypto]==2.8.0 # via # -r requirements/base.txt @@ -372,7 +416,7 @@ pynacl==1.5.0 # edx-django-utils pyproject-api==1.6.1 # via tox -pytest==8.0.2 +pytest==8.1.1 # via # pytest-cov # pytest-django @@ -380,7 +424,7 @@ pytest-cov==4.1.0 # via -r requirements/test.in pytest-django==4.8.0 # via -r requirements/test.in -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via # -r requirements/base.txt # analytics-python @@ -425,7 +469,7 @@ requests==2.31.0 # responses # slumber # social-auth-core -requests-oauthlib==1.3.1 +requests-oauthlib==1.4.0 # via # -r requirements/base.txt # social-auth-core @@ -457,6 +501,12 @@ slumber==0.7.1 # via # -r requirements/base.txt # edx-rest-api-client +sniffio==1.3.1 + # via + # -r requirements/base.txt + # anyio + # httpx + # openai social-auth-app-django==5.4.0 # via # -r requirements/base.txt @@ -487,19 +537,28 @@ tomli==2.0.1 # pyproject-api # pytest # tox -tomlkit==0.12.3 +tomlkit==0.12.4 # via pylint -tox==4.13.0 +tox==4.14.1 # via -r requirements/test.in +tqdm==4.66.2 + # via + # -r requirements/base.txt + # openai typing-extensions==4.10.0 # via # -r requirements/base.txt + # annotated-types + # anyio # asgiref # astroid # drf-spectacular # edx-opaque-keys # faker # kombu + # openai + # pydantic + # pydantic-core # pylint tzdata==2024.1 # via @@ -531,7 +590,7 @@ wrapt==1.16.0 # via astroid xlsxwriter==3.2.0 # via -r requirements/base.txt -zipp==3.17.0 +zipp==3.18.0 # via # -r requirements/base.txt # importlib-resources diff --git a/requirements/validation.txt b/requirements/validation.txt index e41698044..5d77b31ce 100644 --- a/requirements/validation.txt +++ b/requirements/validation.txt @@ -17,6 +17,17 @@ analytics-python==1.4.post1 # via # -r requirements/quality.txt # -r requirements/test.txt +annotated-types==0.6.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # pydantic +anyio==4.3.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # httpx + # openai asgiref==3.7.2 # via # -r requirements/quality.txt @@ -54,7 +65,7 @@ billiard==4.2.0 # -r requirements/quality.txt # -r requirements/test.txt # celery -cachetools==5.3.2 +cachetools==5.3.3 # via # -r requirements/test.txt # tox @@ -69,6 +80,8 @@ certifi==2024.2.2 # via # -r requirements/quality.txt # -r requirements/test.txt + # httpcore + # httpx # requests cffi==1.16.0 # via @@ -137,7 +150,7 @@ cryptography==42.0.5 # -r requirements/test.txt # pyjwt # social-auth-core -ddt==1.7.1 +ddt==1.7.2 # via -r requirements/test.txt defusedxml==0.7.1 # via @@ -155,7 +168,12 @@ distlib==0.3.8 # via # -r requirements/test.txt # virtualenv -django==4.2.10 +distro==1.9.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # openai +django==4.2.11 # via # -c requirements/constraints.txt # -r requirements/quality.txt @@ -270,7 +288,7 @@ edx-django-release-util==1.3.0 # via # -r requirements/quality.txt # -r requirements/test.txt -edx-django-utils==5.10.1 +edx-django-utils==5.11.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -307,11 +325,13 @@ edx-toggles==5.1.1 # -r requirements/test.txt exceptiongroup==1.2.0 # via + # -r requirements/quality.txt # -r requirements/test.txt + # anyio # pytest factory-boy==3.3.0 # via -r requirements/test.txt -faker==23.2.1 +faker==24.1.0 # via # -r requirements/test.txt # factory-boy @@ -320,12 +340,29 @@ filelock==3.13.1 # -r requirements/test.txt # tox # virtualenv +h11==0.14.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # httpcore +httpcore==1.0.4 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # httpx +httpx==0.27.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # openai idna==3.6 # via # -r requirements/quality.txt # -r requirements/test.txt + # anyio + # httpx # requests -importlib-resources==6.1.2 +importlib-resources==6.3.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -398,7 +435,7 @@ mysqlclient==2.2.4 # via # -r requirements/quality.txt # -r requirements/test.txt -newrelic==9.7.0 +newrelic==9.7.1 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -409,7 +446,12 @@ oauthlib==3.2.2 # -r requirements/test.txt # requests-oauthlib # social-auth-core -packaging==23.2 +openai==1.13.3 + # via + # -c requirements/constraints.txt + # -r requirements/quality.txt + # -r requirements/test.txt +packaging==24.0 # via # -r requirements/test.txt # pyproject-api @@ -459,6 +501,16 @@ pycparser==2.21 # -r requirements/quality.txt # -r requirements/test.txt # cffi +pydantic==2.6.4 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # openai +pydantic-core==2.16.3 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # pydantic pydocstyle==6.3.0 # via -r requirements/quality.txt pyjwt[crypto]==2.8.0 @@ -513,7 +565,7 @@ pyproject-api==1.6.1 # via # -r requirements/test.txt # tox -pytest==8.0.2 +pytest==8.1.1 # via # -r requirements/test.txt # pytest-cov @@ -522,7 +574,7 @@ pytest-cov==4.1.0 # via -r requirements/test.txt pytest-django==4.8.0 # via -r requirements/test.txt -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -575,7 +627,7 @@ requests==2.31.0 # responses # slumber # social-auth-core -requests-oauthlib==1.3.1 +requests-oauthlib==1.4.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -616,6 +668,13 @@ slumber==0.7.1 # -r requirements/quality.txt # -r requirements/test.txt # edx-rest-api-client +sniffio==1.3.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # anyio + # httpx + # openai snowballstemmer==2.2.0 # via # -r requirements/quality.txt @@ -657,23 +716,33 @@ tomli==2.0.1 # pyproject-api # pytest # tox -tomlkit==0.12.3 +tomlkit==0.12.4 # via # -r requirements/quality.txt # -r requirements/test.txt # pylint -tox==4.13.0 +tox==4.14.1 # via -r requirements/test.txt +tqdm==4.66.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # openai typing-extensions==4.10.0 # via # -r requirements/quality.txt # -r requirements/test.txt + # annotated-types + # anyio # asgiref # astroid # drf-spectacular # edx-opaque-keys # faker # kombu + # openai + # pydantic + # pydantic-core # pylint tzdata==2024.1 # via @@ -717,7 +786,7 @@ xlsxwriter==3.2.0 # via # -r requirements/quality.txt # -r requirements/test.txt -zipp==3.17.0 +zipp==3.18.0 # via # -r requirements/quality.txt # -r requirements/test.txt