Session id: int -> UUID (#2814)

* session id: int -> UUID

* nit

* validated

* validated downgrade + upgrade + all functionality

* nit

* minor nit

* fix test case
This commit is contained in:
pablodanswer 2024-10-16 15:18:45 -07:00 committed by GitHub
parent f3fb7c572e
commit db0779dd02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 276 additions and 106 deletions

View File

@ -0,0 +1,153 @@
"""
Revision ID: 6756efa39ada
Revises: 5d12a446f5c0
Create Date: 2024-10-15 17:47:44.108537
"""
from alembic import op
import sqlalchemy as sa
revision = "6756efa39ada"
down_revision = "5d12a446f5c0"
branch_labels = None
depends_on = None
"""
Migrate chat_session and chat_message tables to use UUID primary keys.
This script:
1. Adds UUID columns to chat_session and chat_message
2. Populates new columns with UUIDs
3. Updates foreign key relationships
4. Removes old integer ID columns
Note: Downgrade will assign new integer IDs, not restore original ones.
"""
def upgrade() -> None:
op.execute("CREATE EXTENSION IF NOT EXISTS pgcrypto;")
op.add_column(
"chat_session",
sa.Column(
"new_id",
sa.UUID(as_uuid=True),
server_default=sa.text("gen_random_uuid()"),
nullable=False,
),
)
op.execute("UPDATE chat_session SET new_id = gen_random_uuid();")
op.add_column(
"chat_message",
sa.Column("new_chat_session_id", sa.UUID(as_uuid=True), nullable=True),
)
op.execute(
"""
UPDATE chat_message
SET new_chat_session_id = cs.new_id
FROM chat_session cs
WHERE chat_message.chat_session_id = cs.id;
"""
)
op.drop_constraint(
"chat_message_chat_session_id_fkey", "chat_message", type_="foreignkey"
)
op.drop_column("chat_message", "chat_session_id")
op.alter_column(
"chat_message", "new_chat_session_id", new_column_name="chat_session_id"
)
op.drop_constraint("chat_session_pkey", "chat_session", type_="primary")
op.drop_column("chat_session", "id")
op.alter_column("chat_session", "new_id", new_column_name="id")
op.create_primary_key("chat_session_pkey", "chat_session", ["id"])
op.create_foreign_key(
"chat_message_chat_session_id_fkey",
"chat_message",
"chat_session",
["chat_session_id"],
["id"],
ondelete="CASCADE",
)
def downgrade() -> None:
op.drop_constraint(
"chat_message_chat_session_id_fkey", "chat_message", type_="foreignkey"
)
op.add_column(
"chat_session",
sa.Column("old_id", sa.Integer, autoincrement=True, nullable=True),
)
op.execute("CREATE SEQUENCE chat_session_old_id_seq OWNED BY chat_session.old_id;")
op.execute(
"ALTER TABLE chat_session ALTER COLUMN old_id SET DEFAULT nextval('chat_session_old_id_seq');"
)
op.execute(
"UPDATE chat_session SET old_id = nextval('chat_session_old_id_seq') WHERE old_id IS NULL;"
)
op.alter_column("chat_session", "old_id", nullable=False)
op.drop_constraint("chat_session_pkey", "chat_session", type_="primary")
op.create_primary_key("chat_session_pkey", "chat_session", ["old_id"])
op.add_column(
"chat_message",
sa.Column("old_chat_session_id", sa.Integer, nullable=True),
)
op.execute(
"""
UPDATE chat_message
SET old_chat_session_id = cs.old_id
FROM chat_session cs
WHERE chat_message.chat_session_id = cs.id;
"""
)
op.drop_column("chat_message", "chat_session_id")
op.alter_column(
"chat_message", "old_chat_session_id", new_column_name="chat_session_id"
)
op.create_foreign_key(
"chat_message_chat_session_id_fkey",
"chat_message",
"chat_session",
["chat_session_id"],
["old_id"],
ondelete="CASCADE",
)
op.drop_column("chat_session", "id")
op.alter_column("chat_session", "old_id", new_column_name="id")
op.alter_column(
"chat_session",
"id",
type_=sa.Integer(),
existing_type=sa.Integer(),
existing_nullable=False,
existing_server_default=False,
)
# Rename the sequence
op.execute("ALTER SEQUENCE chat_session_old_id_seq RENAME TO chat_session_id_seq;")
# Update the default value to use the renamed sequence
op.alter_column(
"chat_session",
"id",
server_default=sa.text("nextval('chat_session_id_seq'::regclass)"),
)

View File

@ -1,5 +1,6 @@
import re import re
from typing import cast from typing import cast
from uuid import UUID
from fastapi.datastructures import Headers from fastapi.datastructures import Headers
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@ -34,7 +35,7 @@ def llm_doc_from_inference_section(inference_section: InferenceSection) -> LlmDo
def create_chat_chain( def create_chat_chain(
chat_session_id: int, chat_session_id: UUID,
db_session: Session, db_session: Session,
prefetch_tool_calls: bool = True, prefetch_tool_calls: bool = True,
# Optional id at which we finish processing # Optional id at which we finish processing

View File

@ -43,7 +43,7 @@ logger = setup_logger()
def get_chat_session_by_id( def get_chat_session_by_id(
chat_session_id: int, chat_session_id: UUID,
user_id: UUID | None, user_id: UUID | None,
db_session: Session, db_session: Session,
include_deleted: bool = False, include_deleted: bool = False,
@ -87,9 +87,9 @@ def get_chat_sessions_by_slack_thread_id(
def get_valid_messages_from_query_sessions( def get_valid_messages_from_query_sessions(
chat_session_ids: list[int], chat_session_ids: list[UUID],
db_session: Session, db_session: Session,
) -> dict[int, str]: ) -> dict[UUID, str]:
user_message_subquery = ( user_message_subquery = (
select( select(
ChatMessage.chat_session_id, func.min(ChatMessage.id).label("user_msg_id") ChatMessage.chat_session_id, func.min(ChatMessage.id).label("user_msg_id")
@ -196,7 +196,7 @@ def delete_orphaned_search_docs(db_session: Session) -> None:
def delete_messages_and_files_from_chat_session( def delete_messages_and_files_from_chat_session(
chat_session_id: int, db_session: Session chat_session_id: UUID, db_session: Session
) -> None: ) -> None:
# Select messages older than cutoff_time with files # Select messages older than cutoff_time with files
messages_with_files = db_session.execute( messages_with_files = db_session.execute(
@ -253,7 +253,7 @@ def create_chat_session(
def update_chat_session( def update_chat_session(
db_session: Session, db_session: Session,
user_id: UUID | None, user_id: UUID | None,
chat_session_id: int, chat_session_id: UUID,
description: str | None = None, description: str | None = None,
sharing_status: ChatSessionSharedStatus | None = None, sharing_status: ChatSessionSharedStatus | None = None,
) -> ChatSession: ) -> ChatSession:
@ -276,7 +276,7 @@ def update_chat_session(
def delete_chat_session( def delete_chat_session(
user_id: UUID | None, user_id: UUID | None,
chat_session_id: int, chat_session_id: UUID,
db_session: Session, db_session: Session,
hard_delete: bool = HARD_DELETE_CHATS, hard_delete: bool = HARD_DELETE_CHATS,
) -> None: ) -> None:
@ -337,7 +337,7 @@ def get_chat_message(
def get_chat_messages_by_sessions( def get_chat_messages_by_sessions(
chat_session_ids: list[int], chat_session_ids: list[UUID],
user_id: UUID | None, user_id: UUID | None,
db_session: Session, db_session: Session,
skip_permission_check: bool = False, skip_permission_check: bool = False,
@ -370,7 +370,7 @@ def get_search_docs_for_chat_message(
def get_chat_messages_by_session( def get_chat_messages_by_session(
chat_session_id: int, chat_session_id: UUID,
user_id: UUID | None, user_id: UUID | None,
db_session: Session, db_session: Session,
skip_permission_check: bool = False, skip_permission_check: bool = False,
@ -397,7 +397,7 @@ def get_chat_messages_by_session(
def get_or_create_root_message( def get_or_create_root_message(
chat_session_id: int, chat_session_id: UUID,
db_session: Session, db_session: Session,
) -> ChatMessage: ) -> ChatMessage:
try: try:
@ -433,7 +433,7 @@ def get_or_create_root_message(
def reserve_message_id( def reserve_message_id(
db_session: Session, db_session: Session,
chat_session_id: int, chat_session_id: UUID,
parent_message: int, parent_message: int,
message_type: MessageType, message_type: MessageType,
) -> int: ) -> int:
@ -460,7 +460,7 @@ def reserve_message_id(
def create_new_chat_message( def create_new_chat_message(
chat_session_id: int, chat_session_id: UUID,
parent_message: ChatMessage, parent_message: ChatMessage,
message: str, message: str,
prompt_id: int | None, prompt_id: int | None,

View File

@ -5,9 +5,12 @@ from typing import Any
from typing import Literal from typing import Literal
from typing import NotRequired from typing import NotRequired
from typing import Optional from typing import Optional
from uuid import uuid4
from typing_extensions import TypedDict # noreorder from typing_extensions import TypedDict # noreorder
from uuid import UUID from uuid import UUID
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from fastapi_users_db_sqlalchemy import SQLAlchemyBaseOAuthAccountTableUUID from fastapi_users_db_sqlalchemy import SQLAlchemyBaseOAuthAccountTableUUID
from fastapi_users_db_sqlalchemy import SQLAlchemyBaseUserTableUUID from fastapi_users_db_sqlalchemy import SQLAlchemyBaseUserTableUUID
from fastapi_users_db_sqlalchemy.access_token import SQLAlchemyBaseAccessTokenTableUUID from fastapi_users_db_sqlalchemy.access_token import SQLAlchemyBaseAccessTokenTableUUID
@ -920,7 +923,9 @@ class ToolCall(Base):
class ChatSession(Base): class ChatSession(Base):
__tablename__ = "chat_session" __tablename__ = "chat_session"
id: Mapped[int] = mapped_column(primary_key=True) id: Mapped[UUID] = mapped_column(
PGUUID(as_uuid=True), primary_key=True, default=uuid4
)
user_id: Mapped[UUID | None] = mapped_column( user_id: Mapped[UUID | None] = mapped_column(
ForeignKey("user.id", ondelete="CASCADE"), nullable=True ForeignKey("user.id", ondelete="CASCADE"), nullable=True
) )
@ -990,7 +995,9 @@ class ChatMessage(Base):
__tablename__ = "chat_message" __tablename__ = "chat_message"
id: Mapped[int] = mapped_column(primary_key=True) id: Mapped[int] = mapped_column(primary_key=True)
chat_session_id: Mapped[int] = mapped_column(ForeignKey("chat_session.id")) chat_session_id: Mapped[UUID] = mapped_column(
PGUUID(as_uuid=True), ForeignKey("chat_session.id")
)
alternate_assistant_id = mapped_column( alternate_assistant_id = mapped_column(
Integer, ForeignKey("persona.id"), nullable=True Integer, ForeignKey("persona.id"), nullable=True

View File

@ -1,3 +1,5 @@
from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
from danswer.server.query_and_chat.models import ChatSessionDetails from danswer.server.query_and_chat.models import ChatSessionDetails
@ -23,7 +25,7 @@ class FolderUpdateRequest(BaseModel):
class FolderChatSessionRequest(BaseModel): class FolderChatSessionRequest(BaseModel):
chat_session_id: int chat_session_id: UUID
class DeleteFolderOptions(BaseModel): class DeleteFolderOptions(BaseModel):

View File

@ -4,6 +4,7 @@ import uuid
from collections.abc import Callable from collections.abc import Callable
from collections.abc import Generator from collections.abc import Generator
from typing import Tuple from typing import Tuple
from uuid import UUID
from fastapi import APIRouter from fastapi import APIRouter
from fastapi import Depends from fastapi import Depends
@ -131,7 +132,7 @@ def update_chat_session_model(
@router.get("/get-chat-session/{session_id}") @router.get("/get-chat-session/{session_id}")
def get_chat_session( def get_chat_session(
session_id: int, session_id: UUID,
is_shared: bool = False, is_shared: bool = False,
user: User | None = Depends(current_user), user: User | None = Depends(current_user),
db_session: Session = Depends(get_session), db_session: Session = Depends(get_session),
@ -254,7 +255,7 @@ def rename_chat_session(
@router.patch("/chat-session/{session_id}") @router.patch("/chat-session/{session_id}")
def patch_chat_session( def patch_chat_session(
session_id: int, session_id: UUID,
chat_session_update_req: ChatSessionUpdateRequest, chat_session_update_req: ChatSessionUpdateRequest,
user: User | None = Depends(current_user), user: User | None = Depends(current_user),
db_session: Session = Depends(get_session), db_session: Session = Depends(get_session),
@ -271,7 +272,7 @@ def patch_chat_session(
@router.delete("/delete-chat-session/{session_id}") @router.delete("/delete-chat-session/{session_id}")
def delete_chat_session_by_id( def delete_chat_session_by_id(
session_id: int, session_id: UUID,
user: User | None = Depends(current_user), user: User | None = Depends(current_user),
db_session: Session = Depends(get_session), db_session: Session = Depends(get_session),
) -> None: ) -> None:

View File

@ -1,5 +1,6 @@
from datetime import datetime from datetime import datetime
from typing import Any from typing import Any
from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
from pydantic import model_validator from pydantic import model_validator
@ -34,7 +35,7 @@ class SimpleQueryRequest(BaseModel):
class UpdateChatSessionThreadRequest(BaseModel): class UpdateChatSessionThreadRequest(BaseModel):
# If not specified, use Danswer default persona # If not specified, use Danswer default persona
chat_session_id: int chat_session_id: UUID
new_alternate_model: str new_alternate_model: str
@ -45,7 +46,7 @@ class ChatSessionCreationRequest(BaseModel):
class CreateChatSessionID(BaseModel): class CreateChatSessionID(BaseModel):
chat_session_id: int chat_session_id: UUID
class ChatFeedbackRequest(BaseModel): class ChatFeedbackRequest(BaseModel):
@ -75,7 +76,7 @@ Currently the different branches are generated by changing the search query
class CreateChatMessageRequest(ChunkContext): class CreateChatMessageRequest(ChunkContext):
"""Before creating messages, be sure to create a chat_session and get an id""" """Before creating messages, be sure to create a chat_session and get an id"""
chat_session_id: int chat_session_id: UUID
# This is the primary-key (unique identifier) for the previous message of the tree # This is the primary-key (unique identifier) for the previous message of the tree
parent_message_id: int | None parent_message_id: int | None
# New message contents # New message contents
@ -115,13 +116,18 @@ class CreateChatMessageRequest(ChunkContext):
) )
return self return self
def model_dump(self, *args: Any, **kwargs: Any) -> dict[str, Any]:
data = super().model_dump(*args, **kwargs)
data["chat_session_id"] = str(data["chat_session_id"])
return data
class ChatMessageIdentifier(BaseModel): class ChatMessageIdentifier(BaseModel):
message_id: int message_id: int
class ChatRenameRequest(BaseModel): class ChatRenameRequest(BaseModel):
chat_session_id: int chat_session_id: UUID
name: str | None = None name: str | None = None
@ -134,7 +140,7 @@ class RenameChatSessionResponse(BaseModel):
class ChatSessionDetails(BaseModel): class ChatSessionDetails(BaseModel):
id: int id: UUID
name: str name: str
persona_id: int | None = None persona_id: int | None = None
time_created: str time_created: str
@ -175,7 +181,7 @@ class ChatMessageDetail(BaseModel):
overridden_model: str | None overridden_model: str | None
alternate_assistant_id: int | None = None alternate_assistant_id: int | None = None
# Dict mapping citation number to db_doc_id # Dict mapping citation number to db_doc_id
chat_session_id: int | None = None chat_session_id: UUID | None = None
citations: dict[int, int] | None = None citations: dict[int, int] | None = None
files: list[FileDescriptor] files: list[FileDescriptor]
tool_calls: list[ToolCallFinalResult] tool_calls: list[ToolCallFinalResult]
@ -187,14 +193,14 @@ class ChatMessageDetail(BaseModel):
class SearchSessionDetailResponse(BaseModel): class SearchSessionDetailResponse(BaseModel):
search_session_id: int search_session_id: UUID
description: str description: str
documents: list[SearchDoc] documents: list[SearchDoc]
messages: list[ChatMessageDetail] messages: list[ChatMessageDetail]
class ChatSessionDetailResponse(BaseModel): class ChatSessionDetailResponse(BaseModel):
chat_session_id: int chat_session_id: UUID
description: str description: str
persona_id: int | None = None persona_id: int | None = None
persona_name: str | None persona_name: str | None

View File

@ -1,3 +1,5 @@
from uuid import UUID
from fastapi import APIRouter from fastapi import APIRouter
from fastapi import Depends from fastapi import Depends
from fastapi import HTTPException from fastapi import HTTPException
@ -186,7 +188,7 @@ def get_user_search_sessions(
@basic_router.get("/search-session/{session_id}") @basic_router.get("/search-session/{session_id}")
def get_search_session( def get_search_session(
session_id: int, session_id: UUID,
is_shared: bool = False, is_shared: bool = False,
user: User | None = Depends(current_user), user: User | None = Depends(current_user),
db_session: Session = Depends(get_session), db_session: Session = Depends(get_session),

View File

@ -1,4 +1,5 @@
from typing import Any from typing import Any
from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
from pydantic import model_validator from pydantic import model_validator
@ -40,7 +41,7 @@ class ToolCallFinalResult(ToolCallKickoff):
class DynamicSchemaInfo(BaseModel): class DynamicSchemaInfo(BaseModel):
chat_session_id: int | None chat_session_id: UUID | None
message_id: int | None message_id: int | None

View File

@ -42,7 +42,7 @@ def get_empty_chat_messages_entries__paginated(
message_skeletons.append( message_skeletons.append(
ChatMessageSkeleton( ChatMessageSkeleton(
message_id=chat_session.id, message_id=message.id,
chat_session_id=chat_session.id, chat_session_id=chat_session.id,
user_id=str(chat_session.user_id) if chat_session.user_id else None, user_id=str(chat_session.user_id) if chat_session.user_id else None,
flow_type=flow_type, flow_type=flow_type,

View File

@ -1,3 +1,5 @@
from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
from pydantic import Field from pydantic import Field
@ -36,7 +38,7 @@ class BasicCreateChatMessageRequest(ChunkContext):
Note, for simplicity this option only allows for a single linear chain of messages Note, for simplicity this option only allows for a single linear chain of messages
""" """
chat_session_id: int chat_session_id: UUID
# New message contents # New message contents
message: str message: str
# Defaults to using retrieval with no additional filters # Defaults to using retrieval with no additional filters

View File

@ -4,6 +4,7 @@ from datetime import datetime
from datetime import timedelta from datetime import timedelta
from datetime import timezone from datetime import timezone
from typing import Literal from typing import Literal
from uuid import UUID
from fastapi import APIRouter from fastapi import APIRouter
from fastapi import Depends from fastapi import Depends
@ -83,7 +84,7 @@ class MessageSnapshot(BaseModel):
class ChatSessionMinimal(BaseModel): class ChatSessionMinimal(BaseModel):
id: int id: UUID
user_email: str user_email: str
name: str | None name: str | None
first_user_message: str first_user_message: str
@ -95,7 +96,7 @@ class ChatSessionMinimal(BaseModel):
class ChatSessionSnapshot(BaseModel): class ChatSessionSnapshot(BaseModel):
id: int id: UUID
user_email: str user_email: str
name: str | None name: str | None
messages: list[MessageSnapshot] messages: list[MessageSnapshot]
@ -105,7 +106,7 @@ class ChatSessionSnapshot(BaseModel):
class QuestionAnswerPairSnapshot(BaseModel): class QuestionAnswerPairSnapshot(BaseModel):
chat_session_id: int chat_session_id: UUID
# 1-indexed message number in the chat_session # 1-indexed message number in the chat_session
# e.g. the first message pair in the chat_session is 1, the second is 2, etc. # e.g. the first message pair in the chat_session is 1, the second is 2, etc.
message_pair_num: int message_pair_num: int
@ -350,7 +351,7 @@ def get_chat_session_history(
@router.get("/admin/chat-session-history/{chat_session_id}") @router.get("/admin/chat-session-history/{chat_session_id}")
def get_chat_session_admin( def get_chat_session_admin(
chat_session_id: int, chat_session_id: UUID,
_: User | None = Depends(current_admin_user), _: User | None = Depends(current_admin_user),
db_session: Session = Depends(get_session), db_session: Session = Depends(get_session),
) -> ChatSessionSnapshot: ) -> ChatSessionSnapshot:

View File

@ -1,5 +1,6 @@
from datetime import datetime from datetime import datetime
from enum import Enum from enum import Enum
from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
@ -14,7 +15,7 @@ class FlowType(str, Enum):
class ChatMessageSkeleton(BaseModel): class ChatMessageSkeleton(BaseModel):
message_id: int message_id: int
chat_session_id: int chat_session_id: UUID
user_id: str | None user_id: str | None
flow_type: FlowType flow_type: FlowType
time_sent: datetime time_sent: datetime

View File

@ -1,4 +1,5 @@
import json import json
from uuid import UUID
import requests import requests
from requests.models import Response from requests.models import Response
@ -44,7 +45,7 @@ class ChatSessionManager:
@staticmethod @staticmethod
def send_message( def send_message(
chat_session_id: int, chat_session_id: UUID,
message: str, message: str,
parent_message_id: int | None = None, parent_message_id: int | None = None,
user_performing_action: DATestUser | None = None, user_performing_action: DATestUser | None = None,

View File

@ -123,14 +123,14 @@ class DATestPersona(BaseModel):
# #
class DATestChatSession(BaseModel): class DATestChatSession(BaseModel):
id: int id: UUID
persona_id: int persona_id: int
description: str description: str
class DATestChatMessage(BaseModel): class DATestChatMessage(BaseModel):
id: str | None = None id: str | None = None
chat_session_id: int chat_session_id: UUID
parent_message_id: str | None parent_message_id: str | None
message: str message: str
response: str response: str

View File

@ -1,4 +1,5 @@
import unittest import unittest
import uuid
from typing import Any from typing import Any
from unittest.mock import patch from unittest.mock import patch
@ -73,7 +74,7 @@ class TestCustomTool(unittest.TestCase):
} }
validate_openapi_schema(self.openapi_schema) validate_openapi_schema(self.openapi_schema)
self.dynamic_schema_info: DynamicSchemaInfo = DynamicSchemaInfo( self.dynamic_schema_info: DynamicSchemaInfo = DynamicSchemaInfo(
chat_session_id=10, message_id=20 chat_session_id=uuid.uuid4(), message_id=20
) )
@patch("danswer.tools.custom.custom_tool.requests.request") @patch("danswer.tools.custom.custom_tool.requests.request")

View File

@ -145,19 +145,17 @@ export function ChatPage({
const existingChatIdRaw = searchParams.get("chatId"); const existingChatIdRaw = searchParams.get("chatId");
const currentPersonaId = searchParams.get(SEARCH_PARAM_NAMES.PERSONA_ID); const currentPersonaId = searchParams.get(SEARCH_PARAM_NAMES.PERSONA_ID);
const existingChatSessionId = existingChatIdRaw const existingChatSessionId = existingChatIdRaw ? existingChatIdRaw : null;
? parseInt(existingChatIdRaw)
: null;
const selectedChatSession = chatSessions.find( const selectedChatSession = chatSessions.find(
(chatSession) => chatSession.id === existingChatSessionId (chatSession) => chatSession.id === existingChatSessionId
); );
const chatSessionIdRef = useRef<number | null>(existingChatSessionId); const chatSessionIdRef = useRef<string | null>(existingChatSessionId);
// Only updates on session load (ie. rename / switching chat session) // Only updates on session load (ie. rename / switching chat session)
// Useful for determining which session has been loaded (i.e. still on `new, empty session` or `previous session`) // Useful for determining which session has been loaded (i.e. still on `new, empty session` or `previous session`)
const loadedIdSessionRef = useRef<number | null>(existingChatSessionId); const loadedIdSessionRef = useRef<string | null>(existingChatSessionId);
// Assistants in order // Assistants in order
const { finalAssistants } = useMemo(() => { const { finalAssistants } = useMemo(() => {
@ -448,11 +446,11 @@ export function ChatPage({
); );
const [completeMessageDetail, setCompleteMessageDetail] = useState< const [completeMessageDetail, setCompleteMessageDetail] = useState<
Map<number | null, Map<number, Message>> Map<string | null, Map<number, Message>>
>(new Map()); >(new Map());
const updateCompleteMessageDetail = ( const updateCompleteMessageDetail = (
sessionId: number | null, sessionId: string | null,
messageMap: Map<number, Message> messageMap: Map<number, Message>
) => { ) => {
setCompleteMessageDetail((prevState) => { setCompleteMessageDetail((prevState) => {
@ -463,13 +461,13 @@ export function ChatPage({
}; };
const currentMessageMap = ( const currentMessageMap = (
messageDetail: Map<number | null, Map<number, Message>> messageDetail: Map<string | null, Map<number, Message>>
) => { ) => {
return ( return (
messageDetail.get(chatSessionIdRef.current) || new Map<number, Message>() messageDetail.get(chatSessionIdRef.current) || new Map<number, Message>()
); );
}; };
const currentSessionId = (): number => { const currentSessionId = (): string => {
return chatSessionIdRef.current!; return chatSessionIdRef.current!;
}; };
@ -484,7 +482,7 @@ export function ChatPage({
// if calling this function repeatedly with short delay, stay may not update in time // if calling this function repeatedly with short delay, stay may not update in time
// and result in weird behavipr // and result in weird behavipr
completeMessageMapOverride?: Map<number, Message> | null; completeMessageMapOverride?: Map<number, Message> | null;
chatSessionId?: number; chatSessionId?: string;
replacementsMap?: Map<number, number> | null; replacementsMap?: Map<number, number> | null;
makeLatestChildMessage?: boolean; makeLatestChildMessage?: boolean;
}) => { }) => {
@ -559,23 +557,23 @@ export function ChatPage({
const [submittedMessage, setSubmittedMessage] = useState(""); const [submittedMessage, setSubmittedMessage] = useState("");
const [chatState, setChatState] = useState<Map<number | null, ChatState>>( const [chatState, setChatState] = useState<Map<string | null, ChatState>>(
new Map([[chatSessionIdRef.current, "input"]]) new Map([[chatSessionIdRef.current, "input"]])
); );
const [regenerationState, setRegenerationState] = useState< const [regenerationState, setRegenerationState] = useState<
Map<number | null, RegenerationState | null> Map<string | null, RegenerationState | null>
>(new Map([[null, null]])); >(new Map([[null, null]]));
const [abortControllers, setAbortControllers] = useState< const [abortControllers, setAbortControllers] = useState<
Map<number | null, AbortController> Map<string | null, AbortController>
>(new Map()); >(new Map());
// Updates "null" session values to new session id for // Updates "null" session values to new session id for
// regeneration, chat, and abort controller state, messagehistory // regeneration, chat, and abort controller state, messagehistory
const updateStatesWithNewSessionId = (newSessionId: number) => { const updateStatesWithNewSessionId = (newSessionId: string) => {
const updateState = ( const updateState = (
setState: Dispatch<SetStateAction<Map<number | null, any>>>, setState: Dispatch<SetStateAction<Map<string | null, any>>>,
defaultValue?: any defaultValue?: any
) => { ) => {
setState((prevState) => { setState((prevState) => {
@ -610,7 +608,7 @@ export function ChatPage({
chatSessionIdRef.current = newSessionId; chatSessionIdRef.current = newSessionId;
}; };
const updateChatState = (newState: ChatState, sessionId?: number | null) => { const updateChatState = (newState: ChatState, sessionId?: string | null) => {
setChatState((prevState) => { setChatState((prevState) => {
const newChatState = new Map(prevState); const newChatState = new Map(prevState);
newChatState.set( newChatState.set(
@ -635,7 +633,7 @@ export function ChatPage({
const updateRegenerationState = ( const updateRegenerationState = (
newState: RegenerationState | null, newState: RegenerationState | null,
sessionId?: number | null sessionId?: string | null
) => { ) => {
setRegenerationState((prevState) => { setRegenerationState((prevState) => {
const newRegenerationState = new Map(prevState); const newRegenerationState = new Map(prevState);
@ -647,18 +645,18 @@ export function ChatPage({
}); });
}; };
const resetRegenerationState = (sessionId?: number | null) => { const resetRegenerationState = (sessionId?: string | null) => {
updateRegenerationState(null, sessionId); updateRegenerationState(null, sessionId);
}; };
const currentRegenerationState = (): RegenerationState | null => { const currentRegenerationState = (): RegenerationState | null => {
return regenerationState.get(currentSessionId()) || null; return regenerationState.get(currentSessionId()) || null;
}; };
const [canContinue, setCanContinue] = useState<Map<number | null, boolean>>( const [canContinue, setCanContinue] = useState<Map<string | null, boolean>>(
new Map([[null, false]]) new Map([[null, false]])
); );
const updateCanContinue = (newState: boolean, sessionId?: number | null) => { const updateCanContinue = (newState: boolean, sessionId?: string | null) => {
setCanContinue((prevState) => { setCanContinue((prevState) => {
const newCanContinueState = new Map(prevState); const newCanContinueState = new Map(prevState);
newCanContinueState.set( newCanContinueState.set(
@ -1003,7 +1001,7 @@ export function ChatPage({
setAlternativeGeneratingAssistant(alternativeAssistantOverride); setAlternativeGeneratingAssistant(alternativeAssistantOverride);
clientScrollToBottom(); clientScrollToBottom();
let currChatSessionId: number; let currChatSessionId: string;
const isNewSession = chatSessionIdRef.current === null; const isNewSession = chatSessionIdRef.current === null;
const searchParamBasedChatSessionName = const searchParamBasedChatSessionName =
searchParams.get(SEARCH_PARAM_NAMES.TITLE) || null; searchParams.get(SEARCH_PARAM_NAMES.TITLE) || null;
@ -1014,7 +1012,7 @@ export function ChatPage({
searchParamBasedChatSessionName searchParamBasedChatSessionName
); );
} else { } else {
currChatSessionId = chatSessionIdRef.current as number; currChatSessionId = chatSessionIdRef.current as string;
} }
frozenSessionId = currChatSessionId; frozenSessionId = currChatSessionId;
@ -1598,7 +1596,7 @@ export function ChatPage({
} }
const [visibleRange, setVisibleRange] = useState< const [visibleRange, setVisibleRange] = useState<
Map<number | null, VisibleRange> Map<string | null, VisibleRange>
>(() => { >(() => {
const initialRange: VisibleRange = { const initialRange: VisibleRange = {
start: 0, start: 0,

View File

@ -30,7 +30,7 @@ const FolderItem = ({
initiallySelected, initiallySelected,
}: { }: {
folder: Folder; folder: Folder;
currentChatId?: number; currentChatId?: string;
isInitiallyExpanded: boolean; isInitiallyExpanded: boolean;
initiallySelected: boolean; initiallySelected: boolean;
}) => { }) => {
@ -145,10 +145,7 @@ const FolderItem = ({
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => { const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); event.preventDefault();
setIsDragOver(false); setIsDragOver(false);
const chatSessionId = parseInt( const chatSessionId = event.dataTransfer.getData(CHAT_SESSION_ID_KEY);
event.dataTransfer.getData(CHAT_SESSION_ID_KEY),
10
);
try { try {
await addChatToFolder(folder.folder_id, chatSessionId); await addChatToFolder(folder.folder_id, chatSessionId);
router.refresh(); // Refresh to show the updated folder contents router.refresh(); // Refresh to show the updated folder contents
@ -302,7 +299,7 @@ export const FolderList = ({
newFolderId, newFolderId,
}: { }: {
folders: Folder[]; folders: Folder[];
currentChatId?: number; currentChatId?: string;
openedFolders?: { [key: number]: boolean }; openedFolders?: { [key: number]: boolean };
newFolderId: number | null; newFolderId: number | null;
}) => { }) => {

View File

@ -17,7 +17,7 @@ export async function createFolder(folderName: string): Promise<number> {
// Function to add a chat session to a folder // Function to add a chat session to a folder
export async function addChatToFolder( export async function addChatToFolder(
folderId: number, folderId: number,
chatSessionId: number chatSessionId: string
): Promise<void> { ): Promise<void> {
const response = await fetch(`/api/folder/${folderId}/add-chat-session`, { const response = await fetch(`/api/folder/${folderId}/add-chat-session`, {
method: "POST", method: "POST",
@ -34,7 +34,7 @@ export async function addChatToFolder(
// Function to remove a chat session from a folder // Function to remove a chat session from a folder
export async function removeChatFromFolder( export async function removeChatFromFolder(
folderId: number, folderId: number,
chatSessionId: number chatSessionId: string
): Promise<void> { ): Promise<void> {
const response = await fetch(`/api/folder/${folderId}/remove-chat-session`, { const response = await fetch(`/api/folder/${folderId}/remove-chat-session`, {
method: "POST", method: "POST",

View File

@ -86,7 +86,7 @@ export function ChatInputBar({
setFiles: (files: FileDescriptor[]) => void; setFiles: (files: FileDescriptor[]) => void;
handleFileUpload: (files: File[]) => void; handleFileUpload: (files: File[]) => void;
textAreaRef: React.RefObject<HTMLTextAreaElement>; textAreaRef: React.RefObject<HTMLTextAreaElement>;
chatSessionId?: number; chatSessionId?: string;
refreshUser: () => void; refreshUser: () => void;
}) { }) {
useEffect(() => { useEffect(() => {

View File

@ -60,7 +60,7 @@ export interface ToolCallFinalResult {
} }
export interface ChatSession { export interface ChatSession {
id: number; id: string;
name: string; name: string;
persona_id: number; persona_id: number;
time_created: string; time_created: string;
@ -70,7 +70,7 @@ export interface ChatSession {
} }
export interface SearchSession { export interface SearchSession {
search_session_id: number; search_session_id: string;
documents: SearchDanswerDocument[]; documents: SearchDanswerDocument[];
messages: BackendMessage[]; messages: BackendMessage[];
description: string; description: string;
@ -97,7 +97,7 @@ export interface Message {
} }
export interface BackendChatSession { export interface BackendChatSession {
chat_session_id: number; chat_session_id: string;
description: string; description: string;
persona_id: number; persona_id: number;
persona_name: string; persona_name: string;
@ -110,7 +110,7 @@ export interface BackendChatSession {
export interface BackendMessage { export interface BackendMessage {
message_id: number; message_id: number;
comments: any; comments: any;
chat_session_id: number; chat_session_id: string;
parent_message: number | null; parent_message: number | null;
latest_child_message: number | null; latest_child_message: number | null;
message: string; message: string;

View File

@ -55,7 +55,7 @@ export function getChatRetentionInfo(
} }
export async function updateModelOverrideForChatSession( export async function updateModelOverrideForChatSession(
chatSessionId: number, chatSessionId: string,
newAlternateModel: string newAlternateModel: string
) { ) {
const response = await fetch("/api/chat/update-chat-session-model", { const response = await fetch("/api/chat/update-chat-session-model", {
@ -74,7 +74,7 @@ export async function updateModelOverrideForChatSession(
export async function createChatSession( export async function createChatSession(
personaId: number, personaId: number,
description: string | null description: string | null
): Promise<number> { ): Promise<string> {
const createChatSessionResponse = await fetch( const createChatSessionResponse = await fetch(
"/api/chat/create-chat-session", "/api/chat/create-chat-session",
{ {
@ -131,7 +131,7 @@ export async function* sendMessage({
message: string; message: string;
fileDescriptors: FileDescriptor[]; fileDescriptors: FileDescriptor[];
parentMessageId: number | null; parentMessageId: number | null;
chatSessionId: number; chatSessionId: string;
promptId: number | null | undefined; promptId: number | null | undefined;
filters: Filters | null; filters: Filters | null;
selectedDocumentIds: number[] | null; selectedDocumentIds: number[] | null;
@ -203,7 +203,7 @@ export async function* sendMessage({
yield* handleSSEStream<PacketType>(response); yield* handleSSEStream<PacketType>(response);
} }
export async function nameChatSession(chatSessionId: number, message: string) { export async function nameChatSession(chatSessionId: string, message: string) {
const response = await fetch("/api/chat/rename-chat-session", { const response = await fetch("/api/chat/rename-chat-session", {
method: "PUT", method: "PUT",
headers: { headers: {
@ -252,7 +252,7 @@ export async function handleChatFeedback(
return response; return response;
} }
export async function renameChatSession( export async function renameChatSession(
chatSessionId: number, chatSessionId: string,
newName: string newName: string
) { ) {
const response = await fetch(`/api/chat/rename-chat-session`, { const response = await fetch(`/api/chat/rename-chat-session`, {
@ -269,7 +269,7 @@ export async function renameChatSession(
return response; return response;
} }
export async function deleteChatSession(chatSessionId: number) { export async function deleteChatSession(chatSessionId: string) {
const response = await fetch( const response = await fetch(
`/api/chat/delete-chat-session/${chatSessionId}`, `/api/chat/delete-chat-session/${chatSessionId}`,
{ {
@ -348,6 +348,7 @@ export function getCitedDocumentsFromMessage(message: Message) {
} }
export function groupSessionsByDateRange(chatSessions: ChatSession[]) { export function groupSessionsByDateRange(chatSessions: ChatSession[]) {
console.log(chatSessions);
const today = new Date(); const today = new Date();
today.setHours(0, 0, 0, 0); // Set to start of today for accurate comparison today.setHours(0, 0, 0, 0); // Set to start of today for accurate comparison
@ -584,7 +585,7 @@ const PARAMS_TO_SKIP = [
export function buildChatUrl( export function buildChatUrl(
existingSearchParams: ReadonlyURLSearchParams, existingSearchParams: ReadonlyURLSearchParams,
chatSessionId: number | null, chatSessionId: string | null,
personaId: number | null, personaId: number | null,
search?: boolean search?: boolean
) { ) {

View File

@ -6,12 +6,12 @@ import { ChatSessionSharedStatus } from "../interfaces";
import { FiCopy } from "react-icons/fi"; import { FiCopy } from "react-icons/fi";
import { CopyButton } from "@/components/CopyButton"; import { CopyButton } from "@/components/CopyButton";
function buildShareLink(chatSessionId: number) { function buildShareLink(chatSessionId: string) {
const baseUrl = `${window.location.protocol}//${window.location.host}`; const baseUrl = `${window.location.protocol}//${window.location.host}`;
return `${baseUrl}/chat/shared/${chatSessionId}`; return `${baseUrl}/chat/shared/${chatSessionId}`;
} }
async function generateShareLink(chatSessionId: number) { async function generateShareLink(chatSessionId: string) {
const response = await fetch(`/api/chat/chat-session/${chatSessionId}`, { const response = await fetch(`/api/chat/chat-session/${chatSessionId}`, {
method: "PATCH", method: "PATCH",
headers: { headers: {
@ -26,7 +26,7 @@ async function generateShareLink(chatSessionId: number) {
return null; return null;
} }
async function deleteShareLink(chatSessionId: number) { async function deleteShareLink(chatSessionId: string) {
const response = await fetch(`/api/chat/chat-session/${chatSessionId}`, { const response = await fetch(`/api/chat/chat-session/${chatSessionId}`, {
method: "PATCH", method: "PATCH",
headers: { headers: {
@ -44,7 +44,7 @@ export function ShareChatSessionModal({
onShare, onShare,
onClose, onClose,
}: { }: {
chatSessionId: number; chatSessionId: string;
existingSharedStatus: ChatSessionSharedStatus; existingSharedStatus: ChatSessionSharedStatus;
onShare?: (shared: boolean) => void; onShare?: (shared: boolean) => void;
onClose: () => void; onClose: () => void;

View File

@ -14,7 +14,7 @@ interface LlmTabProps {
llmOverrideManager: LlmOverrideManager; llmOverrideManager: LlmOverrideManager;
currentLlm: string; currentLlm: string;
openModelSettings: () => void; openModelSettings: () => void;
chatSessionId?: number; chatSessionId?: string;
close: () => void; close: () => void;
currentAssistant: Persona; currentAssistant: Persona;
} }

View File

@ -23,7 +23,7 @@ export function PagesTab({
}: { }: {
page: pageType; page: pageType;
existingChats?: ChatSession[]; existingChats?: ChatSession[];
currentChatId?: number; currentChatId?: string;
folders?: Folder[]; folders?: Folder[];
openedFolders?: { [key: number]: boolean }; openedFolders?: { [key: number]: boolean };
closeSidebar?: () => void; closeSidebar?: () => void;
@ -44,10 +44,7 @@ export function PagesTab({
) => { ) => {
event.preventDefault(); event.preventDefault();
setIsDragOver(false); // Reset drag over state on drop setIsDragOver(false); // Reset drag over state on drop
const chatSessionId = parseInt( const chatSessionId = event.dataTransfer.getData(CHAT_SESSION_ID_KEY);
event.dataTransfer.getData(CHAT_SESSION_ID_KEY),
10
);
const folderId = event.dataTransfer.getData(FOLDER_ID_KEY); const folderId = event.dataTransfer.getData(FOLDER_ID_KEY);
if (folderId) { if (folderId) {

View File

@ -48,7 +48,7 @@ export default async function Page({ params }: { params: { chatId: string } }) {
const user = results[1] as User | null; const user = results[1] as User | null;
const chatSession = results[2] as BackendChatSession | null; const chatSession = results[2] as BackendChatSession | null;
const assistantsResponse = results[3] as FetchAssistantsResponse | null; const assistantsResponse = results[3] as FetchAssistantsResponse | null;
const [availableAssistants, _] = assistantsResponse ?? [[], null]; const [availableAssistants, error] = assistantsResponse ?? [[], null];
const authDisabled = authTypeMetadata?.authType === "disabled"; const authDisabled = authTypeMetadata?.authType === "disabled";
if (!authDisabled && !user) { if (!authDisabled && !user) {
@ -58,7 +58,6 @@ export default async function Page({ params }: { params: { chatId: string } }) {
if (user && !user.is_verified && authTypeMetadata?.requiresVerification) { if (user && !user.is_verified && authTypeMetadata?.requiresVerification) {
return redirect("/auth/waiting-on-verification"); return redirect("/auth/waiting-on-verification");
} }
const persona: Persona = const persona: Persona =
chatSession?.persona_id && availableAssistants?.length chatSession?.persona_id && availableAssistants?.length
? (availableAssistants.find((p) => p.id === chatSession.persona_id) ?? ? (availableAssistants.find((p) => p.id === chatSession.persona_id) ??
@ -72,7 +71,7 @@ export default async function Page({ params }: { params: { chatId: string } }) {
</div> </div>
<div className="flex relative bg-background text-default overflow-hidden pt-16 h-screen"> <div className="flex relative bg-background text-default overflow-hidden pt-16 h-screen">
<SharedChatDisplay chatSession={chatSession} persona={persona!} /> <SharedChatDisplay chatSession={chatSession} persona={persona} />
</div> </div>
</div> </div>
); );

View File

@ -168,13 +168,10 @@ export const SearchSection = ({
}); });
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const existingSearchIdRaw = searchParams.get("searchId"); const existingSearchessionId = searchParams.get("searchId");
const existingSearchessionId = existingSearchIdRaw
? parseInt(existingSearchIdRaw)
: null;
useEffect(() => { useEffect(() => {
if (existingSearchIdRaw == null) { if (existingSearchessionId == null) {
return; return;
} }
function extractFirstMessageByType( function extractFirstMessageByType(
@ -207,7 +204,7 @@ export const SearchSection = ({
quotes: null, quotes: null,
selectedDocIndices: null, selectedDocIndices: null,
error: null, error: null,
messageId: existingSearchIdRaw ? parseInt(existingSearchIdRaw) : null, messageId: searchSession.messages[0].message_id,
suggestedFlowType: null, suggestedFlowType: null,
additional_relevance: undefined, additional_relevance: undefined,
}; };
@ -219,7 +216,7 @@ export const SearchSection = ({
} }
} }
initialSessionFetch(); initialSessionFetch();
}, [existingSearchessionId, existingSearchIdRaw]); }, [existingSearchessionId]);
// Overrides for default behavior that only last a single query // Overrides for default behavior that only last a single query
const [defaultOverrides, setDefaultOverrides] = const [defaultOverrides, setDefaultOverrides] =
@ -328,7 +325,7 @@ export const SearchSection = ({
}; };
const updateMessageAndThreadId = ( const updateMessageAndThreadId = (
messageId: number, messageId: number,
chat_session_id: number chat_session_id: string
) => { ) => {
setSearchResponse((prevState) => ({ setSearchResponse((prevState) => ({
...(prevState || initialSearchResponse), ...(prevState || initialSearchResponse),

View File

@ -136,8 +136,10 @@ export async function fetchChatData(searchParams: {
); );
} }
// Larger ID -> created later chatSessions.sort(
chatSessions.sort((a, b) => (a.id > b.id ? -1 : 1)); (a, b) =>
new Date(b.time_created).getTime() - new Date(a.time_created).getTime()
);
let documentSets: DocumentSet[] = []; let documentSets: DocumentSet[] = [];
if (documentSetsResponse?.ok) { if (documentSetsResponse?.ok) {

View File

@ -152,7 +152,7 @@ export interface SearchRequestArgs {
updateError: (error: string) => void; updateError: (error: string) => void;
updateMessageAndThreadId: ( updateMessageAndThreadId: (
messageId: number, messageId: number,
chat_session_id: number chat_session_id: string
) => void; ) => void;
finishedSearching: () => void; finishedSearching: () => void;
updateComments: (comments: any) => void; updateComments: (comments: any) => void;