본문으로 건너뛰기

[Open WebUI] 채팅 목록 조회 시 불필요한 전체 JSON 로딩 제거

PR 링크: open-webui/open-webui#21591 상태: Merged | 변경: +47 / -26

들어가며

Open WebUI의 고정(pinned), 보관(archived), 공유(shared) 채팅 목록 API는 제목, ID, 타임스탬프만 반환합니다. 그런데 내부적으로는 대화 내용 전체가 포함된 JSON blob을 포함하는 ChatModel 객체를 로딩하고 있었습니다. 수천 건의 대화가 있는 사용자의 경우, 수 MB에서 수십 MB의 불필요한 데이터를 매번 DB에서 읽어오는 셈입니다.

핵심 코드 분석

with_entities()를 활용한 컬럼 선택

Before (get_pinned_chats_by_user_id):

def get_pinned_chats_by_user_id(
    self, user_id: str, db: Optional[Session] = None
) -> list[ChatModel]:
    with get_db_context(db) as db:
        all_chats = (
            db.query(Chat)
            .filter_by(user_id=user_id, pinned=True, archived=False)
            .order_by(Chat.updated_at.desc())
        )
        return [ChatModel.model_validate(chat) for chat in all_chats]

After:

def get_pinned_chats_by_user_id(
    self, user_id: str, db: Optional[Session] = None
) -> list[ChatTitleIdResponse]:
    with get_db_context(db) as db:
        all_chats = (
            db.query(Chat)
            .filter_by(user_id=user_id, pinned=True, archived=False)
            .order_by(Chat.updated_at.desc())
            .with_entities(
                Chat.id, Chat.title, Chat.updated_at, Chat.created_at
            )
        )
        return [
            ChatTitleIdResponse.model_validate({
                "id": chat[0],
                "title": chat[1],
                "updated_at": chat[2],
                "created_at": chat[3],
            })
            for chat in all_chats
        ]

동일한 패턴이 get_archived_chat_list_by_user_id와 라우터 단에서도 적용되었습니다. 라우터에서는 이제 DB 계층이 이미 올바른 타입을 반환하므로, 추가 변환 단계가 제거되었습니다.

# Before (router)
return [
    ChatTitleIdResponse(**chat.model_dump())
    for chat in Chats.get_pinned_chats_by_user_id(user.id, db=db)
]

# After (router)
return Chats.get_pinned_chats_by_user_id(user.id, db=db)

왜 이게 좋은가

  1. DB I/O 대폭 감소: 대화 내용 JSON blob(수 KB~수 MB/건)을 읽지 않으므로, DB에서 전송되는 데이터량이 크게 줄어듭니다.
  2. 메모리 사용량 감소: Python 프로세스에서 불필요한 ChatModel 객체와 그 안의 대화 데이터를 생성하지 않습니다.
  3. 역직렬화 비용 절감: Pydantic의 model_validate로 전체 ChatModel을 파싱하는 대신, 4개 필드만 가진 경량 응답 모델을 생성합니다.
  4. 기존 패턴 적용: 코드베이스에 이미 존재하던 get_chat_title_id_list_by_user_id의 패턴을 다른 엔드포인트에도 일관되게 적용한 것입니다.

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글