@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/lib/base-completer.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
|
3
3
|
import { BaseCompleter, IBaseCompleter } from '../../base-completer';
|
|
4
4
|
export declare class AnthropicCompleter implements IBaseCompleter {
|
|
5
5
|
constructor(options: BaseCompleter.IOptions);
|
|
6
|
-
get
|
|
6
|
+
get completer(): BaseChatModel;
|
|
7
7
|
/**
|
|
8
8
|
* Getter and setter for the initial prompt.
|
|
9
9
|
*/
|
|
@@ -14,6 +14,6 @@ export declare class AnthropicCompleter implements IBaseCompleter {
|
|
|
14
14
|
insertText: string;
|
|
15
15
|
}[];
|
|
16
16
|
}>;
|
|
17
|
-
private
|
|
17
|
+
private _completer;
|
|
18
18
|
private _prompt;
|
|
19
19
|
}
|
|
@@ -4,10 +4,10 @@ import { COMPLETION_SYSTEM_PROMPT } from '../../provider';
|
|
|
4
4
|
export class AnthropicCompleter {
|
|
5
5
|
constructor(options) {
|
|
6
6
|
this._prompt = COMPLETION_SYSTEM_PROMPT;
|
|
7
|
-
this.
|
|
7
|
+
this._completer = new ChatAnthropic({ ...options.settings });
|
|
8
8
|
}
|
|
9
|
-
get
|
|
10
|
-
return this.
|
|
9
|
+
get completer() {
|
|
10
|
+
return this._completer;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Getter and setter for the initial prompt.
|
|
@@ -28,7 +28,7 @@ export class AnthropicCompleter {
|
|
|
28
28
|
new AIMessage(trimmedPrompt)
|
|
29
29
|
];
|
|
30
30
|
try {
|
|
31
|
-
const response = await this.
|
|
31
|
+
const response = await this._completer.invoke(messages);
|
|
32
32
|
const items = [];
|
|
33
33
|
// Anthropic can return string or complex content, a list of string/images/other.
|
|
34
34
|
if (typeof response.content === 'string') {
|
|
@@ -8,12 +8,12 @@ export declare class ChromeCompleter implements IBaseCompleter {
|
|
|
8
8
|
*/
|
|
9
9
|
get prompt(): string;
|
|
10
10
|
set prompt(value: string);
|
|
11
|
-
get
|
|
11
|
+
get completer(): LLM;
|
|
12
12
|
fetch(request: CompletionHandler.IRequest, context: IInlineCompletionContext): Promise<{
|
|
13
13
|
items: {
|
|
14
14
|
insertText: string;
|
|
15
15
|
}[];
|
|
16
16
|
}>;
|
|
17
|
-
private
|
|
17
|
+
private _completer;
|
|
18
18
|
private _prompt;
|
|
19
19
|
}
|
|
@@ -23,7 +23,7 @@ const CODE_BLOCK_END_REGEX = /```$/;
|
|
|
23
23
|
export class ChromeCompleter {
|
|
24
24
|
constructor(options) {
|
|
25
25
|
this._prompt = COMPLETION_SYSTEM_PROMPT;
|
|
26
|
-
this.
|
|
26
|
+
this._completer = new ChromeAI({ ...options.settings });
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
29
|
* Getter and setter for the initial prompt.
|
|
@@ -34,8 +34,8 @@ export class ChromeCompleter {
|
|
|
34
34
|
set prompt(value) {
|
|
35
35
|
this._prompt = value;
|
|
36
36
|
}
|
|
37
|
-
get
|
|
38
|
-
return this.
|
|
37
|
+
get completer() {
|
|
38
|
+
return this._completer;
|
|
39
39
|
}
|
|
40
40
|
async fetch(request, context) {
|
|
41
41
|
const { text, offset: cursorOffset } = request;
|
|
@@ -46,7 +46,7 @@ export class ChromeCompleter {
|
|
|
46
46
|
new HumanMessage(trimmedPrompt)
|
|
47
47
|
];
|
|
48
48
|
try {
|
|
49
|
-
let response = await this.
|
|
49
|
+
let response = await this._completer.invoke(messages);
|
|
50
50
|
// ChromeAI sometimes returns a string starting with '```',
|
|
51
51
|
// so process the response to remove the code block delimiters
|
|
52
52
|
if (CODE_BLOCK_START_REGEX.test(response)) {
|
|
@@ -3,7 +3,7 @@ import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
|
3
3
|
import { BaseCompleter, IBaseCompleter } from '../../base-completer';
|
|
4
4
|
export declare class CodestralCompleter implements IBaseCompleter {
|
|
5
5
|
constructor(options: BaseCompleter.IOptions);
|
|
6
|
-
get
|
|
6
|
+
get completer(): BaseChatModel;
|
|
7
7
|
/**
|
|
8
8
|
* Getter and setter for the initial prompt.
|
|
9
9
|
*/
|
|
@@ -11,6 +11,6 @@ export declare class CodestralCompleter implements IBaseCompleter {
|
|
|
11
11
|
set prompt(value: string);
|
|
12
12
|
fetch(request: CompletionHandler.IRequest, context: IInlineCompletionContext): Promise<any>;
|
|
13
13
|
private _throttler;
|
|
14
|
-
private
|
|
14
|
+
private _completer;
|
|
15
15
|
private _prompt;
|
|
16
16
|
}
|
|
@@ -9,9 +9,9 @@ const INTERVAL = 1000;
|
|
|
9
9
|
export class CodestralCompleter {
|
|
10
10
|
constructor(options) {
|
|
11
11
|
this._prompt = COMPLETION_SYSTEM_PROMPT;
|
|
12
|
-
this.
|
|
12
|
+
this._completer = new ChatMistralAI({ ...options.settings });
|
|
13
13
|
this._throttler = new Throttler(async (messages) => {
|
|
14
|
-
const response = await this.
|
|
14
|
+
const response = await this._completer.invoke(messages);
|
|
15
15
|
// Extract results of completion request.
|
|
16
16
|
const items = [];
|
|
17
17
|
if (typeof response.content === 'string') {
|
|
@@ -32,8 +32,8 @@ export class CodestralCompleter {
|
|
|
32
32
|
return { items };
|
|
33
33
|
}, { limit: INTERVAL });
|
|
34
34
|
}
|
|
35
|
-
get
|
|
36
|
-
return this.
|
|
35
|
+
get completer() {
|
|
36
|
+
return this._completer;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* Getter and setter for the initial prompt.
|
|
@@ -3,7 +3,7 @@ import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
|
3
3
|
import { BaseCompleter, IBaseCompleter } from '../../base-completer';
|
|
4
4
|
export declare class OpenAICompleter implements IBaseCompleter {
|
|
5
5
|
constructor(options: BaseCompleter.IOptions);
|
|
6
|
-
get
|
|
6
|
+
get completer(): BaseChatModel;
|
|
7
7
|
/**
|
|
8
8
|
* Getter and setter for the initial prompt.
|
|
9
9
|
*/
|
|
@@ -14,6 +14,6 @@ export declare class OpenAICompleter implements IBaseCompleter {
|
|
|
14
14
|
insertText: string;
|
|
15
15
|
}[];
|
|
16
16
|
}>;
|
|
17
|
-
private
|
|
17
|
+
private _completer;
|
|
18
18
|
private _prompt;
|
|
19
19
|
}
|
|
@@ -4,10 +4,10 @@ import { COMPLETION_SYSTEM_PROMPT } from '../../provider';
|
|
|
4
4
|
export class OpenAICompleter {
|
|
5
5
|
constructor(options) {
|
|
6
6
|
this._prompt = COMPLETION_SYSTEM_PROMPT;
|
|
7
|
-
this.
|
|
7
|
+
this._completer = new ChatOpenAI({ ...options.settings });
|
|
8
8
|
}
|
|
9
|
-
get
|
|
10
|
-
return this.
|
|
9
|
+
get completer() {
|
|
10
|
+
return this._completer;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Getter and setter for the initial prompt.
|
|
@@ -23,7 +23,7 @@ export class OpenAICompleter {
|
|
|
23
23
|
const prompt = text.slice(0, cursorOffset);
|
|
24
24
|
const messages = [new SystemMessage(this._prompt), new AIMessage(prompt)];
|
|
25
25
|
try {
|
|
26
|
-
const response = await this.
|
|
26
|
+
const response = await this._completer.invoke(messages);
|
|
27
27
|
const items = [];
|
|
28
28
|
if (typeof response.content === 'string') {
|
|
29
29
|
items.push({
|
package/lib/index.js
CHANGED
|
@@ -5,16 +5,16 @@ import { INotebookTracker } from '@jupyterlab/notebook';
|
|
|
5
5
|
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
6
6
|
import { ISettingConnector, ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
7
7
|
import { IFormRendererRegistry } from '@jupyterlab/ui-components';
|
|
8
|
-
import { ISecretsManager } from 'jupyter-secrets-manager';
|
|
8
|
+
import { ISecretsManager, SecretsManager } from 'jupyter-secrets-manager';
|
|
9
9
|
import { ChatHandler } from './chat-handler';
|
|
10
10
|
import { CompletionProvider } from './completion-provider';
|
|
11
11
|
import { defaultProviderPlugins } from './default-providers';
|
|
12
12
|
import { AIProviderRegistry } from './provider';
|
|
13
13
|
import { aiSettingsRenderer, SettingConnector } from './settings';
|
|
14
|
-
import { IAIProviderRegistry } from './tokens';
|
|
14
|
+
import { IAIProviderRegistry, PLUGIN_IDS } from './tokens';
|
|
15
15
|
import { stopItem } from './components/stop-button';
|
|
16
16
|
const chatCommandRegistryPlugin = {
|
|
17
|
-
id:
|
|
17
|
+
id: PLUGIN_IDS.chatCommandRegistry,
|
|
18
18
|
description: 'Autocompletion registry',
|
|
19
19
|
autoStart: true,
|
|
20
20
|
provides: IChatCommandRegistry,
|
|
@@ -25,7 +25,7 @@ const chatCommandRegistryPlugin = {
|
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
27
|
const chatPlugin = {
|
|
28
|
-
id:
|
|
28
|
+
id: PLUGIN_IDS.chat,
|
|
29
29
|
description: 'LLM chat extension',
|
|
30
30
|
autoStart: true,
|
|
31
31
|
requires: [IAIProviderRegistry, IRenderMimeRegistry, IChatCommandRegistry],
|
|
@@ -98,7 +98,7 @@ const chatPlugin = {
|
|
|
98
98
|
}
|
|
99
99
|
};
|
|
100
100
|
const completerPlugin = {
|
|
101
|
-
id:
|
|
101
|
+
id: PLUGIN_IDS.completer,
|
|
102
102
|
autoStart: true,
|
|
103
103
|
requires: [IAIProviderRegistry, ICompletionProviderManager],
|
|
104
104
|
activate: (app, providerRegistry, manager) => {
|
|
@@ -109,16 +109,20 @@ const completerPlugin = {
|
|
|
109
109
|
manager.registerInlineProvider(completer);
|
|
110
110
|
}
|
|
111
111
|
};
|
|
112
|
-
const providerRegistryPlugin = {
|
|
113
|
-
id:
|
|
112
|
+
const providerRegistryPlugin = SecretsManager.sign(PLUGIN_IDS.providerRegistry, token => ({
|
|
113
|
+
id: PLUGIN_IDS.providerRegistry,
|
|
114
114
|
autoStart: true,
|
|
115
115
|
requires: [IFormRendererRegistry, ISettingRegistry],
|
|
116
116
|
optional: [IRenderMimeRegistry, ISecretsManager, ISettingConnector],
|
|
117
117
|
provides: IAIProviderRegistry,
|
|
118
118
|
activate: (app, editorRegistry, settingRegistry, rmRegistry, secretsManager, settingConnector) => {
|
|
119
|
-
const providerRegistry = new AIProviderRegistry({
|
|
120
|
-
|
|
119
|
+
const providerRegistry = new AIProviderRegistry({
|
|
120
|
+
token,
|
|
121
|
+
secretsManager
|
|
122
|
+
});
|
|
123
|
+
editorRegistry.addRenderer(`${PLUGIN_IDS.providerRegistry}.AIprovider`, aiSettingsRenderer({
|
|
121
124
|
providerRegistry,
|
|
125
|
+
secretsToken: token,
|
|
122
126
|
rmRegistry,
|
|
123
127
|
secretsManager,
|
|
124
128
|
settingConnector
|
|
@@ -145,14 +149,14 @@ const providerRegistryPlugin = {
|
|
|
145
149
|
});
|
|
146
150
|
return providerRegistry;
|
|
147
151
|
}
|
|
148
|
-
};
|
|
152
|
+
}));
|
|
149
153
|
/**
|
|
150
154
|
* Provides the settings connector as a separate plugin to allow for alternative
|
|
151
155
|
* implementations that may want to fetch settings from a different source or
|
|
152
156
|
* endpoint.
|
|
153
157
|
*/
|
|
154
158
|
const settingsConnector = {
|
|
155
|
-
id:
|
|
159
|
+
id: PLUGIN_IDS.settingsConnector,
|
|
156
160
|
description: 'Provides a settings connector which does not save passwords.',
|
|
157
161
|
autoStart: true,
|
|
158
162
|
provides: ISettingConnector,
|
package/lib/provider.d.ts
CHANGED
|
@@ -84,6 +84,10 @@ export declare namespace AIProviderRegistry {
|
|
|
84
84
|
* The secrets manager used in the application.
|
|
85
85
|
*/
|
|
86
86
|
secretsManager?: ISecretsManager;
|
|
87
|
+
/**
|
|
88
|
+
* The token used to request the secrets manager.
|
|
89
|
+
*/
|
|
90
|
+
token: symbol;
|
|
87
91
|
}
|
|
88
92
|
/**
|
|
89
93
|
* The options for the Chat system prompt.
|
package/lib/provider.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Signal } from '@lumino/signaling';
|
|
2
|
-
import { getSecretId,
|
|
2
|
+
import { getSecretId, SECRETS_REPLACEMENT } from './settings';
|
|
3
|
+
import { PLUGIN_IDS } from './tokens';
|
|
4
|
+
const SECRETS_NAMESPACE = PLUGIN_IDS.providerRegistry;
|
|
3
5
|
export const chatSystemPrompt = (options) => `
|
|
4
6
|
You are Jupyternaut, a conversational assistant living in JupyterLab to help users.
|
|
5
7
|
You are not a language model, but rather an application built on a foundation model from ${options.provider_name}.
|
|
@@ -41,6 +43,7 @@ export class AIProviderRegistry {
|
|
|
41
43
|
this._providers = new Map();
|
|
42
44
|
this._deferredProvider = null;
|
|
43
45
|
this._secretsManager = options.secretsManager || null;
|
|
46
|
+
Private.setToken(options.token);
|
|
44
47
|
}
|
|
45
48
|
/**
|
|
46
49
|
* Get the list of provider names.
|
|
@@ -149,8 +152,10 @@ export class AIProviderRegistry {
|
|
|
149
152
|
for (const key of Object.keys(settings)) {
|
|
150
153
|
if (settings[key] === SECRETS_REPLACEMENT) {
|
|
151
154
|
const id = getSecretId(name, key);
|
|
152
|
-
const secrets = await ((_b = this._secretsManager) === null || _b === void 0 ? void 0 : _b.get(SECRETS_NAMESPACE, id));
|
|
153
|
-
|
|
155
|
+
const secrets = await ((_b = this._secretsManager) === null || _b === void 0 ? void 0 : _b.get(Private.getToken(), SECRETS_NAMESPACE, id));
|
|
156
|
+
if (secrets !== undefined) {
|
|
157
|
+
fullSettings[key] = secrets.value;
|
|
158
|
+
}
|
|
154
159
|
continue;
|
|
155
160
|
}
|
|
156
161
|
fullSettings[key] = settings[key];
|
|
@@ -158,7 +163,7 @@ export class AIProviderRegistry {
|
|
|
158
163
|
if (((_c = this._currentProvider) === null || _c === void 0 ? void 0 : _c.completer) !== undefined) {
|
|
159
164
|
try {
|
|
160
165
|
this._completer = new this._currentProvider.completer({
|
|
161
|
-
|
|
166
|
+
settings: fullSettings
|
|
162
167
|
});
|
|
163
168
|
this._completerError = '';
|
|
164
169
|
}
|
|
@@ -231,3 +236,24 @@ export class AIProviderRegistry {
|
|
|
231
236
|
}
|
|
232
237
|
AIProviderRegistry.updateConfig = updateConfig;
|
|
233
238
|
})(AIProviderRegistry || (AIProviderRegistry = {}));
|
|
239
|
+
var Private;
|
|
240
|
+
(function (Private) {
|
|
241
|
+
/**
|
|
242
|
+
* The token to use with the secrets manager.
|
|
243
|
+
*/
|
|
244
|
+
let secretsToken;
|
|
245
|
+
/**
|
|
246
|
+
* Set of the token.
|
|
247
|
+
*/
|
|
248
|
+
function setToken(value) {
|
|
249
|
+
secretsToken = value;
|
|
250
|
+
}
|
|
251
|
+
Private.setToken = setToken;
|
|
252
|
+
/**
|
|
253
|
+
* get the token.
|
|
254
|
+
*/
|
|
255
|
+
function getToken() {
|
|
256
|
+
return secretsToken;
|
|
257
|
+
}
|
|
258
|
+
Private.getToken = getToken;
|
|
259
|
+
})(Private || (Private = {}));
|
package/lib/settings/panel.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import React from 'react';
|
|
|
8
8
|
import { IAIProviderRegistry, IDict } from '../tokens';
|
|
9
9
|
export declare const aiSettingsRenderer: (options: {
|
|
10
10
|
providerRegistry: IAIProviderRegistry;
|
|
11
|
+
secretsToken?: symbol;
|
|
11
12
|
rmRegistry?: IRenderMimeRegistry;
|
|
12
13
|
secretsManager?: ISecretsManager;
|
|
13
14
|
settingConnector?: ISettingConnector;
|
|
@@ -19,6 +20,7 @@ export interface ISettingsFormStates {
|
|
|
19
20
|
export declare class AiSettings extends React.Component<FieldProps, ISettingsFormStates> {
|
|
20
21
|
constructor(props: FieldProps);
|
|
21
22
|
componentDidUpdate(): Promise<void>;
|
|
23
|
+
componentWillUnmount(): void;
|
|
22
24
|
/**
|
|
23
25
|
* Get the current provider from the local storage.
|
|
24
26
|
*/
|
|
@@ -35,12 +37,12 @@ export declare class AiSettings extends React.Component<FieldProps, ISettingsFor
|
|
|
35
37
|
* Save settings in local storage for a given provider.
|
|
36
38
|
*/
|
|
37
39
|
saveSettings(value: IDict<any>): void;
|
|
38
|
-
private updateUseSecretsManager;
|
|
39
40
|
/**
|
|
40
|
-
* Update the
|
|
41
|
-
*
|
|
41
|
+
* Update the settings whether the secrets manager is used or not.
|
|
42
|
+
*
|
|
43
|
+
* @param value - whether to use the secrets manager or not.
|
|
42
44
|
*/
|
|
43
|
-
private
|
|
45
|
+
private _updateUseSecretsManager;
|
|
44
46
|
/**
|
|
45
47
|
* Build the schema for a given provider.
|
|
46
48
|
*/
|
|
@@ -75,6 +77,7 @@ export declare class AiSettings extends React.Component<FieldProps, ISettingsFor
|
|
|
75
77
|
private _provider;
|
|
76
78
|
private _providerSchema;
|
|
77
79
|
private _useSecretsManager;
|
|
80
|
+
private _hideSecretFields;
|
|
78
81
|
private _rmRegistry;
|
|
79
82
|
private _secretsManager;
|
|
80
83
|
private _settingConnector;
|
|
@@ -82,6 +85,5 @@ export declare class AiSettings extends React.Component<FieldProps, ISettingsFor
|
|
|
82
85
|
private _uiSchema;
|
|
83
86
|
private _settings;
|
|
84
87
|
private _formRef;
|
|
85
|
-
private
|
|
86
|
-
private _formInputs;
|
|
88
|
+
private _secretFields;
|
|
87
89
|
}
|
package/lib/settings/panel.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { FormComponent } from '@jupyterlab/ui-components';
|
|
2
|
-
import { ArrayExt } from '@lumino/algorithm';
|
|
3
2
|
import { JSONExt } from '@lumino/coreutils';
|
|
4
3
|
import validator from '@rjsf/validator-ajv8';
|
|
5
4
|
import React from 'react';
|
|
6
|
-
import { getSecretId,
|
|
5
|
+
import { getSecretId, SettingConnector } from '.';
|
|
7
6
|
import baseSettings from './base.json';
|
|
7
|
+
import { PLUGIN_IDS } from '../tokens';
|
|
8
8
|
const MD_MIME_TYPE = 'text/markdown';
|
|
9
9
|
const STORAGE_NAME = '@jupyterlite/ai:settings';
|
|
10
10
|
const INSTRUCTION_CLASS = 'jp-AISettingsInstructions';
|
|
11
|
+
const SECRETS_NAMESPACE = PLUGIN_IDS.providerRegistry;
|
|
11
12
|
export const aiSettingsRenderer = (options) => {
|
|
13
|
+
const { secretsToken } = options;
|
|
14
|
+
delete options.secretsToken;
|
|
15
|
+
if (secretsToken) {
|
|
16
|
+
Private.setToken(secretsToken);
|
|
17
|
+
}
|
|
12
18
|
return {
|
|
13
19
|
fieldRenderer: (props) => {
|
|
14
20
|
props.formContext = { ...props.formContext, ...options };
|
|
@@ -21,25 +27,27 @@ const WrappedFormComponent = (props) => {
|
|
|
21
27
|
};
|
|
22
28
|
export class AiSettings extends React.Component {
|
|
23
29
|
constructor(props) {
|
|
24
|
-
var _a, _b, _c, _d, _e;
|
|
30
|
+
var _a, _b, _c, _d, _e, _f;
|
|
25
31
|
super(props);
|
|
26
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Update the settings whether the secrets manager is used or not.
|
|
34
|
+
*
|
|
35
|
+
* @param value - whether to use the secrets manager or not.
|
|
36
|
+
*/
|
|
37
|
+
this._updateUseSecretsManager = (value) => {
|
|
27
38
|
var _a;
|
|
28
39
|
this._useSecretsManager = value;
|
|
29
40
|
if (!value) {
|
|
30
41
|
// Detach all the password inputs attached to the secrets manager, and save the
|
|
31
42
|
// current settings to the local storage to save the password.
|
|
32
|
-
(_a = this._secretsManager) === null || _a === void 0 ? void 0 : _a.detachAll(SECRETS_NAMESPACE);
|
|
33
|
-
this._formInputs = [];
|
|
34
|
-
this._unsavedFields = [];
|
|
43
|
+
(_a = this._secretsManager) === null || _a === void 0 ? void 0 : _a.detachAll(Private.getToken(), SECRETS_NAMESPACE);
|
|
35
44
|
if (this._settingConnector instanceof SettingConnector) {
|
|
36
45
|
this._settingConnector.doNotSave = [];
|
|
37
46
|
}
|
|
38
47
|
this.saveSettings(this._currentSettings);
|
|
39
48
|
}
|
|
40
49
|
else {
|
|
41
|
-
// Remove all the keys stored locally
|
|
42
|
-
// secrets manager.
|
|
50
|
+
// Remove all the keys stored locally.
|
|
43
51
|
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
|
|
44
52
|
Object.keys(settings).forEach(provider => {
|
|
45
53
|
Object.keys(settings[provider])
|
|
@@ -49,6 +57,11 @@ export class AiSettings extends React.Component {
|
|
|
49
57
|
});
|
|
50
58
|
});
|
|
51
59
|
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
|
|
60
|
+
// Update the fields not to save in settings.
|
|
61
|
+
if (this._settingConnector instanceof SettingConnector) {
|
|
62
|
+
this._settingConnector.doNotSave = this._secretFields;
|
|
63
|
+
}
|
|
64
|
+
// Attach the password inputs to the secrets manager.
|
|
52
65
|
this.componentDidUpdate();
|
|
53
66
|
}
|
|
54
67
|
this._settings
|
|
@@ -98,8 +111,7 @@ export class AiSettings extends React.Component {
|
|
|
98
111
|
this._currentSettings = { provider: 'None' };
|
|
99
112
|
this._uiSchema = {};
|
|
100
113
|
this._formRef = React.createRef();
|
|
101
|
-
this.
|
|
102
|
-
this._formInputs = [];
|
|
114
|
+
this._secretFields = [];
|
|
103
115
|
if (!props.formContext.providerRegistry) {
|
|
104
116
|
throw new Error('The provider registry is needed to enable the jupyterlite-ai settings panel');
|
|
105
117
|
}
|
|
@@ -110,6 +122,8 @@ export class AiSettings extends React.Component {
|
|
|
110
122
|
this._settings = props.formContext.settings;
|
|
111
123
|
this._useSecretsManager =
|
|
112
124
|
(_d = this._settings.get('UseSecretsManager').composite) !== null && _d !== void 0 ? _d : true;
|
|
125
|
+
this._hideSecretFields =
|
|
126
|
+
(_e = this._settings.get('HideSecretFields').composite) !== null && _e !== void 0 ? _e : true;
|
|
113
127
|
// Initialize the providers schema.
|
|
114
128
|
const providerSchema = JSONExt.deepCopy(baseSettings);
|
|
115
129
|
providerSchema.properties.provider = {
|
|
@@ -127,7 +141,7 @@ export class AiSettings extends React.Component {
|
|
|
127
141
|
const labSettings = this._settings.get('AIprovider').composite;
|
|
128
142
|
if (labSettings && Object.keys(labSettings).includes('provider')) {
|
|
129
143
|
// Get the provider name.
|
|
130
|
-
const provider = (
|
|
144
|
+
const provider = (_f = Object.entries(labSettings).find(v => v[0] === 'provider')) === null || _f === void 0 ? void 0 : _f[1];
|
|
131
145
|
// Save the settings.
|
|
132
146
|
const settings = {
|
|
133
147
|
_current: provider
|
|
@@ -148,10 +162,15 @@ export class AiSettings extends React.Component {
|
|
|
148
162
|
.set('AIprovider', this._currentSettings)
|
|
149
163
|
.catch(console.error);
|
|
150
164
|
this._settings.changed.connect(() => {
|
|
151
|
-
var _a;
|
|
165
|
+
var _a, _b;
|
|
152
166
|
const useSecretsManager = (_a = this._settings.get('UseSecretsManager').composite) !== null && _a !== void 0 ? _a : true;
|
|
153
167
|
if (useSecretsManager !== this._useSecretsManager) {
|
|
154
|
-
this.
|
|
168
|
+
this._updateUseSecretsManager(useSecretsManager);
|
|
169
|
+
}
|
|
170
|
+
const hideSecretFields = (_b = this._settings.get('HideSecretFields').composite) !== null && _b !== void 0 ? _b : true;
|
|
171
|
+
if (hideSecretFields !== this._hideSecretFields) {
|
|
172
|
+
this._hideSecretFields = hideSecretFields;
|
|
173
|
+
this._updateSchema();
|
|
155
174
|
}
|
|
156
175
|
});
|
|
157
176
|
}
|
|
@@ -160,27 +179,24 @@ export class AiSettings extends React.Component {
|
|
|
160
179
|
if (!this._secretsManager || !this._useSecretsManager) {
|
|
161
180
|
return;
|
|
162
181
|
}
|
|
163
|
-
// Attach the password inputs to the secrets manager
|
|
182
|
+
// Attach the password inputs to the secrets manager.
|
|
183
|
+
await this._secretsManager.detachAll(Private.getToken(), SECRETS_NAMESPACE);
|
|
164
184
|
const inputs = ((_a = this._formRef.current) === null || _a === void 0 ? void 0 : _a.getElementsByTagName('input')) || [];
|
|
165
|
-
if (ArrayExt.shallowEqual(inputs, this._formInputs)) {
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
await this._secretsManager.detachAll(SECRETS_NAMESPACE);
|
|
169
|
-
this._formInputs = [...inputs];
|
|
170
|
-
this._unsavedFields = [];
|
|
171
185
|
for (let i = 0; i < inputs.length; i++) {
|
|
172
186
|
if (inputs[i].type.toLowerCase() === 'password') {
|
|
173
187
|
const label = inputs[i].getAttribute('label');
|
|
174
188
|
if (label) {
|
|
175
189
|
const id = getSecretId(this._provider, label);
|
|
176
|
-
this._secretsManager.attach(SECRETS_NAMESPACE, id, inputs[i], (value) => this._onPasswordUpdated(label, value));
|
|
177
|
-
this._unsavedFields.push(label);
|
|
190
|
+
this._secretsManager.attach(Private.getToken(), SECRETS_NAMESPACE, id, inputs[i], (value) => this._onPasswordUpdated(label, value));
|
|
178
191
|
}
|
|
179
192
|
}
|
|
180
193
|
}
|
|
181
|
-
|
|
182
|
-
|
|
194
|
+
}
|
|
195
|
+
componentWillUnmount() {
|
|
196
|
+
if (!this._secretsManager || !this._useSecretsManager) {
|
|
197
|
+
return;
|
|
183
198
|
}
|
|
199
|
+
this._secretsManager.detachAll(Private.getToken(), SECRETS_NAMESPACE);
|
|
184
200
|
}
|
|
185
201
|
/**
|
|
186
202
|
* Get the current provider from the local storage.
|
|
@@ -213,19 +229,13 @@ export class AiSettings extends React.Component {
|
|
|
213
229
|
var _a;
|
|
214
230
|
const currentSettings = { ...value };
|
|
215
231
|
const settings = JSON.parse((_a = localStorage.getItem(STORAGE_NAME)) !== null && _a !== void 0 ? _a : '{}');
|
|
216
|
-
|
|
232
|
+
// Do not save secrets in local storage if using the secrets manager.
|
|
233
|
+
if (this._secretsManager && this._useSecretsManager) {
|
|
234
|
+
this._secretFields.forEach(field => delete currentSettings[field]);
|
|
235
|
+
}
|
|
217
236
|
settings[this._provider] = currentSettings;
|
|
218
237
|
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
|
|
219
238
|
}
|
|
220
|
-
/**
|
|
221
|
-
* Update the UI schema of the form.
|
|
222
|
-
* Currently use to hide API keys.
|
|
223
|
-
*/
|
|
224
|
-
_updateUiSchema(key) {
|
|
225
|
-
if (key.toLowerCase().includes('key')) {
|
|
226
|
-
this._uiSchema[key] = { 'ui:widget': 'password' };
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
239
|
/**
|
|
230
240
|
* Build the schema for a given provider.
|
|
231
241
|
*/
|
|
@@ -233,12 +243,25 @@ export class AiSettings extends React.Component {
|
|
|
233
243
|
const schema = JSONExt.deepCopy(baseSettings);
|
|
234
244
|
this._uiSchema = {};
|
|
235
245
|
const settingsSchema = this._providerRegistry.getSettingsSchema(this._provider);
|
|
246
|
+
this._secretFields = [];
|
|
236
247
|
if (settingsSchema) {
|
|
237
248
|
Object.entries(settingsSchema).forEach(([key, value]) => {
|
|
249
|
+
if (key.toLowerCase().includes('key')) {
|
|
250
|
+
this._secretFields.push(key);
|
|
251
|
+
if (this._hideSecretFields) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
this._uiSchema[key] = { 'ui:widget': 'password' };
|
|
255
|
+
}
|
|
238
256
|
schema.properties[key] = value;
|
|
239
|
-
this._updateUiSchema(key);
|
|
240
257
|
});
|
|
241
258
|
}
|
|
259
|
+
// Do not save secrets in settings if using the secrets manager.
|
|
260
|
+
if (this._secretsManager &&
|
|
261
|
+
this._useSecretsManager &&
|
|
262
|
+
this._settingConnector instanceof SettingConnector) {
|
|
263
|
+
this._settingConnector.doNotSave = this._secretFields;
|
|
264
|
+
}
|
|
242
265
|
return schema;
|
|
243
266
|
}
|
|
244
267
|
/**
|
|
@@ -275,3 +298,24 @@ export class AiSettings extends React.Component {
|
|
|
275
298
|
React.createElement(WrappedFormComponent, { formData: this._currentSettings, schema: this.state.schema, onChange: this._onFormChange, uiSchema: this._uiSchema })));
|
|
276
299
|
}
|
|
277
300
|
}
|
|
301
|
+
var Private;
|
|
302
|
+
(function (Private) {
|
|
303
|
+
/**
|
|
304
|
+
* The token to use with the secrets manager.
|
|
305
|
+
*/
|
|
306
|
+
let secretsToken;
|
|
307
|
+
/**
|
|
308
|
+
* Set of the token.
|
|
309
|
+
*/
|
|
310
|
+
function setToken(value) {
|
|
311
|
+
secretsToken = value;
|
|
312
|
+
}
|
|
313
|
+
Private.setToken = setToken;
|
|
314
|
+
/**
|
|
315
|
+
* get the token.
|
|
316
|
+
*/
|
|
317
|
+
function getToken() {
|
|
318
|
+
return secretsToken;
|
|
319
|
+
}
|
|
320
|
+
Private.getToken = getToken;
|
|
321
|
+
})(Private || (Private = {}));
|
|
@@ -49,10 +49,10 @@ export class SettingConnector extends DataConnector {
|
|
|
49
49
|
}
|
|
50
50
|
async save(id, raw) {
|
|
51
51
|
const settings = json5.parse(raw);
|
|
52
|
+
// Replace secrets fields with the replacement string.
|
|
53
|
+
// Create the field if it does not exist in settings.
|
|
52
54
|
this._doNotSave.forEach(field => {
|
|
53
|
-
if (settings['AIprovider'] !== undefined
|
|
54
|
-
settings['AIprovider'][field] !== undefined &&
|
|
55
|
-
settings['AIprovider'][field] !== '') {
|
|
55
|
+
if (settings['AIprovider'] !== undefined) {
|
|
56
56
|
settings['AIprovider'][field] = SECRETS_REPLACEMENT;
|
|
57
57
|
}
|
|
58
58
|
});
|
package/lib/settings/utils.d.ts
CHANGED
package/lib/settings/utils.js
CHANGED
package/lib/tokens.d.ts
CHANGED
|
@@ -3,6 +3,13 @@ import { ReadonlyPartialJSONObject, Token } from '@lumino/coreutils';
|
|
|
3
3
|
import { ISignal } from '@lumino/signaling';
|
|
4
4
|
import { JSONSchema7 } from 'json-schema';
|
|
5
5
|
import { IBaseCompleter } from './base-completer';
|
|
6
|
+
export declare const PLUGIN_IDS: {
|
|
7
|
+
chat: string;
|
|
8
|
+
chatCommandRegistry: string;
|
|
9
|
+
completer: string;
|
|
10
|
+
providerRegistry: string;
|
|
11
|
+
settingsConnector: string;
|
|
12
|
+
};
|
|
6
13
|
export interface IDict<T = any> {
|
|
7
14
|
[key: string]: T;
|
|
8
15
|
}
|
package/lib/tokens.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { Token } from '@lumino/coreutils';
|
|
2
|
+
export const PLUGIN_IDS = {
|
|
3
|
+
chat: '@jupyterlite/ai:chat',
|
|
4
|
+
chatCommandRegistry: '@jupyterlite/ai:autocompletion-registry',
|
|
5
|
+
completer: '@jupyterlite/ai:completer',
|
|
6
|
+
providerRegistry: '@jupyterlite/ai:provider-registry',
|
|
7
|
+
settingsConnector: '@jupyterlite/ai:settings-connector'
|
|
8
|
+
};
|
|
2
9
|
/**
|
|
3
10
|
* The provider registry token.
|
|
4
11
|
*/
|