woocommerce/plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts

400 lines
9.9 KiB
TypeScript
Raw Normal View History

/* eslint-disable @woocommerce/dependency-group */
/* eslint-disable @typescript-eslint/ban-ts-comment */
CYS - Add LookAndFeel and ToneOfVoice pages (#39979) * Add ProgressBar component to @woocommerce/components * Add changelog * Remove html.wp-toolbar in fullscreen mode * Add base style * Add Tell us a bit more about your business page * Fix merge conflict issues * Send BUSINESS_INFO_DESCRIPTION_COMPLETE event when continue button is clicked * Remove duplicated style import * Add changefile(s) from automation for the following project(s): @woocommerce/components, woocommerce * Lint fix * Add 'Look and Feel' and 'Tone of voice' pages'; * Use correct classname * Minor changes * Textearea color should be gray-900 after the user enter text * guide font weight should be 500 * Fix layout shift when a choice is selected * Fix choices width for tone of voice page * Use context value for the default * Revert button margin top * Fix default selection * Add X button * Decrease the margin by 20px to accommodate the height of the close button * Add close action * Include @woocommerce/ai package * Add AI service * Use AI service * Parse JSON from in function * Fix assignLookAndTone event type * Update plugins/woocommerce-admin/client/customize-store/design-with-ai/components/choice/choice.scss Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> * Update plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> * Log when AI API endpoint request fails * Add spinner when user clicks the continue button * streamlined unnecessary isRequesting context and forwarded close event * pnpm-lock changes from trunk * lint fixes * ai package test passWithNoTests * changelog * reset pnpm-lock to trunk * Dev: update pnpm-lock.yaml and jest preset config (#40045) * Update pnpm-lock.yaml * Update jest-preset config to fix unexpected token error --------- Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> Co-authored-by: rjchow <me@rjchow.com>
2023-09-06 06:21:09 +00:00
/**
* External dependencies
*/
import { __experimentalRequestJetpackToken as requestJetpackToken } from '@woocommerce/ai';
import apiFetch from '@wordpress/api-fetch';
import { recordEvent } from '@woocommerce/tracks';
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
import { Sender, assign, createMachine } from 'xstate';
import { dispatch, resolveSelect } from '@wordpress/data';
// @ts-ignore No types for this exist yet.
import { store as coreStore } from '@wordpress/core-data';
// @ts-ignore No types for this exist yet.
import { mergeBaseAndUserConfigs } from '@wordpress/edit-site/build-module/components/global-styles/global-styles-provider';
CYS - Add LookAndFeel and ToneOfVoice pages (#39979) * Add ProgressBar component to @woocommerce/components * Add changelog * Remove html.wp-toolbar in fullscreen mode * Add base style * Add Tell us a bit more about your business page * Fix merge conflict issues * Send BUSINESS_INFO_DESCRIPTION_COMPLETE event when continue button is clicked * Remove duplicated style import * Add changefile(s) from automation for the following project(s): @woocommerce/components, woocommerce * Lint fix * Add 'Look and Feel' and 'Tone of voice' pages'; * Use correct classname * Minor changes * Textearea color should be gray-900 after the user enter text * guide font weight should be 500 * Fix layout shift when a choice is selected * Fix choices width for tone of voice page * Use context value for the default * Revert button margin top * Fix default selection * Add X button * Decrease the margin by 20px to accommodate the height of the close button * Add close action * Include @woocommerce/ai package * Add AI service * Use AI service * Parse JSON from in function * Fix assignLookAndTone event type * Update plugins/woocommerce-admin/client/customize-store/design-with-ai/components/choice/choice.scss Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> * Update plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> * Log when AI API endpoint request fails * Add spinner when user clicks the continue button * streamlined unnecessary isRequesting context and forwarded close event * pnpm-lock changes from trunk * lint fixes * ai package test passWithNoTests * changelog * reset pnpm-lock to trunk * Dev: update pnpm-lock.yaml and jest preset config (#40045) * Update pnpm-lock.yaml * Update jest-preset config to fix unexpected token error --------- Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> Co-authored-by: rjchow <me@rjchow.com>
2023-09-06 06:21:09 +00:00
/**
* Internal dependencies
*/
import { designWithAiStateMachineContext } from './types';
import { lookAndTone } from './prompts';
import { FONT_PAIRINGS } from '../assembler-hub/sidebar/global-styles/font-pairing-variations/constants';
import { COLOR_PALETTES } from '../assembler-hub/sidebar/global-styles/color-palette-variations/constants';
import {
patternsToNameMap,
getTemplatePatterns,
LARGE_BUSINESS_TEMPLATES,
SMALL_MEDIUM_BUSINESS_TEMPLATES,
} from '../assembler-hub/hooks/use-home-templates';
const browserPopstateHandler =
() => ( sendBack: Sender< { type: 'EXTERNAL_URL_UPDATE' } > ) => {
const popstateHandler = () => {
sendBack( { type: 'EXTERNAL_URL_UPDATE' } );
};
window.addEventListener( 'popstate', popstateHandler );
return () => {
window.removeEventListener( 'popstate', popstateHandler );
};
};
export const getCompletion = async < ValidResponseObject >( {
queryId,
prompt,
version,
responseValidation,
retryCount,
}: {
queryId: string;
prompt: string;
version: string;
responseValidation: ( arg0: string ) => ValidResponseObject;
retryCount: number;
} ) => {
const { token } = await requestJetpackToken();
let data: {
completion: string;
};
let parsedCompletionJson;
try {
const url = new URL(
'https://public-api.wordpress.com/wpcom/v2/text-completion'
);
url.searchParams.append( 'feature', 'woo_cys' );
data = await apiFetch( {
url: url.toString(),
method: 'POST',
data: {
token,
prompt,
_fields: 'completion',
},
} );
} catch ( error ) {
recordEvent( 'customize_your_store_ai_completion_api_error', {
query_id: queryId,
version,
retry_count: retryCount,
error_type: 'api_request_error',
} );
throw error;
}
try {
parsedCompletionJson = JSON.parse( data.completion );
} catch {
recordEvent( 'customize_your_store_ai_completion_response_error', {
query_id: queryId,
version,
retry_count: retryCount,
error_type: 'json_parse_error',
response: data.completion,
} );
throw new Error(
`Error validating Jetpack AI text completions response for ${ queryId }`
);
}
try {
const validatedResponse = responseValidation( parsedCompletionJson );
recordEvent( 'customize_your_store_ai_completion_success', {
query_id: queryId,
version,
retry_count: retryCount,
} );
return validatedResponse;
} catch ( error ) {
recordEvent( 'customize_your_store_ai_completion_response_error', {
query_id: queryId,
version,
retry_count: retryCount,
error_type: 'valid_json_invalid_values',
response: data.completion,
} );
throw error;
}
};
CYS - Add LookAndFeel and ToneOfVoice pages (#39979) * Add ProgressBar component to @woocommerce/components * Add changelog * Remove html.wp-toolbar in fullscreen mode * Add base style * Add Tell us a bit more about your business page * Fix merge conflict issues * Send BUSINESS_INFO_DESCRIPTION_COMPLETE event when continue button is clicked * Remove duplicated style import * Add changefile(s) from automation for the following project(s): @woocommerce/components, woocommerce * Lint fix * Add 'Look and Feel' and 'Tone of voice' pages'; * Use correct classname * Minor changes * Textearea color should be gray-900 after the user enter text * guide font weight should be 500 * Fix layout shift when a choice is selected * Fix choices width for tone of voice page * Use context value for the default * Revert button margin top * Fix default selection * Add X button * Decrease the margin by 20px to accommodate the height of the close button * Add close action * Include @woocommerce/ai package * Add AI service * Use AI service * Parse JSON from in function * Fix assignLookAndTone event type * Update plugins/woocommerce-admin/client/customize-store/design-with-ai/components/choice/choice.scss Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> * Update plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> * Log when AI API endpoint request fails * Add spinner when user clicks the continue button * streamlined unnecessary isRequesting context and forwarded close event * pnpm-lock changes from trunk * lint fixes * ai package test passWithNoTests * changelog * reset pnpm-lock to trunk * Dev: update pnpm-lock.yaml and jest preset config (#40045) * Update pnpm-lock.yaml * Update jest-preset config to fix unexpected token error --------- Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> Co-authored-by: rjchow <me@rjchow.com>
2023-09-06 06:21:09 +00:00
export const getLookAndTone = async (
context: designWithAiStateMachineContext
) => {
return getCompletion( {
...lookAndTone,
prompt: lookAndTone.prompt(
context.businessInfoDescription.descriptionText
),
retryCount: 0,
CYS - Add LookAndFeel and ToneOfVoice pages (#39979) * Add ProgressBar component to @woocommerce/components * Add changelog * Remove html.wp-toolbar in fullscreen mode * Add base style * Add Tell us a bit more about your business page * Fix merge conflict issues * Send BUSINESS_INFO_DESCRIPTION_COMPLETE event when continue button is clicked * Remove duplicated style import * Add changefile(s) from automation for the following project(s): @woocommerce/components, woocommerce * Lint fix * Add 'Look and Feel' and 'Tone of voice' pages'; * Use correct classname * Minor changes * Textearea color should be gray-900 after the user enter text * guide font weight should be 500 * Fix layout shift when a choice is selected * Fix choices width for tone of voice page * Use context value for the default * Revert button margin top * Fix default selection * Add X button * Decrease the margin by 20px to accommodate the height of the close button * Add close action * Include @woocommerce/ai package * Add AI service * Use AI service * Parse JSON from in function * Fix assignLookAndTone event type * Update plugins/woocommerce-admin/client/customize-store/design-with-ai/components/choice/choice.scss Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> * Update plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> * Log when AI API endpoint request fails * Add spinner when user clicks the continue button * streamlined unnecessary isRequesting context and forwarded close event * pnpm-lock changes from trunk * lint fixes * ai package test passWithNoTests * changelog * reset pnpm-lock to trunk * Dev: update pnpm-lock.yaml and jest preset config (#40045) * Update pnpm-lock.yaml * Update jest-preset config to fix unexpected token error --------- Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> Co-authored-by: rjchow <me@rjchow.com>
2023-09-06 06:21:09 +00:00
} );
};
export const queryAiEndpoint = createMachine(
{
id: 'query-ai-endpoint',
predictableActionArguments: true,
initial: 'init',
context: {
// these values are all overwritten by incoming parameters
prompt: '',
queryId: '',
version: '',
responseValidation: () => true,
retryCount: 0,
validatedResponse: {} as unknown,
},
states: {
init: {
always: 'querying',
entry: [ 'setRetryCount' ],
},
querying: {
invoke: {
src: 'getCompletion',
onDone: {
target: 'success',
actions: [ 'handleAiResponse' ],
},
onError: {
target: 'error',
},
},
},
error: {
always: [
{
cond: ( context ) => context.retryCount >= 3,
target: 'failed',
},
{
target: 'querying',
actions: assign( {
retryCount: ( context ) => context.retryCount + 1,
} ),
},
],
},
failed: {
type: 'final',
data: {
result: 'failed',
},
},
success: {
type: 'final',
data: ( context ) => {
return {
result: 'success',
response: context.validatedResponse,
};
},
},
},
},
{
actions: {
handleAiResponse: assign( {
validatedResponse: ( _context, event: unknown ) =>
( event as { data: unknown } ).data,
} ),
setRetryCount: assign( {
retryCount: 0,
} ),
},
services: {
getCompletion,
},
}
);
export const updateStorePatterns = async (
context: designWithAiStateMachineContext
) => {
try {
// TODO: Probably move this to a more appropriate place with a check. We should set this when the user granted permissions during the onboarding phase.
await dispatch( OPTIONS_STORE_NAME ).updateOptions( {
woocommerce_blocks_allow_ai_connection: true,
} );
await apiFetch( {
path: '/wc/store/patterns',
method: 'POST',
data: {
business_description:
context.businessInfoDescription.descriptionText,
},
} );
} catch ( error ) {
recordEvent( 'customize_your_store_update_store_pattern_api_error', {
error: error instanceof Error ? error.message : 'unknown',
} );
throw error;
}
};
// Update the current global styles of theme
const updateGlobalStyles = async ( {
colorPaletteName = COLOR_PALETTES[ 0 ].title,
fontPairingName = FONT_PAIRINGS[ 0 ].title,
}: {
colorPaletteName: string;
fontPairingName: string;
} ) => {
const colorPalette = COLOR_PALETTES.find(
( palette ) => palette.title === colorPaletteName
);
const fontPairing = FONT_PAIRINGS.find(
( pairing ) => pairing.title === fontPairingName
);
const globalStylesId = await resolveSelect(
coreStore
// @ts-ignore No types for this exist yet.
).__experimentalGetCurrentGlobalStylesId();
// @ts-ignore No types for this exist yet.
const { saveEntityRecord } = dispatch( coreStore );
await saveEntityRecord(
'root',
'globalStyles',
{
id: globalStylesId,
styles: mergeBaseAndUserConfigs(
colorPalette?.styles || {},
fontPairing?.styles || {}
),
settings: mergeBaseAndUserConfigs(
colorPalette?.settings || {},
fontPairing?.settings || {}
),
},
{
throwOnError: true,
}
);
};
// Update the current theme template
const updateTemplate = async ( {
headerSlug,
businessSize,
homepageTemplateId,
footerSlug,
}: {
headerSlug: string;
businessSize: 'SMB' | 'LB';
homepageTemplateId:
| keyof typeof SMALL_MEDIUM_BUSINESS_TEMPLATES
| keyof typeof LARGE_BUSINESS_TEMPLATES;
footerSlug: string;
} ) => {
const patterns = ( await resolveSelect(
coreStore
// @ts-ignore No types for this exist yet.
).getBlockPatterns() ) as Pattern[];
const patternsByName = patternsToNameMap( patterns );
const headerPattern = patternsByName[ headerSlug ];
const footerPattern = patternsByName[ footerSlug ];
const homepageTemplate = getTemplatePatterns(
businessSize === 'SMB'
? SMALL_MEDIUM_BUSINESS_TEMPLATES[ homepageTemplateId ]
: LARGE_BUSINESS_TEMPLATES[ homepageTemplateId ],
patternsByName
);
const content = [ headerPattern, ...homepageTemplate, footerPattern ]
.filter( Boolean )
.map( ( pattern ) => pattern.content )
.join( '\n\n' );
const currentTemplate = await resolveSelect(
coreStore
// @ts-ignore No types for this exist yet.
).__experimentalGetTemplateForLink( '/' );
// @ts-ignore No types for this exist yet.
const { saveEntityRecord } = dispatch( coreStore );
await saveEntityRecord(
'postType',
currentTemplate.type,
{
id: currentTemplate.id,
content,
},
{
throwOnError: true,
}
);
};
export const assembleSite = async (
context: designWithAiStateMachineContext
) => {
try {
await updateGlobalStyles( {
colorPaletteName: context.aiSuggestions.defaultColorPalette.default,
fontPairingName: context.aiSuggestions.fontPairing,
} );
recordEvent( 'customize_your_store_ai_update_global_styles_success' );
} catch ( error ) {
// TODO handle error
// eslint-disable-next-line no-console
console.error( error );
recordEvent(
'customize_your_store_ai_update_global_styles_response_error',
{
error: error instanceof Error ? error.message : 'unknown',
}
);
}
try {
await updateTemplate( {
headerSlug: context.aiSuggestions.header,
// TODO: Get from context
businessSize: 'SMB',
homepageTemplateId: 'template1',
footerSlug: context.aiSuggestions.footer,
} );
recordEvent( 'customize_your_store_ai_update_template_success' );
} catch ( error ) {
// TODO handle error
// eslint-disable-next-line no-console
console.error( error );
recordEvent( 'customize_your_store_ai_update_template_response_error', {
error: error instanceof Error ? error.message : 'unknown',
} );
}
// @ts-ignore No types for this exist yet.
const { invalidateResolutionForStoreSelector } = dispatch( coreStore );
// Invalid the selectors so that the new template/style are used in assembler hub.
invalidateResolutionForStoreSelector( 'getEntityRecord' );
invalidateResolutionForStoreSelector(
'__experimentalGetCurrentGlobalStylesId'
);
invalidateResolutionForStoreSelector( '__experimentalGetTemplateForLink' );
};
const saveAiResponseToOption = ( context: designWithAiStateMachineContext ) => {
return dispatch( OPTIONS_STORE_NAME ).updateOptions( {
woocommerce_customize_store_ai_suggestions: context.aiSuggestions,
} );
};
CYS - Add LookAndFeel and ToneOfVoice pages (#39979) * Add ProgressBar component to @woocommerce/components * Add changelog * Remove html.wp-toolbar in fullscreen mode * Add base style * Add Tell us a bit more about your business page * Fix merge conflict issues * Send BUSINESS_INFO_DESCRIPTION_COMPLETE event when continue button is clicked * Remove duplicated style import * Add changefile(s) from automation for the following project(s): @woocommerce/components, woocommerce * Lint fix * Add 'Look and Feel' and 'Tone of voice' pages'; * Use correct classname * Minor changes * Textearea color should be gray-900 after the user enter text * guide font weight should be 500 * Fix layout shift when a choice is selected * Fix choices width for tone of voice page * Use context value for the default * Revert button margin top * Fix default selection * Add X button * Decrease the margin by 20px to accommodate the height of the close button * Add close action * Include @woocommerce/ai package * Add AI service * Use AI service * Parse JSON from in function * Fix assignLookAndTone event type * Update plugins/woocommerce-admin/client/customize-store/design-with-ai/components/choice/choice.scss Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> * Update plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> * Log when AI API endpoint request fails * Add spinner when user clicks the continue button * streamlined unnecessary isRequesting context and forwarded close event * pnpm-lock changes from trunk * lint fixes * ai package test passWithNoTests * changelog * reset pnpm-lock to trunk * Dev: update pnpm-lock.yaml and jest preset config (#40045) * Update pnpm-lock.yaml * Update jest-preset config to fix unexpected token error --------- Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> Co-authored-by: rjchow <me@rjchow.com>
2023-09-06 06:21:09 +00:00
export const services = {
getLookAndTone,
browserPopstateHandler,
queryAiEndpoint,
assembleSite,
updateStorePatterns,
saveAiResponseToOption,
CYS - Add LookAndFeel and ToneOfVoice pages (#39979) * Add ProgressBar component to @woocommerce/components * Add changelog * Remove html.wp-toolbar in fullscreen mode * Add base style * Add Tell us a bit more about your business page * Fix merge conflict issues * Send BUSINESS_INFO_DESCRIPTION_COMPLETE event when continue button is clicked * Remove duplicated style import * Add changefile(s) from automation for the following project(s): @woocommerce/components, woocommerce * Lint fix * Add 'Look and Feel' and 'Tone of voice' pages'; * Use correct classname * Minor changes * Textearea color should be gray-900 after the user enter text * guide font weight should be 500 * Fix layout shift when a choice is selected * Fix choices width for tone of voice page * Use context value for the default * Revert button margin top * Fix default selection * Add X button * Decrease the margin by 20px to accommodate the height of the close button * Add close action * Include @woocommerce/ai package * Add AI service * Use AI service * Parse JSON from in function * Fix assignLookAndTone event type * Update plugins/woocommerce-admin/client/customize-store/design-with-ai/components/choice/choice.scss Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> * Update plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> * Log when AI API endpoint request fails * Add spinner when user clicks the continue button * streamlined unnecessary isRequesting context and forwarded close event * pnpm-lock changes from trunk * lint fixes * ai package test passWithNoTests * changelog * reset pnpm-lock to trunk * Dev: update pnpm-lock.yaml and jest preset config (#40045) * Update pnpm-lock.yaml * Update jest-preset config to fix unexpected token error --------- Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com> Co-authored-by: rjchow <me@rjchow.com>
2023-09-06 06:21:09 +00:00
};