|
61 | 61 | from utils.responses import ( |
62 | 62 | _build_chunk_attributes, |
63 | 63 | _merge_tools, |
64 | | - _resolve_source_for_result, |
65 | 64 | build_mcp_tool_call_from_arguments_done, |
66 | 65 | build_tool_call_summary, |
67 | 66 | build_tool_result_from_mcp_output_item_done, |
|
81 | 80 | prepare_responses_params, |
82 | 81 | prepare_tools, |
83 | 82 | resolve_client_tool_choice, |
| 83 | + resolve_source_for_result, |
84 | 84 | resolve_tool_choice, |
85 | 85 | resolve_vector_store_ids, |
86 | 86 | ) |
@@ -2250,12 +2250,11 @@ def test_parse_referenced_documents_file_search_call( |
2250 | 2250 | "document_id": "doc_1", |
2251 | 2251 | } |
2252 | 2252 |
|
2253 | | - mock_result2 = { |
2254 | | - "attributes": { |
2255 | | - "url": "https://example.com/doc2", |
2256 | | - "title": "Document 2", |
2257 | | - "doc_id": "doc_2", |
2258 | | - }, |
| 2253 | + mock_result2 = mocker.Mock() |
| 2254 | + mock_result2.attributes = { |
| 2255 | + "url": "https://example.com/doc2", |
| 2256 | + "title": "Document 2", |
| 2257 | + "doc_id": "doc_2", |
2259 | 2258 | } |
2260 | 2259 |
|
2261 | 2260 | mock_output_item = mocker.Mock() |
@@ -2465,9 +2464,14 @@ def test_build_tool_call_summary_file_search_call( |
2465 | 2464 | mock_result.text = "chunk text" |
2466 | 2465 | mock_result.filename = "doc.pdf" |
2467 | 2466 | mock_result.score = 0.9 |
2468 | | - mock_result.attributes = None |
| 2467 | + mock_result.attributes = {} |
2469 | 2468 | mock_result.model_dump = mocker.Mock( |
2470 | | - return_value={"text": "chunk text", "filename": "doc.pdf", "score": 0.9} |
| 2469 | + return_value={ |
| 2470 | + "text": "chunk text", |
| 2471 | + "filename": "doc.pdf", |
| 2472 | + "score": 0.9, |
| 2473 | + "attributes": {}, |
| 2474 | + } |
2471 | 2475 | ) |
2472 | 2476 |
|
2473 | 2477 | mock_item = mocker.Mock(spec=FileSearchCall) |
@@ -2620,13 +2624,13 @@ def test_extract_rag_chunks_with_results(self, mocker: MockerFixture) -> None: |
2620 | 2624 | mock_result1.text = "chunk 1" |
2621 | 2625 | mock_result1.filename = "doc1.pdf" |
2622 | 2626 | mock_result1.score = 0.9 |
2623 | | - mock_result1.attributes = None |
| 2627 | + mock_result1.attributes = {} |
2624 | 2628 |
|
2625 | 2629 | mock_result2 = mocker.Mock() |
2626 | 2630 | mock_result2.text = "chunk 2" |
2627 | 2631 | mock_result2.filename = "doc2.pdf" |
2628 | 2632 | mock_result2.score = 0.8 |
2629 | | - mock_result2.attributes = None |
| 2633 | + mock_result2.attributes = {} |
2630 | 2634 |
|
2631 | 2635 | mock_item = mocker.Mock(spec=FileSearchCall) |
2632 | 2636 | mock_item.results = [mock_result1, mock_result2] |
@@ -2778,133 +2782,84 @@ def test_build_mcp_tool_result_none_output(self, mocker: MockerFixture) -> None: |
2778 | 2782 |
|
2779 | 2783 |
|
2780 | 2784 | class TestResolveSourceForResult: |
2781 | | - """Tests for _resolve_source_for_result function.""" |
| 2785 | + """Tests for resolve_source_for_result function.""" |
2782 | 2786 |
|
2783 | | - def test_single_store_mapped(self, mocker: MockerFixture) -> None: |
| 2787 | + def test_single_store_mapped(self) -> None: |
2784 | 2788 | """Test resolution with single vector store that has a mapping.""" |
2785 | | - mock_result = mocker.Mock() |
2786 | | - mock_result.filename = "file-abc123" |
2787 | | - |
2788 | | - source = _resolve_source_for_result( |
2789 | | - mock_result, ["vs-001"], {"vs-001": "ocp-4.18-docs"} |
2790 | | - ) |
| 2789 | + source = resolve_source_for_result({}, ["vs-001"], {"vs-001": "ocp-4.18-docs"}) |
2791 | 2790 | assert source == "ocp-4.18-docs" |
2792 | 2791 |
|
2793 | | - def test_single_store_unmapped(self, mocker: MockerFixture) -> None: |
| 2792 | + def test_single_store_unmapped(self) -> None: |
2794 | 2793 | """Test resolution with single vector store without mapping returns raw store ID.""" |
2795 | | - mock_result = mocker.Mock() |
2796 | | - mock_result.filename = "file-abc123" |
2797 | | - |
2798 | | - source = _resolve_source_for_result(mock_result, ["vs-unknown"], {}) |
| 2794 | + source = resolve_source_for_result({}, ["vs-unknown"], {}) |
2799 | 2795 | assert source == "vs-unknown" |
2800 | 2796 |
|
2801 | | - def test_multiple_stores_with_attribute(self, mocker: MockerFixture) -> None: |
2802 | | - """Test resolution with multiple stores using result attributes.""" |
2803 | | - mock_result = mocker.Mock() |
2804 | | - mock_result.filename = "file-abc123" |
2805 | | - mock_result.attributes = {"vector_store_id": "vs-002"} |
2806 | | - |
2807 | | - source = _resolve_source_for_result( |
2808 | | - mock_result, |
| 2797 | + def test_multiple_stores_with_attribute(self) -> None: |
| 2798 | + """Test resolution with multiple stores using vector_store_id attribute.""" |
| 2799 | + source = resolve_source_for_result( |
| 2800 | + {"vector_store_id": "vs-002"}, |
2809 | 2801 | ["vs-001", "vs-002"], |
2810 | 2802 | {"vs-001": "ocp-4.18-docs", "vs-002": "rhel-9-docs"}, |
2811 | 2803 | ) |
2812 | 2804 | assert source == "rhel-9-docs" |
2813 | 2805 |
|
2814 | | - def test_multiple_stores_no_attribute(self, mocker: MockerFixture) -> None: |
2815 | | - """Test resolution with multiple stores and no vector_store_id attribute returns None.""" |
2816 | | - mock_result = mocker.Mock() |
2817 | | - mock_result.filename = "file-abc123" |
2818 | | - mock_result.attributes = {} |
2819 | | - |
2820 | | - source = _resolve_source_for_result( |
2821 | | - mock_result, |
| 2806 | + def test_multiple_stores_no_attribute(self) -> None: |
| 2807 | + """Test resolution with multiple stores and empty attributes returns None.""" |
| 2808 | + source = resolve_source_for_result( |
| 2809 | + {}, |
2822 | 2810 | ["vs-001", "vs-002"], |
2823 | 2811 | {"vs-001": "ocp-4.18-docs", "vs-002": "rhel-9-docs"}, |
2824 | 2812 | ) |
2825 | 2813 | assert source is None |
2826 | 2814 |
|
2827 | | - def test_no_stores(self, mocker: MockerFixture) -> None: |
| 2815 | + def test_no_stores(self) -> None: |
2828 | 2816 | """Test resolution with no vector stores returns None.""" |
2829 | | - mock_result = mocker.Mock() |
2830 | | - mock_result.filename = "file-abc123" |
2831 | | - |
2832 | | - source = _resolve_source_for_result(mock_result, [], {}) |
| 2817 | + source = resolve_source_for_result({}, [], {}) |
2833 | 2818 | assert source is None |
2834 | 2819 |
|
2835 | | - def test_multiple_stores_attribute_not_in_mapping( |
2836 | | - self, mocker: MockerFixture |
2837 | | - ) -> None: |
| 2820 | + def test_multiple_stores_attribute_not_in_mapping(self) -> None: |
2838 | 2821 | """Test resolution when attribute store ID is not in mapping returns raw store ID.""" |
2839 | | - mock_result = mocker.Mock() |
2840 | | - mock_result.filename = "file-abc123" |
2841 | | - mock_result.attributes = {"vector_store_id": "vs-unknown"} |
2842 | | - |
2843 | | - source = _resolve_source_for_result( |
2844 | | - mock_result, |
| 2822 | + source = resolve_source_for_result( |
| 2823 | + {"vector_store_id": "vs-unknown"}, |
2845 | 2824 | ["vs-001", "vs-002"], |
2846 | 2825 | {"vs-001": "ocp-docs"}, |
2847 | 2826 | ) |
2848 | 2827 | assert source == "vs-unknown" |
2849 | 2828 |
|
2850 | | - def test_multiple_stores_source_attribute_fallback( |
2851 | | - self, mocker: MockerFixture |
2852 | | - ) -> None: |
| 2829 | + def test_multiple_stores_source_attribute_fallback(self) -> None: |
2853 | 2830 | """Test resolution falls back to source attribute when no vector_store_id.""" |
2854 | | - mock_result = mocker.Mock() |
2855 | | - mock_result.filename = "file-abc123" |
2856 | | - mock_result.attributes = {"source": "ocp-documentation"} |
2857 | | - |
2858 | | - source = _resolve_source_for_result( |
2859 | | - mock_result, |
| 2831 | + source = resolve_source_for_result( |
| 2832 | + {"source": "ocp-documentation"}, |
2860 | 2833 | ["vs-001", "vs-002"], |
2861 | 2834 | {"vs-001": "ocp-4.18-docs"}, |
2862 | 2835 | ) |
2863 | 2836 | assert source == "ocp-documentation" |
2864 | 2837 |
|
2865 | | - def test_multiple_stores_source_attribute_ignores_mapping( |
2866 | | - self, mocker: MockerFixture |
2867 | | - ) -> None: |
| 2838 | + def test_multiple_stores_source_attribute_ignores_mapping(self) -> None: |
2868 | 2839 | """Test source attribute is returned directly without rag_id_mapping lookup.""" |
2869 | | - mock_result = mocker.Mock() |
2870 | | - mock_result.filename = "file-abc123" |
2871 | | - mock_result.attributes = {"source": "custom-index"} |
2872 | | - |
2873 | | - source = _resolve_source_for_result( |
2874 | | - mock_result, |
| 2840 | + source = resolve_source_for_result( |
| 2841 | + {"source": "custom-index"}, |
2875 | 2842 | ["vs-001", "vs-002"], |
2876 | 2843 | {"custom-index": "should-not-be-used"}, |
2877 | 2844 | ) |
2878 | 2845 | assert source == "custom-index" |
2879 | 2846 |
|
2880 | | - def test_multiple_stores_source_preferred_over_vector_store_id( |
2881 | | - self, mocker: MockerFixture |
2882 | | - ) -> None: |
| 2847 | + def test_multiple_stores_source_preferred_over_vector_store_id(self) -> None: |
2883 | 2848 | """Test source attribute takes precedence over vector_store_id.""" |
2884 | | - mock_result = mocker.Mock() |
2885 | | - mock_result.filename = "file-abc123" |
2886 | | - mock_result.attributes = { |
2887 | | - "vector_store_id": "vs-002", |
2888 | | - "source": "ocp-documentation", |
2889 | | - } |
2890 | | - |
2891 | | - source = _resolve_source_for_result( |
2892 | | - mock_result, |
| 2849 | + source = resolve_source_for_result( |
| 2850 | + { |
| 2851 | + "vector_store_id": "vs-002", |
| 2852 | + "source": "ocp-documentation", |
| 2853 | + }, |
2893 | 2854 | ["vs-001", "vs-002"], |
2894 | 2855 | {"vs-002": "rhel-9-docs"}, |
2895 | 2856 | ) |
2896 | 2857 | assert source == "ocp-documentation" |
2897 | 2858 |
|
2898 | | - def test_multiple_stores_no_vector_store_id_no_source( |
2899 | | - self, mocker: MockerFixture |
2900 | | - ) -> None: |
| 2859 | + def test_multiple_stores_no_vector_store_id_no_source(self) -> None: |
2901 | 2860 | """Test resolution returns None when neither vector_store_id nor source present.""" |
2902 | | - mock_result = mocker.Mock() |
2903 | | - mock_result.filename = "file-abc123" |
2904 | | - mock_result.attributes = {"title": "some doc"} |
2905 | | - |
2906 | | - source = _resolve_source_for_result( |
2907 | | - mock_result, |
| 2861 | + source = resolve_source_for_result( |
| 2862 | + {"title": "some doc"}, |
2908 | 2863 | ["vs-001", "vs-002"], |
2909 | 2864 | {"vs-001": "ocp-docs"}, |
2910 | 2865 | ) |
@@ -3001,12 +2956,12 @@ def test_chunks_resolved_single_store(self, mocker: MockerFixture) -> None: |
3001 | 2956 | assert rag_chunks[0].score == 0.95 |
3002 | 2957 |
|
3003 | 2958 | def test_chunks_no_mapping_falls_back(self, mocker: MockerFixture) -> None: |
3004 | | - """Test RAG chunk source falls back to filename when no mapping.""" |
| 2959 | + """Test RAG chunk source is None with empty attributes and no vector stores.""" |
3005 | 2960 | mock_result = mocker.Mock() |
3006 | 2961 | mock_result.text = "content" |
3007 | 2962 | mock_result.filename = "file-abc" |
3008 | 2963 | mock_result.score = 0.5 |
3009 | | - mock_result.attributes = None |
| 2964 | + mock_result.attributes = {} |
3010 | 2965 |
|
3011 | 2966 | mock_item = mocker.Mock(spec=FileSearchCall) |
3012 | 2967 | mock_item.results = [mock_result] |
@@ -3090,9 +3045,14 @@ def test_file_search_without_mapping(self, mocker: MockerFixture) -> None: |
3090 | 3045 | mock_result.text = "chunk text" |
3091 | 3046 | mock_result.filename = "doc.pdf" |
3092 | 3047 | mock_result.score = 0.9 |
3093 | | - mock_result.attributes = None |
| 3048 | + mock_result.attributes = {} |
3094 | 3049 | mock_result.model_dump = mocker.Mock( |
3095 | | - return_value={"text": "chunk text", "filename": "doc.pdf", "score": 0.9} |
| 3050 | + return_value={ |
| 3051 | + "text": "chunk text", |
| 3052 | + "filename": "doc.pdf", |
| 3053 | + "score": 0.9, |
| 3054 | + "attributes": {}, |
| 3055 | + } |
3096 | 3056 | ) |
3097 | 3057 |
|
3098 | 3058 | mock_item = mocker.Mock(spec=FileSearchCall) |
|
0 commit comments