Skip to content

Commit 8c17ca5

Browse files
committed
Change frontend for vector fields
1 parent 78383ec commit 8c17ca5

File tree

7 files changed

+126
-83
lines changed

7 files changed

+126
-83
lines changed

app/frontend/src/api/models.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@ export const enum LLMInputs {
1010
Texts = "texts"
1111
}
1212

13-
export const enum VectorFields {
14-
Embedding = "textEmbeddingOnly",
15-
ImageEmbedding = "imageEmbeddingOnly",
16-
TextAndImageEmbeddings = "textAndImageEmbeddings"
17-
}
18-
1913
export type ChatAppRequestOverrides = {
2014
retrieval_mode?: RetrievalMode;
2115
semantic_ranker?: boolean;
@@ -38,7 +32,8 @@ export type ChatAppRequestOverrides = {
3832
use_oid_security_filter?: boolean;
3933
use_groups_security_filter?: boolean;
4034
llm_inputs: LLMInputs;
41-
vector_fields: VectorFields;
35+
search_text_embeddings: boolean;
36+
search_image_embeddings: boolean;
4237
language: string;
4338
use_agentic_retrieval: boolean;
4439
};

app/frontend/src/components/Settings/Settings.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next";
33
import { TextField, ITextFieldProps, Checkbox, ICheckboxProps, Dropdown, IDropdownProps, IDropdownOption } from "@fluentui/react";
44
import { HelpCallout } from "../HelpCallout";
55
import { VectorSettings } from "../VectorSettings";
6-
import { RetrievalMode, VectorFields, LLMInputs } from "../../api";
6+
import { RetrievalMode, LLMInputs } from "../../api";
77
import styles from "./Settings.module.css";
88

99
// Add type for onRenderLabel
@@ -26,7 +26,8 @@ export interface SettingsProps {
2626
includeCategory: string;
2727
retrievalMode: RetrievalMode;
2828
llmInputs: LLMInputs;
29-
vectorFields: VectorFields;
29+
searchTextEmbeddings: boolean;
30+
searchImageEmbeddings: boolean;
3031
showSemanticRankerOption: boolean;
3132
showQueryRewritingOption: boolean;
3233
showReasoningEffortOption: boolean;
@@ -65,7 +66,8 @@ export const Settings = ({
6566
excludeCategory,
6667
includeCategory,
6768
retrievalMode,
68-
vectorFields,
69+
searchTextEmbeddings,
70+
searchImageEmbeddings,
6971
llmInputs,
7072
showSemanticRankerOption,
7173
showQueryRewritingOption,
@@ -355,10 +357,12 @@ export const Settings = ({
355357
<>
356358
<VectorSettings
357359
defaultRetrievalMode={retrievalMode}
358-
defaultVectorFields={vectorFields}
360+
defaultSearchTextEmbeddings={searchTextEmbeddings}
361+
defaultSearchImageEmbeddings={searchImageEmbeddings}
359362
showImageOptions={showMultimodalOptions}
360-
updateVectorFields={val => onChange("vectorFields", val)}
361363
updateRetrievalMode={val => onChange("retrievalMode", val)}
364+
updateSearchTextEmbeddings={val => onChange("searchTextEmbeddings", val)}
365+
updateSearchImageEmbeddings={val => onChange("searchImageEmbeddings", val)}
362366
/>
363367
</>
364368
)}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
11
.container {
22
margin-top: 0.625em;
33
}
4+
5+
.fieldset {
6+
border: none;
7+
padding: 0;
8+
margin: 0;
9+
}
10+
11+
.legend {
12+
font-size: 14px;
13+
margin-bottom: 5px;
14+
padding: 0;
15+
}

app/frontend/src/components/VectorSettings/VectorSettings.tsx

Lines changed: 69 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,62 @@
11
import { useEffect, useState } from "react";
2-
import { Stack, IDropdownOption, Dropdown, IDropdownProps } from "@fluentui/react";
2+
import { Stack, IDropdownOption, Dropdown, Checkbox, IDropdownProps } from "@fluentui/react";
33
import { useId } from "@fluentui/react-hooks";
44
import { useTranslation } from "react-i18next";
55

66
import styles from "./VectorSettings.module.css";
77
import { HelpCallout } from "../../components/HelpCallout";
8-
import { RetrievalMode, VectorFields } from "../../api";
8+
import { RetrievalMode } from "../../api";
99

1010
interface Props {
1111
showImageOptions?: boolean;
1212
defaultRetrievalMode: RetrievalMode;
13-
defaultVectorFields?: VectorFields;
13+
defaultSearchTextEmbeddings?: boolean;
14+
defaultSearchImageEmbeddings?: boolean;
1415
updateRetrievalMode: (retrievalMode: RetrievalMode) => void;
15-
updateVectorFields: (vectorFields: VectorFields) => void;
16+
updateSearchTextEmbeddings: (searchTextEmbeddings: boolean) => void;
17+
updateSearchImageEmbeddings: (searchImageEmbeddings: boolean) => void;
1618
}
1719

18-
export const VectorSettings = ({ updateRetrievalMode, updateVectorFields, showImageOptions, defaultRetrievalMode, defaultVectorFields }: Props) => {
20+
export const VectorSettings = ({
21+
updateRetrievalMode,
22+
updateSearchTextEmbeddings,
23+
updateSearchImageEmbeddings,
24+
showImageOptions,
25+
defaultRetrievalMode,
26+
defaultSearchTextEmbeddings = true,
27+
defaultSearchImageEmbeddings = true
28+
}: Props) => {
1929
const [retrievalMode, setRetrievalMode] = useState<RetrievalMode>(defaultRetrievalMode || RetrievalMode.Hybrid);
20-
const [vectorFields, setVectorFields] = useState<VectorFields>(defaultVectorFields || VectorFields.TextAndImageEmbeddings);
30+
const [searchTextEmbeddings, setSearchTextEmbeddings] = useState<boolean>(defaultSearchTextEmbeddings);
31+
const [searchImageEmbeddings, setSearchImageEmbeddings] = useState<boolean>(defaultSearchImageEmbeddings);
2132

2233
const onRetrievalModeChange = (_ev: React.FormEvent<HTMLDivElement>, option?: IDropdownOption<RetrievalMode> | undefined) => {
2334
setRetrievalMode(option?.data || RetrievalMode.Hybrid);
2435
updateRetrievalMode(option?.data || RetrievalMode.Hybrid);
2536
};
2637

27-
const onVectorFieldsChange = (_ev: React.FormEvent<HTMLDivElement>, option?: IDropdownOption<VectorFields> | undefined) => {
28-
setVectorFields(option?.data || VectorFields.TextAndImageEmbeddings);
29-
updateVectorFields(option?.data || VectorFields.TextAndImageEmbeddings);
38+
const onSearchTextEmbeddingsChange = (_ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean) => {
39+
setSearchTextEmbeddings(checked || false);
40+
updateSearchTextEmbeddings(checked || false);
41+
};
42+
43+
const onSearchImageEmbeddingsChange = (_ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean) => {
44+
setSearchImageEmbeddings(checked || false);
45+
updateSearchImageEmbeddings(checked || false);
3046
};
3147

3248
// Only run if showImageOptions changes from true to false or false to true
3349
useEffect(() => {
3450
if (!showImageOptions) {
35-
// If images are disabled, we must force to text-only embeddings
36-
setVectorFields(VectorFields.Embedding);
37-
updateVectorFields(VectorFields.Embedding);
51+
// If images are disabled, we must disable image embeddings
52+
setSearchImageEmbeddings(false);
53+
updateSearchImageEmbeddings(false);
3854
} else {
39-
// When image options become available, reset to default or use TextAndImageEmbeddings
40-
setVectorFields(defaultVectorFields || VectorFields.TextAndImageEmbeddings);
41-
updateVectorFields(defaultVectorFields || VectorFields.TextAndImageEmbeddings);
55+
// When image options become available, reset to default
56+
setSearchImageEmbeddings(defaultSearchImageEmbeddings);
57+
updateSearchImageEmbeddings(defaultSearchImageEmbeddings);
4258
}
43-
}, [showImageOptions, updateVectorFields, defaultVectorFields]);
59+
}, [showImageOptions, updateSearchImageEmbeddings, defaultSearchImageEmbeddings]);
4460

4561
const retrievalModeId = useId("retrievalMode");
4662
const retrievalModeFieldId = useId("retrievalModeField");
@@ -78,36 +94,43 @@ export const VectorSettings = ({ updateRetrievalMode, updateVectorFields, showIm
7894
/>
7995

8096
{showImageOptions && [RetrievalMode.Vectors, RetrievalMode.Hybrid].includes(retrievalMode) && (
81-
<Dropdown
82-
id={vectorFieldsFieldId}
83-
label={t("labels.vector.label")}
84-
selectedKey={vectorFields}
85-
options={[
86-
{
87-
key: VectorFields.Embedding,
88-
text: t("labels.vector.options.embedding"),
89-
selected: vectorFields === VectorFields.Embedding,
90-
data: VectorFields.Embedding
91-
},
92-
{
93-
key: VectorFields.ImageEmbedding,
94-
text: t("labels.vector.options.imageEmbedding"),
95-
selected: vectorFields === VectorFields.ImageEmbedding,
96-
data: VectorFields.ImageEmbedding
97-
},
98-
{
99-
key: VectorFields.TextAndImageEmbeddings,
100-
text: t("labels.vector.options.both"),
101-
selected: vectorFields === VectorFields.TextAndImageEmbeddings,
102-
data: VectorFields.TextAndImageEmbeddings
103-
}
104-
]}
105-
onChange={onVectorFieldsChange}
106-
aria-labelledby={vectorFieldsId}
107-
onRenderLabel={(props: IDropdownProps | undefined) => (
108-
<HelpCallout labelId={vectorFieldsId} fieldId={vectorFieldsFieldId} helpText={t("helpTexts.vectorFields")} label={props?.label} />
109-
)}
110-
/>
97+
<fieldset className={styles.fieldset}>
98+
<legend className={styles.legend}>{t("labels.vector.label")}</legend>
99+
<Stack tokens={{ childrenGap: 8 }}>
100+
<Checkbox
101+
id={vectorFieldsFieldId + "-text"}
102+
className={styles.settingsSeparator}
103+
label={t("labels.vector.options.embedding")}
104+
checked={searchTextEmbeddings}
105+
onChange={onSearchTextEmbeddingsChange}
106+
aria-labelledby={vectorFieldsId + "-text"}
107+
onRenderLabel={props => (
108+
<HelpCallout
109+
labelId={vectorFieldsId + "-text"}
110+
fieldId={vectorFieldsFieldId + "-text"}
111+
helpText={t("helpTexts.textEmbeddings")}
112+
label={props?.label}
113+
/>
114+
)}
115+
/>
116+
<Checkbox
117+
id={vectorFieldsFieldId + "-image"}
118+
className={styles.settingsSeparator}
119+
label={t("labels.vector.options.imageEmbedding")}
120+
checked={searchImageEmbeddings}
121+
onChange={onSearchImageEmbeddingsChange}
122+
aria-labelledby={vectorFieldsId + "-image"}
123+
onRenderLabel={props => (
124+
<HelpCallout
125+
labelId={vectorFieldsId + "-image"}
126+
fieldId={vectorFieldsFieldId + "-image"}
127+
helpText={t("helpTexts.imageEmbeddings")}
128+
label={props?.label}
129+
/>
130+
)}
131+
/>
132+
</Stack>
133+
</fieldset>
111134
)}
112135
</Stack>
113136
);

app/frontend/src/locales/en/translation.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,10 @@
120120
}
121121
},
122122
"vector": {
123-
"label": "Vector fields (Multi-query vector search)",
123+
"label": "Included vector fields",
124124
"options": {
125-
"embedding": "Text Embeddings",
126-
"imageEmbedding": "Image Embeddings",
127-
"both": "Text and Image embeddings"
125+
"embedding": "Text embeddings",
126+
"imageEmbedding": "Image embeddings"
128127
}
129128
},
130129
"useOidSecurityFilter": "Use oid security filter",
@@ -163,6 +162,8 @@
163162
"suggestFollowupQuestions": "Asks the LLM to suggest follow-up questions based on the user's query.",
164163
"vectorFields":
165164
"Specifies which embedding fields in the Azure AI Search Index will be searched, both the 'Images and text' embeddings, 'Images' only, or 'Text' only.",
165+
"textEmbeddings": "When selected, search will use embeddings from the text-only embeddings model of extracted text chunks.",
166+
"imageEmbeddings": "When selected, search will use embeddings from the multimodal embeddings model of extracted images.",
166167
"llmInputs":
167168
"Sets what will be sent to the vision model. 'Images and text' sends both images and text to the model, 'Images' sends only images, and 'Text' sends only text.",
168169
"retrievalMode":

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

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Panel, DefaultButton, Spinner } from "@fluentui/react";
55

66
import styles from "./Ask.module.css";
77

8-
import { askApi, configApi, ChatAppResponse, ChatAppRequest, RetrievalMode, VectorFields, LLMInputs, SpeechConfig } from "../../api";
8+
import { askApi, configApi, ChatAppResponse, ChatAppRequest, RetrievalMode, LLMInputs, SpeechConfig } from "../../api";
99
import { Answer, AnswerError } from "../../components/Answer";
1010
import { QuestionInput } from "../../components/QuestionInput";
1111
import { ExampleList } from "../../components/Example";
@@ -41,7 +41,8 @@ export function Component(): JSX.Element {
4141

4242
const [excludeCategory, setExcludeCategory] = useState<string>("");
4343
const [question, setQuestion] = useState<string>("");
44-
const [vectorFields, setVectorFields] = useState<VectorFields>(VectorFields.TextAndImageEmbeddings);
44+
const [searchTextEmbeddings, setSearchTextEmbeddings] = useState<boolean>(true);
45+
const [searchImageEmbeddings, setSearchImageEmbeddings] = useState<boolean>(true);
4546
const [useOidSecurityFilter, setUseOidSecurityFilter] = useState<boolean>(false);
4647
const [useGroupsSecurityFilter, setUseGroupsSecurityFilter] = useState<boolean>(false);
4748
const [showMultimodalOptions, setShowMultimodalOptions] = useState<boolean>(false);
@@ -88,11 +89,9 @@ export function Component(): JSX.Element {
8889
// Set default LLM inputs based on config override or fallback to Texts
8990
const defaultLlmInputs = config.ragLlmInputsOverride ? (config.ragLlmInputsOverride as LLMInputs) : LLMInputs.Texts;
9091
setLLMInputs(defaultLlmInputs);
91-
// Set default vector fields based on config override or fallback to TextAndImageEmbeddings
92-
const defaultVectorFields = config.ragVectorFieldsDefault
93-
? (config.ragVectorFieldsDefault as VectorFields)
94-
: VectorFields.TextAndImageEmbeddings;
95-
setVectorFields(defaultVectorFields);
92+
// Set default vector field settings
93+
setSearchTextEmbeddings(true);
94+
setSearchImageEmbeddings(true);
9695
}
9796
setUseSemanticRanker(config.showSemanticRankerOption);
9897
setShowSemanticRankerOption(config.showSemanticRankerOption);
@@ -161,7 +160,8 @@ export function Component(): JSX.Element {
161160
reasoning_effort: reasoningEffort,
162161
use_oid_security_filter: useOidSecurityFilter,
163162
use_groups_security_filter: useGroupsSecurityFilter,
164-
vector_fields: vectorFields,
163+
search_text_embeddings: searchTextEmbeddings,
164+
search_image_embeddings: searchImageEmbeddings,
165165
llm_inputs: llmInputs,
166166
language: i18n.language,
167167
use_agentic_retrieval: useAgenticRetrieval,
@@ -240,8 +240,11 @@ export function Component(): JSX.Element {
240240
case "llmInputs":
241241
setLLMInputs(value);
242242
break;
243-
case "vectorFields":
244-
setVectorFields(value);
243+
case "searchTextEmbeddings":
244+
setSearchTextEmbeddings(value);
245+
break;
246+
case "searchImageEmbeddings":
247+
setSearchImageEmbeddings(value);
245248
break;
246249
case "retrievalMode":
247250
setRetrievalMode(value);
@@ -373,7 +376,8 @@ export function Component(): JSX.Element {
373376
includeCategory={includeCategory}
374377
retrievalMode={retrievalMode}
375378
llmInputs={llmInputs}
376-
vectorFields={vectorFields}
379+
searchTextEmbeddings={searchTextEmbeddings}
380+
searchImageEmbeddings={searchImageEmbeddings}
377381
showSemanticRankerOption={showSemanticRankerOption}
378382
showQueryRewritingOption={showQueryRewritingOption}
379383
showReasoningEffortOption={showReasoningEffortOption}

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
ChatAppResponseOrError,
1616
ChatAppRequest,
1717
ResponseMessage,
18-
VectorFields,
1918
LLMInputs,
2019
SpeechConfig
2120
} from "../../api";
@@ -58,7 +57,8 @@ const Chat = () => {
5857
const [includeCategory, setIncludeCategory] = useState<string>("");
5958
const [excludeCategory, setExcludeCategory] = useState<string>("");
6059
const [useSuggestFollowupQuestions, setUseSuggestFollowupQuestions] = useState<boolean>(false);
61-
const [vectorFields, setVectorFields] = useState<VectorFields>(VectorFields.TextAndImageEmbeddings);
60+
const [searchTextEmbeddings, setSearchTextEmbeddings] = useState<boolean>(true);
61+
const [searchImageEmbeddings, setSearchImageEmbeddings] = useState<boolean>(true);
6262
const [useOidSecurityFilter, setUseOidSecurityFilter] = useState<boolean>(false);
6363
const [useGroupsSecurityFilter, setUseGroupsSecurityFilter] = useState<boolean>(false);
6464
const [llmInputs, setLLMInputs] = useState<LLMInputs>(LLMInputs.TextAndImages);
@@ -112,10 +112,9 @@ const Chat = () => {
112112
const defaultLlmInputs = config.ragLlmInputsOverride ? (config.ragLlmInputsOverride as LLMInputs) : LLMInputs.TextAndImages;
113113
setLLMInputs(defaultLlmInputs);
114114
// Set default vector fields based on config override or fallback to TextAndImageEmbeddings
115-
const defaultVectorFields = config.ragVectorFieldsDefault
116-
? (config.ragVectorFieldsDefault as VectorFields)
117-
: VectorFields.TextAndImageEmbeddings;
118-
setVectorFields(defaultVectorFields);
115+
// Set default vector field settings
116+
setSearchTextEmbeddings(true);
117+
setSearchImageEmbeddings(true);
119118
}
120119
setUseSemanticRanker(config.showSemanticRankerOption);
121120
setShowSemanticRankerOption(config.showSemanticRankerOption);
@@ -238,7 +237,8 @@ const Chat = () => {
238237
suggest_followup_questions: useSuggestFollowupQuestions,
239238
use_oid_security_filter: useOidSecurityFilter,
240239
use_groups_security_filter: useGroupsSecurityFilter,
241-
vector_fields: vectorFields,
240+
search_text_embeddings: searchTextEmbeddings,
241+
search_image_embeddings: searchImageEmbeddings,
242242
llm_inputs: llmInputs,
243243
language: i18n.language,
244244
use_agentic_retrieval: useAgenticRetrieval,
@@ -359,8 +359,11 @@ const Chat = () => {
359359
case "llmInputs":
360360
setLLMInputs(value);
361361
break;
362-
case "vectorFields":
363-
setVectorFields(value);
362+
case "searchTextEmbeddings":
363+
setSearchTextEmbeddings(value);
364+
break;
365+
case "searchImageEmbeddings":
366+
setSearchImageEmbeddings(value);
364367
break;
365368
case "retrievalMode":
366369
setRetrievalMode(value);
@@ -559,7 +562,8 @@ const Chat = () => {
559562
retrievalMode={retrievalMode}
560563
showMultimodalOptions={showMultimodalOptions}
561564
llmInputs={llmInputs}
562-
vectorFields={vectorFields}
565+
searchTextEmbeddings={searchTextEmbeddings}
566+
searchImageEmbeddings={searchImageEmbeddings}
563567
showSemanticRankerOption={showSemanticRankerOption}
564568
showQueryRewritingOption={showQueryRewritingOption}
565569
showReasoningEffortOption={showReasoningEffortOption}

0 commit comments

Comments
 (0)