feat(v5): Add prompt suggestions to keyword search (#2912)
* feat(v5): Add prompt suggestions to keyword search * cleanup: Move consistent object to reusable constant
This commit is contained in:
parent
e26256bb41
commit
907c844f83
10 changed files with 360 additions and 58 deletions
|
|
@ -40,6 +40,9 @@ export default function BasicAskAI({ theme }: { theme: DemoTheme }): JSX.Element
|
|||
},
|
||||
}}
|
||||
resultBadgeKey="type"
|
||||
promptSuggestions={{
|
||||
indexName: 'docsearch-markdown_prompt_suggestions',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,6 +184,19 @@ export interface Memory {
|
|||
userToken?: string;
|
||||
}
|
||||
|
||||
export interface PromptSuggestions {
|
||||
/**
|
||||
* The name of the index where the prompt suggestions are stored.
|
||||
*/
|
||||
indexName: string;
|
||||
/**
|
||||
* The number of prompt suggestions that are retrieved and displayed.
|
||||
*
|
||||
* @default 3
|
||||
*/
|
||||
hitsPerPage?: number;
|
||||
}
|
||||
|
||||
export interface DocSearchAIProps extends DocSearchProps {
|
||||
/**
|
||||
* Configuration or assistant id to enable ask ai mode. Pass a string assistant id or a full config object.
|
||||
|
|
@ -208,6 +221,15 @@ export interface DocSearchAIProps extends DocSearchProps {
|
|||
* Configuration for the Agent Studio memory feature.
|
||||
*/
|
||||
memory?: Memory;
|
||||
/**
|
||||
* Enables and configures prompt suggestions that are displayed during keyword search.
|
||||
*
|
||||
* @see https://www.algolia.com/doc/guides/algolia-ai/agent-studio/how-to/integration#prompt-suggestions
|
||||
*
|
||||
* @example
|
||||
* { indexName: 'docsearch-markdown_prompt_suggestions', hitsPerPage: 1 }
|
||||
*/
|
||||
promptSuggestions?: PromptSuggestions;
|
||||
}
|
||||
|
||||
function DocSearchAIComponent(props: DocSearchAIProps, ref: React.ForwardedRef<DocSearchRef>): JSX.Element {
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ export function DocSearchAskAiModal({
|
|||
facets,
|
||||
isHybridModeSupported = false,
|
||||
tools = EMPTY_TOOLS,
|
||||
promptSuggestions,
|
||||
...props
|
||||
}: DocSearchAskAiModalProps): JSX.Element {
|
||||
const {
|
||||
|
|
@ -328,7 +329,7 @@ export function DocSearchAskAiModal({
|
|||
onStateChange(changes) {
|
||||
setState(changes.state);
|
||||
},
|
||||
getSources({ query, state: sourcesState, setContext, setStatus }) {
|
||||
async getSources({ query, state: sourcesState, setContext, setStatus }) {
|
||||
if (!query) {
|
||||
const noQuerySources = buildNoQuerySources({
|
||||
recentSearches,
|
||||
|
|
@ -371,11 +372,19 @@ export function DocSearchAskAiModal({
|
|||
facetSelections: facetSelectionsRef,
|
||||
});
|
||||
|
||||
const askAiSource = canHandleAskAi ? buildAskAiActionSources({ query, handleSelectAskAiQuestion }) : [];
|
||||
const askAiSourcesPromise = canHandleAskAi
|
||||
? buildAskAiActionSources({
|
||||
query,
|
||||
handleSelectAskAiQuestion,
|
||||
promptSuggestionsOptions: promptSuggestions,
|
||||
searchClient,
|
||||
})
|
||||
: Promise.resolve([]);
|
||||
|
||||
const [askAiSources, algoliaSources] = await Promise.all([askAiSourcesPromise, algoliaSourcesPromise]);
|
||||
|
||||
// Combine Algolia results (once resolved) with the Ask AI source
|
||||
return algoliaSourcesPromise.then((algoliaSources) => {
|
||||
return [...askAiSource, ...algoliaSources];
|
||||
});
|
||||
return [...askAiSources, ...algoliaSources];
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,14 @@ import { useRelativeFormattedDate } from './hooks/useRelativeFormattedDate';
|
|||
import { SparklesIcon } from './icons/SparklesIcon';
|
||||
import { Snippet } from './Snippet';
|
||||
import type { InternalDocSearchHit, StoredDocSearchHit } from './types';
|
||||
import { decodeHtmlEntities, getHitItemBreadcrumbs } from './utils';
|
||||
import { decodeHtmlEntities, getHitItemBreadcrumbs, SOURCE_IDS } from './utils';
|
||||
|
||||
export type ResultsTranslations = HitResultBadgeTranslations &
|
||||
Partial<{
|
||||
askAiPlaceholder: string;
|
||||
noResultsAskAiPlaceholder: string;
|
||||
recentConversationTimestampFallback: string;
|
||||
askAiResultsTitle: string;
|
||||
}>;
|
||||
interface ResultsProps<TItem extends BaseItem>
|
||||
extends AutocompleteApi<TItem, React.FormEvent, React.MouseEvent, React.KeyboardEvent> {
|
||||
|
|
@ -31,6 +32,9 @@ interface ResultsProps<TItem extends BaseItem>
|
|||
}
|
||||
|
||||
export function Results<TItem extends StoredDocSearchHit>(props: ResultsProps<TItem>): JSX.Element | null {
|
||||
const { askAiResultsTitle = 'Ask AI Assistant' } = props.translations || {};
|
||||
|
||||
const askAiResultsId = React.useId();
|
||||
// The collection title, decoded to handle encoded HTML entities
|
||||
// If there is not a title, return null to not render anything
|
||||
const decodedTitle = React.useMemo(() => {
|
||||
|
|
@ -45,18 +49,26 @@ export function Results<TItem extends StoredDocSearchHit>(props: ResultsProps<TI
|
|||
return null;
|
||||
}
|
||||
|
||||
if (props.collection.source.sourceId === 'askAI') {
|
||||
if (props.collection.source.sourceId === SOURCE_IDS.askAI) {
|
||||
return (
|
||||
<section className="DocSearch-Hits">
|
||||
<div className="DocSearch-Hit-source">Ask AI Assistant</div>
|
||||
<ul className="DocSearch-Hits-padded" {...props.getListProps({ source: props.collection.source })}>
|
||||
<AskAiButton item={props.collection.items[0]} translations={props.translations} {...props} />
|
||||
<h2 id={askAiResultsId} className="DocSearch-Hit-source">
|
||||
{askAiResultsTitle}
|
||||
</h2>
|
||||
<ul
|
||||
className="DocSearch-Hits-padded"
|
||||
{...props.getListProps({ source: props.collection.source })}
|
||||
aria-labelledby={askAiResultsId}
|
||||
>
|
||||
{props.collection.items.map((item) => (
|
||||
<AskAiButton key={item.objectID} item={item} translations={props.translations} {...props} />
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
if (props.collection.source.sourceId === 'recentConversations') {
|
||||
if (props.collection.source.sourceId === SOURCE_IDS.recentConversations) {
|
||||
return (
|
||||
<section className="DocSearch-Hits">
|
||||
<div className="DocSearch-Hit-source">
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export type ResultsScreenTranslations = Partial<{
|
|||
askAiPlaceholder: string;
|
||||
noResultsAskAiPlaceholder: string;
|
||||
resultsSectionTitle: string;
|
||||
askAiResultsTitle: string;
|
||||
}> &
|
||||
ResultsTranslations;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { render, act, fireEvent, screen, cleanup } from '@testing-library/react';
|
||||
import React, { type JSX } from 'react';
|
||||
import { describe, it, expect, afterEach } from 'vitest';
|
||||
import { describe, it, expect, afterEach, vi } from 'vitest';
|
||||
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
|
||||
|
|
@ -38,6 +38,33 @@ function noResultSearch(_queries: any, _requestOptions?: any): Promise<any> {
|
|||
});
|
||||
}
|
||||
|
||||
function promptSuggestionsSearch(queries: any, _requestOptions?: any): Promise<any> {
|
||||
const [request] = queries.requests;
|
||||
|
||||
if (request.indexName === 'prompt-suggestions') {
|
||||
return Promise.resolve({
|
||||
results: [
|
||||
{
|
||||
hits: [
|
||||
{ objectID: 'prompt-1', prompt: 'How do I configure DocSearch?' },
|
||||
{ objectID: 'prompt-2', prompt: 'How do I add facets?' },
|
||||
],
|
||||
hitsPerPage: 3,
|
||||
nbHits: 2,
|
||||
nbPages: 1,
|
||||
page: 0,
|
||||
processingTimeMS: 0,
|
||||
exhaustiveNbHits: true,
|
||||
params: '',
|
||||
query: request.query,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
return noResultSearch(queries);
|
||||
}
|
||||
|
||||
describe('api', () => {
|
||||
const docSearchSelector = '.DocSearch';
|
||||
|
||||
|
|
@ -300,7 +327,11 @@ describe('api', () => {
|
|||
});
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(await screen.findByText('hello', { selector: '.DocSearch-Hit-AskAIButton-title-query' }));
|
||||
fireEvent.click(
|
||||
await screen.findByText('hello', {
|
||||
selector: '.DocSearch-Hit-AskAIButton-title-query',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
expect(document.querySelector('.DocSearch-AskAiScreen')).toBeInTheDocument();
|
||||
|
|
@ -308,6 +339,56 @@ describe('api', () => {
|
|||
// could be "Answering..." or "Ask another question..."
|
||||
expect(screen.getByText('Answering...')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders and selects prompt suggestions', async () => {
|
||||
const interceptAskAiEvent = vi.fn(() => true);
|
||||
|
||||
render(
|
||||
<DocSearchAI
|
||||
promptSuggestions={{ indexName: 'prompt-suggestions' }}
|
||||
transformSearchClient={(searchClient) => ({
|
||||
...searchClient,
|
||||
search: promptSuggestionsSearch,
|
||||
})}
|
||||
interceptAskAiEvent={interceptAskAiEvent}
|
||||
translations={{
|
||||
modal: {
|
||||
resultsScreen: {
|
||||
askAiResultsTitle: 'Suggested questions',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(await screen.findByText('Search'));
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.input(await screen.findByPlaceholderText('Search docs or ask AI a question'), {
|
||||
target: { value: 'configure' },
|
||||
});
|
||||
});
|
||||
|
||||
const heading = await screen.findByRole('heading', {
|
||||
name: 'Suggested questions',
|
||||
});
|
||||
const results = document.querySelector(`[aria-labelledby="${heading.id}"]`);
|
||||
|
||||
expect(results).toBeInTheDocument();
|
||||
expect(await screen.findByText('How do I configure DocSearch?')).toBeInTheDocument();
|
||||
expect(screen.getByText('How do I add facets?')).toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(screen.getByText('How do I configure DocSearch?'));
|
||||
});
|
||||
|
||||
expect(interceptAskAiEvent).toHaveBeenCalledWith({
|
||||
query: 'How do I configure DocSearch?',
|
||||
suggestedQuestionId: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('portalContainer', () => {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export function SparklesIcon({ className = '' }: Props): JSX.Element {
|
|||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={`DocSearch-Hit-icon-sparkles ${className}`}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z" />
|
||||
<path d="M20 3v4" />
|
||||
|
|
|
|||
|
|
@ -109,3 +109,7 @@ export type AlgoliaMCPSearchOutputPart = ToolUIPart<
|
|||
}
|
||||
>;
|
||||
export type SearchOutputPart = AlgoliaMCPSearchOutputPart | SearchIndexOutputPart;
|
||||
|
||||
export interface PromptSuggestion {
|
||||
prompt: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { buildAskAiActionSources } from '../createAskAiSources';
|
||||
|
||||
const searchClient = {
|
||||
search: vi.fn(),
|
||||
} as any;
|
||||
|
||||
describe('buildAskAiActionSources', () => {
|
||||
it('returns only the Ask AI action when prompt suggestions are disabled', async () => {
|
||||
const handleSelectAskAiQuestion = vi.fn();
|
||||
const sources = await buildAskAiActionSources({
|
||||
query: 'How do I install DocSearch?',
|
||||
handleSelectAskAiQuestion,
|
||||
searchClient,
|
||||
});
|
||||
|
||||
expect(searchClient.search).not.toHaveBeenCalled();
|
||||
expect(sources[0].getItems({} as any)).toMatchObject([
|
||||
{
|
||||
objectID: 'ask-ai-button',
|
||||
query: 'How do I install DocSearch?',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('requests and returns prompt suggestions after the Ask AI action', async () => {
|
||||
searchClient.search.mockResolvedValue({
|
||||
results: [
|
||||
{
|
||||
hits: [
|
||||
{ objectID: 'prompt-1', prompt: 'How do I configure DocSearch?' },
|
||||
{ objectID: 'prompt-2', prompt: 'How do I add facets?' },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const sources = await buildAskAiActionSources({
|
||||
query: 'configure',
|
||||
handleSelectAskAiQuestion: vi.fn(),
|
||||
promptSuggestionsOptions: {
|
||||
indexName: 'prompt-suggestions',
|
||||
hitsPerPage: 2,
|
||||
},
|
||||
searchClient,
|
||||
});
|
||||
|
||||
expect(searchClient.search).toHaveBeenCalledWith({
|
||||
requests: [
|
||||
{
|
||||
query: 'configure',
|
||||
indexName: 'prompt-suggestions',
|
||||
hitsPerPage: 2,
|
||||
attributesToRetrieve: ['prompt'],
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(sources[0].getItems({} as any)).toMatchObject([
|
||||
{ objectID: 'ask-ai-button', query: 'configure' },
|
||||
{ objectID: 'prompt-1', query: 'How do I configure DocSearch?' },
|
||||
{ objectID: 'prompt-2', query: 'How do I add facets?' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('selects a prompt suggestion as the Ask AI query', async () => {
|
||||
searchClient.search.mockResolvedValue({
|
||||
results: [
|
||||
{
|
||||
hits: [{ objectID: 'prompt-1', prompt: 'How do I configure DocSearch?' }],
|
||||
},
|
||||
],
|
||||
});
|
||||
const handleSelectAskAiQuestion = vi.fn();
|
||||
const sources = await buildAskAiActionSources({
|
||||
query: 'configure',
|
||||
handleSelectAskAiQuestion,
|
||||
promptSuggestionsOptions: { indexName: 'prompt-suggestions' },
|
||||
searchClient,
|
||||
});
|
||||
const [, suggestion] = (await sources[0].getItems({} as any)) as any[];
|
||||
|
||||
sources[0].onSelect?.({ item: suggestion } as any);
|
||||
|
||||
expect(handleSelectAskAiQuestion).toHaveBeenCalledWith(true, 'How do I configure DocSearch?');
|
||||
});
|
||||
|
||||
it('keeps the Ask AI action when prompt suggestion retrieval fails', async () => {
|
||||
searchClient.search.mockRejectedValue(new Error('Network error'));
|
||||
|
||||
const sources = await buildAskAiActionSources({
|
||||
query: 'configure',
|
||||
handleSelectAskAiQuestion: vi.fn(),
|
||||
promptSuggestionsOptions: { indexName: 'prompt-suggestions' },
|
||||
searchClient,
|
||||
});
|
||||
|
||||
expect(sources[0].getItems({} as any)).toMatchObject([{ objectID: 'ask-ai-button', query: 'configure' }]);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,12 +1,40 @@
|
|||
import type { AutocompleteSource } from '@algolia/autocomplete-core';
|
||||
import type { SearchResponse } from 'algoliasearch/lite';
|
||||
|
||||
import type { DocSearchTransformClient } from '../DocSearch';
|
||||
import type { PromptSuggestions } from '../DocSearchAI';
|
||||
import type { InternalDocSearchHit } from '../types';
|
||||
import type { AIMessage } from '../types/AskiAi';
|
||||
import type { AIMessage, PromptSuggestion } from '../types/AskiAi';
|
||||
|
||||
import { SOURCE_IDS } from './collections';
|
||||
|
||||
const MAX_RECENT_CONVERSATIONS_DISPLAYED = 3;
|
||||
|
||||
const EMPTY_RESULT_CONTENT = {
|
||||
value: '',
|
||||
matchLevel: 'none',
|
||||
matchedWords: [],
|
||||
} satisfies InternalDocSearchHit['_highlightResult']['content'];
|
||||
const EMPTY_HIERARCHY_HIGHLIGHT_RESULT = {
|
||||
lvl0: EMPTY_RESULT_CONTENT,
|
||||
lvl1: EMPTY_RESULT_CONTENT,
|
||||
lvl2: EMPTY_RESULT_CONTENT,
|
||||
lvl3: EMPTY_RESULT_CONTENT,
|
||||
lvl4: EMPTY_RESULT_CONTENT,
|
||||
lvl5: EMPTY_RESULT_CONTENT,
|
||||
lvl6: EMPTY_RESULT_CONTENT,
|
||||
} satisfies InternalDocSearchHit['_highlightResult']['hierarchy'];
|
||||
const EMPTY_HIGHLIGHT_RESULT: InternalDocSearchHit['_highlightResult'] = {
|
||||
content: EMPTY_RESULT_CONTENT,
|
||||
hierarchy: EMPTY_HIERARCHY_HIGHLIGHT_RESULT,
|
||||
hierarchy_camel: [],
|
||||
};
|
||||
const EMPTY_SNIPPET_RESULT: InternalDocSearchHit['_snippetResult'] = {
|
||||
content: EMPTY_RESULT_CONTENT,
|
||||
hierarchy: EMPTY_HIERARCHY_HIGHLIGHT_RESULT,
|
||||
hierarchy_camel: [],
|
||||
};
|
||||
|
||||
export function buildRecentConversationSources({
|
||||
conversations,
|
||||
disableUserPersonalization,
|
||||
|
|
@ -40,60 +68,101 @@ export function buildRecentConversationSources({
|
|||
];
|
||||
}
|
||||
|
||||
export function buildAskAiActionSources({
|
||||
function createAskAiHit({
|
||||
query,
|
||||
objectID,
|
||||
lvl0,
|
||||
}: {
|
||||
query: string;
|
||||
objectID: string;
|
||||
lvl0: string;
|
||||
}): InternalDocSearchHit {
|
||||
return {
|
||||
type: 'askAI',
|
||||
query,
|
||||
url_without_anchor: '',
|
||||
objectID,
|
||||
content: null,
|
||||
url: '',
|
||||
anchor: null,
|
||||
hierarchy: {
|
||||
lvl0,
|
||||
lvl1: query,
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null,
|
||||
lvl6: null,
|
||||
},
|
||||
_highlightResult: EMPTY_HIGHLIGHT_RESULT,
|
||||
_snippetResult: EMPTY_SNIPPET_RESULT,
|
||||
__docsearch_parent: null,
|
||||
};
|
||||
}
|
||||
|
||||
async function getPromptSuggestions({
|
||||
query,
|
||||
indexName,
|
||||
searchClient,
|
||||
hitsPerPage = 3,
|
||||
}: {
|
||||
query: string;
|
||||
indexName: string;
|
||||
searchClient: DocSearchTransformClient;
|
||||
hitsPerPage?: number;
|
||||
}): Promise<InternalDocSearchHit[]> {
|
||||
try {
|
||||
const { results } = await searchClient.search<PromptSuggestion>({
|
||||
requests: [
|
||||
{
|
||||
query,
|
||||
indexName,
|
||||
hitsPerPage,
|
||||
attributesToRetrieve: ['prompt'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const res = results[0] as SearchResponse<PromptSuggestion>;
|
||||
|
||||
return res.hits.map((hit) =>
|
||||
createAskAiHit({
|
||||
query: hit.prompt,
|
||||
objectID: hit.objectID,
|
||||
lvl0: 'Prompt Suggestions',
|
||||
}),
|
||||
);
|
||||
// NOTE: Not checking the exception here and just returning empty array since this isn't the hot path
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function buildAskAiActionSources({
|
||||
query,
|
||||
handleSelectAskAiQuestion,
|
||||
promptSuggestionsOptions,
|
||||
searchClient,
|
||||
}: {
|
||||
query: string;
|
||||
handleSelectAskAiQuestion: (toggle: boolean, query: string) => void;
|
||||
}): Array<AutocompleteSource<InternalDocSearchHit>> {
|
||||
const emptyHierarchyHighlightResult = {
|
||||
lvl0: { value: '', matchLevel: 'none', matchedWords: [] },
|
||||
lvl1: { value: '', matchLevel: 'none', matchedWords: [] },
|
||||
lvl2: { value: '', matchLevel: 'none', matchedWords: [] },
|
||||
lvl3: { value: '', matchLevel: 'none', matchedWords: [] },
|
||||
lvl4: { value: '', matchLevel: 'none', matchedWords: [] },
|
||||
lvl5: { value: '', matchLevel: 'none', matchedWords: [] },
|
||||
lvl6: { value: '', matchLevel: 'none', matchedWords: [] },
|
||||
} satisfies InternalDocSearchHit['_highlightResult']['hierarchy'];
|
||||
const emptyHighlightResult: InternalDocSearchHit['_highlightResult'] = {
|
||||
content: { value: '', matchLevel: 'none', matchedWords: [] },
|
||||
hierarchy: emptyHierarchyHighlightResult,
|
||||
hierarchy_camel: [],
|
||||
};
|
||||
const emptySnippetResult: InternalDocSearchHit['_snippetResult'] = {
|
||||
content: { value: '', matchLevel: 'none' },
|
||||
hierarchy: emptyHierarchyHighlightResult,
|
||||
hierarchy_camel: [],
|
||||
};
|
||||
promptSuggestionsOptions?: PromptSuggestions;
|
||||
searchClient: DocSearchTransformClient;
|
||||
}): Promise<Array<AutocompleteSource<InternalDocSearchHit>>> {
|
||||
const promptSuggestions = promptSuggestionsOptions
|
||||
? await getPromptSuggestions({
|
||||
query,
|
||||
indexName: promptSuggestionsOptions.indexName,
|
||||
hitsPerPage: promptSuggestionsOptions.hitsPerPage,
|
||||
searchClient,
|
||||
})
|
||||
: [];
|
||||
|
||||
return [
|
||||
{
|
||||
sourceId: SOURCE_IDS.askAI,
|
||||
getItems(): InternalDocSearchHit[] {
|
||||
return [
|
||||
{
|
||||
type: 'askAI',
|
||||
query,
|
||||
url_without_anchor: '',
|
||||
objectID: 'ask-ai-button',
|
||||
content: null,
|
||||
url: '',
|
||||
anchor: null,
|
||||
hierarchy: {
|
||||
lvl0: 'Ask AI',
|
||||
lvl1: query,
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null,
|
||||
lvl6: null,
|
||||
},
|
||||
_highlightResult: emptyHighlightResult,
|
||||
_snippetResult: emptySnippetResult,
|
||||
__docsearch_parent: null,
|
||||
},
|
||||
];
|
||||
return [createAskAiHit({ query, objectID: 'ask-ai-button', lvl0: 'Ask AI' }), ...promptSuggestions];
|
||||
},
|
||||
onSelect({ item }): void {
|
||||
if (item.type === 'askAI' && item.query) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue