Skip to content

Commit

Permalink
add upload file
Browse files Browse the repository at this point in the history
  • Loading branch information
anntish committed Sep 11, 2024
1 parent 1c7be39 commit 7a9e642
Show file tree
Hide file tree
Showing 11 changed files with 165 additions and 12 deletions.
2 changes: 2 additions & 0 deletions services/backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from routes.message.get_message import get_user_messages
from routes.conversation.delete_conversation import delete_message
from routes.conversation.get_conversations import get_conversation
from routes.message.upload_file import send_message_with_file

broker_url = os.environ.get('BROKER_URL')
result_backend = os.environ.get('RESULT_BACKEND')
Expand Down Expand Up @@ -47,6 +48,7 @@ def create_app():
app.add_url_rule('/message', view_func=send_message, methods=['POST'])
app.add_url_rule('/conversation/create', view_func=create_conversation, methods=['POST'])
app.add_url_rule('/conversations/get', view_func=get_conversation, methods=['POST'])
app.add_url_rule('/message/upload', view_func=send_message_with_file, methods=['POST'])
app.add_url_rule('/task/<id>', view_func=task_result, methods=['GET'])
app.add_url_rule('/messages/<conversation_id>', view_func=get_user_messages, methods=['GET'])
app.add_url_rule('/conversation/delete/<conversation_id>', view_func=delete_message, methods=['DELETE'])
Expand Down
10 changes: 10 additions & 0 deletions services/backend/models/file_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from models import db

class FileModel(db.Model):
__tablename__ = 'files'

id = db.Column(db.Integer, primary_key=True)
file_path = db.Column(db.String, nullable=False) # Путь к файлу

def __repr__(self):
return f"<FileMessage(id={self.id}, filepath={self.filepath})>"
3 changes: 2 additions & 1 deletion services/backend/models/message_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class MessageModel(db.Model):
id = db.Column(db.Integer, primary_key=True)

conversation_id = db.Column(db.Integer, db.ForeignKey('conversations.id'), nullable=False)
file_id = db.Column(db.Integer, db.ForeignKey('files.id'), nullable=True)
task_id = db.Column(db.String, nullable=True)

role = db.Column(db.Integer, nullable=False) # 1 - 'system' или 2 - 'user'
Expand All @@ -16,4 +17,4 @@ class MessageModel(db.Model):
updated_at = db.Column(db.DateTime, default=db.func.current_timestamp(), onupdate=db.func.current_timestamp())

def __repr__(self):
return f"<Message(id={self.id}, conversation_id={self.conversation_id} role={self.role}, text={self.text})>"
return f"<Message(id={self.id}, conversation_id={self.conversation_id}, file_id={self.file_id}, role={self.role}, text={self.text})>"
1 change: 1 addition & 0 deletions services/backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ flasgger==0.9.7.1
Flask==3.0.3
Flask-Cors==5.0.0
Flask-SQLAlchemy==3.1.1
flask-wtf==1.2.1
greenlet==3.0.3
idna==3.8
itsdangerous==2.2.0
Expand Down
34 changes: 34 additions & 0 deletions services/backend/routes/message/upload_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import traceback
from flask import request, jsonify, Blueprint
from services.conversation_service import ConversationService

from services.file_service import FileService

api = Blueprint('api', __name__)


@api.route('/message/upload', methods=['POST'])
def send_message_with_file():
"""
Создает задачу с учетом контекста вопроса пользователя к отправленному файлу.
Принимает conversation_id, message и файл.
Возвращает ID созданной задачи.
"""

conversation_id = request.form.get('conversation_id')
message = request.form.get('message')
file = request.files.get('file')

if not conversation_id or not message or not file:
return jsonify({"error": "Missing required fields"}), 400


file = FileService.save_file(file)

try:
task_id = ConversationService.from_id(int(conversation_id)).handle_file(file, message)
return jsonify({"task_id": task_id}), 202

except Exception as e:
print(traceback.format_exc())
return jsonify({"error": str(e)}), 400
14 changes: 14 additions & 0 deletions services/backend/services/conversation_service.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import os
import uuid
from flask import jsonify
from models import db
from models.conversation_model import ConversationModel
from models.file_model import FileModel
from services.message_service import MessageService, SaveMessageOptions, Role
from services.rag_service import RagService

project_root = os.path.dirname(os.path.abspath(__file__))

class ConversationService:

Expand Down Expand Up @@ -40,6 +43,17 @@ def handle_message(self, text: str) -> str:

return task_id

def handle_file(self, file: FileModel, text: str) -> str:

task_id = RagService.create_file_question_task(file, text)

MessageService.save_message(
SaveMessageOptions(text=text, task_id=task_id, conversation_id=self.model.id,
role=Role.USER, file_id=file.id)
)

return task_id

def delete_conversation(self):

delete_result, code = MessageService.delete_message(self.model.id)
Expand Down
32 changes: 32 additions & 0 deletions services/backend/services/file_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import os

