Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

query history pagination tests #3700

Merged
merged 6 commits into from
Jan 18, 2025
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion backend/ee/onyx/server/query_history/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def get_user_chat_sessions(
@router.get("/admin/chat-session-history")
def get_chat_session_history(
page_num: int = Query(0, ge=0),
page_size: int = Query(10, ge=10),
page_size: int = Query(10, ge=1),
feedback_type: QAFeedbackType | None = None,
start_time: datetime | None = None,
end_time: datetime | None = None,
Expand Down
53 changes: 18 additions & 35 deletions backend/tests/integration/common_utils/managers/chat.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import json
from datetime import datetime
from urllib.parse import urlencode
from uuid import UUID

import requests
from requests.models import Response

from ee.onyx.server.query_history.models import ChatSessionMinimal
from onyx.configs.constants import QAFeedbackType
from onyx.context.search.models import RetrievalDetails
from onyx.file_store.models import FileDescriptor
from onyx.llm.override_models import LLMOverride
from onyx.llm.override_models import PromptOverride
from onyx.server.documents.models import PaginatedReturn
from onyx.server.query_and_chat.models import ChatSessionCreationRequest
from onyx.server.query_and_chat.models import CreateChatMessageRequest
from tests.integration.common_utils.constants import API_SERVER_URL
Expand Down Expand Up @@ -140,35 +135,23 @@ def get_chat_history(
]

@staticmethod
def get_chat_session_history(
user_performing_action: DATestUser,
page_size: int,
page_num: int,
feedback_type: QAFeedbackType | None = None,
start_time: datetime | None = None,
end_time: datetime | None = None,
) -> PaginatedReturn[ChatSessionMinimal]:
query_params = {
"page_num": page_num,
"page_size": page_size,
"feedback_type": feedback_type if feedback_type else None,
"start_time": start_time if start_time else None,
"end_time": end_time if end_time else None,
}
# Remove None values
query_params = {
key: value for key, value in query_params.items() if value is not None
}

response = requests.get(
f"{API_SERVER_URL}/admin/chat-session-history?{urlencode(query_params, doseq=True)}",
headers=user_performing_action.headers,
def create_chat_message_feedback(
message_id: int,
is_positive: bool,
user_performing_action: DATestUser | None = None,
feedback_text: str | None = None,
predefined_feedback: str | None = None,
) -> None:
response = requests.post(
url=f"{API_SERVER_URL}/chat/create-chat-message-feedback",
json={
"chat_message_id": message_id,
"is_positive": is_positive,
"feedback_text": feedback_text,
"predefined_feedback": predefined_feedback,
},
headers=user_performing_action.headers
if user_performing_action
else GENERAL_HEADERS,
)
response.raise_for_status()
data = response.json()
return PaginatedReturn(
items=[
ChatSessionMinimal(**chat_session) for chat_session in data["items"]
],
total_items=data["total_items"],
)
83 changes: 83 additions & 0 deletions backend/tests/integration/common_utils/managers/query_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from datetime import datetime
from urllib.parse import urlencode
from uuid import UUID

import requests

from ee.onyx.server.query_history.models import ChatSessionMinimal
from ee.onyx.server.query_history.models import ChatSessionSnapshot
from onyx.configs.constants import QAFeedbackType
from onyx.server.documents.models import PaginatedReturn
from tests.integration.common_utils.constants import API_SERVER_URL
from tests.integration.common_utils.constants import GENERAL_HEADERS
from tests.integration.common_utils.test_models import DATestUser


class QueryHistoryManager:
@staticmethod
def get_query_history_page(
page_num: int = 0,
page_size: int = 10,
feedback_type: QAFeedbackType | None = None,
start_time: datetime | None = None,
end_time: datetime | None = None,
user_performing_action: DATestUser | None = None,
) -> PaginatedReturn[ChatSessionMinimal]:
query_params: dict[str, str | int] = {
"page_num": page_num,
"page_size": page_size,
}
if feedback_type:
query_params["feedback_type"] = feedback_type.value
if start_time:
query_params["start_time"] = start_time.isoformat()
if end_time:
query_params["end_time"] = end_time.isoformat()

response = requests.get(
url=f"{API_SERVER_URL}/admin/chat-session-history?{urlencode(query_params, doseq=True)}",
headers=user_performing_action.headers
if user_performing_action
else GENERAL_HEADERS,
)
response.raise_for_status()
data = response.json()
return PaginatedReturn(
items=[ChatSessionMinimal(**item) for item in data["items"]],
total_items=data["total_items"],
)

@staticmethod
def get_chat_session_admin(
chat_session_id: UUID | str,
user_performing_action: DATestUser | None = None,
) -> ChatSessionSnapshot:
response = requests.get(
url=f"{API_SERVER_URL}/admin/chat-session-history/{chat_session_id}",
headers=user_performing_action.headers
if user_performing_action
else GENERAL_HEADERS,
)
response.raise_for_status()
return ChatSessionSnapshot(**response.json())

@staticmethod
def get_query_history_as_csv(
start_time: datetime | None = None,
end_time: datetime | None = None,
user_performing_action: DATestUser | None = None,
) -> tuple[dict[str, str], str]:
query_params: dict[str, str | int] = {}
if start_time:
query_params["start"] = start_time.isoformat()
if end_time:
query_params["end"] = end_time.isoformat()

response = requests.get(
url=f"{API_SERVER_URL}/admin/query-history-csv?{urlencode(query_params, doseq=True)}",
headers=user_performing_action.headers
if user_performing_action
else GENERAL_HEADERS,
)
response.raise_for_status()
return dict(response.headers), response.content.decode()
15 changes: 7 additions & 8 deletions backend/tests/integration/common_utils/managers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,17 +213,16 @@ def get_user_page(
is_active_filter: bool | None = None,
user_performing_action: DATestUser | None = None,
) -> PaginatedReturn[FullUserSnapshot]:
query_params = {
query_params: dict[str, str | list[str] | int] = {
"page_num": page_num,
"page_size": page_size,
"q": search_query if search_query else None,
"roles": [role.value for role in role_filter] if role_filter else None,
"is_active": is_active_filter if is_active_filter is not None else None,
}
# Remove None values
query_params = {
key: value for key, value in query_params.items() if value is not None
}
if search_query:
query_params["q"] = search_query
if role_filter:
query_params["roles"] = [role.value for role in role_filter]
if is_active_filter is not None:
query_params["is_active"] = is_active_filter

response = requests.get(
url=f"{API_SERVER_URL}/manage/users/accepted?{urlencode(query_params, doseq=True)}",
Expand Down
12 changes: 8 additions & 4 deletions backend/tests/integration/common_utils/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from pydantic import Field

from onyx.auth.schemas import UserRole
from onyx.configs.constants import QAFeedbackType
from onyx.context.search.enums import RecencyBiasSetting
from onyx.db.enums import AccessType
from onyx.server.documents.models import DocumentSource
Expand Down Expand Up @@ -131,10 +132,6 @@ class DATestPersona(BaseModel):


#
class DATestChatSession(BaseModel):
id: UUID
persona_id: int
description: str


class DATestChatMessage(BaseModel):
Expand All @@ -144,6 +141,13 @@ class DATestChatMessage(BaseModel):
message: str


class DATestChatSession(BaseModel):
id: UUID
persona_id: int
description: str
feedback_type: QAFeedbackType | None = None
hagen-danswer marked this conversation as resolved.
Show resolved Hide resolved


class StreamedResponse(BaseModel):
full_message: str = ""
rephrased_query: str | None = None
Expand Down
97 changes: 37 additions & 60 deletions backend/tests/integration/tests/query-history/test_query_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@
from datetime import timezone

import pytest
import requests

from onyx.configs.constants import QAFeedbackType
from onyx.configs.constants import SessionType
from tests.integration.common_utils.constants import API_SERVER_URL
from tests.integration.common_utils.managers.api_key import APIKeyManager
from tests.integration.common_utils.managers.cc_pair import CCPairManager
from tests.integration.common_utils.managers.chat import ChatSessionManager
from tests.integration.common_utils.managers.document import DocumentManager
from tests.integration.common_utils.managers.llm_provider import LLMProviderManager
from tests.integration.common_utils.managers.query_history import QueryHistoryManager
from tests.integration.common_utils.managers.user import UserManager
from tests.integration.common_utils.test_models import DATestUser

Expand Down Expand Up @@ -69,66 +68,52 @@ def test_chat_history_endpoints(
) -> None:
admin_user, first_chat_id = setup_chat_session

response = requests.get(
f"{API_SERVER_URL}/admin/chat-session-history",
headers=admin_user.headers,
# Get chat history
history_response = QueryHistoryManager.get_query_history_page(
user_performing_action=admin_user
)
hagen-danswer marked this conversation as resolved.
Show resolved Hide resolved
assert response.status_code == 200
history_response = response.json()

# Verify we got back the one chat session we created
assert len(history_response) == 1
assert len(history_response.items) == 1

# Verify the first chat session details
first_session = history_response[0]
assert first_session["user_email"] == admin_user.email
assert first_session["name"] == "Test chat session"
assert first_session["first_user_message"] == "What was the Q1 revenue?"
assert first_session["first_ai_message"] is not None
assert first_session["assistant_id"] == 0
assert first_session["feedback_type"] is None
assert first_session["flow_type"] == SessionType.CHAT.value
assert first_session["conversation_length"] == 4 # 2 User messages + 2 AI responses
first_session = history_response.items[0]
assert first_session.user_email == admin_user.email
assert first_session.name == "Test chat session"
assert first_session.first_user_message == "What was the Q1 revenue?"
assert first_session.first_ai_message is not None
assert first_session.assistant_id == 0
assert first_session.feedback_type is None
assert first_session.flow_type == SessionType.CHAT
assert first_session.conversation_length == 4 # 2 User messages + 2 AI responses

# Test date filtering - should return no results
past_end = datetime.now(tz=timezone.utc) - timedelta(days=1)
past_start = past_end - timedelta(days=1)
response = requests.get(
f"{API_SERVER_URL}/admin/chat-session-history",
params={
"start": past_start.isoformat(),
"end": past_end.isoformat(),
},
headers=admin_user.headers,
history_response = QueryHistoryManager.get_query_history_page(
start_time=past_start,
end_time=past_end,
user_performing_action=admin_user,
)
assert response.status_code == 200
history_response = response.json()
assert len(history_response) == 0
assert len(history_response.items) == 0

# Test get specific chat session endpoint
response = requests.get(
f"{API_SERVER_URL}/admin/chat-session-history/{first_chat_id}",
headers=admin_user.headers,
session_details = QueryHistoryManager.get_chat_session_admin(
chat_session_id=first_chat_id,
user_performing_action=admin_user,
)
assert response.status_code == 200
session_details = response.json()

# Verify the session details
assert session_details["id"] == first_chat_id
assert len(session_details["messages"]) > 0
assert session_details["flow_type"] == SessionType.CHAT.value
assert str(session_details.id) == first_chat_id
assert len(session_details.messages) > 0
assert session_details.flow_type == SessionType.CHAT

# Test filtering by feedback
response = requests.get(
f"{API_SERVER_URL}/admin/chat-session-history",
params={
"feedback_type": QAFeedbackType.LIKE.value,
},
headers=admin_user.headers,
history_response = QueryHistoryManager.get_query_history_page(
feedback_type=QAFeedbackType.LIKE,
user_performing_action=admin_user,
)
assert response.status_code == 200
history_response = response.json()
assert len(history_response) == 0
assert len(history_response.items) == 0


def test_chat_history_csv_export(
Expand All @@ -137,16 +122,13 @@ def test_chat_history_csv_export(
admin_user, _ = setup_chat_session

# Test CSV export endpoint with date filtering
response = requests.get(
f"{API_SERVER_URL}/admin/query-history-csv",
headers=admin_user.headers,
headers, csv_content = QueryHistoryManager.get_query_history_as_csv(
user_performing_action=admin_user,
)
assert response.status_code == 200
assert response.headers["Content-Type"] == "text/csv; charset=utf-8"
assert "Content-Disposition" in response.headers
assert headers["Content-Type"] == "text/csv; charset=utf-8"
assert "Content-Disposition" in headers

# Verify CSV content
csv_content = response.content.decode()
csv_lines = csv_content.strip().split("\n")
assert len(csv_lines) == 3 # Header + 2 QA pairs
assert "chat_session_id" in csv_content
Expand All @@ -158,15 +140,10 @@ def test_chat_history_csv_export(
# Test CSV export with date filtering - should return no results
past_end = datetime.now(tz=timezone.utc) - timedelta(days=1)
past_start = past_end - timedelta(days=1)
response = requests.get(
f"{API_SERVER_URL}/admin/query-history-csv",
params={
"start": past_start.isoformat(),
"end": past_end.isoformat(),
},
headers=admin_user.headers,
headers, csv_content = QueryHistoryManager.get_query_history_as_csv(
start_time=past_start,
end_time=past_end,
user_performing_action=admin_user,
)
assert response.status_code == 200
csv_content = response.content.decode()
csv_lines = csv_content.strip().split("\n")
assert len(csv_lines) == 1 # Only header, no data rows
Loading
Loading