Skip to content

Commit

Permalink
AI Assistant: Load Breve spelling dictionaries from CDN (#38943)
Browse files Browse the repository at this point in the history
* add function to get dictionary from endpoint

* load the spelling dictionary context from the backend

* changelog

* change endpoint to CDN

* update changelog
  • Loading branch information
dhasilva authored Aug 21, 2024
1 parent 8788f2c commit e2e6b98
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: other

AI Assistant: Load dictionaries from CDN
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,23 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import debugFactory from 'debug';
import nspell from 'nspell';
/**
* Internal dependencies
*/
import getDictionary from '../../utils/get-dictionary';
/**
* Types
*/
import type { BreveFeatureConfig, HighlightedText, SpellChecker } from '../../types';
import type {
BreveFeatureConfig,
SpellingDictionaryContext,
HighlightedText,
SpellChecker,
} from '../../types';

const debug = debugFactory( 'jetpack-ai-breve:spelling-mistakes' );

export const SPELLING_MISTAKES: BreveFeatureConfig = {
name: 'spelling-mistakes',
Expand All @@ -17,16 +29,46 @@ export const SPELLING_MISTAKES: BreveFeatureConfig = {
};

const spellcheckers: { [ key: string ]: SpellChecker } = {};
const spellingContexts: {
[ key: string ]: {
affix: string;
dictionary: string;
};
const contextRequests: {
[ key: string ]: { loading: boolean; loaded: boolean; failed: boolean };
} = {};

const loadContext = ( language: string ) => {
// TODO: Load dictionaries dynamically and save on localStorage
return spellingContexts[ language ];
const fetchContext = async ( language: string ) => {
debug( 'Fetching spelling context from the server' );

try {
contextRequests[ language ] = { loading: true, loaded: false, failed: false };
const data = await getDictionary( SPELLING_MISTAKES.name, language );

localStorage.setItem(
`jetpack-ai-breve-spelling-context-${ language }`,
JSON.stringify( data )
);

contextRequests[ language ] = { loading: false, loaded: true, failed: false };
debug( 'Loaded spelling context from the server' );
} catch ( error ) {
debug( 'Failed to fetch spelling context', error );
contextRequests[ language ] = { loading: false, loaded: false, failed: true };
// TODO: Handle retries
}
};

const getContext = ( language: string ) => {
// First check if the context is already defined in local storage
const storedContext = localStorage.getItem( `jetpack-ai-breve-spelling-context-${ language }` );
let context: SpellingDictionaryContext | null = null;
const { loading, failed } = contextRequests[ language ] || {};

if ( storedContext ) {
context = JSON.parse( storedContext );
debug( 'Loaded spelling context from local storage' );
} else if ( ! loading && ! failed ) {
// If the context is not in local storage and we haven't failed to fetch it before, try to fetch it once
fetchContext( language );
}

return context;
};

const getSpellchecker = ( { language = 'en' }: { language?: string } = {} ) => {
Expand All @@ -36,7 +78,7 @@ const getSpellchecker = ( { language = 'en' }: { language?: string } = {} ) => {

// Cannot await here as the Rich Text function needs to be synchronous.
// Load of the dictionary in the background if necessary and re-trigger the highlights later.
const spellingContext = loadContext( language );
const spellingContext = getContext( language );

if ( ! spellingContext ) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,8 @@ export type SpellChecker = {
correct: ( word: string ) => boolean;
suggest: ( word: string ) => Array< string >;
};

export type SpellingDictionaryContext = {
affix: string;
dictionary: string;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* External dependencies
*/
import debugFactory from 'debug';
/**
* Types
*/
import { SpellingDictionaryContext } from '../types';

const debug = debugFactory( 'jetpack-ai-breve:get-dictionary' );

/**
* A function that gets a Breve dictionary context object.
*
* @param {string} type
* @param {string} language
* @return {Promise<SpellingDictionaryContext>} - A promise that resolves a dictionary context object.
*/
export default async function getDictionary(
type: string,
language: string
): Promise< SpellingDictionaryContext > {
debug( 'Asking dictionary for type: %s. language: %s', type, language );

// Randomize the server to balance the load
const counter = Math.floor( Math.random() * 3 );
const url = `https://s${ counter }.wp.com/wp-content/lib/jetpack-ai/breve-dictionaries/${ type }/${ language }.json`;

try {
const data = await fetch( url );

if ( data.status === 404 ) {
throw new Error( 'The requested dictionary does not exist' );
} else if ( data.status !== 200 ) {
throw new Error( 'Failed to fetch dictionary' );
}

return data.json();
} catch ( error ) {
debug( 'Error getting dictionary: %o', error );
return Promise.reject( error );
}
}

0 comments on commit e2e6b98

Please sign in to comment.