feat: agent studio feedback integration (#2868)
Cherry-picked from v5 branch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e20d30de7b
commit
f83f5a911c
9 changed files with 171 additions and 117 deletions
|
|
@ -7,7 +7,8 @@ import './App.css';
|
||||||
import '@docsearch/css/dist/style.css';
|
import '@docsearch/css/dist/style.css';
|
||||||
import '@docsearch/css/dist/sidepanel.css';
|
import '@docsearch/css/dist/sidepanel.css';
|
||||||
|
|
||||||
import { AgentStudioExample } from './examples/agent-studio';
|
import AgentStudio from './examples/agent-studio';
|
||||||
|
import AgentStudioSidepanel from './examples/agent-studio-sidepanel';
|
||||||
import Basic from './examples/basic';
|
import Basic from './examples/basic';
|
||||||
import BasicAskAI from './examples/basic-askai';
|
import BasicAskAI from './examples/basic-askai';
|
||||||
import Composable from './examples/composable';
|
import Composable from './examples/composable';
|
||||||
|
|
@ -102,7 +103,14 @@ function App(): JSX.Element {
|
||||||
<section className="demo-section">
|
<section className="demo-section">
|
||||||
<p className="section-description">Agent Studio</p>
|
<p className="section-description">Agent Studio</p>
|
||||||
<div className="search-wrapper column">
|
<div className="search-wrapper column">
|
||||||
<AgentStudioExample />
|
<AgentStudio />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="demo-section">
|
||||||
|
<p className="section-description">Agent Studio sidepanel</p>
|
||||||
|
<div className="search-wrapper column">
|
||||||
|
<AgentStudioSidepanel />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
||||||
25
examples/demo-react/src/examples/agent-studio-sidepanel.tsx
Normal file
25
examples/demo-react/src/examples/agent-studio-sidepanel.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* eslint-disable react/react-in-jsx-scope */
|
||||||
|
import { DocSearch } from '@docsearch/core';
|
||||||
|
import { SidepanelButton } from '@docsearch/sidepanel/button';
|
||||||
|
import { Sidepanel } from '@docsearch/sidepanel/sidepanel';
|
||||||
|
import type { JSX } from 'react';
|
||||||
|
|
||||||
|
export default function AgentStudioSidepanel(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<DocSearch>
|
||||||
|
<SidepanelButton
|
||||||
|
variant="inline"
|
||||||
|
translations={{
|
||||||
|
buttonText: 'Agent Studio',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Sidepanel
|
||||||
|
indexName="docsearch-markdown"
|
||||||
|
appId="PMZUYBQDAK"
|
||||||
|
apiKey="a00716d83c64f6c61905c078b7d5ab66"
|
||||||
|
assistantId="ccdec697-e3fe-465b-a1c3-657e7bf18aef"
|
||||||
|
agentStudio={true}
|
||||||
|
/>
|
||||||
|
</DocSearch>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -2,45 +2,25 @@
|
||||||
import { DocSearch } from '@docsearch/core';
|
import { DocSearch } from '@docsearch/core';
|
||||||
import { DocSearchButton } from '@docsearch/modal/button';
|
import { DocSearchButton } from '@docsearch/modal/button';
|
||||||
import { DocSearchModal } from '@docsearch/modal/modal';
|
import { DocSearchModal } from '@docsearch/modal/modal';
|
||||||
import { SidepanelButton } from '@docsearch/sidepanel/button';
|
|
||||||
import { Sidepanel } from '@docsearch/sidepanel/sidepanel';
|
|
||||||
import type { JSX } from 'react';
|
import type { JSX } from 'react';
|
||||||
|
|
||||||
export function AgentStudioExample(): JSX.Element {
|
export default function AgentStudio(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<DocSearch>
|
||||||
<DocSearch>
|
<DocSearchButton
|
||||||
<DocSearchButton
|
translations={{
|
||||||
translations={{
|
buttonText: 'Ask AI with Agent Studio',
|
||||||
buttonText: 'Ask AI with Agent Studio',
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
<DocSearchModal
|
||||||
<DocSearchModal
|
indexName="docsearch"
|
||||||
indexName="docsearch"
|
appId="PMZUYBQDAK"
|
||||||
appId="PMZUYBQDAK"
|
apiKey="a00716d83c64f6c61905c078b7d5ab66"
|
||||||
apiKey="a00716d83c64f6c61905c078b7d5ab66"
|
askAi={{
|
||||||
askAi={{
|
assistantId: 'ccdec697-e3fe-465b-a1c3-657e7bf18aef',
|
||||||
assistantId: 'ccdec697-e3fe-465b-a1c3-657e7bf18aef',
|
agentStudio: true,
|
||||||
agentStudio: true,
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</DocSearch>
|
||||||
</DocSearch>
|
|
||||||
|
|
||||||
<DocSearch>
|
|
||||||
<SidepanelButton
|
|
||||||
variant="inline"
|
|
||||||
translations={{
|
|
||||||
buttonText: 'Agent Studio',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Sidepanel
|
|
||||||
indexName="docsearch-markdown"
|
|
||||||
appId="PMZUYBQDAK"
|
|
||||||
apiKey="a00716d83c64f6c61905c078b7d5ab66"
|
|
||||||
assistantId="ccdec697-e3fe-465b-a1c3-657e7bf18aef"
|
|
||||||
agentStudio={true}
|
|
||||||
/>
|
|
||||||
</DocSearch>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,8 @@ function AskAiExchangeCard({
|
||||||
isLastExchange &&
|
isLastExchange &&
|
||||||
!displayParts.some((part) => part.type !== 'step-start');
|
!displayParts.some((part) => part.type !== 'step-start');
|
||||||
|
|
||||||
|
const messageId = agentStudio ? assistantMessage?.id || exchange.id : userMessage?.id || exchange.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="DocSearch-AskAiScreen-Response-Container">
|
<div className="DocSearch-AskAiScreen-Response-Container">
|
||||||
<div className="DocSearch-AskAiScreen-Response">
|
<div className="DocSearch-AskAiScreen-Response">
|
||||||
|
|
@ -236,12 +238,11 @@ function AskAiExchangeCard({
|
||||||
</div>
|
</div>
|
||||||
<div className="DocSearch-AskAiScreen-Answer-Footer">
|
<div className="DocSearch-AskAiScreen-Answer-Footer">
|
||||||
<AskAiScreenFooterActions
|
<AskAiScreenFooterActions
|
||||||
id={userMessage?.id || exchange.id}
|
id={messageId}
|
||||||
showActions={showActions}
|
showActions={showActions}
|
||||||
latestAssistantMessageContent={assistantContent?.text || null}
|
latestAssistantMessageContent={assistantContent?.text || null}
|
||||||
translations={translations}
|
translations={translations}
|
||||||
conversations={conversations}
|
conversations={conversations}
|
||||||
agentStudio={agentStudio}
|
|
||||||
onFeedback={onFeedback}
|
onFeedback={onFeedback}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -262,7 +263,6 @@ interface AskAiScreenFooterActionsProps {
|
||||||
translations: AskAiScreenTranslations;
|
translations: AskAiScreenTranslations;
|
||||||
conversations: StoredSearchPlugin<StoredAskAiState>;
|
conversations: StoredSearchPlugin<StoredAskAiState>;
|
||||||
onFeedback?: (messageId: string, thumbs: 0 | 1) => Promise<void>;
|
onFeedback?: (messageId: string, thumbs: 0 | 1) => Promise<void>;
|
||||||
agentStudio?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AskAiScreenFooterActions({
|
export function AskAiScreenFooterActions({
|
||||||
|
|
@ -272,7 +272,6 @@ export function AskAiScreenFooterActions({
|
||||||
translations,
|
translations,
|
||||||
conversations,
|
conversations,
|
||||||
onFeedback,
|
onFeedback,
|
||||||
agentStudio,
|
|
||||||
}: AskAiScreenFooterActionsProps): JSX.Element | null {
|
}: AskAiScreenFooterActionsProps): JSX.Element | null {
|
||||||
// local state for feedback, initialised from stored conversations
|
// local state for feedback, initialised from stored conversations
|
||||||
const initialFeedback = React.useMemo(() => {
|
const initialFeedback = React.useMemo(() => {
|
||||||
|
|
@ -310,26 +309,25 @@ export function AskAiScreenFooterActions({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="DocSearch-AskAiScreen-Actions">
|
<div className="DocSearch-AskAiScreen-Actions">
|
||||||
{!agentStudio &&
|
{feedback === null ? (
|
||||||
(feedback === null ? (
|
<>
|
||||||
<>
|
{saving ? (
|
||||||
{saving ? (
|
<LoadingIcon className="DocSearch-AskAiScreen-SmallerLoadingIcon" />
|
||||||
<LoadingIcon className="DocSearch-AskAiScreen-SmallerLoadingIcon" />
|
) : (
|
||||||
) : (
|
<>
|
||||||
<>
|
<LikeButton title={likeButtonTitle} onClick={() => handleFeedback('like')} />
|
||||||
<LikeButton title={likeButtonTitle} onClick={() => handleFeedback('like')} />
|
<DislikeButton title={dislikeButtonTitle} onClick={() => handleFeedback('dislike')} />
|
||||||
<DislikeButton title={dislikeButtonTitle} onClick={() => handleFeedback('dislike')} />
|
</>
|
||||||
</>
|
)}
|
||||||
)}
|
{savingError && (
|
||||||
{savingError && (
|
<p className="DocSearch-AskAiScreen-FeedbackText">{savingError.message || 'An error occured'}</p>
|
||||||
<p className="DocSearch-AskAiScreen-FeedbackText">{savingError.message || 'An error occured'}</p>
|
)}
|
||||||
)}
|
</>
|
||||||
</>
|
) : (
|
||||||
) : (
|
<p className="DocSearch-AskAiScreen-FeedbackText DocSearch-AskAiScreen-FeedbackText--visible">
|
||||||
<p className="DocSearch-AskAiScreen-FeedbackText DocSearch-AskAiScreen-FeedbackText--visible">
|
{thanksForFeedbackText}
|
||||||
{thanksForFeedbackText}
|
</p>
|
||||||
</p>
|
)}
|
||||||
))}
|
|
||||||
<CopyButton
|
<CopyButton
|
||||||
translations={translations}
|
translations={translations}
|
||||||
onClick={() => navigator.clipboard.writeText(latestAssistantMessageContent)}
|
onClick={() => navigator.clipboard.writeText(latestAssistantMessageContent)}
|
||||||
|
|
@ -373,7 +371,7 @@ export function AskAiScreen({ translations = {}, ...props }: AskAiScreenProps):
|
||||||
startNewConversationButtonText = 'Start a new conversation',
|
startNewConversationButtonText = 'Start a new conversation',
|
||||||
} = translations;
|
} = translations;
|
||||||
|
|
||||||
const { messages, askAiError, status } = props;
|
const { messages, askAiError, status, agentStudio } = props;
|
||||||
|
|
||||||
// Check if there's a thread depth error
|
// Check if there's a thread depth error
|
||||||
const hasThreadDepthError = useMemo(() => {
|
const hasThreadDepthError = useMemo(() => {
|
||||||
|
|
@ -448,7 +446,7 @@ export function AskAiScreen({ translations = {}, ...props }: AskAiScreenProps):
|
||||||
loadingStatus={props.status}
|
loadingStatus={props.status}
|
||||||
translations={translations}
|
translations={translations}
|
||||||
conversations={props.conversations}
|
conversations={props.conversations}
|
||||||
agentStudio={props.agentStudio}
|
agentStudio={agentStudio}
|
||||||
onSearchQueryClick={handleSearchQueryClick}
|
onSearchQueryClick={handleSearchQueryClick}
|
||||||
onFeedback={props.onFeedback}
|
onFeedback={props.onFeedback}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,12 @@ import type { ScreenStateTranslations } from './ScreenState';
|
||||||
import { ScreenState } from './ScreenState';
|
import { ScreenState } from './ScreenState';
|
||||||
import type { SearchBoxTranslations } from './SearchBox';
|
import type { SearchBoxTranslations } from './SearchBox';
|
||||||
import { SearchBox } from './SearchBox';
|
import { SearchBox } from './SearchBox';
|
||||||
import { createStoredConversations, createStoredSearches } from './stored-searches';
|
import { createStoredSearches } from './stored-searches';
|
||||||
import type {
|
import type {
|
||||||
DocSearchHit,
|
DocSearchHit,
|
||||||
DocSearchState,
|
DocSearchState,
|
||||||
InternalDocSearchHit,
|
InternalDocSearchHit,
|
||||||
StoredAskAiMessage,
|
StoredAskAiMessage,
|
||||||
StoredAskAiState,
|
|
||||||
StoredDocSearchHit,
|
StoredDocSearchHit,
|
||||||
SuggestedQuestionHit,
|
SuggestedQuestionHit,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
@ -384,12 +383,6 @@ export function DocSearchModal({
|
||||||
const defaultIndexName = indexes[0].name;
|
const defaultIndexName = indexes[0].name;
|
||||||
|
|
||||||
// storage
|
// storage
|
||||||
const conversations = React.useRef(
|
|
||||||
createStoredConversations<StoredAskAiState>({
|
|
||||||
key: `__DOCSEARCH_ASKAI_CONVERSATIONS__${askAiConfig?.indexName || defaultIndexName}`,
|
|
||||||
limit: 10,
|
|
||||||
}),
|
|
||||||
).current;
|
|
||||||
const favoriteSearches = React.useRef(
|
const favoriteSearches = React.useRef(
|
||||||
createStoredSearches<StoredDocSearchHit>({
|
createStoredSearches<StoredDocSearchHit>({
|
||||||
key: `__DOCSEARCH_FAVORITE_SEARCHES__${defaultIndexName}`,
|
key: `__DOCSEARCH_FAVORITE_SEARCHES__${defaultIndexName}`,
|
||||||
|
|
@ -405,15 +398,16 @@ export function DocSearchModal({
|
||||||
|
|
||||||
const [stoppedStream, setStoppedStream] = React.useState(false);
|
const [stoppedStream, setStoppedStream] = React.useState(false);
|
||||||
|
|
||||||
const { messages, status, setMessages, sendMessage, stopAskAiStreaming, askAiError, sendFeedback } = useAskAi({
|
const { messages, status, setMessages, sendMessage, stopAskAiStreaming, askAiError, sendFeedback, conversations } =
|
||||||
assistantId: askAiConfigurationId,
|
useAskAi({
|
||||||
apiKey: askAiConfig?.apiKey || apiKey,
|
assistantId: askAiConfigurationId,
|
||||||
appId: askAiConfig?.appId || appId,
|
apiKey: askAiConfig?.apiKey || apiKey,
|
||||||
indexName: askAiConfig?.indexName || defaultIndexName,
|
appId: askAiConfig?.appId || appId,
|
||||||
searchParameters: askAiSearchParameters,
|
indexName: askAiConfig?.indexName || defaultIndexName,
|
||||||
useStagingEnv: askAiUseStagingEnv,
|
searchParameters: askAiSearchParameters,
|
||||||
agentStudio,
|
useStagingEnv: askAiUseStagingEnv,
|
||||||
});
|
agentStudio,
|
||||||
|
});
|
||||||
|
|
||||||
const prevStatus = React.useRef(status);
|
const prevStatus = React.useRef(status);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ interface ConversationActionsProps {
|
||||||
conversations: StoredSearchPlugin<StoredAskAiState>;
|
conversations: StoredSearchPlugin<StoredAskAiState>;
|
||||||
onFeedback?: (messageId: string, thumbs: 0 | 1) => Promise<void>;
|
onFeedback?: (messageId: string, thumbs: 0 | 1) => Promise<void>;
|
||||||
isSidepanel?: boolean;
|
isSidepanel?: boolean;
|
||||||
agentStudio?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ConversationActions({
|
export function ConversationActions({
|
||||||
|
|
@ -24,7 +23,6 @@ export function ConversationActions({
|
||||||
onFeedback,
|
onFeedback,
|
||||||
latestAssistantMessageContent,
|
latestAssistantMessageContent,
|
||||||
showActions,
|
showActions,
|
||||||
agentStudio,
|
|
||||||
}: ConversationActionsProps): JSX.Element | null {
|
}: ConversationActionsProps): JSX.Element | null {
|
||||||
const initialFeedback = React.useMemo(() => {
|
const initialFeedback = React.useMemo(() => {
|
||||||
const message = conversations.getOne?.(id);
|
const message = conversations.getOne?.(id);
|
||||||
|
|
@ -65,26 +63,25 @@ export function ConversationActions({
|
||||||
translations={translations}
|
translations={translations}
|
||||||
onClick={() => navigator.clipboard.writeText(latestAssistantMessageContent)}
|
onClick={() => navigator.clipboard.writeText(latestAssistantMessageContent)}
|
||||||
/>
|
/>
|
||||||
{!agentStudio &&
|
{feedback === null ? (
|
||||||
(feedback === null ? (
|
<>
|
||||||
<>
|
{saving ? (
|
||||||
{saving ? (
|
<LoadingIcon className="DocSearch-AskAiScreen-SmallerLoadingIcon" />
|
||||||
<LoadingIcon className="DocSearch-AskAiScreen-SmallerLoadingIcon" />
|
) : (
|
||||||
) : (
|
<>
|
||||||
<>
|
<LikeButton title={likeButtonTitle} onClick={() => handleFeedback('like')} />
|
||||||
<LikeButton title={likeButtonTitle} onClick={() => handleFeedback('like')} />
|
<DislikeButton title={dislikeButtonTitle} onClick={() => handleFeedback('dislike')} />
|
||||||
<DislikeButton title={dislikeButtonTitle} onClick={() => handleFeedback('dislike')} />
|
</>
|
||||||
</>
|
)}
|
||||||
)}
|
{savingError && (
|
||||||
{savingError && (
|
<p className="DocSearch-AskAiScreen-FeedbackText">{savingError.message || 'An error occured'}</p>
|
||||||
<p className="DocSearch-AskAiScreen-FeedbackText">{savingError.message || 'An error occured'}</p>
|
)}
|
||||||
)}
|
</>
|
||||||
</>
|
) : (
|
||||||
) : (
|
<p className="DocSearch-AskAiScreen-FeedbackText DocSearch-AskAiScreen-FeedbackText--visible">
|
||||||
<p className="DocSearch-AskAiScreen-FeedbackText DocSearch-AskAiScreen-FeedbackText--visible">
|
{thanksForFeedbackText}
|
||||||
{thanksForFeedbackText}
|
</p>
|
||||||
</p>
|
)}
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,8 @@ const ConversationExchange = React.forwardRef<HTMLDivElement, ConversationnExcha
|
||||||
const showActions =
|
const showActions =
|
||||||
!wasStopped && (!isLastExchange || (isLastExchange && status === 'ready' && Boolean(assistantMessage)));
|
!wasStopped && (!isLastExchange || (isLastExchange && status === 'ready' && Boolean(assistantMessage)));
|
||||||
|
|
||||||
|
const messageId = agentStudio ? assistantMessage?.id || exchange.id : userMessage?.id || exchange.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="DocSearch-AskAiScreen-Response-Container" ref={conversationRef}>
|
<div className="DocSearch-AskAiScreen-Response-Container" ref={conversationRef}>
|
||||||
<div className="DocSearch-AskAiScreen-Response">
|
<div className="DocSearch-AskAiScreen-Response">
|
||||||
|
|
@ -212,12 +214,11 @@ const ConversationExchange = React.forwardRef<HTMLDivElement, ConversationnExcha
|
||||||
|
|
||||||
<div className="DocSearch-AskAiScreen-Answer-Footer">
|
<div className="DocSearch-AskAiScreen-Answer-Footer">
|
||||||
<ConversationActions
|
<ConversationActions
|
||||||
id={userMessage?.id || exchange.id}
|
id={messageId}
|
||||||
showActions={showActions}
|
showActions={showActions}
|
||||||
latestAssistantMessageContent={assistantContent?.text || null}
|
latestAssistantMessageContent={assistantContent?.text || null}
|
||||||
translations={translations}
|
translations={translations}
|
||||||
conversations={conversations}
|
conversations={conversations}
|
||||||
agentStudio={agentStudio}
|
|
||||||
onFeedback={onFeedback}
|
onFeedback={onFeedback}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import { ASK_AI_API_URL, BETA_ASK_AI_API_URL } from './constants';
|
||||||
// ... existing imports ...
|
// ... existing imports ...
|
||||||
const TOKEN_KEY = 'askai_token';
|
const TOKEN_KEY = 'askai_token';
|
||||||
|
|
||||||
|
export const agentStudioBaseUrl = (appId: string): string => `https://${appId}.algolia.net/agent-studio/1`;
|
||||||
|
|
||||||
type TokenPayload = { exp: number };
|
type TokenPayload = { exp: number };
|
||||||
|
|
||||||
const decode = (token: string): TokenPayload => {
|
const decode = (token: string): TokenPayload => {
|
||||||
|
|
@ -134,3 +136,37 @@ export const getAgentStudioErrorMessage = (error: Error): Error => {
|
||||||
|
|
||||||
return new Error(errorMessage);
|
return new Error(errorMessage);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const postAgentStudioFeedback = ({
|
||||||
|
agentId,
|
||||||
|
vote,
|
||||||
|
messageId,
|
||||||
|
appId,
|
||||||
|
apiKey,
|
||||||
|
abortSignal,
|
||||||
|
}: {
|
||||||
|
agentId: string;
|
||||||
|
vote: 0 | 1;
|
||||||
|
messageId: string;
|
||||||
|
appId: string;
|
||||||
|
apiKey: string;
|
||||||
|
abortSignal: AbortSignal;
|
||||||
|
}): Promise<Response> => {
|
||||||
|
const headers = new Headers();
|
||||||
|
headers.set('x-algolia-application-id', appId);
|
||||||
|
headers.set('x-algolia-api-key', apiKey);
|
||||||
|
headers.set('content-type', 'application/json');
|
||||||
|
|
||||||
|
const baseUrl = `${agentStudioBaseUrl(appId)}/feedback`;
|
||||||
|
|
||||||
|
return fetch(baseUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
messageId,
|
||||||
|
agentId,
|
||||||
|
vote,
|
||||||
|
}),
|
||||||
|
headers,
|
||||||
|
signal: abortSignal,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,13 @@ import { useChat } from '@ai-sdk/react';
|
||||||
import { DefaultChatTransport, lastAssistantMessageIsCompleteWithToolCalls } from 'ai';
|
import { DefaultChatTransport, lastAssistantMessageIsCompleteWithToolCalls } from 'ai';
|
||||||
import { useCallback, useMemo, useRef } from 'react';
|
import { useCallback, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
import { getAgentStudioErrorMessage, getValidToken, postFeedback } from './askai';
|
import {
|
||||||
|
agentStudioBaseUrl,
|
||||||
|
getAgentStudioErrorMessage,
|
||||||
|
getValidToken,
|
||||||
|
postAgentStudioFeedback,
|
||||||
|
postFeedback,
|
||||||
|
} from './askai';
|
||||||
import type { Exchange } from './AskAiScreen';
|
import type { Exchange } from './AskAiScreen';
|
||||||
import { ASK_AI_API_URL, BETA_ASK_AI_API_URL } from './constants';
|
import { ASK_AI_API_URL, BETA_ASK_AI_API_URL } from './constants';
|
||||||
import type { StoredSearchPlugin } from './stored-searches';
|
import type { StoredSearchPlugin } from './stored-searches';
|
||||||
|
|
@ -59,7 +65,7 @@ const getAgentStudioTransport = ({
|
||||||
searchParameters,
|
searchParameters,
|
||||||
}: AgentStudioTransportParams): DefaultChatTransport<AIMessage> => {
|
}: AgentStudioTransportParams): DefaultChatTransport<AIMessage> => {
|
||||||
return new DefaultChatTransport({
|
return new DefaultChatTransport({
|
||||||
api: `https://${appId}.algolia.net/agent-studio/1/agents/${assistantId}/completions?stream=true&compatibilityMode=ai-sdk-5`,
|
api: `${agentStudioBaseUrl(appId)}/agents/${assistantId}/completions?stream=true&compatibilityMode=ai-sdk-5`,
|
||||||
headers: {
|
headers: {
|
||||||
'x-algolia-application-id': appId,
|
'x-algolia-application-id': appId,
|
||||||
'x-algolia-api-key': apiKey,
|
'x-algolia-api-key': apiKey,
|
||||||
|
|
@ -145,19 +151,28 @@ export const useAskAi: UseAskAi = ({ assistantId, apiKey, appId, indexName, useS
|
||||||
async (messageId: string, thumbs: 0 | 1): Promise<void> => {
|
async (messageId: string, thumbs: 0 | 1): Promise<void> => {
|
||||||
if (!assistantId) return;
|
if (!assistantId) return;
|
||||||
|
|
||||||
const res = await postFeedback({
|
const res = await (params.agentStudio
|
||||||
assistantId,
|
? postAgentStudioFeedback({
|
||||||
thumbs,
|
agentId: assistantId,
|
||||||
messageId,
|
vote: thumbs,
|
||||||
appId,
|
messageId,
|
||||||
abortSignal: abortControllerRef.current.signal,
|
appId,
|
||||||
useStagingEnv,
|
apiKey,
|
||||||
});
|
abortSignal: abortControllerRef.current.signal,
|
||||||
|
})
|
||||||
|
: postFeedback({
|
||||||
|
assistantId,
|
||||||
|
thumbs,
|
||||||
|
messageId,
|
||||||
|
appId,
|
||||||
|
abortSignal: abortControllerRef.current.signal,
|
||||||
|
useStagingEnv,
|
||||||
|
}));
|
||||||
|
|
||||||
if (res.status >= 300) throw new Error('Failed, try again later.');
|
if (res.status >= 300) throw new Error('Failed, try again later.');
|
||||||
conversations.addFeedback?.(messageId, thumbs === 1 ? 'like' : 'dislike');
|
conversations.addFeedback?.(messageId, thumbs === 1 ? 'like' : 'dislike');
|
||||||
},
|
},
|
||||||
[assistantId, appId, conversations, useStagingEnv],
|
[assistantId, params.agentStudio, appId, apiKey, useStagingEnv, conversations],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onStopStreaming = async (): Promise<void> => {
|
const onStopStreaming = async (): Promise<void> => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue