Initial commit for deployment
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
Main application package for the chatbot application.
|
||||
"""
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from app.config.config import Config
|
||||
|
||||
def create_app(config_class=Config):
|
||||
"""
|
||||
Create and configure the Flask application.
|
||||
|
||||
Args:
|
||||
config_class: Configuration class to use.
|
||||
|
||||
Returns:
|
||||
Flask application instance.
|
||||
"""
|
||||
# Initialize Flask app
|
||||
flask_app = Flask(__name__)
|
||||
flask_app.config.from_object(config_class)
|
||||
|
||||
# Register Flask routes
|
||||
from app.api import routes as flask_routes
|
||||
flask_app.register_blueprint(flask_routes.bp)
|
||||
|
||||
# For now, we'll use only Flask routes and disable FastAPI integration
|
||||
# until we resolve the integration issues
|
||||
|
||||
# Initialize database
|
||||
from app.database import db
|
||||
db.init_app(flask_app)
|
||||
|
||||
return flask_app
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
FastAPI routes for the application.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
from app.services.chatbot_service import chatbot_service
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
class MessageRequest(BaseModel):
|
||||
"""Request model for sending a message."""
|
||||
message: str
|
||||
user_id: str = "default_user"
|
||||
|
||||
class MessageResponse(BaseModel):
|
||||
"""Response model for a message."""
|
||||
content: str
|
||||
is_user: bool
|
||||
timestamp: str
|
||||
|
||||
class ChatResponse(BaseModel):
|
||||
"""Response model for a chat."""
|
||||
chat_id: int
|
||||
messages: List[MessageResponse]
|
||||
|
||||
@router.get("/health")
|
||||
async def health_check():
|
||||
"""
|
||||
Health check endpoint.
|
||||
|
||||
Returns:
|
||||
JSON response with health status.
|
||||
"""
|
||||
return {"status": "healthy"}
|
||||
|
||||
@router.post("/chat", response_model=ChatResponse)
|
||||
async def create_chat(user_id: str = "default_user"):
|
||||
"""
|
||||
Create a new chat.
|
||||
|
||||
Args:
|
||||
user_id: ID of the user creating the chat.
|
||||
|
||||
Returns:
|
||||
Created chat.
|
||||
"""
|
||||
chat_id = chatbot_service.create_chat(user_id)
|
||||
|
||||
return {
|
||||
"chat_id": chat_id,
|
||||
"messages": []
|
||||
}
|
||||
|
||||
@router.post("/chat/{chat_id}/message", response_model=MessageResponse)
|
||||
async def send_message(chat_id: int, request: MessageRequest):
|
||||
"""
|
||||
Send a message to the chatbot.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the chat.
|
||||
request: Message request.
|
||||
|
||||
Returns:
|
||||
Bot response.
|
||||
"""
|
||||
try:
|
||||
response = chatbot_service.get_response(chat_id, request.message)
|
||||
|
||||
# Get the last message (bot response)
|
||||
messages = chatbot_service.get_chat_messages(chat_id)
|
||||
last_message = messages[-1]
|
||||
|
||||
return last_message
|
||||
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
|
||||
@router.get("/chat/{chat_id}", response_model=ChatResponse)
|
||||
async def get_chat(chat_id: int):
|
||||
"""
|
||||
Get a chat by ID.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the chat.
|
||||
|
||||
Returns:
|
||||
Chat with messages.
|
||||
"""
|
||||
try:
|
||||
messages = chatbot_service.get_chat_messages(chat_id)
|
||||
|
||||
return {
|
||||
"chat_id": chat_id,
|
||||
"messages": messages
|
||||
}
|
||||
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
|
||||
def init_app(app):
|
||||
"""
|
||||
Initialize FastAPI application with routes.
|
||||
|
||||
Args:
|
||||
app: FastAPI application instance.
|
||||
"""
|
||||
app.include_router(router, prefix="/api")
|
||||
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
Flask routes for the application.
|
||||
"""
|
||||
|
||||
from flask import Blueprint, jsonify, request, abort
|
||||
|
||||
from app.services.chatbot_service import chatbot_service
|
||||
|
||||
bp = Blueprint('main', __name__)
|
||||
|
||||
@bp.route('/')
|
||||
def index():
|
||||
"""
|
||||
Root endpoint.
|
||||
|
||||
Returns:
|
||||
JSON response with application information.
|
||||
"""
|
||||
return jsonify({
|
||||
'name': 'Chatbot Application',
|
||||
'version': '1.0.0',
|
||||
'status': 'running'
|
||||
})
|
||||
|
||||
@bp.route('/api/health')
|
||||
def health_check():
|
||||
"""
|
||||
Health check endpoint.
|
||||
|
||||
Returns:
|
||||
JSON response with health status.
|
||||
"""
|
||||
return jsonify({
|
||||
'status': 'healthy'
|
||||
})
|
||||
|
||||
@bp.route('/api/chat', methods=['POST'])
|
||||
def create_chat():
|
||||
"""
|
||||
Create a new chat.
|
||||
|
||||
Returns:
|
||||
JSON response with chat ID.
|
||||
"""
|
||||
user_id = request.json.get('user_id', 'default_user')
|
||||
chat_id = chatbot_service.create_chat(user_id)
|
||||
|
||||
return jsonify({
|
||||
'chat_id': chat_id,
|
||||
'messages': []
|
||||
})
|
||||
|
||||
@bp.route('/api/chat/<int:chat_id>/message', methods=['POST'])
|
||||
def send_message(chat_id):
|
||||
"""
|
||||
Send a message to the chatbot.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the chat.
|
||||
|
||||
Returns:
|
||||
JSON response with bot response.
|
||||
"""
|
||||
if not request.json or 'message' not in request.json:
|
||||
abort(400, description="Message is required")
|
||||
|
||||
try:
|
||||
message = request.json['message']
|
||||
response = chatbot_service.get_response(chat_id, message)
|
||||
|
||||
# Get the last message (bot response)
|
||||
messages = chatbot_service.get_chat_messages(chat_id)
|
||||
last_message = messages[-1]
|
||||
|
||||
return jsonify(last_message)
|
||||
|
||||
except ValueError as e:
|
||||
abort(404, description=str(e))
|
||||
|
||||
@bp.route('/api/chat/<int:chat_id>', methods=['GET'])
|
||||
def get_chat(chat_id):
|
||||
"""
|
||||
Get a chat by ID.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the chat.
|
||||
|
||||
Returns:
|
||||
JSON response with chat messages.
|
||||
"""
|
||||
try:
|
||||
messages = chatbot_service.get_chat_messages(chat_id)
|
||||
|
||||
return jsonify({
|
||||
'chat_id': chat_id,
|
||||
'messages': messages
|
||||
})
|
||||
|
||||
except ValueError as e:
|
||||
abort(404, description=str(e))
|
||||
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
Configuration settings for the application.
|
||||
"""
|
||||
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
class Config:
|
||||
"""Base configuration."""
|
||||
|
||||
# Flask configuration
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-key-for-development-only')
|
||||
DEBUG = False
|
||||
TESTING = False
|
||||
|
||||
# Database configuration
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get(
|
||||
'DATABASE_URL',
|
||||
'sqlite:///chatbot.db'
|
||||
)
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
INITIALIZE_DATABASE = os.environ.get('INITIALIZE_DATABASE', 'False').lower() == 'true'
|
||||
|
||||
# Pinecone configuration
|
||||
PINECONE_API_KEY = os.environ.get('PINECONE_API_KEY', '')
|
||||
PINECONE_ENVIRONMENT = os.environ.get('PINECONE_ENVIRONMENT', '')
|
||||
PINECONE_INDEX_NAME = os.environ.get('PINECONE_INDEX_NAME', 'chatbot-index')
|
||||
|
||||
# Model configuration
|
||||
DEFAULT_MODEL = os.environ.get('DEFAULT_MODEL', 'gpt-3.5-turbo')
|
||||
OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY', '')
|
||||
|
||||
|
||||
class DevelopmentConfig(Config):
|
||||
"""Development configuration."""
|
||||
|
||||
DEBUG = True
|
||||
|
||||
|
||||
class TestingConfig(Config):
|
||||
"""Testing configuration."""
|
||||
|
||||
TESTING = True
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
|
||||
|
||||
|
||||
class ProductionConfig(Config):
|
||||
"""Production configuration."""
|
||||
|
||||
# Ensure all required environment variables are set in production
|
||||
@classmethod
|
||||
def init_app(cls, app):
|
||||
"""Initialize production application."""
|
||||
# Check for required environment variables
|
||||
required_vars = [
|
||||
'SECRET_KEY',
|
||||
'DATABASE_URL',
|
||||
'PINECONE_API_KEY',
|
||||
'PINECONE_ENVIRONMENT',
|
||||
'OPENAI_API_KEY'
|
||||
]
|
||||
|
||||
missing_vars = [var for var in required_vars if not os.environ.get(var)]
|
||||
if missing_vars:
|
||||
raise RuntimeError(
|
||||
f"Missing required environment variables: {', '.join(missing_vars)}"
|
||||
)
|
||||
|
||||
|
||||
# Configuration dictionary
|
||||
config = {
|
||||
'development': DevelopmentConfig,
|
||||
'testing': TestingConfig,
|
||||
'production': ProductionConfig,
|
||||
'default': DevelopmentConfig
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
Database module for the application.
|
||||
"""
|
||||
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from sqlalchemy import MetaData
|
||||
|
||||
# Define naming convention for constraints
|
||||
convention = {
|
||||
"ix": 'ix_%(column_0_label)s',
|
||||
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
||||
"ck": "ck_%(table_name)s_%(constraint_name)s",
|
||||
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
||||
"pk": "pk_%(table_name)s"
|
||||
}
|
||||
|
||||
# Create SQLAlchemy instance with naming convention
|
||||
db = SQLAlchemy(metadata=MetaData(naming_convention=convention))
|
||||
|
||||
def init_app(app):
|
||||
"""
|
||||
Initialize the database with the Flask application.
|
||||
|
||||
Args:
|
||||
app: Flask application instance.
|
||||
"""
|
||||
db.init_app(app)
|
||||
|
||||
# Only initialize database if configured to do so
|
||||
if app.config.get('INITIALIZE_DATABASE', False):
|
||||
# Import models to ensure they are registered with SQLAlchemy
|
||||
from app.models import user, chat, document
|
||||
|
||||
# Create tables if they don't exist
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
@@ -0,0 +1,67 @@
|
||||
"""
|
||||
Chat models for the application.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from app.database.db import db
|
||||
|
||||
class Chat(db.Model):
|
||||
"""Chat model representing a chat session."""
|
||||
|
||||
__tablename__ = 'chats'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
title = db.Column(db.String(100), nullable=True)
|
||||
is_team_chat = db.Column(db.Boolean, default=False)
|
||||
model_name = db.Column(db.String(50), nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# Foreign keys
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
|
||||
# Relationships
|
||||
messages = db.relationship('Message', backref='chat', lazy='dynamic', cascade='all, delete-orphan')
|
||||
team_members = db.relationship('TeamChatMember', backref='chat', lazy='dynamic', cascade='all, delete-orphan')
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Chat {self.id}: {self.title or "Untitled"}>'
|
||||
|
||||
|
||||
class Message(db.Model):
|
||||
"""Message model representing a single message in a chat."""
|
||||
|
||||
__tablename__ = 'messages'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
content = db.Column(db.Text, nullable=False)
|
||||
is_user_message = db.Column(db.Boolean, default=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
# Foreign keys
|
||||
chat_id = db.Column(db.Integer, db.ForeignKey('chats.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Message {self.id}: {self.content[:20]}...>'
|
||||
|
||||
|
||||
class TeamChatMember(db.Model):
|
||||
"""Model representing a member of a team chat."""
|
||||
|
||||
__tablename__ = 'team_chat_members'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
joined_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
# Foreign keys
|
||||
chat_id = db.Column(db.Integer, db.ForeignKey('chats.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
|
||||
# Ensure a user can only be added to a team chat once
|
||||
__table_args__ = (
|
||||
db.UniqueConstraint('chat_id', 'user_id', name='uq_team_chat_member'),
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<TeamChatMember chat_id={self.chat_id}, user_id={self.user_id}>'
|
||||
@@ -0,0 +1,59 @@
|
||||
"""
|
||||
Document models for the application.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
import json
|
||||
from app.database.db import db
|
||||
|
||||
class Document(db.Model):
|
||||
"""Document model representing a document in the library."""
|
||||
|
||||
__tablename__ = 'documents'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
title = db.Column(db.String(255), nullable=False)
|
||||
description = db.Column(db.Text, nullable=True)
|
||||
file_path = db.Column(db.String(255), nullable=True)
|
||||
content_type = db.Column(db.String(50), nullable=False)
|
||||
status = db.Column(db.String(20), default='pending') # pending, processing, completed, error
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# Foreign keys
|
||||
uploaded_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
|
||||
# Relationships
|
||||
chunks = db.relationship('DocumentChunk', backref='document', lazy='dynamic', cascade='all, delete-orphan')
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Document {self.id}: {self.title}>'
|
||||
|
||||
|
||||
class DocumentChunk(db.Model):
|
||||
"""Model representing a chunk of a document for embedding."""
|
||||
|
||||
__tablename__ = 'document_chunks'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
content = db.Column(db.Text, nullable=False)
|
||||
chunk_index = db.Column(db.Integer, nullable=False)
|
||||
embedding_id = db.Column(db.String(100), nullable=True) # ID in Pinecone
|
||||
meta_data = db.Column(db.Text, nullable=True) # JSON string of metadata
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
# Foreign keys
|
||||
document_id = db.Column(db.Integer, db.ForeignKey('documents.id'), nullable=False)
|
||||
|
||||
def set_metadata(self, metadata_dict):
|
||||
"""Set metadata as JSON string."""
|
||||
self.meta_data = json.dumps(metadata_dict)
|
||||
|
||||
def get_metadata(self):
|
||||
"""Get metadata as dictionary."""
|
||||
if self.meta_data:
|
||||
return json.loads(self.meta_data)
|
||||
return {}
|
||||
|
||||
def __repr__(self):
|
||||
return f'<DocumentChunk {self.id}: doc_id={self.document_id}, index={self.chunk_index}>'
|
||||
@@ -0,0 +1,24 @@
|
||||
"""
|
||||
User model for the application.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from app.database.db import db
|
||||
|
||||
class User(db.Model):
|
||||
"""User model representing application users."""
|
||||
|
||||
__tablename__ = 'users'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(64), unique=True, nullable=False, index=True)
|
||||
email = db.Column(db.String(120), unique=True, nullable=False, index=True)
|
||||
password_hash = db.Column(db.String(128), nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# Relationships
|
||||
chats = db.relationship('Chat', backref='user', lazy='dynamic')
|
||||
|
||||
def __repr__(self):
|
||||
return f'<User {self.username}>'
|
||||
@@ -0,0 +1,227 @@
|
||||
"""
|
||||
Service for chat functionality.
|
||||
"""
|
||||
|
||||
from typing import List, Dict, Any, Optional
|
||||
from app.database.db import db
|
||||
from app.models.chat import Chat, Message, TeamChatMember
|
||||
from app.models.user import User
|
||||
|
||||
class ChatService:
|
||||
"""Service for chat functionality."""
|
||||
|
||||
def create_chat(self, user_id: int, title: Optional[str] = None,
|
||||
is_team_chat: bool = False, model_name: Optional[str] = None) -> Chat:
|
||||
"""
|
||||
Create a new chat.
|
||||
|
||||
Args:
|
||||
user_id: ID of the user creating the chat.
|
||||
title: Optional title for the chat.
|
||||
is_team_chat: Whether this is a team chat.
|
||||
model_name: Name of the model to use for this chat.
|
||||
|
||||
Returns:
|
||||
Created chat.
|
||||
"""
|
||||
from app.config.config import Config
|
||||
|
||||
chat = Chat(
|
||||
user_id=user_id,
|
||||
title=title,
|
||||
is_team_chat=is_team_chat,
|
||||
model_name=model_name or Config().DEFAULT_MODEL
|
||||
)
|
||||
|
||||
db.session.add(chat)
|
||||
db.session.commit()
|
||||
|
||||
# If it's a team chat, add the creator as a member
|
||||
if is_team_chat:
|
||||
self.add_team_member(chat.id, user_id)
|
||||
|
||||
return chat
|
||||
|
||||
def get_chat(self, chat_id: int) -> Optional[Chat]:
|
||||
"""
|
||||
Get a chat by ID.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the chat.
|
||||
|
||||
Returns:
|
||||
Chat if found, None otherwise.
|
||||
"""
|
||||
return Chat.query.get(chat_id)
|
||||
|
||||
def get_user_chats(self, user_id: int) -> List[Chat]:
|
||||
"""
|
||||
Get all chats for a user.
|
||||
|
||||
Args:
|
||||
user_id: ID of the user.
|
||||
|
||||
Returns:
|
||||
List of chats.
|
||||
"""
|
||||
# Get private chats
|
||||
private_chats = Chat.query.filter_by(
|
||||
user_id=user_id,
|
||||
is_team_chat=False
|
||||
).order_by(Chat.updated_at.desc()).all()
|
||||
|
||||
# Get team chats where user is a member
|
||||
team_chat_ids = db.session.query(TeamChatMember.chat_id).filter_by(user_id=user_id).all()
|
||||
team_chat_ids = [chat_id for (chat_id,) in team_chat_ids]
|
||||
|
||||
team_chats = Chat.query.filter(
|
||||
Chat.id.in_(team_chat_ids)
|
||||
).order_by(Chat.updated_at.desc()).all()
|
||||
|
||||
# Combine and sort by updated_at
|
||||
all_chats = private_chats + team_chats
|
||||
all_chats.sort(key=lambda x: x.updated_at, reverse=True)
|
||||
|
||||
return all_chats
|
||||
|
||||
def add_message(self, chat_id: int, content: str,
|
||||
is_user_message: bool = True, user_id: Optional[int] = None) -> Message:
|
||||
"""
|
||||
Add a message to a chat.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the chat.
|
||||
content: Message content.
|
||||
is_user_message: Whether this is a user message (vs. bot message).
|
||||
user_id: ID of the user sending the message (required for user messages).
|
||||
|
||||
Returns:
|
||||
Created message.
|
||||
"""
|
||||
message = Message(
|
||||
chat_id=chat_id,
|
||||
content=content,
|
||||
is_user_message=is_user_message,
|
||||
user_id=user_id if is_user_message else None
|
||||
)
|
||||
|
||||
db.session.add(message)
|
||||
|
||||
# Update chat's updated_at timestamp
|
||||
chat = Chat.query.get(chat_id)
|
||||
if chat:
|
||||
chat.updated_at = message.created_at
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return message
|
||||
|
||||
def get_chat_messages(self, chat_id: int) -> List[Message]:
|
||||
"""
|
||||
Get all messages for a chat.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the chat.
|
||||
|
||||
Returns:
|
||||
List of messages.
|
||||
"""
|
||||
return Message.query.filter_by(chat_id=chat_id).order_by(Message.created_at).all()
|
||||
|
||||
def add_team_member(self, chat_id: int, user_id: int) -> Optional[TeamChatMember]:
|
||||
"""
|
||||
Add a user to a team chat.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the team chat.
|
||||
user_id: ID of the user to add.
|
||||
|
||||
Returns:
|
||||
Created team chat member if successful, None otherwise.
|
||||
"""
|
||||
chat = Chat.query.get(chat_id)
|
||||
if not chat or not chat.is_team_chat:
|
||||
return None
|
||||
|
||||
# Check if user is already a member
|
||||
existing_member = TeamChatMember.query.filter_by(
|
||||
chat_id=chat_id,
|
||||
user_id=user_id
|
||||
).first()
|
||||
|
||||
if existing_member:
|
||||
return existing_member
|
||||
|
||||
member = TeamChatMember(
|
||||
chat_id=chat_id,
|
||||
user_id=user_id
|
||||
)
|
||||
|
||||
db.session.add(member)
|
||||
db.session.commit()
|
||||
|
||||
return member
|
||||
|
||||
def get_team_members(self, chat_id: int) -> List[User]:
|
||||
"""
|
||||
Get all members of a team chat.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the team chat.
|
||||
|
||||
Returns:
|
||||
List of users.
|
||||
"""
|
||||
member_ids = db.session.query(TeamChatMember.user_id).filter_by(chat_id=chat_id).all()
|
||||
member_ids = [user_id for (user_id,) in member_ids]
|
||||
|
||||
return User.query.filter(User.id.in_(member_ids)).all()
|
||||
|
||||
def remove_team_member(self, chat_id: int, user_id: int) -> bool:
|
||||
"""
|
||||
Remove a user from a team chat.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the team chat.
|
||||
user_id: ID of the user to remove.
|
||||
|
||||
Returns:
|
||||
True if removal was successful, False otherwise.
|
||||
"""
|
||||
member = TeamChatMember.query.filter_by(
|
||||
chat_id=chat_id,
|
||||
user_id=user_id
|
||||
).first()
|
||||
|
||||
if not member:
|
||||
return False
|
||||
|
||||
db.session.delete(member)
|
||||
db.session.commit()
|
||||
|
||||
return True
|
||||
|
||||
def delete_chat(self, chat_id: int) -> bool:
|
||||
"""
|
||||
Delete a chat and all its messages.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the chat to delete.
|
||||
|
||||
Returns:
|
||||
True if deletion was successful, False otherwise.
|
||||
"""
|
||||
chat = Chat.query.get(chat_id)
|
||||
if not chat:
|
||||
return False
|
||||
|
||||
try:
|
||||
db.session.delete(chat)
|
||||
db.session.commit()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
# Log the error
|
||||
print(f"Error deleting chat {chat_id}: {str(e)}")
|
||||
db.session.rollback()
|
||||
return False
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
Service for chatbot functionality without database dependency.
|
||||
"""
|
||||
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
class ChatbotService:
|
||||
"""Service for chatbot functionality."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the chatbot service."""
|
||||
# In-memory storage for chat history
|
||||
self.chat_history = {}
|
||||
self.current_chat_id = 0
|
||||
|
||||
def create_chat(self, user_id: str) -> int:
|
||||
"""
|
||||
Create a new chat session.
|
||||
|
||||
Args:
|
||||
user_id: ID of the user creating the chat.
|
||||
|
||||
Returns:
|
||||
ID of the created chat.
|
||||
"""
|
||||
self.current_chat_id += 1
|
||||
chat_id = self.current_chat_id
|
||||
|
||||
self.chat_history[chat_id] = {
|
||||
'user_id': user_id,
|
||||
'messages': []
|
||||
}
|
||||
|
||||
return chat_id
|
||||
|
||||
def add_message(self, chat_id: int, content: str, is_user: bool = True) -> Dict[str, Any]:
|
||||
"""
|
||||
Add a message to a chat.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the chat.
|
||||
content: Message content.
|
||||
is_user: Whether this is a user message (vs. bot message).
|
||||
|
||||
Returns:
|
||||
Added message.
|
||||
"""
|
||||
if chat_id not in self.chat_history:
|
||||
raise ValueError(f"Chat with ID {chat_id} not found")
|
||||
|
||||
message = {
|
||||
'content': content,
|
||||
'is_user': is_user,
|
||||
'timestamp': self._get_timestamp()
|
||||
}
|
||||
|
||||
self.chat_history[chat_id]['messages'].append(message)
|
||||
|
||||
return message
|
||||
|
||||
def get_chat_messages(self, chat_id: int) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get all messages for a chat.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the chat.
|
||||
|
||||
Returns:
|
||||
List of messages.
|
||||
"""
|
||||
if chat_id not in self.chat_history:
|
||||
raise ValueError(f"Chat with ID {chat_id} not found")
|
||||
|
||||
return self.chat_history[chat_id]['messages']
|
||||
|
||||
def get_response(self, chat_id: int, message: str) -> str:
|
||||
"""
|
||||
Get a response from the chatbot.
|
||||
|
||||
Args:
|
||||
chat_id: ID of the chat.
|
||||
message: User message.
|
||||
|
||||
Returns:
|
||||
Bot response.
|
||||
"""
|
||||
# Add user message to chat history
|
||||
self.add_message(chat_id, message, is_user=True)
|
||||
|
||||
# Simple echo response for now
|
||||
response = f"You said: {message}"
|
||||
|
||||
# Add bot response to chat history
|
||||
self.add_message(chat_id, response, is_user=False)
|
||||
|
||||
return response
|
||||
|
||||
def _get_timestamp(self) -> str:
|
||||
"""Get current timestamp."""
|
||||
from datetime import datetime
|
||||
return datetime.utcnow().isoformat()
|
||||
|
||||
|
||||
# Create a singleton instance
|
||||
chatbot_service = ChatbotService()
|
||||
@@ -0,0 +1,165 @@
|
||||
"""
|
||||
Service for document processing and embedding.
|
||||
"""
|
||||
|
||||
import os
|
||||
from typing import List, Dict, Any, Optional
|
||||
import pinecone
|
||||
from app.database.db import db
|
||||
from app.models.document import Document, DocumentChunk
|
||||
from app.config.config import Config
|
||||
|
||||
class DocumentService:
|
||||
"""Service for document processing and embedding."""
|
||||
|
||||
def __init__(self, config: Config = None):
|
||||
"""
|
||||
Initialize the document service.
|
||||
|
||||
Args:
|
||||
config: Configuration object.
|
||||
"""
|
||||
self.config = config or Config()
|
||||
self._initialize_pinecone()
|
||||
|
||||
def _initialize_pinecone(self):
|
||||
"""Initialize Pinecone client."""
|
||||
pinecone.init(
|
||||
api_key=self.config.PINECONE_API_KEY,
|
||||
environment=self.config.PINECONE_ENVIRONMENT
|
||||
)
|
||||
|
||||
# Check if index exists, create if it doesn't
|
||||
if self.config.PINECONE_INDEX_NAME not in pinecone.list_indexes():
|
||||
pinecone.create_index(
|
||||
name=self.config.PINECONE_INDEX_NAME,
|
||||
dimension=768, # Default dimension for sentence-transformers
|
||||
metric="cosine"
|
||||
)
|
||||
|
||||
self.index = pinecone.Index(self.config.PINECONE_INDEX_NAME)
|
||||
|
||||
def create_document(self, title: str, file_path: str, content_type: str,
|
||||
description: Optional[str], user_id: int) -> Document:
|
||||
"""
|
||||
Create a new document record.
|
||||
|
||||
Args:
|
||||
title: Document title.
|
||||
file_path: Path to the document file.
|
||||
content_type: MIME type of the document.
|
||||
description: Optional description of the document.
|
||||
user_id: ID of the user who uploaded the document.
|
||||
|
||||
Returns:
|
||||
Created document.
|
||||
"""
|
||||
document = Document(
|
||||
title=title,
|
||||
file_path=file_path,
|
||||
content_type=content_type,
|
||||
description=description,
|
||||
uploaded_by=user_id,
|
||||
status='pending'
|
||||
)
|
||||
|
||||
db.session.add(document)
|
||||
db.session.commit()
|
||||
|
||||
return document
|
||||
|
||||
def process_document(self, document_id: int) -> bool:
|
||||
"""
|
||||
Process a document for embedding.
|
||||
|
||||
Args:
|
||||
document_id: ID of the document to process.
|
||||
|
||||
Returns:
|
||||
True if processing was successful, False otherwise.
|
||||
"""
|
||||
document = Document.query.get(document_id)
|
||||
if not document:
|
||||
return False
|
||||
|
||||
try:
|
||||
# Update status to processing
|
||||
document.status = 'processing'
|
||||
db.session.commit()
|
||||
|
||||
# TODO: Implement document parsing and chunking
|
||||
# This will be implemented in the next step
|
||||
|
||||
# Update status to completed
|
||||
document.status = 'completed'
|
||||
db.session.commit()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
# Update status to error
|
||||
document.status = 'error'
|
||||
db.session.commit()
|
||||
# Log the error
|
||||
print(f"Error processing document {document_id}: {str(e)}")
|
||||
return False
|
||||
|
||||
def get_document(self, document_id: int) -> Optional[Document]:
|
||||
"""
|
||||
Get a document by ID.
|
||||
|
||||
Args:
|
||||
document_id: ID of the document.
|
||||
|
||||
Returns:
|
||||
Document if found, None otherwise.
|
||||
"""
|
||||
return Document.query.get(document_id)
|
||||
|
||||
def get_all_documents(self, user_id: Optional[int] = None) -> List[Document]:
|
||||
"""
|
||||
Get all documents, optionally filtered by user.
|
||||
|
||||
Args:
|
||||
user_id: Optional user ID to filter by.
|
||||
|
||||
Returns:
|
||||
List of documents.
|
||||
"""
|
||||
query = Document.query
|
||||
if user_id:
|
||||
query = query.filter_by(uploaded_by=user_id)
|
||||
return query.order_by(Document.created_at.desc()).all()
|
||||
|
||||
def delete_document(self, document_id: int) -> bool:
|
||||
"""
|
||||
Delete a document and its chunks.
|
||||
|
||||
Args:
|
||||
document_id: ID of the document to delete.
|
||||
|
||||
Returns:
|
||||
True if deletion was successful, False otherwise.
|
||||
"""
|
||||
document = Document.query.get(document_id)
|
||||
if not document:
|
||||
return False
|
||||
|
||||
try:
|
||||
# Delete document chunks from Pinecone
|
||||
chunks = DocumentChunk.query.filter_by(document_id=document_id).all()
|
||||
embedding_ids = [chunk.embedding_id for chunk in chunks if chunk.embedding_id]
|
||||
|
||||
if embedding_ids:
|
||||
self.index.delete(ids=embedding_ids)
|
||||
|
||||
# Delete document from database
|
||||
db.session.delete(document)
|
||||
db.session.commit()
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
# Log the error
|
||||
print(f"Error deleting document {document_id}: {str(e)}")
|
||||
db.session.rollback()
|
||||
return False
|
||||
@@ -0,0 +1,95 @@
|
||||
"""
|
||||
Service for model management and interaction.
|
||||
"""
|
||||
|
||||
from typing import List, Dict, Any, Optional
|
||||
from app.config.config import Config
|
||||
|
||||
class ModelService:
|
||||
"""Service for model management and interaction."""
|
||||
|
||||
# Available models
|
||||
AVAILABLE_MODELS = {
|
||||
'gpt-3.5-turbo': {
|
||||
'name': 'GPT-3.5 Turbo',
|
||||
'description': 'OpenAI GPT-3.5 Turbo model',
|
||||
'provider': 'openai',
|
||||
'max_tokens': 4096
|
||||
},
|
||||
'gpt-4': {
|
||||
'name': 'GPT-4',
|
||||
'description': 'OpenAI GPT-4 model',
|
||||
'provider': 'openai',
|
||||
'max_tokens': 8192
|
||||
},
|
||||
# Add more models as needed
|
||||
}
|
||||
|
||||
def __init__(self, config: Config = None):
|
||||
"""
|
||||
Initialize the model service.
|
||||
|
||||
Args:
|
||||
config: Configuration object.
|
||||
"""
|
||||
self.config = config or Config()
|
||||
self.default_model = self.config.DEFAULT_MODEL
|
||||
|
||||
def get_available_models(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get a list of available models.
|
||||
|
||||
Returns:
|
||||
List of model information dictionaries.
|
||||
"""
|
||||
models = []
|
||||
for model_id, model_info in self.AVAILABLE_MODELS.items():
|
||||
model_data = {
|
||||
'id': model_id,
|
||||
'is_default': model_id == self.default_model,
|
||||
**model_info
|
||||
}
|
||||
models.append(model_data)
|
||||
|
||||
return models
|
||||
|
||||
def get_model_info(self, model_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Get information about a specific model.
|
||||
|
||||
Args:
|
||||
model_id: ID of the model.
|
||||
|
||||
Returns:
|
||||
Model information dictionary if found, None otherwise.
|
||||
"""
|
||||
if model_id not in self.AVAILABLE_MODELS:
|
||||
return None
|
||||
|
||||
return {
|
||||
'id': model_id,
|
||||
'is_default': model_id == self.default_model,
|
||||
**self.AVAILABLE_MODELS[model_id]
|
||||
}
|
||||
|
||||
def generate_response(self, model_id: str, prompt: str,
|
||||
context: Optional[List[Dict[str, str]]] = None) -> str:
|
||||
"""
|
||||
Generate a response from the model.
|
||||
|
||||
Args:
|
||||
model_id: ID of the model to use.
|
||||
prompt: User prompt.
|
||||
context: Optional conversation context.
|
||||
|
||||
Returns:
|
||||
Generated response.
|
||||
"""
|
||||
# TODO: Implement actual model integration
|
||||
# This is a placeholder that will be implemented in the next steps
|
||||
|
||||
if model_id not in self.AVAILABLE_MODELS:
|
||||
model_id = self.default_model
|
||||
|
||||
# Placeholder response
|
||||
return f"This is a placeholder response from {self.AVAILABLE_MODELS[model_id]['name']}. The actual model integration will be implemented in the next steps."
|
||||
Reference in New Issue
Block a user