Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions app/api/api_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@

from fastapi import APIRouter

from app.api import annotation_api, api_key_api, chat_tab_api, driver_api, query_api, test_api, user_db_api
from app.api import (
annotation_api,
api_key_api,
chat_tab_api,
driver_api,
query_api,
test_api,
user_db_api,
chat_messages_api,
)

api_router = APIRouter()

Expand All @@ -13,6 +22,7 @@
api_router.include_router(driver_api.router, prefix="/driver", tags=["Driver"])
api_router.include_router(user_db_api.router, prefix="/user/db", tags=["UserDb"])
api_router.include_router(api_key_api.router, prefix="/keys", tags=["API Key"])
api_router.include_router(chat_tab_api.router, prefix="/chats", tags=["AI Chat"])
api_router.include_router(chat_tab_api.router, prefix="/chatTabs", tags=["Chat Tab"])
api_router.include_router(annotation_api.router, prefix="/annotations", tags=["Annotation"])
api_router.include_router(query_api.router, prefix="/query", tags=["query"])
api_router.include_router(chat_messages_api.router, prefix="/chatMessages", tags=["Chat Message"])
39 changes: 39 additions & 0 deletions app/api/chat_messages_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from fastapi import APIRouter, Depends

from app.core.enum.sender import SenderEnum
from app.core.response import ResponseMessage
from app.core.status import CommonCode
from app.schemas.chat_message.request_model import ChatMessagesReqeust
from app.schemas.chat_message.response_model import ChatMessagesResponse
from app.services.chat_message_service import ChatMessageService, chat_message_service

chat_message_service_dependency = Depends(lambda: chat_message_service)

router = APIRouter()


@router.post(
"/create",
response_model=ResponseMessage[ChatMessagesResponse],
summary="새로운 사용자 질의 생성",
)
async def create_chat_message(
request: ChatMessagesReqeust, service: ChatMessageService = chat_message_service_dependency
) -> ResponseMessage[ChatMessagesResponse]:
"""
`tabId`, `message`를 받아 DB에 저장하고 AI를 통해 사용자 질의를 분석하고 답변을 생성하여 반환합니다.
"""
new_messages = await service.create_chat_message(request)

print(ChatMessagesResponse.model_json_schema())

response_data = ChatMessagesResponse(
id=new_messages.id,
chat_tab_id=new_messages.chat_tab_id,
sender=SenderEnum(new_messages.sender),
message=new_messages.message,
created_at=new_messages.created_at,
updated_at=new_messages.updated_at,
)

