@automattic/jetpack-ai-client 0.32.0 → 0.32.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 CHANGED
@@ -5,6 +5,10 @@ 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.32.1] - 2025-06-24
9
+ ### Added
10
+ - Improve error handling on Chrome AI events. [#44048]
11
+
8
12
  ## [0.32.0] - 2025-06-23
9
13
  ### Changed
10
14
  - AI Assistant: Add A/B test for Chrome AI API features. [#43690]
@@ -646,6 +650,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
646
650
  - AI Client: stop using smart document visibility handling on the fetchEventSource library, so it does not restart the completion when changing tabs. [#32004]
647
651
  - Updated package dependencies. [#31468] [#31659] [#31785]
648
652
 
653
+ [0.32.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.32.0...v0.32.1
649
654
  [0.32.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.31.2...v0.32.0
650
655
  [0.31.2]: https://github.com/Automattic/jetpack-ai-client/compare/v0.31.1...v0.31.2
651
656
  [0.31.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.31.0...v0.31.1
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { initializeExPlat, loadExperimentAssignment } from '@automattic/jetpack-explat';
4
+ import { initializeExPlat, loadExperimentAssignmentWithAuth } from '@automattic/jetpack-explat';
5
5
  import { select } from '@wordpress/data';
6
6
  import debugFactory from 'debug';
7
7
  const debug = debugFactory('ai-client:chrome-ai-availability');
@@ -28,7 +28,7 @@ export async function isChromeAIAvailable() {
28
28
  }
29
29
  initializeExPlat();
30
30
  debug('initialized explat');
31
- const { variationName } = await loadExperimentAssignment('calypso_jetpack_ai_gemini_api_202503_v1');
31
+ const { variationName } = await loadExperimentAssignmentWithAuth('calypso_jetpack_ai_gemini_api_202503_v1');
32
32
  debug('variationName', variationName);
33
33
  return variationName === 'treatment';
34
34
  }
@@ -22,6 +22,7 @@ export default class ChromeAISuggestionsEventSource extends EventTarget {
22
22
  this.initSource(data);
23
23
  }
24
24
  initSource({ content, promptType, options = {}, }) {
25
+ debug('initSource', content, promptType, options);
25
26
  if (promptType === PROMPT_TYPE_CHANGE_LANGUAGE) {
26
27
  this.translate(content, options.targetLanguage, options.sourceLanguage);
27
28
  }
@@ -62,11 +63,25 @@ export default class ChromeAISuggestionsEventSource extends EventTarget {
62
63
  if (!('Translator' in self)) {
63
64
  return;
64
65
  }
66
+ const translatorAvailability = await self.Translator.availability({
67
+ sourceLanguage: source,
68
+ targetLanguage: target,
69
+ });
70
+ if (translatorAvailability === 'unavailable') {
71
+ debug('awaiting translator ready');
72
+ this.processErrorEvent({
73
+ message: 'Translator is unavailable',
74
+ });
75
+ return;
76
+ }
65
77
  const translator = await self.Translator.create({
66
78
  sourceLanguage: source,
67
79
  targetLanguage: target,
68
80
  });
69
81
  if (!translator) {
82
+ this.processErrorEvent({
83
+ message: 'Translator failed to initialize',
84
+ });
70
85
  return;
71
86
  }
72
87
  try {
@@ -100,24 +115,32 @@ export default class ChromeAISuggestionsEventSource extends EventTarget {
100
115
  }
101
116
  // use the Chrome AI summarizer
102
117
  async summarize(text, tone, wordCount) {
118
+ debug('summarize', text, tone, wordCount);
103
119
  if (!('Summarizer' in self)) {
104
120
  return;
105
121
  }
106
122
  const availability = await self.Summarizer.availability();
107
123
  if (availability === 'unavailable') {
124
+ this.processErrorEvent({
125
+ data: { message: 'Summarizer is unavailable' },
126
+ });
108
127
  return;
109
128
  }
110
129
  const summarizerOptions = this.getSummarizerOptions(tone, wordCount);
111
130
  const summarizer = await self.Summarizer.create(summarizerOptions);
112
131
  if (availability !== 'available') {
132
+ debug('awaiting summarizer ready');
113
133
  await summarizer.ready;
114
134
  }
115
135
  try {
116
136
  const context = `Write with a ${tone} tone.`;
137
+ debug('context', context);
117
138
  let summary = await summarizer.summarize(text, { context: context });
139
+ debug('summary', summary);
118
140
  wordCount = wordCount ?? 50;
119
141
  // gemini-nano has a tendency to exceed the word count, so we need to check and summarize again if necessary
120
142
  if (summary.split(' ').length > wordCount) {
143
+ debug('summary exceeds word count');
121
144
  summary = await summarizer.summarize(summary, { context: context });
122
145
  }
123
146
  this.processEvent({
@@ -130,6 +153,7 @@ export default class ChromeAISuggestionsEventSource extends EventTarget {
130
153
  });
131
154
  }
132
155
  catch (error) {
156
+ debug('error', error);
133
157
  this.processErrorEvent(error);
134
158
  }
135
159
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@automattic/jetpack-ai-client",
4
- "version": "0.32.0",
4
+ "version": "0.32.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": {
@@ -46,11 +46,11 @@
46
46
  "types": "./build/index.d.ts",
47
47
  "dependencies": {
48
48
  "@automattic/jetpack-base-styles": "^1.0.1",
49
- "@automattic/jetpack-components": "^1.1.5",
50
- "@automattic/jetpack-connection": "^1.2.5",
51
- "@automattic/jetpack-script-data": "^0.4.3",
49
+ "@automattic/jetpack-components": "^1.1.6",
50
+ "@automattic/jetpack-connection": "^1.2.6",
51
+ "@automattic/jetpack-script-data": "^0.4.4",
52
52
  "@automattic/jetpack-explat": "workspace:*",
53
- "@automattic/jetpack-shared-extension-utils": "^1.2.0",
53
+ "@automattic/jetpack-shared-extension-utils": "^1.2.1",
54
54
  "@microsoft/fetch-event-source": "2.0.1",
55
55
  "@types/jest": "30.0.0",
56
56
  "@types/react": "18.3.23",
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { initializeExPlat, loadExperimentAssignment } from '@automattic/jetpack-explat';
4
+ import { initializeExPlat, loadExperimentAssignmentWithAuth } from '@automattic/jetpack-explat';
5
5
  import { select } from '@wordpress/data';
6
6
  import debugFactory from 'debug';
7
7
 
@@ -48,7 +48,7 @@ export async function isChromeAIAvailable() {
48
48
  initializeExPlat();
49
49
  debug( 'initialized explat' );
50
50
 
51
- const { variationName } = await loadExperimentAssignment(
51
+ const { variationName } = await loadExperimentAssignmentWithAuth(
52
52
  'calypso_jetpack_ai_gemini_api_202503_v1'
53
53
  );
54
54
 
@@ -66,6 +66,7 @@ export default class ChromeAISuggestionsEventSource extends EventTarget {
66
66
  promptType,
67
67
  options = {},
68
68
  }: ChromeAISuggestionsEventSourceConstructorArgs ) {
69
+ debug( 'initSource', content, promptType, options );
69
70
  if ( promptType === PROMPT_TYPE_CHANGE_LANGUAGE ) {
70
71
  this.translate( content, options.targetLanguage, options.sourceLanguage );
71
72
  }
@@ -119,12 +120,28 @@ export default class ChromeAISuggestionsEventSource extends EventTarget {
119
120
  return;
120
121
  }
121
122
 
123
+ const translatorAvailability = await self.Translator.availability( {
124
+ sourceLanguage: source,
125
+ targetLanguage: target,
126
+ } );
127
+
128
+ if ( translatorAvailability === 'unavailable' ) {
129
+ debug( 'awaiting translator ready' );
130
+ this.processErrorEvent( {
131
+ message: 'Translator is unavailable',
132
+ } );
133
+ return;
134
+ }
135
+
122
136
  const translator = await self.Translator.create( {
123
137
  sourceLanguage: source,
124
138
  targetLanguage: target,
125
139
  } );
126
140
 
127
141
  if ( ! translator ) {
142
+ this.processErrorEvent( {
143
+ message: 'Translator failed to initialize',
144
+ } );
128
145
  return;
129
146
  }
130
147
 
@@ -166,6 +183,7 @@ export default class ChromeAISuggestionsEventSource extends EventTarget {
166
183
 
167
184
  // use the Chrome AI summarizer
168
185
  async summarize( text: string, tone?: string, wordCount?: number ) {
186
+ debug( 'summarize', text, tone, wordCount );
169
187
  if ( ! ( 'Summarizer' in self ) ) {
170
188
  return;
171
189
  }
@@ -173,6 +191,9 @@ export default class ChromeAISuggestionsEventSource extends EventTarget {
173
191
  const availability = await self.Summarizer.availability();
174
192
 
175
193
  if ( availability === 'unavailable' ) {
194
+ this.processErrorEvent( {
195
+ data: { message: 'Summarizer is unavailable' },
196
+ } );
176
197
  return;
177
198
  }
178
199
 
@@ -181,17 +202,20 @@ export default class ChromeAISuggestionsEventSource extends EventTarget {
181
202
  const summarizer = await self.Summarizer.create( summarizerOptions );
182
203
 
183
204
  if ( availability !== 'available' ) {
205
+ debug( 'awaiting summarizer ready' );
184
206
  await summarizer.ready;
185
207
  }
186
208
 
187
209
  try {
188
210
  const context = `Write with a ${ tone } tone.`;
211
+ debug( 'context', context );
189
212
  let summary = await summarizer.summarize( text, { context: context } );
190
-
213
+ debug( 'summary', summary );
191
214
  wordCount = wordCount ?? 50;
192
215
 
193
216
  // gemini-nano has a tendency to exceed the word count, so we need to check and summarize again if necessary
194
217
  if ( summary.split( ' ' ).length > wordCount ) {
218
+ debug( 'summary exceeds word count' );
195
219
  summary = await summarizer.summarize( summary, { context: context } );
196
220
  }
197
221
 
@@ -204,6 +228,7 @@ export default class ChromeAISuggestionsEventSource extends EventTarget {
204
228
  } ),
205
229
  } );
206
230
  } catch ( error ) {
231
+ debug( 'error', error );
207
232
  this.processErrorEvent( error );
208
233
  }
209
234
  }