@automattic/jetpack-ai-client 0.10.0 → 0.11.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 CHANGED
@@ -5,6 +5,19 @@ 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.11.0] - 2024-04-01
9
+ ### Added
10
+ - AI Client: include prompt to generate featured image based on post content. [#36591]
11
+ - Support different responses in image hook [#36626]
12
+
13
+ ### Fixed
14
+ - AI Client: fix a bug where quick prompts would not work after getting suggested content [#36651]
15
+ - AI Client: set request content type as JSON on image generation hook and use rectangular images instead of square images. [#36620]
16
+
17
+ ## [0.10.1] - 2024-03-27
18
+ ### Changed
19
+ - Updated package dependencies. [#36539, #36585]
20
+
8
21
  ## [0.10.0] - 2024-03-18
9
22
  ### Added
10
23
  - Add image generator hook [#36415]
@@ -259,6 +272,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
259
272
  - Updated package dependencies. [#31659]
260
273
  - Updated package dependencies. [#31785]
261
274
 
275
+ [0.11.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.10.1...v0.11.0
276
+ [0.10.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.10.0...v0.10.1
262
277
  [0.10.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.9.0...v0.10.0
263
278
  [0.9.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.8.2...v0.9.0
264
279
  [0.8.2]: https://github.com/Automattic/jetpack-ai-client/compare/v0.8.1...v0.8.2
@@ -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 { forwardRef } from 'react';
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
- if (!editRequest && lastValue !== null && value !== lastValue) {
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: Array<{
6
- url: string;
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,16 +19,30 @@ const useImageGenerator = () => {
19
19
  }
20
20
  try {
21
21
  debug('Generating image');
22
- // TODO: Find a proper prompt for the image generation
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;
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: 'url',
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',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@automattic/jetpack-ai-client",
4
- "version": "0.10.0",
4
+ "version": "0.11.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": "7.6.17",
26
- "@storybook/blocks": "7.6.17",
27
- "@storybook/react": "7.6.17",
26
+ "@storybook/addon-actions": "8.0.4",
27
+ "@storybook/blocks": "8.0.4",
28
+ "@storybook/react": "8.0.4",
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.19",
42
- "@automattic/jetpack-connection": "^0.33.3",
43
- "@automattic/jetpack-shared-extension-utils": "^0.14.7",
42
+ "@automattic/jetpack-base-styles": "^0.6.20",
43
+ "@automattic/jetpack-connection": "^0.33.5",
44
+ "@automattic/jetpack-shared-extension-utils": "^0.14.8",
44
45
  "@microsoft/fetch-event-source": "2.0.1",
45
46
  "@types/react": "18.2.61",
46
- "@wordpress/api-fetch": "6.50.0",
47
- "@wordpress/block-editor": "12.21.0",
48
- "@wordpress/components": "27.1.0",
49
- "@wordpress/compose": "6.30.0",
50
- "@wordpress/data": "9.23.0",
51
- "@wordpress/element": "5.30.0",
52
- "@wordpress/i18n": "4.53.0",
53
- "@wordpress/icons": "9.44.0",
47
+ "@wordpress/api-fetch": "6.51.0",
48
+ "@wordpress/block-editor": "12.22.0",
49
+ "@wordpress/components": "27.2.0",
50
+ "@wordpress/compose": "6.31.0",
51
+ "@wordpress/data": "9.24.0",
52
+ "@wordpress/element": "5.31.0",
53
+ "@wordpress/i18n": "4.54.0",
54
+ "@wordpress/icons": "9.45.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 { forwardRef } from 'react';
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
- } ): Promise< { data: Array< { url: string } > } > {
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: Find a proper prompt for the image generation
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;
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: 'url',
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,7 +67,7 @@ const useImageGenerator = () => {
48
67
  body: JSON.stringify( body ),
49
68
  } ).then( response => response.json() );
50
69
 
51
- return data as { data: { url: string }[] };
70
+ return data as { data: { [ key: string ]: string }[] };
52
71
  } catch ( error ) {
53
72
  return;
54
73
  }