feat: Add Hybrid Search functionality to AstraDB + AstraPy / LangChain Updates (#7358)
* feat: Add Hybrid Search functionality and AstraPy 2.0 and associated deps (#7357) * astrapy 2.0 tentative full pass * Update the create collection function --------- Co-authored-by: Stefano Lottini <stefano.lottini@datastax.com> * Update deps * Update uv.lock * Fix linting errors in astradb * Update package lock * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * Add basic UI scaffolding for hybrid search * [autofix.ci] apply automated fixes * Continue to clean up component * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * Fix the keyspace compatibility * [autofix.ci] apply automated fixes * feat: add nodeId, nodeClass, and handleNodeClass props to dropdown an… (#7406) feat: add nodeId, nodeClass, and handleNodeClass props to dropdown and string render components Co-authored-by: deon-sanchez <deon.sanchez@datastax.com> * Update uv.lock * Update uv.lock * Add hybrid search support in collection creation * [autofix.ci] apply automated fixes * Updates from review comments * [autofix.ci] apply automated fixes * Add in lexical search support * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * Detect collection hybrid params * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * Pass lexical terms at search time * [autofix.ci] apply automated fixes * Update test_astra_component.py * Add Query Input and Mixin on backend * Adds Query on supported types * Adds types for query modal and component * Adds size for new query modal * Adds query modal * Adds query component * Adds query component on parameter render * [autofix.ci] apply automated fixes * Feedback from review * [autofix.ci] apply automated fixes * ✨ (switch-case-size.ts): Update height value to 'h-fit' for 'small-query' case to improve responsiveness ✨ (queryInputComponent.spec.ts): Add unit test for user interaction with query input component, including updating code and testing functionality * Switch to multiline for lexical terms * [autofix.ci] apply automated fixes * Create Hybrid Search RAG.json * Update Hybrid Search RAG.json * Added queryInput in vectorstore model * Added queryInput in lexical terms * Update model.py * Update Hybrid Search RAG.json * Add query support in field validation * fix: bump Astra Assistants version to support AstraPy 2.0 (#7535) 2.2.12 Co-authored-by: phact <estevezsebastian@gmail.com> * Update uv.lock * Fixed QueryInput not receiving text from handle * Set search type to similarity search when hybrid * Always set to similarity when we have the reranker * [autofix.ci] apply automated fixes * Add logging for hybrid search support * Update starter projects * Update Hybrid Search RAG.json * Added dropdown toggle on backend * Added toggle on dropdown on frontend * Added showing only value if there is just one option in the dropdown * Added toggle to Dropdown Input on Astra Db * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * init toggle value as true or false * Change it to send null value if toggle is disabled * Added resizer on search query * Added Search Hybrid, Lexical and Vector icons * Added icons and new Lexical Search on Dropdown Input of Astra DB * Updated starter projects * Changed descriptions on astradb component * Changed starter projects * Lexical search option for dropdown * Update astradb.py * Update starter projects * One small lexical update * Update astradb.py * Update projects * [autofix.ci] apply automated fixes * Fixed dropdown changing when toggle is off * Update astradb.py * [autofix.ci] apply automated fixes * Don't show lexical terms on new collection creation * ✨ (actionsMainPage-shard-0.spec.ts): add functionality to add flow to test on empty langflow button click ✨ (filterEdge-shard-1.spec.ts): add functionality to add flow to test on empty langflow button click ♻️ (await-bootstrap-test.ts): refactor code to reuse addFlowToTestOnEmptyLangflow function for adding flow to test on empty langflow button click * [autofix.ci] apply automated fixes * 🐛 (filterEdge-shard-1.spec.ts): fix incorrect reference to memoriesAstra DB Chat Memory, update to memoriesMem0 Chat Memory for accurate testing data. --------- Co-authored-by: Stefano Lottini <stefano.lottini@datastax.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: deon-sanchez <deon.sanchez@datastax.com> Co-authored-by: Lucas Oliveira <lucas.edu.oli@hotmail.com> Co-authored-by: cristhianzl <cristhian.lousa@gmail.com> Co-authored-by: phact <estevezsebastian@gmail.com>
This commit is contained in:
parent
fb79b80f91
commit
907a594428
30 changed files with 4901 additions and 1804 deletions
|
|
@ -53,6 +53,7 @@ export default function Dropdown({
|
|||
name,
|
||||
dialogInputs,
|
||||
handleOnNewValue,
|
||||
toggle,
|
||||
...baseInputProps
|
||||
}: BaseInputProps & DropDownComponent): JSX.Element {
|
||||
const validOptions = useMemo(
|
||||
|
|
@ -482,6 +483,19 @@ export default function Dropdown({
|
|||
<PopoverAnchor>{children}</PopoverAnchor>
|
||||
) : refreshOptions || isLoading ? (
|
||||
renderLoadingButton()
|
||||
) : validOptions.length === 1 &&
|
||||
toggle &&
|
||||
!combobox &&
|
||||
value === validOptions[0] ? (
|
||||
<div className="flex w-full items-center gap-2 truncate">
|
||||
{optionsMetaData?.[0]?.icon && (
|
||||
<ForwardedIconComponent
|
||||
name={optionsMetaData?.[0]?.icon}
|
||||
className="h-4 w-4 flex-shrink-0"
|
||||
/>
|
||||
)}
|
||||
<span className="truncate text-sm">{value}</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-full truncate">{renderTriggerButton()}</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import Dropdown from "../../../dropdownComponent";
|
||||
import { DropDownComponentType, InputProps } from "../../types";
|
||||
import ToggleShadComponent from "../toggleShadComponent";
|
||||
|
||||
export default function DropdownComponent({
|
||||
id,
|
||||
|
|
@ -15,6 +16,8 @@ export default function DropdownComponent({
|
|||
nodeClass,
|
||||
nodeId,
|
||||
handleNodeClass,
|
||||
toggle,
|
||||
toggleValue,
|
||||
...baseInputProps
|
||||
}: InputProps<string, DropDownComponentType>) {
|
||||
const onChange = (value: any, dbValue?: boolean, skipSnapshot?: boolean) => {
|
||||
|
|
@ -22,22 +25,39 @@ export default function DropdownComponent({
|
|||
};
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
disabled={disabled}
|
||||
editNode={editNode}
|
||||
options={options}
|
||||
nodeId={nodeId}
|
||||
nodeClass={nodeClass}
|
||||
handleNodeClass={handleNodeClass}
|
||||
optionsMetaData={optionsMetaData}
|
||||
onSelect={onChange}
|
||||
combobox={combobox}
|
||||
value={value || ""}
|
||||
id={`dropdown_${id}`}
|
||||
name={name}
|
||||
dialogInputs={dialogInputs}
|
||||
handleOnNewValue={handleOnNewValue}
|
||||
{...baseInputProps}
|
||||
/>
|
||||
<div className="flex w-full items-center gap-4">
|
||||
<Dropdown
|
||||
disabled={disabled || toggleValue === false}
|
||||
editNode={editNode}
|
||||
toggle={toggle}
|
||||
options={options}
|
||||
nodeId={nodeId}
|
||||
nodeClass={nodeClass}
|
||||
handleNodeClass={handleNodeClass}
|
||||
optionsMetaData={optionsMetaData}
|
||||
onSelect={onChange}
|
||||
combobox={combobox}
|
||||
value={value || (toggleValue === false && toggle ? options[0] : "")}
|
||||
id={`dropdown_${id}`}
|
||||
name={name}
|
||||
dialogInputs={dialogInputs}
|
||||
handleOnNewValue={handleOnNewValue}
|
||||
{...baseInputProps}
|
||||
/>
|
||||
{toggle && (
|
||||
<ToggleShadComponent
|
||||
value={toggleValue ?? true}
|
||||
handleOnNewValue={(data) => {
|
||||
handleOnNewValue({
|
||||
value: data.value === true ? options[0] : null,
|
||||
toggle_value: data.value,
|
||||
});
|
||||
}}
|
||||
editNode={editNode}
|
||||
id={`toggle_dropdown_${id}`}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ export function StrRenderComponent({
|
|||
optionsMetaData={templateData.options_metadata}
|
||||
combobox={templateData.combobox}
|
||||
name={templateData?.name!}
|
||||
toggle={templateData.toggle}
|
||||
toggleValue={templateData.toggle_value}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,8 @@ export type DropDownComponentType = {
|
|||
nodeId: string;
|
||||
nodeClass: APIClassType;
|
||||
handleNodeClass: (value: any, code?: string, type?: string) => void;
|
||||
toggle?: boolean;
|
||||
toggleValue?: boolean;
|
||||
};
|
||||
|
||||
export type TextAreaComponentType = {
|
||||
|
|
|
|||
23
src/frontend/src/icons/SearchHybrid/SearchHybridIcon.jsx
Normal file
23
src/frontend/src/icons/SearchHybrid/SearchHybridIcon.jsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
const SvgSearchHybridIcon = (props) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
>
|
||||
<path d="M15.75 15.75L12.525 12.525L15.75 15.75Z" fill="currentColor" />
|
||||
<path
|
||||
d="M1.5 11.625C3.36396 11.625 4.875 10.114 4.875 8.25C4.875 10.114 6.38604 11.625 8.25 11.625C6.38604 11.625 4.875 13.136 4.875 15C4.875 13.136 3.36396 11.625 1.5 11.625Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M15.75 15.75L12.525 12.525M2.43903 6.75C3.10509 4.16216 5.45424 2.25 8.25 2.25C11.5637 2.25 14.25 4.93629 14.25 8.25C14.25 11.0458 12.3378 13.3949 9.75 14.061M4.875 8.25C4.875 10.114 3.36396 11.625 1.5 11.625C3.36396 11.625 4.875 13.136 4.875 15C4.875 13.136 6.38604 11.625 8.25 11.625C6.38604 11.625 4.875 10.114 4.875 8.25Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgSearchHybridIcon;
|
||||
9
src/frontend/src/icons/SearchHybrid/index.tsx
Normal file
9
src/frontend/src/icons/SearchHybrid/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import SvgSearchHybridIcon from "./SearchHybridIcon";
|
||||
|
||||
export const SearchHybridIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <SvgSearchHybridIcon ref={ref} {...props} />;
|
||||
});
|
||||
22
src/frontend/src/icons/SearchLexical/SearchLexicalIcon.jsx
Normal file
22
src/frontend/src/icons/SearchLexical/SearchLexicalIcon.jsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
const SvgSearchLexicalIcon = (props) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
>
|
||||
<path d="M15.75 15.75L12.525 12.525L15.75 15.75Z" fill="currentColor" />
|
||||
<path d="M3.75 15H5.25H6.75" fill="currentColor" />
|
||||
<path d="M5.25 9.75V15V9.75Z" fill="currentColor" />
|
||||
<path d="M8.25 11.25V9.75H5.25H2.25V11.25" fill="currentColor" />
|
||||
<path
|
||||
d="M15.75 15.75L12.525 12.525M2.43903 6.75C3.1051 4.16216 5.45425 2.25 8.25001 2.25C11.5637 2.25 14.25 4.93629 14.25 8.25C14.25 11.0458 12.3378 13.3949 9.75001 14.061M3.75 15H5.25M6.75 15H5.25M5.25 9.75V15M5.25 9.75H8.25V11.25M5.25 9.75H2.25V11.25"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgSearchLexicalIcon;
|
||||
9
src/frontend/src/icons/SearchLexical/index.tsx
Normal file
9
src/frontend/src/icons/SearchLexical/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import SvgSearchLexicalIcon from "./SearchLexicalIcon";
|
||||
|
||||
export const SearchLexicalIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <SvgSearchLexicalIcon ref={ref} {...props} />;
|
||||
});
|
||||
19
src/frontend/src/icons/SearchVector/SearchVectorIcon.jsx
Normal file
19
src/frontend/src/icons/SearchVector/SearchVectorIcon.jsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
const SvgSearchVectorIcon = (props) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
>
|
||||
<path d="M15.75 15.75L12.525 12.525L15.75 15.75Z" fill="currentColor" />
|
||||
<path
|
||||
d="M15.75 15.75L12.525 12.525M2.43903 6.75C3.1051 4.16216 5.45425 2.25 8.25001 2.25C11.5637 2.25 14.25 4.93629 14.25 8.25C14.25 11.0458 12.3378 13.3949 9.75001 14.061M2.25 14.25V11.25M2.25 14.25H5.25M2.25 14.25L7.5 9"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgSearchVectorIcon;
|
||||
9
src/frontend/src/icons/SearchVector/index.tsx
Normal file
9
src/frontend/src/icons/SearchVector/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import SvgSearchVectorIcon from "./SearchVectorIcon";
|
||||
|
||||
export const SearchVectorIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <SvgSearchVectorIcon ref={ref} {...props} />;
|
||||
});
|
||||
|
|
@ -47,7 +47,7 @@ export default function QueryModal({
|
|||
<div className={classNames("flex h-full w-full rounded-lg border")}>
|
||||
<Textarea
|
||||
ref={textRef}
|
||||
className="form-input h-full w-full resize-none overflow-auto rounded-lg focus-visible:ring-1"
|
||||
className="form-input h-full min-h-28 w-full overflow-auto rounded-lg focus-visible:ring-1"
|
||||
value={inputValue}
|
||||
onChange={(event) => {
|
||||
setInputValue(event.target.value);
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ export type DropDownComponent = {
|
|||
children?: ReactNode;
|
||||
name: string;
|
||||
dialogInputs?: any;
|
||||
toggle?: boolean;
|
||||
};
|
||||
export type ParameterComponentType = {
|
||||
selected?: boolean;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ import { MilvusIcon } from "@/icons/Milvus";
|
|||
import { OneDriveIcon } from "@/icons/OneDrive";
|
||||
import Perplexity from "@/icons/Perplexity/Perplexity";
|
||||
import { SearchAPIIcon } from "@/icons/SearchAPI";
|
||||
import { SearchHybridIcon } from "@/icons/SearchHybrid";
|
||||
import { SearchLexicalIcon } from "@/icons/SearchLexical";
|
||||
import { SearchVectorIcon } from "@/icons/SearchVector";
|
||||
import { SerpSearchIcon } from "@/icons/SerpSearch";
|
||||
import { TavilyIcon } from "@/icons/Tavily";
|
||||
import { UnstructuredIcon } from "@/icons/Unstructured";
|
||||
|
|
@ -754,6 +757,9 @@ export const nodeIconsLucide: iconsType = {
|
|||
OpenAI: OpenAiIcon,
|
||||
OpenRouter: OpenRouterIcon,
|
||||
DeepSeek: DeepSeekIcon,
|
||||
SearchLexical: SearchLexicalIcon,
|
||||
SearchHybrid: SearchHybridIcon,
|
||||
SearchVector: SearchVectorIcon,
|
||||
xAI: XAIIcon,
|
||||
OpenAIEmbeddings: OpenAiIcon,
|
||||
Pinecone: PineconeIcon,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
import { addFlowToTestOnEmptyLangflow } from "../../utils/add-flow-to-test-on-empty-langflow";
|
||||
import { adjustScreenView } from "../../utils/adjust-screen-view";
|
||||
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||
|
||||
|
|
@ -62,6 +63,12 @@ test(
|
|||
await page.waitForSelector('[data-testid="mainpage_title"]', {
|
||||
timeout: 30000,
|
||||
});
|
||||
const countEmptyButton = await page
|
||||
.getByTestId("new_project_btn_empty_page")
|
||||
.count();
|
||||
if (countEmptyButton > 0) {
|
||||
await addFlowToTestOnEmptyLangflow(page);
|
||||
}
|
||||
await page.getByTestId("upload-folder-button").last().click();
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
import { addFlowToTestOnEmptyLangflow } from "../../utils/add-flow-to-test-on-empty-langflow";
|
||||
import { addLegacyComponents } from "../../utils/add-legacy-components";
|
||||
import { adjustScreenView } from "../../utils/adjust-screen-view";
|
||||
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||
|
|
@ -9,6 +10,10 @@ test(
|
|||
async ({ page }) => {
|
||||
await awaitBootstrapTest(page);
|
||||
|
||||
await page.waitForSelector('[data-testid="mainpage_title"]', {
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
await page.getByTestId("blank-flow").click();
|
||||
|
||||
await page.waitForSelector('[data-testid="sidebar-options-trigger"]', {
|
||||
|
|
@ -73,7 +78,7 @@ test(
|
|||
"vectorstoresAstra DB",
|
||||
"langchain_utilitiesTool Calling Agent",
|
||||
"langchain_utilitiesConversationChain",
|
||||
"memoriesAstra DB Chat Memory",
|
||||
"memoriesMem0 Chat Memory",
|
||||
"logicCondition",
|
||||
"langchain_utilitiesSelf Query Retriever",
|
||||
"langchain_utilitiesCharacterTextSplitter",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Page } from "playwright/test";
|
||||
import { addFlowToTestOnEmptyLangflow } from "./add-flow-to-test-on-empty-langflow";
|
||||
|
||||
export const awaitBootstrapTest = async (
|
||||
page: Page,
|
||||
|
|
@ -15,6 +16,13 @@ export const awaitBootstrapTest = async (
|
|||
timeout: 30000,
|
||||
});
|
||||
|
||||
const countEmptyButton = await page
|
||||
.getByTestId("new_project_btn_empty_page")
|
||||
.count();
|
||||
if (countEmptyButton > 0) {
|
||||
await addFlowToTestOnEmptyLangflow(page);
|
||||
}
|
||||
|
||||
await page.waitForSelector('[id="new-project-btn"]', {
|
||||
timeout: 30000,
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue