Skip to content

Commit

Permalink
AI Assistant: Enhance thumbs feedback on AI Assistant and extensions …
Browse files Browse the repository at this point in the history
…(#40746)

* centralize prompt types and translations on AI client

* compute humanText only when necessary

* change extension implementation away from placeholder

* add last action to AI Assistant block

* add block type to event

* changelog

Committed via a GitHub action: https://github.com/Automattic/jetpack/actions/runs/12517741349

Upstream-Ref: Automattic/jetpack@3464284
  • Loading branch information
dhasilva authored and matticbot committed Dec 27, 2024
1 parent bff33ba commit bf78b89
Show file tree
Hide file tree
Showing 25 changed files with 578 additions and 60 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ This is an alpha version! The changes listed here are not final.
### Added
- AI Client: Add thumbs feedback on AI Assistant

### Changed
- AI Client: Move prompt types and update thumbs feedback event

## [0.25.3] - 2024-12-23
### Added
- Jetpack AI: Add thumbs up/down component to AI logo generator [#40610]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type BlockAIControlProps = {
showRemove?: boolean;
banner?: ReactElement;
error?: ReactElement;
lastAction?: string;
};
/**
* BlockAIControl component. Used by the AI Assistant block, adding logic and components to the base AIControl component.
Expand All @@ -32,6 +33,6 @@ type BlockAIControlProps = {
* @param {React.MutableRefObject} ref - Ref to the component
* @return {ReactElement} Rendered component
*/
export declare function BlockAIControl({ disabled, value, placeholder, showAccept, acceptLabel, showButtonLabels, isTransparent, state, showGuideLine, customFooter, onChange, onSend, onStop, onAccept, onDiscard, showRemove, banner, error, }: BlockAIControlProps, ref: React.MutableRefObject<HTMLInputElement>): ReactElement;
export declare function BlockAIControl({ disabled, value, placeholder, showAccept, acceptLabel, showButtonLabels, isTransparent, state, showGuideLine, customFooter, onChange, onSend, onStop, onAccept, onDiscard, showRemove, banner, error, lastAction, }: BlockAIControlProps, ref: React.MutableRefObject<HTMLInputElement>): ReactElement;
declare const _default: React.ForwardRefExoticComponent<BlockAIControlProps & React.RefAttributes<HTMLInputElement>>;
export default _default;
9 changes: 7 additions & 2 deletions build/ai-client/src/components/ai-control/block-ai-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const debug = debugFactory('jetpack-ai-client:block-ai-control');
* @param {React.MutableRefObject} ref - Ref to the component
* @return {ReactElement} Rendered component
*/
export function BlockAIControl({ disabled = false, value = '', placeholder = '', showAccept = false, acceptLabel = __('Accept', 'jetpack-ai-client'), showButtonLabels = true, isTransparent = false, state = 'init', showGuideLine = false, customFooter = null, onChange, onSend, onStop, onAccept, onDiscard, showRemove = false, banner = null, error = null, }, ref) {
export function BlockAIControl({ disabled = false, value = '', placeholder = '', showAccept = false, acceptLabel = __('Accept', 'jetpack-ai-client'), showButtonLabels = true, isTransparent = false, state = 'init', showGuideLine = false, customFooter = null, onChange, onSend, onStop, onAccept, onDiscard, showRemove = false, banner = null, error = null, lastAction, }, ref) {
const loading = state === 'requesting' || state === 'suggesting';
const [editRequest, setEditRequest] = useState(false);
const [lastValue, setLastValue] = useState(value || null);
Expand Down Expand Up @@ -79,7 +79,12 @@ export function BlockAIControl({ disabled = false, value = '', placeholder = '',
const message = showGuideLine &&
!loading &&
!editRequest &&
(customFooter || (_jsx(GuidelineMessage, { showAIFeedbackThumbs: true, ratedItem: 'ai-assistant', prompt: value })));
(customFooter || (_jsx(GuidelineMessage, { aiFeedbackThumbsOptions: {
showAIFeedbackThumbs: true,
ratedItem: 'ai-assistant',
prompt: lastAction,
block: 'ai-assistant',
} })));
return (_jsx(AIControl, { disabled: disabled || loading, value: value, placeholder: placeholder, isTransparent: isTransparent, state: state, onChange: changeHandler, banner: banner, error: error, actions: actions, message: message, promptUserInputRef: promptUserInputRef }));
}
export default forwardRef(BlockAIControl);
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ type ExtensionAIControlProps = {
onUndo?: () => void;
onUpgrade?: (event: MouseEvent<HTMLButtonElement>) => void;
onTryAgain?: () => void;
lastAction?: string;
blockType: string;
};
/**
* ExtensionAIControl component. Used by the AI Assistant inline extensions, adding logic and components to the base AIControl component.
Expand All @@ -35,6 +37,6 @@ type ExtensionAIControlProps = {
* @param {React.MutableRefObject} ref - Ref to the component
* @return {ReactElement} Rendered component
*/
export declare function ExtensionAIControl({ className, disabled, value, placeholder, showButtonLabels, isTransparent, state, showGuideLine, error, requestsRemaining, showUpgradeMessage, showFairUsageMessage, upgradeUrl, wrapperRef, onChange, onSend, onStop, onClose, onUndo, onUpgrade, onTryAgain, }: ExtensionAIControlProps, ref: React.MutableRefObject<HTMLInputElement>): ReactElement;
export declare function ExtensionAIControl({ className, disabled, value, placeholder, showButtonLabels, isTransparent, state, showGuideLine, error, requestsRemaining, showUpgradeMessage, showFairUsageMessage, upgradeUrl, wrapperRef, onChange, onSend, onStop, onClose, onUndo, onUpgrade, onTryAgain, lastAction, blockType, }: ExtensionAIControlProps, ref: React.MutableRefObject<HTMLInputElement>): ReactElement;
declare const _default: React.ForwardRefExoticComponent<ExtensionAIControlProps & React.RefAttributes<HTMLInputElement>>;
export default _default;
18 changes: 7 additions & 11 deletions build/ai-client/src/components/ai-control/extension-ai-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,21 @@ import './style.scss';
* @param {React.MutableRefObject} ref - Ref to the component
* @return {ReactElement} Rendered component
*/
export function ExtensionAIControl({ className, disabled = false, value = '', placeholder = '', showButtonLabels = true, isTransparent = false, state = 'init', showGuideLine = false, error, requestsRemaining, showUpgradeMessage = false, showFairUsageMessage = false, upgradeUrl, wrapperRef, onChange, onSend, onStop, onClose, onUndo, onUpgrade, onTryAgain, }, ref) {
export function ExtensionAIControl({ className, disabled = false, value = '', placeholder = '', showButtonLabels = true, isTransparent = false, state = 'init', showGuideLine = false, error, requestsRemaining, showUpgradeMessage = false, showFairUsageMessage = false, upgradeUrl, wrapperRef, onChange, onSend, onStop, onClose, onUndo, onUpgrade, onTryAgain, lastAction, blockType, }, ref) {
const loading = state === 'requesting' || state === 'suggesting';
const [editRequest, setEditRequest] = useState(false);
const [lastValue, setLastValue] = useState(value || null);
const promptUserInputRef = useRef(null);
const isDone = value?.length <= 0 && state === 'done';
const [initialPlaceholder] = useState(placeholder);
const [prompt, setPrompt] = useState(null);
// Pass the ref to forwardRef.
useImperativeHandle(ref, () => promptUserInputRef.current);
useEffect(() => {
if (editRequest) {
promptUserInputRef?.current?.focus();
}
}, [editRequest]);
useEffect(() => {
if (placeholder !== initialPlaceholder) {
// The prompt is used to determine if there was a toolbar action
setPrompt(placeholder);
}
}, [placeholder]);
const sendHandler = useCallback(() => {
setLastValue(value);
setPrompt(value);
setEditRequest(false);
onSend?.(value);
}, [onSend, value]);
Expand Down Expand Up @@ -95,7 +86,12 @@ export function ExtensionAIControl({ className, disabled = false, value = '', pl
message = (_jsx(UpgradeMessage, { requestsRemaining: requestsRemaining, onUpgradeClick: upgradeHandler, upgradeUrl: upgradeUrl }));
}
else if (showGuideLine) {
message = isDone ? (_jsx(GuidelineMessage, { showAIFeedbackThumbs: true, ratedItem: 'ai-assistant', prompt: prompt })) : (_jsx(GuidelineMessage, {}));
message = isDone ? (_jsx(GuidelineMessage, { aiFeedbackThumbsOptions: {
showAIFeedbackThumbs: true,
ratedItem: 'ai-assistant',
prompt: lastAction,
block: blockType,
} })) : (_jsx(GuidelineMessage, {}));
}
return (_jsx(AIControl, { className: className, disabled: disabled || loading, value: value, placeholder: placeholder, isTransparent: isTransparent, state: state, onChange: changeHandler, actions: actions, message: message, promptUserInputRef: promptUserInputRef, wrapperRef: wrapperRef }));
}
Expand Down
1 change: 1 addition & 0 deletions build/ai-client/src/components/ai-feedback/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type AiFeedbackThumbsProps = {
mediaLibraryId?: number;
prompt?: string;
revisedPrompt?: string;
block?: string | null;
};
onRate?: (rating: string) => void;
};
Expand Down
1 change: 1 addition & 0 deletions build/ai-client/src/components/ai-feedback/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export default function AiFeedbackThumbs({ disabled = false, iconSize = 24, rate
mediaLibraryId: options.mediaLibraryId || null,
prompt: options.prompt || null,
revisedPrompt: options.revisedPrompt || null,
block: options.block || null,
});
}
};
Expand Down
16 changes: 9 additions & 7 deletions build/ai-client/src/components/message/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,22 @@ export declare const MESSAGE_SEVERITY_ERROR = "error";
export declare const MESSAGE_SEVERITY_SUCCESS = "success";
export declare const MESSAGE_SEVERITY_INFO = "info";
export type MessageSeverityProp = typeof MESSAGE_SEVERITY_WARNING | typeof MESSAGE_SEVERITY_ERROR | typeof MESSAGE_SEVERITY_SUCCESS | typeof MESSAGE_SEVERITY_INFO | null;
type RateProps = {
type AiFeedbackThumbsOptions = {
showAIFeedbackThumbs?: boolean;
ratedItem?: string;
prompt?: string;
block?: string | null;
onRate?: (rating: string) => void;
};
export type MessageProps = {
icon?: React.ReactNode;
severity?: MessageSeverityProp;
showAIFeedbackThumbs?: boolean;
aiFeedbackThumbsOptions?: AiFeedbackThumbsOptions;
children: React.ReactNode;
} & RateProps;
};
export type GuidelineMessageProps = {
showAIFeedbackThumbs?: boolean;
} & RateProps;
aiFeedbackThumbsOptions?: AiFeedbackThumbsOptions;
};
export type OnUpgradeClick = (event?: React.MouseEvent<HTMLButtonElement>) => void;
export type UpgradeMessageProps = {
requestsRemaining: number;
Expand All @@ -46,14 +48,14 @@ export type ErrorMessageProps = {
* @param {MessageProps} props - Component props.
* @return {React.ReactElement} Banner component.
*/
export default function Message({ severity, icon, showAIFeedbackThumbs, ratedItem, prompt, onRate, children, }: MessageProps): React.ReactElement;
export default function Message({ severity, icon, aiFeedbackThumbsOptions, children, }: MessageProps): React.ReactElement;
/**
* React component to render a guideline message.
*
* @param {GuidelineMessageProps} props - Component props.
* @return {React.ReactElement} - Message component.
*/
export declare function GuidelineMessage({ showAIFeedbackThumbs, ...props }: GuidelineMessageProps): React.ReactElement;
export declare function GuidelineMessage({ aiFeedbackThumbsOptions, }: GuidelineMessageProps): React.ReactElement;
/**
* React component to render a fair usage limit message.
*
Expand Down
25 changes: 19 additions & 6 deletions build/ai-client/src/components/message/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,17 @@ const messageIconsMap = {
* @param {MessageProps} props - Component props.
* @return {React.ReactElement} Banner component.
*/
export default function Message({ severity = MESSAGE_SEVERITY_INFO, icon = null, showAIFeedbackThumbs = false, ratedItem = '', prompt = '', onRate = () => { }, children, }) {
return (_jsxs("div", { className: clsx('jetpack-ai-assistant__message', `jetpack-ai-assistant__message-severity-${severity}`), children: [(messageIconsMap[severity] || icon) && (_jsx(Icon, { icon: messageIconsMap[severity] || icon })), _jsx("div", { className: "jetpack-ai-assistant__message-content", children: children }), showAIFeedbackThumbs && (_jsx(AiFeedbackThumbs, { disabled: false, ratedItem: ratedItem, feature: "ai-assistant", options: {
prompt,
}, onRate: onRate }))] }));
export default function Message({ severity = MESSAGE_SEVERITY_INFO, icon = null, aiFeedbackThumbsOptions = {
showAIFeedbackThumbs: false,
ratedItem: '',
prompt: '',
block: null,
onRate: () => { },
}, children, }) {
return (_jsxs("div", { className: clsx('jetpack-ai-assistant__message', `jetpack-ai-assistant__message-severity-${severity}`), children: [(messageIconsMap[severity] || icon) && (_jsx(Icon, { icon: messageIconsMap[severity] || icon })), _jsx("div", { className: "jetpack-ai-assistant__message-content", children: children }), aiFeedbackThumbsOptions.showAIFeedbackThumbs && aiFeedbackThumbsOptions.prompt && (_jsx(AiFeedbackThumbs, { disabled: false, ratedItem: aiFeedbackThumbsOptions.ratedItem, feature: "ai-assistant", options: {
prompt: aiFeedbackThumbsOptions.prompt,
block: aiFeedbackThumbsOptions.block,
}, onRate: aiFeedbackThumbsOptions.onRate }))] }));
}
/**
* React component to render a learn more link.
Expand All @@ -49,8 +56,14 @@ function LearnMoreLink() {
* @param {GuidelineMessageProps} props - Component props.
* @return {React.ReactElement} - Message component.
*/
export function GuidelineMessage({ showAIFeedbackThumbs = false, ...props }) {
return (_jsxs(Message, { showAIFeedbackThumbs: showAIFeedbackThumbs, ...props, children: [_jsx("span", { children: __('AI-generated content could be inaccurate or biased.', 'jetpack-ai-client') }), _jsx(LearnMoreLink, {})] }));
export function GuidelineMessage({ aiFeedbackThumbsOptions = {
showAIFeedbackThumbs: false,
ratedItem: '',
prompt: '',
block: null,
onRate: () => { },
}, }) {
return (_jsxs(Message, { aiFeedbackThumbsOptions: aiFeedbackThumbsOptions, children: [_jsx("span", { children: __('AI-generated content could be inaccurate or biased.', 'jetpack-ai-client') }), _jsx(LearnMoreLink, {})] }));
}
/**
* React component to render a fair usage limit message.
Expand Down
106 changes: 106 additions & 0 deletions build/ai-client/src/constants.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
export declare const LANGUAGE_MAP: {
en: {
label: string;
};
es: {
label: string;
};
fr: {
label: string;
};
de: {
label: string;
};
it: {
label: string;
};
pt: {
label: string;
};
ru: {
label: string;
};
zh: {
label: string;
};
ja: {
label: string;
};
ar: {
label: string;
};
hi: {
label: string;
};
ko: {
label: string;
};
};
export declare const PROMPT_TONES_MAP: {
formal: {
label: string;
emoji: string;
};
informal: {
label: string;
emoji: string;
};
optimistic: {
label: string;
emoji: string;
};
humorous: {
label: string;
emoji: string;
};
serious: {
label: string;
emoji: string;
};
skeptical: {
label: string;
emoji: string;
};
empathetic: {
label: string;
emoji: string;
};
confident: {
label: string;
emoji: string;
};
passionate: {
label: string;
emoji: string;
};
provocative: {
label: string;
emoji: string;
};
};
export declare const PROMPT_TYPE_SUMMARY_BY_TITLE: "titleSummary";
export declare const PROMPT_TYPE_CONTINUE: "continue";
export declare const PROMPT_TYPE_SIMPLIFY: "simplify";
export declare const PROMPT_TYPE_CORRECT_SPELLING: "correctSpelling";
export declare const PROMPT_TYPE_GENERATE_TITLE: "generateTitle";
export declare const PROMPT_TYPE_MAKE_LONGER: "makeLonger";
export declare const PROMPT_TYPE_MAKE_SHORTER: "makeShorter";
export declare const PROMPT_TYPE_CHANGE_TONE: "changeTone";
export declare const PROMPT_TYPE_SUMMARIZE: "summarize";
export declare const PROMPT_TYPE_CHANGE_LANGUAGE: "changeLanguage";
export declare const PROMPT_TYPE_USER_PROMPT: "userPrompt";
export declare const PROMPT_TYPE_JETPACK_FORM_CUSTOM_PROMPT: "jetpackFormCustomPrompt";
export declare const PROMPT_TYPE_TRANSFORM_LIST_TO_TABLE: "transformListToTable";
export declare const PROMPT_TYPE_WRITE_POST_FROM_LIST: "writePostFromList";
export declare const TRANSLATE_LABEL: string;
export declare const TONE_LABEL: string;
export declare const CORRECT_SPELLING_LABEL: string;
export declare const SIMPLIFY_LABEL: string;
export declare const SUMMARIZE_LABEL: string;
export declare const MAKE_SHORTER_LABEL: string;
export declare const MAKE_LONGER_LABEL: string;
export declare const TURN_LIST_INTO_TABLE_LABEL: string;
export declare const WRITE_POST_FROM_LIST_LABEL: string;
export declare const GENERATE_TITLE_LABEL: string;
export declare const SUMMARY_BASED_ON_TITLE_LABEL: string;
export declare const CONTINUE_LABEL: string;
Loading

0 comments on commit bf78b89

Please sign in to comment.