Skip to content

Commit

Permalink
Performance Profiler: Improve LLM recommendations area (#94254)
Browse files Browse the repository at this point in the history
* Set feature flag to true and use a new endpoint

* Display loading and generated with IA messages

* Add survey on the IA generated message

* Fix responsiveness
  • Loading branch information
WBerredo authored Sep 6, 2024
1 parent ecd26fa commit 28740d3
Show file tree
Hide file tree
Showing 15 changed files with 265 additions and 11 deletions.
49 changes: 49 additions & 0 deletions client/assets/images/performance-profiler/ia-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ export const PerformanceProfilerDashboardContent = ( {
/>
<NewsletterBanner link={ `/speed-test-tool/weekly-report?url=${ url }&hash=${ hash }` } />
<ScreenshotTimeline screenshots={ screenshots ?? [] } />
{ audits && <InsightsSection audits={ audits } url={ url } isWpcom={ is_wpcom } /> }
{ audits && (
<InsightsSection audits={ audits } url={ url } isWpcom={ is_wpcom } hash={ hash } />
) }
</div>

<Disclaimer />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ type InsightsSectionProps = {
audits: Record< string, PerformanceMetricsItemQueryResponse >;
url: string;
isWpcom: boolean;
hash: string;
};

export const InsightsSection = ( props: InsightsSectionProps ) => {
const translate = useTranslate();
const { audits, isWpcom } = props;
const { audits, isWpcom, hash } = props;

return (
<div className="performance-profiler-insights-section">
Expand All @@ -26,6 +27,7 @@ export const InsightsSection = ( props: InsightsSectionProps ) => {
index={ index }
url={ props.url }
isWpcom={ isWpcom }
hash={ hash }
/>
) ) }
</div>
Expand Down
34 changes: 34 additions & 0 deletions client/performance-profiler/components/insights-section/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ $blueberry-color: #3858e9;
color: var(--studio-orange-40);
}
}

.header-code {
color: #3858e9;
}
}

&.is-expanded .foldable-card__main {
Expand Down Expand Up @@ -124,6 +128,36 @@ $blueberry-color: #3858e9;
}
}

.generated-with-ia {
font-weight: 500;
}

.metrics-insight-content {
.survey {
display: flex;
align-items: center;
gap: 16px;
flex-wrap: wrap;
}

.options {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;

&.good {
color: var(--studio-green-50);
fill: var(--studio-green-50);
}

&.bad {
color: var(--studio-red-50);
fill: var(--studio-red-50);
}
}
}

