mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-09 15:17:59 -05:00
feat(frontend): Add advanced block search with relevance ranking (#9711)
- fix #9425 - Enhancing the functionality of searching blocks on the build page Currently, it only performs exact matching on the block name and description. I added a scoring mechanism for searching. - The scoring algorithm works as follows: - Returns 1 if no query (all blocks match equally) - Normalized query for case-insensitive matching - Returns 3 for exact substring matches in block name (highest priority) - Returns 2 when all query words appear in the block name (regardless of order) - Returns 1.X for blocks with names similar to query using Jaro-Winkler distance (X is similarity score) - Returns 0.5 when all query words appear in the block description (lowest priority) - Returns 0 for no match Higher scores will appear first in search results. > I have used an external library for Jaro-Winkler distance - [link](https://www.npmjs.com/package/jaro-winkler) Before  After 
This commit is contained in:
@@ -48,6 +48,7 @@
|
|||||||
"@supabase/ssr": "^0.5.2",
|
"@supabase/ssr": "^0.5.2",
|
||||||
"@supabase/supabase-js": "^2.49.1",
|
"@supabase/supabase-js": "^2.49.1",
|
||||||
"@tanstack/react-table": "^8.21.2",
|
"@tanstack/react-table": "^8.21.2",
|
||||||
|
"@types/jaro-winkler": "^0.2.4",
|
||||||
"@xyflow/react": "12.4.2",
|
"@xyflow/react": "12.4.2",
|
||||||
"ajv": "^8.17.1",
|
"ajv": "^8.17.1",
|
||||||
"boring-avatars": "^1.11.2",
|
"boring-avatars": "^1.11.2",
|
||||||
@@ -62,6 +63,7 @@
|
|||||||
"embla-carousel-react": "^8.5.2",
|
"embla-carousel-react": "^8.5.2",
|
||||||
"framer-motion": "^12.4.11",
|
"framer-motion": "^12.4.11",
|
||||||
"geist": "^1.3.1",
|
"geist": "^1.3.1",
|
||||||
|
"jaro-winkler": "^0.2.8",
|
||||||
"launchdarkly-react-client-sdk": "^3.6.1",
|
"launchdarkly-react-client-sdk": "^3.6.1",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lucide-react": "^0.479.0",
|
"lucide-react": "^0.479.0",
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { GraphMeta } from "@/lib/autogpt-server-api";
|
import { GraphMeta } from "@/lib/autogpt-server-api";
|
||||||
|
import jaro from "jaro-winkler";
|
||||||
|
|
||||||
interface BlocksControlProps {
|
interface BlocksControlProps {
|
||||||
blocks: Block[];
|
blocks: Block[];
|
||||||
@@ -89,21 +90,73 @@ export const BlocksControl: React.FC<BlocksControlProps> = ({
|
|||||||
}) satisfies Block,
|
}) satisfies Block,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates how well a block matches the search query and returns a relevance score.
|
||||||
|
* The scoring algorithm works as follows:
|
||||||
|
* - Returns 1 if no query (all blocks match equally)
|
||||||
|
* - Normalized query for case-insensitive matching
|
||||||
|
* - Returns 3 for exact substring matches in block name (highest priority)
|
||||||
|
* - Returns 2 when all query words appear in the block name (regardless of order)
|
||||||
|
* - Returns 1.X for blocks with names similar to query using Jaro-Winkler distance (X is similarity score)
|
||||||
|
* - Returns 0.5 when all query words appear in the block description (lowest priority)
|
||||||
|
* - Returns 0 for no match
|
||||||
|
*
|
||||||
|
* Higher scores will appear first in search results.
|
||||||
|
*/
|
||||||
|
const matchesSearch = (block: Block, query: string): number => {
|
||||||
|
if (!query) return 1;
|
||||||
|
const normalizedQuery = query.toLowerCase().trim();
|
||||||
|
const queryWords = normalizedQuery.split(/\s+/);
|
||||||
|
const blockName = block.name.toLowerCase();
|
||||||
|
const beautifiedName = beautifyString(block.name).toLowerCase();
|
||||||
|
const description = block.description.toLowerCase();
|
||||||
|
|
||||||
|
// 1. Exact match in name (highest priority)
|
||||||
|
if (
|
||||||
|
blockName.includes(normalizedQuery) ||
|
||||||
|
beautifiedName.includes(normalizedQuery)
|
||||||
|
) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. All query words in name (regardless of order)
|
||||||
|
const allWordsInName = queryWords.every(
|
||||||
|
(word) => blockName.includes(word) || beautifiedName.includes(word),
|
||||||
|
);
|
||||||
|
if (allWordsInName) return 2;
|
||||||
|
|
||||||
|
// 3. Similarity with name (Jaro-Winkler)
|
||||||
|
const similarityThreshold = 0.65;
|
||||||
|
const nameSimilarity = jaro(blockName, normalizedQuery);
|
||||||
|
const beautifiedSimilarity = jaro(beautifiedName, normalizedQuery);
|
||||||
|
const maxSimilarity = Math.max(nameSimilarity, beautifiedSimilarity);
|
||||||
|
if (maxSimilarity > similarityThreshold) {
|
||||||
|
return 1 + maxSimilarity; // Score between 1 and 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. All query words in description (lower priority)
|
||||||
|
const allWordsInDescription = queryWords.every((word) =>
|
||||||
|
description.includes(word),
|
||||||
|
);
|
||||||
|
if (allWordsInDescription) return 0.5;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
return blockList
|
return blockList
|
||||||
.concat(agentBlockList)
|
.concat(agentBlockList)
|
||||||
|
.map((block) => ({
|
||||||
|
block,
|
||||||
|
score: matchesSearch(block, searchQuery),
|
||||||
|
}))
|
||||||
.filter(
|
.filter(
|
||||||
(block: Block) =>
|
({ block, score }) =>
|
||||||
(block.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
score > 0 &&
|
||||||
beautifyString(block.name)
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchQuery.toLowerCase()) ||
|
|
||||||
block.description
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchQuery.toLowerCase())) &&
|
|
||||||
(!selectedCategory ||
|
(!selectedCategory ||
|
||||||
block.categories.some((cat) => cat.category === selectedCategory)),
|
block.categories.some((cat) => cat.category === selectedCategory)),
|
||||||
)
|
)
|
||||||
.map((block) => ({
|
.sort((a, b) => b.score - a.score)
|
||||||
|
.map(({ block }) => ({
|
||||||
...block,
|
...block,
|
||||||
notAvailable:
|
notAvailable:
|
||||||
(block.uiType == BlockUIType.WEBHOOK &&
|
(block.uiType == BlockUIType.WEBHOOK &&
|
||||||
|
|||||||
@@ -3783,6 +3783,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/istanbul-lib-report" "*"
|
"@types/istanbul-lib-report" "*"
|
||||||
|
|
||||||
|
"@types/jaro-winkler@^0.2.4":
|
||||||
|
version "0.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/jaro-winkler/-/jaro-winkler-0.2.4.tgz#fb8d9df08a984f99aef8e5a96ded3f662c738241"
|
||||||
|
integrity sha512-TNVu6vL0Z3h+hYcW78IRloINA0y0MTVJ1PFVtVpBSgk+ejmaH5aVfcVghzNXZ0fa6gXe4zapNMQtMGWOJKTLig==
|
||||||
|
|
||||||
"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
|
"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
|
||||||
version "7.0.15"
|
version "7.0.15"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||||
@@ -7858,6 +7863,11 @@ jackspeak@^3.1.2:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
"@pkgjs/parseargs" "^0.11.0"
|
"@pkgjs/parseargs" "^0.11.0"
|
||||||
|
|
||||||
|
jaro-winkler@^0.2.8:
|
||||||
|
version "0.2.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/jaro-winkler/-/jaro-winkler-0.2.8.tgz#6727e0d0b7091e2436f9356de9bf88fad23e534a"
|
||||||
|
integrity sha512-yr+mElb6dWxA1mzFu0+26njV5DWAQRNTi5pB6fFMm79zHrfAs3d0qjhe/IpZI4AHIUJkzvu5QXQRWOw2O0GQyw==
|
||||||
|
|
||||||
jest-changed-files@^29.7.0:
|
jest-changed-files@^29.7.0:
|
||||||
version "29.7.0"
|
version "29.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a"
|
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a"
|
||||||
|
|||||||
Reference in New Issue
Block a user