Initial commit for deployment
This commit is contained in:
@@ -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