diff --git a/plugins/speculation-rules/hooks.php b/plugins/speculation-rules/hooks.php index fa478b9843..43bf5f1b9b 100644 --- a/plugins/speculation-rules/hooks.php +++ b/plugins/speculation-rules/hooks.php @@ -38,3 +38,21 @@ function plsr_render_generator_meta_tag(): void { echo '' . "\n"; } add_action( 'wp_head', 'plsr_render_generator_meta_tag' ); + +/** + * Load the predict.js script which will uses on-device AI to predict links users are most likely to visit for prerendering. + * + * @since n.e.x.t + */ +function plsr_load_predict_script(): void { + wp_enqueue_script( + 'plsr-predict', + plugin_dir_url( __FILE__ ) . 'predict.js', // @todo switch to build version. + array(), + SPECULATION_RULES_VERSION, + array( + 'strategy' => 'defer', + ) + ); +} +add_action( 'wp_enqueue_scripts', 'plsr_load_predict_script' ); diff --git a/plugins/speculation-rules/predict.js b/plugins/speculation-rules/predict.js new file mode 100644 index 0000000000..3e1306032a --- /dev/null +++ b/plugins/speculation-rules/predict.js @@ -0,0 +1,97 @@ +( async () => { + // Start the AI engine. + // eslint-disable-next-line no-console -- For testing only. + console.log( 'Starting...' ); + + async function predictLinks() { + const session = await window.ai.assistant.create(); + + const body = document.getElementsByTagName( 'body' )[ 0 ].innerHTML; + + //const prompt = `Predict the five links users are most likely to visit on this page, returning ONLY the 5 full urls as a JSON object: ${ body }`; + // const prompt = `Given a web page's HTML, find the links and predict which is most likely to be clicked by a user. Return the top 5 links as a JSON object. Only return the JSON object as your complete answer. Here is the HTML: ${ body }`; + const prompt = `In order to prerender links so users get instant navigations, can you predict the three links users are most likely to visit on this page, returning ONLY the 3 full urls as a JSON object in the format ['url','url2','url3']. Here is the HTML: ${ body }`; + + // eslint-disable-next-line no-console -- For testing only. + console.log( `The size of the prompt is ${ prompt.length }.` ); + + let result = false; + try { + result = await session.prompt( prompt ); + } catch ( error ) { + // eslint-disable-next-line no-console -- For testing only. + console.error( error ); + return; + } + + if ( ! result ) { + // eslint-disable-next-line no-console -- For testing only. + console.log( 'No result.' ); + return; + } + + // Log result so far + // eslint-disable-next-line no-console -- For testing only. + console.log( result ); + + // Grab everything after "Output:" or "```json" strings etc... + const output = + result.split( 'Output:' )[ 1 ] || + result.split( '```json' )[ 1 ] || + result.split( 'Answer:' )[ 1 ] || + result.split( 'Solution:' )[ 1 ]; + + // If there is no output, return. + if ( ! output ) { + // eslint-disable-next-line no-console -- For testing only. + console.log( 'No output.' ); + return; + } + + // Remove the two "```" formatting strings. + result = output.replace( /```JSON/g, '' ); + result = output.replace( /```/g, '' ); + + // Remove any newlines. + result = result.replace( /\n/g, '' ); + + // Remove any whitespace. + result = result.replace( / /g, '' ); + + // eslint-disable-next-line no-console -- For testing only. + console.log( result ); + + // Add a prerender link for each URL using the Speculation Rules API + const resultData = JSON.parse( result ); + + for ( const link of resultData.links ) { + // eslint-disable-next-line no-console -- For testing only. + console.log( `Link: ${ link }` ); + /** + * Add speculation rules API prerendering. + * + * It will be something like this: + * + * + */ + const prerenderLink = document.createElement( 'link' ); + prerenderLink.type = 'speculationrules'; + const rules = + '{ "prerender": [ { "where": { "href_matches": "URL" }, "eagerness": "moderate" }] }'; + prerenderLink.textContent = rules.replace( 'URL', link.href ); + document.head.appendChild( prerenderLink ); + + // eslint-disable-next-line no-console -- For testing only. + console.log( `Prerendering link: ${ link }` ); + } + } + await predictLinks(); +} )();