fix(askai): Ask AI fixes for v5 (#2945)
This commit is contained in:
parent
b87c28fc87
commit
9fe6738a35
16 changed files with 217 additions and 111 deletions
20
.changeset/ready-clubs-serve.md
Normal file
20
.changeset/ready-clubs-serve.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
"@docsearch/react": patch
|
||||
"@docsearch/css": patch
|
||||
---
|
||||
|
||||
fix(askai): Ask AI fixes for v5
|
||||
|
||||
- `getMessageContent` now joins every assistant text part instead of stopping
|
||||
at the first one, so copying an answer that interleaves text with tool calls
|
||||
returns the complete response (#2782)
|
||||
- Key Ask AI conversation storage by `appId` instead of `indexName`, fixes
|
||||
triggering a new conversation in hybrid mode
|
||||
- Negative feedback panel is labelled with `aria-labelledby` and reason chips
|
||||
expose selection via `aria-pressed` (styling moved off the
|
||||
`--selected` modifier)
|
||||
- `Popover.Trigger` forwards refs, fixes sources panel not showing in some cases
|
||||
- Add `--docsearch-error-soft-color`, `--docsearch-error-text-color`, and
|
||||
`--docsearch-code-block-background` variables; restyle the Ask AI error panel
|
||||
and markdown code blocks to use them
|
||||
```
|
||||
|
|
@ -317,7 +317,8 @@
|
|||
"typescript/prefer-namespace-keyword": ["error"],
|
||||
"typescript/prefer-ts-expect-error": ["error"],
|
||||
"typescript/unified-signatures": ["error"],
|
||||
"vitest/require-mock-type-parameters": "off"
|
||||
"vitest/require-mock-type-parameters": "off",
|
||||
"no-debugger": ["error"]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
|
|
|
|||
21
AGENTS.md
21
AGENTS.md
|
|
@ -78,27 +78,6 @@ bun run pw:run:webkit
|
|||
|
||||
## Code Style Guidelines
|
||||
|
||||
### Imports
|
||||
|
||||
Imported modules must be ordered alphabetically with newlines between groups:
|
||||
|
||||
1. Built-in modules
|
||||
2. External dependencies
|
||||
3. Parent directory imports
|
||||
4. Sibling imports
|
||||
5. Index imports
|
||||
|
||||
Internal `@/**/*` paths go before parent imports.
|
||||
|
||||
```typescript
|
||||
// Correct order
|
||||
import type { AutocompleteOptions } from '@algolia/autocomplete-core';
|
||||
import React, { type JSX } from 'react';
|
||||
|
||||
import { DocSearchButton } from './DocSearchButton';
|
||||
import type { DocSearchHit } from './types';
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
- Use `type` imports for type-only imports: `import type { Foo } from './types'`
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
/* eslint-disable react/react-in-jsx-scope */
|
||||
import { version } from '@docsearch/react';
|
||||
import { DocSearchSidepanel } from '@docsearch/react/sidepanel';
|
||||
import { useState, type JSX } from 'react';
|
||||
|
||||
import './App.css';
|
||||
|
|
@ -121,17 +120,6 @@ function App(): JSX.Element {
|
|||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<DocSearchSidepanel
|
||||
assistantId="ccdec697-e3fe-465b-a1c3-657e7bf18aef"
|
||||
indexName="docsearch"
|
||||
appId="PMZUYBQDAK"
|
||||
apiKey="24b09689d5b4223813d9b8e48563c8f6"
|
||||
panel={{
|
||||
suggestedQuestions: true,
|
||||
}}
|
||||
theme={theme}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,19 +201,12 @@
|
|||
|
||||
.DocSearch-AskAiScreen-Answer-Footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.DocSearch-AskAiScreen-Actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
gap: var(--docsearch-spacing);
|
||||
}
|
||||
|
||||
.DocSearch-AskAiScreen-Actions-Controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
|
@ -226,13 +219,16 @@
|
|||
.DocSearch-Feedback-Panel {
|
||||
flex-basis: 100%;
|
||||
width: 100%;
|
||||
padding: var(--docsearch-spacing);
|
||||
padding-block: var(--docsearch-spacing);
|
||||
padding-inline: var(--docsearch-spacing);
|
||||
margin-block-start: var(--docsearch-spacing);
|
||||
border-top: 1px solid var(--docsearch-subtle-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
gap: 0.75rem;
|
||||
text-align: start;
|
||||
animation: fade-in 0.2s ease-in forwards;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.DocSearch-Feedback-Panel-Header {
|
||||
|
|
@ -253,7 +249,8 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px;
|
||||
padding-inline: 0.25rem;
|
||||
padding-block: 0.25rem;
|
||||
border: 0;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
|
|
@ -267,8 +264,8 @@
|
|||
}
|
||||
|
||||
.DocSearch-Feedback-Panel-Close svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.DocSearch-Feedback-Panel-Reasons {
|
||||
|
|
@ -280,8 +277,9 @@
|
|||
.DocSearch-Feedback-Panel-Reason {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
gap: 0.375rem;
|
||||
padding-block: 0.375rem;
|
||||
padding-inline: 0.75rem;
|
||||
border-radius: var(--docsearch-border-radius);
|
||||
border: 1px solid var(--docsearch-subtle-color);
|
||||
background: var(--docsearch-hit-background);
|
||||
|
|
@ -293,18 +291,13 @@
|
|||
border-color 0.2s ease,
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease;
|
||||
}
|
||||
|
||||
.DocSearch-Feedback-Panel-Reason:hover {
|
||||
border-color: var(--docsearch-highlight-color);
|
||||
background: var(--docsearch-hit-highlight-color);
|
||||
}
|
||||
|
||||
.DocSearch-Feedback-Panel-Reason--selected {
|
||||
border-color: var(--docsearch-highlight-color);
|
||||
background: var(--docsearch-soft-primary-color);
|
||||
color: var(--docsearch-highlight-color);
|
||||
font-weight: 500;
|
||||
&:hover,
|
||||
&[aria-pressed='true'] {
|
||||
border-color: var(--docsearch-highlight-color);
|
||||
background: var(--docsearch-hit-highlight-color);
|
||||
color: var(--docsearch-highlight-color);
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-Feedback-Panel-Reason svg {
|
||||
|
|
@ -365,8 +358,8 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 36px;
|
||||
padding: 6px 15px;
|
||||
padding-block: 0.5em;
|
||||
padding-inline: 1em;
|
||||
border: 0;
|
||||
border-radius: var(--docsearch-border-radius);
|
||||
font-family: inherit;
|
||||
|
|
@ -502,9 +495,9 @@
|
|||
padding-inline: 1em;
|
||||
padding-block: 1em;
|
||||
gap: 8px;
|
||||
background-color: var(--docsearch-background-color);
|
||||
border-radius: var(--docsearch-modal-radius);
|
||||
color: var(--docsearch-text-color);
|
||||
background-color: var(--docsearch-error-soft-color);
|
||||
border-radius: var(--docsearch-border-radius);
|
||||
color: var(--docsearch-error-text-color);
|
||||
font-size: 1em;
|
||||
font-weight: 400;
|
||||
flex-direction: row;
|
||||
|
|
@ -525,6 +518,7 @@
|
|||
|
||||
.DocSearch-AskAiScreen-Error p {
|
||||
margin: 0;
|
||||
color: var(--docsearch-error-text-color);
|
||||
}
|
||||
|
||||
.DocSearch-AskAiScreen-Error-Content {
|
||||
|
|
@ -543,7 +537,7 @@
|
|||
margin: 12px 0 8px;
|
||||
color: var(--docsearch-text-color);
|
||||
font-size: 12px;
|
||||
border: 1px solid #febdc5;
|
||||
border: 1px solid var(--docsearch-error-soft-color);
|
||||
animation: slide-down 0.3s ease-out;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -679,7 +673,8 @@
|
|||
}
|
||||
|
||||
.DocSearch-Markdown-Content p {
|
||||
margin: 1em 0;
|
||||
margin-block: 1em;
|
||||
margin-inline: 0;
|
||||
}
|
||||
|
||||
.DocSearch-Markdown-Content p:last-child {
|
||||
|
|
@ -691,11 +686,12 @@
|
|||
}
|
||||
|
||||
.DocSearch-Markdown-Content code {
|
||||
background-color: var(--docsearch-key-background);
|
||||
background-color: var(--docsearch-code-block-background);
|
||||
color: var(--docsearch-text-color);
|
||||
padding: 0.2em 0;
|
||||
padding-block: 0.2em;
|
||||
padding-inline: 0;
|
||||
margin: 0;
|
||||
border-radius: 3px;
|
||||
border-radius: var(--docsearch-border-radius);
|
||||
font-family:
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
|
|
@ -704,17 +700,19 @@
|
|||
Consolas,
|
||||
'Liberation Mono',
|
||||
monospace;
|
||||
font-size: 0.9em;
|
||||
font-size: 0.9rem;
|
||||
letter-spacing: normal;
|
||||
}
|
||||
|
||||
.DocSearch-Markdown-Content pre {
|
||||
background-color: var(--docsearch-key-background);
|
||||
background-color: var(--docsearch-code-block-background);
|
||||
color: var(--docsearch-text-color);
|
||||
padding: 1.2em;
|
||||
border-radius: 6px;
|
||||
padding-block: 1.2em;
|
||||
padding-inline: 1.2em;
|
||||
border-radius: var(--docsearch-border-radius);
|
||||
overflow-x: auto;
|
||||
margin: 1.5em 0;
|
||||
margin-block: 1.5em;
|
||||
margin-inline: 0;
|
||||
}
|
||||
|
||||
.DocSearch-Markdown-Content pre code {
|
||||
|
|
@ -722,7 +720,6 @@
|
|||
color: inherit;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 0.8em;
|
||||
border-radius: 0;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
|
|
@ -1059,7 +1056,8 @@
|
|||
|
||||
.DocSearch-AskAiScreen-Sources {
|
||||
padding-inline: 0;
|
||||
margin-top: 0.5rem;
|
||||
margin-block: 0;
|
||||
margin-block-start: 0.5rem;
|
||||
color: var(--docsearch-text-color);
|
||||
list-style-type: none;
|
||||
display: flex;
|
||||
|
|
@ -1100,11 +1098,12 @@
|
|||
position: absolute;
|
||||
inset-block-start: 8px;
|
||||
inset-inline-end: 8px;
|
||||
background: var(--docsearch-key-background);
|
||||
background: var(--docsearch-code-block-background);
|
||||
color: var(--docsearch-text-color);
|
||||
border: 0;
|
||||
padding: 0.2em 0.6em;
|
||||
font-size: 0.75em;
|
||||
padding-block: 0.2em;
|
||||
padding-inline: 0.6em;
|
||||
font-size: 0.95em;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.2s ease;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
--docsearch-subtle-color: rgb(214 214 231);
|
||||
--docsearch-text-color: rgb(35 38 59 / 100%);
|
||||
--docsearch-error-color: #ef5350;
|
||||
--docsearch-error-soft-color: rgb(254 189 197);
|
||||
--docsearch-error-text-color: rgb(122 28 43);
|
||||
--docsearch-success-color: #e8f5e9;
|
||||
--docsearch-secondary-text-color: rgb(90 94 154 / 100%);
|
||||
--docsearch-background-color: rgb(245 245 250);
|
||||
|
|
@ -88,6 +90,9 @@
|
|||
0 0 0 1px rgb(33 36 61 / 5%),
|
||||
0 4px 8px -2px rgb(33 36 61 / 25%);
|
||||
--docsearch-popover-arrow-color: rgb(230 230 240);
|
||||
|
||||
/* markdown */
|
||||
--docsearch-code-block-background: rgb(235 235 240);
|
||||
}
|
||||
|
||||
/* Darkmode */
|
||||
|
|
@ -143,4 +148,7 @@
|
|||
0 0 0 1px rgb(255 255 255 / 8%),
|
||||
0 4px 8px -2px rgb(0 0 0 / 55%);
|
||||
--docsearch-popover-arrow-color: var(--docsearch-hit-focus-background);
|
||||
|
||||
/* markdown */
|
||||
--docsearch-code-block-background: rgb(33 33 57);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -717,6 +717,14 @@
|
|||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.DocSearch-Sidepanel .DocSearch-Markdown-Content code {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.DocSearch-Sidepanel .DocSearch-CodeSnippet pre {
|
||||
padding-block-start: 2.4em;
|
||||
}
|
||||
|
||||
.DocSearch-Sidepanel .DocSearch-AskAiScreen-MessageContent-Tool-Query:hover {
|
||||
cursor: unset;
|
||||
box-shadow: none;
|
||||
|
|
|
|||
|
|
@ -241,9 +241,7 @@ function AskAiExchangeCard({
|
|||
<div className="DocSearch-AskAiScreen-Response-Container">
|
||||
<div className="DocSearch-AskAiScreen-Response">
|
||||
<div className="DocSearch-AskAiScreen-Message DocSearch-AskAiScreen-Message--user">
|
||||
<p className="DocSearch-AskAiScreen-Query">
|
||||
{userContent?.text ?? ''}
|
||||
</p>
|
||||
<p className="DocSearch-AskAiScreen-Query">{userContent ?? ''}</p>
|
||||
</div>
|
||||
<div className="DocSearch-AskAiScreen-Message DocSearch-AskAiScreen-Message--assistant">
|
||||
<div className="DocSearch-AskAiScreen-MessageContent">
|
||||
|
|
@ -361,7 +359,7 @@ function AskAiExchangeCard({
|
|||
<FeedbackActions
|
||||
id={messageId}
|
||||
showActions={showActions}
|
||||
latestAssistantMessageContent={assistantContent?.text || null}
|
||||
latestAssistantMessageContent={assistantContent || null}
|
||||
translations={translations}
|
||||
conversations={conversations}
|
||||
onFeedback={onFeedback}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export type ConversationScreenTranslations = Partial<
|
|||
feedbackTagSafetyOrLegal: string;
|
||||
feedbackTagOther: string;
|
||||
/** Error title shown if there is an error while chatting. */
|
||||
errorTitleText;
|
||||
errorTitleText: string;
|
||||
suggestedPromptsTitleText: string;
|
||||
}
|
||||
>;
|
||||
|
|
@ -171,9 +171,7 @@ const ConversationExchange = React.forwardRef<
|
|||
>
|
||||
<div className="DocSearch-AskAiScreen-Response">
|
||||
<div className="DocSearch-AskAiScreen-Message DocSearch-AskAiScreen-Message--user">
|
||||
<p className="DocSearch-AskAiScreen-Query">
|
||||
{userContent?.text ?? ''}
|
||||
</p>
|
||||
<p className="DocSearch-AskAiScreen-Query">{userContent ?? ''}</p>
|
||||
</div>
|
||||
<div className="DocSearch-AskAiScreen-Message DocSearch-AskAiScreen-Message--assistant">
|
||||
<div className="DocSearch-AskAiScreen-MessageContent">
|
||||
|
|
@ -288,7 +286,7 @@ const ConversationExchange = React.forwardRef<
|
|||
isSidepanel={true}
|
||||
id={messageId}
|
||||
showActions={showActions}
|
||||
latestAssistantMessageContent={assistantContent?.text || null}
|
||||
latestAssistantMessageContent={assistantContent || null}
|
||||
translations={translations}
|
||||
conversations={conversations}
|
||||
onFeedback={onFeedback}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { render } from '@testing-library/react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import type { UIMessage } from 'ai';
|
||||
import React from 'react';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
|
||||
import { AskAiScreen } from '../AskAiScreen';
|
||||
|
|
@ -17,6 +17,7 @@ const baseProps = {
|
|||
setQuery: (): void => {},
|
||||
messages: [],
|
||||
status: 'ready' as const,
|
||||
tools: {},
|
||||
disableUserPersonalization: false,
|
||||
resultsFooterComponent: null,
|
||||
} as any;
|
||||
|
|
@ -47,4 +48,46 @@ describe('AskAiScreen', () => {
|
|||
|
||||
expect(getByText('oh no')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('copies all assistant text parts around tool calls', () => {
|
||||
const writeText = vi.fn();
|
||||
Object.defineProperty(navigator, 'clipboard', {
|
||||
configurable: true,
|
||||
value: { writeText },
|
||||
});
|
||||
const messages: UIMessage[] = [
|
||||
{
|
||||
id: 'user-1',
|
||||
role: 'user',
|
||||
parts: [{ type: 'text', text: 'What is Docusaurus?' }],
|
||||
},
|
||||
{
|
||||
id: 'assistant-1',
|
||||
role: 'assistant',
|
||||
parts: [
|
||||
{ type: 'text', text: 'Let me look that up.', state: 'done' },
|
||||
{
|
||||
type: 'tool-algolia_search_index',
|
||||
toolCallId: 'tool-1',
|
||||
state: 'output-available',
|
||||
input: { query: 'Docusaurus', index: 'docs' },
|
||||
output: { hits: [] },
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Docusaurus is a static site generator.',
|
||||
state: 'done',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
render(<AskAiScreen {...baseProps} messages={messages} />);
|
||||
|
||||
fireEvent.click(screen.getByTitle('Copy'));
|
||||
|
||||
expect(writeText).toHaveBeenCalledWith(
|
||||
'Let me look that up.\n\nDocusaurus is a static site generator.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ export function FeedbackActions({
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="DocSearch-AskAiScreen-Actions">
|
||||
<>
|
||||
<div className="DocSearch-AskAiScreen-Actions-Controls">
|
||||
{isSidepanel ? copyButton : null}
|
||||
|
||||
|
|
@ -259,7 +259,7 @@ export function FeedbackActions({
|
|||
onClose={handleCloseNote}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -286,6 +286,7 @@ function NegativeFeedbackPanel({
|
|||
onSubmit,
|
||||
onClose,
|
||||
}: NegativeFeedbackPanelProps): JSX.Element {
|
||||
const feedbackTitleId = React.useId();
|
||||
const {
|
||||
feedbackPanelTitle = 'What went wrong? (optional)',
|
||||
feedbackDetailsPlaceholder = 'Share some details...',
|
||||
|
|
@ -297,10 +298,12 @@ function NegativeFeedbackPanel({
|
|||
<div
|
||||
className="DocSearch-Feedback-Panel"
|
||||
role="group"
|
||||
aria-label={feedbackPanelTitle}
|
||||
aria-labelledby={feedbackTitleId}
|
||||
>
|
||||
<div className="DocSearch-Feedback-Panel-Header">
|
||||
<p className="DocSearch-Feedback-Panel-Title">{feedbackPanelTitle}</p>
|
||||
<p className="DocSearch-Feedback-Panel-Title" id={feedbackTitleId}>
|
||||
{feedbackPanelTitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="DocSearch-Feedback-Panel-Reasons">
|
||||
|
|
@ -310,9 +313,7 @@ function NegativeFeedbackPanel({
|
|||
<button
|
||||
key={reason.value}
|
||||
type="button"
|
||||
className={`DocSearch-Feedback-Panel-Reason${
|
||||
isSelected ? ' DocSearch-Feedback-Panel-Reason--selected' : ''
|
||||
}`}
|
||||
className="DocSearch-Feedback-Panel-Reason"
|
||||
aria-pressed={isSelected}
|
||||
onClick={() => onToggleTag(reason.value)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -156,9 +156,20 @@ describe('FeedbackActions', () => {
|
|||
const { onFeedback } = renderComponent();
|
||||
|
||||
fireEvent.click(screen.getByTitle('Dislike'));
|
||||
const tag = screen.getByText('Incorrect or incomplete');
|
||||
const tag = screen.getByRole('button', {
|
||||
name: 'Incorrect or incomplete',
|
||||
});
|
||||
|
||||
expect(tag).toHaveAttribute('aria-pressed', 'false');
|
||||
|
||||
fireEvent.click(tag);
|
||||
|
||||
expect(tag).toHaveAttribute('aria-pressed', 'true');
|
||||
|
||||
fireEvent.click(tag);
|
||||
|
||||
expect(tag).toHaveAttribute('aria-pressed', 'false');
|
||||
|
||||
fireEvent.click(screen.getByText('Submit'));
|
||||
|
||||
await waitFor(() =>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Popover as PopoverPrimitive } from '@base-ui/react/popover';
|
||||
import React from 'react';
|
||||
import type { JSX } from 'react';
|
||||
|
||||
import { CloseIcon } from '../../icons';
|
||||
|
||||
|
|
@ -7,13 +8,13 @@ export function Popover({ ...props }: PopoverPrimitive.Root.Props) {
|
|||
return <PopoverPrimitive.Root {...props} />;
|
||||
}
|
||||
|
||||
function PopoverTrigger({
|
||||
className,
|
||||
...props
|
||||
}: PopoverPrimitive.Trigger.Props) {
|
||||
const PopoverTrigger = React.forwardRef<
|
||||
HTMLButtonElement,
|
||||
PopoverPrimitive.Trigger.Props
|
||||
>(({ className, ...props }, ref): JSX.Element => {
|
||||
const cn = `DocSearch-Popover-Trigger${className ? ` ${className}` : ''}`;
|
||||
return <PopoverPrimitive.Trigger className={cn} {...props} />;
|
||||
}
|
||||
return <PopoverPrimitive.Trigger className={cn} ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
Popover.Trigger = PopoverTrigger;
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,6 @@ export const useAskAi: UseAskAi = ({
|
|||
assistantId,
|
||||
apiKey,
|
||||
appId,
|
||||
indexName,
|
||||
tools = EMPTY_TOOLS,
|
||||
searchParameters,
|
||||
memory,
|
||||
|
|
@ -218,7 +217,7 @@ export const useAskAi: UseAskAi = ({
|
|||
|
||||
const conversations = useRef(
|
||||
createStoredConversations<StoredAskAiState>({
|
||||
key: `__DOCSEARCH_ASKAI_CONVERSATIONS__${indexName}`,
|
||||
key: `__DOCSEARCH_ASKAI_CONVERSATIONS__${appId}`,
|
||||
limit: 10,
|
||||
})
|
||||
).current;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
isAIToolPart,
|
||||
isAlgoliaMCPSearchOutputPart,
|
||||
sanitizeMessagesForRequest,
|
||||
getMessageContent,
|
||||
} from '../ai';
|
||||
|
||||
function message(id: string, parts: AIMessagePart[]): AIMessage {
|
||||
|
|
@ -247,3 +248,50 @@ describe('getSearchToolQueries', () => {
|
|||
expect(getSearchToolQueries(part)).toEqual(['foo']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMessageContent', () => {
|
||||
it('joins the text parts around tool calls so copying returns the full answer', () => {
|
||||
expect(
|
||||
getMessageContent(
|
||||
message('a1', [
|
||||
{ type: 'text', text: 'Let me look that up.', state: 'done' },
|
||||
{
|
||||
type: 'tool-algolia_search_index',
|
||||
toolCallId: 't1',
|
||||
state: 'output-available',
|
||||
input: {
|
||||
query: 'foo',
|
||||
index: 'bar',
|
||||
},
|
||||
output: { hits: [] },
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Docusaurus is a static site generator.',
|
||||
state: 'done',
|
||||
},
|
||||
])
|
||||
)
|
||||
).toBe('Let me look that up.\n\nDocusaurus is a static site generator.');
|
||||
});
|
||||
|
||||
it('returns an empty string without a message or text parts', () => {
|
||||
expect(getMessageContent(null)).toBe('');
|
||||
expect(
|
||||
getMessageContent(
|
||||
message('a2', [
|
||||
{
|
||||
type: 'tool-algolia_search_index',
|
||||
toolCallId: 't2',
|
||||
state: 'output-available',
|
||||
input: {
|
||||
query: 'foo',
|
||||
index: 'bar',
|
||||
},
|
||||
output: { hits: [] },
|
||||
},
|
||||
])
|
||||
)
|
||||
).toBe('');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -113,10 +113,14 @@ export const buildDummyAskAiHit = (
|
|||
};
|
||||
};
|
||||
|
||||
export const getMessageContent = (
|
||||
message: AIMessage | null
|
||||
): TextUIPart | undefined =>
|
||||
message?.parts.find((part) => part.type === 'text');
|
||||
// answers can interleave text with tool calls, so join every text part
|
||||
// instead of stopping at the first one
|
||||
// see https://github.com/algolia/docsearch/issues/2782
|
||||
export const getMessageContent = (message: AIMessage | null): string =>
|
||||
(message?.parts ?? [])
|
||||
.filter((part): part is TextUIPart => part.type === 'text')
|
||||
.map((part) => part.text)
|
||||
.join('\n\n');
|
||||
|
||||
/** Helper function to check if error is a thread depth error (AI-217). */
|
||||
export function isThreadDepthError(error?: Error): boolean {
|
||||
|
|
|
|||
Loading…
Reference in a new issue