@automattic/jetpack-ai-client 0.1.0 → 0.1.1
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 +20 -0
- package/index.ts +23 -1
- package/package.json +13 -3
- package/src/ask-question/Readme.md +1 -1
- package/src/ask-question/index.ts +21 -7
- package/src/components/ai-control/index.tsx +130 -0
- package/src/components/ai-control/stories/index.stories.tsx +42 -0
- package/src/components/ai-control/style.scss +107 -0
- package/src/data-flow/Readme.md +113 -0
- package/src/data-flow/context.tsx +74 -0
- package/src/data-flow/index.ts +3 -0
- package/src/data-flow/use-ai-context.ts +73 -0
- package/src/data-flow/with-ai-assistant-data.tsx +52 -0
- package/src/hooks/use-ai-suggestions/Readme.md +127 -0
- package/src/hooks/use-ai-suggestions/index.ts +354 -0
- package/src/icons/Readme.md +22 -0
- package/src/icons/ai-assistant.tsx +31 -0
- package/src/icons/index.ts +3 -0
- package/src/icons/origami-plane.tsx +20 -0
- package/src/icons/speak-tone.tsx +24 -0
- package/src/icons/stories/index.stories.tsx +46 -0
- package/src/icons/stories/style.module.scss +21 -0
- package/src/suggestions-event-source/index.ts +83 -15
- package/src/types.ts +20 -0
- package/src/index.js +0 -26
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@ 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.1.1] - 2023-08-01
|
|
9
|
+
### Added
|
|
10
|
+
- Add AI Client icon components [#32079]
|
|
11
|
+
- AI Assistant: add function calling feature. [#32161]
|
|
12
|
+
- AI Client: add AI Assistant data context. [#32129]
|
|
13
|
+
- AI Client: add useAiContext() react hook. [#32145]
|
|
14
|
+
- AI Client: add useAiSuggestions() react custom hook. [#32022]
|
|
15
|
+
- AI Client: introduce AI Control component. [#32163]
|
|
16
|
+
- AI Client: introduce withAiDataProvider HOC. [#32142]
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- AI Client: add Icon suffix to icon components. [#32173]
|
|
20
|
+
- AI Client: handle properly passing the post_id parameter to endpoint. [#32104]
|
|
21
|
+
- AI Client: replace using CSS modules with the regular way. [#32171]
|
|
22
|
+
|
|
23
|
+
### Removed
|
|
24
|
+
- AI Client: remove unused image library [#32127]
|
|
25
|
+
|
|
8
26
|
## 0.1.0 - 2023-07-25
|
|
9
27
|
### Added
|
|
10
28
|
- Add Jetpack AI Client [#30855]
|
|
@@ -17,3 +35,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
17
35
|
- Updated package dependencies. [#31468]
|
|
18
36
|
- Updated package dependencies. [#31659]
|
|
19
37
|
- Updated package dependencies. [#31785]
|
|
38
|
+
|
|
39
|
+
[0.1.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.0...v0.1.1
|
package/index.ts
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Core library exports
|
|
3
|
+
*/
|
|
1
4
|
export { default as requestJwt } from './src/jwt';
|
|
2
5
|
export { default as SuggestionsEventSource } from './src/suggestions-event-source';
|
|
3
6
|
export { default as askQuestion } from './src/ask-question';
|
|
4
|
-
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* Hooks
|
|
10
|
+
*/
|
|
11
|
+
export { default as useAiSuggestions } from './src/hooks/use-ai-suggestions';
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* Components: Icons
|
|
15
|
+
*/
|
|
16
|
+
export * from './src/icons';
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* Components
|
|
20
|
+
*/
|
|
21
|
+
export { default as AIControl } from './src/components/ai-control';
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* Contexts
|
|
25
|
+
*/
|
|
26
|
+
export * from './src/data-flow';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "@automattic/jetpack-ai-client",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.1",
|
|
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": {
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
},
|
|
22
22
|
"type": "module",
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"
|
|
24
|
+
"@storybook/react": "7.1.0",
|
|
25
|
+
"jest": "^29.6.2",
|
|
25
26
|
"jest-environment-jsdom": "29.5.0",
|
|
26
27
|
"typescript": "5.0.4"
|
|
27
28
|
},
|
|
@@ -33,6 +34,15 @@
|
|
|
33
34
|
"@automattic/jetpack-shared-extension-utils": "^0.10.9",
|
|
34
35
|
"@microsoft/fetch-event-source": "2.0.1",
|
|
35
36
|
"@wordpress/api-fetch": "6.34.0",
|
|
36
|
-
"
|
|
37
|
+
"@wordpress/block-editor": "12.5.0",
|
|
38
|
+
"@wordpress/components": "25.3.0",
|
|
39
|
+
"@wordpress/data": "9.7.0",
|
|
40
|
+
"@wordpress/element": "5.14.0",
|
|
41
|
+
"@wordpress/i18n": "4.37.0",
|
|
42
|
+
"@wordpress/icons": "9.28.0",
|
|
43
|
+
"classnames": "2.3.2",
|
|
44
|
+
"debug": "4.3.4",
|
|
45
|
+
"react": "18.2.0",
|
|
46
|
+
"react-dom": "18.2.0"
|
|
37
47
|
}
|
|
38
48
|
}
|
|
@@ -7,9 +7,9 @@ import SuggestionsEventSource from '../suggestions-event-source';
|
|
|
7
7
|
/*
|
|
8
8
|
* Types & constants
|
|
9
9
|
*/
|
|
10
|
-
import type {
|
|
10
|
+
import type { PromptProp } from '../types';
|
|
11
11
|
|
|
12
|
-
type AskQuestionOptionsArgProps = {
|
|
12
|
+
export type AskQuestionOptionsArgProps = {
|
|
13
13
|
/*
|
|
14
14
|
* ID of the post where the question is asked.
|
|
15
15
|
*/
|
|
@@ -24,6 +24,16 @@ type AskQuestionOptionsArgProps = {
|
|
|
24
24
|
* Allows to use a specific AI assistant feature. Default value is undefined.
|
|
25
25
|
*/
|
|
26
26
|
feature?: 'ai-assistant-experimental' | string | undefined;
|
|
27
|
+
|
|
28
|
+
/*
|
|
29
|
+
* Allows the use of function calling. Default value is undefined.
|
|
30
|
+
*/
|
|
31
|
+
functions?: Array< {
|
|
32
|
+
name?: string;
|
|
33
|
+
arguments?: string;
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
35
|
+
implementation?: Function;
|
|
36
|
+
} >;
|
|
27
37
|
};
|
|
28
38
|
|
|
29
39
|
const debug = debugFactory( 'jetpack-ai-client:ask-question' );
|
|
@@ -32,7 +42,7 @@ const debug = debugFactory( 'jetpack-ai-client:ask-question' );
|
|
|
32
42
|
* An asynchronous function that asks a question
|
|
33
43
|
* and returns an event source with suggestions.
|
|
34
44
|
*
|
|
35
|
-
* @param {
|
|
45
|
+
* @param {PromptProp} question - The question to ask. It can be a simple string or an array of PromptMessageItemProps objects.
|
|
36
46
|
* @param {AskQuestionOptionsArgProps} options - An optional object for additional configuration:
|
|
37
47
|
* @returns {Promise<SuggestionsEventSource>} A promise that resolves to an instance of the SuggestionsEventSource
|
|
38
48
|
* @example
|
|
@@ -47,12 +57,16 @@ const debug = debugFactory( 'jetpack-ai-client:ask-question' );
|
|
|
47
57
|
* } );
|
|
48
58
|
*/
|
|
49
59
|
export default async function askQuestion(
|
|
50
|
-
question:
|
|
51
|
-
{ postId = null, fromCache = false, feature }: AskQuestionOptionsArgProps = {}
|
|
60
|
+
question: PromptProp,
|
|
61
|
+
{ postId = null, fromCache = false, feature, functions }: AskQuestionOptionsArgProps = {}
|
|
52
62
|
): Promise< SuggestionsEventSource > {
|
|
53
|
-
debug( 'Asking question: %o. options: %o', question, { postId, fromCache, feature } );
|
|
63
|
+
debug( 'Asking question: %o. options: %o', question, { postId, fromCache, feature, functions } );
|
|
54
64
|
|
|
55
65
|
const { token } = await requestJwt();
|
|
56
66
|
|
|
57
|
-
return new SuggestionsEventSource( {
|
|
67
|
+
return new SuggestionsEventSource( {
|
|
68
|
+
question,
|
|
69
|
+
token,
|
|
70
|
+
options: { postId, feature, fromCache, functions },
|
|
71
|
+
} );
|
|
58
72
|
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { PlainText } from '@wordpress/block-editor';
|
|
5
|
+
import { Button, Spinner } from '@wordpress/components';
|
|
6
|
+
import { useRef } from '@wordpress/element';
|
|
7
|
+
import { __ } from '@wordpress/i18n';
|
|
8
|
+
import { Icon, closeSmall, check, arrowUp } from '@wordpress/icons';
|
|
9
|
+
import classNames from 'classnames';
|
|
10
|
+
/**
|
|
11
|
+
* Internal dependencies
|
|
12
|
+
*/
|
|
13
|
+
import { aiAssistantIcon } from '../../icons';
|
|
14
|
+
import './style.scss';
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
17
|
+
const noop = () => {};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* AI Control component.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} props - component props
|
|
23
|
+
* @param {boolean} props.loading - loading state
|
|
24
|
+
* @param {string} props.value - input value
|
|
25
|
+
* @param {string} props.placeholder - input placeholder
|
|
26
|
+
* @param {boolean} props.showAccept - show accept button
|
|
27
|
+
* @param {string} props.acceptLabel - accept button label
|
|
28
|
+
* @param {boolean} props.showButtonsLabel - show buttons label
|
|
29
|
+
* @param {boolean} props.isOpaque - is opaque
|
|
30
|
+
* @param {Function} props.onChange - input change handler
|
|
31
|
+
* @param {Function} props.onSend - send request handler
|
|
32
|
+
* @param {Function} props.onStop - stop request handler
|
|
33
|
+
* @param {Function} props.onAccept - accept handler
|
|
34
|
+
* @returns {object} - AI Control component
|
|
35
|
+
*/
|
|
36
|
+
export default function AIControl( {
|
|
37
|
+
loading = false,
|
|
38
|
+
value = '',
|
|
39
|
+
placeholder = '',
|
|
40
|
+
showAccept = false,
|
|
41
|
+
acceptLabel = __( 'Accept', 'jetpack-ai-client' ),
|
|
42
|
+
showButtonsLabel = true,
|
|
43
|
+
isOpaque = false,
|
|
44
|
+
onChange = noop,
|
|
45
|
+
onSend = noop,
|
|
46
|
+
onStop = noop,
|
|
47
|
+
onAccept = noop,
|
|
48
|
+
}: {
|
|
49
|
+
loading?: boolean;
|
|
50
|
+
value: string;
|
|
51
|
+
placeholder?: string;
|
|
52
|
+
showAccept?: boolean;
|
|
53
|
+
acceptLabel?: string;
|
|
54
|
+
showButtonsLabel?: boolean;
|
|
55
|
+
isOpaque?: boolean;
|
|
56
|
+
onChange: ( newValue: string ) => void;
|
|
57
|
+
onSend: ( currentValue: string ) => void;
|
|
58
|
+
onStop: () => void;
|
|
59
|
+
onAccept: () => void;
|
|
60
|
+
} ) {
|
|
61
|
+
const promptUserInputRef = useRef( null );
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className="jetpack-components-ai-control__container">
|
|
65
|
+
<div
|
|
66
|
+
className={ classNames( 'jetpack-components-ai-control__wrapper', {
|
|
67
|
+
'is-opaque': isOpaque,
|
|
68
|
+
} ) }
|
|
69
|
+
>
|
|
70
|
+
<div className="jetpack-components-ai-controlton__icon">
|
|
71
|
+
{ loading ? (
|
|
72
|
+
<Spinner className="input-spinner" />
|
|
73
|
+
) : (
|
|
74
|
+
<Icon className="input-icon" icon={ aiAssistantIcon } size={ 24 } />
|
|
75
|
+
) }
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<PlainText
|
|
79
|
+
value={ value }
|
|
80
|
+
onChange={ onChange }
|
|
81
|
+
placeholder={ placeholder }
|
|
82
|
+
className="jetpack-components-ai-control__input"
|
|
83
|
+
disabled={ loading }
|
|
84
|
+
ref={ promptUserInputRef }
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
<div className="jetpack-components-ai-control__controls">
|
|
88
|
+
<div className="jetpack-components-ai-control__controls-prompt_button_wrapper">
|
|
89
|
+
{ ! loading ? (
|
|
90
|
+
<Button
|
|
91
|
+
className="jetpack-components-ai-control__controls-prompt_button"
|
|
92
|
+
onClick={ () => onSend( value ) }
|
|
93
|
+
isSmall={ true }
|
|
94
|
+
disabled={ value?.length }
|
|
95
|
+
label={ __( 'Send request', 'jetpack-ai-client' ) }
|
|
96
|
+
>
|
|
97
|
+
<Icon icon={ arrowUp } />
|
|
98
|
+
{ showButtonsLabel && __( 'Send', 'jetpack-ai-client' ) }
|
|
99
|
+
</Button>
|
|
100
|
+
) : (
|
|
101
|
+
<Button
|
|
102
|
+
className="jetpack-components-ai-control__controls-prompt_button"
|
|
103
|
+
onClick={ onStop }
|
|
104
|
+
isSmall={ true }
|
|
105
|
+
label={ __( 'Stop request', 'jetpack-ai-client' ) }
|
|
106
|
+
>
|
|
107
|
+
<Icon icon={ closeSmall } />
|
|
108
|
+
{ showButtonsLabel && __( 'Stop', 'jetpack-ai-client' ) }
|
|
109
|
+
</Button>
|
|
110
|
+
) }
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
{ showAccept && (
|
|
114
|
+
<div className="jetpack-components-ai-control__controls-prompt_button_wrapper">
|
|
115
|
+
<Button
|
|
116
|
+
className="jetpack-components-ai-control__controls-prompt_button"
|
|
117
|
+
onClick={ onAccept }
|
|
118
|
+
isSmall={ true }
|
|
119
|
+
label={ acceptLabel }
|
|
120
|
+
>
|
|
121
|
+
<Icon icon={ check } />
|
|
122
|
+
{ acceptLabel }
|
|
123
|
+
</Button>
|
|
124
|
+
</div>
|
|
125
|
+
) }
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useState } from '@wordpress/element';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import AIControl from '../index';
|
|
10
|
+
/**
|
|
11
|
+
* Types
|
|
12
|
+
*/
|
|
13
|
+
import type { Meta } from '@storybook/react';
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
title: 'JS Packages/AI Client/AI Control',
|
|
17
|
+
component: AIControl,
|
|
18
|
+
parameters: {},
|
|
19
|
+
} as Meta< typeof AIControl >;
|
|
20
|
+
|
|
21
|
+
const Template = args => {
|
|
22
|
+
const [ value, setValue ] = useState( '' );
|
|
23
|
+
|
|
24
|
+
const handleChange = ( newValue: string ) => {
|
|
25
|
+
setValue( newValue );
|
|
26
|
+
args?.onChange?.( newValue );
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return <AIControl { ...args } onChange={ handleChange } value={ args?.value ?? value } />;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const DefaultArgs = {
|
|
33
|
+
loading: false,
|
|
34
|
+
isOpaque: false,
|
|
35
|
+
placeholder: '',
|
|
36
|
+
showButtonsLabel: true,
|
|
37
|
+
showAccept: false,
|
|
38
|
+
acceptLabel: 'Accept',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const Default = Template.bind( {} );
|
|
42
|
+
Default.args = DefaultArgs;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
.jetpack-components-ai-control__container {
|
|
2
|
+
color: var( --jp-gray-80 );
|
|
3
|
+
background-color: var( --jp-white );
|
|
4
|
+
box-shadow: var( --wp--preset--color--cyan-bluish-gray ) 0px 0px 0px 1px, var( --wp--preset--color--cyan-bluish-gray ) 0px 0px 8px;
|
|
5
|
+
font-family: "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.jetpack-components-ai-control__wrapper {
|
|
9
|
+
display: flex;
|
|
10
|
+
padding: 12px 14px;
|
|
11
|
+
gap: 8px;
|
|
12
|
+
|
|
13
|
+
&.is-opaque {
|
|
14
|
+
opacity: 0.4;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
textarea.jetpack-components-ai-control__input {
|
|
18
|
+
width: 100%;
|
|
19
|
+
min-height: 20px;
|
|
20
|
+
flex-grow: 1;
|
|
21
|
+
border-radius: 2px;
|
|
22
|
+
padding: 6px 8px;
|
|
23
|
+
|
|
24
|
+
resize: none !important;
|
|
25
|
+
border: none;
|
|
26
|
+
box-shadow: none;
|
|
27
|
+
color: var( --jp-gray-80 );
|
|
28
|
+
|
|
29
|
+
font-size: 16px;
|
|
30
|
+
font-weight: 400;
|
|
31
|
+
line-height: 20px;
|
|
32
|
+
|
|
33
|
+
transition: box-shadow 0.1s linear 0s;
|
|
34
|
+
|
|
35
|
+
&:focus {
|
|
36
|
+
box-shadow: none;
|
|
37
|
+
color: var( --jp-gray-80 );
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@media (min-width: 600px) {
|
|
41
|
+
font-size: 13px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&::placeholder {
|
|
45
|
+
text-overflow: ellipsis;
|
|
46
|
+
white-space: nowrap;
|
|
47
|
+
opacity: 0.75;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.jetpack-components-ai-controlton__icon {
|
|
53
|
+
flex-shrink: 0;
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
justify-content: center;
|
|
57
|
+
|
|
58
|
+
.input-icon {
|
|
59
|
+
// Brand green regardless of theme
|
|
60
|
+
color: var( --jp-green-40 );
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.input-spinner {
|
|
64
|
+
margin: 0;
|
|
65
|
+
width: 24px;
|
|
66
|
+
height: 24px;
|
|
67
|
+
|
|
68
|
+
path {
|
|
69
|
+
color: var( --jp-green-40 );
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.jetpack-components-ai-control__controls {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.jetpack-components-ai-control__controls-prompt_button_wrapper {
|
|
80
|
+
text-transform: uppercase;
|
|
81
|
+
font-size: 11px;
|
|
82
|
+
font-weight: 600;
|
|
83
|
+
line-height: 16px;
|
|
84
|
+
user-select: none;
|
|
85
|
+
white-space: nowrap;
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.jetpack-components-ai-control__controls-prompt_button {
|
|
91
|
+
color: var( --jp-gray-80 );
|
|
92
|
+
text-transform: uppercase;
|
|
93
|
+
|
|
94
|
+
> SVG {
|
|
95
|
+
margin-right: 4px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
&:hover,
|
|
99
|
+
&:active {
|
|
100
|
+
color: var( --wp-components-color-accent, var( --wp-admin-theme-color ) );
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
&:disabled {
|
|
104
|
+
opacity: 0.6;
|
|
105
|
+
cursor: not-allowed;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
|
|
2
|
+
# AI Assistant Data Flow
|
|
3
|
+
|
|
4
|
+
```jsx
|
|
5
|
+
import { withAiAssistantData, useAiContext } from '@automattic/jetpack-ai-client';
|
|
6
|
+
|
|
7
|
+
const MyComponent = () => {
|
|
8
|
+
const { suggestion, requestingState, requestSuggestion } = useAiContext( {
|
|
9
|
+
onDone: content => console.log( `Content is done: ${ content }.` );
|
|
10
|
+
} );
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<>
|
|
14
|
+
<div>{ suggestions }</div>
|
|
15
|
+
<button
|
|
16
|
+
onClick={ () => requestSuggestion( 'How to make a cake' ) }
|
|
17
|
+
disabled={ requestingState === 'suggesting' }
|
|
18
|
+
>
|
|
19
|
+
Request
|
|
20
|
+
</button>
|
|
21
|
+
<>
|
|
22
|
+
)
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Ensure to provide the data context to `MyComponent`.
|
|
26
|
+
export default withAiAssistantData( MyComponent );
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## In-depth Analysis
|
|
31
|
+
|
|
32
|
+
* [AI Data Context](#ai-assistant-content)
|
|
33
|
+
* [withAiDataProvider HOC](#with-ai-data-provider)
|
|
34
|
+
* [useAiContext Hook](#use-ai-context)
|
|
35
|
+
|
|
36
|
+
<h2 id="ai-assistant-content">Ai Data Context</h2>
|
|
37
|
+
|
|
38
|
+
The Ai Data Context is a React context implementation for managing the state and functionality of an AI Assistant. It manages the suggestion values, error states, and request functionality.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Usage
|
|
42
|
+
|
|
43
|
+
Import the Ai Data Context and Provider into your component:
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
import { AiDataContext, AiDataContextProvider } from '@automattic/jetpack-ai-client';
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Use the Provider in your component's render method to wrap the children components:
|
|
50
|
+
|
|
51
|
+
```es6
|
|
52
|
+
<AiDataContextProvider value={ value }>
|
|
53
|
+
{ children }
|
|
54
|
+
</AiDataContextProvider>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
You can access the context values in your child components using the `useContext` hook:
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
const aiContext = React.useContext( AiDataContext );
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Context Values
|
|
64
|
+
|
|
65
|
+
The Ai Data Context has the following values:
|
|
66
|
+
|
|
67
|
+
#### `suggestion`
|
|
68
|
+
The suggestion value from the AI.
|
|
69
|
+
|
|
70
|
+
#### `requestingError`
|
|
71
|
+
The error object returned from the AI suggestion request. It contains the following properties:
|
|
72
|
+
- `code`: A code referring to the type of error. The possible error codes are `ERROR_SERVICE_UNAVAILABLE`, `ERROR_QUOTA_EXCEEDED`, `ERROR_MODERATION`, `ERROR_NETWORK`, `ERROR_UNCLEAR_PROMPT`.
|
|
73
|
+
- `message`: A user-friendly error message.
|
|
74
|
+
- `severity`: The severity of the error. It can either be 'info' or 'error'.
|
|
75
|
+
|
|
76
|
+
#### `requestingState`
|
|
77
|
+
The current state of the suggestion request. It can be one of the following:
|
|
78
|
+
- `init`: The initial state before a request is made.
|
|
79
|
+
- `requesting`: The state when a request is being made.
|
|
80
|
+
- `suggesting`: The state when the AI is generating a suggestion.
|
|
81
|
+
- `done`: The state when a suggestion has been received.
|
|
82
|
+
- `error`: The state when an error has occurred during the request.
|
|
83
|
+
|
|
84
|
+
#### `requestSuggestion`
|
|
85
|
+
A function to request a suggestion from the AI. The function takes a prompt parameter which can be an object of `PromptMessagesProp` or a string.
|
|
86
|
+
|
|
87
|
+
<h2 id="with-ai-data-provider">withAiDataProvider HOC</h2>
|
|
88
|
+
|
|
89
|
+
Higher Order Component (HOC) that wraps a given component and provides it with the AI Assistant Data context. This HOC is instrumental in the data flow of the AI Assistant functionality and helps manage the interaction with the AI Assistant's communication layer.
|
|
90
|
+
|
|
91
|
+
<h2 id="use-ai-context">useAiContext Hook</h2>
|
|
92
|
+
|
|
93
|
+
The `useAiContext` hook provides a convenient way to access the
|
|
94
|
+
Ai Data Context and subscribe to the `done` and `suggestion` events emitted by SuggestionsEventSource.
|
|
95
|
+
|
|
96
|
+
```es6
|
|
97
|
+
const { suggestion } = useAiContext( {
|
|
98
|
+
onDone: content => console.log( content ),
|
|
99
|
+
onSuggestion: suggestion => console.log( suggestion ),
|
|
100
|
+
} );
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
_Before using the hook, ensure the data is provided by the [Ai Data Context](#ai-assistant-content). [withAiDataProvider HOC](#with-ai-data-provider) is usually the best option_
|
|
105
|
+
|
|
106
|
+
Optional options object:
|
|
107
|
+
|
|
108
|
+
- `onDone`: A callback function to be called when a request completes. The callback receives the request result as a parameter.
|
|
109
|
+
- `onSuggestion`: A callback function to be called when a new suggestion is received. The callback receives the suggestion as a parameter.
|
|
110
|
+
|
|
111
|
+
These callbacks will be invoked with the detail of the corresponding event emitted by SuggestionsEventSource.
|
|
112
|
+
|
|
113
|
+
When called, the hook returns the Ai Data Context.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { createContext } from '@wordpress/element';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
/**
|
|
7
|
+
* Types & Constants
|
|
8
|
+
*/
|
|
9
|
+
import SuggestionsEventSource from '../suggestions-event-source';
|
|
10
|
+
import type { RequestingStateProp, RequestingErrorProps } from '../hooks/use-ai-suggestions';
|
|
11
|
+
import type { PromptProp } from '../types';
|
|
12
|
+
|
|
13
|
+
export type AiDataContextProps = {
|
|
14
|
+
/*
|
|
15
|
+
* Suggestion value
|
|
16
|
+
*/
|
|
17
|
+
suggestion: string;
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
* Suggestion error
|
|
21
|
+
*/
|
|
22
|
+
requestingError: RequestingErrorProps;
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* Requesting state
|
|
26
|
+
*/
|
|
27
|
+
requestingState: RequestingStateProp;
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
* Request suggestion function
|
|
31
|
+
*/
|
|
32
|
+
requestSuggestion: ( prompt: PromptProp ) => void;
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* The Suggestions Event Source instance
|
|
36
|
+
*/
|
|
37
|
+
eventSource: SuggestionsEventSource | null;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type AiDataContextProviderProps = {
|
|
41
|
+
/*
|
|
42
|
+
* Data to provide to the context
|
|
43
|
+
*/
|
|
44
|
+
value: AiDataContextProps;
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
* Children
|
|
48
|
+
*/
|
|
49
|
+
children: React.ReactNode;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* AI Data Context
|
|
54
|
+
*
|
|
55
|
+
* @returns {AiDataContextProps} Context.
|
|
56
|
+
*/
|
|
57
|
+
export const AiDataContext = createContext( {} as AiDataContextProps );
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* AI Data Context Provider
|
|
61
|
+
*
|
|
62
|
+
* @param {AiDataContextProviderProps} props - Component props.
|
|
63
|
+
* @returns {React.ReactNode} Context provider.
|
|
64
|
+
* @example
|
|
65
|
+
* <AiDataContextProvider value={ value }>
|
|
66
|
+
* { children }
|
|
67
|
+
* </AiDataContextProvider>
|
|
68
|
+
*/
|
|
69
|
+
export const AiDataContextProvider = ( {
|
|
70
|
+
value,
|
|
71
|
+
children,
|
|
72
|
+
}: AiDataContextProviderProps ): React.ReactNode => (
|
|
73
|
+
<AiDataContext.Provider value={ value } children={ children } />
|
|
74
|
+
);
|