|
26 | 26 | ConversationUpdateResponse, |
27 | 27 | ) |
28 | 28 | from tests.unit.utils.auth_helpers import mock_authorization_resolvers |
29 | | -from utils.types import ToolCallSummary, ToolResultSummary |
| 29 | +from utils.types import ReferencedDocument, ToolCallSummary, ToolResultSummary |
30 | 30 |
|
31 | 31 | MOCK_AUTH = ("mock_user_id", "mock_username", False, "mock_token") |
32 | 32 | VALID_CONVERSATION_ID = "123e4567-e89b-12d3-a456-426614174000" |
@@ -95,6 +95,133 @@ def test_build_turn_with_tool_calls(self) -> None: |
95 | 95 | assert len(turn.tool_results) == 1 |
96 | 96 | assert turn.tool_results[0].status == "success" |
97 | 97 |
|
| 98 | + def test_build_turn_with_referenced_documents(self) -> None: |
| 99 | + """Test that referenced_documents from cache are included in the assistant message.""" |
| 100 | + ref_docs = [ |
| 101 | + ReferencedDocument( |
| 102 | + doc_url="https://docs.example.com/page1", |
| 103 | + doc_title="Page 1", |
| 104 | + source="vs_abc123", |
| 105 | + ), |
| 106 | + ReferencedDocument( |
| 107 | + doc_url="https://docs.example.com/page2", |
| 108 | + doc_title="Page 2", |
| 109 | + source="vs_abc123", |
| 110 | + ), |
| 111 | + ] |
| 112 | + entry = CacheEntry( |
| 113 | + query="What is RHDH?", |
| 114 | + response="RHDH is a developer hub.", |
| 115 | + provider="vllm", |
| 116 | + model="llama-3", |
| 117 | + started_at="2024-01-01T00:00:00Z", |
| 118 | + completed_at="2024-01-01T00:00:05Z", |
| 119 | + referenced_documents=ref_docs, |
| 120 | + ) |
| 121 | + |
| 122 | + turn = build_conversation_turn_from_cache_entry(entry) |
| 123 | + |
| 124 | + assert len(turn.messages) == 2 |
| 125 | + user_msg = turn.messages[0] |
| 126 | + assistant_msg = turn.messages[1] |
| 127 | + |
| 128 | + assert user_msg.type == "user" |
| 129 | + assert user_msg.referenced_documents is None |
| 130 | + |
| 131 | + assert assistant_msg.type == "assistant" |
| 132 | + assert assistant_msg.referenced_documents is not None |
| 133 | + assert len(assistant_msg.referenced_documents) == 2 |
| 134 | + assert ( |
| 135 | + str(assistant_msg.referenced_documents[0].doc_url) |
| 136 | + == "https://docs.example.com/page1" |
| 137 | + ) |
| 138 | + assert assistant_msg.referenced_documents[0].doc_title == "Page 1" |
| 139 | + assert ( |
| 140 | + str(assistant_msg.referenced_documents[1].doc_url) |
| 141 | + == "https://docs.example.com/page2" |
| 142 | + ) |
| 143 | + assert assistant_msg.referenced_documents[1].doc_title == "Page 2" |
| 144 | + |
| 145 | + def test_build_turn_without_referenced_documents(self) -> None: |
| 146 | + """Test that assistant message has no referenced_documents when cache entry has none.""" |
| 147 | + entry = CacheEntry( |
| 148 | + query="Hello", |
| 149 | + response="Hi there!", |
| 150 | + provider="openai", |
| 151 | + model="gpt-4", |
| 152 | + started_at="2024-01-01T00:00:00Z", |
| 153 | + completed_at="2024-01-01T00:00:05Z", |
| 154 | + ) |
| 155 | + |
| 156 | + turn = build_conversation_turn_from_cache_entry(entry) |
| 157 | + |
| 158 | + assert turn.messages[1].type == "assistant" |
| 159 | + assert turn.messages[1].referenced_documents is None |
| 160 | + |
| 161 | + def test_build_turn_with_empty_referenced_documents(self) -> None: |
| 162 | + """Test assistant message has no referenced_documents when cache has empty list.""" |
| 163 | + entry = CacheEntry( |
| 164 | + query="Hello", |
| 165 | + response="Hi there!", |
| 166 | + provider="openai", |
| 167 | + model="gpt-4", |
| 168 | + started_at="2024-01-01T00:00:00Z", |
| 169 | + completed_at="2024-01-01T00:00:05Z", |
| 170 | + referenced_documents=[], |
| 171 | + ) |
| 172 | + |
| 173 | + turn = build_conversation_turn_from_cache_entry(entry) |
| 174 | + |
| 175 | + assert turn.messages[1].type == "assistant" |
| 176 | + assert turn.messages[1].referenced_documents is None |
| 177 | + |
| 178 | + def test_build_turn_serialization_excludes_none_referenced_documents(self) -> None: |
| 179 | + """Test that model_dump(exclude_none=True) omits referenced_documents when None.""" |
| 180 | + entry = CacheEntry( |
| 181 | + query="Hello", |
| 182 | + response="Hi there!", |
| 183 | + provider="openai", |
| 184 | + model="gpt-4", |
| 185 | + started_at="2024-01-01T00:00:00Z", |
| 186 | + completed_at="2024-01-01T00:00:05Z", |
| 187 | + ) |
| 188 | + |
| 189 | + turn = build_conversation_turn_from_cache_entry(entry) |
| 190 | + dumped = turn.model_dump(exclude_none=True) |
| 191 | + |
| 192 | + user_msg_dict = dumped["messages"][0] |
| 193 | + assistant_msg_dict = dumped["messages"][1] |
| 194 | + assert "referenced_documents" not in user_msg_dict |
| 195 | + assert "referenced_documents" not in assistant_msg_dict |
| 196 | + |
| 197 | + def test_build_turn_serialization_includes_referenced_documents(self) -> None: |
| 198 | + """Test that model_dump(exclude_none=True) includes referenced_documents when present.""" |
| 199 | + ref_docs = [ |
| 200 | + ReferencedDocument( |
| 201 | + doc_url="https://docs.example.com/page1", |
| 202 | + doc_title="Page 1", |
| 203 | + ), |
| 204 | + ] |
| 205 | + entry = CacheEntry( |
| 206 | + query="What is RHDH?", |
| 207 | + response="RHDH is a developer hub.", |
| 208 | + provider="vllm", |
| 209 | + model="llama-3", |
| 210 | + started_at="2024-01-01T00:00:00Z", |
| 211 | + completed_at="2024-01-01T00:00:05Z", |
| 212 | + referenced_documents=ref_docs, |
| 213 | + ) |
| 214 | + |
| 215 | + turn = build_conversation_turn_from_cache_entry(entry) |
| 216 | + dumped = turn.model_dump(exclude_none=True) |
| 217 | + |
| 218 | + user_msg_dict = dumped["messages"][0] |
| 219 | + assistant_msg_dict = dumped["messages"][1] |
| 220 | + assert "referenced_documents" not in user_msg_dict |
| 221 | + assert "referenced_documents" in assistant_msg_dict |
| 222 | + assert len(assistant_msg_dict["referenced_documents"]) == 1 |
| 223 | + assert assistant_msg_dict["referenced_documents"][0]["doc_title"] == "Page 1" |
| 224 | + |
98 | 225 |
|
99 | 226 | @pytest.fixture |
100 | 227 | def mock_configuration(mocker: MockerFixture) -> MockType: |
@@ -428,6 +555,95 @@ async def test_successful_retrieval( |
428 | 555 | assert len(response.chat_history) == 1 |
429 | 556 | assert response.chat_history[0].messages[0].content == "query" |
430 | 557 |
|
| 558 | + @pytest.mark.asyncio |
| 559 | + async def test_successful_retrieval_includes_referenced_documents( |
| 560 | + self, mocker: MockerFixture, mock_configuration: MockType |
| 561 | + ) -> None: |
| 562 | + """Test that GET conversation includes referenced_documents in assistant messages.""" |
| 563 | + mock_authorization_resolvers(mocker) |
| 564 | + mocker.patch("app.endpoints.conversations_v2.configuration", mock_configuration) |
| 565 | + mocker.patch("app.endpoints.conversations_v2.check_suid", return_value=True) |
| 566 | + ref_docs = [ |
| 567 | + ReferencedDocument( |
| 568 | + doc_url="https://docs.example.com/intro", |
| 569 | + doc_title="Introduction", |
| 570 | + source="vs_abc123", |
| 571 | + ), |
| 572 | + ReferencedDocument( |
| 573 | + doc_url="https://docs.example.com/guide", |
| 574 | + doc_title="User Guide", |
| 575 | + source="vs_abc123", |
| 576 | + ), |
| 577 | + ] |
| 578 | + mock_configuration.conversation_cache.list.return_value = [ |
| 579 | + mocker.Mock(conversation_id=VALID_CONVERSATION_ID) |
| 580 | + ] |
| 581 | + mock_configuration.conversation_cache.get.return_value = [ |
| 582 | + CacheEntry( |
| 583 | + query="What is RHDH?", |
| 584 | + response="RHDH is a developer hub.", |
| 585 | + provider="vllm", |
| 586 | + model="llama-3", |
| 587 | + started_at="2024-01-01T00:00:00Z", |
| 588 | + completed_at="2024-01-01T00:00:05Z", |
| 589 | + referenced_documents=ref_docs, |
| 590 | + ) |
| 591 | + ] |
| 592 | + |
| 593 | + response = await get_conversation_endpoint_handler( |
| 594 | + request=mocker.Mock(), |
| 595 | + conversation_id=VALID_CONVERSATION_ID, |
| 596 | + auth=MOCK_AUTH, |
| 597 | + ) |
| 598 | + |
| 599 | + assert response is not None |
| 600 | + assert len(response.chat_history) == 1 |
| 601 | + turn = response.chat_history[0] |
| 602 | + |
| 603 | + user_msg = turn.messages[0] |
| 604 | + assert user_msg.type == "user" |
| 605 | + assert user_msg.referenced_documents is None |
| 606 | + |
| 607 | + assistant_msg = turn.messages[1] |
| 608 | + assert assistant_msg.type == "assistant" |
| 609 | + assert assistant_msg.referenced_documents is not None |
| 610 | + assert len(assistant_msg.referenced_documents) == 2 |
| 611 | + assert assistant_msg.referenced_documents[0].doc_title == "Introduction" |
| 612 | + assert assistant_msg.referenced_documents[1].doc_title == "User Guide" |
| 613 | + |
| 614 | + @pytest.mark.asyncio |
| 615 | + async def test_successful_retrieval_without_referenced_documents( |
| 616 | + self, mocker: MockerFixture, mock_configuration: MockType |
| 617 | + ) -> None: |
| 618 | + """Test that GET conversation works when cache entry has no referenced_documents.""" |
| 619 | + mock_authorization_resolvers(mocker) |
| 620 | + mocker.patch("app.endpoints.conversations_v2.configuration", mock_configuration) |
| 621 | + mocker.patch("app.endpoints.conversations_v2.check_suid", return_value=True) |
| 622 | + mock_configuration.conversation_cache.list.return_value = [ |
| 623 | + mocker.Mock(conversation_id=VALID_CONVERSATION_ID) |
| 624 | + ] |
| 625 | + mock_configuration.conversation_cache.get.return_value = [ |
| 626 | + CacheEntry( |
| 627 | + query="Hello", |
| 628 | + response="Hi there!", |
| 629 | + provider="openai", |
| 630 | + model="gpt-4", |
| 631 | + started_at="2024-01-01T00:00:00Z", |
| 632 | + completed_at="2024-01-01T00:00:05Z", |
| 633 | + ) |
| 634 | + ] |
| 635 | + |
| 636 | + response = await get_conversation_endpoint_handler( |
| 637 | + request=mocker.Mock(), |
| 638 | + conversation_id=VALID_CONVERSATION_ID, |
| 639 | + auth=MOCK_AUTH, |
| 640 | + ) |
| 641 | + |
| 642 | + assert response is not None |
| 643 | + turn = response.chat_history[0] |
| 644 | + assert turn.messages[1].type == "assistant" |
| 645 | + assert turn.messages[1].referenced_documents is None |
| 646 | + |
431 | 647 | @pytest.mark.asyncio |
432 | 648 | async def test_with_skip_userid_check( |
433 | 649 | self, mocker: MockerFixture, mock_configuration: MockType |
|
0 commit comments