Add float16 data type and make vector embedding policies mutable (#2433)

* vector index work

* format

* fixed tests

---------

Co-authored-by: Asier Isayas <aisayas@microsoft.com>
This commit is contained in:
asier-isayas
2026-03-30 06:38:37 -07:00
committed by GitHub
parent c42e35f97a
commit eac5842176
33 changed files with 381 additions and 94 deletions

View File

@@ -12,6 +12,7 @@ export interface CollapsibleSectionProps {
showDelete?: boolean;
onDelete?: () => void;
disabled?: boolean;
disableDelete?: boolean;
}
export interface CollapsibleSectionState {
@@ -75,7 +76,7 @@ export class CollapsibleSectionComponent extends React.Component<CollapsibleSect
{this.props.showDelete && (
<Stack.Item style={{ marginLeft: "auto" }}>
<IconButton
disabled={this.props.disabled}
disabled={this.props.disableDelete ?? this.props.disabled}
id={`delete-${this.props.title.split(" ").join("-")}`}
iconProps={{ iconName: "Delete" }}
style={{ height: 27, marginRight: "20px" }}

View File

@@ -124,6 +124,7 @@ export interface SettingsComponentState {
vectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy;
vectorEmbeddingPolicyBaseline: DataModels.VectorEmbeddingPolicy;
isVectorEmbeddingPolicyValid: boolean;
fullTextPolicy: DataModels.FullTextPolicy;
fullTextPolicyBaseline: DataModels.FullTextPolicy;
shouldDiscardContainerPolicies: boolean;
@@ -245,6 +246,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
vectorEmbeddingPolicy: undefined,
vectorEmbeddingPolicyBaseline: undefined,
isVectorEmbeddingPolicyValid: true,
fullTextPolicy: undefined,
fullTextPolicyBaseline: undefined,
shouldDiscardContainerPolicies: false,
@@ -372,6 +374,10 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
return false;
}
if (!this.state.isVectorEmbeddingPolicyValid) {
return false;
}
return (
this.state.isScaleSaveable ||
this.state.isSubSettingsSaveable ||
@@ -506,6 +512,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
changeFeedPolicy: this.state.changeFeedPolicyBaseline,
autoPilotThroughput: this.state.autoPilotThroughputBaseline,
isAutoPilotSelected: this.state.wasAutopilotOriginallySet,
isVectorEmbeddingPolicyValid: true,
shouldDiscardContainerPolicies: true,
shouldDiscardIndexingPolicy: true,
isScaleSaveable: false,
@@ -650,6 +657,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
private onVectorEmbeddingPolicyDirtyChange = (isVectorEmbeddingPolicyDirty: boolean): void =>
this.setState({ isContainerPolicyDirty: isVectorEmbeddingPolicyDirty });
private onVectorEmbeddingPolicyValidationChange = (isVectorEmbeddingPolicyValid: boolean): void =>
this.setState({ isVectorEmbeddingPolicyValid });
private onFullTextPolicyDirtyChange = (isFullTextPolicyDirty: boolean): void =>
this.setState({ isContainerPolicyDirty: isFullTextPolicyDirty });
@@ -1321,6 +1331,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
vectorEmbeddingPolicyBaseline: this.state.vectorEmbeddingPolicyBaseline,
onVectorEmbeddingPolicyChange: this.onVectorEmbeddingPolicyChange,
onVectorEmbeddingPolicyDirtyChange: this.onVectorEmbeddingPolicyDirtyChange,
onVectorEmbeddingPolicyValidationChange: this.onVectorEmbeddingPolicyValidationChange,
isVectorSearchEnabled: this.isVectorSearchEnabled,
fullTextPolicy: this.state.fullTextPolicy,
fullTextPolicyBaseline: this.state.fullTextPolicyBaseline,

View File

@@ -15,6 +15,7 @@ export interface ContainerPolicyComponentProps {
vectorEmbeddingPolicyBaseline: VectorEmbeddingPolicy;
onVectorEmbeddingPolicyChange: (newVectorEmbeddingPolicy: VectorEmbeddingPolicy) => void;
onVectorEmbeddingPolicyDirtyChange: (isVectorEmbeddingPolicyDirty: boolean) => void;
onVectorEmbeddingPolicyValidationChange: (isValid: boolean) => void;
isVectorSearchEnabled: boolean;
fullTextPolicy: FullTextPolicy;
fullTextPolicyBaseline: FullTextPolicy;
@@ -31,6 +32,7 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
vectorEmbeddingPolicyBaseline,
onVectorEmbeddingPolicyChange,
onVectorEmbeddingPolicyDirtyChange,
onVectorEmbeddingPolicyValidationChange,
isVectorSearchEnabled,
fullTextPolicy,
fullTextPolicyBaseline,
@@ -43,8 +45,12 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
const [selectedTab, setSelectedTab] = React.useState<ContainerPolicyTabTypes>(
ContainerPolicyTabTypes.VectorPolicyTab,
);
const [vectorEmbeddings, setVectorEmbeddings] = React.useState<VectorEmbedding[]>();
const [vectorEmbeddingsBaseline, setVectorEmbeddingsBaseline] = React.useState<VectorEmbedding[]>();
const [vectorEmbeddings, setVectorEmbeddings] = React.useState<VectorEmbedding[]>(
vectorEmbeddingPolicy?.vectorEmbeddings ?? [],
);
const [vectorEmbeddingsBaseline, setVectorEmbeddingsBaseline] = React.useState<VectorEmbedding[]>(
vectorEmbeddingPolicyBaseline?.vectorEmbeddings ?? [],
);
const [discardVectorChanges, setDiscardVectorChanges] = React.useState<boolean>(false);
const [fullTextSearchPolicy, setFullTextSearchPolicy] = React.useState<FullTextPolicy>();
const [fullTextSearchPolicyBaseline, setFullTextSearchPolicyBaseline] = React.useState<FullTextPolicy>();
@@ -53,7 +59,7 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
React.useEffect(() => {
setVectorEmbeddings(vectorEmbeddingPolicy?.vectorEmbeddings);
setVectorEmbeddingsBaseline(vectorEmbeddingPolicyBaseline?.vectorEmbeddings);
}, [vectorEmbeddingPolicy]);
}, [vectorEmbeddingPolicy, vectorEmbeddingPolicyBaseline]);
React.useEffect(() => {
setFullTextSearchPolicy(fullTextPolicy);
@@ -70,12 +76,15 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
}
});
const checkAndSendVectorEmbeddingPoliciesToSettings = (newVectorEmbeddings: VectorEmbedding[]): void => {
if (isDirty(newVectorEmbeddings, vectorEmbeddingsBaseline)) {
onVectorEmbeddingPolicyDirtyChange(true);
const checkAndSendVectorEmbeddingPoliciesToSettings = (
newVectorEmbeddings: VectorEmbedding[],
validationPassed: boolean,
): void => {
onVectorEmbeddingPolicyValidationChange(validationPassed);
const isVectorDirty: boolean = isDirty(newVectorEmbeddings, vectorEmbeddingsBaseline);
onVectorEmbeddingPolicyDirtyChange(isVectorDirty);
if (isVectorDirty) {
onVectorEmbeddingPolicyChange({ vectorEmbeddings: newVectorEmbeddings });
} else {
resetShouldDiscardContainerPolicyChange();
}
};
@@ -157,18 +166,18 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
headerText={t(Keys.controls.settings.containerPolicy.vectorPolicy)}
>
<Stack {...titleAndInputStackProps} styles={{ root: { position: "relative", maxWidth: "400px" } }}>
{vectorEmbeddings && (
<VectorEmbeddingPoliciesComponent
disabled={true}
vectorEmbeddings={vectorEmbeddings}
vectorIndexes={undefined}
onVectorEmbeddingChange={(vectorEmbeddings: VectorEmbedding[]) =>
checkAndSendVectorEmbeddingPoliciesToSettings(vectorEmbeddings)
}
discardChanges={discardVectorChanges}
onChangesDiscarded={onVectorChangesDiscarded}
/>
)}
<VectorEmbeddingPoliciesComponent
vectorEmbeddingsBaseline={vectorEmbeddingsBaseline}
vectorEmbeddings={vectorEmbeddings}
vectorIndexes={undefined}
onVectorEmbeddingChange={(
vectorEmbeddings: VectorEmbedding[],
_vectorIndexingPolicies,
validationPassed: boolean,
) => checkAndSendVectorEmbeddingPoliciesToSettings(vectorEmbeddings, validationPassed)}
discardChanges={discardVectorChanges}
onChangesDiscarded={onVectorChangesDiscarded}
/>
</Stack>
</PivotItem>
)}

View File

@@ -358,6 +358,7 @@ exports[`SettingsComponent renders 1`] = `
onFullTextPolicyDirtyChange={[Function]}
onVectorEmbeddingPolicyChange={[Function]}
onVectorEmbeddingPolicyDirtyChange={[Function]}
onVectorEmbeddingPolicyValidationChange={[Function]}
resetShouldDiscardContainerPolicyChange={[Function]}
shouldDiscardContainerPolicies={false}
vectorEmbeddingPolicy={{}}

View File

@@ -18,6 +18,7 @@ describe("AddVectorEmbeddingPolicyForm", () => {
beforeEach(() => {
component = render(
<VectorEmbeddingPoliciesComponent
vectorEmbeddingsBaseline={mockVectorEmbedding}
vectorEmbeddings={mockVectorEmbedding}
vectorIndexes={mockVectorIndex}
onVectorEmbeddingChange={mockOnVectorEmbeddingChange}

View File

@@ -20,6 +20,7 @@ import {
import React, { FunctionComponent, useState } from "react";
export interface IVectorEmbeddingPoliciesComponentProps {
vectorEmbeddingsBaseline: VectorEmbedding[];
vectorEmbeddings: VectorEmbedding[];
onVectorEmbeddingChange: (
vectorEmbeddings: VectorEmbedding[],
@@ -29,7 +30,6 @@ export interface IVectorEmbeddingPoliciesComponentProps {
vectorIndexes?: VectorIndex[];
discardChanges?: boolean;
onChangesDiscarded?: () => void;
disabled?: boolean;
isGlobalSecondaryIndex?: boolean;
}
@@ -85,14 +85,28 @@ const dropdownStyles = {
};
export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddingPoliciesComponentProps> = ({
vectorEmbeddingsBaseline,
vectorEmbeddings,
vectorIndexes,
onVectorEmbeddingChange,
discardChanges,
onChangesDiscarded,
disabled,
isGlobalSecondaryIndex,
}): JSX.Element => {
const isExistingPolicy = (policy: VectorEmbeddingPolicyData): boolean => {
if (!vectorEmbeddingsBaseline || vectorEmbeddingsBaseline.length === 0) {
return false;
}
return vectorEmbeddingsBaseline.some(
(baseline) =>
baseline.path === policy.path &&
baseline.dataType === policy.dataType &&
baseline.dimensions === policy.dimensions &&
baseline.distanceFunction === policy.distanceFunction,
);
};
const onVectorEmbeddingPathError = (path: string, index?: number): string => {
let error = "";
if (!path) {
@@ -139,7 +153,7 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
const initializeData = (vectorEmbeddings: VectorEmbedding[], vectorIndexes: VectorIndex[]) => {
const mergedData: VectorEmbeddingPolicyData[] = [];
vectorEmbeddings.forEach((embedding) => {
vectorEmbeddings?.forEach((embedding) => {
const matchingIndex = displayIndexes ? vectorIndexes.find((index) => index.path === embedding.path) : undefined;
mergedData.push({
...embedding,
@@ -151,6 +165,7 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
dimensionsError: onVectorEmbeddingDimensionError(embedding.dimensions, matchingIndex?.type || "none"),
});
});
return mergedData;
};
@@ -192,6 +207,7 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
const validationPassed = vectorEmbeddingPolicyData.every(
(policy: VectorEmbeddingPolicyData) => policy.pathError === "" && policy.dimensionsError === "",
);
onVectorEmbeddingChange(vectorEmbeddings, vectorIndexes, validationPassed);
};
@@ -300,12 +316,13 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
vectorEmbeddingPolicyData.length > 0 &&
vectorEmbeddingPolicyData.map((vectorEmbeddingPolicy: VectorEmbeddingPolicyData, index: number) => (
<CollapsibleSectionComponent
disabled={disabled}
disabled={isExistingPolicy(vectorEmbeddingPolicy)}
key={index}
isExpandedByDefault={true}
title={`Vector embedding ${index + 1}`}
showDelete={true}
onDelete={() => onDelete(index)}
disableDelete={false}
>
<Stack horizontal tokens={{ childrenGap: 4 }}>
<Stack
@@ -319,11 +336,11 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
}}
>
<Stack>
<Label disabled={disabled} styles={labelStyles}>
<Label disabled={isExistingPolicy(vectorEmbeddingPolicy)} styles={labelStyles}>
Path
</Label>
<TextField
disabled={disabled}
disabled={isExistingPolicy(vectorEmbeddingPolicy)}
id={`vector-policy-path-${index + 1}`}
required={true}
placeholder="/vector1"
@@ -334,11 +351,11 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
/>
</Stack>
<Stack>
<Label disabled={disabled} styles={labelStyles}>
<Label disabled={isExistingPolicy(vectorEmbeddingPolicy)} styles={labelStyles}>
Data type
</Label>
<Dropdown
disabled={disabled}
disabled={isExistingPolicy(vectorEmbeddingPolicy)}
required={true}
styles={dropdownStyles}
options={getDataTypeOptions()}
@@ -349,11 +366,11 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
></Dropdown>
</Stack>
<Stack>
<Label disabled={disabled} styles={labelStyles}>
<Label disabled={isExistingPolicy(vectorEmbeddingPolicy)} styles={labelStyles}>
Distance function
</Label>
<Dropdown
disabled={disabled}
disabled={isExistingPolicy(vectorEmbeddingPolicy)}
required={true}
styles={dropdownStyles}
options={getDistanceFunctionOptions()}
@@ -364,11 +381,11 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
></Dropdown>
</Stack>
<Stack>
<Label disabled={disabled} styles={labelStyles}>
<Label disabled={isExistingPolicy(vectorEmbeddingPolicy)} styles={labelStyles}>
Dimensions
</Label>
<TextField
disabled={disabled}
disabled={isExistingPolicy(vectorEmbeddingPolicy)}
id={`vector-policy-dimension-${index + 1}`}
required={true}
styles={textFieldStyles}
@@ -381,11 +398,11 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
</Stack>
{displayIndexes && (
<Stack>
<Label disabled={disabled} styles={labelStyles}>
<Label disabled={isExistingPolicy(vectorEmbeddingPolicy)} styles={labelStyles}>
Index type
</Label>
<Dropdown
disabled={disabled}
disabled={isExistingPolicy(vectorEmbeddingPolicy)}
required={true}
styles={dropdownStyles}
options={getIndexTypeOptions()}
@@ -397,7 +414,7 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
<Stack style={{ marginLeft: "10px" }}>
<Label
disabled={
disabled ||
isExistingPolicy(vectorEmbeddingPolicy) ||
(vectorEmbeddingPolicy.indexType !== "quantizedFlat" &&
vectorEmbeddingPolicy.indexType !== "diskANN")
}
@@ -408,7 +425,7 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
</Label>
<TextField
disabled={
disabled ||
isExistingPolicy(vectorEmbeddingPolicy) ||
(vectorEmbeddingPolicy.indexType !== "quantizedFlat" &&
vectorEmbeddingPolicy.indexType !== "diskANN")
}
@@ -421,11 +438,18 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
/>
</Stack>
<Stack style={{ marginLeft: "10px" }}>
<Label disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"} styles={labelStyles}>
<Label
disabled={
isExistingPolicy(vectorEmbeddingPolicy) || vectorEmbeddingPolicy.indexType !== "diskANN"
}
styles={labelStyles}
>
Indexing search list size
</Label>
<TextField
disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"}
disabled={
isExistingPolicy(vectorEmbeddingPolicy) || vectorEmbeddingPolicy.indexType !== "diskANN"
}
id={`vector-policy-indexingSearchListSize-${index + 1}`}
styles={textFieldStyles}
value={String(vectorEmbeddingPolicy.indexingSearchListSize || "")}
@@ -435,11 +459,18 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
/>
</Stack>
<Stack style={{ marginLeft: "10px" }}>
<Label disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"} styles={labelStyles}>
<Label
disabled={
isExistingPolicy(vectorEmbeddingPolicy) || vectorEmbeddingPolicy.indexType !== "diskANN"
}
styles={labelStyles}
>
Vector index shard key
</Label>
<TextField
disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"}
disabled={
isExistingPolicy(vectorEmbeddingPolicy) || vectorEmbeddingPolicy.indexType !== "diskANN"
}
id={`vector-policy-vectorIndexShardKey-${index + 1}`}
styles={textFieldStyles}
value={String(vectorEmbeddingPolicy.vectorIndexShardKey?.[0] ?? "")}
@@ -452,12 +483,7 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
</Stack>
</CollapsibleSectionComponent>
))}
<DefaultButton
disabled={disabled}
id={`add-vector-policy`}
styles={{ root: { maxWidth: 170, fontSize: 12 } }}
onClick={onAdd}
>
<DefaultButton id={`add-vector-policy`} styles={{ root: { maxWidth: 170, fontSize: 12 } }} onClick={onAdd}>
Add vector embedding
</DefaultButton>
</Stack>

View File

@@ -1,6 +1,6 @@
import { IDropdownOption } from "@fluentui/react";
const dataTypes = ["float32", "uint8", "int8"];
const dataTypes = ["float32", "uint8", "int8", "float16"];
const distanceFunctions = ["euclidean", "cosine", "dotproduct"];
const indexTypes = ["none", "flat", "diskANN", "quantizedFlat"];

View File

@@ -897,6 +897,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
<Stack id="collapsibleVectorPolicySectionContent" styles={{ root: { position: "relative" } }}>
<Stack styles={{ root: { paddingLeft: 40 } }}>
<VectorEmbeddingPoliciesComponent
vectorEmbeddingsBaseline={[]}
vectorEmbeddings={this.state.vectorEmbeddingPolicy}
vectorIndexes={this.state.vectorIndexingPolicy}
onVectorEmbeddingChange={(

View File

@@ -40,6 +40,7 @@ export const VectorSearchComponent = (props: VectorSearchComponentProps): JSX.El
<Stack id="collapsibleVectorPolicySectionContent" styles={{ root: { position: "relative" } }}>
<Stack styles={{ root: { paddingLeft: 40 } }}>
<VectorEmbeddingPoliciesComponent
vectorEmbeddingsBaseline={[]}
vectorEmbeddings={vectorEmbeddingPolicy}
vectorIndexes={vectorIndexingPolicy}
onVectorEmbeddingChange={(