@automattic/jetpack-ai-client 0.26.0 → 0.26.2

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,14 @@ 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.26.2] - 2025-02-17
9
+ ### Added
10
+ - Jetpack AI: Adding translation support using Chrome's Gemini AI mini. [#41724]
11
+
12
+ ## [0.26.1] - 2025-02-11
13
+ ### Changed
14
+ - Update dependencies. [#38958]
15
+
8
16
  ## [0.26.0] - 2025-02-10
9
17
  ### Added
10
18
  - Add shared components from ai-assistant-plugin [#41078]
@@ -517,6 +525,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
517
525
  - AI Client: stop using smart document visibility handling on the fetchEventSource library, so it does not restart the completion when changing tabs. [#32004]
518
526
  - Updated package dependencies. [#31468] [#31659] [#31785]
519
527
 
528
+ [0.26.2]: https://github.com/Automattic/jetpack-ai-client/compare/v0.26.1...v0.26.2
529
+ [0.26.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.26.0...v0.26.1
520
530
  [0.26.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.25.7...v0.26.0
521
531
  [0.25.7]: https://github.com/Automattic/jetpack-ai-client/compare/v0.25.6...v0.25.7
522
532
  [0.25.6]: https://github.com/Automattic/jetpack-ai-client/compare/v0.25.5...v0.25.6
@@ -0,0 +1,9 @@
1
+ import { PromptProp } from '../types.js';
2
+ import ChromeAISuggestionsEventSource from './suggestions.js';
3
+ /**
4
+ * This will return an instance of ChromeAISuggestionsEventSource or false.
5
+ *
6
+ * @param promptArg - The messages array of the prompt.
7
+ * @return ChromeAISuggestionsEventSource | bool
8
+ */
9
+ export default function ChromeAIFactory(promptArg: PromptProp): Promise<false | ChromeAISuggestionsEventSource>;
@@ -0,0 +1,99 @@
1
+ import { getJetpackExtensionAvailability } from '@automattic/jetpack-shared-extension-utils';
2
+ import { PROMPT_TYPE_CHANGE_LANGUAGE,
3
+ //PROMPT_TYPE_SUMMARIZE,
4
+ } from '../constants.js';
5
+ import ChromeAISuggestionsEventSource from './suggestions.js';
6
+ /**
7
+ * Check for the feature flag.
8
+ *
9
+ * @return boolean
10
+ */
11
+ function shouldUseChromeAI() {
12
+ return getJetpackExtensionAvailability('ai-use-chrome-ai-sometimes').available === true;
13
+ }
14
+ /**
15
+ * This will return an instance of ChromeAISuggestionsEventSource or false.
16
+ *
17
+ * @param promptArg - The messages array of the prompt.
18
+ * @return ChromeAISuggestionsEventSource | bool
19
+ */
20
+ export default async function ChromeAIFactory(promptArg) {
21
+ if (!shouldUseChromeAI()) {
22
+ return false;
23
+ }
24
+ const context = {
25
+ content: '',
26
+ language: '',
27
+ };
28
+ let promptType = '';
29
+ if (Array.isArray(promptArg)) {
30
+ for (let i = 0; i < promptArg.length; i++) {
31
+ const prompt = promptArg[i];
32
+ if (prompt.content) {
33
+ context.content = prompt.content;
34
+ }
35
+ if (!('context' in prompt)) {
36
+ continue;
37
+ }
38
+ const promptContext = prompt.context;
39
+ if (promptContext.type) {
40
+ promptType = promptContext.type;
41
+ }
42
+ if (promptContext.language) {
43
+ context.language = promptContext.language;
44
+ }
45
+ if (promptContext.content) {
46
+ context.content = promptContext.content;
47
+ }
48
+ }
49
+ }
50
+ if (promptType.startsWith('ai-assistant-change-language')) {
51
+ const [language] = context.language.split(' ');
52
+ if (!('translation' in self) ||
53
+ !self.translation.createTranslator ||
54
+ !self.translation.canTranslate) {
55
+ return false;
56
+ }
57
+ const languageOpts = {
58
+ sourceLanguage: 'en',
59
+ targetLanguage: language,
60
+ };
61
+ // see if we can detect the source language
62
+ if ('ai' in self && self.ai.languageDetector) {
63
+ const detector = await self.ai.languageDetector.create();
64
+ const confidences = await detector.detect(context.content);
65
+ for (const confidence of confidences) {
66
+ // 75% confidence is just a value that was picked. Generally
67
+ // 80% of higher is pretty safe, but the source language is
68
+ // required for the translator to work at all, which is also
69
+ // why en is the default language.
70
+ if (confidence.confidence > 0.75) {
71
+ languageOpts.sourceLanguage = confidence.detectedLanguage;
72
+ break;
73
+ }
74
+ }
75
+ }
76
+ const canTranslate = await self.translation.canTranslate(languageOpts);
77
+ if (canTranslate === 'no') {
78
+ return false;
79
+ }
80
+ const chromeAI = new ChromeAISuggestionsEventSource({
81
+ content: context.content,
82
+ promptType: PROMPT_TYPE_CHANGE_LANGUAGE,
83
+ options: languageOpts,
84
+ });
85
+ return chromeAI;
86
+ }
87
+ // TODO
88
+ if (promptType.startsWith('ai-assistant-summarize')) {
89
+ /*
90
+ return new ChromeAISuggestionsEventSource({
91
+ content: "",
92
+ promptType: PROMPT_TYPE_SUMMARIZE,
93
+ options: {},
94
+ } );
95
+ */
96
+ return false;
97
+ }
98
+ return false;
99
+ }
@@ -0,0 +1,2 @@
1
+ export { default as ChromeAIFactory } from './factory.js';
2
+ export { default as ChromeAISuggestionsEventSource } from './suggestions.js';
@@ -0,0 +1,2 @@
1
+ export { default as ChromeAIFactory } from './factory.js';
2
+ export { default as ChromeAISuggestionsEventSource } from './suggestions.js';
@@ -0,0 +1,35 @@
1
+ import { EventSourceMessage } from '@microsoft/fetch-event-source';
2
+ import { AiModelTypeProp } from '../types.js';
3
+ type ChromeAISuggestionsEventSourceConstructorArgs = {
4
+ content: string;
5
+ promptType: string;
6
+ options?: {
7
+ postId?: number | string;
8
+ feature?: 'ai-assistant-experimental' | string | undefined;
9
+ sourceLanguage?: string;
10
+ targetLanguage?: string;
11
+ functions?: Array<object>;
12
+ model?: AiModelTypeProp;
13
+ };
14
+ };
15
+ type FunctionCallProps = {
16
+ name?: string;
17
+ arguments?: string;
18
+ };
19
+ export default class ChromeAISuggestionsEventSource extends EventTarget {
20
+ fullMessage: string;
21
+ fullFunctionCall: FunctionCallProps;
22
+ isPromptClear: boolean;
23
+ controller: AbortController;
24
+ errorUnclearPromptTriggered: boolean;
25
+ constructor(data: ChromeAISuggestionsEventSourceConstructorArgs);
26
+ initSource({ content, promptType, options, }: ChromeAISuggestionsEventSourceConstructorArgs): void;
27
+ initEventSource(): Promise<void>;
28
+ close(): void;
29
+ checkForUnclearPrompt(): void;
30
+ processEvent(e: EventSourceMessage): void;
31
+ processErrorEvent(e: any): void;
32
+ translate(text: string, target: string, source?: string): Promise<void>;
33
+ summarize(text: string): Promise<string>;
34
+ }
35
+ export {};
@@ -0,0 +1,87 @@
1
+ import { PROMPT_TYPE_CHANGE_LANGUAGE, PROMPT_TYPE_SUMMARIZE } from '../constants.js';
2
+ import { getErrorData } from '../hooks/use-ai-suggestions/index.js';
3
+ import { renderHTMLFromMarkdown, renderMarkdownFromHTML } from '../libs/markdown/index.js';
4
+ import { ERROR_RESPONSE, ERROR_NETWORK } from '../types.js';
5
+ export default class ChromeAISuggestionsEventSource extends EventTarget {
6
+ fullMessage;
7
+ fullFunctionCall;
8
+ isPromptClear;
9
+ controller;
10
+ errorUnclearPromptTriggered;
11
+ constructor(data) {
12
+ super();
13
+ this.fullMessage = '';
14
+ this.fullFunctionCall = {
15
+ name: '',
16
+ arguments: '',
17
+ };
18
+ this.isPromptClear = false;
19
+ this.controller = new AbortController();
20
+ this.initSource(data);
21
+ }
22
+ initSource({ content, promptType, options = {}, }) {
23
+ if (promptType === PROMPT_TYPE_CHANGE_LANGUAGE) {
24
+ this.translate(content, options.targetLanguage, options.sourceLanguage);
25
+ }
26
+ if (promptType === PROMPT_TYPE_SUMMARIZE) {
27
+ this.summarize(content);
28
+ }
29
+ }
30
+ async initEventSource() { }
31
+ close() { }
32
+ checkForUnclearPrompt() { }
33
+ processEvent(e) {
34
+ let data;
35
+ try {
36
+ data = JSON.parse(e.data);
37
+ }
38
+ catch (err) {
39
+ this.processErrorEvent(err);
40
+ return;
41
+ }
42
+ if (e.event === 'translation') {
43
+ this.dispatchEvent(new CustomEvent('suggestion', { detail: data.message }));
44
+ }
45
+ if (data.complete) {
46
+ this.dispatchEvent(new CustomEvent('done', { detail: data.message }));
47
+ }
48
+ }
49
+ processErrorEvent(e) {
50
+ // Dispatch a generic network error event
51
+ this.dispatchEvent(new CustomEvent(ERROR_NETWORK, { detail: e }));
52
+ this.dispatchEvent(new CustomEvent(ERROR_RESPONSE, {
53
+ detail: getErrorData(ERROR_NETWORK),
54
+ }));
55
+ }
56
+ // use the Chrome AI translator
57
+ async translate(text, target, source = '') {
58
+ if (!('translation' in self)) {
59
+ return;
60
+ }
61
+ const translator = await self.translation.createTranslator({
62
+ sourceLanguage: source,
63
+ targetLanguage: target,
64
+ });
65
+ if (!translator) {
66
+ return;
67
+ }
68
+ try {
69
+ const translation = await translator.translate(renderHTMLFromMarkdown({ content: text }));
70
+ this.processEvent({
71
+ id: '',
72
+ event: 'translation',
73
+ data: JSON.stringify({
74
+ message: renderMarkdownFromHTML({ content: translation }),
75
+ complete: true,
76
+ }),
77
+ });
78
+ }
79
+ catch (error) {
80
+ this.processErrorEvent(error);
81
+ }
82
+ }
83
+ // TODO
84
+ async summarize(text) {
85
+ return text;
86
+ }
87
+ }
@@ -7,6 +7,7 @@ import { __ } from '@wordpress/i18n';
7
7
  * Internal dependencies
8
8
  */
9
9
  import askQuestion from '../../ask-question/index.js';
10
+ import ChromeAIFactory from '../../chrome-ai/factory.js';
10
11
  import { ERROR_CONTEXT_TOO_LARGE, ERROR_MODERATION, ERROR_NETWORK, ERROR_QUOTA_EXCEEDED, ERROR_SERVICE_UNAVAILABLE, ERROR_UNCLEAR_PROMPT, ERROR_RESPONSE, } from '../../types.js';
11
12
  /**
12
13
  * Get the error data for a given error code.
@@ -129,7 +130,14 @@ export default function useAiSuggestions({ prompt, autoRequest = false, askQuest
129
130
  setError(undefined);
130
131
  // Set the request status.
131
132
  setRequestingState('requesting');
132
- eventSourceRef.current = await askQuestion(promptArg, options);
133
+ // check if we can (or should) use Chrome AI
134
+ const chromeAI = await ChromeAIFactory(promptArg);
135
+ if (chromeAI !== false) {
136
+ eventSourceRef.current = chromeAI;
137
+ }
138
+ else {
139
+ eventSourceRef.current = await askQuestion(promptArg, options);
140
+ }
133
141
  if (!eventSourceRef?.current) {
134
142
  return;
135
143
  }
package/build/index.d.ts CHANGED
@@ -20,3 +20,7 @@ export * from './types.js';
20
20
  export * from './libs/index.js';
21
21
  export * from './constants.js';
22
22
  export * from './logo-generator/index.js';
23
+ /**
24
+ * Chrome AI
25
+ */
26
+ export * from './chrome-ai/index.js';
package/build/index.js CHANGED
@@ -47,3 +47,7 @@ export * from './constants.js';
47
47
  * Logo Generator
48
48
  */
49
49
  export * from './logo-generator/index.js';
50
+ /**
51
+ * Chrome AI
52
+ */
53
+ export * from './chrome-ai/index.js';
package/build/types.d.ts CHANGED
@@ -51,3 +51,29 @@ export interface BlockEditorStore {
51
51
  [key in keyof typeof BlockEditorSelectors]: (typeof BlockEditorSelectors)[key];
52
52
  };
53
53
  }
54
+ declare global {
55
+ interface Window {
56
+ translation?: {
57
+ canTranslate: (options: {
58
+ sourceLanguage: string;
59
+ targetLanguage: string;
60
+ }) => Promise<'no' | 'yes' | string>;
61
+ createTranslator: (options: {
62
+ sourceLanguage: string;
63
+ targetLanguage: string;
64
+ }) => Promise<{
65
+ translate: (text: string) => Promise<string>;
66
+ }>;
67
+ };
68
+ ai?: {
69
+ languageDetector: {
70
+ create: () => Promise<{
71
+ detect: (text: string) => Promise<{
72
+ detectedLanguage: string;
73
+ confidence: number;
74
+ }[]>;
75
+ }>;
76
+ };
77
+ };
78
+ }
79
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@automattic/jetpack-ai-client",
4
- "version": "0.26.0",
4
+ "version": "0.26.2",
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": {
@@ -45,9 +45,9 @@
45
45
  "types": "./build/index.d.ts",
46
46
  "dependencies": {
47
47
  "@automattic/jetpack-base-styles": "^0.6.42",
48
- "@automattic/jetpack-components": "^0.66.0",
49
- "@automattic/jetpack-connection": "^0.36.5",
50
- "@automattic/jetpack-shared-extension-utils": "^0.17.0",
48
+ "@automattic/jetpack-components": "^0.67.0",
49
+ "@automattic/jetpack-connection": "^0.36.7",
50
+ "@automattic/jetpack-shared-extension-utils": "^0.17.2",
51
51
  "@microsoft/fetch-event-source": "2.0.1",
52
52
  "@types/jest": "29.5.14",
53
53
  "@types/react": "18.3.18",
@@ -0,0 +1,129 @@
1
+ import { getJetpackExtensionAvailability } from '@automattic/jetpack-shared-extension-utils';
2
+ import {
3
+ PROMPT_TYPE_CHANGE_LANGUAGE,
4
+ //PROMPT_TYPE_SUMMARIZE,
5
+ } from '../constants.js';
6
+ import { PromptProp, PromptItemProps } from '../types.js';
7
+ import ChromeAISuggestionsEventSource from './suggestions.js';
8
+
9
+ /**
10
+ * Check for the feature flag.
11
+ *
12
+ * @return boolean
13
+ */
14
+ function shouldUseChromeAI() {
15
+ return getJetpackExtensionAvailability( 'ai-use-chrome-ai-sometimes' ).available === true;
16
+ }
17
+
18
+ interface PromptContext {
19
+ type?: string;
20
+ content?: string;
21
+ language?: string;
22
+ }
23
+
24
+ /**
25
+ * This will return an instance of ChromeAISuggestionsEventSource or false.
26
+ *
27
+ * @param promptArg - The messages array of the prompt.
28
+ * @return ChromeAISuggestionsEventSource | bool
29
+ */
30
+ export default async function ChromeAIFactory( promptArg: PromptProp ) {
31
+ if ( ! shouldUseChromeAI() ) {
32
+ return false;
33
+ }
34
+
35
+ const context = {
36
+ content: '',
37
+ language: '',
38
+ };
39
+ let promptType = '';
40
+ if ( Array.isArray( promptArg ) ) {
41
+ for ( let i = 0; i < promptArg.length; i++ ) {
42
+ const prompt: PromptItemProps = promptArg[ i ];
43
+ if ( prompt.content ) {
44
+ context.content = prompt.content;
45
+ }
46
+
47
+ if ( ! ( 'context' in prompt ) ) {
48
+ continue;
49
+ }
50
+
51
+ const promptContext: PromptContext = prompt.context;
52
+
53
+ if ( promptContext.type ) {
54
+ promptType = promptContext.type;
55
+ }
56
+
57
+ if ( promptContext.language ) {
58
+ context.language = promptContext.language;
59
+ }
60
+
61
+ if ( promptContext.content ) {
62
+ context.content = promptContext.content;
63
+ }
64
+ }
65
+ }
66
+
67
+ if ( promptType.startsWith( 'ai-assistant-change-language' ) ) {
68
+ const [ language ] = context.language.split( ' ' );
69
+
70
+ if (
71
+ ! ( 'translation' in self ) ||
72
+ ! self.translation.createTranslator ||
73
+ ! self.translation.canTranslate
74
+ ) {
75
+ return false;
76
+ }
77
+
78
+ const languageOpts = {
79
+ sourceLanguage: 'en',
80
+ targetLanguage: language,
81
+ };
82
+
83
+ // see if we can detect the source language
84
+ if ( 'ai' in self && self.ai.languageDetector ) {
85
+ const detector = await self.ai.languageDetector.create();
86
+ const confidences = await detector.detect( context.content );
87
+
88
+ for ( const confidence of confidences ) {
89
+ // 75% confidence is just a value that was picked. Generally
90
+ // 80% of higher is pretty safe, but the source language is
91
+ // required for the translator to work at all, which is also
92
+ // why en is the default language.
93
+ if ( confidence.confidence > 0.75 ) {
94
+ languageOpts.sourceLanguage = confidence.detectedLanguage;
95
+ break;
96
+ }
97
+ }
98
+ }
99
+
100
+ const canTranslate = await self.translation.canTranslate( languageOpts );
101
+
102
+ if ( canTranslate === 'no' ) {
103
+ return false;
104
+ }
105
+
106
+ const chromeAI = new ChromeAISuggestionsEventSource( {
107
+ content: context.content,
108
+ promptType: PROMPT_TYPE_CHANGE_LANGUAGE,
109
+ options: languageOpts,
110
+ } );
111
+
112
+ return chromeAI;
113
+ }
114
+
115
+ // TODO
116
+ if ( promptType.startsWith( 'ai-assistant-summarize' ) ) {
117
+ /*
118
+ return new ChromeAISuggestionsEventSource({
119
+ content: "",
120
+ promptType: PROMPT_TYPE_SUMMARIZE,
121
+ options: {},
122
+ } );
123
+ */
124
+
125
+ return false;
126
+ }
127
+
128
+ return false;
129
+ }
@@ -0,0 +1,2 @@
1
+ export { default as ChromeAIFactory } from './factory.js';
2
+ export { default as ChromeAISuggestionsEventSource } from './suggestions.js';
@@ -0,0 +1,139 @@
1
+ import { EventSourceMessage } from '@microsoft/fetch-event-source';
2
+ import { PROMPT_TYPE_CHANGE_LANGUAGE, PROMPT_TYPE_SUMMARIZE } from '../constants.js';
3
+ import { getErrorData } from '../hooks/use-ai-suggestions/index.js';
4
+ import { renderHTMLFromMarkdown, renderMarkdownFromHTML } from '../libs/markdown/index.js';
5
+ import { AiModelTypeProp, ERROR_RESPONSE, ERROR_NETWORK } from '../types.js';
6
+
7
+ type ChromeAISuggestionsEventSourceConstructorArgs = {
8
+ content: string;
9
+ promptType: string;
10
+ options?: {
11
+ postId?: number | string;
12
+ feature?: 'ai-assistant-experimental' | string | undefined;
13
+
14
+ // translation
15
+ sourceLanguage?: string;
16
+ targetLanguage?: string;
17
+
18
+ // not sure if we need these
19
+ functions?: Array< object >;
20
+ model?: AiModelTypeProp;
21
+ };
22
+ };
23
+
24
+ type ChromeAIEvent = {
25
+ type: string;
26
+ message: string;
27
+ complete?: boolean;
28
+ };
29
+
30
+ type FunctionCallProps = {
31
+ name?: string;
32
+ arguments?: string;
33
+ };
34
+
35
+ export default class ChromeAISuggestionsEventSource extends EventTarget {
36
+ fullMessage: string;
37
+ fullFunctionCall: FunctionCallProps;
38
+ isPromptClear: boolean;
39
+ controller: AbortController;
40
+
41
+ errorUnclearPromptTriggered: boolean;
42
+
43
+ constructor( data: ChromeAISuggestionsEventSourceConstructorArgs ) {
44
+ super();
45
+ this.fullMessage = '';
46
+ this.fullFunctionCall = {
47
+ name: '',
48
+ arguments: '',
49
+ };
50
+ this.isPromptClear = false;
51
+
52
+ this.controller = new AbortController();
53
+
54
+ this.initSource( data );
55
+ }
56
+
57
+ initSource( {
58
+ content,
59
+ promptType,
60
+ options = {},
61
+ }: ChromeAISuggestionsEventSourceConstructorArgs ) {
62
+ if ( promptType === PROMPT_TYPE_CHANGE_LANGUAGE ) {
63
+ this.translate( content, options.targetLanguage, options.sourceLanguage );
64
+ }
65
+
66
+ if ( promptType === PROMPT_TYPE_SUMMARIZE ) {
67
+ this.summarize( content );
68
+ }
69
+ }
70
+
71
+ async initEventSource() {}
72
+
73
+ close() {}
74
+
75
+ checkForUnclearPrompt() {}
76
+
77
+ processEvent( e: EventSourceMessage ) {
78
+ let data: ChromeAIEvent;
79
+ try {
80
+ data = JSON.parse( e.data );
81
+ } catch ( err ) {
82
+ this.processErrorEvent( err );
83
+ return;
84
+ }
85
+
86
+ if ( e.event === 'translation' ) {
87
+ this.dispatchEvent( new CustomEvent( 'suggestion', { detail: data.message } ) );
88
+ }
89
+
90
+ if ( data.complete ) {
91
+ this.dispatchEvent( new CustomEvent( 'done', { detail: data.message } ) );
92
+ }
93
+ }
94
+
95
+ processErrorEvent( e ) {
96
+ // Dispatch a generic network error event
97
+ this.dispatchEvent( new CustomEvent( ERROR_NETWORK, { detail: e } ) );
98
+ this.dispatchEvent(
99
+ new CustomEvent( ERROR_RESPONSE, {
100
+ detail: getErrorData( ERROR_NETWORK ),
101
+ } )
102
+ );
103
+ }
104
+
105
+ // use the Chrome AI translator
106
+ async translate( text: string, target: string, source: string = '' ) {
107
+ if ( ! ( 'translation' in self ) ) {
108
+ return;
109
+ }
110
+
111
+ const translator = await self.translation.createTranslator( {
112
+ sourceLanguage: source,
113
+ targetLanguage: target,
114
+ } );
115
+
116
+ if ( ! translator ) {
117
+ return;
118
+ }
119
+
120
+ try {
121
+ const translation = await translator.translate( renderHTMLFromMarkdown( { content: text } ) );
122
+ this.processEvent( {
123
+ id: '',
124
+ event: 'translation',
125
+ data: JSON.stringify( {
126
+ message: renderMarkdownFromHTML( { content: translation } ),
127
+ complete: true,
128
+ } ),
129
+ } );
130
+ } catch ( error ) {
131
+ this.processErrorEvent( error );
132
+ }
133
+ }
134
+
135
+ // TODO
136
+ async summarize( text: string ) {
137
+ return text;
138
+ }
139
+ }
@@ -7,6 +7,7 @@ import { __ } from '@wordpress/i18n';
7
7
  * Internal dependencies
8
8
  */
9
9
  import askQuestion from '../../ask-question/index.js';
10
+ import ChromeAIFactory from '../../chrome-ai/factory.js';
10
11
  import {
11
12
  ERROR_CONTEXT_TOO_LARGE,
12
13
  ERROR_MODERATION,
@@ -314,7 +315,14 @@ export default function useAiSuggestions( {
314
315
  // Set the request status.
315
316
  setRequestingState( 'requesting' );
316
317
 
317
- eventSourceRef.current = await askQuestion( promptArg, options );
318
+ // check if we can (or should) use Chrome AI
319
+ const chromeAI = await ChromeAIFactory( promptArg );
320
+
321
+ if ( chromeAI !== false ) {
322
+ eventSourceRef.current = chromeAI;
323
+ } else {
324
+ eventSourceRef.current = await askQuestion( promptArg, options );
325
+ }
318
326
 
319
327
  if ( ! eventSourceRef?.current ) {
320
328
  return;
package/src/index.ts CHANGED
@@ -55,3 +55,8 @@ export * from './constants.js';
55
55
  * Logo Generator
56
56
  */
57
57
  export * from './logo-generator/index.js';
58
+
59
+ /**
60
+ * Chrome AI
61
+ */
62
+ export * from './chrome-ai/index.js';
package/src/types.ts CHANGED
@@ -132,3 +132,32 @@ export interface BlockEditorStore {
132
132
  [ key in keyof typeof BlockEditorSelectors ]: ( typeof BlockEditorSelectors )[ key ];
133
133
  };
134
134
  }
135
+
136
+ declare global {
137
+ interface Window {
138
+ translation?: {
139
+ canTranslate: ( options: {
140
+ sourceLanguage: string;
141
+ targetLanguage: string;
142
+ } ) => Promise< 'no' | 'yes' | string >;
143
+ createTranslator: ( options: {
144
+ sourceLanguage: string;
145
+ targetLanguage: string;
146
+ } ) => Promise< {
147
+ translate: ( text: string ) => Promise< string >;
148
+ } >;
149
+ };
150
+ ai?: {
151
+ languageDetector: {
152
+ create: () => Promise< {
153
+ detect: ( text: string ) => Promise<
154
+ {
155
+ detectedLanguage: string;
156
+ confidence: number;
157
+ }[]
158
+ >;
159
+ } >;
160
+ };
161
+ };
162
+ }
163
+ }