@automattic/jetpack-ai-client 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/build/components/ai-control/index.js +5 -6
- package/build/hooks/use-image-generator/index.d.ts +6 -4
- package/build/hooks/use-image-generator/index.js +24 -5
- package/package.json +18 -17
- package/src/components/ai-control/index.tsx +6 -7
- package/src/hooks/use-image-generator/index.ts +31 -6
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.12.0] - 2024-04-08
|
|
9
|
+
### Added
|
|
10
|
+
- Add error rejection in image generation. [#36709]
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Updated package dependencies. [#36756] [#36760] [#36761]
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- AI Featured Image: handle posts longer than the limit of Dall-e generation prompt. [#36703]
|
|
17
|
+
|
|
18
|
+
## [0.11.0] - 2024-04-01
|
|
19
|
+
### Added
|
|
20
|
+
- AI Client: include prompt to generate featured image based on post content. [#36591]
|
|
21
|
+
- Support different responses in image hook [#36626]
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- AI Client: fix a bug where quick prompts would not work after getting suggested content [#36651]
|
|
25
|
+
- AI Client: set request content type as JSON on image generation hook and use rectangular images instead of square images. [#36620]
|
|
26
|
+
|
|
8
27
|
## [0.10.1] - 2024-03-27
|
|
9
28
|
### Changed
|
|
10
29
|
- Updated package dependencies. [#36539, #36585]
|
|
@@ -263,6 +282,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
263
282
|
- Updated package dependencies. [#31659]
|
|
264
283
|
- Updated package dependencies. [#31785]
|
|
265
284
|
|
|
285
|
+
[0.12.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.11.0...v0.12.0
|
|
286
|
+
[0.11.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.10.1...v0.11.0
|
|
266
287
|
[0.10.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.10.0...v0.10.1
|
|
267
288
|
[0.10.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.9.0...v0.10.0
|
|
268
289
|
[0.9.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.8.2...v0.9.0
|
|
@@ -9,8 +9,8 @@ import { useImperativeHandle, useRef, useEffect, useCallback } from '@wordpress/
|
|
|
9
9
|
import { __ } from '@wordpress/i18n';
|
|
10
10
|
import { Icon, closeSmall, check, arrowUp, trash, reusableBlock } from '@wordpress/icons';
|
|
11
11
|
import classNames from 'classnames';
|
|
12
|
-
import
|
|
13
|
-
import React from 'react';
|
|
12
|
+
import debugFactory from 'debug';
|
|
13
|
+
import React, { forwardRef } from 'react';
|
|
14
14
|
/**
|
|
15
15
|
* Internal dependencies
|
|
16
16
|
*/
|
|
@@ -19,6 +19,7 @@ import AiStatusIndicator from '../ai-status-indicator/index.js';
|
|
|
19
19
|
import { GuidelineMessage } from './message.js';
|
|
20
20
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
21
21
|
const noop = () => { };
|
|
22
|
+
const debug = debugFactory('jetpack-ai-client:ai-control');
|
|
22
23
|
/**
|
|
23
24
|
* AI Control component.
|
|
24
25
|
*
|
|
@@ -35,10 +36,7 @@ export function AIControl({ disabled = false, value = '', placeholder = '', show
|
|
|
35
36
|
if (editRequest) {
|
|
36
37
|
promptUserInputRef?.current?.focus();
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
-
onChange?.(lastValue);
|
|
40
|
-
}
|
|
41
|
-
}, [editRequest, lastValue, value]);
|
|
39
|
+
}, [editRequest]);
|
|
42
40
|
const sendRequest = useCallback(() => {
|
|
43
41
|
setLastValue(value);
|
|
44
42
|
setEditRequest(false);
|
|
@@ -62,6 +60,7 @@ export function AIControl({ disabled = false, value = '', placeholder = '', show
|
|
|
62
60
|
onDiscard?.();
|
|
63
61
|
}, []);
|
|
64
62
|
const cancelEdit = useCallback(() => {
|
|
63
|
+
debug('cancelEdit, revert to last value', lastValue);
|
|
65
64
|
onChange(lastValue || '');
|
|
66
65
|
setEditRequest(false);
|
|
67
66
|
}, [lastValue]);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
declare const useImageGenerator: () => {
|
|
2
|
-
generateImage: ({ feature, }: {
|
|
2
|
+
generateImage: ({ feature, postContent, responseFormat, }: {
|
|
3
3
|
feature: string;
|
|
4
|
+
postContent: string;
|
|
5
|
+
responseFormat?: 'url' | 'b64_json';
|
|
4
6
|
}) => Promise<{
|
|
5
|
-
data:
|
|
6
|
-
|
|
7
|
-
}
|
|
7
|
+
data: {
|
|
8
|
+
[key: string]: string;
|
|
9
|
+
}[];
|
|
8
10
|
}>;
|
|
9
11
|
};
|
|
10
12
|
export default useImageGenerator;
|
|
@@ -8,7 +8,7 @@ import debugFactory from 'debug';
|
|
|
8
8
|
import requestJwt from '../../jwt/index.js';
|
|
9
9
|
const debug = debugFactory('ai-client:use-image-generator');
|
|
10
10
|
const useImageGenerator = () => {
|
|
11
|
-
const generateImage = async function ({ feature, }) {
|
|
11
|
+
const generateImage = async function ({ feature, postContent, responseFormat = 'url', }) {
|
|
12
12
|
let token = '';
|
|
13
13
|
try {
|
|
14
14
|
token = (await requestJwt()).token;
|
|
@@ -19,26 +19,45 @@ const useImageGenerator = () => {
|
|
|
19
19
|
}
|
|
20
20
|
try {
|
|
21
21
|
debug('Generating image');
|
|
22
|
-
// TODO:
|
|
23
|
-
const imageGenerationPrompt =
|
|
22
|
+
// TODO: fine tune the prompt as we move forward
|
|
23
|
+
const imageGenerationPrompt = `I need a cover image for a blog post.
|
|
24
|
+
Before creating the image, identify the main topic of the content and only represent it.
|
|
25
|
+
Do not represent the whole content in one image, keep it simple and just represent one single idea.
|
|
26
|
+
Do not add details, detailed explanations or highlights from the content, just represent the main idea as if it was a photograph.
|
|
27
|
+
Do not use collages or compositions with multiple elements or scenes. Stick to one single scene. Do not compose unrealistic scenes.
|
|
28
|
+
If the content describes facts, objects or concepts from the real world, represent them on a realistic style and do not make unreal compositions.
|
|
29
|
+
If the content is more abstract, use a more abstract style to represent the main idea.
|
|
30
|
+
Make sure the light and the style are visually appealing.
|
|
31
|
+
Do not add text to the image.
|
|
32
|
+
|
|
33
|
+
This is the post content:
|
|
34
|
+
|
|
35
|
+
` + (postContent.length > 3000 ? postContent.substring(0, 3000) + ` [...]` : postContent); // truncating the content so the whole prompt is not longer than 4000 characters, the model limit.
|
|
24
36
|
const URL = 'https://public-api.wordpress.com/wpcom/v2/jetpack-ai-image';
|
|
25
37
|
const body = {
|
|
26
38
|
prompt: imageGenerationPrompt,
|
|
27
|
-
response_format:
|
|
39
|
+
response_format: responseFormat,
|
|
28
40
|
feature,
|
|
41
|
+
size: '1792x1024',
|
|
29
42
|
};
|
|
30
43
|
const headers = {
|
|
31
44
|
Authorization: `Bearer ${token}`,
|
|
45
|
+
'Content-Type': 'application/json',
|
|
32
46
|
};
|
|
33
47
|
const data = await fetch(URL, {
|
|
34
48
|
method: 'POST',
|
|
35
49
|
headers,
|
|
36
50
|
body: JSON.stringify(body),
|
|
37
51
|
}).then(response => response.json());
|
|
52
|
+
if (data?.data?.status && data?.data?.status > 200) {
|
|
53
|
+
debug('Error generating image: %o', data);
|
|
54
|
+
return Promise.reject(data);
|
|
55
|
+
}
|
|
38
56
|
return data;
|
|
39
57
|
}
|
|
40
58
|
catch (error) {
|
|
41
|
-
|
|
59
|
+
debug('Error generating image: %o', error);
|
|
60
|
+
return Promise.reject(error);
|
|
42
61
|
}
|
|
43
62
|
};
|
|
44
63
|
return {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "@automattic/jetpack-ai-client",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.12.0",
|
|
5
5
|
"description": "A JS client for consuming Jetpack AI services",
|
|
6
6
|
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/ai-client/#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -18,13 +18,14 @@
|
|
|
18
18
|
"build": "pnpm run clean && pnpm run compile-ts",
|
|
19
19
|
"clean": "rm -rf build/",
|
|
20
20
|
"compile-ts": "tsc --pretty",
|
|
21
|
-
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
|
|
21
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
22
|
+
"watch": "tsc --watch --pretty"
|
|
22
23
|
},
|
|
23
24
|
"type": "module",
|
|
24
25
|
"devDependencies": {
|
|
25
|
-
"@storybook/addon-actions": "8.0.
|
|
26
|
-
"@storybook/blocks": "8.0.
|
|
27
|
-
"@storybook/react": "8.0.
|
|
26
|
+
"@storybook/addon-actions": "8.0.6",
|
|
27
|
+
"@storybook/blocks": "8.0.6",
|
|
28
|
+
"@storybook/react": "8.0.6",
|
|
28
29
|
"jest": "^29.6.2",
|
|
29
30
|
"jest-environment-jsdom": "29.7.0",
|
|
30
31
|
"typescript": "5.0.4"
|
|
@@ -38,19 +39,19 @@
|
|
|
38
39
|
"main": "./build/index.js",
|
|
39
40
|
"types": "./build/index.d.ts",
|
|
40
41
|
"dependencies": {
|
|
41
|
-
"@automattic/jetpack-base-styles": "^0.6.
|
|
42
|
-
"@automattic/jetpack-connection": "^0.33.
|
|
43
|
-
"@automattic/jetpack-shared-extension-utils": "^0.14.
|
|
42
|
+
"@automattic/jetpack-base-styles": "^0.6.21",
|
|
43
|
+
"@automattic/jetpack-connection": "^0.33.7",
|
|
44
|
+
"@automattic/jetpack-shared-extension-utils": "^0.14.9",
|
|
44
45
|
"@microsoft/fetch-event-source": "2.0.1",
|
|
45
|
-
"@types/react": "18.2.
|
|
46
|
-
"@wordpress/api-fetch": "6.
|
|
47
|
-
"@wordpress/block-editor": "12.
|
|
48
|
-
"@wordpress/components": "27.
|
|
49
|
-
"@wordpress/compose": "6.
|
|
50
|
-
"@wordpress/data": "9.
|
|
51
|
-
"@wordpress/element": "5.
|
|
52
|
-
"@wordpress/i18n": "4.
|
|
53
|
-
"@wordpress/icons": "9.
|
|
46
|
+
"@types/react": "18.2.74",
|
|
47
|
+
"@wordpress/api-fetch": "6.52.0",
|
|
48
|
+
"@wordpress/block-editor": "12.23.0",
|
|
49
|
+
"@wordpress/components": "27.3.0",
|
|
50
|
+
"@wordpress/compose": "6.32.0",
|
|
51
|
+
"@wordpress/data": "9.25.0",
|
|
52
|
+
"@wordpress/element": "5.32.0",
|
|
53
|
+
"@wordpress/i18n": "4.55.0",
|
|
54
|
+
"@wordpress/icons": "9.46.0",
|
|
54
55
|
"classnames": "2.3.2",
|
|
55
56
|
"debug": "4.3.4",
|
|
56
57
|
"react": "18.2.0",
|
|
@@ -8,8 +8,8 @@ import { useImperativeHandle, useRef, useEffect, useCallback } from '@wordpress/
|
|
|
8
8
|
import { __ } from '@wordpress/i18n';
|
|
9
9
|
import { Icon, closeSmall, check, arrowUp, trash, reusableBlock } from '@wordpress/icons';
|
|
10
10
|
import classNames from 'classnames';
|
|
11
|
-
import
|
|
12
|
-
import React from 'react';
|
|
11
|
+
import debugFactory from 'debug';
|
|
12
|
+
import React, { forwardRef } from 'react';
|
|
13
13
|
/**
|
|
14
14
|
* Internal dependencies
|
|
15
15
|
*/
|
|
@@ -45,6 +45,8 @@ type AiControlProps = {
|
|
|
45
45
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
46
46
|
const noop = () => {};
|
|
47
47
|
|
|
48
|
+
const debug = debugFactory( 'jetpack-ai-client:ai-control' );
|
|
49
|
+
|
|
48
50
|
/**
|
|
49
51
|
* AI Control component.
|
|
50
52
|
*
|
|
@@ -84,11 +86,7 @@ export function AIControl(
|
|
|
84
86
|
if ( editRequest ) {
|
|
85
87
|
promptUserInputRef?.current?.focus();
|
|
86
88
|
}
|
|
87
|
-
|
|
88
|
-
if ( ! editRequest && lastValue !== null && value !== lastValue ) {
|
|
89
|
-
onChange?.( lastValue );
|
|
90
|
-
}
|
|
91
|
-
}, [ editRequest, lastValue, value ] );
|
|
89
|
+
}, [ editRequest ] );
|
|
92
90
|
|
|
93
91
|
const sendRequest = useCallback( () => {
|
|
94
92
|
setLastValue( value );
|
|
@@ -119,6 +117,7 @@ export function AIControl(
|
|
|
119
117
|
}, [] );
|
|
120
118
|
|
|
121
119
|
const cancelEdit = useCallback( () => {
|
|
120
|
+
debug( 'cancelEdit, revert to last value', lastValue );
|
|
122
121
|
onChange( lastValue || '' );
|
|
123
122
|
setEditRequest( false );
|
|
124
123
|
}, [ lastValue ] );
|
|
@@ -12,9 +12,13 @@ const debug = debugFactory( 'ai-client:use-image-generator' );
|
|
|
12
12
|
const useImageGenerator = () => {
|
|
13
13
|
const generateImage = async function ( {
|
|
14
14
|
feature,
|
|
15
|
+
postContent,
|
|
16
|
+
responseFormat = 'url',
|
|
15
17
|
}: {
|
|
16
18
|
feature: string;
|
|
17
|
-
|
|
19
|
+
postContent: string;
|
|
20
|
+
responseFormat?: 'url' | 'b64_json';
|
|
21
|
+
} ): Promise< { data: Array< { [ key: string ]: string } > } > {
|
|
18
22
|
let token = '';
|
|
19
23
|
|
|
20
24
|
try {
|
|
@@ -27,19 +31,34 @@ const useImageGenerator = () => {
|
|
|
27
31
|
try {
|
|
28
32
|
debug( 'Generating image' );
|
|
29
33
|
|
|
30
|
-
// TODO:
|
|
31
|
-
const imageGenerationPrompt =
|
|
34
|
+
// TODO: fine tune the prompt as we move forward
|
|
35
|
+
const imageGenerationPrompt =
|
|
36
|
+
`I need a cover image for a blog post.
|
|
37
|
+
Before creating the image, identify the main topic of the content and only represent it.
|
|
38
|
+
Do not represent the whole content in one image, keep it simple and just represent one single idea.
|
|
39
|
+
Do not add details, detailed explanations or highlights from the content, just represent the main idea as if it was a photograph.
|
|
40
|
+
Do not use collages or compositions with multiple elements or scenes. Stick to one single scene. Do not compose unrealistic scenes.
|
|
41
|
+
If the content describes facts, objects or concepts from the real world, represent them on a realistic style and do not make unreal compositions.
|
|
42
|
+
If the content is more abstract, use a more abstract style to represent the main idea.
|
|
43
|
+
Make sure the light and the style are visually appealing.
|
|
44
|
+
Do not add text to the image.
|
|
45
|
+
|
|
46
|
+
This is the post content:
|
|
47
|
+
|
|
48
|
+
` + ( postContent.length > 3000 ? postContent.substring( 0, 3000 ) + ` [...]` : postContent ); // truncating the content so the whole prompt is not longer than 4000 characters, the model limit.
|
|
32
49
|
|
|
33
50
|
const URL = 'https://public-api.wordpress.com/wpcom/v2/jetpack-ai-image';
|
|
34
51
|
|
|
35
52
|
const body = {
|
|
36
53
|
prompt: imageGenerationPrompt,
|
|
37
|
-
response_format:
|
|
54
|
+
response_format: responseFormat,
|
|
38
55
|
feature,
|
|
56
|
+
size: '1792x1024',
|
|
39
57
|
};
|
|
40
58
|
|
|
41
59
|
const headers = {
|
|
42
60
|
Authorization: `Bearer ${ token }`,
|
|
61
|
+
'Content-Type': 'application/json',
|
|
43
62
|
};
|
|
44
63
|
|
|
45
64
|
const data = await fetch( URL, {
|
|
@@ -48,9 +67,15 @@ const useImageGenerator = () => {
|
|
|
48
67
|
body: JSON.stringify( body ),
|
|
49
68
|
} ).then( response => response.json() );
|
|
50
69
|
|
|
51
|
-
|
|
70
|
+
if ( data?.data?.status && data?.data?.status > 200 ) {
|
|
71
|
+
debug( 'Error generating image: %o', data );
|
|
72
|
+
return Promise.reject( data );
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return data as { data: { [ key: string ]: string }[] };
|
|
52
76
|
} catch ( error ) {
|
|
53
|
-
|
|
77
|
+
debug( 'Error generating image: %o', error );
|
|
78
|
+
return Promise.reject( error );
|
|
54
79
|
}
|
|
55
80
|
};
|
|
56
81
|
|