@jupyterlite/ai 0.6.1 → 0.7.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.
Files changed (65) hide show
  1. package/README.md +1 -1
  2. package/lib/base-completer.d.ts +0 -5
  3. package/lib/chat-handler.d.ts +19 -5
  4. package/lib/chat-handler.js +47 -26
  5. package/lib/completion-provider.d.ts +2 -2
  6. package/lib/completion-provider.js +4 -3
  7. package/lib/components/stop-button.d.ts +0 -1
  8. package/lib/default-providers/Anthropic/completer.d.ts +1 -3
  9. package/lib/default-providers/Anthropic/completer.js +4 -6
  10. package/lib/default-providers/ChromeAI/completer.d.ts +1 -3
  11. package/lib/default-providers/ChromeAI/completer.js +4 -6
  12. package/lib/default-providers/ChromeAI/instructions.d.ts +4 -0
  13. package/lib/default-providers/ChromeAI/instructions.js +18 -0
  14. package/lib/default-providers/MistralAI/completer.d.ts +1 -3
  15. package/lib/default-providers/MistralAI/completer.js +5 -6
  16. package/lib/default-providers/Ollama/completer.d.ts +17 -0
  17. package/lib/default-providers/Ollama/completer.js +49 -0
  18. package/lib/default-providers/Ollama/instructions.d.ts +2 -0
  19. package/lib/default-providers/Ollama/instructions.js +70 -0
  20. package/lib/default-providers/Ollama/settings-schema.json +146 -0
  21. package/lib/default-providers/OpenAI/completer.d.ts +1 -3
  22. package/lib/default-providers/OpenAI/completer.js +4 -6
  23. package/lib/default-providers/WebLLM/completer.d.ts +27 -0
  24. package/lib/default-providers/WebLLM/completer.js +136 -0
  25. package/lib/default-providers/WebLLM/instructions.d.ts +6 -0
  26. package/lib/default-providers/WebLLM/instructions.js +32 -0
  27. package/lib/default-providers/WebLLM/settings-schema.json +21 -0
  28. package/lib/default-providers/index.js +119 -4
  29. package/lib/index.d.ts +2 -2
  30. package/lib/index.js +16 -26
  31. package/lib/provider.d.ts +11 -13
  32. package/lib/provider.js +124 -54
  33. package/lib/settings/index.d.ts +0 -1
  34. package/lib/settings/index.js +0 -1
  35. package/lib/settings/panel.d.ts +44 -11
  36. package/lib/settings/panel.js +231 -130
  37. package/lib/tokens.d.ts +21 -2
  38. package/lib/types/ai-model.d.ts +24 -0
  39. package/lib/types/ai-model.js +5 -0
  40. package/package.json +15 -12
  41. package/schema/provider-registry.json +0 -6
  42. package/src/base-completer.ts +0 -6
  43. package/src/chat-handler.ts +40 -7
  44. package/src/completion-provider.ts +2 -2
  45. package/src/default-providers/Anthropic/completer.ts +3 -8
  46. package/src/default-providers/ChromeAI/completer.ts +3 -8
  47. package/src/default-providers/ChromeAI/instructions.ts +21 -0
  48. package/src/default-providers/MistralAI/completer.ts +3 -8
  49. package/src/default-providers/Ollama/completer.ts +62 -0
  50. package/src/default-providers/Ollama/instructions.ts +70 -0
  51. package/src/default-providers/OpenAI/completer.ts +3 -8
  52. package/src/default-providers/WebLLM/completer.ts +162 -0
  53. package/src/default-providers/WebLLM/instructions.ts +33 -0
  54. package/src/default-providers/index.ts +151 -14
  55. package/src/index.ts +17 -29
  56. package/src/provider.ts +135 -46
  57. package/src/settings/index.ts +0 -1
  58. package/src/settings/panel.tsx +223 -79
  59. package/src/tokens.ts +23 -2
  60. package/src/types/ai-model.ts +37 -0
  61. package/src/types/service-worker.d.ts +6 -0
  62. package/style/base.css +5 -0
  63. package/lib/settings/settings-connector.d.ts +0 -31
  64. package/lib/settings/settings-connector.js +0 -61
  65. package/src/settings/settings-connector.ts +0 -89
