@automattic/jetpack-ai-client 0.8.2 → 0.10.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 +14 -0
- package/build/api-fetch/index.d.ts +2 -3
- package/build/api-fetch/index.js +5 -1
- package/build/components/ai-control/index.d.ts +2 -2
- package/build/hooks/use-image-generator/index.d.ts +10 -0
- package/build/hooks/use-image-generator/index.js +48 -0
- package/build/hooks/use-media-recording/index.js +19 -4
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/types.d.ts +0 -12
- package/package.json +12 -12
- package/src/api-fetch/index.ts +4 -3
- package/src/components/ai-control/index.tsx +1 -1
- package/src/hooks/use-image-generator/index.ts +62 -0
- package/src/hooks/use-media-recording/index.ts +21 -4
- package/src/index.ts +1 -0
- package/src/types.ts +0 -17
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ 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.10.0] - 2024-03-18
|
|
9
|
+
### Added
|
|
10
|
+
- Add image generator hook [#36415]
|
|
11
|
+
|
|
12
|
+
## [0.9.0] - 2024-03-12
|
|
13
|
+
### Changed
|
|
14
|
+
- Fix typescript errors [#35904]
|
|
15
|
+
- Updated package dependencies. [#36325]
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- AI Client: Fix audio recording where WebM is not supported (iOS for example). [#36160]
|
|
19
|
+
|
|
8
20
|
## [0.8.2] - 2024-03-04
|
|
9
21
|
### Added
|
|
10
22
|
- AI Client: add audio validation hook. [#36043]
|
|
@@ -247,6 +259,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
247
259
|
- Updated package dependencies. [#31659]
|
|
248
260
|
- Updated package dependencies. [#31785]
|
|
249
261
|
|
|
262
|
+
[0.10.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.9.0...v0.10.0
|
|
263
|
+
[0.9.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.8.2...v0.9.0
|
|
250
264
|
[0.8.2]: https://github.com/Automattic/jetpack-ai-client/compare/v0.8.1...v0.8.2
|
|
251
265
|
[0.8.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.8.0...v0.8.1
|
|
252
266
|
[0.8.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.7.0...v0.8.0
|
|
@@ -2,6 +2,5 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import apiFetchMod from '@wordpress/api-fetch';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export default apiFetch;
|
|
5
|
+
declare const _default: typeof apiFetchMod.default;
|
|
6
|
+
export default _default;
|
package/build/api-fetch/index.js
CHANGED
|
@@ -2,5 +2,9 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import apiFetchMod from '@wordpress/api-fetch';
|
|
5
|
-
|
|
5
|
+
// @wordpress/api-fetch (as of 6.47.0) declares itself in such a way that tsc and node see the function at apiFetchMod.default
|
|
6
|
+
// while some other environments (including code running inside WordPress itself) see it at apiFetch.
|
|
7
|
+
// See https://arethetypeswrong.github.io/?p=@wordpress/api-fetch@6.47.0
|
|
8
|
+
// This is a helper to simplify the usage of the api-fetch module on the ai-client package.
|
|
9
|
+
const apiFetch = 'default' in apiFetchMod ? apiFetchMod.default : apiFetchMod;
|
|
6
10
|
export default apiFetch;
|
|
@@ -35,6 +35,6 @@ type AiControlProps = {
|
|
|
35
35
|
* @param {React.MutableRefObject} ref - Ref to the component.
|
|
36
36
|
* @returns {ReactElement} Rendered component.
|
|
37
37
|
*/
|
|
38
|
-
export declare function AIControl({ disabled, value, placeholder, showAccept, acceptLabel, showButtonLabels, isTransparent, state, showGuideLine, customFooter, onChange, onSend, onStop, onAccept, onDiscard, showRemove, bannerComponent, errorComponent, }: AiControlProps, ref: React.MutableRefObject<
|
|
39
|
-
declare const _default: React.ForwardRefExoticComponent<AiControlProps & React.RefAttributes<
|
|
38
|
+
export declare function AIControl({ disabled, value, placeholder, showAccept, acceptLabel, showButtonLabels, isTransparent, state, showGuideLine, customFooter, onChange, onSend, onStop, onAccept, onDiscard, showRemove, bannerComponent, errorComponent, }: AiControlProps, ref: React.MutableRefObject<HTMLInputElement>): ReactElement;
|
|
39
|
+
declare const _default: React.ForwardRefExoticComponent<AiControlProps & React.RefAttributes<HTMLInputElement>>;
|
|
40
40
|
export default _default;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import debugFactory from 'debug';
|
|
5
|
+
/**
|
|
6
|
+
* Internal dependencies
|
|
7
|
+
*/
|
|
8
|
+
import requestJwt from '../../jwt/index.js';
|
|
9
|
+
const debug = debugFactory('ai-client:use-image-generator');
|
|
10
|
+
const useImageGenerator = () => {
|
|
11
|
+
const generateImage = async function ({ feature, }) {
|
|
12
|
+
let token = '';
|
|
13
|
+
try {
|
|
14
|
+
token = (await requestJwt()).token;
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
debug('Error getting token: %o', error);
|
|
18
|
+
return Promise.reject(error);
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
debug('Generating image');
|
|
22
|
+
// TODO: Find a proper prompt for the image generation
|
|
23
|
+
const imageGenerationPrompt = ``;
|
|
24
|
+
const URL = 'https://public-api.wordpress.com/wpcom/v2/jetpack-ai-image';
|
|
25
|
+
const body = {
|
|
26
|
+
prompt: imageGenerationPrompt,
|
|
27
|
+
response_format: 'url',
|
|
28
|
+
feature,
|
|
29
|
+
};
|
|
30
|
+
const headers = {
|
|
31
|
+
Authorization: `Bearer ${token}`,
|
|
32
|
+
};
|
|
33
|
+
const data = await fetch(URL, {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers,
|
|
36
|
+
body: JSON.stringify(body),
|
|
37
|
+
}).then(response => response.json());
|
|
38
|
+
return data;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
generateImage,
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
export default useImageGenerator;
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { useRef, useState, useEffect, useCallback } from '@wordpress/element';
|
|
5
|
+
/**
|
|
6
|
+
* Media types
|
|
7
|
+
*/
|
|
8
|
+
const MEDIA_TYPE_MP4_MP4A = 'audio/mp4;codecs=mp4a';
|
|
9
|
+
const MEDIA_TYPE_MP4 = 'audio/mp4';
|
|
10
|
+
const MEDIA_TYPE_WEBM = 'audio/webm';
|
|
5
11
|
/**
|
|
6
12
|
* react custom hook to handle media recording.
|
|
7
13
|
*
|
|
@@ -32,9 +38,10 @@ export default function useMediaRecording({ onDone, } = {}) {
|
|
|
32
38
|
* @returns {Blob} The recorded blob
|
|
33
39
|
*/
|
|
34
40
|
function getBlob() {
|
|
35
|
-
|
|
36
|
-
type:
|
|
37
|
-
}
|
|
41
|
+
if (MediaRecorder.isTypeSupported(MEDIA_TYPE_MP4_MP4A)) {
|
|
42
|
+
return new Blob(recordedChunks, { type: MEDIA_TYPE_MP4 }); // omit the codecs parameter
|
|
43
|
+
}
|
|
44
|
+
return new Blob(recordedChunks, { type: MEDIA_TYPE_WEBM });
|
|
38
45
|
}
|
|
39
46
|
// `start` recording handler
|
|
40
47
|
const start = useCallback((timeslice) => {
|
|
@@ -115,7 +122,15 @@ export default function useMediaRecording({ onDone, } = {}) {
|
|
|
115
122
|
audioStream.current = stream;
|
|
116
123
|
const source = audioCtx.createMediaStreamSource(stream);
|
|
117
124
|
source.connect(analyser.current);
|
|
118
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Special handling for iOS devices.
|
|
127
|
+
*/
|
|
128
|
+
if (MediaRecorder.isTypeSupported(MEDIA_TYPE_MP4_MP4A)) {
|
|
129
|
+
mediaRecordRef.current = new MediaRecorder(stream, { mimeType: MEDIA_TYPE_MP4_MP4A });
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
mediaRecordRef.current = new MediaRecorder(stream, { mimeType: MEDIA_TYPE_WEBM });
|
|
133
|
+
}
|
|
119
134
|
mediaRecordRef.current.addEventListener('start', onStartListener);
|
|
120
135
|
mediaRecordRef.current.addEventListener('stop', onStopListener);
|
|
121
136
|
mediaRecordRef.current.addEventListener('pause', onPauseListener);
|
package/build/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { default as useMediaRecording } from './hooks/use-media-recording/index.
|
|
|
7
7
|
export { default as useAudioTranscription } from './hooks/use-audio-transcription/index.js';
|
|
8
8
|
export { default as useTranscriptionPostProcessing } from './hooks/use-transcription-post-processing/index.js';
|
|
9
9
|
export { default as useAudioValidation } from './hooks/use-audio-validation/index.js';
|
|
10
|
+
export { default as useImageGenerator } from './hooks/use-image-generator/index.js';
|
|
10
11
|
export * from './icons/index.js';
|
|
11
12
|
export * from './components/index.js';
|
|
12
13
|
export * from './data-flow/index.js';
|
package/build/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export { default as useMediaRecording } from './hooks/use-media-recording/index.
|
|
|
13
13
|
export { default as useAudioTranscription } from './hooks/use-audio-transcription/index.js';
|
|
14
14
|
export { default as useTranscriptionPostProcessing } from './hooks/use-transcription-post-processing/index.js';
|
|
15
15
|
export { default as useAudioValidation } from './hooks/use-audio-validation/index.js';
|
|
16
|
+
export { default as useImageGenerator } from './hooks/use-image-generator/index.js';
|
|
16
17
|
/*
|
|
17
18
|
* Components: Icons
|
|
18
19
|
*/
|
package/build/types.d.ts
CHANGED
|
@@ -29,15 +29,3 @@ export type CancelablePromise<T = void> = Promise<T> & {
|
|
|
29
29
|
canceled?: boolean;
|
|
30
30
|
};
|
|
31
31
|
export type TranscriptionState = RecordingState | 'validating' | 'processing' | 'error';
|
|
32
|
-
interface JPConnectionInitialState {
|
|
33
|
-
apiNonce: string;
|
|
34
|
-
siteSuffix: string;
|
|
35
|
-
connectionStatus: {
|
|
36
|
-
isActive: boolean;
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
declare global {
|
|
40
|
-
interface Window {
|
|
41
|
-
JP_CONNECTION_INITIAL_STATE: JPConnectionInitialState;
|
|
42
|
-
}
|
|
43
|
-
}
|
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.10.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": {
|
|
@@ -38,19 +38,19 @@
|
|
|
38
38
|
"main": "./build/index.js",
|
|
39
39
|
"types": "./build/index.d.ts",
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@automattic/jetpack-base-styles": "^0.6.
|
|
42
|
-
"@automattic/jetpack-connection": "^0.33.
|
|
43
|
-
"@automattic/jetpack-shared-extension-utils": "^0.14.
|
|
41
|
+
"@automattic/jetpack-base-styles": "^0.6.19",
|
|
42
|
+
"@automattic/jetpack-connection": "^0.33.3",
|
|
43
|
+
"@automattic/jetpack-shared-extension-utils": "^0.14.7",
|
|
44
44
|
"@microsoft/fetch-event-source": "2.0.1",
|
|
45
45
|
"@types/react": "18.2.61",
|
|
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
|
+
"@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",
|
|
54
54
|
"classnames": "2.3.2",
|
|
55
55
|
"debug": "4.3.4",
|
|
56
56
|
"react": "18.2.0",
|
package/src/api-fetch/index.ts
CHANGED
|
@@ -7,7 +7,8 @@ import apiFetchMod from '@wordpress/api-fetch';
|
|
|
7
7
|
// while some other environments (including code running inside WordPress itself) see it at apiFetch.
|
|
8
8
|
// See https://arethetypeswrong.github.io/?p=@wordpress/api-fetch@6.47.0
|
|
9
9
|
// This is a helper to simplify the usage of the api-fetch module on the ai-client package.
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const apiFetch = 'default' in apiFetchMod ? apiFetchMod.default : apiFetchMod;
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
12
|
+
type ApiFetchType = typeof apiFetch extends Function ? typeof apiFetch : typeof apiFetchMod;
|
|
12
13
|
|
|
13
|
-
export default apiFetch;
|
|
14
|
+
export default apiFetch as ApiFetchType;
|
|
@@ -73,7 +73,7 @@ export function AIControl(
|
|
|
73
73
|
bannerComponent = null,
|
|
74
74
|
errorComponent = null,
|
|
75
75
|
}: AiControlProps,
|
|
76
|
-
ref: React.MutableRefObject<
|
|
76
|
+
ref: React.MutableRefObject< HTMLInputElement >
|
|
77
77
|
): ReactElement {
|
|
78
78
|
const promptUserInputRef = useRef( null );
|
|
79
79
|
const loading = state === 'requesting' || state === 'suggesting';
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import debugFactory from 'debug';
|
|
5
|
+
/**
|
|
6
|
+
* Internal dependencies
|
|
7
|
+
*/
|
|
8
|
+
import requestJwt from '../../jwt/index.js';
|
|
9
|
+
|
|
10
|
+
const debug = debugFactory( 'ai-client:use-image-generator' );
|
|
11
|
+
|
|
12
|
+
const useImageGenerator = () => {
|
|
13
|
+
const generateImage = async function ( {
|
|
14
|
+
feature,
|
|
15
|
+
}: {
|
|
16
|
+
feature: string;
|
|
17
|
+
} ): Promise< { data: Array< { url: string } > } > {
|
|
18
|
+
let token = '';
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
token = ( await requestJwt() ).token;
|
|
22
|
+
} catch ( error ) {
|
|
23
|
+
debug( 'Error getting token: %o', error );
|
|
24
|
+
return Promise.reject( error );
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
debug( 'Generating image' );
|
|
29
|
+
|
|
30
|
+
// TODO: Find a proper prompt for the image generation
|
|
31
|
+
const imageGenerationPrompt = ``;
|
|
32
|
+
|
|
33
|
+
const URL = 'https://public-api.wordpress.com/wpcom/v2/jetpack-ai-image';
|
|
34
|
+
|
|
35
|
+
const body = {
|
|
36
|
+
prompt: imageGenerationPrompt,
|
|
37
|
+
response_format: 'url',
|
|
38
|
+
feature,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const headers = {
|
|
42
|
+
Authorization: `Bearer ${ token }`,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const data = await fetch( URL, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers,
|
|
48
|
+
body: JSON.stringify( body ),
|
|
49
|
+
} ).then( response => response.json() );
|
|
50
|
+
|
|
51
|
+
return data as { data: { url: string }[] };
|
|
52
|
+
} catch ( error ) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
generateImage,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default useImageGenerator;
|
|
@@ -10,6 +10,13 @@ type UseMediaRecordingProps = {
|
|
|
10
10
|
onDone?: ( blob: Blob ) => void;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Media types
|
|
15
|
+
*/
|
|
16
|
+
const MEDIA_TYPE_MP4_MP4A = 'audio/mp4;codecs=mp4a';
|
|
17
|
+
const MEDIA_TYPE_MP4 = 'audio/mp4';
|
|
18
|
+
const MEDIA_TYPE_WEBM = 'audio/webm';
|
|
19
|
+
|
|
13
20
|
type UseMediaRecordingReturn = {
|
|
14
21
|
/**
|
|
15
22
|
* The current recording state
|
|
@@ -114,9 +121,11 @@ export default function useMediaRecording( {
|
|
|
114
121
|
* @returns {Blob} The recorded blob
|
|
115
122
|
*/
|
|
116
123
|
function getBlob() {
|
|
117
|
-
|
|
118
|
-
type:
|
|
119
|
-
}
|
|
124
|
+
if ( MediaRecorder.isTypeSupported( MEDIA_TYPE_MP4_MP4A ) ) {
|
|
125
|
+
return new Blob( recordedChunks, { type: MEDIA_TYPE_MP4 } ); // omit the codecs parameter
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return new Blob( recordedChunks, { type: MEDIA_TYPE_WEBM } );
|
|
120
129
|
}
|
|
121
130
|
|
|
122
131
|
// `start` recording handler
|
|
@@ -217,7 +226,15 @@ export default function useMediaRecording( {
|
|
|
217
226
|
const source = audioCtx.createMediaStreamSource( stream );
|
|
218
227
|
source.connect( analyser.current );
|
|
219
228
|
|
|
220
|
-
|
|
229
|
+
/**
|
|
230
|
+
* Special handling for iOS devices.
|
|
231
|
+
*/
|
|
232
|
+
if ( MediaRecorder.isTypeSupported( MEDIA_TYPE_MP4_MP4A ) ) {
|
|
233
|
+
mediaRecordRef.current = new MediaRecorder( stream, { mimeType: MEDIA_TYPE_MP4_MP4A } );
|
|
234
|
+
} else {
|
|
235
|
+
mediaRecordRef.current = new MediaRecorder( stream, { mimeType: MEDIA_TYPE_WEBM } );
|
|
236
|
+
}
|
|
237
|
+
|
|
221
238
|
mediaRecordRef.current.addEventListener( 'start', onStartListener );
|
|
222
239
|
mediaRecordRef.current.addEventListener( 'stop', onStopListener );
|
|
223
240
|
mediaRecordRef.current.addEventListener( 'pause', onPauseListener );
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ export { default as useMediaRecording } from './hooks/use-media-recording/index.
|
|
|
14
14
|
export { default as useAudioTranscription } from './hooks/use-audio-transcription/index.js';
|
|
15
15
|
export { default as useTranscriptionPostProcessing } from './hooks/use-transcription-post-processing/index.js';
|
|
16
16
|
export { default as useAudioValidation } from './hooks/use-audio-validation/index.js';
|
|
17
|
+
export { default as useImageGenerator } from './hooks/use-image-generator/index.js';
|
|
17
18
|
|
|
18
19
|
/*
|
|
19
20
|
* Components: Icons
|
package/src/types.ts
CHANGED
|
@@ -97,20 +97,3 @@ export type CancelablePromise< T = void > = Promise< T > & { canceled?: boolean
|
|
|
97
97
|
* Transcription types
|
|
98
98
|
*/
|
|
99
99
|
export type TranscriptionState = RecordingState | 'validating' | 'processing' | 'error';
|
|
100
|
-
|
|
101
|
-
// Connection initial state
|
|
102
|
-
// @todo: it should be provided by the connection package
|
|
103
|
-
interface JPConnectionInitialState {
|
|
104
|
-
apiNonce: string;
|
|
105
|
-
siteSuffix: string;
|
|
106
|
-
connectionStatus: {
|
|
107
|
-
isActive: boolean;
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Global
|
|
112
|
-
declare global {
|
|
113
|
-
interface Window {
|
|
114
|
-
JP_CONNECTION_INITIAL_STATE: JPConnectionInitialState;
|
|
115
|
-
}
|
|
116
|
-
}
|