Skip to content

Commit 74b66bd

Browse files
authored
Adjust defaults for multimodal-related parameters to work for default deployments (#2717)
* Fix frontend defaults and e2e test * Add backend guards and tests * Deault send_image_sources to self.multimodal_enabled
1 parent d08040f commit 74b66bd

File tree

7 files changed

+93
-38
lines changed

7 files changed

+93
-38
lines changed

app/backend/approaches/approach.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ async def get_sources_content(
412412
self,
413413
results: list[Document],
414414
use_semantic_captions: bool,
415+
include_text_sources: bool,
415416
download_image_sources: bool,
416417
user_oid: Optional[str] = None,
417418
) -> DataPoints:
@@ -442,10 +443,13 @@ def nonewlines(s: str) -> str:
442443
citations.append(citation)
443444

444445
# If semantic captions are used, extract captions; otherwise, use content
445-
if use_semantic_captions and doc.captions:
446-
text_sources.append(f"{citation}: {nonewlines(' . '.join([cast(str, c.text) for c in doc.captions]))}")
447-
else:
448-
text_sources.append(f"{citation}: {nonewlines(doc.content or '')}")
446+
if include_text_sources:
447+
if use_semantic_captions and doc.captions:
448+
text_sources.append(
449+
f"{citation}: {nonewlines(' . '.join([cast(str, c.text) for c in doc.captions]))}"
450+
)
451+
else:
452+
text_sources.append(f"{citation}: {nonewlines(doc.content or '')}")
449453

450454
if download_image_sources and hasattr(doc, "images") and doc.images:
451455
for img in doc.images:
@@ -457,9 +461,7 @@ def nonewlines(s: str) -> str:
457461
if url:
458462
image_sources.append(url)
459463
citations.append(self.get_image_citation(doc.sourcepage or "", img["url"]))
460-
if download_image_sources:
461-
return DataPoints(text=text_sources, images=image_sources, citations=citations)
462-
return DataPoints(text=text_sources, citations=citations)
464+
return DataPoints(text=text_sources, images=image_sources, citations=citations)
463465

464466
def get_citation(self, sourcepage: Optional[str]):
465467
return sourcepage or ""

app/backend/approaches/chatreadretrieveread.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
from approaches.approach import (
1818
Approach,
19-
DataPoints,
2019
ExtraInfo,
2120
ThoughtStep,
2221
)
@@ -284,9 +283,11 @@ async def run_search_approach(
284283
minimum_reranker_score = overrides.get("minimum_reranker_score", 0.0)
285284
search_index_filter = self.build_filter(overrides, auth_claims)
286285
send_text_sources = overrides.get("send_text_sources", True)
287-
send_image_sources = overrides.get("send_image_sources", True)
286+
send_image_sources = overrides.get("send_image_sources", self.multimodal_enabled) and self.multimodal_enabled
288287
search_text_embeddings = overrides.get("search_text_embeddings", True)
289-
search_image_embeddings = overrides.get("search_image_embeddings", self.multimodal_enabled)
288+
search_image_embeddings = (
289+
overrides.get("search_image_embeddings", self.multimodal_enabled) and self.multimodal_enabled
290+
)
290291

291292
original_user_query = messages[-1]["content"]
292293
if not isinstance(original_user_query, str):
@@ -342,11 +343,12 @@ async def run_search_approach(
342343

343344
# STEP 3: Generate a contextual and content specific answer using the search results and chat history
344345
data_points = await self.get_sources_content(
345-
results, use_semantic_captions, download_image_sources=send_image_sources, user_oid=auth_claims.get("oid")
346+
results,
347+
use_semantic_captions,
348+
include_text_sources=send_text_sources,
349+
download_image_sources=send_image_sources,
350+
user_oid=auth_claims.get("oid"),
346351
)
347-
if not send_text_sources:
348-
data_points = DataPoints(text=[], images=data_points.images, citations=data_points.citations)
349-
350352
extra_info = ExtraInfo(
351353
data_points,
352354
thoughts=[
@@ -396,7 +398,7 @@ async def run_agentic_retrieval_approach(
396398
# 50 is the amount of documents that the reranker can process per query
397399
max_docs_for_reranker = max_subqueries * 50
398400
send_text_sources = overrides.get("send_text_sources", True)
399-
send_image_sources = overrides.get("send_image_sources", True)
401+
send_image_sources = overrides.get("send_image_sources", self.multimodal_enabled) and self.multimodal_enabled
400402

401403
response, results = await self.run_agentic_retrieval(
402404
messages=messages,
@@ -412,12 +414,10 @@ async def run_agentic_retrieval_approach(
412414
data_points = await self.get_sources_content(
413415
results,
414416
use_semantic_captions=False,
417+
include_text_sources=send_text_sources,
415418
download_image_sources=send_image_sources,
416419
user_oid=auth_claims.get("oid"),
417420
)
418-
if not send_text_sources:
419-
data_points = DataPoints(text=[], images=data_points.images, citations=data_points.citations)
420-
421421
extra_info = ExtraInfo(
422422
data_points,
423423
thoughts=[

app/backend/approaches/retrievethenread.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,12 @@ async def run_search_approach(
159159
minimum_reranker_score = overrides.get("minimum_reranker_score", 0.0)
160160
filter = self.build_filter(overrides, auth_claims)
161161
q = str(messages[-1]["content"])
162-
send_image_sources = overrides.get("send_image_sources", True)
162+
send_text_sources = overrides.get("send_text_sources", True)
163+
send_image_sources = overrides.get("send_image_sources", self.multimodal_enabled) and self.multimodal_enabled
163164
search_text_embeddings = overrides.get("search_text_embeddings", True)
164-
search_image_embeddings = overrides.get("search_image_embeddings", self.multimodal_enabled)
165+
search_image_embeddings = (
166+
overrides.get("search_image_embeddings", self.multimodal_enabled) and self.multimodal_enabled
167+
)
165168

166169
vectors: list[VectorQuery] = []
167170
if use_vector_search:
@@ -185,7 +188,11 @@ async def run_search_approach(
185188
)
186189

187190
data_points = await self.get_sources_content(
188-
results, use_semantic_captions, download_image_sources=send_image_sources, user_oid=auth_claims.get("oid")
191+
results,
192+
use_semantic_captions,
193+
include_text_sources=send_text_sources,
194+
download_image_sources=send_image_sources,
195+
user_oid=auth_claims.get("oid"),
189196
)
190197

191198
return ExtraInfo(
@@ -226,7 +233,8 @@ async def run_agentic_retrieval_approach(
226233
results_merge_strategy = overrides.get("results_merge_strategy", "interleaved")
227234
# 50 is the amount of documents that the reranker can process per query
228235
max_docs_for_reranker = max_subqueries * 50
229-
send_image_sources = overrides.get("send_image_sources", True)
236+
send_text_sources = overrides.get("send_text_sources", True)
237+
send_image_sources = overrides.get("send_image_sources", self.multimodal_enabled) and self.multimodal_enabled
230238

231239
response, results = await self.run_agentic_retrieval(
232240
messages,
@@ -242,6 +250,7 @@ async def run_agentic_retrieval_approach(
242250
data_points = await self.get_sources_content(
243251
results,
244252
use_semantic_captions=False,
253+
include_text_sources=send_text_sources,
245254
download_image_sources=send_image_sources,
246255
user_oid=auth_claims.get("oid"),
247256
)

app/frontend/src/pages/ask/Ask.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ export function Component(): JSX.Element {
3737
const [useQueryRewriting, setUseQueryRewriting] = useState<boolean>(false);
3838
const [reasoningEffort, setReasoningEffort] = useState<string>("");
3939
const [sendTextSources, setSendTextSources] = useState<boolean>(true);
40-
const [sendImageSources, setSendImageSources] = useState<boolean>(true);
40+
const [sendImageSources, setSendImageSources] = useState<boolean>(false);
4141
const [includeCategory, setIncludeCategory] = useState<string>("");
4242

4343
const [excludeCategory, setExcludeCategory] = useState<string>("");
4444
const [question, setQuestion] = useState<string>("");
4545
const [searchTextEmbeddings, setSearchTextEmbeddings] = useState<boolean>(true);
46-
const [searchImageEmbeddings, setSearchImageEmbeddings] = useState<boolean>(true);
46+
const [searchImageEmbeddings, setSearchImageEmbeddings] = useState<boolean>(false);
4747
const [useOidSecurityFilter, setUseOidSecurityFilter] = useState<boolean>(false);
4848
const [useGroupsSecurityFilter, setUseGroupsSecurityFilter] = useState<boolean>(false);
4949
const [showMultimodalOptions, setShowMultimodalOptions] = useState<boolean>(false);
@@ -87,12 +87,11 @@ export function Component(): JSX.Element {
8787
configApi().then(config => {
8888
setShowMultimodalOptions(config.showMultimodalOptions);
8989
if (config.showMultimodalOptions) {
90-
// Set default LLM inputs based on config override or fallback to Texts
91-
setSendTextSources(true);
92-
setSendImageSources(true);
93-
// Set default vector field settings
94-
setSearchTextEmbeddings(true);
95-
setSearchImageEmbeddings(true);
90+
// Initialize from server config so defaults follow deployment settings
91+
setSendTextSources(config.ragSendTextSources !== undefined ? config.ragSendTextSources : true);
92+
setSendImageSources(config.ragSendImageSources);
93+
setSearchTextEmbeddings(config.ragSearchTextEmbeddings);
94+
setSearchImageEmbeddings(config.ragSearchImageEmbeddings);
9695
}
9796
setUseSemanticRanker(config.showSemanticRankerOption);
9897
setShowSemanticRankerOption(config.showSemanticRankerOption);

app/frontend/src/pages/chat/Chat.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ const Chat = () => {
4848
const [excludeCategory, setExcludeCategory] = useState<string>("");
4949
const [useSuggestFollowupQuestions, setUseSuggestFollowupQuestions] = useState<boolean>(false);
5050
const [searchTextEmbeddings, setSearchTextEmbeddings] = useState<boolean>(true);
51-
const [searchImageEmbeddings, setSearchImageEmbeddings] = useState<boolean>(true);
51+
const [searchImageEmbeddings, setSearchImageEmbeddings] = useState<boolean>(false);
5252
const [useOidSecurityFilter, setUseOidSecurityFilter] = useState<boolean>(false);
5353
const [useGroupsSecurityFilter, setUseGroupsSecurityFilter] = useState<boolean>(false);
5454
const [sendTextSources, setSendTextSources] = useState<boolean>(true);
55-
const [sendImageSources, setSendImageSources] = useState<boolean>(true);
55+
const [sendImageSources, setSendImageSources] = useState<boolean>(false);
5656

5757
const lastQuestionRef = useRef<string>("");
5858
const chatMessageStreamEnd = useRef<HTMLDivElement | null>(null);
@@ -99,7 +99,7 @@ const Chat = () => {
9999
configApi().then(config => {
100100
setShowMultimodalOptions(config.showMultimodalOptions);
101101
if (config.showMultimodalOptions) {
102-
// Always have at least one source enabled, default to text if none specified
102+
// Initialize from server config so defaults match deployment settings
103103
setSendTextSources(config.ragSendTextSources !== undefined ? config.ragSendTextSources : true);
104104
setSendImageSources(config.ragSendImageSources);
105105
setSearchTextEmbeddings(config.ragSearchTextEmbeddings);

tests/e2e.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,21 @@ def test_chat(sized_page: Page, live_server_url: str):
9393

9494
# Set up a mock route to the /chat endpoint with streaming results
9595
def handle(route: Route):
96-
# Assert that session_state is specified in the request (None for now)
9796
try:
9897
post_data = route.request.post_data_json
99-
if post_data and "session_state" in post_data:
100-
session_state = post_data["session_state"]
101-
assert session_state is None
98+
# Assert that session_state is specified (None initially)
99+
if "session_state" in post_data:
100+
assert post_data["session_state"] is None
101+
overrides = post_data["context"]["overrides"]
102+
# Assert that the default overrides are correct
103+
assert overrides.get("send_text_sources") is True
104+
assert overrides.get("send_image_sources") is False
105+
assert overrides.get("search_text_embeddings") is True
106+
assert overrides.get("search_image_embeddings") is False
107+
# retrieval_mode may be explicitly "hybrid" or omitted (interpreted as hybrid)
108+
assert overrides.get("retrieval_mode") in ["hybrid", None]
102109
except Exception as e:
103-
print(f"Error in test_chat handler: {e}")
110+
print(f"Error in test_chat handler (defaults validation): {e}")
104111

105112
# Read the JSONL from our snapshot results and return as the response
106113
f = open("tests/snapshots/test_app/test_chat_stream_text/client0/result.jsonlines")

tests/test_app.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,44 @@ async def test_ask_rtr_text_semanticcaptions(client, snapshot):
296296
snapshot.assert_match(json.dumps(result, indent=4), "result.json")
297297

298298

299+
@pytest.mark.asyncio
300+
@pytest.mark.parametrize("route", ["/ask", "/chat"])
301+
async def test_send_text_sources_false(client, route):
302+
"""When send_text_sources is False, text sources should be omitted while citations remain."""
303+
response = await client.post(
304+
route,
305+
json={
306+
"messages": [{"content": "What is the capital of France?", "role": "user"}],
307+
"context": {"overrides": {"retrieval_mode": "text", "send_text_sources": False}},
308+
},
309+
)
310+
assert response.status_code == 200
311+
result = await response.get_json()
312+
data_points = result["context"]["data_points"]
313+
assert data_points["text"] == []
314+
assert "citations" in data_points and len(data_points["citations"]) > 0
315+
316+
317+
@pytest.mark.asyncio
318+
@pytest.mark.parametrize("route", ["/ask", "/chat"])
319+
async def test_search_image_embeddings_ignored_without_multimodal(client, route):
320+
"""Sending search_image_embeddings=True when USE_MULTIMODAL is false should be ignored and still succeed (200)."""
321+
response = await client.post(
322+
route,
323+
json={
324+
"messages": [{"content": "What is the capital of France?", "role": "user"}],
325+
"context": {"overrides": {"search_image_embeddings": True, "send_image_sources": True}},
326+
},
327+
)
328+
assert response.status_code == 200
329+
result = await response.get_json()
330+
# Ensure the thought step recorded search_image_embeddings as False
331+
search_thought = [
332+
thought for thought in result["context"]["thoughts"] if thought["title"].startswith("Search using")
333+
][0]
334+
assert search_thought["props"]["search_image_embeddings"] is False
335+
336+
299337
@pytest.mark.asyncio
300338
async def test_ask_rtr_hybrid(client, snapshot):
301339
response = await client.post(

0 commit comments

Comments
 (0)