Skip to content

Commit

Permalink
AI Assistant: Add image index information on caption generation reque…
Browse files Browse the repository at this point in the history
…st (#42274)

* Add getAllBlocks and getSerializedPostContent lib functions

* add image index for caption requests

* changelog

* treat response for common parsing error

* change indexes to positions

* change positions to block clientId
  • Loading branch information
dhasilva authored Mar 7, 2025
1 parent ee422b5 commit 671754d
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: added
Comment: Added utility functions


18 changes: 13 additions & 5 deletions projects/js-packages/ai-client/src/hooks/use-post-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,24 @@ const usePostContent = () => {
};
}, [] );

const getSerializedPostContent = useCallback( () => {
const blocks = getBlocks();

if ( blocks.length === 0 ) {
return '';
}

return serialize( blocks );
}, [ getBlocks ] );

const getPostContent = useCallback(
( preprocess?: ( serialized: string ) => string ) => {
const blocks = getBlocks();
let serialized = getSerializedPostContent();

if ( blocks.length === 0 ) {
if ( ! serialized ) {
return '';
}

let serialized = serialize( blocks );

if ( preprocess && typeof preprocess === 'function' ) {
serialized = preprocess( serialized );
}
Expand All @@ -47,7 +55,7 @@ const usePostContent = () => {
[ getBlocks ]
);

return { getPostContent, isEditedPostEmpty };
return { getPostContent, isEditedPostEmpty, getSerializedPostContent };
};

export default usePostContent;
29 changes: 29 additions & 0 deletions projects/js-packages/ai-client/src/libs/get-all-blocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* External dependencies
*/
import { select } from '@wordpress/data';
/**
* Internal dependencies
*/
import type { Block } from '../types.js';

/**
* Recursively get all blocks from the post, including nested innerBlocks
* @return {Array} Array of all blocks in the post
*/
export const getAllBlocks = (): Array< Block > => {
const topLevelBlocks = select( 'core/block-editor' ).getBlocks();
const allBlocks: Array< Block > = [];

const processBlock = ( block: Block ) => {
allBlocks.push( block );

if ( block.innerBlocks?.length ) {
block.innerBlocks.forEach( processBlock );
}
};

topLevelBlocks.forEach( processBlock );

return allBlocks;
};
2 changes: 2 additions & 0 deletions projects/js-packages/ai-client/src/libs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export { mapActionToHumanText } from './map-action-to-human-text.js';
export { openBlockSidebar } from './open-block-sidebar.js';

export { showAiAssistantSection } from './show-ai-assistant-section.js';

export { getAllBlocks } from './get-all-blocks.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: other

AI Assistant: Add image index information on caption generation request
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,16 @@ const blockEditWithAiComponents = createHigherOrderComponent( BlockEdit => {
startLoading( type );

try {
const context: { positions?: number[] } = {};

if ( type === TYPE_ALT_TEXT ) {
openBlockSidebar( props.clientId );
}

if ( type === TYPE_CAPTION ) {
context.positions = [ props.clientId ];
}

dequeueAsyncRequest();

const response = await askQuestionSync(
Expand All @@ -150,8 +156,7 @@ const blockEditWithAiComponents = createHigherOrderComponent( BlockEdit => {
context: {
type: type,
content: getPostContent( preprocessImageContent ),
// URL of the image for the AI to find where the image is in the post.
urls: [ props.attributes.url ],
...context,
images: [
{
// We convert the image to a base64 string to avoid inaccesible URLs for private images.
Expand All @@ -169,7 +174,11 @@ const blockEditWithAiComponents = createHigherOrderComponent( BlockEdit => {

increaseRequestsCount();

const parsedResponse: { texts?: string[]; captions?: string[] } = JSON.parse( response );
const parsedResponse: { texts?: string[]; captions?: string[] } = JSON.parse(
response
?.replace?.( /^```json\s*/, '' ) // Remove the markdown code block if it exists.
?.replace( /```$/, '' )
);

if ( type === TYPE_ALT_TEXT ) {
const alt = parsedResponse.texts?.[ 0 ];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
/**
* Preprocess the serialized post content to remove image URLs and alt text, adding a unique count to each image.
* This is used to ensure that the AI is not confused by the URLs in the post content.
* External dependencies
*/
import { getAllBlocks } from '@automattic/jetpack-ai-client';

/**
* Preprocess the serialized post content to remove image captions and alt text, changing the image urls to their clientId.
* This is used to ensure that the AI is not confused by URLs in the post content, while still being able to identify the image blocks.
* @param {string} content - The content to preprocess.
* @return {string} The preprocessed content.
*/
export const preprocessImageContent = ( content: string ): string => {
let imageCounter = 0;
const imageBlocks = getAllBlocks().filter( block => block.name === 'core/image' );
let currentImageIndex = 0;

// Remove figcaption elements
content = content.replace( /<figcaption[^>]*>.*?<\/figcaption>/g, '' );

// Replace image URLs with a unique count and remove alt text
content = content.replace( /<img[^>]*>/g, () => {
imageCounter++;
// Replace the urls of images within image blocks with their clientId
content = content.replace( /<!-- wp:image[^>]*>.*?<img[^>]*>.*?<!-- \/wp:image -->/gs, () => {
// The assumption here is that the image blocks are always in the same order as the images in the post's HTML content
const imageBlock = imageBlocks[ currentImageIndex ];
currentImageIndex++;

if ( ! imageBlock ) {
return '';
}

return `<img src="image-${ imageCounter }">`;
return `<img src="${ imageBlock.clientId }">`;
} );

return content;
Expand Down

0 comments on commit 671754d

Please sign in to comment.