init
This commit is contained in:
44
api.py
Normal file
44
api.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
import deepseek_module
|
||||||
|
import nlp_processor
|
||||||
|
import nlp_teacher
|
||||||
|
|
||||||
|
app = FastAPI(title="NLP API", version="1.0")
|
||||||
|
|
||||||
|
# Загрузить сохраненную модель
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.get('/predict')
|
||||||
|
async def predict(req, cloud:int=0, async_op: int=0):
|
||||||
|
print('here')
|
||||||
|
# Получить входные данные из запроса
|
||||||
|
if cloud==1:
|
||||||
|
if async_op==1:
|
||||||
|
response_msg = await deepseek_module.nlp_yandex(req)
|
||||||
|
else:
|
||||||
|
response_msg = deepseek_module.fast_nlp_yandex(req)
|
||||||
|
print(response_msg)
|
||||||
|
return json.loads(response_msg.encode('latin1').decode('utf-8'))
|
||||||
|
else:
|
||||||
|
# Сделать предсказание модели
|
||||||
|
prediction = nlp_processor.predict_category(req)
|
||||||
|
|
||||||
|
# Вернуть предсказание в виде JSON
|
||||||
|
return prediction
|
||||||
|
|
||||||
|
|
||||||
|
@app.get('/reteach')
|
||||||
|
async def reteach():
|
||||||
|
print(nlp_teacher.reteach())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import uvicorn
|
||||||
|
uvicorn.run(app, port=8000)
|
||||||
169
deepseek_module.py
Normal file
169
deepseek_module.py
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
import asyncio
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
from yandex_cloud_ml_sdk import YCloudML
|
||||||
|
from deepseek import DeepSeekAPI
|
||||||
|
|
||||||
|
# Инициализация клиентов
|
||||||
|
sdk = YCloudML(
|
||||||
|
folder_id="b1g9ebhd2j6dapolmspn",
|
||||||
|
auth="t1.9euelZqOxsiNmczGlYmVjZqPnciemu3rnpWamMbLiY7Ix8zHyMvPlZGJmc3l9PcLQDFA-e93IjGb3fT3S24uQPnvdyIxm83n9euelZqSyo6cyJ2PnMjInceWmI6QnO_8xeuelZqSyo6cyJ2PnMjInceWmI6QnA.73RaOj7lFKgEkAFnhrpy1LkS3EvH-hYeBPZ1mX43hyPgSCZ4ShrVFjarra1xCHGpRA3F_Z576viaMyg6UXR-BQ"
|
||||||
|
)
|
||||||
|
|
||||||
|
api_client = DeepSeekAPI("sk-2a2045f744474426b03fc18e30e9f561")
|
||||||
|
|
||||||
|
|
||||||
|
async def nlp_yandex(question: str) -> str:
|
||||||
|
"""Асинхронный запрос к Yandex GPT"""
|
||||||
|
start_time = datetime.datetime.now()
|
||||||
|
try:
|
||||||
|
model = sdk.models.completions("yandexgpt", model_version="rc")
|
||||||
|
model.configure(response_format="json", temperature=1)
|
||||||
|
|
||||||
|
operation = await model._run_deferred([
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"text": "Ты используешься как nlp процессор. Твоя задача по заданному комментарию траты понять в какую категорию эта трата относится. "
|
||||||
|
"Ответ предположением 3х категории с рассчитанным весом в формате json [{'category': 'cat1', 'weight': 0.5}, ...]."
|
||||||
|
"Используй категории из данных."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"text": "Aвто,Аптеки,Дни рождения и подарки,Другое,ЖКХ,Интернет и связь,Кофе,Красота,Кредиты и долги,Мебель,Медицина,Одежда,Продукты,Прочие услуги,Путешествия,Развлечения,Ребенок,Рестораны и кафе,Сбережения,Спорт,Транспорт,Электроника"
|
||||||
|
},
|
||||||
|
{"role": "user", "text": question},
|
||||||
|
])
|
||||||
|
|
||||||
|
while operation.get_status().is_running:
|
||||||
|
print(operation.get_status())
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
|
result = operation.get_result()
|
||||||
|
response_msg = "".join(alt.text for alt in result)
|
||||||
|
|
||||||
|
print(f"Yandex NLP time: {datetime.datetime.now() - start_time}")
|
||||||
|
return response_msg.replace('```', '')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Yandex NLP error: {str(e)}")
|
||||||
|
return "Ошибка обработки запроса"
|
||||||
|
|
||||||
|
|
||||||
|
def fast_nlp_yandex(question: str) -> str:
|
||||||
|
"""Cинхронный запрос к Yandex GPT"""
|
||||||
|
start_time = datetime.datetime.now()
|
||||||
|
try:
|
||||||
|
model = sdk.models.completions("yandexgpt", model_version="rc")
|
||||||
|
model.configure(response_format="json", temperature=1)
|
||||||
|
|
||||||
|
result = model.run([
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"text": "Ты используешься как nlp процессор. Твоя задача по заданному комментарию траты понять в какую категорию эта трата относится. "
|
||||||
|
"Ответ предположением 3х категории с рассчитанным весом в формате json [{'category': 'cat1', 'weight': 0.5}, ...]."
|
||||||
|
"Используй категории из данных."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"text": "Aвто,Аптеки,Дни рождения и подарки,Другое,ЖКХ,Интернет и связь,Кофе,Красота,Кредиты и долги,Мебель,Медицина,Одежда,Продукты,Прочие услуги,Путешествия,Развлечения,Ребенок,Рестораны и кафе,Сбережения,Спорт,Транспорт,Электроника"
|
||||||
|
},
|
||||||
|
{"role": "user", "text": question},
|
||||||
|
])
|
||||||
|
|
||||||
|
response_msg = "".join(alt.text for alt in result)
|
||||||
|
|
||||||
|
print(f"Sync Yandex NLP time: {datetime.datetime.now() - start_time}")
|
||||||
|
return response_msg.replace('```', '')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Yandex NLP error: {str(e)}")
|
||||||
|
return "Ошибка обработки запроса"
|
||||||
|
|
||||||
|
|
||||||
|
def nlp_cloud(question: str) -> str:
|
||||||
|
"""Запрос к DeepSeek API"""
|
||||||
|
start_time = datetime.datetime.now()
|
||||||
|
try:
|
||||||
|
prompt = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "Ты используешься как nlp процессор. Твоя задача по заданному комментарию траты понять в какую категорию эта трата относится. "
|
||||||
|
"Ответ предположением как минимум 3х категории с рассчитанным весом в формате json [{'id': 'id1', 'weight': 0.5}, ...]. "
|
||||||
|
"Используй ТОЛЬКО категории из данных."
|
||||||
|
},
|
||||||
|
{"role": "user", "content": question},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "Aвто(id=677bc767c7857460a491bd52),Аптеки(id=677bc767c7857460a491bd59),Дни рождения и подарки(id=677bc767c7857460a491bd54),Другое(id=67b83e141fc0575a3f0a383f),ЖКХ(id=677bc767c7857460a491bd4b),Интернет и связь(id=677bc767c7857460a491bd53),Кофе(id=677bc767c7857460a491bd4e),Красота(id=677bc767c7857460a491bd50),Кредиты и долги(id=677bc767c7857460a491bd49),Мебель(id=677bc767c7857460a491bd4c),Медицина(id=677bc767c7857460a491bd5a),Одежда(id=677bc767c7857460a491bd5b),Продукты(id=677bc767c7857460a491bd4a),Прочие услуги(id=677bc767c7857460a491bd4d),Путешествия(id=677bc767c7857460a491bd56),Развлечения(id=677bc767c7857460a491bd58),Ребенок(id=677bc767c7857460a491bd5d),Рестораны и кафе(id=677bc767c7857460a491bd55),Сбережения(id=677bc767c7857460a491bd4f),Спорт(id=677bc767c7857460a491bd57),Транспорт(id=677bc767c7857460a491bd51),Электроника(id=677bc767c7857460a491bd5c)"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
response = ""
|
||||||
|
for chunk in api_client.chat_completion(prompt=prompt, stream=True):
|
||||||
|
response += chunk
|
||||||
|
|
||||||
|
# Очистка и парсинг JSON
|
||||||
|
clean_response = response.replace('```json', '').replace('```', '').strip()
|
||||||
|
try:
|
||||||
|
parsed = json.loads(clean_response)
|
||||||
|
print(f"DeepSeek NLP time: {datetime.datetime.now() - start_time}")
|
||||||
|
return json.dumps(parsed, ensure_ascii=False, indent=2)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return clean_response
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"DeepSeek NLP error: {str(e)}")
|
||||||
|
return json.dumps({"error": str(e)})
|
||||||
|
|
||||||
|
|
||||||
|
def generate_spends():
|
||||||
|
"""Запрос к DeepSeek API"""
|
||||||
|
start_time = datetime.datetime.now()
|
||||||
|
try:
|
||||||
|
prompt = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "Ты используешься для генерации виртуальных трат, для последующего обучения NLP модели. "
|
||||||
|
"Тебе даны категории с их идентификаторами. По запросу сгенерируй 50 человеческих транзакций."
|
||||||
|
"Используй ТОЛЬКО категории из данных.Ответь в формате csv id, comment, category"
|
||||||
|
},
|
||||||
|
{"role": "user", "content": "Давай"},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "Aвто(id=677bc767c7857460a491bd52),Аптеки(id=677bc767c7857460a491bd59),Дни рождения и подарки(id=677bc767c7857460a491bd54),Другое(id=67b83e141fc0575a3f0a383f),ЖКХ(id=677bc767c7857460a491bd4b),Интернет и связь(id=677bc767c7857460a491bd53),Кофе(id=677bc767c7857460a491bd4e),Красота(id=677bc767c7857460a491bd50),Кредиты и долги(id=677bc767c7857460a491bd49),Мебель(id=677bc767c7857460a491bd4c),Медицина(id=677bc767c7857460a491bd5a),Одежда(id=677bc767c7857460a491bd5b),Продукты(id=677bc767c7857460a491bd4a),Прочие услуги(id=677bc767c7857460a491bd4d),Путешествия(id=677bc767c7857460a491bd56),Развлечения(id=677bc767c7857460a491bd58),Ребенок(id=677bc767c7857460a491bd5d),Рестораны и кафе(id=677bc767c7857460a491bd55),Сбережения(id=677bc767c7857460a491bd4f),Спорт(id=677bc767c7857460a491bd57),Транспорт(id=677bc767c7857460a491bd51),Электроника(id=677bc767c7857460a491bd5c)"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
response = ""
|
||||||
|
for chunk in api_client.chat_completion(prompt=prompt, stream=True):
|
||||||
|
response += chunk
|
||||||
|
|
||||||
|
# Очистка и парсинг JSON
|
||||||
|
clean_response = response.replace('```json', '').replace('```', '').strip()
|
||||||
|
try:
|
||||||
|
parsed = json.loads(clean_response)
|
||||||
|
print(f"DeepSeek process time: {datetime.datetime.now() - start_time}")
|
||||||
|
return json.dumps(parsed, ensure_ascii=False, indent=2)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return clean_response
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"DeepSeek NLP error: {str(e)}")
|
||||||
|
return json.dumps({"error": str(e)})
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
# Тестирование функций
|
||||||
|
# req = 'мойка'
|
||||||
|
# yandex_result = fast_nlp_yandex(req)
|
||||||
|
# print("Yandex:", yandex_result)
|
||||||
|
#
|
||||||
|
# cloud_result = nlp_cloud(req)
|
||||||
|
# print("DeepSeek:", cloud_result)
|
||||||
|
print( generate_spends())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
59
nlp_processor.py
Normal file
59
nlp_processor.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
import joblib
|
||||||
|
import nltk
|
||||||
|
import numpy as np
|
||||||
|
from nltk import word_tokenize
|
||||||
|
from nltk.corpus import stopwords
|
||||||
|
|
||||||
|
|
||||||
|
nltk.download('stopwords')
|
||||||
|
nltk.download('punkt_tab')
|
||||||
|
|
||||||
|
model, vectorizer = joblib.load('model.pkl')
|
||||||
|
|
||||||
|
|
||||||
|
def model_reload():
|
||||||
|
print('Reloading the model')
|
||||||
|
global model, vectorizer
|
||||||
|
model, vectorizer = joblib.load('model.pkl')
|
||||||
|
|
||||||
|
def preprocess_comment(comment):
|
||||||
|
tokens = word_tokenize(comment)
|
||||||
|
stop_words = set(stopwords.words('russian'))
|
||||||
|
filtered_tokens = [t for t in tokens if t.lower() not in stop_words]
|
||||||
|
return ' '.join(filtered_tokens)
|
||||||
|
|
||||||
|
|
||||||
|
def comment_to_vector(comment):
|
||||||
|
vector = vectorizer.transform([comment])
|
||||||
|
return vector
|
||||||
|
|
||||||
|
|
||||||
|
# Определение категории по комменту
|
||||||
|
def predict_category(comment):
|
||||||
|
# Преобразуем текст в вектор
|
||||||
|
vector = vectorizer.transform([comment])
|
||||||
|
|
||||||
|
# Получаем "вероятности" через decision function
|
||||||
|
decision_scores = model.decision_function(vector)
|
||||||
|
|
||||||
|
# Преобразуем scores в "псевдо-вероятности" через softmax
|
||||||
|
exp_scores = np.exp(decision_scores - np.max(decision_scores))
|
||||||
|
probabilities = exp_scores / np.sum(exp_scores)
|
||||||
|
|
||||||
|
# Получаем топ-3 категорий
|
||||||
|
top_3_indices = np.argsort(probabilities[0])[::-1][:3]
|
||||||
|
top_3_categories = [
|
||||||
|
{'category': model.classes_[i].encode('latin1').decode('utf-8'), 'weight': float(probabilities[0][i])}
|
||||||
|
for i in top_3_indices
|
||||||
|
]
|
||||||
|
|
||||||
|
return top_3_categories
|
||||||
|
|
||||||
|
|
||||||
|
# Тестирование
|
||||||
|
comment = "ремешок часы" # Пример комментария
|
||||||
|
result = predict_category(comment)
|
||||||
|
for item in result:
|
||||||
|
print(f"Категория: {item['category']}, Вес: {item['weight']:.4f}")
|
||||||
119
nlp_teacher.py
Normal file
119
nlp_teacher.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
import joblib
|
||||||
|
import nltk
|
||||||
|
import pandas as pd
|
||||||
|
import requests
|
||||||
|
from nltk.tokenize import word_tokenize
|
||||||
|
from nltk.corpus import stopwords
|
||||||
|
|
||||||
|
from pymorphy2 import MorphAnalyzer
|
||||||
|
from requests.compat import chardet
|
||||||
|
|
||||||
|
from sklearn.feature_extraction.text import TfidfVectorizer
|
||||||
|
|
||||||
|
from sklearn.metrics import accuracy_score
|
||||||
|
from sklearn.model_selection import train_test_split
|
||||||
|
|
||||||
|
from sklearn.svm import LinearSVC
|
||||||
|
|
||||||
|
import nlp_processor
|
||||||
|
|
||||||
|
nltk.download('stopwords')
|
||||||
|
nltk.download('punkt_tab')
|
||||||
|
morph = MorphAnalyzer()
|
||||||
|
|
||||||
|
|
||||||
|
def fix_encoding(text):
|
||||||
|
if isinstance(text, str):
|
||||||
|
try:
|
||||||
|
# Преобразуем неправильно декодированные строки
|
||||||
|
return text.encode('latin1').decode('utf-8')
|
||||||
|
except:
|
||||||
|
return text
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
# Загрузка данных
|
||||||
|
|
||||||
|
def reteach() -> str:
|
||||||
|
# URL вашего API, возвращающего CSV
|
||||||
|
api_url = "https://luminic.space/api/spaces/67af3c0f652da946a7dd9931/transactions/csv"
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Authorization": "Bearer eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJ2b3JvbmludiIsImlhdCI6MTc0NDAzMDk5MywiZXhwIjoxNzQ0ODk0OTkzfQ.mybRBMsjPfJ8_kbDfJiPSr8UOIWS6WYddSFptplBTIfnPQm9SsdH3ixlEM-UY0CQ",
|
||||||
|
}
|
||||||
|
# Отправляем GET-запрос
|
||||||
|
response = requests.get(api_url, headers=headers)
|
||||||
|
|
||||||
|
print(response)
|
||||||
|
# Проверяем успешность запроса
|
||||||
|
if response.status_code == 200:
|
||||||
|
|
||||||
|
encoding = chardet.detect(response.content)['encoding']
|
||||||
|
print(f"Detected encoding: {encoding}")
|
||||||
|
# Используем StringIO для преобразования текста в файлоподобный объект
|
||||||
|
csv_data = StringIO(response.text)
|
||||||
|
|
||||||
|
# Читаем CSV в DataFrame
|
||||||
|
data = pd.read_csv(csv_data)
|
||||||
|
# result = chardet.detect(data["comment"])
|
||||||
|
# print(result)
|
||||||
|
print(data.head(3))
|
||||||
|
|
||||||
|
# Токенизация и удаление стоп-слов
|
||||||
|
stop_words = set(stopwords.words('russian'))
|
||||||
|
|
||||||
|
def tokenize_text(text):
|
||||||
|
text = re.sub(r'\d+([.,]\d+)?\s*(р|руб|коп)?', '', text)
|
||||||
|
|
||||||
|
# Лемматизация с обработкой денежных единиц
|
||||||
|
tokens = []
|
||||||
|
for token in word_tokenize(text.lower()):
|
||||||
|
if token in {'$', '€', '₽'}:
|
||||||
|
continue
|
||||||
|
parsed = morph.parse(token)[0]
|
||||||
|
if parsed.tag.POS in {'NOUN', 'VERB', 'ADJ', 'ADV'}:
|
||||||
|
tokens.append(parsed.normal_form)
|
||||||
|
|
||||||
|
return ' '.join([t for t in tokens if t not in stop_words and len(t) > 2])
|
||||||
|
|
||||||
|
data['comment'] = data['comment'].apply(fix_encoding)
|
||||||
|
print(data.head(3))
|
||||||
|
data['processed_comment'] = data['comment'].apply(tokenize_text)
|
||||||
|
|
||||||
|
# Нормализация и удаление лишних символов
|
||||||
|
data['processed_comment'] = data['processed_comment'].apply(lambda x: x.lower())
|
||||||
|
data['processed_comment'] = data['processed_comment'].apply(lambda x: x.replace(',', ''))
|
||||||
|
data['processed_comment'] = data['processed_comment'].apply(lambda x: x.replace('.', ''))
|
||||||
|
|
||||||
|
# Преобразование текста в числовой вектор
|
||||||
|
vectorizer = TfidfVectorizer(
|
||||||
|
ngram_range=(1, 3),
|
||||||
|
max_features=15000,
|
||||||
|
min_df=2,
|
||||||
|
max_df=0.8,
|
||||||
|
analyzer='char_wb', # Важно для опечаток!
|
||||||
|
sublinear_tf=True
|
||||||
|
)
|
||||||
|
X = vectorizer.fit_transform(data['processed_comment'])
|
||||||
|
# Разделение данных на обучающую и тестовую выборки
|
||||||
|
X_train, X_test, y_train, y_test = train_test_split(X, data['category'], test_size=0.2, random_state=42)
|
||||||
|
|
||||||
|
# Обучение модели классификации
|
||||||
|
model = LinearSVC()
|
||||||
|
model.fit(X_train, y_train)
|
||||||
|
|
||||||
|
# Оценка модели
|
||||||
|
|
||||||
|
y_pred = model.predict(X_test)
|
||||||
|
print(f"Accuracy: {accuracy_score(y_test, y_pred):.2f}")
|
||||||
|
|
||||||
|
# Сохранение модели в файл
|
||||||
|
|
||||||
|
# Сохранение модели в файл
|
||||||
|
joblib.dump((model, vectorizer), 'model.pkl')
|
||||||
|
nlp_processor.model_reload()
|
||||||
|
return accuracy_score(y_test, y_pred)
|
||||||
64
requirements.txt
Normal file
64
requirements.txt
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
aiofiles==24.1.0
|
||||||
|
annotated-types==0.7.0
|
||||||
|
anyio==4.9.0
|
||||||
|
asgiref==3.8.1
|
||||||
|
blinker==1.9.0
|
||||||
|
certifi==2025.1.31
|
||||||
|
cffi==1.17.1
|
||||||
|
charset-normalizer==3.4.1
|
||||||
|
click==8.1.8
|
||||||
|
cryptography==44.0.2
|
||||||
|
DAWG-Python==0.7.2
|
||||||
|
deepseek==1.0.0
|
||||||
|
Deprecated==1.2.18
|
||||||
|
distro==1.9.0
|
||||||
|
docopt==0.6.2
|
||||||
|
exceptiongroup==1.2.2
|
||||||
|
fastapi==0.115.12
|
||||||
|
Flask==3.1.0
|
||||||
|
get-annotations==0.1.2
|
||||||
|
googleapis-common-protos==1.69.2
|
||||||
|
grpcio==1.71.0
|
||||||
|
grpcio-tools==1.71.0
|
||||||
|
h11==0.14.0
|
||||||
|
httpcore==1.0.7
|
||||||
|
httpx==0.28.1
|
||||||
|
idna==3.10
|
||||||
|
importlib_metadata==8.6.1
|
||||||
|
itsdangerous==2.2.0
|
||||||
|
Jinja2==3.1.6
|
||||||
|
jiter==0.9.0
|
||||||
|
joblib==1.4.2
|
||||||
|
MarkupSafe==3.0.2
|
||||||
|
nltk==3.9.1
|
||||||
|
numpy==2.0.2
|
||||||
|
openai==1.70.0
|
||||||
|
pandas==2.2.3
|
||||||
|
protobuf==5.29.4
|
||||||
|
pycparser==2.22
|
||||||
|
pydantic==2.11.2
|
||||||
|
pydantic_core==2.33.1
|
||||||
|
PyJWT==2.10.1
|
||||||
|
pymorphy2==0.9.1
|
||||||
|
pymorphy2-dicts-ru==2.4.417127.4579844
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
pytz==2025.2
|
||||||
|
regex==2024.11.6
|
||||||
|
requests==2.32.3
|
||||||
|
scikit-learn==1.6.1
|
||||||
|
scipy==1.13.1
|
||||||
|
six==1.17.0
|
||||||
|
sniffio==1.3.1
|
||||||
|
starlette==0.46.1
|
||||||
|
threadpoolctl==3.6.0
|
||||||
|
tqdm==4.67.1
|
||||||
|
typing-inspection==0.4.0
|
||||||
|
typing_extensions==4.13.1
|
||||||
|
tzdata==2025.2
|
||||||
|
urllib3==2.3.0
|
||||||
|
uvicorn==0.34.0
|
||||||
|
Werkzeug==3.1.3
|
||||||
|
wrapt==1.17.2
|
||||||
|
yandex-cloud-ml-sdk==0.7.0
|
||||||
|
yandexcloud==0.337.0
|
||||||
|
zipp==3.21.0
|
||||||
Reference in New Issue
Block a user