Skip to content

Commit

Permalink
Merge pull request #3160 from danswer-ai/add-to-admin-chat-sessions-api
Browse files Browse the repository at this point in the history
Extend query history API
  • Loading branch information
hagen-danswer authored Nov 19, 2024
2 parents 9b7cc83 + ce3124f commit 487ef4a
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 10 deletions.
26 changes: 19 additions & 7 deletions backend/ee/danswer/server/query_history/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,21 @@ class ChatSessionMinimal(BaseModel):
name: str | None
first_user_message: str
first_ai_message: str
persona_name: str | None
assistant_id: int | None
assistant_name: str | None
time_created: datetime
feedback_type: QAFeedbackType | Literal["mixed"] | None
flow_type: SessionType
conversation_length: int


class ChatSessionSnapshot(BaseModel):
id: UUID
user_email: str
name: str | None
messages: list[MessageSnapshot]
persona_name: str | None
assistant_id: int | None
assistant_name: str | None
time_created: datetime
flow_type: SessionType

Expand Down Expand Up @@ -146,7 +149,7 @@ def from_chat_session_snapshot(
retrieved_documents=ai_message.documents,
feedback_type=ai_message.feedback_type,
feedback_text=ai_message.feedback_text,
persona_name=chat_session_snapshot.persona_name,
persona_name=chat_session_snapshot.assistant_name,
user_email=get_display_email(chat_session_snapshot.user_email),
time_created=user_message.time_created,
flow_type=chat_session_snapshot.flow_type,
Expand Down Expand Up @@ -257,12 +260,20 @@ def fetch_and_process_chat_session_history_minimal(
name=chat_session.description,
first_user_message=first_user_message,
first_ai_message=first_ai_message,
persona_name=chat_session.persona.name
if chat_session.persona
else None,
assistant_id=chat_session.persona_id,
assistant_name=(
chat_session.persona.name if chat_session.persona else None
),
time_created=chat_session.time_created,
feedback_type=feedback_type,
flow_type=flow_type,
conversation_length=len(
[
m
for m in chat_session.messages
if m.message_type != MessageType.SYSTEM
]
),
)
)

Expand Down Expand Up @@ -327,7 +338,8 @@ def snapshot_from_chat_session(
for message in messages
if message.message_type != MessageType.SYSTEM
],
persona_name=chat_session.persona.name if chat_session.persona else None,
assistant_id=chat_session.persona_id,
assistant_name=chat_session.persona.name if chat_session.persona else None,
time_created=chat_session.time_created,
flow_type=flow_type,
)
Expand Down
119 changes: 119 additions & 0 deletions backend/tests/integration/tests/query-history/test_query_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
from datetime import datetime
from datetime import timedelta
from datetime import timezone

import requests

from danswer.configs.constants import QAFeedbackType
from danswer.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.user import UserManager
from tests.integration.common_utils.test_models import DATestUser


def test_query_history_endpoints(reset: None) -> None:
# Create admin user and required resources
admin_user: DATestUser = UserManager.create(name="admin_user")
cc_pair = CCPairManager.create_from_scratch(user_performing_action=admin_user)
api_key = APIKeyManager.create(user_performing_action=admin_user)
LLMProviderManager.create(user_performing_action=admin_user)

# Seed a document
cc_pair.documents = []
cc_pair.documents.append(
DocumentManager.seed_doc_with_content(
cc_pair=cc_pair,
content="The company's revenue in Q1 was $1M",
api_key=api_key,
)
)

# Create chat session and send a message
chat_session = ChatSessionManager.create(
persona_id=0,
description="Test chat session",
user_performing_action=admin_user,
)

ChatSessionManager.send_message(
chat_session_id=chat_session.id,
message="What was the Q1 revenue?",
user_performing_action=admin_user,
)

# Test get chat session history endpoint
end_time = datetime.now(tz=timezone.utc)
start_time = end_time - timedelta(days=1)

response = requests.get(
f"{API_SERVER_URL}/admin/chat-session-history",
params={
"start": start_time.isoformat(),
"end": end_time.isoformat(),
},
headers=admin_user.headers,
)
assert response.status_code == 200
history_response = response.json()

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

# Verify the first chat session details
first_session = history_response[0]
first_chat_id = first_session["id"]
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"] == 2 # User message + AI response

# Test get specific chat session endpoint
response = requests.get(
f"{API_SERVER_URL}/admin/chat-session-history/{first_chat_id}",
headers=admin_user.headers,
)
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

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

# Verify CSV content
csv_content = response.content.decode()
assert "chat_session_id" in csv_content
assert "user_message" in csv_content
assert "ai_response" in csv_content

# Test filtering by feedback
response = requests.get(
f"{API_SERVER_URL}/admin/chat-session-history",
params={
"feedback_type": QAFeedbackType.LIKE.value,
"start": start_time.isoformat(),
"end": end_time.isoformat(),
},
headers=admin_user.headers,
)
assert response.status_code == 200
history_response = response.json()
assert len(history_response) == 0
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function QueryHistoryTableRow({
<FeedbackBadge feedback={chatSessionMinimal.feedback_type} />
</TableCell>
<TableCell>{chatSessionMinimal.user_email || "-"}</TableCell>
<TableCell>{chatSessionMinimal.persona_name || "Unknown"}</TableCell>
<TableCell>{chatSessionMinimal.assistant_name || "Unknown"}</TableCell>
<TableCell>
{timestampToReadableDate(chatSessionMinimal.time_created)}
</TableCell>
Expand Down
7 changes: 5 additions & 2 deletions web/src/app/ee/admin/performance/usage/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ export interface ChatSessionSnapshot {
user_email: string | null;
name: string | null;
messages: MessageSnapshot[];
persona_name: string | null;
assistant_id: number | null;
assistant_name: string | null;
time_created: string;
flow_type: SessionType;
}
Expand All @@ -49,10 +50,12 @@ export interface ChatSessionMinimal {
name: string | null;
first_user_message: string;
first_ai_message: string;
persona_name: string | null;
assistant_id: number | null;
assistant_name: string | null;
time_created: string;
feedback_type: Feedback | "mixed" | null;
flow_type: SessionType;
conversation_length: number;
}

export interface UsageReport {
Expand Down

0 comments on commit 487ef4a

Please sign in to comment.