Skip to content

Commit

Permalink
feat: show completion provider status if down
Browse files Browse the repository at this point in the history
  • Loading branch information
nzambello committed Nov 22, 2023
1 parent c7adf7c commit d5f31fb
Show file tree
Hide file tree
Showing 14 changed files with 441 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.memori--completion-provider-status--icon {
width: 1em;
height: 1em;
fill: var(--memori-error-color, red);
}

.memori--completion-provider-status--tooltip.memori-tooltip.memori-tooltip--align-topLeft:not(.memori-tooltip--disabled).memori-tooltip--visible .memori-tooltip--content,
.memori--completion-provider-status--tooltip.memori-tooltip.memori-tooltip--align-topLeft:not(.memori-tooltip--disabled):not(.memori-tooltip--visible):hover .memori-tooltip--content {
transform: translateY(calc(-100% - 2em)) translateX(3em);
}

.memori--completion-provider-status--tooltip.memori-tooltip .memori-tooltip--content p {
margin: 0.5em auto;
}

.memori--completion-provider-status--tooltip.memori-tooltip .memori-tooltip--content p+p {
margin-top: 1em;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { Meta, Story } from '@storybook/react';
import CompletionProviderStatus, { Props } from './CompletionProviderStatus';

import './CompletionProviderStatus.css';

const meta: Meta = {
title: 'Completion Provider Status',
component: CompletionProviderStatus,
argTypes: {},
parameters: {
layout: 'centered',
controls: { expanded: true },
},
};

export default meta;

const Template: Story<Props> = args => <CompletionProviderStatus {...args} />;

export const Default = Template.bind({});
Default.args = {};

export const Errored = Template.bind({});
Errored.args = {
forceStatus: 'major',
};

export const WithSpecifiedProvider = Template.bind({});
WithSpecifiedProvider.args = {
provider: 'OpenAI',
};

export const ErroredWithSpecifiedProvider = Template.bind({});
ErroredWithSpecifiedProvider.args = {
forceStatus: 'major',
provider: 'OpenAI',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import { render } from '@testing-library/react';
import CompletionProviderStatus from './CompletionProviderStatus';

it('renders CompletionProviderStatus unchanged', () => {
const { container } = render(<CompletionProviderStatus forceStatus="none" />);
expect(container).toMatchSnapshot();
});

it('renders CompletionProviderStatus errored unchanged', () => {
const { container } = render(
<CompletionProviderStatus forceStatus="major" />
);
expect(container).toMatchSnapshot();
});

it('renders CompletionProviderStatus with provider specified unchanged', () => {
const { container } = render(
<CompletionProviderStatus provider="OpenAI" forceStatus="none" />
);
expect(container).toMatchSnapshot();
});

it('renders CompletionProviderStatus errored with provider specified unchanged', () => {
const { container } = render(
<CompletionProviderStatus provider="OpenAI" forceStatus="major" />
);
expect(container).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useEffect, useState } from 'react';
import Tooltip from '../ui/Tooltip';
import Warning from '../icons/Warning';
import { useTranslation } from 'react-i18next';

export interface Props {
forceStatus?: string;
provider?: 'OpenAI' | string | null;
}

const initProviderStatus = (provider?: Props['provider']) => {
switch (provider) {
case 'DEFAULT':
case 'OpenAI':
return {
getStatus: async () => {
const res = await fetch(
'https://status.openai.com/api/v2/summary.json'
);
const data = await res.json();
return data.status.indicator ?? 'none';
},
statusPage: 'https://status.openai.com/',
};
default:
return {
getStatus: async () => 'none',
statusPage: '',
};
}
};

const CompletionProviderStatus = ({ forceStatus, provider }: Props) => {
const { t } = useTranslation();
const [status, setStatus] = useState(forceStatus ?? 'none');

const providerStatus = initProviderStatus(provider);

useEffect(() => {
if (forceStatus) return;

providerStatus.getStatus().then(status => setStatus(status));
}, [forceStatus, provider]);

return status !== 'none' ? (
<Tooltip
className="memori--completion-provider-status--tooltip"
align="topLeft"
content={
<div>
<p>
{t('completionProviderDown', {
provider: provider ?? t('completionProviderFallbackName'),
})}
</p>
{!!providerStatus.statusPage?.length && (
<p>
<a
href={providerStatus.statusPage}
rel="noopener noreferrer"
target="_blank"
>
{t('completionProviderCheckStatusPage')}
</a>
</p>
)}
</div>
}
>
<Warning className="memori--completion-provider-status--icon" />
</Tooltip>
) : null;
};

export default CompletionProviderStatus;
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders CompletionProviderStatus errored unchanged 1`] = `
<div>
<div
class="memori-tooltip memori-tooltip--align-topLeft memori--completion-provider-status--tooltip"
>
<div
class="memori-tooltip--content"
>
<div>
<p>
completionProviderDown
</p>
</div>
</div>
<div
class="memori-tooltip--trigger"
>
<svg
aria-hidden="true"
class="memori--completion-provider-status--icon"
focusable="false"
role="img"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M464 720a48 48 0 1096 0 48 48 0 10-96 0zm16-304v184c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V416c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8zm475.7 440l-416-720c-6.2-10.7-16.9-16-27.7-16s-21.6 5.3-27.7 16l-416 720C56 877.4 71.4 904 96 904h832c24.6 0 40-26.6 27.7-48zm-783.5-27.9L512 239.9l339.8 588.2H172.2z"
/>
</svg>
</div>
</div>
</div>
`;

exports[`renders CompletionProviderStatus errored with provider specified unchanged 1`] = `
<div>
<div
class="memori-tooltip memori-tooltip--align-topLeft memori--completion-provider-status--tooltip"
>
<div
class="memori-tooltip--content"
>
<div>
<p>
completionProviderDown
</p>
<p>
<a
href="https://status.openai.com/"
rel="noopener noreferrer"
target="_blank"
>
completionProviderCheckStatusPage
</a>
</p>
</div>
</div>
<div
class="memori-tooltip--trigger"
>
<svg
aria-hidden="true"
class="memori--completion-provider-status--icon"
focusable="false"
role="img"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M464 720a48 48 0 1096 0 48 48 0 10-96 0zm16-304v184c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V416c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8zm475.7 440l-416-720c-6.2-10.7-16.9-16-27.7-16s-21.6 5.3-27.7 16l-416 720C56 877.4 71.4 904 96 904h832c24.6 0 40-26.6 27.7-48zm-783.5-27.9L512 239.9l339.8 588.2H172.2z"
/>
</svg>
</div>
</div>
</div>
`;

exports[`renders CompletionProviderStatus unchanged 1`] = `<div />`;

exports[`renders CompletionProviderStatus with provider specified unchanged 1`] = `<div />`;
4 changes: 4 additions & 0 deletions src/components/StartPanel/StartPanel.css
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@
opacity: 0.85;
}

.memori--start-button {
margin-right: 1em;
}

.memori--language-chooser {
margin-bottom: 1rem;
}
Expand Down
19 changes: 19 additions & 0 deletions src/components/StartPanel/StartPanel.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,22 @@ WithIntegration.args = {
clickedStart: false,
onClickStart: () => {},
};

export const WithCompletionProviderDown = Template.bind({});
WithCompletionProviderDown.args = {
memori: {
...memori,
enableCompletions: false,
completionProvider: 'OpenAI',
},
tenant,
language: 'it',
userLang: 'en',
setUserLang: () => {},
openPositionDrawer: () => {},
instruct: false,
sessionId: sessionID,
clickedStart: false,
onClickStart: () => {},
_TEST_forceProviderStatus: 'major',
};
22 changes: 22 additions & 0 deletions src/components/StartPanel/StartPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,25 @@ it('renders StartPanel with integrationConfig unchanged', () => {
);
expect(container).toMatchSnapshot();
});

it('renders StartPanel with completion provider down unchanged', () => {
const { container } = render(
<StartPanel
memori={{
...memori,
completionProvider: 'OpenAI',
}}
tenant={tenant}
language="it"
userLang="en"
setUserLang={() => {}}
openPositionDrawer={() => {}}
instruct={false}
sessionId={sessionID}
clickedStart={false}
onClickStart={() => {}}
_TEST_forceProviderStatus="major"
/>
);
expect(container).toMatchSnapshot();
});
8 changes: 8 additions & 0 deletions src/components/StartPanel/StartPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Translation from '../icons/Translation';
import { chatLanguages } from '../../helpers/constants';
import BlockedMemoriBadge from '../BlockedMemoriBadge/BlockedMemoriBadge';
import AI from '../icons/AI';
import CompletionProviderStatus from '../CompletionProviderStatus/CompletionProviderStatus';

export interface Props {
memori: Memori;
Expand All @@ -32,6 +33,7 @@ export interface Props {
clickedStart?: boolean;
onClickStart?: () => void;
initializeTTS?: () => void;
_TEST_forceProviderStatus?: string;
}

const StartPanel: React.FC<Props> = ({
Expand All @@ -50,6 +52,7 @@ const StartPanel: React.FC<Props> = ({
clickedStart,
onClickStart,
initializeTTS,
_TEST_forceProviderStatus,
}) => {
const { t, i18n } = useTranslation();
const [translatedDescription, setTranslatedDescription] = useState(
Expand Down Expand Up @@ -254,6 +257,11 @@ const StartPanel: React.FC<Props> = ({
)}
</Button>

<CompletionProviderStatus
provider={memori.completionProvider}
forceStatus={_TEST_forceProviderStatus}
/>

<p className="memori--start-description">
{instruct
? t('write_and_speak.pageInstructExplanation')
Expand Down
Loading

0 comments on commit d5f31fb

Please sign in to comment.