from werkzeug.utils import secure_filename

from models import db
from models.file_model import FileModel

UPLOAD_FOLDER = 'uploads'

class FileService:

@staticmethod
def check_upload_folder(self):
if not os.path.exists(UPLOAD_FOLDER):
os.makedirs(UPLOAD_FOLDER)


@staticmethod
def save_file(file):

filename = secure_filename(file.filename)
filepath = os.path.join(UPLOAD_FOLDER, filename)
file.save(filepath)

new_file_message = FileModel(
file_path=filepath
)

db.session.add(new_file_message)
db.session.commit()

return new_file_message
6 changes: 4 additions & 2 deletions services/backend/services/message_service.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from collections import defaultdict
from dataclasses import dataclass
from enum import Enum
from flask import jsonify
from typing import Optional
from models import db
from models.message_model import MessageModel

Expand All @@ -16,6 +16,7 @@ class SaveMessageOptions:
conversation_id: int
task_id: str
role: Role = Role.SYSTEM
file_id: Optional[int] = None


class MessageService:
Expand All @@ -39,7 +40,8 @@ def save_message(options: SaveMessageOptions) -> MessageModel:
text = options.text,
conversation_id = options.conversation_id,
task_id = options.task_id,
role = options.role.value
role = options.role.value,
file_id = options.file_id
)

db.session.add(new_message)
Expand Down
63 changes: 59 additions & 4 deletions services/backend/services/rag_service.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import json
import os
import requests
from celery import shared_task, Task

from models.file_model import FileModel
from services.message_service import MessageService, SaveMessageOptions, Role

rag_url = os.environ.get('HOST')


class QuestionTask(Task):
def before_start(self, task_id, args, kwargs):
print('before_start called')
Expand All @@ -19,8 +23,6 @@ def on_failure(self, exc, task_id, args, kwargs, einfo):
super().on_failure(exc, task_id, args, kwargs, einfo)

def on_success(self, retval, task_id, args, kwargs):
print('on_success called')
print(retval)

message = MessageService.from_task_id(task_id).model

Expand All @@ -30,18 +32,45 @@ def on_success(self, retval, task_id, args, kwargs):

super().on_success(retval, task_id, args, kwargs)

class QuestionTaskWithFile(Task):
def before_start(self, task_id, args, kwargs):
print('before_start called')
super().before_start(task_id, args, kwargs)

def after_return(self, status, retval, task_id, args, kwargs, einfo):
print('after_return called')
super().after_return(status, retval, task_id, args, kwargs, einfo)

def on_failure(self, exc, task_id, args, kwargs, einfo):
print('on_failure called')
super().on_failure(exc, task_id, args, kwargs, einfo)

def on_success(self, retval, task_id, args, kwargs):
print('on_success called')
print(retval)

message = MessageService.from_task_id(task_id).model

MessageService.save_message(
SaveMessageOptions(text=retval['result'], task_id=task_id, conversation_id=message.conversation_id, role=Role.SYSTEM, file_id = retval['file_id'])
)

super().on_success(retval, task_id, args, kwargs)

class RagService:
@staticmethod
def create_task(question: str) -> str:
return run_question_task.delay(question).id

@staticmethod
def create_file_question_task(file: FileModel, question: str) -> str:
return run_file_question_task.delay(file.id, file.file_path, question).id



@shared_task(base = QuestionTask,ignore_result=False)
def run_question_task(question: str):

try:

data = {
"input":{
"question": "Какие документы по строительству объектов есть в базе?",
Expand All @@ -57,3 +86,29 @@ def run_question_task(question: str):

except requests.exceptions.RequestException as e:
return {'error': str(e)}


@shared_task(base = QuestionTaskWithFile,ignore_result=False)
def run_file_question_task(file_id, file_path, question: str) -> dict:
try:
result = {
"result": f'Ответ на вопрос с файлом: {question}',
"file_id": file_id,
"file_path": file_path
}

json.dumps(result)
return result
#
# with open(filepath, 'rb') as file:
# files = {'file': file}
# data = {'text': question}
#
# response = requests.post(rag_url, files=files, data=data)
# response.raise_for_status()
#
# result = response.json()
# return result

except requests.exceptions.RequestException as e:
return {'error': str(e)}
Empty file.
12 changes: 7 additions & 5 deletions services/backend/validators/upload_request.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from wtforms import Form, StringField, validators, IntegerField
from flask_wtf import FlaskForm
from wtforms import IntegerField, StringField, FileField
from wtforms.validators import DataRequired, Length


class UploadForm(Form):
conversation_id = IntegerField('conversation_id')
question = StringField('question', [validators.Length(min=1)])
class UploadForm(FlaskForm):
conversation_id = IntegerField('Conversation ID', validators=[DataRequired()])
message = StringField('Message', validators=[DataRequired(), Length(min=1)])
file = FileField('File', validators=[DataRequired()])

0 comments on commit 7a9e642

Please sign in to comment.