return ResponseMessage.success(value=response_data, code=CommonCode.SUCCESS_CREATE_CHAT_MESSAGES)
4 changes: 2 additions & 2 deletions app/api/chat_tab_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
summary="새로운 Chat Tab 생성",
description="새로운 Chat Tab을 생성하여 로컬 데이터베이스에 저장합니다.",
)
def store_chat_tab(
def create_chat_tab(
chatName: ChatTabBase, service: ChatTabService = chat_tab_service_dependency
) -> ResponseMessage[ChatTabResponse]:
"""
- **name**: 새로운 Chat_tab 이름 (예: "채팅 타이틀")
"""
created_chat_tab = service.store_chat_tab(chatName)
created_chat_tab = service.create_chat_tab(chatName)

response_data = ChatTabResponse(
id=created_chat_tab.id,
Expand Down
3 changes: 0 additions & 3 deletions app/api/query_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ def execution(
service: QueryService = query_service_dependency,
userDbservice: UserDbService = user_db_service_dependency,
) -> ResponseMessage[dict | str | None]:

db_info = userDbservice.find_profile(query_info.user_db_id)
result = service.execution(query_info, db_info)

Expand All @@ -46,7 +45,6 @@ def execution_test(
service: QueryService = query_service_dependency,
userDbservice: UserDbService = user_db_service_dependency,
) -> ResponseMessage[Any]:

db_info = userDbservice.find_profile(query_info.user_db_id)
result = service.execution_test(query_info, db_info)

Expand All @@ -62,7 +60,6 @@ def find_query_history(
chat_tab_id: str,
service: QueryService = query_service_dependency,
) -> ResponseMessage[dict]:

result = service.find_query_history(chat_tab_id)

if not result.is_successful:
Expand Down
5 changes: 0 additions & 5 deletions app/api/user_db_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ def delete_profile(
def find_all_profile(
service: UserDbService = user_db_service_dependency,
) -> ResponseMessage[list[DBProfile]]:

result = service.find_all_profile()

if not result.is_successful:
Expand All @@ -104,7 +103,6 @@ def find_all_profile(
summary="특정 DB의 전체 스키마 조회",
)
def find_schemas(profile_id: str, service: UserDbService = user_db_service_dependency) -> ResponseMessage[list[str]]:

db_info = service.find_profile(profile_id)
result = service.find_schemas(db_info)

Expand All @@ -121,7 +119,6 @@ def find_schemas(profile_id: str, service: UserDbService = user_db_service_depen
def find_tables(
profile_id: str, schema_name: str, service: UserDbService = user_db_service_dependency
) -> ResponseMessage[list[str]]:

db_info = service.find_profile(profile_id)
result = service.find_tables(db_info, schema_name)

Expand All @@ -138,7 +135,6 @@ def find_tables(
def find_columns(
profile_id: str, schema_name: str, table_name: str, service: UserDbService = user_db_service_dependency
) -> ResponseMessage[list[ColumnInfo]]:

db_info = service.find_profile(profile_id)
result = service.find_columns(db_info, schema_name, table_name)

Expand All @@ -156,7 +152,6 @@ def find_columns(
def find_all_schema_info(
profile_id: str, service: UserDbService = user_db_service_dependency
) -> ResponseMessage[list[TableInfo]]:

db_info = service.find_profile(profile_id)
full_schema_info = service.get_full_schema_info(db_info)

Expand Down
3 changes: 2 additions & 1 deletion app/core/enum/db_key_prefix_name.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

언더바 말고 중간바로 수정해주실 수 있나여?
AS-IS
chat_message = "CHAT_MESSAGE"
chat_tab = "CHAT_TAB"

TO-BE
chat_message = "CHAT-MESSAGE"
chat_tab = "CHAT-TAB"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다

Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ class DBSaveIdEnum(Enum):
user_db = "USER-DB"
driver = "DRIVER"
api_key = "API-KEY"
chat_tab = "CHAT_TAB"
chat_tab = "CHAT-TAB"
query = "QUERY"
chat_message = "CHAT-MESSAGE"

database_annotation = "DB-ANNO"
table_annotation = "TBL-ANNO"
Expand Down
8 changes: 8 additions & 0 deletions app/core/enum/sender.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from enum import Enum


class SenderEnum(str, Enum):
"""채팅 메시지 발신자 구분"""

user = "U"
ai = "A"
11 changes: 10 additions & 1 deletion app/core/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class CommonCode(Enum):
SUCCESS_UPDATE_API_KEY = (status.HTTP_200_OK, "2201", "API KEY가 성공적으로 수정되었습니다.")
SUCCESS_GET_API_KEY = (status.HTTP_200_OK, "2202", "API KEY 정보를 성공적으로 조회했습니다.")

""" AI CHAT, DB 성공 코드 - 23xx """
""" CHAT TAB 성공 코드 - 23xx """
SUCCESS_CHAT_TAB_CREATE = (status.HTTP_200_OK, "2300", "새로운 채팅 탭이 성공적으로 생성하였습니다.")
SUCCESS_CHAT_TAB_UPDATE = (status.HTTP_200_OK, "2301", "채팅 탭 이름이 성공적으로 수정되었습니다.")
SUCCESS_CHAT_TAB_DELETE = (status.HTTP_200_OK, "2302", "채팅 탭을 성공적으로 삭제되었습니다.")
Expand All @@ -50,6 +50,9 @@ class CommonCode(Enum):
SUCCESS_FIND_QUERY_HISTORY = (status.HTTP_200_OK, "2102", "쿼리 이력 조회를 성공하였습니다.")
SUCCESS_EXECUTION_TEST = (status.HTTP_201_CREATED, "2400", "쿼리 TEST를 성공적으로 수행하였습니다.")

""" ChAT MESSAGE 성공 코드 - 26xx """
SUCCESS_CREATE_CHAT_MESSAGES = (status.HTTP_201_CREATED, "2600", "메시지를 성공적으로 요청하였습니다.")

# =======================================
# 클라이언트 에러 (Client Error) - 4xxx
# =======================================
Expand Down Expand Up @@ -96,6 +99,9 @@ class CommonCode(Enum):
NO_CHAT_KEY = (status.HTTP_400_BAD_REQUEST, "4501", "CHAT 키는 필수 값입니다.")
NO_QUERY = (status.HTTP_400_BAD_REQUEST, "4500", "쿼리는 필수 값입니다.")

""" CHAT MESSAGE 에러 코드 - 46xx """
INVALID_CHAT_MESSAGE_REQUEST = (status.HTTP_400_BAD_REQUEST, "4600", "AI 채팅 요청 데이터가 유효하지 않습니다.")

# ==================================
# 서버 에러 (Server Error) - 5xx
# ==================================
Expand Down Expand Up @@ -144,8 +150,11 @@ class CommonCode(Enum):
)

""" SQL 서버 에러 코드 - 55xx """

FAIL_CREATE_QUERY = (status.HTTP_500_INTERNAL_SERVER_ERROR, "5170", "쿼리 실행 정보 저장 중 에러가 발생했습니다.")

""" CHAT MESSAGE 에러 코드 - 56xx """

def __init__(self, http_status: int, code: str, message: str):
"""Enum 멤버가 생성될 때 각 값을 속성으로 할당합니다."""
self.http_status = http_status
Expand Down
40 changes: 39 additions & 1 deletion app/repository/chat_message_repository.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,48 @@
import sqlite3

from app.core.utils import get_db_path
from app.schemas.chat_tab.db_model import ChatMessageInDB
from app.schemas.chat_message.db_model import ChatMessageInDB


class ChatMessageRepository:
def create_chat_message(self, new_id: str, sender: str, chat_tab_id: str, message: str) -> ChatMessageInDB:
"""
새로운 채팅을 데이터베이스에 저장하고, 저장된 객체를 반환합니다.
"""

db_path = get_db_path()
conn = None
try:
conn = sqlite3.connect(str(db_path), timeout=10)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()

cursor.execute(
"""
INSERT INTO chat_message (id, chat_tab_id, sender, message)
VALUES (?, ?, ?, ?)
""",
(
new_id,
chat_tab_id,
sender,
message,
),
)
conn.commit()

cursor.execute("SELECT * FROM chat_message WHERE id = ?", (new_id,))
created_row = cursor.fetchone()

if not created_row:
return None

return ChatMessageInDB.model_validate(dict(created_row))

finally:
if conn:
conn.close()

def get_chat_messages_by_tabId(self, id: str) -> list[ChatMessageInDB]:
"""주어진 chat_tab_id에 해당하는 모든 메시지를 가져옵니다."""
db_path = get_db_path()
Expand Down
24 changes: 24 additions & 0 deletions app/schemas/chat_message/base_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from datetime import datetime

from pydantic import BaseModel, Field

from app.core.enum.db_key_prefix_name import DBSaveIdEnum
from app.core.exceptions import APIException
from app.core.status import CommonCode


class ChatMessagesBase(BaseModel):
id: str = Field(..., description="고유 ID")
created_at: datetime = Field(..., description="생성 시각")
updated_at: datetime = Field(..., description="마지막 수정 시각")


class RequestBase(BaseModel):
"""요청 스키마의 기본 모델"""

def validate_chat_tab_id(self) -> None:
"""채팅 탭 ID에 대한 유효성 검증 로직을 수행합니다."""

required_prefix = DBSaveIdEnum.chat_tab.value + "-"
if not self.chat_tab_id.startswith(required_prefix):
raise APIException(CommonCode.INVALID_CHAT_TAB_ID_FORMAT)
13 changes: 13 additions & 0 deletions app/schemas/chat_message/db_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from pydantic import Field

from app.core.enum.sender import SenderEnum
from app.schemas.chat_message.base_model import ChatMessagesBase


class ChatMessageInDB(ChatMessagesBase):
chat_tab_id: str = Field(..., description="해당 메시지가 속한 채팅 탭의 ID")
sender: SenderEnum = Field(..., description="메시지 발신자 ('A' 또는 'U')")
message: str = Field(..., description="메시지 내용")

class Config:
use_enum_values = True
13 changes: 13 additions & 0 deletions app/schemas/chat_message/request_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from pydantic import Field

from app.schemas.chat_message.base_model import RequestBase


class ChatMessagesReqeust(RequestBase):
"""채팅 메시지 생성 요청 스키마"""

chat_tab_id: str = Field(..., description="채팅 탭의 고유 ID")
message: str = Field(..., description="메시지 내용")

def validate(self):
self.validate_chat_tab_id()
13 changes: 13 additions & 0 deletions app/schemas/chat_message/response_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from pydantic import Field

from app.core.enum.sender import SenderEnum
from app.schemas.chat_message.base_model import ChatMessagesBase


class ChatMessagesResponse(ChatMessagesBase):
chat_tab_id: str = Field(..., description="해당 메시지가 속한 채팅 탭의 ID")
sender: SenderEnum = Field(..., description="메시지 발신자 ('AI' 또는 'User')")
message: str = Field(..., description="메시지 내용")

class Config:
use_enum_values = True
9 changes: 9 additions & 0 deletions app/schemas/chat_tab/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from pydantic import BaseModel, Field

from app.core.enum.db_key_prefix_name import DBSaveIdEnum
from app.core.exceptions import APIException
from app.core.status import CommonCode

Expand Down Expand Up @@ -35,3 +36,11 @@ def validate_chat_tab_name(self) -> None:
# 특정 특수문자를 검사하는 예시
if re.search(r"[;\"'`<>]", self.name):
raise APIException(CommonCode.INVALID_CHAT_TAB_NAME_CONTENT)

def validate_chat_tab_id(self) -> None:
"""채팅 탭 ID에 대한 유효성 검증 로직을 수행합니다."""

# 1. 'CHAT-TAB-' 접두사 검증
required_prefix = DBSaveIdEnum.chat_tab.value + "-"
if not self.id.startswith(required_prefix):
raise APIException(CommonCode.INVALID_CHAT_TAB_ID_FORMAT)
6 changes: 0 additions & 6 deletions app/schemas/chat_tab/db_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ class ChatTabInDB(ChatTabBase):
created_at: datetime
updated_at: datetime

class Config:
from_attributes = True


class ChatMessageInDB(ChatTabBase):
"""데이터베이스에 저장된 형태의 메시지 스키마 (내부용)"""
Expand All @@ -26,6 +23,3 @@ class ChatMessageInDB(ChatTabBase):
message: str = Field(..., description="메시지 내용")
created_at: datetime
updated_at: datetime

class Config:
from_attributes = True
3 changes: 3 additions & 0 deletions app/schemas/chat_tab/update_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ class ChatTabUpdate(ChatTabBase):
"""채팅 탭 이름 수정을 위한 스키마"""

name: str = Field(None, description="수정할 채팅 탭 이름")

def validate(self):
self.validate_chat_tab_name(["name"])
13 changes: 0 additions & 13 deletions app/schemas/chat_tab/validation_utils.py

This file was deleted.

Loading