Skip to content

Commit 0e8087c

Browse files
committed
Refactor settings code across Chat/Ask
1 parent 023dc1b commit 0e8087c

File tree

4 files changed

+493
-668
lines changed

4 files changed

+493
-668
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.settingsSeparator {
2+
margin-top: 0.75rem;
3+
}
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
import { useId } from "@fluentui/react-hooks";
2+
import { useTranslation } from "react-i18next";
3+
import { TextField, ITextFieldProps, Checkbox, ICheckboxProps, Dropdown, IDropdownProps, IDropdownOption } from "@fluentui/react";
4+
import { HelpCallout } from "../HelpCallout";
5+
import { GPT4VSettings } from "../GPT4VSettings";
6+
import { VectorSettings } from "../VectorSettings";
7+
import { RetrievalMode, VectorFieldOptions, GPT4VInput } from "../../api";
8+
import styles from "./Settings.module.css";
9+
10+
// Add type for onRenderLabel
11+
type RenderLabelType = ITextFieldProps | IDropdownProps | ICheckboxProps;
12+
13+
export interface SettingsProps {
14+
promptTemplate: string;
15+
temperature: number;
16+
retrieveCount: number;
17+
seed: number | null;
18+
minimumSearchScore: number;
19+
minimumRerankerScore: number;
20+
useSemanticRanker: boolean;
21+
useSemanticCaptions: boolean;
22+
excludeCategory: string;
23+
includeCategory: string;
24+
retrievalMode: RetrievalMode;
25+
useGPT4V: boolean;
26+
gpt4vInput: GPT4VInput;
27+
vectorFieldList: VectorFieldOptions[];
28+
showSemanticRankerOption: boolean;
29+
showGPT4VOptions: boolean;
30+
showVectorOption: boolean;
31+
useOidSecurityFilter: boolean;
32+
useGroupsSecurityFilter: boolean;
33+
useLogin: boolean;
34+
loggedIn: boolean;
35+
requireAccessControl: boolean;
36+
className?: string;
37+
onChange: (field: string, value: any) => void;
38+
shouldStream?: boolean; // Only used in Chat
39+
useSuggestFollowupQuestions?: boolean; // Only used in Chat
40+
promptTemplatePrefix?: string;
41+
promptTemplateSuffix?: string;
42+
showSuggestFollowupQuestions?: boolean;
43+
}
44+
45+
export const Settings = ({
46+
promptTemplate,
47+
temperature,
48+
retrieveCount,
49+
seed,
50+
minimumSearchScore,
51+
minimumRerankerScore,
52+
useSemanticRanker,
53+
useSemanticCaptions,
54+
excludeCategory,
55+
includeCategory,
56+
retrievalMode,
57+
useGPT4V,
58+
gpt4vInput,
59+
vectorFieldList,
60+
showSemanticRankerOption,
61+
showGPT4VOptions,
62+
showVectorOption,
63+
useOidSecurityFilter,
64+
useGroupsSecurityFilter,
65+
useLogin,
66+
loggedIn,
67+
requireAccessControl,
68+
className,
69+
onChange,
70+
// Add new parameters
71+
shouldStream,
72+
useSuggestFollowupQuestions,
73+
promptTemplatePrefix,
74+
promptTemplateSuffix,
75+
showSuggestFollowupQuestions
76+
}: SettingsProps) => {
77+
const { t } = useTranslation();
78+
79+
// Form field IDs
80+
const promptTemplateId = useId("promptTemplate");
81+
const promptTemplateFieldId = useId("promptTemplateField");
82+
const temperatureId = useId("temperature");
83+
const temperatureFieldId = useId("temperatureField");
84+
const seedId = useId("seed");
85+
const seedFieldId = useId("seedField");
86+
const searchScoreId = useId("searchScore");
87+
const searchScoreFieldId = useId("searchScoreField");
88+
const rerankerScoreId = useId("rerankerScore");
89+
const rerankerScoreFieldId = useId("rerankerScoreField");
90+
const retrieveCountId = useId("retrieveCount");
91+
const retrieveCountFieldId = useId("retrieveCountField");
92+
const includeCategoryId = useId("includeCategory");
93+
const includeCategoryFieldId = useId("includeCategoryField");
94+
const excludeCategoryId = useId("excludeCategory");
95+
const excludeCategoryFieldId = useId("excludeCategoryField");
96+
const semanticRankerId = useId("semanticRanker");
97+
const semanticRankerFieldId = useId("semanticRankerField");
98+
const semanticCaptionsId = useId("semanticCaptions");
99+
const semanticCaptionsFieldId = useId("semanticCaptionsField");
100+
const useOidSecurityFilterId = useId("useOidSecurityFilter");
101+
const useOidSecurityFilterFieldId = useId("useOidSecurityFilterField");
102+
const useGroupsSecurityFilterId = useId("useGroupsSecurityFilter");
103+
const useGroupsSecurityFilterFieldId = useId("useGroupsSecurityFilterField");
104+
// Add new field IDs
105+
const shouldStreamId = useId("shouldStream");
106+
const shouldStreamFieldId = useId("shouldStreamField");
107+
const suggestFollowupQuestionsId = useId("suggestFollowupQuestions");
108+
const suggestFollowupQuestionsFieldId = useId("suggestFollowupQuestionsField");
109+
110+
const renderLabel = (props: RenderLabelType | undefined, labelId: string, fieldId: string, helpText: string) => (
111+
<HelpCallout labelId={labelId} fieldId={fieldId} helpText={helpText} label={props?.label} />
112+
);
113+
114+
return (
115+
<div className={className}>
116+
<TextField
117+
id={promptTemplateFieldId}
118+
className={styles.settingsSeparator}
119+
defaultValue={promptTemplate}
120+
label={t("labels.promptTemplate")}
121+
multiline
122+
autoAdjustHeight
123+
onChange={(_ev, val) => onChange("promptTemplate", val || "")}
124+
aria-labelledby={promptTemplateId}
125+
onRenderLabel={props => renderLabel(props, promptTemplateId, promptTemplateFieldId, t("helpTexts.promptTemplate"))}
126+
/>
127+
128+
<TextField
129+
id={temperatureFieldId}
130+
className={styles.settingsSeparator}
131+
label={t("labels.temperature")}
132+
type="number"
133+
min={0}
134+
max={1}
135+
step={0.1}
136+
defaultValue={temperature.toString()}
137+
onChange={(_ev, val) => onChange("temperature", parseFloat(val || "0"))}
138+
aria-labelledby={temperatureId}
139+
onRenderLabel={props => renderLabel(props, temperatureId, temperatureFieldId, t("helpTexts.temperature"))}
140+
/>
141+
142+
<TextField
143+
id={seedFieldId}
144+
className={styles.settingsSeparator}
145+
label={t("labels.seed")}
146+
type="text"
147+
defaultValue={seed?.toString() || ""}
148+
onChange={(_ev, val) => onChange("seed", val ? parseInt(val) : null)}
149+
aria-labelledby={seedId}
150+
onRenderLabel={props => renderLabel(props, seedId, seedFieldId, t("helpTexts.seed"))}
151+
/>
152+
153+
<TextField
154+
id={searchScoreFieldId}
155+
className={styles.settingsSeparator}
156+
label={t("labels.minimumSearchScore")}
157+
type="number"
158+
min={0}
159+
step={0.01}
160+
defaultValue={minimumSearchScore.toString()}
161+
onChange={(_ev, val) => onChange("minimumSearchScore", parseFloat(val || "0"))}
162+
aria-labelledby={searchScoreId}
163+
onRenderLabel={props => renderLabel(props, searchScoreId, searchScoreFieldId, t("helpTexts.searchScore"))}
164+
/>
165+
166+
{showSemanticRankerOption && (
167+
<TextField
168+
id={rerankerScoreFieldId}
169+
className={styles.settingsSeparator}
170+
label={t("labels.minimumRerankerScore")}
171+
type="number"
172+
min={1}
173+
max={4}
174+
step={0.1}
175+
defaultValue={minimumRerankerScore.toString()}
176+
onChange={(_ev, val) => onChange("minimumRerankerScore", parseFloat(val || "0"))}
177+
aria-labelledby={rerankerScoreId}
178+
onRenderLabel={props => renderLabel(props, rerankerScoreId, rerankerScoreFieldId, t("helpTexts.rerankerScore"))}
179+
/>
180+
)}
181+
182+
<TextField
183+
id={retrieveCountFieldId}
184+
className={styles.settingsSeparator}
185+
label={t("labels.retrieveCount")}
186+
type="number"
187+
min={1}
188+
max={50}
189+
defaultValue={retrieveCount.toString()}
190+
onChange={(_ev, val) => onChange("retrieveCount", parseInt(val || "3"))}
191+
aria-labelledby={retrieveCountId}
192+
onRenderLabel={props => renderLabel(props, retrieveCountId, retrieveCountFieldId, t("helpTexts.retrieveNumber"))}
193+
/>
194+
195+
<Dropdown
196+
id={includeCategoryFieldId}
197+
className={styles.settingsSeparator}
198+
label={t("labels.includeCategory")}
199+
selectedKey={includeCategory}
200+
onChange={(_ev?: React.FormEvent<HTMLElement | HTMLInputElement>, option?: IDropdownOption) => onChange("includeCategory", option?.key || "")}
201+
aria-labelledby={includeCategoryId}
202+
options={[
203+
{ key: "", text: t("labels.includeCategoryOptions.all") }
204+
// { key: "example", text: "Example Category" } // Add more categories as needed
205+
]}
206+
onRenderLabel={props => renderLabel(props, includeCategoryId, includeCategoryFieldId, t("helpTexts.includeCategory"))}
207+
/>
208+
209+
<TextField
210+
id={excludeCategoryFieldId}
211+
className={styles.settingsSeparator}
212+
label={t("labels.excludeCategory")}
213+
defaultValue={excludeCategory}
214+
onChange={(_ev, val) => onChange("excludeCategory", val || "")}
215+
aria-labelledby={excludeCategoryId}
216+
onRenderLabel={props => renderLabel(props, excludeCategoryId, excludeCategoryFieldId, t("helpTexts.excludeCategory"))}
217+
/>
218+
219+
{showSemanticRankerOption && (
220+
<>
221+
<Checkbox
222+
id={semanticRankerFieldId}
223+
className={styles.settingsSeparator}
224+
checked={useSemanticRanker}
225+
label={t("labels.useSemanticRanker")}
226+
onChange={(_ev, checked) => onChange("useSemanticRanker", !!checked)}
227+
aria-labelledby={semanticRankerId}
228+
onRenderLabel={props => renderLabel(props, semanticRankerId, semanticRankerFieldId, t("helpTexts.useSemanticReranker"))}
229+
/>
230+
231+
<Checkbox
232+
id={semanticCaptionsFieldId}
233+
className={styles.settingsSeparator}
234+
checked={useSemanticCaptions}
235+
label={t("labels.useSemanticCaptions")}
236+
onChange={(_ev, checked) => onChange("useSemanticCaptions", !!checked)}
237+
disabled={!useSemanticRanker}
238+
aria-labelledby={semanticCaptionsId}
239+
onRenderLabel={props => renderLabel(props, semanticCaptionsId, semanticCaptionsFieldId, t("helpTexts.useSemanticCaptions"))}
240+
/>
241+
</>
242+
)}
243+
244+
{useLogin && (
245+
<>
246+
<Checkbox
247+
id={useOidSecurityFilterFieldId}
248+
className={styles.settingsSeparator}
249+
checked={useOidSecurityFilter || requireAccessControl}
250+
label={t("labels.useOidSecurityFilter")}
251+
disabled={!loggedIn || requireAccessControl}
252+
onChange={(_ev, checked) => onChange("useOidSecurityFilter", !!checked)}
253+
aria-labelledby={useOidSecurityFilterId}
254+
onRenderLabel={props => renderLabel(props, useOidSecurityFilterId, useOidSecurityFilterFieldId, t("helpTexts.useOidSecurityFilter"))}
255+
/>
256+
<Checkbox
257+
id={useGroupsSecurityFilterFieldId}
258+
className={styles.settingsSeparator}
259+
checked={useGroupsSecurityFilter || requireAccessControl}
260+
label={t("labels.useGroupsSecurityFilter")}
261+
disabled={!loggedIn || requireAccessControl}
262+
onChange={(_ev, checked) => onChange("useGroupsSecurityFilter", !!checked)}
263+
aria-labelledby={useGroupsSecurityFilterId}
264+
onRenderLabel={props =>
265+
renderLabel(props, useGroupsSecurityFilterId, useGroupsSecurityFilterFieldId, t("helpTexts.useGroupsSecurityFilter"))
266+
}
267+
/>
268+
</>
269+
)}
270+
271+
{showGPT4VOptions && (
272+
<GPT4VSettings
273+
gpt4vInputs={gpt4vInput}
274+
isUseGPT4V={useGPT4V}
275+
updateUseGPT4V={val => onChange("useGPT4V", val)}
276+
updateGPT4VInputs={val => onChange("gpt4vInput", val)}
277+
/>
278+
)}
279+
280+
{showVectorOption && (
281+
<VectorSettings
282+
defaultRetrievalMode={retrievalMode}
283+
showImageOptions={useGPT4V && showGPT4VOptions}
284+
updateVectorFields={val => onChange("vectorFieldList", val)}
285+
updateRetrievalMode={val => onChange("retrievalMode", val)}
286+
/>
287+
)}
288+
289+
{/* Add optional streaming checkbox for Chat */}
290+
{shouldStream !== undefined && (
291+
<Checkbox
292+
id={shouldStreamFieldId}
293+
className={styles.settingsSeparator}
294+
checked={shouldStream}
295+
label={t("labels.shouldStream")}
296+
onChange={(_ev, checked) => onChange("shouldStream", !!checked)}
297+
aria-labelledby={shouldStreamId}
298+
onRenderLabel={props => renderLabel(props, shouldStreamId, shouldStreamFieldId, t("helpTexts.streamChat"))}
299+
/>
300+
)}
301+
302+
{/* Add optional followup questions checkbox for Chat */}
303+
{showSuggestFollowupQuestions && (
304+
<Checkbox
305+
id={suggestFollowupQuestionsFieldId}
306+
className={styles.settingsSeparator}
307+
checked={useSuggestFollowupQuestions}
308+
label={t("labels.useSuggestFollowupQuestions")}
309+
onChange={(_ev, checked) => onChange("useSuggestFollowupQuestions", !!checked)}
310+
aria-labelledby={suggestFollowupQuestionsId}
311+
onRenderLabel={props =>
312+
renderLabel(props, suggestFollowupQuestionsId, suggestFollowupQuestionsFieldId, t("helpTexts.suggestFollowupQuestions"))
313+
}
314+
/>
315+
)}
316+
</div>
317+
);
318+
};

0 commit comments

Comments
 (0)