@jupyterlite/ai 0.6.0 → 0.6.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/lib/base-completer.d.ts +1 -1
- package/lib/default-providers/Anthropic/completer.d.ts +2 -2
- package/lib/default-providers/Anthropic/completer.js +4 -4
- package/lib/default-providers/ChromeAI/completer.d.ts +2 -2
- package/lib/default-providers/ChromeAI/completer.js +4 -4
- package/lib/default-providers/MistralAI/completer.d.ts +2 -2
- package/lib/default-providers/MistralAI/completer.js +4 -4
- package/lib/default-providers/OpenAI/completer.d.ts +2 -2
- package/lib/default-providers/OpenAI/completer.js +4 -4
- package/lib/index.js +15 -11
- package/lib/provider.d.ts +4 -0
- package/lib/provider.js +30 -4
- package/lib/settings/panel.d.ts +8 -6
- package/lib/settings/panel.js +80 -36
- package/lib/settings/settings-connector.js +3 -3
- package/lib/settings/utils.d.ts +0 -1
- package/lib/settings/utils.js +0 -1
- package/lib/tokens.d.ts +7 -0
- package/lib/tokens.js +7 -0
- package/package.json +12 -11
- package/schema/provider-registry.json +6 -0
- package/src/base-completer.ts +1 -1
- package/src/default-providers/Anthropic/completer.ts +5 -5
- package/src/default-providers/ChromeAI/completer.ts +5 -5
- package/src/default-providers/MistralAI/completer.ts +5 -5
- package/src/default-providers/OpenAI/completer.ts +5 -5
- package/src/index.ts +62 -57
- package/src/provider.ts +40 -9
- package/src/settings/panel.tsx +85 -35
- package/src/settings/settings-connector.ts +4 -5
- package/src/settings/utils.ts +0 -1
- package/src/tokens.ts +8 -0
package/src/settings/panel.tsx
CHANGED
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
ISettingRegistry
|
|
5
5
|
} from '@jupyterlab/settingregistry';
|
|
6
6
|
import { FormComponent, IFormRenderer } from '@jupyterlab/ui-components';
|
|
7
|
-
import { ArrayExt } from '@lumino/algorithm';
|
|
8
7
|
import { JSONExt } from '@lumino/coreutils';
|
|
9
8
|
import { IChangeEvent } from '@rjsf/core';
|
|
10
9
|
import type { FieldProps } from '@rjsf/utils';
|
|
@@ -13,20 +12,27 @@ import { JSONSchema7 } from 'json-schema';
|
|
|
13
12
|
import { ISecretsManager } from 'jupyter-secrets-manager';
|
|
14
13
|
import React from 'react';
|
|
15
14
|
|
|
16
|
-
import { getSecretId,
|
|
15
|
+
import { getSecretId, SettingConnector } from '.';
|
|
17
16
|
import baseSettings from './base.json';
|
|
18
|
-
import { IAIProviderRegistry, IDict } from '../tokens';
|
|
17
|
+
import { IAIProviderRegistry, IDict, PLUGIN_IDS } from '../tokens';
|
|
19
18
|
|
|
20
19
|
const MD_MIME_TYPE = 'text/markdown';
|
|
21
20
|
const STORAGE_NAME = '@jupyterlite/ai:settings';
|
|
22
21
|
const INSTRUCTION_CLASS = 'jp-AISettingsInstructions';
|
|
22
|
+
const SECRETS_NAMESPACE = PLUGIN_IDS.providerRegistry;
|
|
23
23
|
|
|
24
24
|
export const aiSettingsRenderer = (options: {
|
|
25
25
|
providerRegistry: IAIProviderRegistry;
|
|
26
|
+
secretsToken?: symbol;
|
|
26
27
|
rmRegistry?: IRenderMimeRegistry;
|
|
27
28
|
secretsManager?: ISecretsManager;
|
|
28
29
|
settingConnector?: ISettingConnector;
|
|
29
30
|
}): IFormRenderer => {
|
|
31
|
+
const { secretsToken } = options;
|
|
32
|
+
delete options.secretsToken;
|
|
33
|
+
if (secretsToken) {
|
|
34
|
+
Private.setToken(secretsToken);
|
|
35
|
+
}
|
|
30
36
|
return {
|
|
31
37
|
fieldRenderer: (props: FieldProps) => {
|
|
32
38
|
props.formContext = { ...props.formContext, ...options };
|
|
@@ -63,6 +69,8 @@ export class AiSettings extends React.Component<
|
|
|
63
69
|
|
|
64
70
|
this._useSecretsManager =
|
|
65
71
|
(this._settings.get('UseSecretsManager').composite as boolean) ?? true;
|
|
72
|
+
this._hideSecretFields =
|
|
73
|
+
(this._settings.get('HideSecretFields').composite as boolean) ?? true;
|
|
66
74
|
|
|
67
75
|
// Initialize the providers schema.
|
|
68
76
|
const providerSchema = JSONExt.deepCopy(baseSettings) as any;
|
|
@@ -113,7 +121,13 @@ export class AiSettings extends React.Component<
|
|
|
113
121
|
const useSecretsManager =
|
|
114
122
|
(this._settings.get('UseSecretsManager').composite as boolean) ?? true;
|
|
115
123
|
if (useSecretsManager !== this._useSecretsManager) {
|
|
116
|
-
this.
|
|
124
|
+
this._updateUseSecretsManager(useSecretsManager);
|
|
125
|
+
}
|
|
126
|
+
const hideSecretFields =
|
|
127
|
+
(this._settings.get('HideSecretFields').composite as boolean) ?? true;
|
|
128
|
+
if (hideSecretFields !== this._hideSecretFields) {
|
|
129
|
+
this._hideSecretFields = hideSecretFields;
|
|
130
|
+
this._updateSchema();
|
|
117
131
|
}
|
|
118
132
|
});
|
|
119
133
|
}
|
|
@@ -122,33 +136,32 @@ export class AiSettings extends React.Component<
|
|
|
122
136
|
if (!this._secretsManager || !this._useSecretsManager) {
|
|
123
137
|
return;
|
|
124
138
|
}
|
|
125
|
-
// Attach the password inputs to the secrets manager only if they have changed.
|
|
126
|
-
const inputs = this._formRef.current?.getElementsByTagName('input') || [];
|
|
127
|
-
if (ArrayExt.shallowEqual(inputs, this._formInputs)) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
139
|
|
|
131
|
-
|
|
132
|
-
this.
|
|
133
|
-
this.
|
|
140
|
+
// Attach the password inputs to the secrets manager.
|
|
141
|
+
await this._secretsManager.detachAll(Private.getToken(), SECRETS_NAMESPACE);
|
|
142
|
+
const inputs = this._formRef.current?.getElementsByTagName('input') || [];
|
|
134
143
|
for (let i = 0; i < inputs.length; i++) {
|
|
135
144
|
if (inputs[i].type.toLowerCase() === 'password') {
|
|
136
145
|
const label = inputs[i].getAttribute('label');
|
|
137
146
|
if (label) {
|
|
138
147
|
const id = getSecretId(this._provider, label);
|
|
139
148
|
this._secretsManager.attach(
|
|
149
|
+
Private.getToken(),
|
|
140
150
|
SECRETS_NAMESPACE,
|
|
141
151
|
id,
|
|
142
152
|
inputs[i],
|
|
143
153
|
(value: string) => this._onPasswordUpdated(label, value)
|
|
144
154
|
);
|
|
145
|
-
this._unsavedFields.push(label);
|
|
146
155
|
}
|
|
147
156
|
}
|
|
148
157
|
}
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
componentWillUnmount(): void {
|
|
161
|
+
if (!this._secretsManager || !this._useSecretsManager) {
|
|
162
|
+
return;
|
|
151
163
|
}
|
|
164
|
+
this._secretsManager.detachAll(Private.getToken(), SECRETS_NAMESPACE);
|
|
152
165
|
}
|
|
153
166
|
|
|
154
167
|
/**
|
|
@@ -182,26 +195,31 @@ export class AiSettings extends React.Component<
|
|
|
182
195
|
saveSettings(value: IDict<any>) {
|
|
183
196
|
const currentSettings = { ...value };
|
|
184
197
|
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) ?? '{}');
|
|
185
|
-
|
|
198
|
+
// Do not save secrets in local storage if using the secrets manager.
|
|
199
|
+
if (this._secretsManager && this._useSecretsManager) {
|
|
200
|
+
this._secretFields.forEach(field => delete currentSettings[field]);
|
|
201
|
+
}
|
|
186
202
|
settings[this._provider] = currentSettings;
|
|
187
203
|
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
|
|
188
204
|
}
|
|
189
205
|
|
|
190
|
-
|
|
206
|
+
/**
|
|
207
|
+
* Update the settings whether the secrets manager is used or not.
|
|
208
|
+
*
|
|
209
|
+
* @param value - whether to use the secrets manager or not.
|
|
210
|
+
*/
|
|
211
|
+
private _updateUseSecretsManager = (value: boolean) => {
|
|
191
212
|
this._useSecretsManager = value;
|
|
192
213
|
if (!value) {
|
|
193
214
|
// Detach all the password inputs attached to the secrets manager, and save the
|
|
194
215
|
// current settings to the local storage to save the password.
|
|
195
|
-
this._secretsManager?.detachAll(SECRETS_NAMESPACE);
|
|
196
|
-
this._formInputs = [];
|
|
197
|
-
this._unsavedFields = [];
|
|
216
|
+
this._secretsManager?.detachAll(Private.getToken(), SECRETS_NAMESPACE);
|
|
198
217
|
if (this._settingConnector instanceof SettingConnector) {
|
|
199
218
|
this._settingConnector.doNotSave = [];
|
|
200
219
|
}
|
|
201
220
|
this.saveSettings(this._currentSettings);
|
|
202
221
|
} else {
|
|
203
|
-
// Remove all the keys stored locally
|
|
204
|
-
// secrets manager.
|
|
222
|
+
// Remove all the keys stored locally.
|
|
205
223
|
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
|
|
206
224
|
Object.keys(settings).forEach(provider => {
|
|
207
225
|
Object.keys(settings[provider])
|
|
@@ -211,6 +229,11 @@ export class AiSettings extends React.Component<
|
|
|
211
229
|
});
|
|
212
230
|
});
|
|
213
231
|
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
|
|
232
|
+
// Update the fields not to save in settings.
|
|
233
|
+
if (this._settingConnector instanceof SettingConnector) {
|
|
234
|
+
this._settingConnector.doNotSave = this._secretFields;
|
|
235
|
+
}
|
|
236
|
+
// Attach the password inputs to the secrets manager.
|
|
214
237
|
this.componentDidUpdate();
|
|
215
238
|
}
|
|
216
239
|
this._settings
|
|
@@ -218,16 +241,6 @@ export class AiSettings extends React.Component<
|
|
|
218
241
|
.catch(console.error);
|
|
219
242
|
};
|
|
220
243
|
|
|
221
|
-
/**
|
|
222
|
-
* Update the UI schema of the form.
|
|
223
|
-
* Currently use to hide API keys.
|
|
224
|
-
*/
|
|
225
|
-
private _updateUiSchema(key: string) {
|
|
226
|
-
if (key.toLowerCase().includes('key')) {
|
|
227
|
-
this._uiSchema[key] = { 'ui:widget': 'password' };
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
244
|
/**
|
|
232
245
|
* Build the schema for a given provider.
|
|
233
246
|
*/
|
|
@@ -238,12 +251,28 @@ export class AiSettings extends React.Component<
|
|
|
238
251
|
this._provider
|
|
239
252
|
);
|
|
240
253
|
|
|
254
|
+
this._secretFields = [];
|
|
241
255
|
if (settingsSchema) {
|
|
242
256
|
Object.entries(settingsSchema).forEach(([key, value]) => {
|
|
257
|
+
if (key.toLowerCase().includes('key')) {
|
|
258
|
+
this._secretFields.push(key);
|
|
259
|
+
if (this._hideSecretFields) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
this._uiSchema[key] = { 'ui:widget': 'password' };
|
|
263
|
+
}
|
|
243
264
|
schema.properties[key] = value;
|
|
244
|
-
this._updateUiSchema(key);
|
|
245
265
|
});
|
|
246
266
|
}
|
|
267
|
+
|
|
268
|
+
// Do not save secrets in settings if using the secrets manager.
|
|
269
|
+
if (
|
|
270
|
+
this._secretsManager &&
|
|
271
|
+
this._useSecretsManager &&
|
|
272
|
+
this._settingConnector instanceof SettingConnector
|
|
273
|
+
) {
|
|
274
|
+
this._settingConnector.doNotSave = this._secretFields;
|
|
275
|
+
}
|
|
247
276
|
return schema as JSONSchema7;
|
|
248
277
|
}
|
|
249
278
|
|
|
@@ -349,6 +378,7 @@ export class AiSettings extends React.Component<
|
|
|
349
378
|
private _provider: string;
|
|
350
379
|
private _providerSchema: JSONSchema7;
|
|
351
380
|
private _useSecretsManager: boolean;
|
|
381
|
+
private _hideSecretFields: boolean;
|
|
352
382
|
private _rmRegistry: IRenderMimeRegistry | null;
|
|
353
383
|
private _secretsManager: ISecretsManager | null;
|
|
354
384
|
private _settingConnector: ISettingConnector | null;
|
|
@@ -356,6 +386,26 @@ export class AiSettings extends React.Component<
|
|
|
356
386
|
private _uiSchema: IDict<any> = {};
|
|
357
387
|
private _settings: ISettingRegistry.ISettings;
|
|
358
388
|
private _formRef = React.createRef<HTMLDivElement>();
|
|
359
|
-
private
|
|
360
|
-
|
|
389
|
+
private _secretFields: string[] = [];
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
namespace Private {
|
|
393
|
+
/**
|
|
394
|
+
* The token to use with the secrets manager.
|
|
395
|
+
*/
|
|
396
|
+
let secretsToken: symbol;
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Set of the token.
|
|
400
|
+
*/
|
|
401
|
+
export function setToken(value: symbol): void {
|
|
402
|
+
secretsToken = value;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* get the token.
|
|
407
|
+
*/
|
|
408
|
+
export function getToken(): symbol {
|
|
409
|
+
return secretsToken;
|
|
410
|
+
}
|
|
361
411
|
}
|
|
@@ -71,12 +71,11 @@ export class SettingConnector
|
|
|
71
71
|
|
|
72
72
|
async save(id: string, raw: string): Promise<void> {
|
|
73
73
|
const settings = json5.parse(raw);
|
|
74
|
+
|
|
75
|
+
// Replace secrets fields with the replacement string.
|
|
76
|
+
// Create the field if it does not exist in settings.
|
|
74
77
|
this._doNotSave.forEach(field => {
|
|
75
|
-
if (
|
|
76
|
-
settings['AIprovider'] !== undefined &&
|
|
77
|
-
settings['AIprovider'][field] !== undefined &&
|
|
78
|
-
settings['AIprovider'][field] !== ''
|
|
79
|
-
) {
|
|
78
|
+
if (settings['AIprovider'] !== undefined) {
|
|
80
79
|
settings['AIprovider'][field] = SECRETS_REPLACEMENT;
|
|
81
80
|
}
|
|
82
81
|
});
|
package/src/settings/utils.ts
CHANGED
package/src/tokens.ts
CHANGED
|
@@ -5,6 +5,14 @@ import { JSONSchema7 } from 'json-schema';
|
|
|
5
5
|
|
|
6
6
|
import { IBaseCompleter } from './base-completer';
|
|
7
7
|
|
|
8
|
+
export const PLUGIN_IDS = {
|
|
9
|
+
chat: '@jupyterlite/ai:chat',
|
|
10
|
+
chatCommandRegistry: '@jupyterlite/ai:autocompletion-registry',
|
|
11
|
+
completer: '@jupyterlite/ai:completer',
|
|
12
|
+
providerRegistry: '@jupyterlite/ai:provider-registry',
|
|
13
|
+
settingsConnector: '@jupyterlite/ai:settings-connector'
|
|
14
|
+
};
|
|
15
|
+
|
|
8
16
|
export interface IDict<T = any> {
|
|
9
17
|
[key: string]: T;
|
|
10
18
|
}
|