Initial commit for deployment

This commit is contained in:
Iyeoluwa Akinrinola
2025-05-09 15:41:16 +01:00
commit ac98999507
54 changed files with 4343 additions and 0 deletions
View File
+227
View File
@@ -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
+105
View File
@@ -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()
+165
View File
@@ -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
+95
View File
@@ -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."