feat(goto-anything): add RAG pipeline node search (#25948)
This commit is contained in:
@@ -167,10 +167,39 @@ import { appAction } from './app'
|
||||
import { knowledgeAction } from './knowledge'
|
||||
import { pluginAction } from './plugin'
|
||||
import { workflowNodesAction } from './workflow-nodes'
|
||||
import { ragPipelineNodesAction } from './rag-pipeline-nodes'
|
||||
import type { ActionItem, SearchResult } from './types'
|
||||
import { slashAction } from './commands'
|
||||
import { slashCommandRegistry } from './commands/registry'
|
||||
|
||||
// Create dynamic Actions based on context
|
||||
export const createActions = (isWorkflowPage: boolean, isRagPipelinePage: boolean) => {
|
||||
const baseActions = {
|
||||
slash: slashAction,
|
||||
app: appAction,
|
||||
knowledge: knowledgeAction,
|
||||
plugin: pluginAction,
|
||||
}
|
||||
|
||||
// Add appropriate node search based on context
|
||||
if (isRagPipelinePage) {
|
||||
return {
|
||||
...baseActions,
|
||||
node: ragPipelineNodesAction,
|
||||
}
|
||||
}
|
||||
else if (isWorkflowPage) {
|
||||
return {
|
||||
...baseActions,
|
||||
node: workflowNodesAction,
|
||||
}
|
||||
}
|
||||
|
||||
// Default actions without node search
|
||||
return baseActions
|
||||
}
|
||||
|
||||
// Legacy export for backward compatibility
|
||||
export const Actions = {
|
||||
slash: slashAction,
|
||||
app: appAction,
|
||||
@@ -183,6 +212,7 @@ export const searchAnything = async (
|
||||
locale: string,
|
||||
query: string,
|
||||
actionItem?: ActionItem,
|
||||
dynamicActions?: Record<string, ActionItem>,
|
||||
): Promise<SearchResult[]> => {
|
||||
if (actionItem) {
|
||||
const searchTerm = query.replace(actionItem.key, '').replace(actionItem.shortcut, '').trim()
|
||||
@@ -198,7 +228,7 @@ export const searchAnything = async (
|
||||
if (query.startsWith('@') || query.startsWith('/'))
|
||||
return []
|
||||
|
||||
const globalSearchActions = Object.values(Actions)
|
||||
const globalSearchActions = Object.values(dynamicActions || Actions)
|
||||
|
||||
// Use Promise.allSettled to handle partial failures gracefully
|
||||
const searchPromises = globalSearchActions.map(async (action) => {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { ActionItem } from './types'
|
||||
|
||||
// Create the RAG pipeline nodes action
|
||||
export const ragPipelineNodesAction: ActionItem = {
|
||||
key: '@node',
|
||||
shortcut: '@node',
|
||||
title: 'Search RAG Pipeline Nodes',
|
||||
description: 'Find and jump to nodes in the current RAG pipeline by name or type',
|
||||
searchFn: undefined, // Will be set by useRagPipelineSearch hook
|
||||
search: async (_, searchTerm = '', _locale) => {
|
||||
try {
|
||||
// Use the searchFn if available (set by useRagPipelineSearch hook)
|
||||
if (ragPipelineNodesAction.searchFn)
|
||||
return ragPipelineNodesAction.searchFn(searchTerm)
|
||||
|
||||
// If not in RAG pipeline context, return empty array
|
||||
return []
|
||||
}
|
||||
catch (error) {
|
||||
console.warn('RAG pipeline nodes search failed:', error)
|
||||
return []
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -7,7 +7,7 @@ export const workflowNodesAction: ActionItem = {
|
||||
title: 'Search Workflow Nodes',
|
||||
description: 'Find and jump to nodes in the current workflow by name or type',
|
||||
searchFn: undefined, // Will be set by useWorkflowSearch hook
|
||||
search: async (_, searchTerm = '', locale) => {
|
||||
search: async (_, searchTerm = '', _locale) => {
|
||||
try {
|
||||
// Use the searchFn if available (set by useWorkflowSearch hook)
|
||||
if (workflowNodesAction.searchFn)
|
||||
|
||||
@@ -12,11 +12,16 @@ type GotoAnythingContextType = {
|
||||
* Whether the current page is a workflow page
|
||||
*/
|
||||
isWorkflowPage: boolean
|
||||
/**
|
||||
* Whether the current page is a RAG pipeline page
|
||||
*/
|
||||
isRagPipelinePage: boolean
|
||||
}
|
||||
|
||||
// Create context with default values
|
||||
const GotoAnythingContext = createContext<GotoAnythingContextType>({
|
||||
isWorkflowPage: false,
|
||||
isRagPipelinePage: false,
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -33,17 +38,28 @@ type GotoAnythingProviderProps = {
|
||||
*/
|
||||
export const GotoAnythingProvider: React.FC<GotoAnythingProviderProps> = ({ children }) => {
|
||||
const [isWorkflowPage, setIsWorkflowPage] = useState(false)
|
||||
const [isRagPipelinePage, setIsRagPipelinePage] = useState(false)
|
||||
const pathname = usePathname()
|
||||
|
||||
// Update context based on current pathname
|
||||
// Update context based on current pathname using more robust route matching
|
||||
useEffect(() => {
|
||||
// Check if current path contains workflow
|
||||
const isWorkflow = pathname?.includes('/workflow') || false
|
||||
if (!pathname) {
|
||||
setIsWorkflowPage(false)
|
||||
setIsRagPipelinePage(false)
|
||||
return
|
||||
}
|
||||
|
||||
// Workflow pages: /app/[appId]/workflow or /workflow/[token] (shared)
|
||||
const isWorkflow = /^\/app\/[^/]+\/workflow$/.test(pathname) || /^\/workflow\/[^/]+$/.test(pathname)
|
||||
// RAG Pipeline pages: /datasets/[datasetId]/pipeline
|
||||
const isRagPipeline = /^\/datasets\/[^/]+\/pipeline$/.test(pathname)
|
||||
|
||||
setIsWorkflowPage(isWorkflow)
|
||||
setIsRagPipelinePage(isRagPipeline)
|
||||
}, [pathname])
|
||||
|
||||
return (
|
||||
<GotoAnythingContext.Provider value={{ isWorkflowPage }}>
|
||||
<GotoAnythingContext.Provider value={{ isWorkflowPage, isRagPipelinePage }}>
|
||||
{children}
|
||||
</GotoAnythingContext.Provider>
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useDebounce, useKeyPress } from 'ahooks'
|
||||
import { getKeyboardKeyCodeBySystem, isEventTargetInputArea, isMac } from '@/app/components/workflow/utils/common'
|
||||
import { selectWorkflowNode } from '@/app/components/workflow/utils/node-navigation'
|
||||
import { RiSearchLine } from '@remixicon/react'
|
||||
import { Actions as AllActions, type SearchResult, matchAction, searchAnything } from './actions'
|
||||
import { type SearchResult, createActions, matchAction, searchAnything } from './actions'
|
||||
import { GotoAnythingProvider, useGotoAnythingContext } from './context'
|
||||
import { slashCommandRegistry } from './actions/commands/registry'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
@@ -29,7 +29,7 @@ const GotoAnything: FC<Props> = ({
|
||||
}) => {
|
||||
const router = useRouter()
|
||||
const defaultLocale = useGetLanguage()
|
||||
const { isWorkflowPage } = useGotoAnythingContext()
|
||||
const { isWorkflowPage, isRagPipelinePage } = useGotoAnythingContext()
|
||||
const { t } = useTranslation()
|
||||
const [show, setShow] = useState<boolean>(false)
|
||||
const [searchQuery, setSearchQuery] = useState<string>('')
|
||||
@@ -38,16 +38,9 @@ const GotoAnything: FC<Props> = ({
|
||||
|
||||
// Filter actions based on context
|
||||
const Actions = useMemo(() => {
|
||||
// Create a filtered copy of actions based on current page context
|
||||
if (isWorkflowPage) {
|
||||
// Include all actions on workflow pages
|
||||
return AllActions
|
||||
}
|
||||
else {
|
||||
const { app, knowledge, plugin, slash } = AllActions
|
||||
return { app, knowledge, plugin, slash }
|
||||
}
|
||||
}, [isWorkflowPage])
|
||||
// Create actions based on current page context
|
||||
return createActions(isWorkflowPage, isRagPipelinePage)
|
||||
}, [isWorkflowPage, isRagPipelinePage])
|
||||
|
||||
const [activePlugin, setActivePlugin] = useState<Plugin>()
|
||||
|
||||
@@ -99,9 +92,11 @@ const GotoAnything: FC<Props> = ({
|
||||
|
||||
const query = searchQueryDebouncedValue.toLowerCase()
|
||||
const action = matchAction(query, Actions)
|
||||
return action
|
||||
? (action.key === '/' ? '@command' : action.key)
|
||||
: 'general'
|
||||
|
||||
if (!action)
|
||||
return 'general'
|
||||
|
||||
return action.key === '/' ? '@command' : action.key
|
||||
}, [searchQueryDebouncedValue, Actions, isCommandsMode, searchQuery])
|
||||
|
||||
const { data: searchResults = [], isLoading, isError, error } = useQuery(
|
||||
@@ -112,13 +107,14 @@ const GotoAnything: FC<Props> = ({
|
||||
searchQueryDebouncedValue,
|
||||
searchMode,
|
||||
isWorkflowPage,
|
||||
isRagPipelinePage,
|
||||
defaultLocale,
|
||||
Object.keys(Actions).sort().join(','),
|
||||
],
|
||||
queryFn: async () => {
|
||||
const query = searchQueryDebouncedValue.toLowerCase()
|
||||
const action = matchAction(query, Actions)
|
||||
return await searchAnything(defaultLocale, query, action)
|
||||
return await searchAnything(defaultLocale, query, action, Actions)
|
||||
},
|
||||
enabled: !!searchQueryDebouncedValue && !isCommandsMode,
|
||||
staleTime: 30000,
|
||||
@@ -446,18 +442,20 @@ const GotoAnything: FC<Props> = ({
|
||||
) : (
|
||||
<>
|
||||
<span className='opacity-60'>
|
||||
{isCommandsMode
|
||||
? t('app.gotoAnything.selectToNavigate')
|
||||
: searchQuery.trim()
|
||||
? t('app.gotoAnything.searching')
|
||||
: t('app.gotoAnything.startTyping')
|
||||
}
|
||||
{(() => {
|
||||
if (isCommandsMode)
|
||||
return t('app.gotoAnything.selectToNavigate')
|
||||
|
||||
if (searchQuery.trim())
|
||||
return t('app.gotoAnything.searching')
|
||||
|
||||
return t('app.gotoAnything.startTyping')
|
||||
})()}
|
||||
</span>
|
||||
<span className='opacity-60'>
|
||||
{searchQuery.trim() || isCommandsMode
|
||||
? t('app.gotoAnything.tips')
|
||||
: t('app.gotoAnything.pressEscToClose')
|
||||
}
|
||||
: t('app.gotoAnything.pressEscToClose')}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user