mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-09 23:28:07 -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/supabase-js": "^2.49.1",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"@types/jaro-winkler": "^0.2.4",
|
||||
"@xyflow/react": "12.4.2",
|
||||
"ajv": "^8.17.1",
|
||||
"boring-avatars": "^1.11.2",
|
||||
@@ -62,6 +63,7 @@
|
||||
"embla-carousel-react": "^8.5.2",
|
||||
"framer-motion": "^12.4.11",
|
||||
"geist": "^1.3.1",
|
||||
"jaro-winkler": "^0.2.8",
|
||||
"launchdarkly-react-client-sdk": "^3.6.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lucide-react": "^0.479.0",
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { GraphMeta } from "@/lib/autogpt-server-api";
|
||||
import jaro from "jaro-winkler";
|
||||
|
||||
interface BlocksControlProps {
|
||||
blocks: Block[];
|
||||
@@ -89,21 +90,73 @@ export const BlocksControl: React.FC<BlocksControlProps> = ({
|
||||
}) 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
|
||||
.concat(agentBlockList)
|
||||
.map((block) => ({
|
||||
block,
|
||||
score: matchesSearch(block, searchQuery),
|
||||
}))
|
||||
.filter(
|
||||
(block: Block) =>
|
||||
(block.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
beautifyString(block.name)
|
||||
.toLowerCase()
|
||||
.includes(searchQuery.toLowerCase()) ||
|
||||
block.description
|
||||
.toLowerCase()
|
||||
.includes(searchQuery.toLowerCase())) &&
|
||||
({ block, score }) =>
|
||||
score > 0 &&
|
||||
(!selectedCategory ||
|
||||
block.categories.some((cat) => cat.category === selectedCategory)),
|
||||
)
|
||||
.map((block) => ({
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.map(({ block }) => ({
|
||||
...block,
|
||||
notAvailable:
|
||||
(block.uiType == BlockUIType.WEBHOOK &&
|
||||
|
||||
@@ -3783,6 +3783,11 @@
|
||||
dependencies:
|
||||
"@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":
|
||||
version "7.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||
@@ -7858,6 +7863,11 @@ jackspeak@^3.1.2:
|
||||
optionalDependencies:
|
||||
"@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:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a"
|
||||
|
||||
Reference in New Issue
Block a user