mirror of
https://github.com/Balshgit/gpt_chat_bot.git
synced 2025-09-11 22:30:41 +03:00
add database and migration logic (#27)
* update chat_microservice * reformat logger_conf * add database * add service and repository logic * fix constants gpt base url * add models endpoints
This commit is contained in:
parent
c401e1006c
commit
23031b0777
41
README.md
41
README.md
@ -92,6 +92,39 @@ on local start can be found at http://localhost/gpt/api/docs
|
||||
|
||||
prod docs https://bot.mywistr.ru/gpt/api/docs/
|
||||
|
||||
|
||||
## Create migrations
|
||||
|
||||
Init alembic
|
||||
|
||||
alembic init alembic
|
||||
|
||||
|
||||
```bash
|
||||
cd bot_microservice
|
||||
alembic revision --autogenerate -m 'create_quads_table'
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
|
||||
Create table in alembic versions
|
||||
|
||||
```bash
|
||||
alembic --config ./alembic.ini revision -m "create account table"
|
||||
alembic --config ./alembic.ini revision --autogenerate -m 'create_quads_table'
|
||||
```
|
||||
|
||||
|
||||
|
||||
Run migrations
|
||||
|
||||
```bash
|
||||
cd ./bot_microservice # alembic root
|
||||
alembic --config ./alembic.ini upgrade head
|
||||
alembic --config ./alembic.ini downgrade 389018a3e0f0
|
||||
```
|
||||
|
||||
|
||||
## Help article
|
||||
|
||||
[Следить за обновлениями этого репозитория](https://github.com/fantasy-peak/cpp-freegpt-webui)
|
||||
@ -99,10 +132,12 @@ prod docs https://bot.mywistr.ru/gpt/api/docs/
|
||||
|
||||
## TODO
|
||||
|
||||
- [] add Database and models
|
||||
- [] add alembic migrations
|
||||
- [x] add Database and models
|
||||
- [x] add alembic migrations
|
||||
- [] add models priority and their rotation
|
||||
- [] add more tests
|
||||
- [x] add update model priority endpoint
|
||||
- [] add more tests for gpt model selection
|
||||
- [] add authorisation for api
|
||||
- [] reformat conftest.py file
|
||||
- [x] Add sentry
|
||||
- [x] Add graylog integration and availability to log to file
|
||||
|
52
bot_microservice/alembic.ini
Normal file
52
bot_microservice/alembic.ini
Normal file
@ -0,0 +1,52 @@
|
||||
[alembic]
|
||||
script_location = infra/database/migrations
|
||||
file_template = %%(year)d-%%(month).2d-%%(day).2d-%%(hour).2d-%%(minute).2d_%%(rev)s
|
||||
prepend_sys_path = .
|
||||
output_encoding = utf-8
|
||||
|
||||
[post_write_hooks]
|
||||
hooks = black,autoflake,isort
|
||||
|
||||
black.type = console_scripts
|
||||
black.entrypoint = black
|
||||
|
||||
autoflake.type = console_scripts
|
||||
autoflake.entrypoint = autoflake
|
||||
|
||||
isort.type = console_scripts
|
||||
isort.entrypoint = isort
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
@ -1,7 +1,17 @@
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi import APIRouter, Body, Depends, Path
|
||||
from starlette import status
|
||||
from starlette.responses import Response
|
||||
from starlette.responses import JSONResponse, Response
|
||||
from telegram import Update
|
||||
|
||||
from api.bot.serializers import (
|
||||
ChatGptModelSerializer,
|
||||
ChatGptModelsPrioritySerializer,
|
||||
GETChatGptModelsSerializer,
|
||||
LightChatGptModel,
|
||||
)
|
||||
from api.deps import get_bot_queue, get_chatgpt_service, get_update_from_request
|
||||
from core.bot.app import BotQueue
|
||||
from core.bot.services import ChatGptService
|
||||
from settings.config import settings
|
||||
|
||||
router = APIRouter()
|
||||
@ -15,5 +25,74 @@ router = APIRouter()
|
||||
summary="process bot updates",
|
||||
include_in_schema=False,
|
||||
)
|
||||
async def process_bot_updates(request: Request) -> None:
|
||||
await request.app.state.queue.put_updates_on_queue(request)
|
||||
async def process_bot_updates(
|
||||
tg_update: Update = Depends(get_update_from_request),
|
||||
queue: BotQueue = Depends(get_bot_queue),
|
||||
) -> None:
|
||||
await queue.put_updates_on_queue(tg_update)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/models",
|
||||
name="bot:models_list",
|
||||
response_class=JSONResponse,
|
||||
response_model=list[ChatGptModelSerializer],
|
||||
status_code=status.HTTP_200_OK,
|
||||
summary="list of models",
|
||||
)
|
||||
async def models_list(
|
||||
chatgpt_service: ChatGptService = Depends(get_chatgpt_service),
|
||||
) -> JSONResponse:
|
||||
"""Получить список всех моделей"""
|
||||
models = await chatgpt_service.get_chatgpt_models()
|
||||
return JSONResponse(
|
||||
content=GETChatGptModelsSerializer(data=models).model_dump(), status_code=status.HTTP_200_OK # type: ignore
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/models/{model_id}/priority",
|
||||
name="bot:change_model_priority",
|
||||
response_class=Response,
|
||||
status_code=status.HTTP_202_ACCEPTED,
|
||||
summary="change gpt model priority",
|
||||
)
|
||||
async def change_model_priority(
|
||||
model_id: int = Path(..., gt=0, description="Id модели для обновления приореитета"),
|
||||
chatgpt_service: ChatGptService = Depends(get_chatgpt_service),
|
||||
gpt_model: ChatGptModelsPrioritySerializer = Body(...),
|
||||
) -> None:
|
||||
"""Изменить приоритет модели в выдаче"""
|
||||
await chatgpt_service.change_chatgpt_model_priority(model_id=model_id, priority=gpt_model.priority)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/models",
|
||||
name="bot:add_new_model",
|
||||
response_model=ChatGptModelSerializer,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
summary="add new model",
|
||||
)
|
||||
async def add_new_model(
|
||||
chatgpt_service: ChatGptService = Depends(get_chatgpt_service),
|
||||
gpt_model: LightChatGptModel = Body(...),
|
||||
) -> JSONResponse:
|
||||
"""Добавить новую модель"""
|
||||
model = await chatgpt_service.add_chatgpt_model(gpt_model=gpt_model.model, priority=gpt_model.priority)
|
||||
|
||||
return JSONResponse(content=model, status_code=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/models/{model_id}",
|
||||
name="bot:delete_gpt_model",
|
||||
response_class=Response,
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
summary="delete gpt model",
|
||||
)
|
||||
async def delete_model(
|
||||
model_id: int = Path(..., gt=0, description="Id модели для удаления"),
|
||||
chatgpt_service: ChatGptService = Depends(get_chatgpt_service),
|
||||
) -> None:
|
||||
"""Удалить gpt модель"""
|
||||
await chatgpt_service.delete_chatgpt_model(model_id=model_id)
|
||||
|
@ -1,13 +0,0 @@
|
||||
from fastapi import Depends
|
||||
from starlette.requests import Request
|
||||
|
||||
from core.bot.services import ChatGptService
|
||||
from settings.config import AppSettings
|
||||
|
||||
|
||||
def get_settings(request: Request) -> AppSettings:
|
||||
return request.app.state.settings
|
||||
|
||||
|
||||
def get_chat_gpt_service(settings: AppSettings = Depends(get_settings)) -> ChatGptService:
|
||||
return ChatGptService(settings.GPT_MODEL)
|
24
bot_microservice/api/bot/serializers.py
Normal file
24
bot_microservice/api/bot/serializers.py
Normal file
@ -0,0 +1,24 @@
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class LightChatGptModel(BaseModel):
|
||||
model: str = Field(..., title="Chat Gpt model")
|
||||
priority: int = Field(default=0, ge=0, title="Приоритет модели")
|
||||
|
||||
|
||||
class ChatGptModelsPrioritySerializer(BaseModel):
|
||||
priority: int = Field(default=0, ge=0, title="Приоритет модели")
|
||||
|
||||
|
||||
class ChatGptModelSerializer(BaseModel):
|
||||
id: int = Field(..., gt=0, title="Id модели")
|
||||
model: str = Field(..., title="Chat Gpt model")
|
||||
priority: int = Field(..., ge=0, title="Приоритет модели")
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class GETChatGptModelsSerializer(BaseModel):
|
||||
data: list[ChatGptModelSerializer] = Field(..., title="Список всех моделей")
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
50
bot_microservice/api/deps.py
Normal file
50
bot_microservice/api/deps.py
Normal file
@ -0,0 +1,50 @@
|
||||
from fastapi import Depends
|
||||
from starlette.requests import Request
|
||||
from telegram import Update
|
||||
|
||||
from core.bot.app import BotApplication, BotQueue
|
||||
from core.bot.repository import ChatGPTRepository
|
||||
from core.bot.services import ChatGptService, SpeechToTextService
|
||||
from infra.database.db_adapter import Database
|
||||
from settings.config import AppSettings
|
||||
|
||||
|
||||
def get_settings(request: Request) -> AppSettings:
|
||||
return request.app.state.settings
|
||||
|
||||
|
||||
def get_bot_app(request: Request) -> BotApplication:
|
||||
return request.app.state.bot_app
|
||||
|
||||
|
||||
def get_bot_queue(request: Request) -> BotQueue:
|
||||
return request.app.state.queue
|
||||
|
||||
|
||||
async def get_update_from_request(request: Request, bot_app: BotApplication = Depends(get_bot_app)) -> Update | None:
|
||||
data = await request.json()
|
||||
return Update.de_json(data, bot_app.bot)
|
||||
|
||||
|
||||
def get_database(settings: AppSettings = Depends(get_settings)) -> Database:
|
||||
return Database(settings=settings)
|
||||
|
||||
|
||||
def get_chat_gpt_repository(
|
||||
db: Database = Depends(get_database), settings: AppSettings = Depends(get_settings)
|
||||
) -> ChatGPTRepository:
|
||||
return ChatGPTRepository(settings=settings, db=db)
|
||||
|
||||
|
||||
def get_speech_to_text_service() -> SpeechToTextService:
|
||||
return SpeechToTextService()
|
||||
|
||||
|
||||
def new_bot_queue(bot_app: BotApplication = Depends(get_bot_app)) -> BotQueue:
|
||||
return BotQueue(bot_app=bot_app)
|
||||
|
||||
|
||||
def get_chatgpt_service(
|
||||
chat_gpt_repository: ChatGPTRepository = Depends(get_chat_gpt_repository),
|
||||
) -> ChatGptService:
|
||||
return ChatGptService(repository=chat_gpt_repository)
|
@ -3,7 +3,7 @@ from fastapi.responses import ORJSONResponse
|
||||
from starlette import status
|
||||
from starlette.responses import Response
|
||||
|
||||
from api.bot.deps import get_chat_gpt_service
|
||||
from api.deps import get_chatgpt_service
|
||||
from api.exceptions import BaseAPIException
|
||||
from constants import INVALID_GPT_REQUEST_MESSAGES
|
||||
from core.bot.services import ChatGptService
|
||||
@ -33,12 +33,11 @@ async def healthcheck() -> ORJSONResponse:
|
||||
)
|
||||
async def gpt_healthcheck(
|
||||
response: Response,
|
||||
chatgpt_service: ChatGptService = Depends(get_chat_gpt_service),
|
||||
chatgpt_service: ChatGptService = Depends(get_chatgpt_service),
|
||||
) -> Response:
|
||||
data = chatgpt_service.build_request_data("Привет!")
|
||||
response.status_code = status.HTTP_200_OK
|
||||
try:
|
||||
chatgpt_response = await chatgpt_service.do_request(data)
|
||||
chatgpt_response = await chatgpt_service.request_to_chatgpt_microservice(question="Привет!")
|
||||
if chatgpt_response.status_code != status.HTTP_200_OK:
|
||||
response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
for message in INVALID_GPT_REQUEST_MESSAGES:
|
||||
|
@ -1,4 +1,4 @@
|
||||
from enum import StrEnum
|
||||
from enum import StrEnum, unique
|
||||
|
||||
AUDIO_SEGMENT_DURATION = 120 * 1000
|
||||
|
||||
@ -26,3 +26,35 @@ class LogLevelEnum(StrEnum):
|
||||
INFO = "info"
|
||||
DEBUG = "debug"
|
||||
NOTSET = ""
|
||||
|
||||
|
||||
@unique
|
||||
class ChatGptModelsEnum(StrEnum):
|
||||
gpt_3_5_turbo_stream_openai = "gpt-3.5-turbo-stream-openai"
|
||||
gpt_3_5_turbo_Aichat = "gpt-3.5-turbo-Aichat"
|
||||
gpt_4_ChatgptAi = "gpt-4-ChatgptAi"
|
||||
gpt_3_5_turbo_weWordle = "gpt-3.5-turbo-weWordle"
|
||||
gpt_3_5_turbo_acytoo = "gpt-3.5-turbo-acytoo"
|
||||
gpt_3_5_turbo_stream_DeepAi = "gpt-3.5-turbo-stream-DeepAi"
|
||||
gpt_3_5_turbo_stream_H2o = "gpt-3.5-turbo-stream-H2o"
|
||||
gpt_3_5_turbo_stream_yqcloud = "gpt-3.5-turbo-stream-yqcloud"
|
||||
gpt_OpenAssistant_stream_HuggingChat = "gpt-OpenAssistant-stream-HuggingChat"
|
||||
gpt_4_turbo_stream_you = "gpt-4-turbo-stream-you"
|
||||
gpt_3_5_turbo_AItianhu = "gpt-3.5-turbo-AItianhu"
|
||||
gpt_3_stream_binjie = "gpt-3-stream-binjie"
|
||||
gpt_3_5_turbo_stream_CodeLinkAva = "gpt-3.5-turbo-stream-CodeLinkAva"
|
||||
gpt_4_stream_ChatBase = "gpt-4-stream-ChatBase"
|
||||
gpt_3_5_turbo_stream_aivvm = "gpt-3.5-turbo-stream-aivvm"
|
||||
gpt_3_5_turbo_16k_stream_Ylokh = "gpt-3.5-turbo-16k-stream-Ylokh"
|
||||
gpt_3_5_turbo_stream_Vitalentum = "gpt-3.5-turbo-stream-Vitalentum"
|
||||
gpt_3_5_turbo_stream_GptGo = "gpt-3.5-turbo-stream-GptGo"
|
||||
gpt_3_5_turbo_stream_AItianhuSpace = "gpt-3.5-turbo-stream-AItianhuSpace"
|
||||
gpt_3_5_turbo_stream_Aibn = "gpt-3.5-turbo-stream-Aibn"
|
||||
gpt_3_5_turbo_ChatgptDuo = "gpt-3.5-turbo-ChatgptDuo"
|
||||
gpt_3_5_turbo_stream_FreeGpt = "gpt-3.5-turbo-stream-FreeGpt"
|
||||
gpt_3_5_turbo_stream_ChatForAi = "gpt-3.5-turbo-stream-ChatForAi"
|
||||
gpt_3_5_turbo_stream_Cromicle = "gpt-3.5-turbo-stream-Cromicle"
|
||||
|
||||
@classmethod
|
||||
def values(cls) -> set[str]:
|
||||
return set(map(str, set(ChatGptModelsEnum)))
|
||||
|
@ -6,7 +6,7 @@ from functools import cached_property
|
||||
from http import HTTPStatus
|
||||
from typing import Any
|
||||
|
||||
from fastapi import Request, Response
|
||||
from fastapi import Response
|
||||
from loguru import logger
|
||||
from telegram import Bot, Update
|
||||
from telegram.ext import Application
|
||||
@ -68,14 +68,11 @@ class BotQueue:
|
||||
bot_app: BotApplication
|
||||
queue: Queue = asyncio.Queue() # type: ignore[type-arg]
|
||||
|
||||
async def put_updates_on_queue(self, request: Request) -> Response:
|
||||
async def put_updates_on_queue(self, tg_update: Update) -> Response:
|
||||
"""
|
||||
Listen /{URL_PREFIX}/{API_PREFIX}/{TELEGRAM_WEB_TOKEN} path and proxy post request to bot
|
||||
"""
|
||||
data = await request.json()
|
||||
tg_update = Update.de_json(data=data, bot=self.bot_app.application.bot)
|
||||
self.queue.put_nowait(tg_update)
|
||||
|
||||
return Response(status_code=HTTPStatus.ACCEPTED)
|
||||
|
||||
async def get_updates_from_queue(self) -> None:
|
||||
|
@ -67,7 +67,7 @@ async def ask_question(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
|
||||
|
||||
await update.message.reply_text("Пожалуйста подождите, ответ в среднем занимает 10-15 секунд")
|
||||
|
||||
chat_gpt_service = ChatGptService(chat_gpt_model=settings.GPT_MODEL)
|
||||
chat_gpt_service = ChatGptService.build()
|
||||
logger.warning("question asked", user=update.message.from_user, question=update.message.text)
|
||||
answer = await chat_gpt_service.request_to_chatgpt(question=update.message.text)
|
||||
await update.message.reply_text(answer)
|
||||
@ -87,9 +87,9 @@ async def voice_recognize(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
||||
|
||||
logger.info("file has been saved", filename=tmpfile.name)
|
||||
|
||||
speech_to_text_service = SpeechToTextService(filename=tmpfile.name)
|
||||
speech_to_text_service = SpeechToTextService()
|
||||
|
||||
speech_to_text_service.get_text_from_audio()
|
||||
speech_to_text_service.get_text_from_audio(filename=tmpfile.name)
|
||||
|
||||
part = 0
|
||||
while speech_to_text_service.text_parts or not speech_to_text_service.text_recognised:
|
||||
|
0
bot_microservice/core/bot/models/__init__.py
Normal file
0
bot_microservice/core/bot/models/__init__.py
Normal file
12
bot_microservice/core/bot/models/chat_gpt.py
Normal file
12
bot_microservice/core/bot/models/chat_gpt.py
Normal file
@ -0,0 +1,12 @@
|
||||
from sqlalchemy import INTEGER, SMALLINT, VARCHAR
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from infra.database.base import Base
|
||||
|
||||
__slots__ = ("ChatGpt",)
|
||||
|
||||
|
||||
class ChatGpt(Base):
|
||||
id: Mapped[int] = mapped_column("id", INTEGER(), primary_key=True, autoincrement=True)
|
||||
model: Mapped[str] = mapped_column("model", VARCHAR(length=256), nullable=False, unique=True)
|
||||
priority: Mapped[int] = mapped_column("priority", SMALLINT(), default=0)
|
106
bot_microservice/core/bot/repository.py
Normal file
106
bot_microservice/core/bot/repository.py
Normal file
@ -0,0 +1,106 @@
|
||||
import random
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Sequence
|
||||
from uuid import uuid4
|
||||
|
||||
import httpx
|
||||
from httpx import AsyncClient, AsyncHTTPTransport, Response
|
||||
from loguru import logger
|
||||
from sqlalchemy import delete, desc, select, update
|
||||
from sqlalchemy.dialects.sqlite import insert
|
||||
|
||||
from constants import CHAT_GPT_BASE_URI, INVALID_GPT_REQUEST_MESSAGES
|
||||
from core.bot.models.chat_gpt import ChatGpt
|
||||
from infra.database.db_adapter import Database
|
||||
from settings.config import AppSettings
|
||||
|
||||
|
||||
@dataclass
|
||||
class ChatGPTRepository:
|
||||
settings: AppSettings
|
||||
db: Database
|
||||
|
||||
async def get_chatgpt_models(self) -> Sequence[ChatGpt]:
|
||||
query = select(ChatGpt).order_by(desc(ChatGpt.priority))
|
||||
|
||||
async with self.db.session() as session:
|
||||
result = await session.execute(query)
|
||||
return result.scalars().all()
|
||||
|
||||
async def change_chatgpt_model_priority(self, model_id: int, priority: int) -> None:
|
||||
current_model = await self.get_current_chatgpt_model()
|
||||
|
||||
reset_priority_query = update(ChatGpt).values(priority=0).filter(ChatGpt.model == current_model)
|
||||
set_new_priority_query = update(ChatGpt).values(priority=priority).filter(ChatGpt.model == model_id)
|
||||
|
||||
async with self.db.get_transaction_session() as session:
|
||||
await session.execute(reset_priority_query)
|
||||
await session.execute(set_new_priority_query)
|
||||
|
||||
async def add_chatgpt_model(self, model: str, priority: int) -> dict[str, str | int]:
|
||||
query = (
|
||||
insert(ChatGpt)
|
||||
.values(
|
||||
{ChatGpt.model: model, ChatGpt.priority: priority},
|
||||
)
|
||||
.prefix_with("OR IGNORE")
|
||||
)
|
||||
async with self.db.session() as session:
|
||||
await session.execute(query)
|
||||
await session.commit()
|
||||
return {"model": model, "priority": priority}
|
||||
|
||||
async def delete_chatgpt_model(self, model_id: int) -> None:
|
||||
query = delete(ChatGpt).filter_by(id=model_id)
|
||||
|
||||
async with self.db.session() as session:
|
||||
await session.execute(query)
|
||||
|
||||
async def get_current_chatgpt_model(self) -> str:
|
||||
query = select(ChatGpt.model).order_by(desc(ChatGpt.priority)).limit(1)
|
||||
|
||||
async with self.db.session() as session:
|
||||
result = await session.execute(query)
|
||||
return result.scalar_one()
|
||||
|
||||
async def ask_question(self, question: str, chat_gpt_model: str) -> str:
|
||||
try:
|
||||
response = await self.request_to_chatgpt_microservice(question=question, chat_gpt_model=chat_gpt_model)
|
||||
status = response.status_code
|
||||
for message in INVALID_GPT_REQUEST_MESSAGES:
|
||||
if message in response.text:
|
||||
message = f"{message}: {chat_gpt_model}"
|
||||
logger.info(message, question=question, chat_gpt_model=chat_gpt_model)
|
||||
return message
|
||||
if status != httpx.codes.OK:
|
||||
logger.info(f"got response status: {status} from chat api", response.text)
|
||||
return "Что-то пошло не так, попробуйте еще раз или обратитесь к администратору"
|
||||
return response.text
|
||||
except Exception as error:
|
||||
logger.error("error get data from chat api", error=error)
|
||||
return "Вообще всё сломалось :("
|
||||
|
||||
async def request_to_chatgpt_microservice(self, question: str, chat_gpt_model: str) -> Response:
|
||||
data = self._build_request_data(question=question, chat_gpt_model=chat_gpt_model)
|
||||
|
||||
transport = AsyncHTTPTransport(retries=3)
|
||||
async with AsyncClient(base_url=self.settings.GPT_BASE_HOST, transport=transport, timeout=50) as client:
|
||||
return await client.post(CHAT_GPT_BASE_URI, json=data, timeout=50)
|
||||
|
||||
@staticmethod
|
||||
def _build_request_data(*, question: str, chat_gpt_model: str) -> dict[str, Any]:
|
||||
return {
|
||||
"conversation_id": str(uuid4()),
|
||||
"action": "_ask",
|
||||
"model": chat_gpt_model,
|
||||
"jailbreak": "default",
|
||||
"meta": {
|
||||
"id": random.randint(10**18, 10**19 - 1), # noqa: S311
|
||||
"content": {
|
||||
"conversation": [],
|
||||
"internet_access": False,
|
||||
"content_type": "text",
|
||||
"parts": [{"content": question, "role": "user"}],
|
||||
},
|
||||
},
|
||||
}
|
@ -1,12 +1,10 @@
|
||||
import os
|
||||
import random
|
||||
import subprocess # noqa
|
||||
from concurrent.futures.thread import ThreadPoolExecutor
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Sequence
|
||||
|
||||
import httpx
|
||||
from httpx import AsyncClient, AsyncHTTPTransport, Response
|
||||
from httpx import Response
|
||||
from loguru import logger
|
||||
from pydub import AudioSegment
|
||||
from speech_recognition import (
|
||||
@ -15,33 +13,30 @@ from speech_recognition import (
|
||||
UnknownValueError as SpeechRecognizerError,
|
||||
)
|
||||
|
||||
from constants import (
|
||||
AUDIO_SEGMENT_DURATION,
|
||||
CHAT_GPT_BASE_URI,
|
||||
INVALID_GPT_REQUEST_MESSAGES,
|
||||
)
|
||||
from constants import AUDIO_SEGMENT_DURATION
|
||||
from core.bot.models.chat_gpt import ChatGpt
|
||||
from core.bot.repository import ChatGPTRepository
|
||||
from infra.database.db_adapter import Database
|
||||
from settings.config import settings
|
||||
|
||||
|
||||
class SpeechToTextService:
|
||||
def __init__(self, filename: str) -> None:
|
||||
def __init__(self) -> None:
|
||||
self.executor = ThreadPoolExecutor()
|
||||
|
||||
self.filename = filename
|
||||
self.recognizer = Recognizer()
|
||||
self.recognizer.energy_threshold = 50
|
||||
self.text_parts: dict[int, str] = {}
|
||||
self.text_recognised = False
|
||||
|
||||
def get_text_from_audio(self) -> None:
|
||||
self.executor.submit(self.worker)
|
||||
def get_text_from_audio(self, filename: str) -> None:
|
||||
self.executor.submit(self.worker, filename=filename)
|
||||
|
||||
def worker(self) -> Any:
|
||||
self._convert_file_to_wav()
|
||||
self._convert_audio_to_text()
|
||||
def worker(self, filename: str) -> Any:
|
||||
self._convert_file_to_wav(filename)
|
||||
self._convert_audio_to_text(filename)
|
||||
|
||||
def _convert_audio_to_text(self) -> None:
|
||||
wav_filename = f"{self.filename}.wav"
|
||||
def _convert_audio_to_text(self, filename: str) -> None:
|
||||
wav_filename = f"{filename}.wav"
|
||||
|
||||
speech = AudioSegment.from_wav(wav_filename)
|
||||
speech_duration = len(speech)
|
||||
@ -63,18 +58,19 @@ class SpeechToTextService:
|
||||
# clean temp voice message main files
|
||||
try:
|
||||
os.remove(wav_filename)
|
||||
os.remove(self.filename)
|
||||
os.remove(filename)
|
||||
except FileNotFoundError as error:
|
||||
logger.error("error temps files not deleted", error=error, filenames=[self.filename, self.filename])
|
||||
logger.error("error temps files not deleted", error=error, filenames=[filename, wav_filename])
|
||||
|
||||
def _convert_file_to_wav(self) -> None:
|
||||
new_filename = self.filename + ".wav"
|
||||
cmd = ["ffmpeg", "-loglevel", "quiet", "-i", self.filename, "-vn", new_filename]
|
||||
@staticmethod
|
||||
def _convert_file_to_wav(filename: str) -> None:
|
||||
new_filename = filename + ".wav"
|
||||
cmd = ["ffmpeg", "-loglevel", "quiet", "-i", filename, "-vn", new_filename]
|
||||
try:
|
||||
subprocess.run(args=cmd) # noqa: S603
|
||||
logger.info("file has been converted to wav", filename=new_filename)
|
||||
except Exception as error:
|
||||
logger.error("cant convert voice", error=error, filename=self.filename)
|
||||
logger.error("cant convert voice", error=error, filename=filename)
|
||||
|
||||
def _recognize_by_google(self, filename: str, sound_segment: AudioSegment) -> str:
|
||||
tmp_filename = f"{filename}_tmp_part"
|
||||
@ -91,48 +87,36 @@ class SpeechToTextService:
|
||||
raise error
|
||||
|
||||
|
||||
@dataclass
|
||||
class ChatGptService:
|
||||
def __init__(self, chat_gpt_model: str) -> None:
|
||||
self.chat_gpt_model = chat_gpt_model
|
||||
repository: ChatGPTRepository
|
||||
|
||||
async def get_chatgpt_models(self) -> Sequence[ChatGpt]:
|
||||
return await self.repository.get_chatgpt_models()
|
||||
|
||||
async def request_to_chatgpt(self, question: str | None) -> str:
|
||||
question = question or "Привет!"
|
||||
chat_gpt_request = self.build_request_data(question)
|
||||
try:
|
||||
response = await self.do_request(chat_gpt_request)
|
||||
status = response.status_code
|
||||
for message in INVALID_GPT_REQUEST_MESSAGES:
|
||||
if message in response.text:
|
||||
message = f"{message}: {settings.GPT_MODEL}"
|
||||
logger.info(message, data=chat_gpt_request)
|
||||
return message
|
||||
if status != httpx.codes.OK:
|
||||
logger.info(f"got response status: {status} from chat api", data=chat_gpt_request)
|
||||
return "Что-то пошло не так, попробуйте еще раз или обратитесь к администратору"
|
||||
return response.text
|
||||
except Exception as error:
|
||||
logger.error("error get data from chat api", error=error)
|
||||
return "Вообще всё сломалось :("
|
||||
chat_gpt_model = await self.get_current_chatgpt_model()
|
||||
return await self.repository.ask_question(question=question, chat_gpt_model=chat_gpt_model)
|
||||
|
||||
@staticmethod
|
||||
async def do_request(data: dict[str, Any]) -> Response:
|
||||
transport = AsyncHTTPTransport(retries=3)
|
||||
async with AsyncClient(base_url=settings.GPT_BASE_HOST, transport=transport, timeout=50) as client:
|
||||
return await client.post(CHAT_GPT_BASE_URI, json=data, timeout=50)
|
||||
async def request_to_chatgpt_microservice(self, question: str) -> Response:
|
||||
chat_gpt_model = await self.get_current_chatgpt_model()
|
||||
return await self.repository.request_to_chatgpt_microservice(question=question, chat_gpt_model=chat_gpt_model)
|
||||
|
||||
def build_request_data(self, question: str) -> dict[str, Any]:
|
||||
return {
|
||||
"conversation_id": str(uuid4()),
|
||||
"action": "_ask",
|
||||
"model": self.chat_gpt_model,
|
||||
"jailbreak": "default",
|
||||
"meta": {
|
||||
"id": random.randint(10**18, 10**19 - 1), # noqa: S311
|
||||
"content": {
|
||||
"conversation": [],
|
||||
"internet_access": False,
|
||||
"content_type": "text",
|
||||
"parts": [{"content": question, "role": "user"}],
|
||||
},
|
||||
},
|
||||
}
|
||||
async def get_current_chatgpt_model(self) -> str:
|
||||
return await self.repository.get_current_chatgpt_model()
|
||||
|
||||
async def change_chatgpt_model_priority(self, model_id: int, priority: int) -> None:
|
||||
return await self.repository.change_chatgpt_model_priority(model_id=model_id, priority=priority)
|
||||
|
||||
async def add_chatgpt_model(self, gpt_model: str, priority: int) -> dict[str, str | int]:
|
||||
return await self.repository.add_chatgpt_model(model=gpt_model, priority=priority)
|
||||
|
||||
async def delete_chatgpt_model(self, model_id: int) -> None:
|
||||
return await self.repository.delete_chatgpt_model(model_id=model_id)
|
||||
|
||||
@classmethod
|
||||
def build(cls) -> "ChatGptService":
|
||||
db = Database(settings=settings)
|
||||
repository = ChatGPTRepository(settings=settings, db=db)
|
||||
return ChatGptService(repository=repository)
|
||||
|
73
bot_microservice/core/lifetime.py
Normal file
73
bot_microservice/core/lifetime.py
Normal file
@ -0,0 +1,73 @@
|
||||
from asyncio import current_task
|
||||
from typing import Awaitable, Callable
|
||||
|
||||
from fastapi import FastAPI
|
||||
from sqlalchemy.ext.asyncio import (
|
||||
AsyncSession,
|
||||
async_scoped_session,
|
||||
async_sessionmaker,
|
||||
create_async_engine,
|
||||
)
|
||||
|
||||
from settings.config import AppSettings
|
||||
|
||||
|
||||
def startup(app: FastAPI, settings: AppSettings) -> Callable[[], Awaitable[None]]:
|
||||
"""
|
||||
Actions to run on application startup.
|
||||
|
||||
This function use fastAPI app to store data,
|
||||
such as db_engine.
|
||||
|
||||
:param app: the fastAPI application.
|
||||
:param settings: app settings
|
||||
:return: function that actually performs actions.
|
||||
|
||||
"""
|
||||
|
||||
async def _startup() -> None:
|
||||
_setup_db(app, settings)
|
||||
|
||||
return _startup
|
||||
|
||||
|
||||
def shutdown(app: FastAPI) -> Callable[[], Awaitable[None]]:
|
||||
"""
|
||||
Actions to run on application's shutdown.
|
||||
|
||||
:param app: fastAPI application.
|
||||
:return: function that actually performs actions.
|
||||
|
||||
"""
|
||||
|
||||
async def _shutdown() -> None:
|
||||
await app.state.db_engine.dispose()
|
||||
|
||||
return _shutdown
|
||||
|
||||
|
||||
def _setup_db(app: FastAPI, settings: AppSettings) -> None:
|
||||
"""
|
||||
Create connection to the database.
|
||||
|
||||
This function creates SQLAlchemy engine instance,
|
||||
session_factory for creating sessions
|
||||
and stores them in the application's state property.
|
||||
|
||||
:param app: fastAPI application.
|
||||
"""
|
||||
engine = create_async_engine(
|
||||
str(settings.db_url),
|
||||
echo=settings.DB_ECHO,
|
||||
execution_options={"isolation_level": "AUTOCOMMIT"},
|
||||
)
|
||||
session_factory = async_scoped_session(
|
||||
async_sessionmaker(
|
||||
engine,
|
||||
expire_on_commit=False,
|
||||
class_=AsyncSession,
|
||||
),
|
||||
scopefunc=current_task,
|
||||
)
|
||||
app.state.db_engine = engine
|
||||
app.state.db_session_factory = session_factory
|
@ -1,5 +1,6 @@
|
||||
from datetime import datetime, timedelta
|
||||
from functools import lru_cache, wraps
|
||||
from inspect import cleandoc
|
||||
from typing import Any
|
||||
|
||||
|
||||
@ -22,3 +23,9 @@ def timed_cache(**timedelta_kwargs: Any) -> Any:
|
||||
return _wrapped
|
||||
|
||||
return _wrapper
|
||||
|
||||
|
||||
def clean_doc(cls: Any) -> str | None:
|
||||
if cls.__doc__ is None:
|
||||
return None
|
||||
return cleandoc(cls.__doc__)
|
||||
|
0
bot_microservice/infra/database/__init__.py
Normal file
0
bot_microservice/infra/database/__init__.py
Normal file
34
bot_microservice/infra/database/base.py
Normal file
34
bot_microservice/infra/database/base.py
Normal file
@ -0,0 +1,34 @@
|
||||
from sqlalchemy import Table, inspect
|
||||
from sqlalchemy.orm import as_declarative, declared_attr
|
||||
|
||||
from infra.database.meta import meta
|
||||
|
||||
|
||||
@as_declarative(metadata=meta)
|
||||
class Base:
|
||||
"""
|
||||
Base for all models.
|
||||
|
||||
It has some type definitions to
|
||||
enhance autocompletion.
|
||||
"""
|
||||
|
||||
# Generate __tablename__ automatically
|
||||
@declared_attr
|
||||
def __tablename__(self) -> str:
|
||||
return self.__name__.lower()
|
||||
|
||||
__table__: Table
|
||||
|
||||
@classmethod
|
||||
def get_real_column_name(cls, attr_name: str) -> str:
|
||||
return getattr(inspect(cls).c, attr_name).name # type: ignore
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
try:
|
||||
return f"{self.__class__.__name__}(id={self.id})" # type: ignore[attr-defined]
|
||||
except AttributeError:
|
||||
return super().__repr__()
|
102
bot_microservice/infra/database/db_adapter.py
Normal file
102
bot_microservice/infra/database/db_adapter.py
Normal file
@ -0,0 +1,102 @@
|
||||
import os
|
||||
import pkgutil
|
||||
from asyncio import current_task
|
||||
from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
from typing import AsyncGenerator
|
||||
|
||||
from loguru import logger
|
||||
from sqlalchemy.ext.asyncio import (
|
||||
AsyncEngine,
|
||||
AsyncSession,
|
||||
async_scoped_session,
|
||||
async_sessionmaker,
|
||||
create_async_engine,
|
||||
)
|
||||
|
||||
from settings.config import AppSettings
|
||||
|
||||
|
||||
class Database:
|
||||
def __init__(self, settings: AppSettings) -> None:
|
||||
self.db_connect_url = settings.db_url
|
||||
self.echo_logs = settings.DB_ECHO
|
||||
self.db_file = settings.DB_FILE
|
||||
self._engine: AsyncEngine = create_async_engine(
|
||||
str(settings.db_url),
|
||||
echo=settings.DB_ECHO,
|
||||
execution_options={"isolation_level": "AUTOCOMMIT"},
|
||||
)
|
||||
self._async_session_factory = async_scoped_session(
|
||||
async_sessionmaker(
|
||||
autoflush=False,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False,
|
||||
bind=self._engine,
|
||||
),
|
||||
scopefunc=current_task,
|
||||
)
|
||||
|
||||
@asynccontextmanager
|
||||
async def session(self) -> AsyncGenerator[AsyncSession, None]:
|
||||
session: AsyncSession = self._async_session_factory()
|
||||
|
||||
async with session:
|
||||
try:
|
||||
yield session
|
||||
except Exception:
|
||||
await session.rollback()
|
||||
raise
|
||||
|
||||
@asynccontextmanager
|
||||
async def get_transaction_session(self) -> AsyncGenerator[AsyncSession, None]:
|
||||
async with self._async_session_factory() as session, session.begin():
|
||||
try:
|
||||
yield session
|
||||
except Exception as error:
|
||||
await session.rollback()
|
||||
raise error
|
||||
|
||||
async def create_database(self) -> None:
|
||||
"""
|
||||
Create a test database.
|
||||
|
||||
:param engine: Async engine for database creation
|
||||
:param db_path: path to sqlite file
|
||||
|
||||
"""
|
||||
if not self.db_file.exists():
|
||||
from infra.database.meta import meta
|
||||
|
||||
load_all_models()
|
||||
try:
|
||||
async with self._engine.begin() as connection:
|
||||
await connection.run_sync(meta.create_all)
|
||||
|
||||
logger.info("all migrations are applied")
|
||||
except Exception as err:
|
||||
logger.error("Cant run migrations", err=err)
|
||||
|
||||
async def drop_database(self) -> None:
|
||||
"""
|
||||
Drop current database.
|
||||
|
||||
:param path: Delete sqlite database file
|
||||
|
||||
"""
|
||||
if self.db_file.exists():
|
||||
os.remove(self.db_file)
|
||||
|
||||
|
||||
def load_all_models() -> None:
|
||||
"""Load all models from this folder."""
|
||||
package_dir = Path(__file__).resolve().parent.parent
|
||||
package_dir = package_dir.joinpath("core")
|
||||
modules = pkgutil.walk_packages(path=[str(package_dir)], prefix="core.")
|
||||
models_packages = [module for module in modules if module.ispkg and "models" in module.name]
|
||||
for module in models_packages:
|
||||
model_pkgs = pkgutil.walk_packages(
|
||||
path=[os.path.join(str(module.module_finder.path), "models")], prefix=f"{module.name}." # type: ignore
|
||||
)
|
||||
for model_pkg in model_pkgs:
|
||||
__import__(model_pkg.name)
|
20
bot_microservice/infra/database/deps.py
Normal file
20
bot_microservice/infra/database/deps.py
Normal file
@ -0,0 +1,20 @@
|
||||
from typing import AsyncGenerator
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from starlette.requests import Request
|
||||
|
||||
|
||||
async def get_db_session(request: Request) -> AsyncGenerator[AsyncSession, None]:
|
||||
"""
|
||||
Create and get database session.
|
||||
|
||||
:param request: current request.
|
||||
:yield: database session.
|
||||
"""
|
||||
session: AsyncSession = request.app.state.db_session_factory()
|
||||
|
||||
try:
|
||||
yield session
|
||||
finally:
|
||||
await session.commit()
|
||||
await session.close()
|
3
bot_microservice/infra/database/meta.py
Normal file
3
bot_microservice/infra/database/meta.py
Normal file
@ -0,0 +1,3 @@
|
||||
from sqlalchemy import MetaData
|
||||
|
||||
meta = MetaData()
|
1
bot_microservice/infra/database/migrations/__init__.py
Normal file
1
bot_microservice/infra/database/migrations/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Alembic migraions."""
|
81
bot_microservice/infra/database/migrations/env.py
Normal file
81
bot_microservice/infra/database/migrations/env.py
Normal file
@ -0,0 +1,81 @@
|
||||
import asyncio
|
||||
from logging.config import fileConfig
|
||||
|
||||
from alembic import context
|
||||
from sqlalchemy.ext.asyncio.engine import create_async_engine
|
||||
from sqlalchemy.future import Connection
|
||||
|
||||
from infra.database.db_adapter import load_all_models
|
||||
from infra.database.meta import meta
|
||||
from settings.config import settings
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# for 'autogenerate' support from myapp import mymodel
|
||||
load_all_models()
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
if config.config_file_name is not None:
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
# add your model's MetaData object here
|
||||
target_metadata = meta
|
||||
|
||||
|
||||
async def run_migrations_offline() -> None:
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
context.configure(
|
||||
url=str(settings.db_url),
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def do_run_migrations(connection: Connection) -> None:
|
||||
"""
|
||||
Run actual sync migrations.
|
||||
|
||||
:param connection: connection to the database.
|
||||
|
||||
"""
|
||||
context.configure(connection=connection, target_metadata=target_metadata)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
async def run_migrations_online() -> None:
|
||||
"""
|
||||
Run migrations in 'online' mode.
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
connectable = create_async_engine(str(settings.db_url))
|
||||
|
||||
async with connectable.connect() as connection:
|
||||
await connection.run_sync(do_run_migrations)
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
asyncio.run(run_migrations_offline())
|
||||
else:
|
||||
asyncio.run(run_migrations_online())
|
24
bot_microservice/infra/database/migrations/script.py.mako
Normal file
24
bot_microservice/infra/database/migrations/script.py.mako
Normal file
@ -0,0 +1,24 @@
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
branch_labels = ${repr(branch_labels)}
|
||||
depends_on = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
${downgrades if downgrades else "pass"}
|
@ -0,0 +1,34 @@
|
||||
"""initial commit
|
||||
|
||||
Revision ID: eb78565abec7
|
||||
Revises:
|
||||
Create Date: 2023-10-05 18:28:30.915361
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "eb78565abec7"
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"chatgpt",
|
||||
sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False),
|
||||
sa.Column("model", sa.VARCHAR(length=256), nullable=False),
|
||||
sa.Column("priority", sa.SMALLINT(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("model"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table("chatgpt")
|
||||
# ### end Alembic commands ###
|
@ -0,0 +1,49 @@
|
||||
"""create chat gpt models
|
||||
|
||||
Revision ID: c2e443941930
|
||||
Revises: eb78565abec7
|
||||
Create Date: 2025-10-05 20:44:05.414977
|
||||
|
||||
"""
|
||||
|
||||
from sqlalchemy import create_engine, select
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from constants import ChatGptModelsEnum
|
||||
from core.bot.models.chat_gpt import ChatGpt
|
||||
from settings.config import settings
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "c2e443941930"
|
||||
down_revision = "eb78565abec7"
|
||||
branch_labels: str | None = None
|
||||
depends_on: str | None = None
|
||||
|
||||
engine = create_engine(str(settings.db_url), echo=settings.DB_ECHO)
|
||||
session_factory = sessionmaker(engine)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
with session_factory() as session:
|
||||
query = select(ChatGpt)
|
||||
results = session.execute(query)
|
||||
models = results.scalars().all()
|
||||
|
||||
if models:
|
||||
return None
|
||||
models = []
|
||||
for model in ChatGptModelsEnum:
|
||||
priority = 0 if model != "gpt-3.5-turbo-stream-FreeGpt" else 1
|
||||
fields = {"model": model, "priority": priority}
|
||||
models.append(ChatGpt(**fields))
|
||||
session.add_all(models)
|
||||
session.commit()
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
with session_factory() as session:
|
||||
session.execute(f"""TRUNCATE TABLE {ChatGpt.__tablename__}""")
|
||||
session.commit()
|
||||
|
||||
|
||||
engine.dispose()
|
@ -17,6 +17,56 @@ else:
|
||||
Record = dict[str, Any]
|
||||
|
||||
|
||||
class Formatter:
|
||||
@staticmethod
|
||||
def json_formatter(record: Record) -> str:
|
||||
# Обрезаем `\n` в конце логов, т.к. в json формате переносы не нужны
|
||||
return Formatter.scrap_sensitive_info(record.get("message", "").strip())
|
||||
|
||||
@staticmethod
|
||||
def sentry_formatter(record: Record) -> str:
|
||||
if message := record.get("message", ""):
|
||||
record["message"] = Formatter.scrap_sensitive_info(message)
|
||||
return "{name}:{function} {message}"
|
||||
|
||||
@staticmethod
|
||||
def text_formatter(record: Record) -> str:
|
||||
# WARNING !!!
|
||||
# Функция должна возвращать строку, которая содержит только шаблоны для форматирования.
|
||||
# Если в строку прокидывать значения из record (или еще откуда-либо),
|
||||
# то loguru может принять их за f-строки и попытается обработать, что приведет к ошибке.
|
||||
# Например, если нужно достать какое-то значение из поля extra, вместо того чтобы прокидывать его в строку
|
||||
# формата, нужно прокидывать подстроку вида {extra[тут_ключ]}
|
||||
|
||||
if message := record.get("message", ""):
|
||||
record["message"] = Formatter.scrap_sensitive_info(message)
|
||||
|
||||
# Стандартный формат loguru. Задается через env LOGURU_FORMAT
|
||||
format_ = (
|
||||
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
|
||||
"<level>{level: <8}</level> | "
|
||||
"<magenta>{name}</magenta>:<magenta>{function}</magenta>:<magenta>{line}</magenta> - "
|
||||
"<level>{message}</level>"
|
||||
)
|
||||
|
||||
# Добавляем мета параметры по типу user_id, art_id, которые передаются через logger.bind(...)
|
||||
extra = record["extra"]
|
||||
if extra:
|
||||
formatted = ", ".join(f"{key}" + "={extra[" + str(key) + "]}" for key, value in extra.items())
|
||||
format_ += f" - <cyan>{formatted}</cyan>"
|
||||
|
||||
format_ += "\n"
|
||||
|
||||
if record["exception"] is not None:
|
||||
format_ += "{exception}\n"
|
||||
|
||||
return format_
|
||||
|
||||
@staticmethod
|
||||
def scrap_sensitive_info(message: str) -> str:
|
||||
return message.replace(settings.TELEGRAM_API_TOKEN, "TELEGRAM_API_TOKEN".center(24, "*"))
|
||||
|
||||
|
||||
class InterceptHandler(logging.Handler):
|
||||
def emit(self, record: logging.LogRecord) -> None:
|
||||
# Get corresponding Loguru level if it exists
|
||||
@ -31,13 +81,9 @@ class InterceptHandler(logging.Handler):
|
||||
frame = cast(FrameType, frame.f_back)
|
||||
depth += 1
|
||||
|
||||
logger.opt(depth=depth, exception=record.exc_info).log(level, self._scrap_sensitive_info(record))
|
||||
|
||||
@staticmethod
|
||||
def _scrap_sensitive_info(record: logging.LogRecord) -> str:
|
||||
message = record.getMessage()
|
||||
message.replace(settings.TELEGRAM_API_TOKEN, "TELEGRAM_API_TOKEN".center(24, "*"))
|
||||
return message
|
||||
logger.opt(depth=depth, exception=record.exc_info).log(
|
||||
level, Formatter.scrap_sensitive_info(record.getMessage())
|
||||
)
|
||||
|
||||
|
||||
def configure_logging(
|
||||
@ -45,7 +91,7 @@ def configure_logging(
|
||||
) -> None:
|
||||
intercept_handler = InterceptHandler()
|
||||
|
||||
formatter = _json_formatter if enable_json_logs else _text_formatter
|
||||
formatter = Formatter.json_formatter if enable_json_logs else Formatter.text_formatter
|
||||
|
||||
base_config_handlers = [intercept_handler]
|
||||
|
||||
@ -64,10 +110,10 @@ def configure_logging(
|
||||
base_config_handlers.append(graylog_handler)
|
||||
loguru_handlers.append({**base_loguru_handler, "sink": graylog_handler})
|
||||
if log_to_file:
|
||||
file_path = os.path.join(DIR_LOGS, log_to_file)
|
||||
file_path = DIR_LOGS / log_to_file
|
||||
if not os.path.exists(log_to_file):
|
||||
with open(file_path, 'w') as f:
|
||||
f.write('')
|
||||
with open(file_path, "w") as f:
|
||||
f.write("")
|
||||
loguru_handlers.append({**base_loguru_handler, "sink": file_path})
|
||||
|
||||
logging.basicConfig(handlers=base_config_handlers, level=level.name)
|
||||
@ -78,42 +124,4 @@ def configure_logging(
|
||||
# https://forum.sentry.io/t/changing-issue-title-when-logging-with-traceback/446
|
||||
if enable_sentry_logs:
|
||||
handler = EventHandler(level=logging.WARNING)
|
||||
logger.add(handler, diagnose=True, level=logging.WARNING, format=_sentry_formatter)
|
||||
|
||||
|
||||
def _json_formatter(record: Record) -> str:
|
||||
# Обрезаем `\n` в конце логов, т.к. в json формате переносы не нужны
|
||||
return record.get("message", "").strip()
|
||||
|
||||
|
||||
def _sentry_formatter(record: Record) -> str:
|
||||
return "{name}:{function} {message}"
|
||||
|
||||
|
||||
def _text_formatter(record: Record) -> str:
|
||||
# WARNING !!!
|
||||
# Функция должна возвращать строку, которая содержит только шаблоны для форматирования.
|
||||
# Если в строку прокидывать значения из record (или еще откуда-либо),
|
||||
# то loguru может принять их за f-строки и попытается обработать, что приведет к ошибке.
|
||||
# Например, если нужно достать какое-то значение из поля extra, вместо того чтобы прокидывать его в строку формата,
|
||||
# нужно прокидывать подстроку вида {extra[тут_ключ]}
|
||||
|
||||
# Стандартный формат loguru. Задается через env LOGURU_FORMAT
|
||||
format_ = (
|
||||
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
|
||||
"<level>{level: <8}</level> | "
|
||||
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>"
|
||||
)
|
||||
|
||||
# Добавляем мета параметры по типу user_id, art_id, которые передаются через logger.bind(...)
|
||||
extra = record["extra"]
|
||||
if extra:
|
||||
formatted = ", ".join(f"{key}" + "={extra[" + str(key) + "]}" for key, value in extra.items())
|
||||
format_ += f" - <cyan>{formatted}</cyan>"
|
||||
|
||||
format_ += "\n"
|
||||
|
||||
if record["exception"] is not None:
|
||||
format_ += "{exception}\n"
|
||||
|
||||
return format_
|
||||
logger.add(handler, diagnose=True, level=logging.WARNING, format=Formatter.sentry_formatter)
|
||||
|
@ -8,6 +8,7 @@ from fastapi.responses import UJSONResponse
|
||||
from constants import LogLevelEnum
|
||||
from core.bot.app import BotApplication, BotQueue
|
||||
from core.bot.handlers import bot_event_handlers
|
||||
from core.lifetime import shutdown, startup
|
||||
from infra.logging_conf import configure_logging
|
||||
from routers import api_router
|
||||
from settings.config import AppSettings, get_settings
|
||||
@ -26,8 +27,12 @@ class Application:
|
||||
)
|
||||
self.app.state.settings = settings
|
||||
self.app.state.queue = BotQueue(bot_app=bot_app)
|
||||
self.app.state.bot_app = bot_app
|
||||
self.bot_app = bot_app
|
||||
|
||||
self.app.on_event("startup")(startup(self.app, settings))
|
||||
self.app.on_event("shutdown")(shutdown(self.app))
|
||||
|
||||
self.app.include_router(api_router)
|
||||
self.configure_hooks()
|
||||
configure_logging(
|
||||
@ -51,18 +56,18 @@ class Application:
|
||||
|
||||
def configure_hooks(self) -> None:
|
||||
if self.bot_app.start_with_webhook:
|
||||
self.app.add_event_handler("startup", self._on_start_up)
|
||||
self.app.add_event_handler("startup", self._bot_start_up)
|
||||
else:
|
||||
self.app.add_event_handler("startup", self.bot_app.polling)
|
||||
|
||||
self.app.add_event_handler("shutdown", self._on_shutdown)
|
||||
self.app.add_event_handler("shutdown", self._bot_shutdown)
|
||||
|
||||
async def _on_start_up(self) -> None:
|
||||
async def _bot_start_up(self) -> None:
|
||||
await self.bot_app.set_webhook()
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.create_task(self.app.state.queue.get_updates_from_queue())
|
||||
|
||||
async def _on_shutdown(self) -> None:
|
||||
async def _bot_shutdown(self) -> None:
|
||||
await asyncio.gather(self.bot_app.delete_webhook(), self.bot_app.shutdown())
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@ from typing import Any
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import model_validator
|
||||
from pydantic_settings import BaseSettings
|
||||
from yarl import URL
|
||||
|
||||
from constants import API_PREFIX
|
||||
|
||||
@ -52,6 +53,9 @@ class AppSettings(SentrySettings, BaseSettings):
|
||||
DOMAIN: str = "https://localhost"
|
||||
URL_PREFIX: str = ""
|
||||
|
||||
DB_FILE: Path = SHARED_DIR / "chat_gpt.db"
|
||||
DB_ECHO: bool = False
|
||||
|
||||
# ==== gpt settings ====
|
||||
GPT_MODEL: str = "gpt-3.5-turbo-stream-DeepAi"
|
||||
GPT_BASE_HOST: str = "http://chat_service:8858"
|
||||
@ -91,6 +95,18 @@ class AppSettings(SentrySettings, BaseSettings):
|
||||
def bot_webhook_url(self) -> str:
|
||||
return "/".join([self.api_prefix, self.token_part])
|
||||
|
||||
@cached_property
|
||||
def db_url(self) -> URL:
|
||||
"""
|
||||
Assemble database URL from settings.
|
||||
|
||||
:return: database URL.
|
||||
"""
|
||||
return URL.build(
|
||||
scheme="sqlite+aiosqlite",
|
||||
path=f"///{self.DB_FILE}",
|
||||
)
|
||||
|
||||
class Config:
|
||||
case_sensitive = True
|
||||
|
||||
|
2
chat_gpt_microservice/.gitignore
vendored
2
chat_gpt_microservice/.gitignore
vendored
@ -154,4 +154,4 @@ cython_debug/
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
.idea/
|
||||
|
@ -23,7 +23,6 @@ public:
|
||||
boost::asio::awaitable<void> deepAi(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> aiChat(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> chatGptAi(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> weWordle(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> acytoo(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> openAi(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> h2o(std::shared_ptr<Channel>, nlohmann::json);
|
||||
@ -31,7 +30,6 @@ public:
|
||||
boost::asio::awaitable<void> huggingChat(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> you(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> binjie(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> codeLinkAva(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> chatBase(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> aivvm(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> ylokh(std::shared_ptr<Channel>, nlohmann::json);
|
||||
@ -39,6 +37,9 @@ public:
|
||||
boost::asio::awaitable<void> gptGo(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> aibn(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> chatGptDuo(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> chatForAi(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> freeGpt(std::shared_ptr<Channel>, nlohmann::json);
|
||||
boost::asio::awaitable<void> cromicle(std::shared_ptr<Channel>, nlohmann::json);
|
||||
|
||||
private:
|
||||
boost::asio::awaitable<std::expected<boost::beast::ssl_stream<boost::beast::tcp_stream>, std::string>>
|
||||
|
@ -641,11 +641,15 @@ FreeGpt::createHttpClient(boost::asio::ssl::context& ctx, std::string_view host,
|
||||
}
|
||||
|
||||
boost::asio::awaitable<void> FreeGpt::deepAi(std::shared_ptr<Channel> ch, nlohmann::json json) {
|
||||
ScopeExit auto_exit{[&] { ch->close(); }};
|
||||
co_await boost::asio::post(boost::asio::bind_executor(*m_thread_pool_ptr, boost::asio::use_awaitable));
|
||||
|
||||
boost::system::error_code err{};
|
||||
ScopeExit _exit{[=] { boost::asio::post(ch->get_executor(), [=] { ch->close(); }); }};
|
||||
auto prompt = json.at("meta").at("content").at("parts").at(0).at("content").get<std::string>();
|
||||
|
||||
std::string user_agent{
|
||||
R"(Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36)"};
|
||||
R"(Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36)"};
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 mt(rd());
|
||||
std::uniform_int_distribution<uint64_t> dist(0, 100000000);
|
||||
@ -654,19 +658,39 @@ boost::asio::awaitable<void> FreeGpt::deepAi(std::shared_ptr<Channel> ch, nlohma
|
||||
auto api_key = std::format("tryit-{}-{}", part1, part2);
|
||||
|
||||
constexpr char CRLF[] = "\r\n";
|
||||
constexpr char MULTI_PART_BOUNDARY[] = "9bc627aea4f77e150e6057f78036e73f";
|
||||
constexpr std::string_view host{"api.deepai.org"};
|
||||
constexpr std::string_view port{"443"};
|
||||
static std::string MULTI_PART_BOUNDARY = "9bc627aea4f77e150e6057f78036e73f";
|
||||
|
||||
boost::beast::http::request<boost::beast::http::string_body> req{boost::beast::http::verb::post,
|
||||
"/make_me_a_sandwich", 11};
|
||||
req.set(boost::beast::http::field::host, host);
|
||||
req.set(boost::beast::http::field::user_agent, user_agent);
|
||||
req.set("Api-Key", api_key);
|
||||
req.set(boost::beast::http::field::content_type,
|
||||
std::format("multipart/form-data; boundary={}", MULTI_PART_BOUNDARY));
|
||||
CURLcode res;
|
||||
CURL* curl = curl_easy_init();
|
||||
if (!curl) {
|
||||
auto error_info = std::format("curl_easy_init() failed:{}", curl_easy_strerror(res));
|
||||
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
|
||||
ch->try_send(err, error_info);
|
||||
co_return;
|
||||
}
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "https://api.deepai.org/hacking_is_a_crime");
|
||||
|
||||
if (!m_cfg.http_proxy.empty())
|
||||
curl_easy_setopt(curl, CURLOPT_PROXY, m_cfg.http_proxy.c_str());
|
||||
|
||||
struct Input {
|
||||
std::shared_ptr<Channel> ch;
|
||||
std::string recv;
|
||||
};
|
||||
Input input{ch};
|
||||
auto action_cb = [](void* contents, size_t size, size_t nmemb, void* userp) -> size_t {
|
||||
boost::system::error_code err{};
|
||||
auto input_ptr = static_cast<Input*>(userp);
|
||||
std::string data{(char*)contents, size * nmemb};
|
||||
auto& [ch, recv] = *input_ptr;
|
||||
boost::asio::post(ch->get_executor(), [=] { ch->try_send(err, data); });
|
||||
return size * nmemb;
|
||||
};
|
||||
size_t (*action_fn)(void* contents, size_t size, size_t nmemb, void* userp) = action_cb;
|
||||
curlEasySetopt(curl);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, action_fn);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &input);
|
||||
|
||||
auto prompt = json.at("meta").at("content").at("parts").at(0).at("content").get<std::string>();
|
||||
nlohmann::json request_json{{{"role", "user"}, {"content", std::move(prompt)}}};
|
||||
|
||||
std::ostringstream payload;
|
||||
@ -674,30 +698,37 @@ boost::asio::awaitable<void> FreeGpt::deepAi(std::shared_ptr<Channel> ch, nlohma
|
||||
<< CRLF << "chat" << CRLF << "--" << MULTI_PART_BOUNDARY << CRLF
|
||||
<< R"(Content-Disposition: form-data; name="chatHistory")" << CRLF << CRLF << request_json.dump() << CRLF
|
||||
<< "--" << MULTI_PART_BOUNDARY << "--" << CRLF;
|
||||
|
||||
SPDLOG_INFO("{}", payload.str());
|
||||
req.body() = payload.str();
|
||||
req.prepare_payload();
|
||||
auto str = payload.str();
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, str.c_str());
|
||||
|
||||
int recreate_num{0};
|
||||
create_client:
|
||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::tls);
|
||||
ctx.set_verify_mode(boost::asio::ssl::verify_none);
|
||||
auto client = co_await createHttpClient(ctx, host, port);
|
||||
if (!client.has_value()) {
|
||||
SPDLOG_ERROR("createHttpClient: {}", client.error());
|
||||
co_await ch->async_send(err, client.error(), use_nothrow_awaitable);
|
||||
struct curl_slist* headers = nullptr;
|
||||
auto content_type_str = std::format("Content-Type: multipart/form-data; boundary={}", MULTI_PART_BOUNDARY);
|
||||
SPDLOG_INFO("content_type_str: {}", content_type_str);
|
||||
headers = curl_slist_append(headers, content_type_str.c_str());
|
||||
auto api_key_str = std::format("api-key: {}", api_key);
|
||||
headers = curl_slist_append(headers, api_key_str.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
ScopeExit auto_exit{[=] {
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
}};
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
|
||||
auto error_info = std::format("curl_easy_perform() failed:{}", curl_easy_strerror(res));
|
||||
ch->try_send(err, error_info);
|
||||
co_return;
|
||||
}
|
||||
auto& stream_ = client.value();
|
||||
|
||||
auto ret = co_await sendRequestRecvChunk(ch, stream_, req, 200, [&ch](std::string recv_str) {
|
||||
boost::system::error_code ec{};
|
||||
ch->try_send(ec, recv_str);
|
||||
});
|
||||
if (ret == Status::Close && recreate_num == 0) {
|
||||
recreate_num++;
|
||||
goto create_client;
|
||||
int32_t response_code;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
if (response_code != 200) {
|
||||
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
|
||||
ch->try_send(err, std::format("deepai http code:{}", response_code));
|
||||
co_return;
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
@ -1007,116 +1038,6 @@ create_client:
|
||||
co_return;
|
||||
}
|
||||
|
||||
boost::asio::awaitable<void> FreeGpt::weWordle(std::shared_ptr<Channel> ch, nlohmann::json json) {
|
||||
ScopeExit auto_exit{[&] { ch->close(); }};
|
||||
boost::system::error_code err{};
|
||||
|
||||
auto prompt = json.at("meta").at("content").at("parts").at(0).at("content").get<std::string>();
|
||||
|
||||
auto random = [](int len) {
|
||||
static std::string chars{"abcdefghijklmnopqrstuvwxyz0123456789"};
|
||||
static std::string letter{"abcdefghijklmnopqrstuvwxyz"};
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> dis(0, 1000000);
|
||||
std::string random_string;
|
||||
random_string += chars[dis(gen) % letter.length()];
|
||||
len = len - 1;
|
||||
for (int i = 0; i < len; i++)
|
||||
random_string += chars[dis(gen) % chars.length()];
|
||||
return random_string;
|
||||
};
|
||||
auto user_id = random(16);
|
||||
auto app_id = random(31);
|
||||
auto now = std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now());
|
||||
auto request_date = std::format("{:%Y-%m-%dT%H:%M:%S.000Z}", now);
|
||||
|
||||
constexpr std::string_view host = "wewordle.org";
|
||||
constexpr std::string_view port = "443";
|
||||
|
||||
boost::beast::http::request<boost::beast::http::string_body> req{boost::beast::http::verb::post,
|
||||
"/gptapi/v1/android/turbo", 11};
|
||||
req.set(boost::beast::http::field::host, host);
|
||||
req.set("pragma", "no-cache");
|
||||
req.set("accept", "*/*");
|
||||
req.set(
|
||||
boost::beast::http::field::user_agent,
|
||||
R"(Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36)");
|
||||
req.set(boost::beast::http::field::content_type, "application/json");
|
||||
req.set("Accept-Encoding", "gzip, deflate");
|
||||
|
||||
constexpr std::string_view json_str = R"({
|
||||
"user":"j1b892x978flimoa",
|
||||
"messages":[
|
||||
{
|
||||
"role":"user",
|
||||
"content":"user: hello\nassistant:"
|
||||
}
|
||||
],
|
||||
"subscriber":{
|
||||
"originalPurchaseDate":null,
|
||||
"originalApplicationVersion":null,
|
||||
"allPurchaseDatesMillis":{},
|
||||
"entitlements":{
|
||||
"active":{},
|
||||
"all":{}
|
||||
},
|
||||
"allPurchaseDates":{},
|
||||
"allExpirationDatesMillis":{},
|
||||
"allExpirationDates":{},
|
||||
"originalAppUserId":"$RCAnonymousID:z6xyxaasvt841d5zttw7q2iisb023tf",
|
||||
"latestExpirationDate":null,
|
||||
"requestDate":"2023-08-03T00:29:53.000Z",
|
||||
"latestExpirationDateMillis":null,
|
||||
"nonSubscriptionTransactions":[],
|
||||
"originalPurchaseDateMillis":null,
|
||||
"managementURL":null,
|
||||
"allPurchasedProductIdentifiers":[],
|
||||
"firstSeen":"2023-08-03T00:29:53.000Z",
|
||||
"activeSubscriptions":[]
|
||||
}
|
||||
})";
|
||||
|
||||
nlohmann::json request = nlohmann::json::parse(json_str, nullptr, false);
|
||||
|
||||
request["user"] = user_id;
|
||||
request["subscriber"]["originalAppUserId"] = std::format("$RCAnonymousID:{}", app_id);
|
||||
request["subscriber"]["firstSeen"] = request_date;
|
||||
request["subscriber"]["requestDate"] = request_date;
|
||||
request["messages"][0]["content"] = std::format("user: {}\nassistant:", prompt);
|
||||
|
||||
SPDLOG_INFO("{}", request.dump(2));
|
||||
|
||||
req.body() = request.dump();
|
||||
req.prepare_payload();
|
||||
|
||||
auto ret = co_await sendRequestRecvResponse(req, host, port, std::bind_front(&FreeGpt::createHttpClient, *this));
|
||||
if (!ret.has_value()) {
|
||||
co_await ch->async_send(err, ret.error(), use_nothrow_awaitable);
|
||||
co_return;
|
||||
}
|
||||
auto& [res, ctx, stream_] = ret.value();
|
||||
if (boost::beast::http::status::ok != res.result()) {
|
||||
SPDLOG_ERROR("http status code: {}", res.result_int());
|
||||
co_await ch->async_send(err, res.reason(), use_nothrow_awaitable);
|
||||
co_return;
|
||||
}
|
||||
|
||||
nlohmann::json rsp = nlohmann::json::parse(res.body(), nullptr, false);
|
||||
if (rsp.is_discarded()) {
|
||||
SPDLOG_ERROR("json parse error");
|
||||
co_await ch->async_send(err, std::format("json parse error: {}", res.body()), use_nothrow_awaitable);
|
||||
co_return;
|
||||
}
|
||||
if (!rsp.contains("message")) {
|
||||
SPDLOG_ERROR("not contains message: {}", rsp.dump());
|
||||
co_await ch->async_send(err, std::format("not contains message : {}", rsp.dump()), use_nothrow_awaitable);
|
||||
co_return;
|
||||
}
|
||||
co_await ch->async_send(err, rsp["message"].value("content", rsp.dump()), use_nothrow_awaitable);
|
||||
co_return;
|
||||
}
|
||||
|
||||
boost::asio::awaitable<void> FreeGpt::acytoo(std::shared_ptr<Channel> ch, nlohmann::json json) {
|
||||
boost::system::error_code err{};
|
||||
ScopeExit auto_exit{[&] { ch->close(); }};
|
||||
@ -1606,7 +1527,7 @@ boost::asio::awaitable<void> FreeGpt::huggingChat(std::shared_ptr<Channel> ch, n
|
||||
req_init_conversation.set(boost::beast::http::field::user_agent, user_agent);
|
||||
req_init_conversation.set("Accept", "*/*");
|
||||
req_init_conversation.set("Content-Type", "application/json");
|
||||
req_init_conversation.body() = R"({"model": "OpenAssistant/oasst-sft-6-llama-30b-xor"})";
|
||||
req_init_conversation.body() = R"({"model": "meta-llama/Llama-2-70b-chat-hf"})";
|
||||
req_init_conversation.prepare_payload();
|
||||
|
||||
auto [ec, count] = co_await boost::beast::http::async_write(stream_, req_init_conversation, use_nothrow_awaitable);
|
||||
@ -1902,87 +1823,6 @@ boost::asio::awaitable<void> FreeGpt::binjie(std::shared_ptr<Channel> ch, nlohma
|
||||
co_return;
|
||||
}
|
||||
|
||||
boost::asio::awaitable<void> FreeGpt::codeLinkAva(std::shared_ptr<Channel> ch, nlohmann::json json) {
|
||||
boost::system::error_code err{};
|
||||
ScopeExit auto_exit{[&] { ch->close(); }};
|
||||
|
||||
auto prompt = json.at("meta").at("content").at("parts").at(0).at("content").get<std::string>();
|
||||
|
||||
constexpr std::string_view host = "ava-alpha-api.codelink.io";
|
||||
constexpr std::string_view port = "443";
|
||||
|
||||
constexpr std::string_view user_agent{
|
||||
R"(Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36)"};
|
||||
|
||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::tls);
|
||||
ctx.set_verify_mode(boost::asio::ssl::verify_none);
|
||||
|
||||
auto client = co_await createHttpClient(ctx, host, port);
|
||||
if (!client.has_value()) {
|
||||
SPDLOG_ERROR("createHttpClient: {}", client.error());
|
||||
co_await ch->async_send(err, client.error(), use_nothrow_awaitable);
|
||||
co_return;
|
||||
}
|
||||
auto& stream_ = client.value();
|
||||
|
||||
boost::beast::http::request<boost::beast::http::string_body> req{boost::beast::http::verb::post, "/api/chat", 11};
|
||||
req.set(boost::beast::http::field::host, host);
|
||||
req.set(boost::beast::http::field::user_agent, user_agent);
|
||||
req.set("Accept", "*/*");
|
||||
req.set("accept-language", "en,fr-FR;q=0.9,fr;q=0.8,es-ES;q=0.7,es;q=0.6,en-US;q=0.5,am;q=0.4,de;q=0.3");
|
||||
req.set("origin", "https://ava-ai-ef611.web.app");
|
||||
req.set("referer", "https://ava-ai-ef611.web.app/");
|
||||
req.set(boost::beast::http::field::content_type, "application/json");
|
||||
req.set("sec-fetch-dest", "empty");
|
||||
req.set("sec-fetch-mode", "cors");
|
||||
req.set("sec-fetch-site", "same-origin");
|
||||
|
||||
constexpr std::string_view json_str = R"({
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "hello"
|
||||
}
|
||||
],
|
||||
"stream": true,
|
||||
"temperature": 0.6
|
||||
})";
|
||||
nlohmann::json request = nlohmann::json::parse(json_str, nullptr, false);
|
||||
|
||||
request["messages"][0]["content"] = prompt;
|
||||
SPDLOG_INFO("{}", request.dump(2));
|
||||
|
||||
req.body() = request.dump();
|
||||
req.prepare_payload();
|
||||
|
||||
std::string recv;
|
||||
auto result = co_await sendRequestRecvChunk(ch, stream_, req, 200, [&ch, &recv](std::string chunk_str) {
|
||||
recv.append(chunk_str);
|
||||
while (true) {
|
||||
auto position = recv.find("\n");
|
||||
if (position == std::string::npos)
|
||||
break;
|
||||
auto msg = recv.substr(0, position + 1);
|
||||
recv.erase(0, position + 1);
|
||||
msg.pop_back();
|
||||
if (msg.empty() || !msg.contains("content"))
|
||||
continue;
|
||||
auto fields = splitString(msg, "data:");
|
||||
boost::system::error_code err{};
|
||||
nlohmann::json line_json = nlohmann::json::parse(fields.back(), nullptr, false);
|
||||
if (line_json.is_discarded()) {
|
||||
SPDLOG_ERROR("json parse error: [{}]", fields.back());
|
||||
ch->try_send(err, std::format("json parse error: [{}]", fields.back()));
|
||||
continue;
|
||||
}
|
||||
auto str = line_json["choices"][0]["delta"]["content"].get<std::string>();
|
||||
if (!str.empty())
|
||||
ch->try_send(err, str);
|
||||
}
|
||||
});
|
||||
co_return;
|
||||
}
|
||||
|
||||
boost::asio::awaitable<void> FreeGpt::chatBase(std::shared_ptr<Channel> ch, nlohmann::json json) {
|
||||
boost::system::error_code err{};
|
||||
ScopeExit auto_exit{[&] { ch->close(); }};
|
||||
@ -2703,3 +2543,318 @@ boost::asio::awaitable<void> FreeGpt::chatGptDuo(std::shared_ptr<Channel> ch, nl
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
|
||||
boost::asio::awaitable<void> FreeGpt::chatForAi(std::shared_ptr<Channel> ch, nlohmann::json json) {
|
||||
co_await boost::asio::post(boost::asio::bind_executor(*m_thread_pool_ptr, boost::asio::use_awaitable));
|
||||
|
||||
boost::system::error_code err{};
|
||||
ScopeExit _exit{[=] { boost::asio::post(ch->get_executor(), [=] { ch->close(); }); }};
|
||||
auto prompt = json.at("meta").at("content").at("parts").at(0).at("content").get<std::string>();
|
||||
|
||||
CURLcode res;
|
||||
CURL* curl = curl_easy_init();
|
||||
if (!curl) {
|
||||
auto error_info = std::format("curl_easy_init() failed:{}", curl_easy_strerror(res));
|
||||
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
|
||||
ch->try_send(err, error_info);
|
||||
co_return;
|
||||
}
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "https://chatforai.com/api/handle/provider-openai");
|
||||
if (!m_cfg.http_proxy.empty())
|
||||
curl_easy_setopt(curl, CURLOPT_PROXY, m_cfg.http_proxy.c_str());
|
||||
|
||||
struct Input {
|
||||
std::shared_ptr<Channel> ch;
|
||||
std::string recv;
|
||||
};
|
||||
Input input{ch};
|
||||
auto action_cb = [](void* contents, size_t size, size_t nmemb, void* userp) -> size_t {
|
||||
boost::system::error_code err{};
|
||||
auto input_ptr = static_cast<Input*>(userp);
|
||||
std::string data{(char*)contents, size * nmemb};
|
||||
auto& [ch, recv] = *input_ptr;
|
||||
boost::asio::post(ch->get_executor(), [=] { ch->try_send(err, data); });
|
||||
return size * nmemb;
|
||||
};
|
||||
size_t (*action_fn)(void* contents, size_t size, size_t nmemb, void* userp) = action_cb;
|
||||
curlEasySetopt(curl);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, action_fn);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &input);
|
||||
|
||||
auto generate_signature = [](int timestamp, const std::string& conversation_id, const std::string& message) {
|
||||
std::stringstream ss;
|
||||
ss << timestamp << ":" << conversation_id << ":" << message << ":6B46K4pt";
|
||||
std::string data = ss.str();
|
||||
|
||||
unsigned char digest[SHA256_DIGEST_LENGTH];
|
||||
SHA256(reinterpret_cast<const unsigned char*>(data.c_str()), data.length(), digest);
|
||||
|
||||
std::stringstream sha_stream;
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
|
||||
sha_stream << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(digest[i]);
|
||||
}
|
||||
return sha_stream.str();
|
||||
};
|
||||
uint64_t timestamp =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
auto onversation_id = std::format("id_{}", timestamp);
|
||||
std::string signature = generate_signature(timestamp, onversation_id, prompt);
|
||||
|
||||
constexpr std::string_view request_str{R"({
|
||||
"conversationId": "id_1696338587",
|
||||
"conversationType": "chat_continuous",
|
||||
"botId": "chat_continuous",
|
||||
"globalSettings": {
|
||||
"baseUrl": "https://api.openai.com",
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messageHistorySize": 5,
|
||||
"temperature": 0.7,
|
||||
"top_p": 1,
|
||||
"stream": true
|
||||
},
|
||||
"botSettings": {},
|
||||
"prompt": "hello",
|
||||
"messages": [{
|
||||
"role": "user",
|
||||
"content": "hello"
|
||||
}],
|
||||
"sign": "1505ec882d72d5f3175a74ac84d665b1b904e6671b2e7334268f540975929a26",
|
||||
"timestamp": 1696338587
|
||||
})"};
|
||||
nlohmann::json request = nlohmann::json::parse(request_str, nullptr, false);
|
||||
|
||||
request["sign"] = signature;
|
||||
request["conversationId"] = onversation_id;
|
||||
request["messages"] = getConversationJson(json);
|
||||
request["timestamp"] = timestamp;
|
||||
|
||||
auto str = request.dump();
|
||||
SPDLOG_INFO("request : [{}]", str);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, str.c_str());
|
||||
|
||||
struct curl_slist* headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
ScopeExit auto_exit{[=] {
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
}};
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
|
||||
auto error_info = std::format("curl_easy_perform() failed:{}", curl_easy_strerror(res));
|
||||
ch->try_send(err, error_info);
|
||||
co_return;
|
||||
}
|
||||
int32_t response_code;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
if (response_code != 200) {
|
||||
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
|
||||
ch->try_send(err, std::format("you http code:{}", response_code));
|
||||
co_return;
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
|
||||
boost::asio::awaitable<void> FreeGpt::freeGpt(std::shared_ptr<Channel> ch, nlohmann::json json) {
|
||||
co_await boost::asio::post(boost::asio::bind_executor(*m_thread_pool_ptr, boost::asio::use_awaitable));
|
||||
|
||||
boost::system::error_code err{};
|
||||
ScopeExit _exit{[=] { boost::asio::post(ch->get_executor(), [=] { ch->close(); }); }};
|
||||
auto prompt = json.at("meta").at("content").at("parts").at(0).at("content").get<std::string>();
|
||||
|
||||
CURLcode res;
|
||||
CURL* curl = curl_easy_init();
|
||||
if (!curl) {
|
||||
auto error_info = std::format("curl_easy_init() failed:{}", curl_easy_strerror(res));
|
||||
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
|
||||
ch->try_send(err, error_info);
|
||||
co_return;
|
||||
}
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "https://k.aifree.site/api/generate");
|
||||
if (!m_cfg.http_proxy.empty())
|
||||
curl_easy_setopt(curl, CURLOPT_PROXY, m_cfg.http_proxy.c_str());
|
||||
|
||||
struct Input {
|
||||
std::shared_ptr<Channel> ch;
|
||||
std::string recv;
|
||||
};
|
||||
Input input{ch};
|
||||
auto action_cb = [](void* contents, size_t size, size_t nmemb, void* userp) -> size_t {
|
||||
boost::system::error_code err{};
|
||||
auto input_ptr = static_cast<Input*>(userp);
|
||||
std::string data{(char*)contents, size * nmemb};
|
||||
auto& [ch, recv] = *input_ptr;
|
||||
boost::asio::post(ch->get_executor(), [=] { ch->try_send(err, data); });
|
||||
return size * nmemb;
|
||||
};
|
||||
size_t (*action_fn)(void* contents, size_t size, size_t nmemb, void* userp) = action_cb;
|
||||
curlEasySetopt(curl);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, action_fn);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &input);
|
||||
|
||||
auto generate_signature = [](int timestamp, const std::string& message, const std::string& secret = "") {
|
||||
std::stringstream ss;
|
||||
ss << timestamp << ":" << message << ":" << secret;
|
||||
std::string data = ss.str();
|
||||
|
||||
unsigned char digest[SHA256_DIGEST_LENGTH];
|
||||
SHA256(reinterpret_cast<const unsigned char*>(data.c_str()), data.length(), digest);
|
||||
|
||||
std::stringstream sha_stream;
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
|
||||
sha_stream << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(digest[i]);
|
||||
}
|
||||
return sha_stream.str();
|
||||
};
|
||||
uint64_t timestamp =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
std::string signature = generate_signature(timestamp, prompt);
|
||||
|
||||
constexpr std::string_view request_str{R"({
|
||||
"messages":[
|
||||
{
|
||||
"role":"user",
|
||||
"content":"hello"
|
||||
}
|
||||
],
|
||||
"pass":null,
|
||||
"sign":"7c2700b5813053ff8000cb9fb1ebdadbfcf62882829da59e4474bee466de7c89",
|
||||
"time":1695716667
|
||||
})"};
|
||||
nlohmann::json request = nlohmann::json::parse(request_str, nullptr, false);
|
||||
|
||||
request["sign"] = signature;
|
||||
request["time"] = timestamp;
|
||||
request["messages"] = getConversationJson(json);
|
||||
|
||||
auto str = request.dump();
|
||||
SPDLOG_INFO("request : [{}]", str);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, str.c_str());
|
||||
|
||||
struct curl_slist* headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
ScopeExit auto_exit{[=] {
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
}};
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
|
||||
auto error_info = std::format("curl_easy_perform() failed:{}", curl_easy_strerror(res));
|
||||
ch->try_send(err, error_info);
|
||||
co_return;
|
||||
}
|
||||
int32_t response_code;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
if (response_code != 200) {
|
||||
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
|
||||
ch->try_send(err, std::format("you http code:{}", response_code));
|
||||
co_return;
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
|
||||
boost::asio::awaitable<void> FreeGpt::cromicle(std::shared_ptr<Channel> ch, nlohmann::json json) {
|
||||
co_await boost::asio::post(boost::asio::bind_executor(*m_thread_pool_ptr, boost::asio::use_awaitable));
|
||||
|
||||
boost::system::error_code err{};
|
||||
ScopeExit _exit{[=] { boost::asio::post(ch->get_executor(), [=] { ch->close(); }); }};
|
||||
auto prompt = json.at("meta").at("content").at("parts").at(0).at("content").get<std::string>();
|
||||
|
||||
CURLcode res;
|
||||
CURL* curl = curl_easy_init();
|
||||
if (!curl) {
|
||||
auto error_info = std::format("curl_easy_init() failed:{}", curl_easy_strerror(res));
|
||||
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
|
||||
ch->try_send(err, error_info);
|
||||
co_return;
|
||||
}
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "https://cromicle.top/chat");
|
||||
if (!m_cfg.http_proxy.empty())
|
||||
curl_easy_setopt(curl, CURLOPT_PROXY, m_cfg.http_proxy.c_str());
|
||||
|
||||
struct Input {
|
||||
std::shared_ptr<Channel> ch;
|
||||
std::string recv;
|
||||
};
|
||||
Input input{ch};
|
||||
auto action_cb = [](void* contents, size_t size, size_t nmemb, void* userp) -> size_t {
|
||||
boost::system::error_code err{};
|
||||
auto input_ptr = static_cast<Input*>(userp);
|
||||
std::string data{(char*)contents, size * nmemb};
|
||||
auto& [ch, recv] = *input_ptr;
|
||||
boost::asio::post(ch->get_executor(), [=] { ch->try_send(err, data); });
|
||||
return size * nmemb;
|
||||
};
|
||||
size_t (*action_fn)(void* contents, size_t size, size_t nmemb, void* userp) = action_cb;
|
||||
curlEasySetopt(curl);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, action_fn);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &input);
|
||||
|
||||
auto generate_signature = [](const std::string& message) {
|
||||
std::stringstream ss;
|
||||
ss << "asdap" << message;
|
||||
std::string data = ss.str();
|
||||
|
||||
unsigned char digest[SHA256_DIGEST_LENGTH];
|
||||
SHA256(reinterpret_cast<const unsigned char*>(data.c_str()), data.length(), digest);
|
||||
|
||||
std::stringstream sha_stream;
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
|
||||
sha_stream << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(digest[i]);
|
||||
}
|
||||
return sha_stream.str();
|
||||
};
|
||||
std::string signature = generate_signature(prompt);
|
||||
|
||||
constexpr std::string_view request_str{R"({
|
||||
"message": "hello",
|
||||
"hash": "dda6ea4e1dc215f198084018b1df20cfeafe9fbdfe31d8a350d6917509158d8a",
|
||||
"token": "asdap"
|
||||
})"};
|
||||
nlohmann::json request = nlohmann::json::parse(request_str, nullptr, false);
|
||||
|
||||
request["hash"] = signature;
|
||||
request["message"] = prompt;
|
||||
|
||||
auto str = request.dump();
|
||||
SPDLOG_INFO("request : [{}]", str);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, str.c_str());
|
||||
|
||||
struct curl_slist* headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
ScopeExit auto_exit{[=] {
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
}};
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
|
||||
auto error_info = std::format("curl_easy_perform() failed:{}", curl_easy_strerror(res));
|
||||
ch->try_send(err, error_info);
|
||||
co_return;
|
||||
}
|
||||
int32_t response_code;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
if (response_code != 200) {
|
||||
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
|
||||
ch->try_send(err, std::format("you http code:{}", response_code));
|
||||
co_return;
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
|
@ -92,6 +92,18 @@ boost::asio::awaitable<void> sendHttpResponse(auto& stream, auto& request, auto
|
||||
co_return;
|
||||
}
|
||||
|
||||
void setContentType(auto& res, const std::string& file) {
|
||||
SPDLOG_INFO("file: {}", file);
|
||||
if (file.ends_with("js")) {
|
||||
res.set(boost::beast::http::field::content_type, "text/javascript");
|
||||
} else if (file.ends_with("css")) {
|
||||
res.set(boost::beast::http::field::content_type, "text/css");
|
||||
} else if (file.ends_with("png")) {
|
||||
res.set(boost::beast::http::field::content_type, "image/png");
|
||||
} else
|
||||
SPDLOG_ERROR("invalid file type: {}", file);
|
||||
}
|
||||
|
||||
boost::asio::awaitable<void> startSession(boost::asio::ip::tcp::socket sock, Config& cfg,
|
||||
boost::asio::io_context& context) {
|
||||
boost::beast::tcp_stream stream{std::move(sock)};
|
||||
@ -172,7 +184,7 @@ boost::asio::awaitable<void> startSession(boost::asio::ip::tcp::socket sock, Con
|
||||
boost::beast::http::response<boost::beast::http::string_body> res{boost::beast::http::status::ok,
|
||||
request.version()};
|
||||
res.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING);
|
||||
res.set(boost::beast::http::field::content_type, "text/javascript");
|
||||
setContentType(res, file);
|
||||
res.keep_alive(request.keep_alive());
|
||||
res.body() = std::move(chat_js_content);
|
||||
res.prepare_payload();
|
||||
@ -191,8 +203,7 @@ boost::asio::awaitable<void> startSession(boost::asio::ip::tcp::socket sock, Con
|
||||
std::piecewise_construct, std::make_tuple(std::move(body)),
|
||||
std::make_tuple(boost::beast::http::status::ok, request.version())};
|
||||
res.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING);
|
||||
res.set(boost::beast::http::field::content_type,
|
||||
req_path.contains("css") ? "text/css" : "text/javascript");
|
||||
setContentType(res, file);
|
||||
res.content_length(size);
|
||||
res.keep_alive(request.keep_alive());
|
||||
boost::beast::http::message_generator rsp = std::move(res);
|
||||
@ -324,24 +335,25 @@ int main(int argc, char** argv) {
|
||||
ADD_METHOD("gpt-3.5-turbo-stream-openai", FreeGpt::openAi);
|
||||
ADD_METHOD("gpt-3.5-turbo-Aichat", FreeGpt::aiChat);
|
||||
ADD_METHOD("gpt-4-ChatgptAi", FreeGpt::chatGptAi);
|
||||
// ADD_METHOD("gpt-3.5-turbo-weWordle", FreeGpt::weWordle);
|
||||
ADD_METHOD("gpt-3.5-turbo-acytoo", FreeGpt::acytoo);
|
||||
ADD_METHOD("gpt-3.5-turbo-stream-DeepAi", FreeGpt::deepAi);
|
||||
ADD_METHOD("gpt-3.5-turbo-stream-H2o", FreeGpt::h2o);
|
||||
ADD_METHOD("gpt-3.5-turbo-stream-yqcloud", FreeGpt::yqcloud);
|
||||
ADD_METHOD("gpt-OpenAssistant-stream-HuggingChat", FreeGpt::huggingChat)
|
||||
ADD_METHOD("gpt-4-turbo-stream-you", FreeGpt::you);
|
||||
ADD_METHOD("gpt-3.5-turbo-AItianhu", FreeGpt::aiTianhu);
|
||||
// ADD_METHOD("gpt-3.5-turbo-AItianhu", FreeGpt::aiTianhu);
|
||||
ADD_METHOD("gpt-3-stream-binjie", FreeGpt::binjie);
|
||||
// ADD_METHOD("gpt-3.5-turbo-stream-CodeLinkAva", FreeGpt::codeLinkAva);
|
||||
ADD_METHOD("gpt-4-stream-ChatBase", FreeGpt::chatBase);
|
||||
ADD_METHOD("gpt-3.5-turbo-stream-aivvm", FreeGpt::aivvm);
|
||||
ADD_METHOD("gpt-3.5-turbo-16k-stream-Ylokh", FreeGpt::ylokh);
|
||||
ADD_METHOD("gpt-3.5-turbo-stream-Vitalentum", FreeGpt::vitalentum);
|
||||
ADD_METHOD("gpt-3.5-turbo-stream-GptGo", FreeGpt::gptGo);
|
||||
ADD_METHOD("gpt-3.5-turbo-stream-AItianhuSpace", FreeGpt::aiTianhuSpace);
|
||||
// ADD_METHOD("gpt-3.5-turbo-stream-AItianhuSpace", FreeGpt::aiTianhuSpace);
|
||||
ADD_METHOD("gpt-3.5-turbo-stream-Aibn", FreeGpt::aibn);
|
||||
ADD_METHOD("gpt-3.5-turbo-ChatgptDuo", FreeGpt::chatGptDuo);
|
||||
ADD_METHOD("gpt-3.5-turbo-stream-ChatForAi", FreeGpt::chatForAi);
|
||||
ADD_METHOD("gpt-3.5-turbo-stream-FreeGpt", FreeGpt::freeGpt);
|
||||
ADD_METHOD("gpt-3.5-turbo-stream-Cromicle", FreeGpt::cromicle);
|
||||
|
||||
IoContextPool pool{cfg.work_thread_num};
|
||||
pool.start();
|
||||
|
496
poetry.lock
generated
496
poetry.lock
generated
@ -12,14 +12,48 @@ files = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.5.0"
|
||||
description = "Reusable constraint types to use with typing.Annotated"
|
||||
name = "aiosqlite"
|
||||
version = "0.19.0"
|
||||
description = "asyncio bridge to the standard sqlite3 module"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "annotated_types-0.5.0-py3-none-any.whl", hash = "sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd"},
|
||||
{file = "annotated_types-0.5.0.tar.gz", hash = "sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802"},
|
||||
{file = "aiosqlite-0.19.0-py3-none-any.whl", hash = "sha256:edba222e03453e094a3ce605db1b970c4b3376264e56f32e2a4959f948d66a96"},
|
||||
{file = "aiosqlite-0.19.0.tar.gz", hash = "sha256:95ee77b91c8d2808bd08a59fbebf66270e9090c3d92ffbf260dc0db0b979577d"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["aiounittest (==1.4.1)", "attribution (==1.6.2)", "black (==23.3.0)", "coverage[toml] (==7.2.3)", "flake8 (==5.0.4)", "flake8-bugbear (==23.3.12)", "flit (==3.7.1)", "mypy (==1.2.0)", "ufmt (==2.1.0)", "usort (==1.0.6)"]
|
||||
docs = ["sphinx (==6.1.3)", "sphinx-mdinclude (==0.5.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.12.0"
|
||||
description = "A database migration tool for SQLAlchemy."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "alembic-1.12.0-py3-none-any.whl", hash = "sha256:03226222f1cf943deee6c85d9464261a6c710cd19b4fe867a3ad1f25afda610f"},
|
||||
{file = "alembic-1.12.0.tar.gz", hash = "sha256:8e7645c32e4f200675e69f0745415335eb59a3663f5feb487abfa0b30c45888b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Mako = "*"
|
||||
SQLAlchemy = ">=1.3.0"
|
||||
typing-extensions = ">=4"
|
||||
|
||||
[package.extras]
|
||||
tz = ["python-dateutil"]
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.6.0"
|
||||
description = "Reusable constraint types to use with typing.Annotated"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"},
|
||||
{file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1031,75 +1065,77 @@ docs = ["sphinx (>=2.1.2,<3.0.0)", "sphinx-autodoc-typehints (>=1.6.0,<2.0.0)",
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "2.0.2"
|
||||
version = "3.0.0"
|
||||
description = "Lightweight in-process concurrent programming"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "greenlet-2.0.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d"},
|
||||
{file = "greenlet-2.0.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9"},
|
||||
{file = "greenlet-2.0.2-cp27-cp27m-win32.whl", hash = "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74"},
|
||||
{file = "greenlet-2.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343"},
|
||||
{file = "greenlet-2.0.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae"},
|
||||
{file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"},
|
||||
{file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"},
|
||||
{file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"},
|
||||
{file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470"},
|
||||
{file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a"},
|
||||
{file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"},
|
||||
{file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"},
|
||||
{file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"},
|
||||
{file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"},
|
||||
{file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"},
|
||||
{file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"},
|
||||
{file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19"},
|
||||
{file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3"},
|
||||
{file = "greenlet-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5"},
|
||||
{file = "greenlet-2.0.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6"},
|
||||
{file = "greenlet-2.0.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43"},
|
||||
{file = "greenlet-2.0.2-cp35-cp35m-win32.whl", hash = "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a"},
|
||||
{file = "greenlet-2.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394"},
|
||||
{file = "greenlet-2.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0"},
|
||||
{file = "greenlet-2.0.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3"},
|
||||
{file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db"},
|
||||
{file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099"},
|
||||
{file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75"},
|
||||
{file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf"},
|
||||
{file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292"},
|
||||
{file = "greenlet-2.0.2-cp36-cp36m-win32.whl", hash = "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9"},
|
||||
{file = "greenlet-2.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f"},
|
||||
{file = "greenlet-2.0.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b"},
|
||||
{file = "greenlet-2.0.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1"},
|
||||
{file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7"},
|
||||
{file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca"},
|
||||
{file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73"},
|
||||
{file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86"},
|
||||
{file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33"},
|
||||
{file = "greenlet-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7"},
|
||||
{file = "greenlet-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3"},
|
||||
{file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"},
|
||||
{file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"},
|
||||
{file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"},
|
||||
{file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"},
|
||||
{file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857"},
|
||||
{file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a"},
|
||||
{file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"},
|
||||
{file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"},
|
||||
{file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"},
|
||||
{file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"},
|
||||
{file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"},
|
||||
{file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"},
|
||||
{file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b"},
|
||||
{file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b"},
|
||||
{file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8"},
|
||||
{file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9"},
|
||||
{file = "greenlet-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5"},
|
||||
{file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"},
|
||||
{file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"},
|
||||
{file = "greenlet-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e09dea87cc91aea5500262993cbd484b41edf8af74f976719dd83fe724644cd6"},
|
||||
{file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47932c434a3c8d3c86d865443fadc1fbf574e9b11d6650b656e602b1797908a"},
|
||||
{file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c"},
|
||||
{file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a68d670c8f89ff65c82b936275369e532772eebc027c3be68c6b87ad05ca695"},
|
||||
{file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ad562a104cd41e9d4644f46ea37167b93190c6d5e4048fcc4b80d34ecb278f"},
|
||||
{file = "greenlet-3.0.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02a807b2a58d5cdebb07050efe3d7deaf915468d112dfcf5e426d0564aa3aa4a"},
|
||||
{file = "greenlet-3.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b1660a15a446206c8545edc292ab5c48b91ff732f91b3d3b30d9a915d5ec4779"},
|
||||
{file = "greenlet-3.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:813720bd57e193391dfe26f4871186cf460848b83df7e23e6bef698a7624b4c9"},
|
||||
{file = "greenlet-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:aa15a2ec737cb609ed48902b45c5e4ff6044feb5dcdfcf6fa8482379190330d7"},
|
||||
{file = "greenlet-3.0.0-cp310-universal2-macosx_11_0_x86_64.whl", hash = "sha256:7709fd7bb02b31908dc8fd35bfd0a29fc24681d5cc9ac1d64ad07f8d2b7db62f"},
|
||||
{file = "greenlet-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7"},
|
||||
{file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6512592cc49b2c6d9b19fbaa0312124cd4c4c8a90d28473f86f92685cc5fef8e"},
|
||||
{file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:871b0a8835f9e9d461b7fdaa1b57e3492dd45398e87324c047469ce2fc9f516c"},
|
||||
{file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b505fcfc26f4148551826a96f7317e02c400665fa0883fe505d4fcaab1dabfdd"},
|
||||
{file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14"},
|
||||
{file = "greenlet-3.0.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:96d9ea57292f636ec851a9bb961a5cc0f9976900e16e5d5647f19aa36ba6366b"},
|
||||
{file = "greenlet-3.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0b72b802496cccbd9b31acea72b6f87e7771ccfd7f7927437d592e5c92ed703c"},
|
||||
{file = "greenlet-3.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362"},
|
||||
{file = "greenlet-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:37f60b3a42d8b5499be910d1267b24355c495064f271cfe74bf28b17b099133c"},
|
||||
{file = "greenlet-3.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1482fba7fbed96ea7842b5a7fc11d61727e8be75a077e603e8ab49d24e234383"},
|
||||
{file = "greenlet-3.0.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f"},
|
||||
{file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73b2f1922a39d5d59cc0e597987300df3396b148a9bd10b76a058a2f2772fc04"},
|
||||
{file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1e22c22f7826096ad503e9bb681b05b8c1f5a8138469b255eb91f26a76634f2"},
|
||||
{file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99"},
|
||||
{file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:334ef6ed8337bd0b58bb0ae4f7f2dcc84c9f116e474bb4ec250a8bb9bd797a66"},
|
||||
{file = "greenlet-3.0.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6672fdde0fd1a60b44fb1751a7779c6db487e42b0cc65e7caa6aa686874e79fb"},
|
||||
{file = "greenlet-3.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:952256c2bc5b4ee8df8dfc54fc4de330970bf5d79253c863fb5e6761f00dda35"},
|
||||
{file = "greenlet-3.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:269d06fa0f9624455ce08ae0179430eea61085e3cf6457f05982b37fd2cefe17"},
|
||||
{file = "greenlet-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9adbd8ecf097e34ada8efde9b6fec4dd2a903b1e98037adf72d12993a1c80b51"},
|
||||
{file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6b5ce7f40f0e2f8b88c28e6691ca6806814157ff05e794cdd161be928550f4c"},
|
||||
{file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf94aa539e97a8411b5ea52fc6ccd8371be9550c4041011a091eb8b3ca1d810"},
|
||||
{file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80dcd3c938cbcac986c5c92779db8e8ce51a89a849c135172c88ecbdc8c056b7"},
|
||||
{file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e52a712c38e5fb4fd68e00dc3caf00b60cb65634d50e32281a9d6431b33b4af1"},
|
||||
{file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5539f6da3418c3dc002739cb2bb8d169056aa66e0c83f6bacae0cd3ac26b423"},
|
||||
{file = "greenlet-3.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:343675e0da2f3c69d3fb1e894ba0a1acf58f481f3b9372ce1eb465ef93cf6fed"},
|
||||
{file = "greenlet-3.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:abe1ef3d780de56defd0c77c5ba95e152f4e4c4e12d7e11dd8447d338b85a625"},
|
||||
{file = "greenlet-3.0.0-cp37-cp37m-win32.whl", hash = "sha256:e693e759e172fa1c2c90d35dea4acbdd1d609b6936115d3739148d5e4cd11947"},
|
||||
{file = "greenlet-3.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bdd696947cd695924aecb3870660b7545a19851f93b9d327ef8236bfc49be705"},
|
||||
{file = "greenlet-3.0.0-cp37-universal2-macosx_11_0_x86_64.whl", hash = "sha256:cc3e2679ea13b4de79bdc44b25a0c4fcd5e94e21b8f290791744ac42d34a0353"},
|
||||
{file = "greenlet-3.0.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:63acdc34c9cde42a6534518e32ce55c30f932b473c62c235a466469a710bfbf9"},
|
||||
{file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a1a6244ff96343e9994e37e5b4839f09a0207d35ef6134dce5c20d260d0302c"},
|
||||
{file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b822fab253ac0f330ee807e7485769e3ac85d5eef827ca224feaaefa462dc0d0"},
|
||||
{file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8060b32d8586e912a7b7dac2d15b28dbbd63a174ab32f5bc6d107a1c4143f40b"},
|
||||
{file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:621fcb346141ae08cb95424ebfc5b014361621b8132c48e538e34c3c93ac7365"},
|
||||
{file = "greenlet-3.0.0-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bb36985f606a7c49916eff74ab99399cdfd09241c375d5a820bb855dfb4af9f"},
|
||||
{file = "greenlet-3.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10b5582744abd9858947d163843d323d0b67be9432db50f8bf83031032bc218d"},
|
||||
{file = "greenlet-3.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f351479a6914fd81a55c8e68963609f792d9b067fb8a60a042c585a621e0de4f"},
|
||||
{file = "greenlet-3.0.0-cp38-cp38-win32.whl", hash = "sha256:9de687479faec7db5b198cc365bc34addd256b0028956501f4d4d5e9ca2e240a"},
|
||||
{file = "greenlet-3.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:3fd2b18432e7298fcbec3d39e1a0aa91ae9ea1c93356ec089421fabc3651572b"},
|
||||
{file = "greenlet-3.0.0-cp38-universal2-macosx_11_0_x86_64.whl", hash = "sha256:3c0d36f5adc6e6100aedbc976d7428a9f7194ea79911aa4bf471f44ee13a9464"},
|
||||
{file = "greenlet-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4"},
|
||||
{file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a5b2d4cdaf1c71057ff823a19d850ed5c6c2d3686cb71f73ae4d6382aaa7a06"},
|
||||
{file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e7dcdfad252f2ca83c685b0fa9fba00e4d8f243b73839229d56ee3d9d219314"},
|
||||
{file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870"},
|
||||
{file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6fb737e46b8bd63156b8f59ba6cdef46fe2b7db0c5804388a2d0519b8ddb99"},
|
||||
{file = "greenlet-3.0.0-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d55db1db455c59b46f794346efce896e754b8942817f46a1bada2d29446e305a"},
|
||||
{file = "greenlet-3.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692"},
|
||||
{file = "greenlet-3.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a812224a5fb17a538207e8cf8e86f517df2080c8ee0f8c1ed2bdaccd18f38f4"},
|
||||
{file = "greenlet-3.0.0-cp39-cp39-win32.whl", hash = "sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9"},
|
||||
{file = "greenlet-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:831d6f35037cf18ca5e80a737a27d822d87cd922521d18ed3dbc8a6967be50ce"},
|
||||
{file = "greenlet-3.0.0-cp39-universal2-macosx_11_0_x86_64.whl", hash = "sha256:a048293392d4e058298710a54dfaefcefdf49d287cd33fb1f7d63d55426e4355"},
|
||||
{file = "greenlet-3.0.0.tar.gz", hash = "sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["Sphinx", "docutils (<0.18)"]
|
||||
docs = ["Sphinx"]
|
||||
test = ["objgraph", "psutil"]
|
||||
|
||||
[[package]]
|
||||
@ -1156,13 +1192,13 @@ lxml = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "0.17.3"
|
||||
version = "0.18.0"
|
||||
description = "A minimal low-level HTTP client."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "httpcore-0.17.3-py3-none-any.whl", hash = "sha256:c2789b767ddddfa2a5782e3199b2b7f6894540b17b16ec26b2c4d8e103510b87"},
|
||||
{file = "httpcore-0.17.3.tar.gz", hash = "sha256:a6f30213335e34c1ade7be6ec7c47f19f50c56db36abef1a9dfa3815b1cb3888"},
|
||||
{file = "httpcore-0.18.0-py3-none-any.whl", hash = "sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced"},
|
||||
{file = "httpcore-0.18.0.tar.gz", hash = "sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1177,18 +1213,18 @@ socks = ["socksio (==1.*)"]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.24.1"
|
||||
version = "0.25.0"
|
||||
description = "The next generation HTTP client."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "httpx-0.24.1-py3-none-any.whl", hash = "sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd"},
|
||||
{file = "httpx-0.24.1.tar.gz", hash = "sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd"},
|
||||
{file = "httpx-0.25.0-py3-none-any.whl", hash = "sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100"},
|
||||
{file = "httpx-0.25.0.tar.gz", hash = "sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
certifi = "*"
|
||||
httpcore = ">=0.15.0,<0.18.0"
|
||||
httpcore = ">=0.18.0,<0.19.0"
|
||||
idna = "*"
|
||||
sniffio = "*"
|
||||
|
||||
@ -1347,6 +1383,25 @@ win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
|
||||
[package.extras]
|
||||
dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "mako"
|
||||
version = "1.2.4"
|
||||
description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"},
|
||||
{file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=0.9.2"
|
||||
|
||||
[package.extras]
|
||||
babel = ["Babel"]
|
||||
lingua = ["lingua"]
|
||||
testing = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "3.0.0"
|
||||
@ -1551,6 +1606,89 @@ files = [
|
||||
{file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multidict"
|
||||
version = "6.0.4"
|
||||
description = "multidict implementation"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"},
|
||||
{file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.5.1"
|
||||
@ -2474,33 +2612,33 @@ cli = ["click (>=5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "python-telegram-bot"
|
||||
version = "20.5"
|
||||
version = "20.6"
|
||||
description = "We have made you a wrapper you can't refuse"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "python-telegram-bot-20.5.tar.gz", hash = "sha256:2f45a94c861cbd40440ece2be176ef0fc69e10d84e6dfa17f9a456e32aeece13"},
|
||||
{file = "python_telegram_bot-20.5-py3-none-any.whl", hash = "sha256:fc9605a855794231c802cc3948e6f7c319a817b5cd1827371f170bc7ca0ca279"},
|
||||
{file = "python-telegram-bot-20.6.tar.gz", hash = "sha256:c6951dc6b9368d80a17b5d0496ced39bca2c9bafab06cbfdae6226493d8b06fe"},
|
||||
{file = "python_telegram_bot-20.6-py3-none-any.whl", hash = "sha256:be8ebda350fd0c69c3ebdd7ce87b1e6f8b3fb1d218a193dd821d1d6285b9f649"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
aiolimiter = {version = ">=1.1.0,<1.2.0", optional = true, markers = "extra == \"ext\""}
|
||||
APScheduler = {version = ">=3.10.4,<3.11.0", optional = true, markers = "extra == \"ext\""}
|
||||
cachetools = {version = ">=5.3.1,<5.4.0", optional = true, markers = "extra == \"ext\""}
|
||||
httpx = ">=0.24.1,<0.25.0"
|
||||
httpx = ">=0.25.0,<0.26.0"
|
||||
pytz = {version = ">=2018.6", optional = true, markers = "extra == \"ext\""}
|
||||
tornado = {version = ">=6.2,<7.0", optional = true, markers = "extra == \"ext\""}
|
||||
tornado = {version = ">=6.3.3,<6.4.0", optional = true, markers = "extra == \"ext\""}
|
||||
|
||||
[package.extras]
|
||||
all = ["APScheduler (>=3.10.4,<3.11.0)", "aiolimiter (>=1.1.0,<1.2.0)", "cachetools (>=5.3.1,<5.4.0)", "cryptography (>=39.0.1)", "httpx[http2]", "httpx[socks]", "pytz (>=2018.6)", "tornado (>=6.2,<7.0)"]
|
||||
all = ["APScheduler (>=3.10.4,<3.11.0)", "aiolimiter (>=1.1.0,<1.2.0)", "cachetools (>=5.3.1,<5.4.0)", "cryptography (>=39.0.1)", "httpx[http2]", "httpx[socks]", "pytz (>=2018.6)", "tornado (>=6.3.3,<6.4.0)"]
|
||||
callback-data = ["cachetools (>=5.3.1,<5.4.0)"]
|
||||
ext = ["APScheduler (>=3.10.4,<3.11.0)", "aiolimiter (>=1.1.0,<1.2.0)", "cachetools (>=5.3.1,<5.4.0)", "pytz (>=2018.6)", "tornado (>=6.2,<7.0)"]
|
||||
ext = ["APScheduler (>=3.10.4,<3.11.0)", "aiolimiter (>=1.1.0,<1.2.0)", "cachetools (>=5.3.1,<5.4.0)", "pytz (>=2018.6)", "tornado (>=6.3.3,<6.4.0)"]
|
||||
http2 = ["httpx[http2]"]
|
||||
job-queue = ["APScheduler (>=3.10.4,<3.11.0)", "pytz (>=2018.6)"]
|
||||
passport = ["cryptography (>=39.0.1)"]
|
||||
rate-limiter = ["aiolimiter (>=1.1.0,<1.2.0)"]
|
||||
socks = ["httpx[socks]"]
|
||||
webhooks = ["tornado (>=6.2,<7.0)"]
|
||||
webhooks = ["tornado (>=6.3.3,<6.4.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
@ -2631,17 +2769,17 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
||||
|
||||
[[package]]
|
||||
name = "ruamel-yaml"
|
||||
version = "0.17.33"
|
||||
version = "0.17.35"
|
||||
description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
|
||||
optional = false
|
||||
python-versions = ">=3"
|
||||
files = [
|
||||
{file = "ruamel.yaml-0.17.33-py3-none-any.whl", hash = "sha256:2080c7a02b8a30fb3c06727cdf3e254a64055eedf3aa2d17c2b669639c04971b"},
|
||||
{file = "ruamel.yaml-0.17.33.tar.gz", hash = "sha256:5c56aa0bff2afceaa93bffbfc78b450b7dc1e01d5edb80b3a570695286ae62b1"},
|
||||
{file = "ruamel.yaml-0.17.35-py3-none-any.whl", hash = "sha256:b105e3e6fc15b41fdb201ba1b95162ae566a4ef792b9f884c46b4ccc5513a87a"},
|
||||
{file = "ruamel.yaml-0.17.35.tar.gz", hash = "sha256:801046a9caacb1b43acc118969b49b96b65e8847f29029563b29ac61d02db61b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.12\""}
|
||||
"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["ryd"]
|
||||
@ -2834,6 +2972,85 @@ requests = ">=2.26.0"
|
||||
[package.extras]
|
||||
whisper-api = ["openai"]
|
||||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
version = "2.0.21"
|
||||
description = "Database Abstraction Library"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "SQLAlchemy-2.0.21-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e7dc99b23e33c71d720c4ae37ebb095bebebbd31a24b7d99dfc4753d2803ede"},
|
||||
{file = "SQLAlchemy-2.0.21-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f0c4ee579acfe6c994637527c386d1c22eb60bc1c1d36d940d8477e482095d4"},
|
||||
{file = "SQLAlchemy-2.0.21-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f7d57a7e140efe69ce2d7b057c3f9a595f98d0bbdfc23fd055efdfbaa46e3a5"},
|
||||
{file = "SQLAlchemy-2.0.21-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca38746eac23dd7c20bec9278d2058c7ad662b2f1576e4c3dbfcd7c00cc48fa"},
|
||||
{file = "SQLAlchemy-2.0.21-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3cf229704074bce31f7f47d12883afee3b0a02bb233a0ba45ddbfe542939cca4"},
|
||||
{file = "SQLAlchemy-2.0.21-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fb87f763b5d04a82ae84ccff25554ffd903baafba6698e18ebaf32561f2fe4aa"},
|
||||
{file = "SQLAlchemy-2.0.21-cp310-cp310-win32.whl", hash = "sha256:89e274604abb1a7fd5c14867a412c9d49c08ccf6ce3e1e04fffc068b5b6499d4"},
|
||||
{file = "SQLAlchemy-2.0.21-cp310-cp310-win_amd64.whl", hash = "sha256:e36339a68126ffb708dc6d1948161cea2a9e85d7d7b0c54f6999853d70d44430"},
|
||||
{file = "SQLAlchemy-2.0.21-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bf8eebccc66829010f06fbd2b80095d7872991bfe8415098b9fe47deaaa58063"},
|
||||
{file = "SQLAlchemy-2.0.21-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b977bfce15afa53d9cf6a632482d7968477625f030d86a109f7bdfe8ce3c064a"},
|
||||
{file = "SQLAlchemy-2.0.21-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ff3dc2f60dbf82c9e599c2915db1526d65415be323464f84de8db3e361ba5b9"},
|
||||
{file = "SQLAlchemy-2.0.21-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44ac5c89b6896f4740e7091f4a0ff2e62881da80c239dd9408f84f75a293dae9"},
|
||||
{file = "SQLAlchemy-2.0.21-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:87bf91ebf15258c4701d71dcdd9c4ba39521fb6a37379ea68088ce8cd869b446"},
|
||||
{file = "SQLAlchemy-2.0.21-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b69f1f754d92eb1cc6b50938359dead36b96a1dcf11a8670bff65fd9b21a4b09"},
|
||||
{file = "SQLAlchemy-2.0.21-cp311-cp311-win32.whl", hash = "sha256:af520a730d523eab77d754f5cf44cc7dd7ad2d54907adeb3233177eeb22f271b"},
|
||||
{file = "SQLAlchemy-2.0.21-cp311-cp311-win_amd64.whl", hash = "sha256:141675dae56522126986fa4ca713739d00ed3a6f08f3c2eb92c39c6dfec463ce"},
|
||||
{file = "SQLAlchemy-2.0.21-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7614f1eab4336df7dd6bee05bc974f2b02c38d3d0c78060c5faa4cd1ca2af3b8"},
|
||||
{file = "SQLAlchemy-2.0.21-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d59cb9e20d79686aa473e0302e4a82882d7118744d30bb1dfb62d3c47141b3ec"},
|
||||
{file = "SQLAlchemy-2.0.21-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a95aa0672e3065d43c8aa80080cdd5cc40fe92dc873749e6c1cf23914c4b83af"},
|
||||
{file = "SQLAlchemy-2.0.21-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8c323813963b2503e54d0944813cd479c10c636e3ee223bcbd7bd478bf53c178"},
|
||||
{file = "SQLAlchemy-2.0.21-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:419b1276b55925b5ac9b4c7044e999f1787c69761a3c9756dec6e5c225ceca01"},
|
||||
{file = "SQLAlchemy-2.0.21-cp37-cp37m-win32.whl", hash = "sha256:4615623a490e46be85fbaa6335f35cf80e61df0783240afe7d4f544778c315a9"},
|
||||
{file = "SQLAlchemy-2.0.21-cp37-cp37m-win_amd64.whl", hash = "sha256:cca720d05389ab1a5877ff05af96551e58ba65e8dc65582d849ac83ddde3e231"},
|
||||
{file = "SQLAlchemy-2.0.21-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b4eae01faee9f2b17f08885e3f047153ae0416648f8e8c8bd9bc677c5ce64be9"},
|
||||
{file = "SQLAlchemy-2.0.21-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3eb7c03fe1cd3255811cd4e74db1ab8dca22074d50cd8937edf4ef62d758cdf4"},
|
||||
{file = "SQLAlchemy-2.0.21-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2d494b6a2a2d05fb99f01b84cc9af9f5f93bf3e1e5dbdafe4bed0c2823584c1"},
|
||||
{file = "SQLAlchemy-2.0.21-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b19ae41ef26c01a987e49e37c77b9ad060c59f94d3b3efdfdbf4f3daaca7b5fe"},
|
||||
{file = "SQLAlchemy-2.0.21-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fc6b15465fabccc94bf7e38777d665b6a4f95efd1725049d6184b3a39fd54880"},
|
||||
{file = "SQLAlchemy-2.0.21-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:014794b60d2021cc8ae0f91d4d0331fe92691ae5467a00841f7130fe877b678e"},
|
||||
{file = "SQLAlchemy-2.0.21-cp38-cp38-win32.whl", hash = "sha256:0268256a34806e5d1c8f7ee93277d7ea8cc8ae391f487213139018b6805aeaf6"},
|
||||
{file = "SQLAlchemy-2.0.21-cp38-cp38-win_amd64.whl", hash = "sha256:73c079e21d10ff2be54a4699f55865d4b275fd6c8bd5d90c5b1ef78ae0197301"},
|
||||
{file = "SQLAlchemy-2.0.21-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:785e2f2c1cb50d0a44e2cdeea5fd36b5bf2d79c481c10f3a88a8be4cfa2c4615"},
|
||||
{file = "SQLAlchemy-2.0.21-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c111cd40910ffcb615b33605fc8f8e22146aeb7933d06569ac90f219818345ef"},
|
||||
{file = "SQLAlchemy-2.0.21-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9cba4e7369de663611ce7460a34be48e999e0bbb1feb9130070f0685e9a6b66"},
|
||||
{file = "SQLAlchemy-2.0.21-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a69067af86ec7f11a8e50ba85544657b1477aabf64fa447fd3736b5a0a4f67"},
|
||||
{file = "SQLAlchemy-2.0.21-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ccb99c3138c9bde118b51a289d90096a3791658da9aea1754667302ed6564f6e"},
|
||||
{file = "SQLAlchemy-2.0.21-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:513fd5b6513d37e985eb5b7ed89da5fd9e72354e3523980ef00d439bc549c9e9"},
|
||||
{file = "SQLAlchemy-2.0.21-cp39-cp39-win32.whl", hash = "sha256:f9fefd6298433b6e9188252f3bff53b9ff0443c8fde27298b8a2b19f6617eeb9"},
|
||||
{file = "SQLAlchemy-2.0.21-cp39-cp39-win_amd64.whl", hash = "sha256:2e617727fe4091cedb3e4409b39368f424934c7faa78171749f704b49b4bb4ce"},
|
||||
{file = "SQLAlchemy-2.0.21-py3-none-any.whl", hash = "sha256:ea7da25ee458d8f404b93eb073116156fd7d8c2a776d8311534851f28277b4ce"},
|
||||
{file = "SQLAlchemy-2.0.21.tar.gz", hash = "sha256:05b971ab1ac2994a14c56b35eaaa91f86ba080e9ad481b20d99d77f381bb6258"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
|
||||
mypy = {version = ">=0.910", optional = true, markers = "extra == \"mypy\""}
|
||||
typing-extensions = ">=4.2.0"
|
||||
|
||||
[package.extras]
|
||||
aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"]
|
||||
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"]
|
||||
asyncio = ["greenlet (!=0.4.17)"]
|
||||
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"]
|
||||
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"]
|
||||
mssql = ["pyodbc"]
|
||||
mssql-pymssql = ["pymssql"]
|
||||
mssql-pyodbc = ["pyodbc"]
|
||||
mypy = ["mypy (>=0.910)"]
|
||||
mysql = ["mysqlclient (>=1.4.0)"]
|
||||
mysql-connector = ["mysql-connector-python"]
|
||||
oracle = ["cx-oracle (>=7)"]
|
||||
oracle-oracledb = ["oracledb (>=1.0.1)"]
|
||||
postgresql = ["psycopg2 (>=2.7)"]
|
||||
postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
|
||||
postgresql-pg8000 = ["pg8000 (>=1.29.1)"]
|
||||
postgresql-psycopg = ["psycopg (>=3.0.7)"]
|
||||
postgresql-psycopg2binary = ["psycopg2-binary"]
|
||||
postgresql-psycopg2cffi = ["psycopg2cffi"]
|
||||
postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
|
||||
pymysql = ["pymysql"]
|
||||
sqlcipher = ["sqlcipher3-binary"]
|
||||
|
||||
[[package]]
|
||||
name = "stack-data"
|
||||
version = "0.6.3"
|
||||
@ -2953,13 +3170,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "traitlets"
|
||||
version = "5.11.1"
|
||||
version = "5.11.2"
|
||||
description = "Traitlets Python configuration system"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "traitlets-5.11.1-py3-none-any.whl", hash = "sha256:2351372ff87fc912c483d1cb6aa466573d5f44eb4ed9e602c8d0ac012c9daece"},
|
||||
{file = "traitlets-5.11.1.tar.gz", hash = "sha256:813584bb569ac4a098c64ca494e8a3bbfef42e37c36a6132bca554aabd111480"},
|
||||
{file = "traitlets-5.11.2-py3-none-any.whl", hash = "sha256:98277f247f18b2c5cabaf4af369187754f4fb0e85911d473f72329db8a7f4fae"},
|
||||
{file = "traitlets-5.11.2.tar.gz", hash = "sha256:7564b5bf8d38c40fa45498072bf4dc5e8346eb087bbf1e2ae2d8774f6a0f078e"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@ -3012,13 +3229,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "tzlocal"
|
||||
version = "5.0.1"
|
||||
version = "5.1"
|
||||
description = "tzinfo object for the local timezone"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "tzlocal-5.0.1-py3-none-any.whl", hash = "sha256:f3596e180296aaf2dbd97d124fe76ae3a0e3d32b258447de7b939b3fd4be992f"},
|
||||
{file = "tzlocal-5.0.1.tar.gz", hash = "sha256:46eb99ad4bdb71f3f72b7d24f4267753e240944ecfc16f25d2719ba89827a803"},
|
||||
{file = "tzlocal-5.1-py3-none-any.whl", hash = "sha256:2938498395d5f6a898ab8009555cb37a4d360913ad375d4747ef16826b03ef23"},
|
||||
{file = "tzlocal-5.1.tar.gz", hash = "sha256:a5ccb2365b295ed964e0a98ad076fe10c495591e75505d34f154d60a7f1ed722"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3130,7 +3347,94 @@ pyyaml = "*"
|
||||
[package.extras]
|
||||
dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"]
|
||||
|
||||
[[package]]
|
||||
name = "yarl"
|
||||
version = "1.9.2"
|
||||
description = "Yet another URL library"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"},
|
||||
{file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"},
|
||||
{file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"},
|
||||
{file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"},
|
||||
{file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"},
|
||||
{file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"},
|
||||
{file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
idna = ">=2.0"
|
||||
multidict = ">=4.0"
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "ab644b9882ee200392911afc6b71bf87fdb413e4fdd9f06a460ce33da98687d7"
|
||||
content-hash = "15f814322c6f006b5345d8579bb42198bb7c4953d595ea9662f9e7cae78a979f"
|
||||
|
@ -8,9 +8,9 @@ authors = ["Dmitry Afanasyev <Balshbox@gmail.com>"]
|
||||
python = "^3.11"
|
||||
|
||||
fastapi = "^0.103.1"
|
||||
python-telegram-bot = {version = "^20.5", extras=["ext"]}
|
||||
python-telegram-bot = {version = "^20.6", extras=["ext"]}
|
||||
python-dotenv = "^1.0"
|
||||
httpx = "^0.24"
|
||||
httpx = "^0.25"
|
||||
loguru = "^0.7"
|
||||
pydantic = "^2.4"
|
||||
pydantic-settings = "^2.0.3"
|
||||
@ -21,8 +21,12 @@ orjson = "^3.9"
|
||||
sentry-sdk = "^1.31.0"
|
||||
SpeechRecognition = "^3.8"
|
||||
pydub = "^0.25"
|
||||
greenlet = "^2.0.2"
|
||||
greenlet = "^3.0"
|
||||
graypy = "^2.1.0"
|
||||
aiosqlite = "^0.19.0"
|
||||
yarl = "^1.9.2"
|
||||
sqlalchemy = {version = "^2.0.21", extras=["mypy"]}
|
||||
alembic = "^1.12.0"
|
||||
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
@ -159,7 +163,11 @@ warn_unused_configs = true
|
||||
warn_unreachable = true
|
||||
warn_no_return = true
|
||||
exclude = [
|
||||
"chat_gpt_microservice"
|
||||
"chat_gpt_microservice",
|
||||
"bot_microservice/infra/database/migrations/versions/*"
|
||||
]
|
||||
plugins = [
|
||||
"sqlalchemy.ext.mypy.plugin",
|
||||
]
|
||||
|
||||
[tool.black]
|
||||
|
@ -1,5 +1,9 @@
|
||||
#! /bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
alembic upgrade "head"
|
||||
|
||||
echo "starting the bot"
|
||||
|
||||
if [[ "${START_WITH_WEBHOOK}" == "true" ]]
|
||||
|
Loading…
x
Reference in New Issue
Block a user