@@ -1,10 +1,10 @@
1
1
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
2
+ import { ISettingRegistry } from '@jupyterlab/settingregistry';
2
3
  import {
3
- ISettingConnector,
4
- ISettingRegistry
5
- } from '@jupyterlab/settingregistry';
6
- import { FormComponent, IFormRenderer } from '@jupyterlab/ui-components';
7
- import { ArrayExt } from '@lumino/algorithm';
4
+ Button,
5
+ FormComponent,
6
+ IFormRenderer
7
+ } from '@jupyterlab/ui-components';
8
8
  import { JSONExt } from '@lumino/coreutils';
9
9
  import { IChangeEvent } from '@rjsf/core';
10
10
  import type { FieldProps } from '@rjsf/utils';
@@ -13,13 +13,14 @@ import { JSONSchema7 } from 'json-schema';
13
13
  import { ISecretsManager } from 'jupyter-secrets-manager';
14
14
  import React from 'react';
15
15
 
16
- import { getSecretId, SettingConnector } from '.';
16
+ import { getSecretId, SECRETS_REPLACEMENT } from '.';
17
17
  import baseSettings from './base.json';
18
18
  import { IAIProviderRegistry, IDict, PLUGIN_IDS } from '../tokens';
19
19
 
20
20
  const MD_MIME_TYPE = 'text/markdown';
21
21
  const STORAGE_NAME = '@jupyterlite/ai:settings';
22
22
  const INSTRUCTION_CLASS = 'jp-AISettingsInstructions';
23
+ const ERROR_CLASS = 'jp-AISettingsError';
23
24
  const SECRETS_NAMESPACE = PLUGIN_IDS.providerRegistry;
24
25
 