.metrics-insight-detailed-content {
margin-top: 24px;

Expand Down
30 changes: 30 additions & 0 deletions client/performance-profiler/components/llm-message/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import clsx from 'clsx';
import { useTranslate } from 'i18n-calypso';
import { ReactNode } from 'react';
import IAIcon from 'calypso/assets/images/performance-profiler/ia-icon.svg';

import './style.scss';

interface LLMMessageProps {
message: string | ReactNode;
secondaryArea?: ReactNode;
rotate?: boolean;
}

export const LLMMessage = ( { message, rotate, secondaryArea }: LLMMessageProps ) => {
const translate = useTranslate();

return (
<div className="performance-profiler-llm-message">
<div className="content">
<img
src={ IAIcon }
alt={ translate( 'IA generated content icon' ) }
className={ clsx( { rotate: !! rotate } ) }
/>
<span className="message">{ message }</span>
</div>
{ secondaryArea }
</div>
);
};
37 changes: 37 additions & 0 deletions client/performance-profiler/components/llm-message/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.performance-profiler-llm-message {
box-sizing: border-box;
display: flex;
padding: 16px;
align-items: center;
align-self: stretch;
justify-content: space-between;
background: linear-gradient(0deg, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.95) 100%), linear-gradient(90deg, #4458e4 0%, #069e08 100%);
/* stylelint-disable-next-line scales/radii */
border-radius: 6px;

color: #000;
font-family: "SF Pro Text", $sans;
font-size: $font-body-small;
line-height: 20px;
flex-wrap: wrap;
gap: 10px;

.content {
display: flex;
align-items: center;
gap: 8px;
}

.rotate {
animation: rotation 3s infinite linear;
}

@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface MetricsInsightProps {
index: number;
url?: string;
isWpcom: boolean;
hash: string;
}

const Card = styled( FoldableCard )`
Expand Down Expand Up @@ -65,11 +66,13 @@ const Content = styled.div`
export const MetricsInsight: React.FC< MetricsInsightProps > = ( props ) => {
const translate = useTranslate();

const { insight, onClick, index, isWpcom } = props;
const { insight, onClick, index, isWpcom, hash } = props;

const [ retrieveInsight, setRetrieveInsight ] = useState( false );
const { data: llmAnswer, isLoading: isLoadingLlmAnswer } = useSupportChatLLMQuery(
insight.description ?? '',
hash,
isWpcom,
isEnabled( 'performance-profiler/llm' ) && retrieveInsight
);
const tip = tips[ insight.id ];
Expand Down Expand Up @@ -105,6 +108,7 @@ export const MetricsInsight: React.FC< MetricsInsightProps > = ( props ) => {
} }
secondaryArea={ tip && <Tip { ...tip } /> }
isLoading={ isEnabled( 'performance-profiler/llm' ) && isLoadingLlmAnswer }
IAGenerated={ isEnabled( 'performance-profiler/llm' ) }
/>
</Content>
</Card>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
import { useTranslate } from 'i18n-calypso';
import { useState } from 'react';
import Markdown from 'react-markdown';
import { PerformanceMetricsItemQueryResponse } from 'calypso/data/site-profiler/types';
import { recordTracksEvent } from 'calypso/lib/analytics/tracks';
import { LLMMessage } from 'calypso/performance-profiler/components/llm-message';
import { ThumbsUpIcon, ThumbsDownIcon } from 'calypso/performance-profiler/icons/thumbs';
import { InsightDetailedContent } from './insight-detailed-content';

interface InsightContentProps {
data: PerformanceMetricsItemQueryResponse;
secondaryArea?: React.ReactNode;
isLoading?: boolean;
IAGenerated: boolean;
}

export const InsightContent: React.FC< InsightContentProps > = ( props ) => {
const translate = useTranslate();
const { data, isLoading } = props;
const { data, isLoading, IAGenerated } = props;
const { description = '' } = data ?? {};
const [ feedbackSent, setFeedbackSent ] = useState( false );
const onSurveyClick = ( rating: string ) => {
recordTracksEvent( 'calypso_performance_profiler_llm_survey_click', {
rating,
description,
} );

setFeedbackSent( true );
};

return (
<div className="metrics-insight-content">
{ isLoading ? (
translate( 'Looking for the best solution…' )
<LLMMessage message={ translate( 'Finding the best solution…' ) } rotate />
) : (
<>
<div className="description-area">
Expand All @@ -34,6 +48,47 @@ export const InsightContent: React.FC< InsightContentProps > = ( props ) => {
</div>
{ props.secondaryArea }
</div>

{ IAGenerated && (
<LLMMessage
message={
<span className="generated-with-ia">{ translate( 'Generated with IA' ) }</span>
}
secondaryArea={
<div className="survey">
{ feedbackSent ? (
translate( 'Thanks for the feedback!' )
) : (
<>
<span>{ translate( 'How did we do?' ) }</span>
<div
className="options good"
onClick={ () => onSurveyClick( 'good' ) }
onKeyUp={ () => onSurveyClick( 'good' ) }
role="button"
tabIndex={ 0 }
>
<ThumbsUpIcon />

{ translate( "Good, it's helpful" ) }
</div>
<div
className="options bad"
onClick={ () => onSurveyClick( 'bad' ) }
onKeyUp={ () => onSurveyClick( 'bad' ) }
role="button"
tabIndex={ 0 }
>
<ThumbsDownIcon />
{ translate( 'Not helpful' ) }
</div>
</>
) }
</div>
}
/>
) }

{ data.details?.type && (
<div className="metrics-insight-detailed-content">
<InsightDetailedContent data={ data.details } />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const InsightHeader: React.FC< InsightHeaderProps > = ( props ) => {
return <p className="title-description">{ props.children }</p>;
},
code( props ) {
return <span className="value">{ props.children }</span>;
return <span className="header-code">{ props.children }</span>;
},
} }
>
Expand Down
13 changes: 9 additions & 4 deletions client/performance-profiler/hooks/use-support-chat-llm-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,27 @@ function mapResult( response: WPComSupportQueryResponse ) {
return response.messages?.[ 0 ]?.content ?? '';
}

export const useSupportChatLLMQuery = ( description: string, enable: boolean ) => {
export const useSupportChatLLMQuery = (
description: string,
hash: string,
is_wpcom: boolean,
enable: boolean
) => {
const question = `I need to fix the following issue to improve the performance of site: ${ description }.`;
const howToAnswer =
'Answer me in two topics in bold: "Why is this important?" and "How to fix this?"';
const message = `${ question } ${ howToAnswer }`;

return useQuery( {
// eslint-disable-next-line @tanstack/query/exhaustive-deps
queryKey: [ 'support', 'chat', description ],
queryKey: [ 'support', 'chat', description, is_wpcom ],
queryFn: () =>
wp.req.post(
{
path: '/odie/chat/wpcom-support-chat/',
path: `/odie/assistant/performance-profiler?hash=${ hash }`,
apiNamespace: 'wpcom/v2',
},
{ message }
{ message, is_wpcom }
),
meta: {
persist: false,
Expand Down
33 changes: 33 additions & 0 deletions client/performance-profiler/icons/thumbs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';

export const ThumbsUpIcon = ( { className }: { className?: string } ) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
className={ className }
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.24155 10.0159C9.31208 10.0054 9.38426 10 9.45771 10C9.77958 10 10.0633 9.78887 10.1558 9.48058L11.3423 5.52577C11.4359 5.21371 11.7231 5 12.0489 5C13.6788 5 15 6.32124 15 7.95108V9.5C15 9.77614 15.2239 10 15.5 10H17.4384C18.7396 10 19.6943 11.2228 19.3787 12.4851L18.3787 16.4851C18.1561 17.3754 17.3562 18 16.4384 18H10C9.52703 18 9.0924 17.8358 8.75 17.5613C8.4076 17.8358 7.97297 18 7.5 18H6C5.44772 18 5 17.5523 5 17V10C5 9.44772 5.44772 9 6 9H7.5C8.24683 9 8.89806 9.40935 9.24155 10.0159ZM15.5 11.5H17.4384C17.7637 11.5 18.0024 11.8057 17.9235 12.1213L16.9235 16.1213C16.8679 16.3439 16.6679 16.5 16.4384 16.5H10C9.72386 16.5 9.5 16.2761 9.5 16V11.4996C10.4668 11.4814 11.3138 10.8408 11.5926 9.9116L12.5853 6.60237C13.1212 6.81569 13.5 7.33915 13.5 7.95108V9.5C13.5 10.6046 14.3954 11.5 15.5 11.5ZM8 16V11C8 10.7239 7.77614 10.5 7.5 10.5H6.5V16.5H7.5C7.77614 16.5 8 16.2761 8 16Z"
/>
</svg>
);

export const ThumbsDownIcon = ( { className }: { className?: string } ) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
className={ className }
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M15.1979 12.9841C15.1273 12.9946 15.0552 13 14.9817 13C14.6599 13 14.3761 13.2111 14.2836 13.5194L13.0972 17.4742C13.0035 17.7863 12.7163 18 12.3905 18C10.7607 18 9.43943 16.6788 9.43943 15.0489V13.5C9.43943 13.2239 9.21557 13 8.93943 13H7.00098C5.69984 13 4.74513 11.7772 5.0607 10.5149L6.0607 6.51493C6.28328 5.62459 7.08325 5 8.00098 5L14.4394 5C14.9124 5 15.347 5.16418 15.6894 5.43866C16.0318 5.16418 16.4665 5 16.9394 5L18.4394 5C18.9917 5 19.4394 5.44772 19.4394 6V13C19.4394 13.5523 18.9917 14 18.4394 14H16.9394C16.1926 14 15.5414 13.5906 15.1979 12.9841ZM8.93943 11.5H7.00098C6.6757 11.5 6.43702 11.1943 6.51591 10.8787L7.51591 6.87873C7.57156 6.65615 7.77155 6.5 8.00098 6.5L14.4394 6.5C14.7156 6.5 14.9394 6.72386 14.9394 7V11.5004C13.9727 11.5186 13.1256 12.1592 12.8469 13.0884L11.8541 16.3976C11.3182 16.1843 10.9394 15.6608 10.9394 15.0489V13.5C10.9394 12.3954 10.044 11.5 8.93943 11.5ZM16.4394 7V12C16.4394 12.2761 16.6633 12.5 16.9394 12.5H17.9394V6.5H16.9394C16.6633 6.5 16.4394 6.72386 16.4394 7Z"
/>
</svg>
);
Loading

0 comments on commit 28740d3

Please sign in to comment.