Skip to content

Commit

Permalink
feat(boxai-sidebar): Add cache for Agent Selector (#3928)
Browse files Browse the repository at this point in the history
* feat(boxai-sidebar): Add cache for Agent Selector

* chore(): add missing line end

* feat(boxai-sidebar): Add cache for Agent Selector

Add missing Flow types and agents prop to mocked cache in tests. Remove unneeded type import.

---------

Co-authored-by: Dawid Jankowiak <[email protected]>
  • Loading branch information
MateuszMamczarz and jankowiakdawid authored Feb 14, 2025
1 parent dc5d352 commit 0a88f20
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 82 deletions.
41 changes: 22 additions & 19 deletions src/elements/content-sidebar/BoxAISidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@
*/
import * as React from 'react';
import { useIntl } from 'react-intl';
import { type ItemType, type QuestionType } from '@box/box-ai-content-answers';
import { RecordActionType } from '@box/box-ai-agent-selector';
import { type ItemType } from '@box/box-ai-content-answers';
import { AgentsProvider , RecordActionType } from '@box/box-ai-agent-selector';
import BoxAISidebarContent from './BoxAISidebarContent';
import { BoxAISidebarContext } from './context/BoxAISidebarContext';
import { DOCUMENT_SUGGESTED_QUESTIONS, SPREADSHEET_FILE_EXTENSIONS } from '../common/content-answers/constants';
import type { BoxAISidebarCache, BoxAISidebarCacheSetter } from './types/BoxAISidebarTypes';

import messages from '../common/content-answers/messages';

export interface BoxAISidebarProps {
contentName: string;
cache: { encodedSession?: string | null; questions?: QuestionType[] };
cache: BoxAISidebarCache;
createSessionRequest: (payload: Record<string, unknown>, itemID: string) => Promise<unknown>;
elementId: string;
fetchTimeout: Record<string, unknown>;
Expand Down Expand Up @@ -51,7 +52,7 @@ export interface BoxAISidebarProps {
itemSize?: string;
userInfo: { name: string; avatarURL: string };
recordAction: (params: RecordActionType) => void;
setCacheValue: (key: 'encodedSession' | 'questions', value: string | null | QuestionType[]) => void;
setCacheValue: BoxAISidebarCacheSetter;
}

const BoxAISidebar = (props: BoxAISidebarProps) => {
Expand Down Expand Up @@ -127,21 +128,23 @@ const BoxAISidebar = (props: BoxAISidebarProps) => {
return (
// BoxAISidebarContent is using withApiWrapper that is not passing all provided props,
// that's why we need to use provider to pass other props
<BoxAISidebarContext.Provider value={contextValue}>
<BoxAISidebarContent
getSuggestedQuestions={getSuggestedQuestions}
isOpen
isStopResponseEnabled={isStopResponseEnabled}
itemID={fileID}
itemIDs={[fileID]}
restoredQuestions={questionsWithoutInProgress}
restoredSession={cache.encodedSession}
suggestedQuestions={getSuggestedQuestions === null ? localizedQuestions : []}
warningNotice={spreadsheetNotice}
warningNoticeAriaLabel={formatMessage(messages.welcomeMessageSpreadsheetNoticeAriaLabel)}
{...rest}
/>
</BoxAISidebarContext.Provider>
<AgentsProvider value={cache.agents}>
<BoxAISidebarContext.Provider value={contextValue}>
<BoxAISidebarContent
getSuggestedQuestions={getSuggestedQuestions}
isOpen
isStopResponseEnabled={isStopResponseEnabled}
itemID={fileID}
itemIDs={[fileID]}
restoredQuestions={questionsWithoutInProgress}
restoredSession={cache.encodedSession}
suggestedQuestions={getSuggestedQuestions === null ? localizedQuestions : []}
warningNotice={spreadsheetNotice}
warningNoticeAriaLabel={formatMessage(messages.welcomeMessageSpreadsheetNoticeAriaLabel)}
{...rest}
/>
</BoxAISidebarContext.Provider>
</AgentsProvider>
);
};

Expand Down
115 changes: 59 additions & 56 deletions src/elements/content-sidebar/BoxAISidebarContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as React from 'react';
import flow from 'lodash/flow';
import { useIntl } from 'react-intl';
import classNames from 'classnames';
import { AgentsProvider, BoxAiAgentSelectorWithApi } from '@box/box-ai-agent-selector';
import { BoxAiAgentSelectorWithApi, useAgents } from '@box/box-ai-agent-selector';
import { IconButton, Tooltip } from '@box/blueprint-web';
import { ArrowsExpand } from '@box/blueprint-web-assets/icons/Fill';
import {
Expand Down Expand Up @@ -65,6 +65,7 @@ function BoxAISidebarContent(props: ApiWrapperProps) {
setCacheValue,
userInfo,
} = React.useContext(BoxAISidebarContext);
const { agents, requestState, selectedAgent } = useAgents();
const { questions: cacheQuestions } = cache;

if (cache.encodedSession !== encodedSession) {
Expand All @@ -75,6 +76,10 @@ function BoxAISidebarContent(props: ApiWrapperProps) {
setCacheValue('questions', questions);
}

if (cache.agents.selectedAgent !== selectedAgent) {
setCacheValue('agents', { agents, requestState, selectedAgent });
}

const handleModalClose = () => {
setIsModalOpen(false);
};
Expand Down Expand Up @@ -152,61 +157,59 @@ function BoxAISidebarContent(props: ApiWrapperProps) {
);

return (
<AgentsProvider>
<>
<SidebarContent
actions={renderActions()}
className={classNames('bcs-BoxAISidebar', { 'with-modal-open': isModalOpen })}
elementId={elementId}
sidebarView={SIDEBAR_VIEW_BOXAI}
>
<div className="bcs-BoxAISidebar-content">
<BoxAiContentAnswers
className="bcs-BoxAISidebar-contentAnswers"
contentType={formatMessage(messages.sidebarBoxAIContent)}
hostAppName={hostAppName}
isAIStudioAgentSelectorEnabled={isAIStudioAgentSelectorEnabled}
isFeedbackEnabled={isFeedbackEnabled}
isStopResponseEnabled={isStopResponseEnabled}
items={items}
questions={questions}
stopQuestion={stopQuestion}
submitQuestion={sendQuestion}
userInfo={userInfo}
variant="sidebar"
recordAction={recordAction}
{...rest}
/>
</div>
</SidebarContent>
<IntelligenceModal
contentName={contentName}
contentType={formatMessage(messages.sidebarBoxAIContent)}
extension={fileExtension}
getAIStudioAgents={getAIStudioAgents}
hostAppName={hostAppName}
isAIStudioAgentSelectorEnabled={isAIStudioAgentSelectorEnabled}
isFeedbackEnabled={isFeedbackEnabled}
isResetChatEnabled={isResetChatEnabled}
isStopResponseEnabled={isStopResponseEnabled}
items={items}
itemSize={itemSize}
onClearAction={onClearAction}
onOpenChange={handleModalClose}
onSelectAgent={onSelectAgent}
open={isModalOpen}
questions={questions}
recordAction={isModalOpen ? recordAction : undefined}
showLoadingIndicator={false}
stopPropagationOnEsc
submitQuestion={sendQuestion}
userInfo={userInfo}
variant="collapsible"
{...rest}
shouldRenderProviders={false}
/>
</>
</AgentsProvider>
<>
<SidebarContent
actions={renderActions()}
className={classNames('bcs-BoxAISidebar', { 'with-modal-open': isModalOpen })}
elementId={elementId}
sidebarView={SIDEBAR_VIEW_BOXAI}
>
<div className="bcs-BoxAISidebar-content">
<BoxAiContentAnswers
className="bcs-BoxAISidebar-contentAnswers"
contentType={formatMessage(messages.sidebarBoxAIContent)}
hostAppName={hostAppName}
isAIStudioAgentSelectorEnabled={isAIStudioAgentSelectorEnabled}
isFeedbackEnabled={isFeedbackEnabled}
isStopResponseEnabled={isStopResponseEnabled}
items={items}
questions={questions}
stopQuestion={stopQuestion}
submitQuestion={sendQuestion}
userInfo={userInfo}
variant="sidebar"
recordAction={recordAction}
{...rest}
/>
</div>
</SidebarContent>
<IntelligenceModal
contentName={contentName}
contentType={formatMessage(messages.sidebarBoxAIContent)}
extension={fileExtension}
getAIStudioAgents={getAIStudioAgents}
hostAppName={hostAppName}
isAIStudioAgentSelectorEnabled={isAIStudioAgentSelectorEnabled}
isFeedbackEnabled={isFeedbackEnabled}
isResetChatEnabled={isResetChatEnabled}
isStopResponseEnabled={isStopResponseEnabled}
items={items}
itemSize={itemSize}
onClearAction={onClearAction}
onOpenChange={handleModalClose}
onSelectAgent={onSelectAgent}
open={isModalOpen}
questions={questions}
recordAction={isModalOpen ? recordAction : undefined}
showLoadingIndicator={false}
stopPropagationOnEsc
submitQuestion={sendQuestion}
userInfo={userInfo}
variant="collapsible"
{...rest}
shouldRenderProviders={false}
/>
</>
);
}

Expand Down
11 changes: 8 additions & 3 deletions src/elements/content-sidebar/SidebarPanels.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import * as React from 'react';
import flow from 'lodash/flow';
import noop from 'lodash/noop';
import { matchPath, Redirect, Route, Switch, type Location } from 'react-router-dom';
import { type QuestionType } from '@box/box-ai-content-answers';
import SidebarUtils from './SidebarUtils';
import withSidebarAnnotations from './withSidebarAnnotations';
import { withAnnotatorContext } from '../common/annotator-context';
Expand Down Expand Up @@ -42,6 +41,7 @@ import type { VersionsSidebarProps } from './versions';
import type { User, BoxItem } from '../../common/types/core';
import type { Errors } from '../common/flowTypes';
import type { FeatureConfig } from '../common/feature-checking';
import type { BoxAISidebarCache } from './types/BoxAISidebarTypes';

type Props = {
activitySidebarProps: ActivitySidebarProps,
Expand Down Expand Up @@ -133,7 +133,12 @@ class SidebarPanels extends React.Component<Props, State> {

versionsSidebar: ElementRefType = React.createRef();

boxAiSidebarCache: { encodedSession?: string | null, questions?: QuestionType[] } = {
boxAiSidebarCache: BoxAISidebarCache = {
agents: {
agents: [],
selectedAgent: null,
requestState: 'not_started',
},
encodedSession: null,
questions: [],
};
Expand Down Expand Up @@ -166,7 +171,7 @@ class SidebarPanels extends React.Component<Props, State> {
}
};

setBoxAiSidebarCacheValue = (key: 'encodedSession' | 'questions', value: any) => {
setBoxAiSidebarCacheValue = (key: 'agents' | 'encodedSession' | 'questions', value: any) => {
this.boxAiSidebarCache[key] = value;
};

Expand Down
13 changes: 12 additions & 1 deletion src/elements/content-sidebar/__tests__/BoxAISidebar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,19 @@ jest.mock('@box/box-ai-content-answers', () => ({
jest.mock('../BoxAISidebarTitle', () => () => <div data-testid="boxai-sidebar-title" />);

describe('elements/content-sidebar/BoxAISidebar', () => {
const mockAgents = {
agents: [],
requestState: 'success',
selectedAgent: null,
};

const mockProps = {
contentName: 'testName.txt',
cache: { encodedSession: '', questions: [] },
cache: {
encodedSession: '',
questions: [],
agents: mockAgents,
},
createSessionRequest: jest.fn(() => ({ encodedSession: '1234' })),
elementId: '123',
fetchTimeout: {},
Expand Down Expand Up @@ -200,6 +210,7 @@ describe('elements/content-sidebar/BoxAISidebar', () => {
prompt: 'not completed question',
},
],
agents: mockAgents,
},
});

Expand Down
7 changes: 4 additions & 3 deletions src/elements/content-sidebar/context/BoxAISidebarContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import * as React from 'react';
import noop from 'lodash/noop';
import { RecordActionType as AgentSelectorRecordActionType } from '@box/box-ai-agent-selector';
import { RecordActionType as ContentAnswersRecordActionType } from '@box/box-ai-content-answers';
import type { ItemType, QuestionType } from '@box/box-ai-content-answers';
import type { ItemType } from '@box/box-ai-content-answers';
import type { BoxAISidebarCache, BoxAISidebarCacheSetter } from '../types/BoxAISidebarTypes';

type BoxAISidebarRecordActionType =
| AgentSelectorRecordActionType
Expand All @@ -14,7 +15,7 @@ type BoxAISidebarRecordActionType =
});

export interface BoxAISidebarContextValues {
cache: { encodedSession?: string | null; questions?: QuestionType[] };
cache: BoxAISidebarCache;
contentName: string;
elementId: string;
fileExtension: string;
Expand All @@ -23,7 +24,7 @@ export interface BoxAISidebarContextValues {
items: Array<ItemType>;
itemSize?: string;
recordAction: (params: BoxAISidebarRecordActionType) => void;
setCacheValue: (key: 'encodedSession' | 'questions', value: string | null | QuestionType[]) => void;
setCacheValue: BoxAISidebarCacheSetter;
userInfo: { name: string; avatarURL: string };
}

Expand Down
18 changes: 18 additions & 0 deletions src/elements/content-sidebar/types/BoxAISidebarTypes.js.flow
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Flowtype definitions for BoxAISidebarTypes.ts
* Generated by Flowgen from a Typescript Definition
* Flowgen v1.21.0
*/

import { type, QuestionType } from "@box/box-ai-content-answers";
import { type, AgentState } from "@box/box-ai-agent-selector";
export type BoxAISidebarCache = {
agents: AgentState,
encodedSession: string | null,
questions: QuestionType[],
...
};
export type BoxAISidebarCacheSetter = (
key: "agents" | "encodedSession" | "questions",
value: AgentState | QuestionType[] | string | null
) => void;
10 changes: 10 additions & 0 deletions src/elements/content-sidebar/types/BoxAISidebarTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { type QuestionType } from '@box/box-ai-content-answers';
import { type AgentState } from '@box/box-ai-agent-selector';

export type BoxAISidebarCache = {
agents: AgentState,
encodedSession: string | null,
questions: QuestionType[],
};

export type BoxAISidebarCacheSetter = (key: 'agents' | 'encodedSession' | 'questions', value: AgentState | QuestionType[] | string | null) => void;

0 comments on commit 0a88f20

Please sign in to comment.