@@ -1,3 +1,14 @@ from authentication.exceptions.business_host_exceptions.base_already import BaseAlready __all__ = ('BaseAlready',) + + +class InvalidPassword(Exception): ... + + +class InvalidUsername(Exception): ... + + +class InvalidToken(Exception): + def __str__(self) -> str: + return 'Invalid token received, please try relog' @@ -0,0 +1,112 @@ +import json +from datetime import datetime, timezone +from typing import Any, Literal + +import jwt +from django.conf import settings +from django.contrib.auth.hashers import check_password + +from authentication.exceptions import InvalidPassword, InvalidToken, InvalidUsername +from authentication.models.user import CustomUserModel + + +class TokenService: + @classmethod + def issue(cls, *, username: str, password: str) -> dict[str, str]: + try: + user = CustomUserModel.objects.get(username=username) + except CustomUserModel.DoesNotExist: + raise InvalidUsername + if not check_password(password=password, encoded=user.password): + raise InvalidPassword + + user.last_login = datetime.now() + user.save() + + payload = {} + + for attrname in settings.JWT_SETTINGS['encode_attributes']: + attr = getattr(user, attrname) + try: + bool(json.dumps({attrname: attr})) + except TypeError: + attr = str(getattr(user, attrname)) + finally: + payload.update({f'{attrname}': attr}) + return { + 'access': cls._encode(payload=payload, token_type='access'), + 'refresh': cls._encode(payload=payload, token_type='refresh'), + } + + @classmethod + def refresh(cls, *, refresh: str) -> dict[str, str]: + decoded_token = cls._decode(token=refresh) + + return { + 'access': cls._encode(payload=decoded_token, token_type='access'), + } + + @classmethod + def force_refresh(cls, *, refresh: str) -> dict[str, str]: + decoded_token = cls._decode(token=refresh) + user = CustomUserModel.objects.get( + **{ + key: decoded_token[key] + for key in decoded_token + if key in settings.JWT_SETTINGS['encode_attributes'] + } + ) + + payload = {} + for attrname in settings.JWT_SETTINGS['encode_attributes']: + attr = getattr(user, attrname) + try: + bool(json.dumps({attrname: attr})) + except TypeError: + attr = str(getattr(user, attrname)) + finally: + payload.update({attrname: attr}) + + return { + 'access': cls._encode(payload=payload, token_type='access'), + 'refresh': cls._encode(payload=payload, token_type='refresh'), + } + + @classmethod + async def decode(cls, *, token: str) -> dict[str, Any]: + return cls._decode(token=token) + + @classmethod + def _encode( + cls, *, payload: dict[str, Any], token_type: Literal['access', 'refresh'] + ) -> str: + issued_at = datetime.now(tz=timezone.utc) + jwt_signature = { + 'exp': issued_at + settings.JWT_SETTINGS[f'{token_type}_expiry_time'], + 'iat': issued_at, + } + jwt_signature.update(payload) + + return jwt.encode( + jwt_signature, + key=settings.JWT_SETTINGS['secret_key'], + algorithm=settings.JWT_SETTINGS['algorithm'], + ) + + @classmethod + def _decode(cls, *, token: str) -> Any: + try: + return jwt.decode( + jwt=token, + key=settings.JWT_SETTINGS['secret_key'], + algorithms=[settings.JWT_SETTINGS['algorithm']], + verify=True, + ) + except ( + jwt.InvalidAlgorithmError, + jwt.InvalidIssuedAtError, + jwt.ExpiredSignatureError, + jwt.InvalidSignatureError, + jwt.DecodeError, + ): + raise InvalidToken @@ -0,0 +1,26 @@ +from typing import Any + +from asgiref.sync import async_to_sync +from django.conf import settings +from django.http import HttpRequest +from ninja.security import HttpBearer +from oauth2_provider.models import AccessToken + +from authentication.exceptions import InvalidToken +from authentication.models.user import CustomUserModel +from authentication.services.token import TokenService + + +class SyncAuthBearer(HttpBearer): + def authenticate(self, _: HttpRequest, token: str) -> Any | None: + try: + user_payload = async_to_sync(TokenService.decode)(token=token) + return CustomUserModel.objects.get( + **{ + key: user_payload[f'{key}'] + for key in settings.JWT_SETTINGS['encode_attributes'] + } + ) + except InvalidToken: + access = AccessToken.objects.prefetch_related('user').get(token=token) + return access.user @@ -0,0 +1,18 @@ +import os + +from channels.routing import ProtocolTypeRouter, URLRouter +from django.core.asgi import get_asgi_application +from django.urls import path + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') + +application = get_asgi_application() + +from tools.copywrite.router import ws_router as copywrite_ws_router # noqa: E402 + +application = ProtocolTypeRouter( + { + 'http': application, + 'websocket': URLRouter([path('rtc/copywrite/', copywrite_ws_router)]), + } +) @@ -181,6 +181,21 @@ TEMPLATES = [ ] WSGI_APPLICATION = 'backend.wsgi.application' +ASGI_APPLICATION = 'backend.asgi.application' + +CHANNEL_LAYERS = { + 'default': { + 'BACKEND': 'channels_redis.core.RedisChannelLayer', + 'CONFIG': { + 'hosts': [ + ( + env.str('CHANNELS_MDB_HOST', 'channels-mdb'), + env.int('CHANNELS_MDB_PORT', 6379), + ) + ], + }, + }, +} # Database DATABASES = { @@ -195,6 +210,13 @@ DATABASES = { } } +JWT_SETTINGS = { + 'secret_key': SECRET_KEY, + 'algorithm': env.str('JWT_ALGORITHM', 'HS256'), + 'access_expiry_time': env.timedelta('JWT_ACCESS_TOKEN_LIFETIME', 60 * 60 * 24), + 'refresh_expiry_time': env.timedelta('JWT_REFRESH_TOKEN_LIFETIME', 60 * 60 * 24), + 'encode_attributes': ['uid'], +} DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' @@ -3,9 +3,14 @@ from django.conf.urls.static import static from django.contrib import admin from django.urls import include, path from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView +from ninja import NinjaAPI from backend.public import urlpatterns as public_urlpatterns +api = NinjaAPI(title='AIR API', version='2.0.0') +api.add_router('copywrite/', 'tools.copywrite.router.router') + + urlpatterns = [ path('ml_models/', include('ml_model.urls', namespace='ml_model')), path('stories/', include('stories.urls')), @@ -15,7 +20,6 @@ urlpatterns = [ path('reports/', include('reports.urls')), path('achievements/', include('achievements.urls')), path('chats/', include('tools.chats.urls')), - path('copywrite/', include('tools.copywrite.urls')), path('feed/', include('tools.feed.urls')), path('media/', include('tools.media.urls')), path('schema/', SpectacularAPIView.as_view(), name='schema'), @@ -32,6 +36,7 @@ urlpatterns = [ 'public/', SpectacularSwaggerView.as_view(url_name='schema-public'), ), + path('api/', api.urls), ] urlpatterns += public_urlpatterns @@ -0,0 +1,3 @@ +from typing import Dict, List + +PrimitiveType = str | int | float | List | Dict @@ -11,8 +11,9 @@ from ml_model.models import ( ModelParameter, ModelVersion, NeuronModel, - # ModelPaymentRule ) + +# ModelPaymentRule from payments.services.payment_plan_service import PaymentPlanService @@ -23,9 +24,6 @@ class SimpleService(ABC): self.store = store self.translator = Translator() - @classmethod - def init_relations(cls): ... - @property @abstractmethod def title(self) -> str: ... @@ -1,8 +1,10 @@ import time from datetime import timedelta from decimal import Decimal -from io import BytesIO +from io import BufferedReader, BytesIO +from typing import Any, Dict, List, Optional +import httpx from django.core.files.uploadedfile import InMemoryUploadedFile from langchain import hub from langchain.agents import AgentExecutor, create_structured_chat_agent @@ -313,3 +315,21 @@ class Chatgpt(SimpleService): [chat_history.chat_memory.messages[-1]], process_time, save ) return msgs + + @classmethod + def evaluate( + cls, + content: str, + image: Optional[BufferedReader] = None, + context_messages: List[str] = [], + *, + stream: bool = False, + parameters: List[Dict[str, Any]] = [], + ): + with httpx.Client() as client: + data = {} + if not stream: + return client.post('https://openai.com/api/chat/completions', json=data) + resp = client.post( + 'https://openai.com/api/chat/completions', json=data | {'stream': True} + ) @@ -3,7 +3,7 @@ import json # import uuid from io import BytesIO -from typing import IO, Any +from typing import IO, Any, Dict import deepl import replicate @@ -148,3 +148,7 @@ def claude_run(payload: dict[str, Any]): 'https://api.anthropic.com/v1/messages', json.dumps(payload), headers=headers ).content ) + + +@shared_task +def evaluate_model(model_name: str, data: Dict[str, Any]): ... @@ -1,3 +1,4 @@ +from decimal import Decimal from uuid import UUID from celery import shared_task @@ -5,29 +6,18 @@ from celery.utils.log import get_task_logger from django.db.models import F from authentication.models.business_host import BusinessUserHost -from authentication.selectors.user_selector import UserSelector +from authentication.models.user import CustomUserModel from authentication.services.email_service import EmailService -from payments.selectors.payment_plan_selector import PaymentPlanSelector -from payments.services.payment_service import PaymentService +from payments.services.payment_plan_service import PaymentPlanService logger = get_task_logger(__name__) -@shared_task(name='conduct_recurrent', bind=True) -def conduct_recurrent_payment(self, payment_plan_uid: str, user_id: str): - logger.info('START %s %s' % (conduct_recurrent_payment.__name__, self.request.id)) - user = UserSelector.get_by_uid(UUID(user_id)) - logger.info('USER: %s' % user) - payment_plan = PaymentPlanSelector(user).get_payment_plan_by_id(UUID(payment_plan_uid)) - logger.info('PLAN: %s' % payment_plan) - PaymentService(user).conduct_recurring_payment(payment_plan, 'Ежемесячный платеж по подписке AIR') - logger.info('FINISH %s %s' % (conduct_recurrent_payment.__name__, self.request.id)) - - @shared_task def send_low_balance_message(): hosts = BusinessUserHost.objects.filter( - token_cap_enabled=True, token_cap__gt=F('user__payment_plan__current_token_balance') + token_cap_enabled=True, + token_cap__gt=F('user__payment_plan__current_token_balance'), ) for host in hosts: for email in host.token_cap_emails: @@ -36,3 +26,9 @@ def send_low_balance_message(): 'Для пополнения обратитесь по контактам, указанным в договоре', email, ) + + +@shared_task +def withdraw(user_id: UUID, amount: Decimal): + user = CustomUserModel.objects.get(uid=user_id) + PaymentPlanService(user).update_per_token_plan_details(amount) @@ -1,13 +1,11 @@ -# Generated by Django 4.2.13 on 2024-06-12 21:43 +# Generated by Django 5.0.9 on 2024-12-22 13:19 -from django.conf import settings -import django.contrib.postgres.fields -from django.db import migrations, models import django.db.models.deletion -import django.utils.timezone import django_minio_backend.models import tools.copywrite.models import uuid +from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): @@ -15,54 +13,109 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Category', + name='Copywrite', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='ID')), + ('output_content', models.TextField(blank=True, null=True, verbose_name='Исходящий промпт')), + ('starred', models.BooleanField(default=False, verbose_name='Добавлено в избранное')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Когда создано')), + ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_copywrites', to=settings.AUTH_USER_MODEL, verbose_name='Пользователь')), + ], + options={ + 'verbose_name': 'Копирайт', + 'verbose_name_plural': 'Копирайты', + }, + ), + migrations.CreateModel( + name='TemplateCategory', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(default='Общее', max_length=50, verbose_name='Название')), + ('slug', models.CharField(max_length=50, verbose_name='Ярлык')), ], options={ 'verbose_name': 'Категория', 'verbose_name_plural': 'Категории', }, ), + migrations.CreateModel( + name='SelfCopywrite', + fields=[ + ('copywrite_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='copywrite.copywrite')), + ('input_content', models.TextField(blank=True, null=True, verbose_name='Входящий промпт')), + ], + options={ + 'verbose_name': 'Самописный копирайт', + 'verbose_name_plural': 'Самописные копирайты', + }, + bases=('copywrite.copywrite',), + ), migrations.CreateModel( name='Template', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('order', models.PositiveIntegerField(db_index=True, editable=False, verbose_name='order')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=50, unique=True, verbose_name='Название')), - ('description', models.CharField(max_length=200, verbose_name='Описание')), - ('picture', models.FileField(null=True, storage=django_minio_backend.models.MinioBackend(bucket_name='air-templates-pictures'), upload_to=tools.copywrite.models.template_picture_upload, verbose_name='Картинка')), - ('theme', models.CharField(max_length=200, verbose_name='Тема')), - ('content', models.CharField(max_length=2048, verbose_name='Контент')), - ('target_audience', models.CharField(max_length=100, verbose_name='Целевая аудитория')), - ('resources_urls', django.contrib.postgres.fields.ArrayField(base_field=models.URLField(max_length=300, verbose_name='Ссылка на сайт'), help_text='Сайты, откуда брать информацию', size=None, verbose_name='Интернет-ресурсы')), - ('keywords', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=300, verbose_name='Ключевое слово'), size=None, verbose_name='Ключевые слова')), - ('tov', models.CharField(max_length=100, verbose_name='Тон голоса')), - ('language', models.CharField(choices=[('ru', 'Русский'), ('en', 'Английский'), ('ge', 'Немецкий'), ('it', 'Итальянский'), ('fr', 'Французский')], max_length=20, verbose_name='Язык для копирайта')), - ('params', models.JSONField(default=dict, verbose_name='Параметры для GPT')), - ('categories', models.ManyToManyField(related_name='templates_copyright', to='copywrite.category', verbose_name='Категория')), + ('description', models.CharField(blank=True, max_length=200, null=True, verbose_name='Описание')), + ('picture', models.FileField(blank=True, null=True, storage=django_minio_backend.models.MinioBackend(bucket_name='air-templates-pictures'), upload_to=tools.copywrite.models.template_picture_uploader, verbose_name='Картинка')), + ('content', models.TextField(help_text='Синтаксис в этом поле зависит от Движка шаблонов', verbose_name='Контент')), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='templates_categories', to='copywrite.templatecategory', verbose_name='Категория')), ], options={ 'verbose_name': 'Шаблон', 'verbose_name_plural': 'Шаблоны', + 'ordering': ('order',), + 'abstract': False, }, ), migrations.CreateModel( - name='Copywrite', + name='TemplateVariable', + fields=[ + ('order', models.PositiveIntegerField(db_index=True, editable=False, verbose_name='order')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='Название')), + ('sysname', models.CharField(max_length=100, verbose_name='Системное название')), + ('default_value', models.JSONField(blank=True, null=True, verbose_name='Стандартное значение')), + ('template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='template_variables', to='copywrite.template', verbose_name='Шаблон')), + ], + options={ + 'verbose_name': 'Переменная шаблона', + 'verbose_name_plural': 'Переменные шаблона', + 'ordering': ('order',), + 'abstract': False, + }, + ), + migrations.CreateModel( + name='TemplateCopywrite', + fields=[ + ('copywrite_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='copywrite.copywrite')), + ('template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='template_copywrites', to='copywrite.template', verbose_name='Шаблон')), + ], + options={ + 'verbose_name': 'Шаблонный копирайт', + 'verbose_name_plural': 'Шаблонные копирайты', + }, + bases=('copywrite.copywrite',), + ), + migrations.CreateModel( + name='OverridenVariable', fields=[ - ('uid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='Идентификатор')), - ('created_at', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Когда создан')), - ('user', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_user', related_query_name='%(app_label)s_%(class)ss_user', to=settings.AUTH_USER_MODEL, verbose_name='Пользователь')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='ID')), + ('value', models.JSONField(verbose_name='Значение')), + ('variable', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='template_variable_overriden_variables', to='copywrite.templatevariable', verbose_name='Переменная')), + ('copywrite', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='template_copywrite_overriden_variables', to='copywrite.templatecopywrite', verbose_name='Копирайт')), ], options={ - 'verbose_name': 'Хранилище', - 'verbose_name_plural': 'Копирайт-хранилища', - 'ordering': ['-created_at'], + 'verbose_name': 'Переопределенная переменная', + 'verbose_name_plural': 'Переопределенные переменные', + 'unique_together': {('copywrite', 'variable')}, }, ), ] @@ -0,0 +1,77 @@ +from typing import Any, Generator, Literal +from uuid import UUID + +from django.contrib.contenttypes.models import ContentType +from django.db.models import Q, QuerySet + +from authentication.models.user import CustomUserModel +from core.typing import PrimitiveType +from tools.copywrite.models import Copywrite, OverridenVariable + + +class CopywriteService: + @classmethod + def list_copywrites_by_user( + cls, + user: CustomUserModel, + filters: Q = Q(), + ordering: Literal['starred'] = 'starred', + ) -> QuerySet[Copywrite]: + return ( + Copywrite.objects.filter(user=user) + .filter(filters) + .prefetch_related( + 'templatecopywrite__template', + 'templatecopywrite__template__template_variables', + 'polymorphic_ctype', + ) + .order_by(ordering) + ) + + @classmethod + def create_draft( + cls, + user: CustomUserModel, + type: Literal['template', 'self'], + initial: dict[str, Any], + ) -> Copywrite: + klass = ContentType.objects.get_by_natural_key( + 'copywrite', f'{type}copywrite' + ).model_class() + return klass.objects.create(user=user, **initial) + + @classmethod + def get_copywrite_by_id(cls, copywrite_id: UUID): + return Copywrite.objects.prefetch_related( + 'templatecopywrite__template', + 'templatecopywrite__template__template_variables', + 'polymorphic_ctype__model', + ).get(id=copywrite_id) + + @classmethod + def mark_as_starred(cls, copywrite_id: UUID) -> None: + Copywrite.objects.filter(id=copywrite_id).update(starred=True) + + @classmethod + def override_variable( + cls, copywrite_id: UUID, variable_id: UUID, value: PrimitiveType + ) -> OverridenVariable: + return OverridenVariable.objects.create( + copywrite_id=copywrite_id, variable_id=variable_id, value=value + ) + + @classmethod + def update_overriden_variable( + cls, variable_id: UUID, value: PrimitiveType + ) -> OverridenVariable: + ov = OverridenVariable.objects.get(id=variable_id) + ov.value = value + ov.save() + return ov + + @classmethod + def remove_overriden_variable(cls, variable_id: UUID) -> None: + OverridenVariable.objects.get(id=variable_id).delete() + + @classmethod + def generate(cls, copywrite_id: UUID) -> Generator[str, None, None]: ... @@ -0,0 +1,19 @@ +from uuid import UUID + +from django.db.models import Q, QuerySet + +from tools.copywrite.models import Template, TemplateCategory + + +class TemplateService: + @classmethod + def list_templates(cls, filters: Q = Q()) -> QuerySet[Template]: + return Template.objects.filter(filters) + + @classmethod + def get_template_by_id(cls, template_id: UUID) -> Template: + return Template.objects.prefetch_related('template_variables').get(id=template_id) + + @classmethod + def list_categories(cls, filters: Q = Q()) -> QuerySet[TemplateCategory]: + return TemplateCategory.objects.filter(filters) @@ -1,42 +1,52 @@ from django.contrib import admin +from polymorphic.admin import ( + PolymorphicChildModelAdmin, + PolymorphicChildModelFilter, + PolymorphicParentModelAdmin, +) -from messages.inlines import MessageInline +from tools.copywrite.models import ( + Copywrite, + OverridenVariable, + SelfCopywrite, + Template, + TemplateCategory, + TemplateCopywrite, + TemplateVariable, +) -from .models import Category, Copywrite, Template +class CopywriteChildAdmin(PolymorphicChildModelAdmin): ... -@admin.register(Copywrite) -class CopywriteAdmin(admin.ModelAdmin): - list_display = ['_user', 'messages_count'] - raw_id_fields = ['user'] - inlines = [ - MessageInline, - ] - @admin.display(description='Пользователь') - def _user(self, obj): - return obj.user.email +@admin.register(SelfCopywrite) +class SelfCopywriteAdmin(CopywriteChildAdmin): ... - @admin.display(description='Кол-во сообщений') - def messages_count(self, obj: Copywrite): - return obj.all_messages.count() +class OverridenVariableInline(admin.TabularInline): + model = OverridenVariable -@admin.register(Category) -class CategoryAdmin(admin.ModelAdmin): - list_display = ['title', '_templates'] - @admin.display(description='Подключенные шаблоны') - def _templates(self, obj): - return ','.join([str(m) for m in obj.templates]) +@admin.register(TemplateCopywrite) +class TemplateCopywriteAdmin(CopywriteChildAdmin): + inlines = (OverridenVariableInline,) -@admin.register(Template) -class TemplateAdmin(admin.ModelAdmin): - list_display = ['title', 'description', '_picture'] +@admin.register(Copywrite) +class CopywriteAdmin(PolymorphicParentModelAdmin): + child_models = (SelfCopywrite, TemplateCopywrite) + list_filter = (PolymorphicChildModelFilter,) + - @admin.display(description='Миниатюра') - def _picture(self, obj): - from django.utils.html import format_html +@admin.register(TemplateCategory) +class TemplateCategoryAdmin(admin.ModelAdmin): ... - return format_html('', obj.picture) + +class TemplateVariableInline(admin.TabularInline): + model = TemplateVariable + + +@admin.register(Template) +class TemplateAdmin(admin.ModelAdmin): + list_display = ('id', 'title', 'category') + inlines = (TemplateVariableInline,) @@ -1,169 +0,0 @@ -from django.http import StreamingHttpResponse -from drf_spectacular.utils import OpenApiParameter, extend_schema -from rest_framework.permissions import IsAuthenticated -from rest_framework.response import Response -from rest_framework.views import APIView - -from messages.models import Message -from messages.serializers import MessageSerializer -from ml_model.models import NeuronModel -from ml_model.services.chatgpt import Chatgpt - -from .models import Category, Copywrite, Template -from .serializers import ( - CategorySerializer, - TemplateCreateSerializer, - TemplateSerializer, - TemplatesSerializer, -) - - -class CategoriesAPIView(APIView): - @extend_schema(responses={200: CategorySerializer(many=True)}) - def get(self, request, *args, **kwargs): - """ - Get categories - """ - categories = Category.objects.all() - return Response(CategorySerializer(categories, many=True).data, 200) - - -class TemplatesAPIView(APIView): - @extend_schema(responses={200: TemplatesSerializer(many=True)}) - def get(self, request, *args, **kwargs): - """ - Get templates - """ - templates = Template.objects.all() - return Response(TemplatesSerializer(templates, many=True).data, 200) - - @extend_schema(request=TemplateCreateSerializer, responses={201: TemplateSerializer}) - def post(self, request, *args, **kwargs): - """ - Create custom template for users via CopywriteTool and staff-user - """ - if request.user.is_superuser: - serializer = TemplateCreateSerializer(data=request.data) - if serializer.is_valid(): - template = Template.objects.create(**serializer.validated_data) - return Response(TemplateSerializer(template).data, 201) - return Response("Default user can't reach this action, please relog", 403) - - -class TemplateAPIView(APIView): - @extend_schema(responses={200: TemplateSerializer}) - def get(self, request, template_id, *args, **kwargs): - """ - Get template by id - """ - template = Template.objects.get(id=template_id) - return Response(TemplateSerializer(template).data, 200) - - -class CopywritesAPIView(APIView): - permission_classes = [ - IsAuthenticated, - ] - - @extend_schema( - parameters=[ - OpenApiParameter( - 'model', - str, - 'path', - required=True, - ) - ], - responses={200: MessageSerializer(many=True)}, - ) - def get(self, request, *args, **kwargs): - """ - List Messages - """ - copywrite, _ = Copywrite.objects.get_or_create( - user=request.user, defaults={'user': request.user} - ) - return Response(MessageSerializer(copywrite.output_messages, many=True).data, 200) - - @extend_schema( - parameters=[OpenApiParameter('stream', bool)], - request=MessageSerializer, - responses={ - 200: MessageSerializer(many=True), - }, - ) - def post(self, request, *args, **kwargs): - serializer = MessageSerializer(data=request.data) - if serializer.is_valid(): - stream = bool(request.query_params.get('stream')) - if not serializer.validated_data.get('info'): - serializer.validated_data['info'] = {} - serializer.validated_data['info']['model'] = 'gpt-3.5-turbo' - copywrite, _ = Copywrite.objects.get_or_create( - user=request.user, defaults={'user': request.user} - ) - service = Chatgpt - input_message = Message.objects.create( - **serializer.validated_data, - content_object=copywrite, - from_model=False, - ) - setattr( - input_message.content_object, - 'model', - NeuronModel.objects.get(slug='chatgpt'), - ) - if stream: - output_messages = service(copywrite).stream(input_message) - return StreamingHttpResponse( - output_messages, content_type='text/event-stream' - ) - else: - try: - output_messages = service(copywrite).make(input_message) - except Exception as e: - input_message.is_sent = False - input_message.save() - return Response(f'Error: {e}', 400) - return Response(MessageSerializer(output_messages, many=True).data, 200) - else: - return Response(serializer.errors, 400) - - -class CopywriteAPIView(APIView): - permission_classes = [ - IsAuthenticated, - ] - - @extend_schema( - request=None, - responses={ - 204: None, - }, - ) - def put(self, request, message_uid, *args, **kwargs): - """ - Put message into favourite - """ - copywrite = Copywrite.objects.get(user=request.user) - message = Message.objects.get( - uid=message_uid, chats_chats_messages=copywrite, is_deleted=False - ) - message.is_favourite = True - message.save() - return Response(status=204) - - @extend_schema( - responses={204: None}, - ) - def delete(self, request, message_uid, *args, **kwargs): - """ - Hide message - """ - copywrite = Copywrite.objects.get(user=request.user) - message = Message.objects.get( - pk=message_uid, copyright_copyrights_messages=copywrite, is_deleted=False - ) - message.is_deleted = True - message.save() - return Response(status=204) @@ -0,0 +1 @@ +COPYWRITE_WS_KEY = 'copywrite_%s' @@ -0,0 +1,29 @@ +from logging import getLogger + +from channels.generic.websocket import AsyncJsonWebsocketConsumer +from channels_redis.core import RedisChannelLayer + +from tools.copywrite.constants import COPYWRITE_WS_KEY + +logger = getLogger(__name__) + + +class CopywriteConsumer(AsyncJsonWebsocketConsumer): + channel_layer: RedisChannelLayer + + async def connect(self): + await self.channel_layer.group_add( + COPYWRITE_WS_KEY % f"{self.scope['url_route']['kwargs']['copywrite_id']}", + self.channel_name, + ) + return await self.accept() + + async def close(self, code=None, reason=None): + await self.channel_layer.group_discard( + COPYWRITE_WS_KEY % f"{self.scope['url_route']['kwargs']['copywrite_id']}", + self.channel_name, + ) + return await super().close(code, reason) + + async def generate_chunk_event(self, event_data): + return await self.send_json(event_data) @@ -1,84 +1,183 @@ -from django.contrib.postgres.fields import ArrayField +from uuid import uuid4 + +from django.contrib.auth import get_user_model from django.db import models from django.db.models import QuerySet -from django.utils import timezone from django_minio_backend import MinioBackend - -from messages.models import SingleStore - - -def template_picture_upload(instance: 'Template', filename: str): - return filename +from ordered_model.models import OrderedModel +from polymorphic.models import PolymorphicManager, PolymorphicModel -class Copywrite(SingleStore): - created_at = models.DateTimeField(default=timezone.now, verbose_name='Когда создан', editable=False) +def template_picture_uploader(instance: 'Template', filename: str): + return f'{instance.title}/{filename}' - @property - def output_messages(self): - return self.messages.filter(from_model=True) - - def __str__(self) -> str: - return f'Копирайт-хранилище пользователя {self.user}' - class Meta: - verbose_name = 'Хранилище' - verbose_name_plural = 'Копирайт-хранилища' - ordering = ['-created_at'] - - -class Category(models.Model): +class TemplateCategory(models.Model): title = models.CharField(max_length=50, default='Общее', verbose_name='Название') + slug = models.CharField(max_length=50, verbose_name='Ярлык') def __str__(self) -> str: return self.title @property def templates(self) -> QuerySet['Template']: - return self.templates_copyright.all() + return self.templates_categories.all() class Meta: verbose_name = 'Категория' verbose_name_plural = 'Категории' -class Template(models.Model): - class LanguageChoices(models.TextChoices): - RU = ('ru', 'Русский') - EN = ('en', 'Английский') - GE = ('ge', 'Немецкий') - IT = ('it', 'Итальянский') - FR = ('fr', 'Французский') +class Template(OrderedModel): + id = models.UUIDField( + primary_key=True, default=uuid4, editable=False, verbose_name='ID' + ) title = models.CharField(max_length=50, unique=True, verbose_name='Название') - description = models.CharField(max_length=200, verbose_name='Описание') + description = models.CharField( + null=True, blank=True, max_length=200, verbose_name='Описание' + ) picture = models.FileField( verbose_name='Картинка', storage=MinioBackend(bucket_name='air-templates-pictures'), - upload_to=template_picture_upload, + upload_to=template_picture_uploader, null=True, + blank=True, ) - categories = models.ManyToManyField( - 'Category', verbose_name='Категория', related_name='templates_copyright' + category = models.ForeignKey( + TemplateCategory, + on_delete=models.PROTECT, + verbose_name='Категория', + related_name='templates_categories', ) - theme = models.CharField('Тема', max_length=200) - content = models.CharField('Контент', max_length=2048) - target_audience = models.CharField('Целевая аудитория', max_length=100) - resources_urls = ArrayField( - verbose_name='Интернет-ресурсы', - help_text='Сайты, откуда брать информацию', - base_field=models.URLField('Ссылка на сайт', max_length=300), - ) - keywords = ArrayField( - verbose_name='Ключевые слова', base_field=models.CharField('Ключевое слово', max_length=300) + + content = models.TextField( + verbose_name='Контент', + help_text='Синтаксис в этом поле зависит от Движка шаблонов', ) - tov = models.CharField('Тон голоса', max_length=100) - language = models.CharField('Язык для копирайта', max_length=20, choices=LanguageChoices.choices) - params = models.JSONField(verbose_name='Параметры для GPT', default=dict) + + @property + def variables(self) -> QuerySet['TemplateVariable']: + return self.template_variables.all() + + @property + def copywrites(self) -> QuerySet['TemplateCopywrite']: + return self.template_copywrites.all() def __str__(self) -> str: return self.title - class Meta: + class Meta(OrderedModel.Meta): verbose_name = 'Шаблон' verbose_name_plural = 'Шаблоны' + + +class TemplateVariable(OrderedModel): + id = models.UUIDField( + primary_key=True, default=uuid4, editable=False, verbose_name='ID' + ) + + template = models.ForeignKey( + Template, + on_delete=models.CASCADE, + verbose_name='Шаблон', + related_name='template_variables', + ) + + name = models.CharField(max_length=100, verbose_name='Название') + sysname = models.CharField(max_length=100, verbose_name='Системное название') + default_value = models.JSONField( + null=True, blank=True, verbose_name='Стандартное значение' + ) + + class Meta(OrderedModel.Meta): + verbose_name = 'Переменная шаблона' + verbose_name_plural = 'Переменные шаблона' + + +class Copywrite(PolymorphicModel): + id = models.UUIDField( + primary_key=True, default=uuid4, editable=False, verbose_name='ID' + ) + user = models.ForeignKey( + get_user_model(), + on_delete=models.SET_NULL, + null=True, + blank=True, + verbose_name='Пользователь', + related_name='user_copywrites', + ) + output_content = models.TextField( + null=True, blank=True, verbose_name='Исходящий промпт' + ) + starred = models.BooleanField(default=False, verbose_name='Добавлено в избранное') + created_at = models.DateTimeField(auto_now_add=True, verbose_name='Когда создано') + + objects = PolymorphicManager() + + @property + def type(self) -> str: + return self.polymorphic_ctype.model[ + : self.polymorphic_ctype.model.index('copywrite') + ] + + @property + def generated(self): + return self.generated_at is not None and ( + self.output_content is None or self.output_content == '' + ) + + class Meta: + verbose_name = 'Копирайт' + verbose_name_plural = 'Копирайты' + + +class SelfCopywrite(Copywrite): + input_content = models.TextField( + null=True, blank=True, verbose_name='Входящий промпт' + ) + + class Meta: + verbose_name = 'Самописный копирайт' + verbose_name_plural = 'Самописные копирайты' + + +class TemplateCopywrite(Copywrite): + template = models.ForeignKey( + Template, + on_delete=models.CASCADE, + verbose_name='Шаблон', + related_name='template_copywrites', + ) + + @property + def overriden_variables(self) -> QuerySet['OverridenVariable']: + return self.template_copywrite_overriden_variables.all() + + class Meta: + verbose_name = 'Шаблонный копирайт' + verbose_name_plural = 'Шаблонные копирайты' + + +class OverridenVariable(models.Model): + id = models.UUIDField( + primary_key=True, default=uuid4, editable=False, verbose_name='ID' + ) + variable = models.ForeignKey( + TemplateVariable, + on_delete=models.PROTECT, + verbose_name='Переменная', + related_name='template_variable_overriden_variables', + ) + copywrite = models.ForeignKey( + TemplateCopywrite, + on_delete=models.CASCADE, + verbose_name='Копирайт', + related_name='template_copywrite_overriden_variables', + ) + value = models.JSONField(verbose_name='Значение') + + class Meta: + verbose_name = 'Переопределенная переменная' + verbose_name_plural = 'Переопределенные переменные' + unique_together = ['copywrite', 'variable'] @@ -0,0 +1,118 @@ +from typing import List, Literal +from uuid import UUID + +from channels.routing import URLRouter +from django.urls import re_path +from ninja import Query, Router + +from authentication.security import SyncAuthBearer +from payments.tasks import withdraw as withdraw_task +from tools.copywrite.consumers import CopywriteConsumer +from tools.copywrite.schemas import ( + CopywriteFilterSchema, + CreateDraftSchema, + CreateOverridenVariableSchema, + OverridenVariableSchema, + SelfCopywriteSchema, + TemplateCategorySchema, + TemplateCopywriteSchema, + TemplateFilterSchema, + TemplateSchema, + TemplateShortSchema, + UpdateOverridenVariableSchema, +) +from tools.copywrite.services.copywrite import CopywriteService +from tools.copywrite.services.template import TemplateService + +from .tasks import generate as generate_task + +router = Router(tags=['copywrite'], auth=SyncAuthBearer()) + +ws_router = URLRouter( + [ + re_path( + 'copywrites/(?P[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}|)/$', + CopywriteConsumer.as_asgi(), + ) + ] +) + + +@router.get( + 'copywrites/', + tags=['copywrite/copywrites'], + response=List[TemplateCopywriteSchema | SelfCopywriteSchema], +) +def list_copywrites( + request, + filters: CopywriteFilterSchema = Query(...), + ordering: Literal['starred'] = 'starred', +): + return CopywriteService.list_copywrites_by_user( + user=request.auth, filters=filters.get_filter_expression(), ordering=ordering + ) + + +@router.post( + 'copywrites/', + tags=['copywrite/copywrites'], + response=TemplateCopywriteSchema | SelfCopywriteSchema, +) +def create_draft(request, data: CreateDraftSchema): + return CopywriteService.create_draft(user=request.auth, **data.model_dump()) + + +@router.get( + 'copywrites/{id}/', + tags=['copywrite/copywrites'], + response=TemplateCopywriteSchema | SelfCopywriteSchema, +) +def get_copywrite_by_id(request, id: UUID): + return CopywriteService.get_copywrite_by_id(copywrite_id=id) + + +@router.put('copywrites/{id}/generate/', tags=['copywrite/copywrites']) +def generate(request, id: UUID): + generate_task.delay(copywrite_id=id) | withdraw_task.delay() + + +@router.put('copywrites/{id}/', tags=['copywrite/copywrites']) +def update_copywrite(request, id: UUID): + """Не реализовано!!!""" + return CopywriteService.update() + + +@router.post('variables/', tags=['copywrite/variables'], response=OverridenVariableSchema) +def override_variable(request, data: CreateOverridenVariableSchema): + return CopywriteService.override_variable(**data.model_dump()) + + +@router.put('variables/{id}/', tags=['copywrite/variables'], response={204: None}) +def update_overriden_variable(request, id: UUID, data: UpdateOverridenVariableSchema): + return CopywriteService.update_overriden_variable(variable_id=id, **data.model_dump()) + + +@router.delete('variables/{id}/', tags=['copywrite/variables'], response={204: None}) +def remove_overriden_variable(request, id: UUID): + CopywriteService.remove_overriden_variable(variable_id=id) + + +@router.get( + 'templates/categories/', + tags=['copywrite/templates'], + response=List[TemplateCategorySchema], +) +def list_template_categories(request): + return TemplateService.list_categories() + + +@router.get( + 'templates/', tags=['copywrite/templates'], response=List[TemplateShortSchema] +) +def list_templates(request, filters: TemplateFilterSchema = Query(...)): + return TemplateService.list_templates(filters=filters.get_filter_expression()) + + +@router.get('templates/{id}/', tags=['copywrite/templates'], response=TemplateSchema) +def get_template_by_id(request, id: UUID): + return TemplateService.get_template_by_id(template_id=id) @@ -0,0 +1,113 @@ +from typing import Any, List, Literal +from uuid import UUID + +from django.db.models import Q +from ninja import Field, FilterSchema, ModelSchema, Schema + +from tools.copywrite.models import ( + OverridenVariable, + SelfCopywrite, + Template, + TemplateCategory, + TemplateCopywrite, + TemplateVariable, +) + + +class CreateDraftSchema(Schema): + type: str + initial: dict[str, Any] = {} + + +class TemplateVariableSchema(ModelSchema): + class Meta: + model = TemplateVariable + exclude = ('order', 'sysname', 'template') + + +class TemplateShortSchema(ModelSchema): + class Meta: + model = Template + fields = ('id', 'title', 'description', 'picture') + + +class TemplateCategorySchema(ModelSchema): + class Meta: + model = TemplateCategory + fields = ('title', 'slug') + + +class TemplateSchema(ModelSchema): + variables: List[TemplateVariableSchema] + + class Meta: + model = Template + exclude = ('order', 'content', 'category', 'description') + + +class TemplateFilterSchema(FilterSchema): + category: List[str] = Field(None, q='category__slug__in') + + +class BaseCopywriteSchema: + type: str + + +class SelfCopywriteSchema(BaseCopywriteSchema, ModelSchema): + generated: bool + + class Meta: + model = SelfCopywrite + exclude = ( + 'copywrite_ptr', + 'user', + 'polymorphic_ctype', + 'created_at', + ) + + +class OverridenVariableSchema(ModelSchema): + class Meta: + model = OverridenVariable + fields = ('id', 'variable', 'value') + + +class CreateOverridenVariableSchema(ModelSchema): + copywrite_id: UUID + variable_id: UUID + + class Meta: + model = OverridenVariable + exclude = ('id', 'copywrite', 'variable') + + +class TemplateCopywriteSchema(BaseCopywriteSchema, ModelSchema): + template: TemplateSchema + overriden_variables: List[OverridenVariableSchema] + generated: bool + + class Meta: + model = TemplateCopywrite + exclude = ( + 'copywrite_ptr', + 'user', + 'polymorphic_ctype', + 'created_at', + ) + + +class CopywriteFilterSchema(FilterSchema): + types: List[Literal['template', 'self']] = [] + + def filter_types(self, value: List[Literal['template', 'self']]): + if value: + return Q( + polymorphic_ctype__model__in=list(map(lambda x: f'{x}copywrite', value)) + ) + return Q() + + +class UpdateOverridenVariableSchema(ModelSchema): + class Meta: + model = OverridenVariable + fields = ('value',) @@ -1,29 +0,0 @@ -from rest_framework import serializers - -from .models import Category, Template - - -class CategorySerializer(serializers.ModelSerializer): - class Meta: - model = Category - fields = '__all__' - - -class TemplatesSerializer(serializers.ModelSerializer): - class Meta: - model = Template - exclude = ('categories', 'params') - - -class TemplateSerializer(serializers.ModelSerializer): - class Meta: - model = Template - exclude = ('categories',) - - -class TemplateCreateSerializer(serializers.ModelSerializer): - category = serializers.PrimaryKeyRelatedField(queryset=Category.objects.all()) - - class Meta: - model = Template - fields = '__all__' @@ -0,0 +1,18 @@ +from uuid import UUID + +from celery import shared_task +from channels.layers import get_channel_layer +from channels_redis.core import RedisChannelLayer + +from tools.copywrite.constants import COPYWRITE_WS_KEY +from tools.copywrite.services.copywrite import CopywriteService + + +@shared_task +def generate(copywrite_id: UUID): + layer: RedisChannelLayer = get_channel_layer() + for chunk in CopywriteService.generate(copywrite_id=copywrite_id): + layer.group_send( + COPYWRITE_WS_KEY.format(copywrite_id), + {'type': 'generate.chunk.event', 'event_data': {'chunk': chunk}}, + ) @@ -1,14 +0,0 @@ -from django.urls import path - -from .apis import CopywriteAPIView, CopywritesAPIView, TemplateAPIView, TemplatesAPIView - -urlpatterns = [ - path('', CopywritesAPIView.as_view(), name='copywrite'), - path( - '/', - CopywriteAPIView.as_view(), - name='copywrite-message', - ), - path('templates/', TemplatesAPIView.as_view()), - path('templates/', TemplateAPIView.as_view()), -] @@ -73,3 +73,7 @@ MAIN_SITE_URL=https://app.air.fail DJANGO_SUPERUSER_EMAIL=example@root.ru DJANGO_SUPERUSER_USERNAME=root DJANGO_SUPERUSER_PASSWORD=root + +# DJANGO CHANNELS +CHANNELS_HOST_MDB=channels-mdb +CHANNELS_PORT_MDB=6379 \ No newline at end of file @@ -10,11 +10,10 @@ services: - python manage.py initialize_buckets && python manage.py collectstatic --no-input && python manage.py migrate && (python manage.py createsuperuser --no-input || true) && - python -m gunicorn --bind 0.0.0.0:8000 --timeout 300 backend.wsgi:application --log-level debug --threads 1 -w 1 --reload + python -m uvicorn --host 0.0.0.0 --workers 1 --timeout-keep-alive 300 --ws wsproto --loop uvloop --http httptools --lifespan off backend.asgi:application --log-level debug --reload volumes: - .:/code ports: - - "5678:5678" - "8000:8000" env_file: - .env @@ -31,6 +30,10 @@ services: image: redis:alpine restart: unless-stopped + channels-mdb: + image: redis:alpine + restart: unless-stopped + celery: restart: unless-stopped build: @@ -14,7 +14,7 @@ services: - | python manage.py collectstatic --no-input python manage.py compilemessages - python -m gunicorn --bind 0.0.0.0:8000 --timeout 300 backend.wsgi:application --log-level info --threads 4 -w 8 + python -m uvicorn --host 0.0.0.0 --workers 1 --timeout-keep-alive 300 --ws wsproto --loop uvloop --http httptools --lifespan off --log-level info backend.asgi:application ports: - "8000:8000" env_file: @@ -59,6 +59,11 @@ services: restart: unless-stopped container_name: celery-mdb + channels-mdb: + image: redis:alpine + restart: unless-stopped + container_name: channels-mdb + networks: default: name: "air" @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -271,6 +271,49 @@ docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphi tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +[[package]] +name = "autobahn" +version = "24.4.2" +description = "WebSocket client & server library, WAMP real-time framework" +optional = false +python-versions = ">=3.9" +files = [ + {file = "autobahn-24.4.2-py2.py3-none-any.whl", hash = "sha256:c56a2abe7ac78abbfb778c02892d673a4de58fd004d088cd7ab297db25918e81"}, + {file = "autobahn-24.4.2.tar.gz", hash = "sha256:a2d71ef1b0cf780b6d11f8b205fd2c7749765e65795f2ea7d823796642ee92c9"}, +] + +[package.dependencies] +cryptography = ">=3.4.6" +hyperlink = ">=21.0.0" +setuptools = "*" +txaio = ">=21.2.1" + +[package.extras] +all = ["PyGObject (>=3.40.0)", "argon2-cffi (>=20.1.0)", "attrs (>=20.3.0)", "base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "cffi (>=1.14.5)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "flatbuffers (>=22.12.6)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "msgpack (>=1.0.2)", "passlib (>=1.7.4)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "py-ubjson (>=0.16.1)", "pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "python-snappy (>=0.6.0)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "rlp (>=2.0.1)", "service-identity (>=18.1.0)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "twisted (>=24.3.0)", "u-msgpack-python (>=2.1)", "ujson (>=4.0.2)", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)", "zope.interface (>=5.2.0)"] +compress = ["python-snappy (>=0.6.0)"] +dev = ["backports.tempfile (>=1.0)", "build (>=1.2.1)", "bumpversion (>=0.5.3)", "codecov (>=2.0.15)", "flake8 (<5)", "humanize (>=0.5.1)", "mypy (>=0.610)", "passlib", "pep8-naming (>=0.3.3)", "pip (>=9.0.1)", "pyenchant (>=1.6.6)", "pyflakes (>=1.0.0)", "pyinstaller (>=4.2)", "pylint (>=1.9.2)", "pytest (>=3.4.2)", "pytest-aiohttp", "pytest-asyncio (>=0.14.0)", "pytest-runner (>=2.11.1)", "pyyaml (>=4.2b4)", "qualname", "sphinx (>=1.7.1)", "sphinx-autoapi (>=1.7.0)", "sphinx-rtd-theme (>=0.1.9)", "sphinxcontrib-images (>=0.9.1)", "tox (>=4.2.8)", "tox-gh-actions (>=2.2.0)", "twine (>=3.3.0)", "twisted (>=22.10.0)", "txaio (>=20.4.1)", "watchdog (>=0.8.3)", "wheel (>=0.36.2)", "yapf (==0.29.0)"] +encryption = ["pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "service-identity (>=18.1.0)"] +nvx = ["cffi (>=1.14.5)"] +scram = ["argon2-cffi (>=20.1.0)", "cffi (>=1.14.5)", "passlib (>=1.7.4)"] +serialization = ["cbor2 (>=5.2.0)", "flatbuffers (>=22.12.6)", "msgpack (>=1.0.2)", "py-ubjson (>=0.16.1)", "u-msgpack-python (>=2.1)", "ujson (>=4.0.2)"] +twisted = ["attrs (>=20.3.0)", "twisted (>=24.3.0)", "zope.interface (>=5.2.0)"] +ui = ["PyGObject (>=3.40.0)"] +xbr = ["base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "rlp (>=2.0.1)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)"] + +[[package]] +name = "automat" +version = "24.8.1" +description = "Self-service finite-state machines for the programmer on the go." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Automat-24.8.1-py3-none-any.whl", hash = "sha256:bf029a7bc3da1e2c24da2343e7598affaa9f10bf0ab63ff808566ce90551e02a"}, + {file = "automat-24.8.1.tar.gz", hash = "sha256:b34227cf63f6325b8ad2399ede780675083e439b20c323d376373d8ee6306d88"}, +] + +[package.extras] +visualize = ["Twisted (>=16.1.1)", "graphviz (>0.5.1)"] + [[package]] name = "billiard" version = "4.2.0" @@ -455,6 +498,47 @@ files = [ [package.dependencies] pycparser = "*" +[[package]] +name = "channels" +version = "4.2.0" +description = "Brings async, event-driven capabilities to Django." +optional = false +python-versions = ">=3.8" +files = [ + {file = "channels-4.2.0-py3-none-any.whl", hash = "sha256:6b75bc8d6888fb7236e7e7bf1948520b72d296ad08216a242fc56b1db0ffde1a"}, + {file = "channels-4.2.0.tar.gz", hash = "sha256:d9e707487431ba5dbce9af982970dab3b0efd786580fadb99e45dca5e39fdd59"}, +] + +[package.dependencies] +asgiref = ">=3.6.0,<4" +daphne = {version = ">=4.0.0", optional = true, markers = "extra == \"daphne\""} +Django = ">=4.2" + +[package.extras] +daphne = ["daphne (>=4.0.0)"] +tests = ["async-timeout", "coverage (>=4.5,<5.0)", "pytest", "pytest-asyncio", "pytest-django"] + +[[package]] +name = "channels-redis" +version = "4.2.1" +description = "Redis-backed ASGI channel layer implementation" +optional = false +python-versions = ">=3.8" +files = [ + {file = "channels_redis-4.2.1-py3-none-any.whl", hash = "sha256:2ca33105b3a04b5a327a9c47dd762b546f30b76a0cd3f3f593a23d91d346b6f4"}, + {file = "channels_redis-4.2.1.tar.gz", hash = "sha256:8375e81493e684792efe6e6eca60ef3d7782ef76c6664057d2e5c31e80d636dd"}, +] + +[package.dependencies] +asgiref = ">=3.2.10,<4" +channels = "*" +msgpack = ">=1.0,<2.0" +redis = ">=4.6" + +[package.extras] +cryptography = ["cryptography (>=1.3.0)"] +tests = ["async-timeout", "cryptography (>=1.3.0)", "pytest", "pytest-asyncio", "pytest-timeout"] + [[package]] name = "charset-normalizer" version = "3.3.2" @@ -628,6 +712,17 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "constantly" +version = "23.10.4" +description = "Symbolic constants in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "constantly-23.10.4-py3-none-any.whl", hash = "sha256:3fd9b4d1c3dc1ec9757f3c52aef7e53ad9323dbe39f51dfd4c43853b68dfa3f9"}, + {file = "constantly-23.10.4.tar.gz", hash = "sha256:aa92b70a33e2ac0bb33cd745eb61776594dc48764b06c35e0efd050b7f1c7cbd"}, +] + [[package]] name = "coverage" version = "7.6.1" @@ -775,6 +870,25 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "daphne" +version = "4.1.2" +description = "Django ASGI (HTTP/WebSocket) server" +optional = false +python-versions = ">=3.8" +files = [ + {file = "daphne-4.1.2-py3-none-any.whl", hash = "sha256:618d1322bb4d875342b99dd2a10da2d9aae7ee3645f765965fdc1e658ea5290a"}, + {file = "daphne-4.1.2.tar.gz", hash = "sha256:fcbcace38eb86624ae247c7ffdc8ac12f155d7d19eafac4247381896d6f33761"}, +] + +[package.dependencies] +asgiref = ">=3.5.2,<4" +autobahn = ">=22.4.2" +twisted = {version = ">=22.4", extras = ["tls"]} + +[package.extras] +tests = ["django", "hypothesis", "pytest", "pytest-asyncio"] + [[package]] name = "dataclasses-json" version = "0.6.7" @@ -1031,6 +1145,26 @@ files = [ Django = ">=3.2" minio = ">=7.2.8" +[[package]] +name = "django-ninja" +version = "1.3.0" +description = "Django Ninja - Fast Django REST framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "django_ninja-1.3.0-py3-none-any.whl", hash = "sha256:f58096b6c767d1403dfd6c49743f82d780d7b9688d9302ecab316ac1fa6131bb"}, + {file = "django_ninja-1.3.0.tar.gz", hash = "sha256:5b320e2dc0f41a6032bfa7e1ebc33559ae1e911a426f0c6be6674a50b20819be"}, +] + +[package.dependencies] +Django = ">=3.1" +pydantic = ">=2.0,<3.0.0" + +[package.extras] +dev = ["pre-commit"] +doc = ["markdown-include", "mkdocs", "mkdocs-material", "mkdocstrings"] +test = ["django-stubs", "mypy (==1.7.1)", "psycopg2-binary", "pytest", "pytest-asyncio", "pytest-cov", "pytest-django", "ruff (==0.5.7)"] + [[package]] name = "django-oauth-toolkit" version = "2.4.0" @@ -1060,6 +1194,20 @@ files = [ {file = "django_ordered_model-3.7.4-py3-none-any.whl", hash = "sha256:dfcd3183fe0749dad1c9971cba1d6240ce7328742a30ddc92feca41107bb241d"}, ] +[[package]] +name = "django-polymorphic" +version = "3.1.0" +description = "Seamless polymorphic inheritance for Django models" +optional = false +python-versions = "*" +files = [ + {file = "django-polymorphic-3.1.0.tar.gz", hash = "sha256:d6955b5308bf6e41dcb22ba7c96f00b51dfa497a8a5ab1e9c06c7951bf417bf8"}, + {file = "django_polymorphic-3.1.0-py3-none-any.whl", hash = "sha256:08bc4f4f4a773a19b2deced5a56deddd1ef56ebd15207bf4052e2901c25ef57e"}, +] + +[package.dependencies] +Django = ">=2.1" + [[package]] name = "django-prometheus" version = "2.3.1" @@ -1477,69 +1625,6 @@ files = [ {file = "funcy-2.0.tar.gz", hash = "sha256:3963315d59d41c6f30c04bc910e10ab50a3ac4a225868bfa96feed133df075cb"}, ] -[[package]] -name = "gevent" -version = "24.2.1" -description = "Coroutine-based network library" -optional = false -python-versions = ">=3.8" -files = [ - {file = "gevent-24.2.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f947a9abc1a129858391b3d9334c45041c08a0f23d14333d5b844b6e5c17a07"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde283313daf0b34a8d1bab30325f5cb0f4e11b5869dbe5bc61f8fe09a8f66f3"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1df555431f5cd5cc189a6ee3544d24f8c52f2529134685f1e878c4972ab026"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14532a67f7cb29fb055a0e9b39f16b88ed22c66b96641df8c04bdc38c26b9ea5"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd23df885318391856415e20acfd51a985cba6919f0be78ed89f5db9ff3a31cb"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ca80b121bbec76d7794fcb45e65a7eca660a76cc1a104ed439cdbd7df5f0b060"}, - {file = "gevent-24.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9913c45d1be52d7a5db0c63977eebb51f68a2d5e6fd922d1d9b5e5fd758cc98"}, - {file = "gevent-24.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:918cdf8751b24986f915d743225ad6b702f83e1106e08a63b736e3a4c6ead789"}, - {file = "gevent-24.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:3d5325ccfadfd3dcf72ff88a92fb8fc0b56cacc7225f0f4b6dcf186c1a6eeabc"}, - {file = "gevent-24.2.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:03aa5879acd6b7076f6a2a307410fb1e0d288b84b03cdfd8c74db8b4bc882fc5"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8bb35ce57a63c9a6896c71a285818a3922d8ca05d150fd1fe49a7f57287b836"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7f87c2c02e03d99b95cfa6f7a776409083a9e4d468912e18c7680437b29222c"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968581d1717bbcf170758580f5f97a2925854943c45a19be4d47299507db2eb7"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7899a38d0ae7e817e99adb217f586d0a4620e315e4de577444ebeeed2c5729be"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f5e8e8d60e18d5f7fd49983f0c4696deeddaf6e608fbab33397671e2fcc6cc91"}, - {file = "gevent-24.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fbfdce91239fe306772faab57597186710d5699213f4df099d1612da7320d682"}, - {file = "gevent-24.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cdf66977a976d6a3cfb006afdf825d1482f84f7b81179db33941f2fc9673bb1d"}, - {file = "gevent-24.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:1dffb395e500613e0452b9503153f8f7ba587c67dd4a85fc7cd7aa7430cb02cc"}, - {file = "gevent-24.2.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:6c47ae7d1174617b3509f5d884935e788f325eb8f1a7efc95d295c68d83cce40"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7cac622e11b4253ac4536a654fe221249065d9a69feb6cdcd4d9af3503602e0"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf5b9c72b884c6f0c4ed26ef204ee1f768b9437330422492c319470954bc4cc7"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5de3c676e57177b38857f6e3cdfbe8f38d1cd754b63200c0615eaa31f514b4f"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4faf846ed132fd7ebfbbf4fde588a62d21faa0faa06e6f468b7faa6f436b661"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:368a277bd9278ddb0fde308e6a43f544222d76ed0c4166e0d9f6b036586819d9"}, - {file = "gevent-24.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f8a04cf0c5b7139bc6368b461257d4a757ea2fe89b3773e494d235b7dd51119f"}, - {file = "gevent-24.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9d8d0642c63d453179058abc4143e30718b19a85cbf58c2744c9a63f06a1d388"}, - {file = "gevent-24.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:94138682e68ec197db42ad7442d3cf9b328069c3ad8e4e5022e6b5cd3e7ffae5"}, - {file = "gevent-24.2.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:8f4b8e777d39013595a7740b4463e61b1cfe5f462f1b609b28fbc1e4c4ff01e5"}, - {file = "gevent-24.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141a2b24ad14f7b9576965c0c84927fc85f824a9bb19f6ec1e61e845d87c9cd8"}, - {file = "gevent-24.2.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:9202f22ef811053077d01f43cc02b4aaf4472792f9fd0f5081b0b05c926cca19"}, - {file = "gevent-24.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2955eea9c44c842c626feebf4459c42ce168685aa99594e049d03bedf53c2800"}, - {file = "gevent-24.2.1-cp38-cp38-win32.whl", hash = "sha256:44098038d5e2749b0784aabb27f1fcbb3f43edebedf64d0af0d26955611be8d6"}, - {file = "gevent-24.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:117e5837bc74a1673605fb53f8bfe22feb6e5afa411f524c835b2ddf768db0de"}, - {file = "gevent-24.2.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:2ae3a25ecce0a5b0cd0808ab716bfca180230112bb4bc89b46ae0061d62d4afe"}, - {file = "gevent-24.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ceb59986456ce851160867ce4929edaffbd2f069ae25717150199f8e1548b8"}, - {file = "gevent-24.2.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:2e9ac06f225b696cdedbb22f9e805e2dd87bf82e8fa5e17756f94e88a9d37cf7"}, - {file = "gevent-24.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:90cbac1ec05b305a1b90ede61ef73126afdeb5a804ae04480d6da12c56378df1"}, - {file = "gevent-24.2.1-cp39-cp39-win32.whl", hash = "sha256:782a771424fe74bc7e75c228a1da671578c2ba4ddb2ca09b8f959abdf787331e"}, - {file = "gevent-24.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:3adfb96637f44010be8abd1b5e73b5070f851b817a0b182e601202f20fa06533"}, - {file = "gevent-24.2.1-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:7b00f8c9065de3ad226f7979154a7b27f3b9151c8055c162332369262fc025d8"}, - {file = "gevent-24.2.1.tar.gz", hash = "sha256:432fc76f680acf7cf188c2ee0f5d3ab73b63c1f03114c7cd8a34cebbe5aa2056"}, -] - -[package.dependencies] -cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} -greenlet = {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""} -"zope.event" = "*" -"zope.interface" = "*" - -[package.extras] -dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] -docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] -monitor = ["psutil (>=5.7.0)"] -recommended = ["cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] -test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] - [[package]] name = "google-ai-generativelanguage" version = "0.4.0" @@ -1809,28 +1894,6 @@ googleapis-common-protos = ">=1.5.5" grpcio = ">=1.62.3" protobuf = ">=4.21.6" -[[package]] -name = "gunicorn" -version = "23.0.0" -description = "WSGI HTTP Server for UNIX" -optional = false -python-versions = ">=3.7" -files = [ - {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, - {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, -] - -[package.dependencies] -gevent = {version = ">=1.4.0", optional = true, markers = "extra == \"gevent\""} -packaging = "*" - -[package.extras] -eventlet = ["eventlet (>=0.24.1,!=0.36.0)"] -gevent = ["gevent (>=1.4.0)"] -setproctitle = ["setproctitle"] -testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] -tornado = ["tornado (>=0.2)"] - [[package]] name = "h11" version = "0.14.0" @@ -1889,15 +1952,70 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] trio = ["trio (>=0.22.0,<0.26.0)"] +[[package]] +name = "httptools" +version = "0.6.4" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0"}, + {file = "httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da"}, + {file = "httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1"}, + {file = "httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50"}, + {file = "httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959"}, + {file = "httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4"}, + {file = "httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c"}, + {file = "httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069"}, + {file = "httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a"}, + {file = "httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975"}, + {file = "httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636"}, + {file = "httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721"}, + {file = "httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988"}, + {file = "httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17"}, + {file = "httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2"}, + {file = "httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44"}, + {file = "httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1"}, + {file = "httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2"}, + {file = "httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81"}, + {file = "httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f"}, + {file = "httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970"}, + {file = "httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660"}, + {file = "httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083"}, + {file = "httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3"}, + {file = "httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071"}, + {file = "httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5"}, + {file = "httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0"}, + {file = "httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8"}, + {file = "httptools-0.6.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d3f0d369e7ffbe59c4b6116a44d6a8eb4783aae027f2c0b366cf0aa964185dba"}, + {file = "httptools-0.6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:94978a49b8f4569ad607cd4946b759d90b285e39c0d4640c6b36ca7a3ddf2efc"}, + {file = "httptools-0.6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40dc6a8e399e15ea525305a2ddba998b0af5caa2566bcd79dcbe8948181eeaff"}, + {file = "httptools-0.6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab9ba8dcf59de5181f6be44a77458e45a578fc99c31510b8c65b7d5acc3cf490"}, + {file = "httptools-0.6.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fc411e1c0a7dcd2f902c7c48cf079947a7e65b5485dea9decb82b9105ca71a43"}, + {file = "httptools-0.6.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d54efd20338ac52ba31e7da78e4a72570cf729fac82bc31ff9199bedf1dc7440"}, + {file = "httptools-0.6.4-cp38-cp38-win_amd64.whl", hash = "sha256:df959752a0c2748a65ab5387d08287abf6779ae9165916fe053e68ae1fbdc47f"}, + {file = "httptools-0.6.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:85797e37e8eeaa5439d33e556662cc370e474445d5fab24dcadc65a8ffb04003"}, + {file = "httptools-0.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:db353d22843cf1028f43c3651581e4bb49374d85692a85f95f7b9a130e1b2cab"}, + {file = "httptools-0.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ffd262a73d7c28424252381a5b854c19d9de5f56f075445d33919a637e3547"}, + {file = "httptools-0.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c346571fa50d2e9856a37d7cd9435a25e7fd15e236c397bf224afaa355fe9"}, + {file = "httptools-0.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aafe0f1918ed07b67c1e838f950b1c1fabc683030477e60b335649b8020e1076"}, + {file = "httptools-0.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0e563e54979e97b6d13f1bbc05a96109923e76b901f786a5eae36e99c01237bd"}, + {file = "httptools-0.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:b799de31416ecc589ad79dd85a0b2657a8fe39327944998dea368c1d4c9e55e6"}, + {file = "httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c"}, +] + +[package.extras] +test = ["Cython (>=0.29.24)"] + [[package]] name = "httpx" -version = "0.27.2" +version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, ] [package.dependencies] @@ -1905,7 +2023,6 @@ anyio = "*" certifi = "*" httpcore = "==1.*" idna = "*" -sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] @@ -1936,6 +2053,20 @@ files = [ {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, ] +[[package]] +name = "hyperlink" +version = "21.0.0" +description = "A featureful, immutable, and correct URL for Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "hyperlink-21.0.0-py2.py3-none-any.whl", hash = "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4"}, + {file = "hyperlink-21.0.0.tar.gz", hash = "sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b"}, +] + +[package.dependencies] +idna = ">=2.5" + [[package]] name = "idna" version = "3.10" @@ -1950,6 +2081,23 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "incremental" +version = "24.7.2" +description = "A small library that versions your Python projects." +optional = false +python-versions = ">=3.8" +files = [ + {file = "incremental-24.7.2-py3-none-any.whl", hash = "sha256:8cb2c3431530bec48ad70513931a760f446ad6c25e8333ca5d95e24b0ed7b8fe"}, + {file = "incremental-24.7.2.tar.gz", hash = "sha256:fb4f1d47ee60efe87d4f6f0ebb5f70b9760db2b2574c59c8e8912be4ebd464c9"}, +] + +[package.dependencies] +setuptools = ">=61.0" + +[package.extras] +scripts = ["click (>=6.0)"] + [[package]] name = "inflection" version = "0.5.1" @@ -2539,6 +2687,79 @@ pycryptodome = "*" typing-extensions = "*" urllib3 = "*" +[[package]] +name = "msgpack" +version = "1.1.0" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.8" +files = [ + {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"}, + {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"}, + {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, + {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, + {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, + {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, + {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, + {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, + {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"}, + {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"}, + {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"}, + {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"}, + {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"}, + {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, +] + [[package]] name = "multidict" version = "6.1.0" @@ -3379,6 +3600,24 @@ dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pyte docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] +[[package]] +name = "pyopenssl" +version = "24.3.0" +description = "Python wrapper module around the OpenSSL library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyOpenSSL-24.3.0-py3-none-any.whl", hash = "sha256:e474f5a473cd7f92221cc04976e48f4d11502804657a08a989fb3be5514c904a"}, + {file = "pyopenssl-24.3.0.tar.gz", hash = "sha256:49f7a019577d834746bc55c5fce6ecbcec0f2b4ec5ce1cf43a9a173b8138bb36"}, +] + +[package.dependencies] +cryptography = ">=41.0.5,<45" + +[package.extras] +docs = ["sphinx (!=5.2.0,!=5.2.0.post0,!=7.2.5)", "sphinx_rtd_theme"] +test = ["pretend", "pytest (>=3.0.1)", "pytest-rerunfailures"] + [[package]] name = "pypdf2" version = "3.0.1" @@ -4075,6 +4314,30 @@ starlette = ["starlette (>=0.19.1)"] starlite = ["starlite (>=1.48)"] tornado = ["tornado (>=6)"] +[[package]] +name = "service-identity" +version = "24.2.0" +description = "Service identity verification for pyOpenSSL & cryptography." +optional = false +python-versions = ">=3.8" +files = [ + {file = "service_identity-24.2.0-py3-none-any.whl", hash = "sha256:6b047fbd8a84fd0bb0d55ebce4031e400562b9196e1e0d3e0fe2b8a59f6d4a85"}, + {file = "service_identity-24.2.0.tar.gz", hash = "sha256:b8683ba13f0d39c6cd5d625d2c5f65421d6d707b013b375c355751557cbe8e09"}, +] + +[package.dependencies] +attrs = ">=19.1.0" +cryptography = "*" +pyasn1 = "*" +pyasn1-modules = "*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "idna", "mypy", "pyopenssl", "pytest", "types-pyopenssl"] +docs = ["furo", "myst-parser", "pyopenssl", "sphinx", "sphinx-notfound-page"] +idna = ["idna"] +mypy = ["idna", "mypy", "types-pyopenssl"] +tests = ["coverage[toml] (>=5.0.2)", "pytest"] + [[package]] name = "setuptools" version = "75.1.0" @@ -4403,6 +4666,60 @@ notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] +[[package]] +name = "twisted" +version = "24.11.0" +description = "An asynchronous networking framework written in Python" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "twisted-24.11.0-py3-none-any.whl", hash = "sha256:fe403076c71f04d5d2d789a755b687c5637ec3bcd3b2b8252d76f2ba65f54261"}, + {file = "twisted-24.11.0.tar.gz", hash = "sha256:695d0556d5ec579dcc464d2856b634880ed1319f45b10d19043f2b57eb0115b5"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +automat = ">=24.8.0" +constantly = ">=15.1" +hyperlink = ">=17.1.1" +idna = {version = ">=2.4", optional = true, markers = "extra == \"tls\""} +incremental = ">=24.7.0" +pyopenssl = {version = ">=21.0.0", optional = true, markers = "extra == \"tls\""} +service-identity = {version = ">=18.1.0", optional = true, markers = "extra == \"tls\""} +typing-extensions = ">=4.2.0" +zope-interface = ">=5" + +[package.extras] +all-non-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)"] +dev = ["coverage (>=7.5,<8.0)", "cython-test-exception-raiser (>=1.0.2,<2)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "python-subunit (>=1.4,<2.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)"] +dev-release = ["pydoctor (>=23.9.0,<23.10.0)", "pydoctor (>=23.9.0,<23.10.0)", "sphinx (>=6,<7)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "towncrier (>=23.6,<24.0)"] +gtk-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pygobject", "pygobject", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +http2 = ["h2 (>=3.2,<5.0)", "priority (>=1.1.0,<2.0)"] +macos-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +mypy = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "coverage (>=7.5,<8.0)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "idna (>=2.4)", "mypy (==1.10.1)", "mypy-zope (==1.0.6)", "priority (>=1.1.0,<2.0)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)", "types-pyopenssl", "types-setuptools"] +osx-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +serial = ["pyserial (>=3.0)", "pywin32 (!=226)"] +test = ["cython-test-exception-raiser (>=1.0.2,<2)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "pyhamcrest (>=2)"] +tls = ["idna (>=2.4)", "pyopenssl (>=21.0.0)", "service-identity (>=18.1.0)"] +windows-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)", "twisted-iocpsupport (>=1.0.2)", "twisted-iocpsupport (>=1.0.2)"] + +[[package]] +name = "txaio" +version = "23.1.1" +description = "Compatibility API between asyncio/Twisted/Trollius" +optional = false +python-versions = ">=3.7" +files = [ + {file = "txaio-23.1.1-py2.py3-none-any.whl", hash = "sha256:aaea42f8aad50e0ecfb976130ada140797e9dcb85fad2cf72b0f37f8cefcb490"}, + {file = "txaio-23.1.1.tar.gz", hash = "sha256:f9a9216e976e5e3246dfd112ad7ad55ca915606b60b84a757ac769bd404ff704"}, +] + +[package.extras] +all = ["twisted (>=20.3.0)", "zope.interface (>=5.2.0)"] +dev = ["pep8 (>=1.6.2)", "pyenchant (>=1.6.6)", "pytest (>=2.6.4)", "pytest-cov (>=1.8.1)", "sphinx (>=1.2.3)", "sphinx-rtd-theme (>=0.1.9)", "sphinxcontrib-spelling (>=2.1.2)", "tox (>=2.1.1)", "tox-gh-actions (>=2.2.0)", "twine (>=1.6.5)", "wheel"] +twisted = ["twisted (>=20.3.0)", "zope.interface (>=5.2.0)"] + [[package]] name = "types-pillow" version = "10.2.0.20240822" @@ -4526,6 +4843,75 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "uvicorn" +version = "0.34.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.9" +files = [ + {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"}, + {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.21.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"}, + {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"}, + {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26"}, + {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb"}, + {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f"}, + {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c"}, + {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8"}, + {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0"}, + {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e"}, + {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb"}, + {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6"}, + {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d"}, + {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c"}, + {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2"}, + {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d"}, + {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc"}, + {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb"}, + {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f"}, + {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281"}, + {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af"}, + {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6"}, + {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816"}, + {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc"}, + {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553"}, + {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:17df489689befc72c39a08359efac29bbee8eee5209650d4b9f34df73d22e414"}, + {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc09f0ff191e61c2d592a752423c767b4ebb2986daa9ed62908e2b1b9a9ae206"}, + {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0ce1b49560b1d2d8a2977e3ba4afb2414fb46b86a1b64056bc4ab929efdafbe"}, + {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e678ad6fe52af2c58d2ae3c73dc85524ba8abe637f134bf3564ed07f555c5e79"}, + {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:460def4412e473896ef179a1671b40c039c7012184b627898eea5072ef6f017a"}, + {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:10da8046cc4a8f12c91a1c39d1dd1585c41162a15caaef165c2174db9ef18bdc"}, + {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b"}, + {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2"}, + {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0"}, + {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75"}, + {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd"}, + {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff"}, + {file = "uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3"}, +] + +[package.extras] +dev = ["Cython (>=3.0,<4.0)", "setuptools (>=60)"] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["aiohttp (>=3.10.5)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + [[package]] name = "vine" version = "5.1.0" @@ -4673,6 +5059,20 @@ files = [ {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, ] +[[package]] +name = "wsproto" +version = "1.2.0" +description = "WebSockets state-machine based protocol implementation" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, + {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, +] + +[package.dependencies] +h11 = ">=0.9.0,<1" + [[package]] name = "yarl" version = "1.11.1" @@ -4795,24 +5195,6 @@ netaddr = "*" requests = "*" urllib3 = "*" -[[package]] -name = "zope-event" -version = "5.0" -description = "Very basic event publishing system" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"}, - {file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"}, -] - -[package.dependencies] -setuptools = "*" - -[package.extras] -docs = ["Sphinx"] -test = ["zope.testrunner"] - [[package]] name = "zope-interface" version = "7.0.3" @@ -4867,4 +5249,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "ffc7fdebf88ee475f6017c11fd65edf29f20b6f7e929cc960c8675037f7f4af9" +content-hash = "d1fcec7cfd139999f2d9db729e5e19981e8cf17f50cdbdd20552f0eba4e9f268" @@ -17,7 +17,6 @@ djangorestframework-simplejwt = "^5.2.2" djangorestframework = "^3.14.0" yookassa = "^2.4.0" environs = "^9.5.0" -gunicorn = {extras = ["gevent"], version = "^23.0.0"} dj-rest-auth = "^4.0.1" django-celery-beat = "^2.5.0" minio = "^7.1.15" @@ -48,6 +47,15 @@ django-prometheus = "^2.3.1" psycopg2-binary = "^2.9.10" filetype = "^1.2.0" sentry-sdk = {extras = ["django"], version = "^2.19.0"} +django-polymorphic = "^3.1.0" +httpx = "^0.28.1" +django-ninja = "^1.3.0" +channels = {extras = ["daphne"], version = "^4.2.0"} +uvicorn = "^0.34.0" +httptools = "^0.6.4" +uvloop = "^0.21.0" +wsproto = "^1.2.0" +channels-redis = "^4.2.1" [tool.poetry.group.test.dependencies]