SOY NV AI
Add Gemini API integration with REST API support, improve error handling, and add markdown bold formatting for messages
665bcdc
| from flask_sqlalchemy import SQLAlchemy | |
| from flask_login import UserMixin | |
| from datetime import datetime | |
| from werkzeug.security import generate_password_hash, check_password_hash | |
| db = SQLAlchemy() | |
| # ์ฌ์ฉ์ ๋ชจ๋ธ | |
| class User(UserMixin, db.Model): | |
| id = db.Column(db.Integer, primary_key=True) | |
| username = db.Column(db.String(80), unique=True, nullable=False) | |
| nickname = db.Column(db.String(80), nullable=True) # ๋๋ค์ ํ๋ ์ถ๊ฐ | |
| password_hash = db.Column(db.String(255), nullable=False) | |
| is_admin = db.Column(db.Boolean, default=False, nullable=False) | |
| is_active = db.Column(db.Boolean, default=True, nullable=False) | |
| created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) | |
| last_login = db.Column(db.DateTime, nullable=True) | |
| def set_password(self, password): | |
| self.password_hash = generate_password_hash(password) | |
| def check_password(self, password): | |
| return check_password_hash(self.password_hash, password) | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'username': self.username, | |
| 'nickname': self.nickname, | |
| 'is_admin': self.is_admin, | |
| 'is_active': self.is_active, | |
| 'created_at': self.created_at.isoformat() if self.created_at else None, | |
| 'last_login': self.last_login.isoformat() if self.last_login else None | |
| } | |
| # ์ ๋ก๋๋ ํ์ผ ์ ๋ณด ๋ชจ๋ธ | |
| class UploadedFile(db.Model): | |
| id = db.Column(db.Integer, primary_key=True) | |
| filename = db.Column(db.String(255), nullable=False) | |
| original_filename = db.Column(db.String(255), nullable=False) | |
| file_path = db.Column(db.String(500), nullable=False) | |
| file_size = db.Column(db.Integer, nullable=False) | |
| model_name = db.Column(db.String(100), nullable=True) # ์ฐ๊ฒฐ๋ ๋ชจ๋ธ ์ด๋ฆ | |
| uploaded_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) | |
| uploaded_by = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True) | |
| parent_file_id = db.Column(db.Integer, db.ForeignKey('uploaded_file.id'), nullable=True) # ์ด์ด์ ์ ๋ก๋ํ ๊ฒฝ์ฐ ์๋ณธ ํ์ผ ID | |
| # ๊ด๊ณ | |
| parent_file = db.relationship('UploadedFile', remote_side=[id], backref='child_files') | |
| def to_dict(self): | |
| # ์ฒญํฌ ๊ฐ์ ๊ณ์ฐ | |
| chunk_count = len(self.chunks) if hasattr(self, 'chunks') else 0 | |
| return { | |
| 'id': self.id, | |
| 'filename': self.filename, | |
| 'original_filename': self.original_filename, | |
| 'file_size': self.file_size, | |
| 'model_name': self.model_name, | |
| 'uploaded_at': self.uploaded_at.isoformat() if self.uploaded_at else None, | |
| 'uploaded_by': self.uploaded_by, | |
| 'parent_file_id': self.parent_file_id, | |
| 'chunk_count': chunk_count, | |
| 'child_count': len(self.child_files) if self.child_files else 0 | |
| } | |
| # ๋ํ ์ธ์ ๋ชจ๋ธ | |
| class ChatSession(db.Model): | |
| id = db.Column(db.Integer, primary_key=True) | |
| user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) | |
| title = db.Column(db.String(255), nullable=True) | |
| model_name = db.Column(db.String(100), nullable=True) | |
| created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) | |
| updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) | |
| # ๊ด๊ณ | |
| user = db.relationship('User', backref='chat_sessions') | |
| messages = db.relationship('ChatMessage', backref='session', lazy=True, cascade='all, delete-orphan', order_by='ChatMessage.created_at') | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'user_id': self.user_id, | |
| 'title': self.title, | |
| 'model_name': self.model_name, | |
| 'created_at': self.created_at.isoformat() if self.created_at else None, | |
| 'updated_at': self.updated_at.isoformat() if self.updated_at else None, | |
| 'message_count': len(self.messages) | |
| } | |
| # ๋ํ ๋ฉ์์ง ๋ชจ๋ธ | |
| class ChatMessage(db.Model): | |
| id = db.Column(db.Integer, primary_key=True) | |
| session_id = db.Column(db.Integer, db.ForeignKey('chat_session.id'), nullable=False) | |
| role = db.Column(db.String(20), nullable=False) # 'user' or 'ai' | |
| content = db.Column(db.Text, nullable=False) | |
| created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'session_id': self.session_id, | |
| 'role': self.role, | |
| 'content': self.content, | |
| 'created_at': self.created_at.isoformat() if self.created_at else None | |
| } | |
| # ๋ฌธ์ ์ฒญํฌ ๋ชจ๋ธ (RAG์ฉ) | |
| class DocumentChunk(db.Model): | |
| id = db.Column(db.Integer, primary_key=True) | |
| file_id = db.Column(db.Integer, db.ForeignKey('uploaded_file.id'), nullable=False) | |
| chunk_index = db.Column(db.Integer, nullable=False) # ์ฒญํฌ ์์ | |
| content = db.Column(db.Text, nullable=False) # ์ฒญํฌ ๋ด์ฉ | |
| embedding = db.Column(db.Text, nullable=True) # ์๋ฒ ๋ฉ ๋ฒกํฐ (JSON ๋ฌธ์์ด๋ก ์ ์ฅ) | |
| created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) | |
| # ๊ด๊ณ | |
| file = db.relationship('UploadedFile', backref='chunks') | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'file_id': self.file_id, | |
| 'chunk_index': self.chunk_index, | |
| 'content': self.content, | |
| 'created_at': self.created_at.isoformat() if self.created_at else None | |
| } | |
| # Parent Chunk ๋ชจ๋ธ (AI ๋ถ์ ๊ฒฐ๊ณผ ์ ์ฅ) | |
| class ParentChunk(db.Model): | |
| id = db.Column(db.Integer, primary_key=True) | |
| file_id = db.Column(db.Integer, db.ForeignKey('uploaded_file.id'), nullable=False, unique=True) | |
| world_view = db.Column(db.Text, nullable=True) # ์ธ๊ณ๊ด ์ค๋ช | |
| characters = db.Column(db.Text, nullable=True) # ์ฃผ์ ์บ๋ฆญํฐ ๋ถ์ | |
| story = db.Column(db.Text, nullable=True) # ์ฃผ์ ์คํ ๋ฆฌ ๋ถ์ | |
| episodes = db.Column(db.Text, nullable=True) # ์ฃผ์ ์ํผ์๋ ๋ถ์ | |
| others = db.Column(db.Text, nullable=True) # ๊ธฐํ | |
| created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) | |
| updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) | |
| # ๊ด๊ณ | |
| file = db.relationship('UploadedFile', backref='parent_chunk') | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'file_id': self.file_id, | |
| 'world_view': self.world_view, | |
| 'characters': self.characters, | |
| 'story': self.story, | |
| 'episodes': self.episodes, | |
| 'others': self.others, | |
| 'created_at': self.created_at.isoformat() if self.created_at else None, | |
| 'updated_at': self.updated_at.isoformat() if self.updated_at else None | |
| } | |
| # ์์คํ ์ค์ ๋ชจ๋ธ (API ํค ๋ฑ) | |
| class SystemConfig(db.Model): | |
| id = db.Column(db.Integer, primary_key=True) | |
| key = db.Column(db.String(100), unique=True, nullable=False) # ์ค์ ํค (์: 'gemini_api_key') | |
| value = db.Column(db.Text, nullable=True) # ์ค์ ๊ฐ (์ํธํ๋ API ํค) | |
| description = db.Column(db.String(255), nullable=True) # ์ค์ ์ค๋ช | |
| created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) | |
| updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'key': self.key, | |
| 'value': self.value, # ์ค์ ๊ฐ ๋ฐํ (๋ณด์์ ์ํด ๋ง์คํน ํ์ํ ์ ์์) | |
| 'description': self.description, | |
| 'created_at': self.created_at.isoformat() if self.created_at else None, | |
| 'updated_at': self.updated_at.isoformat() if self.updated_at else None | |
| } | |
| def get_config(key, default=None): | |
| """์ค์ ๊ฐ ๊ฐ์ ธ์ค๊ธฐ""" | |
| try: | |
| config = SystemConfig.query.filter_by(key=key).first() | |
| return config.value if config else default | |
| except Exception as e: | |
| # ํ ์ด๋ธ์ด ์๊ฑฐ๋ ์ค๋ฅ ๋ฐ์ ์ ๊ธฐ๋ณธ๊ฐ ๋ฐํ | |
| print(f"[SystemConfig.get_config] ์ค๋ฅ: {e}") | |
| return default | |
| def set_config(key, value, description=None): | |
| """์ค์ ๊ฐ ์ ์ฅ/์ ๋ฐ์ดํธ""" | |
| try: | |
| # ํ ์ด๋ธ์ด ์์ผ๋ฉด ์์ฑ ์๋ | |
| from sqlalchemy import inspect | |
| inspector = inspect(db.engine) | |
| if 'system_config' not in inspector.get_table_names(): | |
| print("[SystemConfig.set_config] SystemConfig ํ ์ด๋ธ ์์ฑ ์ค...") | |
| db.create_all() | |
| config = SystemConfig.query.filter_by(key=key).first() | |
| if config: | |
| config.value = value | |
| if description: | |
| config.description = description | |
| config.updated_at = datetime.utcnow() | |
| else: | |
| config = SystemConfig(key=key, value=value, description=description) | |
| db.session.add(config) | |
| db.session.commit() | |
| return config | |
| except Exception as e: | |
| db.session.rollback() | |
| print(f"[SystemConfig.set_config] ์ค๋ฅ: {e}") | |
| import traceback | |
| traceback.print_exc() | |
| raise | |