25
26
  export const aiSettingsRenderer = (options: {
@@ -27,7 +28,6 @@ export const aiSettingsRenderer = (options: {
27
28
  secretsToken?: symbol;
28
29
  rmRegistry?: IRenderMimeRegistry;
29
30
  secretsManager?: ISecretsManager;
30
- settingConnector?: ISettingConnector;
31
31
  }): IFormRenderer => {
32
32
  const { secretsToken } = options;
33
33
  delete options.secretsToken;
@@ -45,6 +45,8 @@ export const aiSettingsRenderer = (options: {
45
45
  export interface ISettingsFormStates {
46
46
  schema: JSONSchema7;
47
47
  instruction: HTMLElement | null;
48
+ compatibilityError: string | null;
49
+ isModified?: boolean;
48
50
  }
49
51
 
50
52
  const WrappedFormComponent = (props: any): JSX.Element => {
@@ -65,13 +67,12 @@ export class AiSettings extends React.Component<
65
67
  this._providerRegistry = props.formContext.providerRegistry;
66
68
  this._rmRegistry = props.formContext.rmRegistry ?? null;
67
69
  this._secretsManager = props.formContext.secretsManager ?? null;
68
- this._settingConnector = props.formContext.settingConnector ?? null;
69
70
  this._settings = props.formContext.settings;
70
71
 
71
- this._useSecretsManager =
72
+ const useSecretsManagerSetting =
72
73
  (this._settings.get('UseSecretsManager').composite as boolean) ?? true;
73
- this._hideSecretFields =
74
- (this._settings.get('HideSecretFields').composite as boolean) ?? true;
74
+ this._useSecretsManager =
75
+ useSecretsManagerSetting && this._secretsManager !== null;
75
76
 
76
77
  // Initialize the providers schema.
77
78
  const providerSchema = JSONExt.deepCopy(baseSettings) as any;
@@ -85,7 +86,7 @@ export class AiSettings extends React.Component<
85
86
  this._providerSchema = providerSchema as JSONSchema7;
86
87
 
87
88
  // Check if there is saved values in local storage, otherwise use the settings from
88
- // the setting registry (led to default if there are no user settings).
89
+ // the setting registry (leads to default if there are no user settings).
89
90
  const storageSettings = localStorage.getItem(STORAGE_NAME);
90
91
  if (storageSettings === null) {
91
92
  const labSettings = this._settings.get('AIprovider').composite;
@@ -105,47 +106,43 @@ export class AiSettings extends React.Component<
105
106
 
106
107
  // Initialize the settings from the saved ones.
107
108
  this._provider = this.getCurrentProvider();
108
- this._currentSettings = this.getSettings();
109
109
 
110
110
  // Initialize the schema.
111
111
  const schema = this._buildSchema();
112
- this.state = { schema, instruction: null };
113
112
 
113
+ // Initialize the current settings.
114
+ const isModified = this._updatedFormData(
115
+ this.getSettingsFromLocalStorage()
116
+ );
117
+
118
+ this.state = {
119
+ schema,
120
+ instruction: null,
121
+ compatibilityError: null,
122
+ isModified: isModified
123
+ };
114
124
  this._renderInstruction();
115
125
 
126
+ this._checkProviderCompatibility();
127
+
116
128
  // Update the setting registry.
117
- this._settings
118
- .set('AIprovider', this._currentSettings)
119
- .catch(console.error);
129
+ this.saveSettingsToRegistry();
120
130
 
121
- this._settings.changed.connect(() => {
122
- const useSecretsManager =
123
- (this._settings.get('UseSecretsManager').composite as boolean) ?? true;
124
- if (useSecretsManager !== this._useSecretsManager) {
125
- this.updateUseSecretsManager(useSecretsManager);
126
- }
127
- const hideSecretFields =
128
- (this._settings.get('HideSecretFields').composite as boolean) ?? true;
129
- if (hideSecretFields !== this._hideSecretFields) {
130
- this._hideSecretFields = hideSecretFields;
131
- this._updateSchema();
132
- }
133
- });
131
+ this._secretsManager?.fieldVisibilityChanged.connect(
132
+ this._fieldVisibilityChanged
133
+ );
134
+
135
+ this._settings.changed.connect(this._settingsChanged);
134
136
  }
135
137
 
136
138
  async componentDidUpdate(): Promise<void> {
137
139
  if (!this._secretsManager || !this._useSecretsManager) {
138
140
  return;
139
141
  }
140
- // Attach the password inputs to the secrets manager only if they have changed.
141
- const inputs = this._formRef.current?.getElementsByTagName('input') || [];
142
- if (ArrayExt.shallowEqual(inputs, this._formInputs)) {
143
- return;
144
- }
145
142
 
143
+ // Attach the password inputs to the secrets manager.
146
144
  await this._secretsManager.detachAll(Private.getToken(), SECRETS_NAMESPACE);
147
- this._formInputs = [...inputs];
148
- this._unsavedFields = [];
145
+ const inputs = this._formRef.current?.getElementsByTagName('input') || [];
149
146
  for (let i = 0; i < inputs.length; i++) {
150
147
  if (inputs[i].type.toLowerCase() === 'password') {
151
148
  const label = inputs[i].getAttribute('label');
@@ -158,16 +155,16 @@ export class AiSettings extends React.Component<
158
155
  inputs[i],
159
156
  (value: string) => this._onPasswordUpdated(label, value)
160
157
  );
161
- this._unsavedFields.push(label);
162
158
  }
163
159
  }
164
160
  }
165
- if (this._settingConnector instanceof SettingConnector) {
166
- this._settingConnector.doNotSave = this._unsavedFields;
167
- }
168
161
  }
169
162
 
170
163
  componentWillUnmount(): void {
164
+ this._settings.changed.disconnect(this._settingsChanged);
165
+ this._secretsManager?.fieldVisibilityChanged.disconnect(
166
+ this._fieldVisibilityChanged
167
+ );
171
168
  if (!this._secretsManager || !this._useSecretsManager) {
172
169
  return;
173
170
  }
@@ -194,7 +191,7 @@ export class AiSettings extends React.Component<
194
191
  /**
195
192
  * Get settings from local storage for a given provider.
196
193
  */
197
- getSettings(): IDict<any> {
194
+ getSettingsFromLocalStorage(): IDict<any> {
198
195
  const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
199
196
  return settings[this._provider] ?? { provider: this._provider };
200
197
  }
@@ -202,29 +199,72 @@ export class AiSettings extends React.Component<
202
199
  /**
203
200
  * Save settings in local storage for a given provider.
204
201
  */
205
- saveSettings(value: IDict<any>) {
206
- const currentSettings = { ...value };
202
+ saveSettingsToLocalStorage() {
203
+ const currentSettings = { ...this._currentSettings };
207
204
  const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) ?? '{}');
208
- this._unsavedFields.forEach(field => delete currentSettings[field]);
205
+ // Do not save secrets in local storage if using the secrets manager.
206
+ if (this._useSecretsManager) {
207
+ this._secretFields.forEach(field => delete currentSettings[field]);
208
+ }
209
209
  settings[this._provider] = currentSettings;
210
210
  localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
211
211
  }
212
212
 
213
- private updateUseSecretsManager = (value: boolean) => {
213
+ /**
214
+ * Save the settings to the setting registry.
215
+ */
216
+ saveSettingsToRegistry(): void {
217
+ const sanitizedSettings = { ...this._currentSettings };
218
+ if (this._useSecretsManager) {
219
+ this._secretFields.forEach(field => {
220
+ sanitizedSettings[field] = SECRETS_REPLACEMENT;
221
+ });
222
+ }
223
+ this._settings
224
+ .set('AIprovider', { provider: this._provider, ...sanitizedSettings })
225
+ .catch(console.error);
226
+ }
227
+
228
+ /**
229
+ * Triggered when the settings has changed.
230
+ */
231
+ private _settingsChanged = (settings: ISettingRegistry.ISettings) => {
232
+ this._updateUseSecretsManager(
233
+ (this._settings.get('UseSecretsManager').composite as boolean) ?? true
234
+ );
235
+ };
236
+
237
+ /**
238
+ * Triggered when the secret fields visibility has changed.
239
+ */
240
+ private _fieldVisibilityChanged = (
241
+ _: ISecretsManager,
242
+ value: boolean
243
+ ): void => {
244
+ if (this._useSecretsManager) {
245
+ this._updateSchema();
246
+ }
247
+ };
248
+
249
+ /**
250
+ * Update the settings whether the secrets manager is used or not.
251
+ *
252
+ * @param value - whether to use the secrets manager or not.
253
+ */
254
+ private _updateUseSecretsManager = (value: boolean) => {
255
+ // No-op if the value did not change or the secrets manager has not been provided.
256
+ if (value === this._useSecretsManager || this._secretsManager === null) {
257
+ return;
258
+ }
259
+
260
+ // Update the secrets manager.
214
261
  this._useSecretsManager = value;
215
262
  if (!value) {
216
263
  // Detach all the password inputs attached to the secrets manager, and save the
217
264
  // current settings to the local storage to save the password.
218
- this._secretsManager?.detachAll(Private.getToken(), SECRETS_NAMESPACE);
219
- this._formInputs = [];
220
- this._unsavedFields = [];
221
- if (this._settingConnector instanceof SettingConnector) {
222
- this._settingConnector.doNotSave = [];
223
- }
224
- this.saveSettings(this._currentSettings);
265
+ this._secretsManager.detachAll(Private.getToken(), SECRETS_NAMESPACE);
225
266
  } else {
226
- // Remove all the keys stored locally and attach the password inputs to the
227
- // secrets manager.
267
+ // Remove all the keys stored locally.
228
268
  const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
229
269
  Object.keys(settings).forEach(provider => {
230
270
  Object.keys(settings[provider])
@@ -234,11 +274,10 @@ export class AiSettings extends React.Component<
234
274
  });
235
275
  });
236
276
  localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
237
- this.componentDidUpdate();
238
277
  }
239
- this._settings
240
- .set('AIprovider', { provider: this._provider, ...this._currentSettings })
241
- .catch(console.error);
278
+ this._updateSchema();
279
+ this.saveSettingsToLocalStorage();
280
+ this.saveSettingsToRegistry();
242
281
  };
243
282
 
244
283
  /**
@@ -251,17 +290,31 @@ export class AiSettings extends React.Component<
251
290
  this._provider
252
291
  );
253
292
 
293
+ this._secretFields = [];
294
+ this._defaultFormData = {};
254
295
  if (settingsSchema) {
255
296
  Object.entries(settingsSchema).forEach(([key, value]) => {
256
297
  if (key.toLowerCase().includes('key')) {
257
- if (this._hideSecretFields) {
298
+ this._secretFields.push(key);
299
+
300
+ // If the secrets manager is not used, do not show the secrets fields.
301
+ // If the secrets manager is used, check if the fields should be visible.
302
+ const showSecretFields =
303
+ !this._useSecretsManager ||
304
+ (this._secretsManager?.secretFieldsVisibility ?? true);
305
+ if (!showSecretFields) {
258
306
  return;
259
307
  }
308
+
260
309
  this._uiSchema[key] = { 'ui:widget': 'password' };
261
310
  }
262
311
  schema.properties[key] = value;
312
+ if (value.default !== undefined) {
313
+ this._defaultFormData[key] = value.default;
314
+ }
263
315
  });
264
316
  }
317
+
265
318
  return schema as JSONSchema7;
266
319
  }
267
320
 
@@ -293,7 +346,30 @@ export class AiSettings extends React.Component<
293
346
  }
294
347
 
295
348
  /**
296
- * Triggered when the provider hes changed, to update the schema and values.
349
+ * Check for compatibility of the provider with the current environment.
350
+ * If the provider is not compatible, display an error message.
351
+ */
352
+ private async _checkProviderCompatibility(): Promise<void> {
353
+ const compatibilityCheck = this._providerRegistry.getCompatibilityCheck(
354
+ this._provider
355
+ );
356
+ if (!compatibilityCheck) {
357
+ this.setState({ compatibilityError: null });
358
+ return;
359
+ }
360
+ const error = await compatibilityCheck();
361
+ if (!error) {
362
+ this.setState({ compatibilityError: null });
363
+ return;
364
+ }
365
+ const errorDiv = document.createElement('div');
366
+ errorDiv.className = ERROR_CLASS;
367
+ errorDiv.innerHTML = error;
368
+ this.setState({ compatibilityError: error });
369
+ }
370
+
371
+ /**
372
+ * Triggered when the provider has changed, to update the schema and values.
297
373
  * Update the Jupyterlab settings accordingly.
298
374
  */
299
375
  private _onProviderChanged = (e: IChangeEvent) => {
@@ -303,12 +379,18 @@ export class AiSettings extends React.Component<
303
379
  }
304
380
  this._provider = provider;
305
381
  this.saveCurrentProvider();
306
- this._currentSettings = this.getSettings();
307
382
  this._updateSchema();
308
383
  this._renderInstruction();
309
- this._settings
310
- .set('AIprovider', { provider: this._provider, ...this._currentSettings })
311
- .catch(console.error);
384
+ this._checkProviderCompatibility();
385
+
386
+ // Initialize the current settings.
387
+ const isModified = this._updatedFormData(
388
+ this.getSettingsFromLocalStorage()
389
+ );
390
+ if (isModified !== this.state.isModified) {
391
+ this.setState({ isModified });
392
+ }
393
+ this.saveSettingsToRegistry();
312
394
  };
313
395
 
314
396
  /**
@@ -317,22 +399,65 @@ export class AiSettings extends React.Component<
317
399
  */
318
400
  private _onPasswordUpdated = (fieldName: string, value: string) => {
319
401
  this._currentSettings[fieldName] = value;
320
- this._settings
321
- .set('AIprovider', { provider: this._provider, ...this._currentSettings })
322
- .catch(console.error);
402
+ this.saveSettingsToRegistry();
323
403
  };
324
404
 
405
+ /**
406
+ * Update the current settings with the new values from the form.
407
+ *
408
+ * @param data - The form data to update.
409
+ * @returns - Boolean whether the form is not the default one.
410
+ */
411
+ private _updatedFormData(data: IDict): boolean {
412
+ let isModified = false;
413
+ Object.entries(data).forEach(([key, value]) => {
414
+ if (this._defaultFormData[key] !== undefined) {
415
+ if (value === undefined) {
416
+ const schemaProperty = this.state.schema.properties?.[
417
+ key
418
+ ] as JSONSchema7;
419
+ if (schemaProperty.type === 'string') {
420
+ data[key] = '';
421
+ }
422
+ }
423
+ if (value !== this._defaultFormData[key]) {
424
+ isModified = true;
425
+ }
426
+ }
427
+ });
428
+ this._currentSettings = JSONExt.deepCopy(data);
429
+ return isModified;
430
+ }
431
+
325
432
  /**
326
433
  * Triggered when the form value has changed, to update the current settings and save
327
434
  * it in local storage.
328
435
  * Update the Jupyterlab settings accordingly.
329
436
  */
330
- private _onFormChange = (e: IChangeEvent) => {
331
- this._currentSettings = JSONExt.deepCopy(e.formData);
332
- this.saveSettings(this._currentSettings);
333
- this._settings
334
- .set('AIprovider', { provider: this._provider, ...this._currentSettings })
335
- .catch(console.error);
437
+ private _onFormChanged = (e: IChangeEvent): void => {
438
+ const { formData } = e;
439
+ const isModified = this._updatedFormData(formData);
440
+ this.saveSettingsToLocalStorage();
441
+ this.saveSettingsToRegistry();
442
+ if (isModified !== this.state.isModified) {
443
+ this.setState({ isModified });
444
+ }
445
+ };
446
+
447
+ /**
448
+ * Handler for the "Restore to defaults" button - clears all
449
+ * modified settings then calls `setFormData` to restore the
450
+ * values.
451
+ */
452
+ private _reset = async (event: React.MouseEvent): Promise<void> => {
453
+ event.stopPropagation();
454
+ this._currentSettings = {
455
+ ...this._currentSettings,
456
+ ...this._defaultFormData
457
+ };
458
+ this.saveSettingsToLocalStorage();
459
+ this.saveSettingsToRegistry();
460
+ this.setState({ isModified: false });
336
461
  };
337
462
 
338
463
  render(): JSX.Element {
@@ -343,6 +468,12 @@ export class AiSettings extends React.Component<
343
468
  schema={this._providerSchema}
344
469
  onChange={this._onProviderChanged}
345
470
  />
471
+ {this.state.compatibilityError !== null && (
472
+ <div className={ERROR_CLASS}>
473
+ <i className={'fas fa-exclamation-triangle'}></i>
474
+ <span>{this.state.compatibilityError}</span>
475
+ </div>
476
+ )}
346
477
  {this.state.instruction !== null && (
347
478
  <details>
348
479
  <summary className={INSTRUCTION_CLASS}>Instructions</summary>
@@ -353,11 +484,26 @@ export class AiSettings extends React.Component<
353
484
  />
354
485
  </details>
355
486
  )}
487
+ <div className="jp-SettingsHeader">
488
+ <h3 title={this._provider}>{this._provider}</h3>
489
+ <div className="jp-SettingsHeader-buttonbar">
490
+ {this.state.isModified && (
491
+ <Button className="jp-RestoreButton" onClick={this._reset}>
492
+ Restore to Defaults
493
+ </Button>
494
+ )}
495
+ </div>
496
+ </div>
356
497
  <WrappedFormComponent
357
498
  formData={this._currentSettings}
358
499
  schema={this.state.schema}
359
- onChange={this._onFormChange}
500
+ onChange={this._onFormChanged}
360
501
  uiSchema={this._uiSchema}
502
+ idPrefix={`jp-SettingsEditor-${PLUGIN_IDS.providerRegistry}`}
503
+ formContext={{
504
+ ...this.props.formContext,
505
+ defaultFormData: this._defaultFormData
506
+ }}
361
507
  />
362
508
  </div>
363
509
  );
@@ -367,16 +513,14 @@ export class AiSettings extends React.Component<
367
513
  private _provider: string;
368
514
  private _providerSchema: JSONSchema7;
369
515
  private _useSecretsManager: boolean;
370
- private _hideSecretFields: boolean;
371
516
  private _rmRegistry: IRenderMimeRegistry | null;
372
517
  private _secretsManager: ISecretsManager | null;
373
- private _settingConnector: ISettingConnector | null;
374
518
  private _currentSettings: IDict<any> = { provider: 'None' };
375
519
  private _uiSchema: IDict<any> = {};
376
520
  private _settings: ISettingRegistry.ISettings;
377
521
  private _formRef = React.createRef<HTMLDivElement>();
378
- private _unsavedFields: string[] = [];
379
- private _formInputs: HTMLInputElement[] = [];
522
+ private _secretFields: string[] = [];
523
+ private _defaultFormData: IDict<any> = {};
380
524
  }
381
525
 
382
526
  namespace Private {
package/src/tokens.ts CHANGED
@@ -4,6 +4,7 @@ import { ISignal } from '@lumino/signaling';
4
4
  import { JSONSchema7 } from 'json-schema';
5
5
 
6
6
  import { IBaseCompleter } from './base-completer';
7
+ import { AIChatModel, AICompleter } from './types/ai-model';
7
8
 
8
9
  export const PLUGIN_IDS = {
9
10
  chat: '@jupyterlite/ai:chat',
@@ -51,6 +52,20 @@ export interface IAIProvider {
51
52
  * Default to `(error) => error.message`.
52
53
  */
53
54
  errorMessage?: (error: any) => string;
55
+ /**
56
+ * Compatibility check function, to determine if the provider is compatible with the
57
+ * current environment.
58
+ */
59
+ compatibilityCheck?: () => Promise<string | null>;
60
+ /**
61
+ * Whether to expose or not the chat model.
62
+ *
63
+ * ### CAUTION
64
+ * This flag will expose the whole chat model API, which may contain private keys.
65
+ * Be sure to use it with a model that does not expose sensitive information in the
66
+ * API.
67
+ */
68
+ exposeChatModel?: boolean;
54
69
  }
55
70
 
56
71
  /**
@@ -72,11 +87,11 @@ export interface IAIProviderRegistry {
72
87
  /**
73
88
  * Get the current completer of the completion provider.
74
89
  */
75
- currentCompleter: IBaseCompleter | null;
90
+ currentCompleter: AICompleter | null;
76
91
  /**
77
92
  * Get the current llm chat model.
78
93
  */
79
- currentChatModel: BaseChatModel | null;
94
+ currentChatModel: AIChatModel | null;
80
95
  /**
81
96
  * Get the settings schema of a given provider.
82
97
  */
@@ -85,6 +100,12 @@ export interface IAIProviderRegistry {
85
100
  * Get the instructions of a given provider.
86
101
  */
87
102
  getInstructions(provider: string): string | undefined;
103
+ /**
104
+ * Get the compatibility check function of a given provider.
105
+ */
106
+ getCompatibilityCheck(
107
+ provider: string
108
+ ): (() => Promise<string | null>) | undefined;
88
109
  /**
89
110
  * Format an error message from the current provider.
90
111
  */
@@ -0,0 +1,37 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ import {
7
+ CompletionHandler,
8
+ IInlineCompletionContext
9
+ } from '@jupyterlab/completer';
10
+ import { IterableReadableStream } from '@langchain/core/utils/stream';
11
+
12
+ /**
13
+ * The reduced AI chat model interface.
14
+ */
15
+ export type AIChatModel = {
16
+ /**
17
+ * The stream function of the chat model.
18
+ */
19
+ stream: (input: any, options?: any) => Promise<IterableReadableStream<any>>;
20
+ };
21
+
22
+ /**
23
+ * The reduced AI completer interface.
24
+ */
25
+ export type AICompleter = {
26
+ /**
27
+ * The fetch function of the completer.
28
+ */
29
+ fetch: (
30
+ request: CompletionHandler.IRequest,
31
+ context: IInlineCompletionContext
32
+ ) => Promise<any>;
33
+ /**
34
+ * The optional request completion function of the completer.
35
+ */
36
+ requestCompletion?: () => void;
37
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Type declarations for Service Worker APIs not included in standard TypeScript libs
3
+ */
4
+ interface ExtendableMessageEvent extends MessageEvent {
5
+ waitUntil(f: Promise<any>): void;
6
+ }
package/style/base.css CHANGED
@@ -9,3 +9,8 @@
9
9
  .jp-AISettingsInstructions {
10
10
  font-size: var(--jp-content-font-size1);
11
11
  }
12
+
13
+ .jp-AISettingsError {
14
+ color: var(--jp-error-color1);
15
+ font-size: var(--jp-content-font-size1);
16
+ }
@@ -1,31 +0,0 @@
1
- import { ISettingConnector, ISettingRegistry } from '@jupyterlab/settingregistry';
2
- import { DataConnector, IDataConnector } from '@jupyterlab/statedb';
3
- /**
4
- * A data connector for fetching settings.
5
- *
6
- * #### Notes
7
- * This connector adds a query parameter to the base services setting manager.
8
- */
9
- export declare class SettingConnector extends DataConnector<ISettingRegistry.IPlugin, string> implements ISettingConnector {
10
- constructor(connector: IDataConnector<ISettingRegistry.IPlugin, string>);
11
- set doNotSave(fields: string[]);
12
- /**
13
- * Fetch settings for a plugin.
14
- * @param id - The plugin ID
15
- *
16
- * #### Notes
17
- * The REST API requests are throttled at one request per plugin per 100ms.
18
- */
19
- fetch(id: string): Promise<ISettingRegistry.IPlugin | undefined>;
20
- list(query: 'ids'): Promise<{
21
- ids: string[];
22
- }>;
23
- list(query: 'active' | 'all'): Promise<{
24
- ids: string[];
25
- values: ISettingRegistry.IPlugin[];
26
- }>;
27
- save(id: string, raw: string): Promise<void>;
28
- private _connector;
29
- private _doNotSave;
30
- private _throttlers;
31
- }