@jupyterlite/ai 0.3.0 → 0.5.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.
- package/lib/chat-handler.d.ts +10 -4
- package/lib/chat-handler.js +42 -10
- package/lib/completion-provider.d.ts +5 -18
- package/lib/completion-provider.js +8 -34
- package/lib/{llm-models/anthropic-completer.d.ts → default-providers/Anthropic/completer.d.ts} +1 -1
- package/lib/{llm-models/anthropic-completer.js → default-providers/Anthropic/completer.js} +1 -1
- package/lib/{llm-models/chrome-completer.d.ts → default-providers/ChromeAI/completer.d.ts} +1 -1
- package/lib/{llm-models/chrome-completer.js → default-providers/ChromeAI/completer.js} +1 -1
- package/lib/default-providers/ChromeAI/instructions.d.ts +2 -0
- package/lib/default-providers/ChromeAI/instructions.js +24 -0
- package/lib/{llm-models/codestral-completer.d.ts → default-providers/MistralAI/completer.d.ts} +1 -1
- package/lib/{llm-models/codestral-completer.js → default-providers/MistralAI/completer.js} +1 -1
- package/lib/default-providers/MistralAI/instructions.d.ts +2 -0
- package/lib/default-providers/MistralAI/instructions.js +16 -0
- package/lib/{llm-models/openai-completer.d.ts → default-providers/OpenAI/completer.d.ts} +1 -1
- package/lib/{llm-models/openai-completer.js → default-providers/OpenAI/completer.js} +1 -1
- package/lib/default-providers/index.d.ts +2 -0
- package/lib/default-providers/index.js +60 -0
- package/lib/index.d.ts +3 -3
- package/lib/index.js +51 -64
- package/lib/provider.d.ts +45 -17
- package/lib/provider.js +97 -41
- package/lib/settings/base.json +7 -0
- package/lib/settings/panel.d.ts +84 -0
- package/lib/settings/panel.js +267 -0
- package/lib/tokens.d.ts +103 -0
- package/lib/tokens.js +5 -0
- package/package.json +12 -5
- package/schema/provider-registry.json +23 -0
- package/src/chat-handler.ts +50 -13
- package/src/completion-provider.ts +13 -37
- package/src/{llm-models/anthropic-completer.ts → default-providers/Anthropic/completer.ts} +2 -2
- package/src/{llm-models/chrome-completer.ts → default-providers/ChromeAI/completer.ts} +3 -2
- package/src/default-providers/ChromeAI/instructions.ts +24 -0
- package/src/{llm-models/codestral-completer.ts → default-providers/MistralAI/completer.ts} +2 -2
- package/src/default-providers/MistralAI/instructions.ts +16 -0
- package/src/{llm-models/openai-completer.ts → default-providers/OpenAI/completer.ts} +2 -2
- package/src/default-providers/index.ts +71 -0
- package/src/index.ts +75 -77
- package/src/provider.ts +100 -43
- package/src/settings/panel.tsx +346 -0
- package/src/tokens.ts +112 -0
- package/style/base.css +4 -0
- package/lib/llm-models/index.d.ts +0 -3
- package/lib/llm-models/index.js +0 -3
- package/lib/llm-models/utils.d.ts +0 -16
- package/lib/llm-models/utils.js +0 -86
- package/lib/slash-commands.d.ts +0 -16
- package/lib/slash-commands.js +0 -25
- package/lib/token.d.ts +0 -13
- package/lib/token.js +0 -2
- package/schema/ai-provider.json +0 -17
- package/src/llm-models/index.ts +0 -3
- package/src/llm-models/utils.ts +0 -90
- package/src/slash-commands.tsx +0 -55
- package/src/token.ts +0 -19
- /package/lib/{llm-models/base-completer.d.ts → base-completer.d.ts} +0 -0
- /package/lib/{llm-models/base-completer.js → base-completer.js} +0 -0
- /package/lib/{_provider-settings/anthropic.json → default-providers/Anthropic/settings-schema.json} +0 -0
- /package/lib/{_provider-settings/chromeAI.json → default-providers/ChromeAI/settings-schema.json} +0 -0
- /package/lib/{_provider-settings/mistralAI.json → default-providers/MistralAI/settings-schema.json} +0 -0
- /package/lib/{_provider-settings/openAI.json → default-providers/OpenAI/settings-schema.json} +0 -0
- /package/src/{llm-models/base-completer.ts → base-completer.ts} +0 -0
- /package/src/{llm-models/svg.d.ts → global.d.ts} +0 -0
package/lib/provider.d.ts
CHANGED
|
@@ -3,21 +3,44 @@ import { BaseLanguageModel } from '@langchain/core/language_models/base';
|
|
|
3
3
|
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
4
4
|
import { ISignal } from '@lumino/signaling';
|
|
5
5
|
import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
|
|
6
|
-
import { IBaseCompleter } from './
|
|
7
|
-
import { IAIProvider } from './
|
|
8
|
-
|
|
6
|
+
import { IBaseCompleter } from './base-completer';
|
|
7
|
+
import { IAIProvider, IAIProviderRegistry } from './tokens';
|
|
8
|
+
import { JSONSchema7 } from 'json-schema';
|
|
9
|
+
export declare const chatSystemPrompt: (options: AIProviderRegistry.IPromptOptions) => string;
|
|
9
10
|
export declare const COMPLETION_SYSTEM_PROMPT = "\nYou are an application built to provide helpful code completion suggestions.\nYou should only produce code. Keep comments to minimum, use the\nprogramming language comment syntax. Produce clean code.\nThe code is written in JupyterLab, a data analysis and code development\nenvironment which can execute code extended with additional syntax for\ninteractive features, such as magics.\nOnly give raw strings back, do not format the response using backticks.\nThe output should be a single string, and should correspond to what a human users\nwould write.\nDo not include the prompt in the output, only the string that should be appended to the current input.\n";
|
|
10
|
-
export declare class
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
export declare class AIProviderRegistry implements IAIProviderRegistry {
|
|
12
|
+
/**
|
|
13
|
+
* Get the list of provider names.
|
|
14
|
+
*/
|
|
15
|
+
get providers(): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Add a new provider.
|
|
18
|
+
*/
|
|
19
|
+
add(provider: IAIProvider): void;
|
|
20
|
+
/**
|
|
21
|
+
* Get the current provider name.
|
|
22
|
+
*/
|
|
23
|
+
get currentName(): string;
|
|
13
24
|
/**
|
|
14
25
|
* Get the current completer of the completion provider.
|
|
15
26
|
*/
|
|
16
|
-
get
|
|
27
|
+
get currentCompleter(): IBaseCompleter | null;
|
|
17
28
|
/**
|
|
18
29
|
* Get the current llm chat model.
|
|
19
30
|
*/
|
|
20
|
-
get
|
|
31
|
+
get currentChatModel(): BaseChatModel | null;
|
|
32
|
+
/**
|
|
33
|
+
* Get the settings schema of a given provider.
|
|
34
|
+
*/
|
|
35
|
+
getSettingsSchema(provider: string): JSONSchema7;
|
|
36
|
+
/**
|
|
37
|
+
* Get the instructions of a given provider.
|
|
38
|
+
*/
|
|
39
|
+
getInstructions(provider: string): string | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Format an error message from the current provider.
|
|
42
|
+
*/
|
|
43
|
+
formatErrorMessage(error: any): string;
|
|
21
44
|
/**
|
|
22
45
|
* Get the current chat error;
|
|
23
46
|
*/
|
|
@@ -27,22 +50,27 @@ export declare class AIProvider implements IAIProvider {
|
|
|
27
50
|
*/
|
|
28
51
|
get completerError(): string;
|
|
29
52
|
/**
|
|
30
|
-
* Set the
|
|
31
|
-
* Creates the
|
|
53
|
+
* Set the providers (chat model and completer).
|
|
54
|
+
* Creates the providers if the name has changed, otherwise only updates their config.
|
|
32
55
|
*
|
|
33
|
-
* @param name - the name of the
|
|
56
|
+
* @param name - the name of the provider to use.
|
|
34
57
|
* @param settings - the settings for the models.
|
|
35
58
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
59
|
+
setProvider(name: string, settings: ReadonlyPartialJSONObject): void;
|
|
60
|
+
/**
|
|
61
|
+
* A signal emitting when the provider or its settings has changed.
|
|
62
|
+
*/
|
|
63
|
+
get providerChanged(): ISignal<IAIProviderRegistry, void>;
|
|
64
|
+
private _currentProvider;
|
|
65
|
+
private _completer;
|
|
66
|
+
private _chatModel;
|
|
40
67
|
private _name;
|
|
41
|
-
private
|
|
68
|
+
private _providerChanged;
|
|
42
69
|
private _chatError;
|
|
43
70
|
private _completerError;
|
|
71
|
+
private _providers;
|
|
44
72
|
}
|
|
45
|
-
export declare namespace
|
|
73
|
+
export declare namespace AIProviderRegistry {
|
|
46
74
|
/**
|
|
47
75
|
* The options for the LLM provider.
|
|
48
76
|
*/
|
package/lib/provider.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { Signal } from '@lumino/signaling';
|
|
2
|
-
import { CompletionProvider } from './completion-provider';
|
|
3
|
-
import { getChatModel } from './llm-models';
|
|
4
2
|
export const chatSystemPrompt = (options) => `
|
|
5
3
|
You are Jupyternaut, a conversational assistant living in JupyterLab to help users.
|
|
6
4
|
You are not a language model, but rather an application built on a foundation model from ${options.provider_name}.
|
|
@@ -27,40 +25,83 @@ The output should be a single string, and should correspond to what a human user
|
|
|
27
25
|
would write.
|
|
28
26
|
Do not include the prompt in the output, only the string that should be appended to the current input.
|
|
29
27
|
`;
|
|
30
|
-
export class
|
|
31
|
-
constructor(
|
|
32
|
-
this.
|
|
28
|
+
export class AIProviderRegistry {
|
|
29
|
+
constructor() {
|
|
30
|
+
this._currentProvider = null;
|
|
31
|
+
this._completer = null;
|
|
32
|
+
this._chatModel = null;
|
|
33
33
|
this._name = 'None';
|
|
34
|
-
this.
|
|
34
|
+
this._providerChanged = new Signal(this);
|
|
35
35
|
this._chatError = '';
|
|
36
36
|
this._completerError = '';
|
|
37
|
-
this.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
this._providers = new Map();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get the list of provider names.
|
|
41
|
+
*/
|
|
42
|
+
get providers() {
|
|
43
|
+
return Array.from(this._providers.keys());
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Add a new provider.
|
|
47
|
+
*/
|
|
48
|
+
add(provider) {
|
|
49
|
+
if (this._providers.has(provider.name)) {
|
|
50
|
+
throw new Error(`A AI provider named '${provider.name}' is already registered`);
|
|
51
|
+
}
|
|
52
|
+
this._providers.set(provider.name, provider);
|
|
43
53
|
}
|
|
44
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Get the current provider name.
|
|
56
|
+
*/
|
|
57
|
+
get currentName() {
|
|
45
58
|
return this._name;
|
|
46
59
|
}
|
|
47
60
|
/**
|
|
48
61
|
* Get the current completer of the completion provider.
|
|
49
62
|
*/
|
|
50
|
-
get
|
|
51
|
-
if (this._name ===
|
|
63
|
+
get currentCompleter() {
|
|
64
|
+
if (this._name === 'None') {
|
|
52
65
|
return null;
|
|
53
66
|
}
|
|
54
|
-
return this.
|
|
67
|
+
return this._completer;
|
|
55
68
|
}
|
|
56
69
|
/**
|
|
57
70
|
* Get the current llm chat model.
|
|
58
71
|
*/
|
|
59
|
-
get
|
|
60
|
-
if (this._name ===
|
|
72
|
+
get currentChatModel() {
|
|
73
|
+
if (this._name === 'None') {
|
|
61
74
|
return null;
|
|
62
75
|
}
|
|
63
|
-
return this.
|
|
76
|
+
return this._chatModel;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get the settings schema of a given provider.
|
|
80
|
+
*/
|
|
81
|
+
getSettingsSchema(provider) {
|
|
82
|
+
var _a, _b;
|
|
83
|
+
return (((_b = (_a = this._providers.get(provider)) === null || _a === void 0 ? void 0 : _a.settingsSchema) === null || _b === void 0 ? void 0 : _b.properties) ||
|
|
84
|
+
{});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get the instructions of a given provider.
|
|
88
|
+
*/
|
|
89
|
+
getInstructions(provider) {
|
|
90
|
+
var _a;
|
|
91
|
+
return (_a = this._providers.get(provider)) === null || _a === void 0 ? void 0 : _a.instructions;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Format an error message from the current provider.
|
|
95
|
+
*/
|
|
96
|
+
formatErrorMessage(error) {
|
|
97
|
+
var _a, _b;
|
|
98
|
+
if ((_a = this._currentProvider) === null || _a === void 0 ? void 0 : _a.errorMessage) {
|
|
99
|
+
return (_b = this._currentProvider) === null || _b === void 0 ? void 0 : _b.errorMessage(error);
|
|
100
|
+
}
|
|
101
|
+
if (error.message) {
|
|
102
|
+
return error.message;
|
|
103
|
+
}
|
|
104
|
+
return error;
|
|
64
105
|
}
|
|
65
106
|
/**
|
|
66
107
|
* Get the current chat error;
|
|
@@ -75,36 +116,51 @@ export class AIProvider {
|
|
|
75
116
|
return this._completerError;
|
|
76
117
|
}
|
|
77
118
|
/**
|
|
78
|
-
* Set the
|
|
79
|
-
* Creates the
|
|
119
|
+
* Set the providers (chat model and completer).
|
|
120
|
+
* Creates the providers if the name has changed, otherwise only updates their config.
|
|
80
121
|
*
|
|
81
|
-
* @param name - the name of the
|
|
122
|
+
* @param name - the name of the provider to use.
|
|
82
123
|
* @param settings - the settings for the models.
|
|
83
124
|
*/
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
125
|
+
setProvider(name, settings) {
|
|
126
|
+
var _a, _b, _c;
|
|
127
|
+
this._currentProvider = (_a = this._providers.get(name)) !== null && _a !== void 0 ? _a : null;
|
|
128
|
+
if (((_b = this._currentProvider) === null || _b === void 0 ? void 0 : _b.completer) !== undefined) {
|
|
129
|
+
try {
|
|
130
|
+
this._completer = new this._currentProvider.completer({ ...settings });
|
|
131
|
+
this._completerError = '';
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
this._completerError = e.message;
|
|
135
|
+
}
|
|
88
136
|
}
|
|
89
|
-
|
|
90
|
-
this.
|
|
137
|
+
else {
|
|
138
|
+
this._completer = null;
|
|
91
139
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
140
|
+
if (((_c = this._currentProvider) === null || _c === void 0 ? void 0 : _c.chatModel) !== undefined) {
|
|
141
|
+
try {
|
|
142
|
+
this._chatModel = new this._currentProvider.chatModel({ ...settings });
|
|
143
|
+
this._chatError = '';
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
this._chatError = e.message;
|
|
147
|
+
this._chatModel = null;
|
|
148
|
+
}
|
|
95
149
|
}
|
|
96
|
-
|
|
97
|
-
this.
|
|
98
|
-
this._llmChatModel = null;
|
|
150
|
+
else {
|
|
151
|
+
this._chatModel = null;
|
|
99
152
|
}
|
|
100
153
|
this._name = name;
|
|
101
|
-
this.
|
|
154
|
+
this._providerChanged.emit();
|
|
102
155
|
}
|
|
103
|
-
|
|
104
|
-
|
|
156
|
+
/**
|
|
157
|
+
* A signal emitting when the provider or its settings has changed.
|
|
158
|
+
*/
|
|
159
|
+
get providerChanged() {
|
|
160
|
+
return this._providerChanged;
|
|
105
161
|
}
|
|
106
162
|
}
|
|
107
|
-
(function (
|
|
163
|
+
(function (AIProviderRegistry) {
|
|
108
164
|
/**
|
|
109
165
|
* This function indicates whether a key is writable in an object.
|
|
110
166
|
* https://stackoverflow.com/questions/54724875/can-we-check-whether-property-is-readonly-in-typescript
|
|
@@ -119,7 +175,7 @@ export class AIProvider {
|
|
|
119
175
|
{};
|
|
120
176
|
return Boolean(desc.writable);
|
|
121
177
|
}
|
|
122
|
-
|
|
178
|
+
AIProviderRegistry.isWritable = isWritable;
|
|
123
179
|
/**
|
|
124
180
|
* Update the config of a language model.
|
|
125
181
|
* It only updates the writable attributes of the model.
|
|
@@ -139,5 +195,5 @@ export class AIProvider {
|
|
|
139
195
|
}
|
|
140
196
|
});
|
|
141
197
|
}
|
|
142
|
-
|
|
143
|
-
})(
|
|
198
|
+
AIProviderRegistry.updateConfig = updateConfig;
|
|
199
|
+
})(AIProviderRegistry || (AIProviderRegistry = {}));
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
2
|
+
import { IFormRenderer } from '@jupyterlab/ui-components';
|
|
3
|
+
import type { FieldProps } from '@rjsf/utils';
|
|
4
|
+
import { JSONSchema7 } from 'json-schema';
|
|
5
|
+
import { ISecretsManager } from 'jupyter-secrets-manager';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { IAIProviderRegistry, IDict } from '../tokens';
|
|
8
|
+
export declare const aiSettingsRenderer: (options: {
|
|
9
|
+
providerRegistry: IAIProviderRegistry;
|
|
10
|
+
rmRegistry?: IRenderMimeRegistry;
|
|
11
|
+
secretsManager?: ISecretsManager;
|
|
12
|
+
}) => IFormRenderer;
|
|
13
|
+
export interface ISettingsFormStates {
|
|
14
|
+
schema: JSONSchema7;
|
|
15
|
+
instruction: HTMLElement | null;
|
|
16
|
+
}
|
|
17
|
+
export declare class AiSettings extends React.Component<FieldProps, ISettingsFormStates> {
|
|
18
|
+
constructor(props: FieldProps);
|
|
19
|
+
componentDidUpdate(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Get the current provider from the local storage.
|
|
22
|
+
*/
|
|
23
|
+
getCurrentProvider(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Save the current provider to the local storage.
|
|
26
|
+
*/
|
|
27
|
+
saveCurrentProvider(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Get settings from local storage for a given provider.
|
|
30
|
+
*/
|
|
31
|
+
getSettings(): IDict<any>;
|
|
32
|
+
/**
|
|
33
|
+
* Save settings in local storage for a given provider.
|
|
34
|
+
*/
|
|
35
|
+
saveSettings(value: IDict<any>): void;
|
|
36
|
+
private updateUseSecretsManager;
|
|
37
|
+
/**
|
|
38
|
+
* Update the UI schema of the form.
|
|
39
|
+
* Currently use to hide API keys.
|
|
40
|
+
*/
|
|
41
|
+
private _updateUiSchema;
|
|
42
|
+
/**
|
|
43
|
+
* Build the schema for a given provider.
|
|
44
|
+
*/
|
|
45
|
+
private _buildSchema;
|
|
46
|
+
/**
|
|
47
|
+
* Update the schema state for the given provider, that trigger the re-rendering of
|
|
48
|
+
* the component.
|
|
49
|
+
*/
|
|
50
|
+
private _updateSchema;
|
|
51
|
+
/**
|
|
52
|
+
* Render the markdown instructions for the current provider.
|
|
53
|
+
*/
|
|
54
|
+
private _renderInstruction;
|
|
55
|
+
/**
|
|
56
|
+
* Triggered when the provider hes changed, to update the schema and values.
|
|
57
|
+
* Update the Jupyterlab settings accordingly.
|
|
58
|
+
*/
|
|
59
|
+
private _onProviderChanged;
|
|
60
|
+
/**
|
|
61
|
+
* Callback function called when the password input has been programmatically updated
|
|
62
|
+
* with the secret manager.
|
|
63
|
+
*/
|
|
64
|
+
private _onPasswordUpdated;
|
|
65
|
+
/**
|
|
66
|
+
* Triggered when the form value has changed, to update the current settings and save
|
|
67
|
+
* it in local storage.
|
|
68
|
+
* Update the Jupyterlab settings accordingly.
|
|
69
|
+
*/
|
|
70
|
+
private _onFormChange;
|
|
71
|
+
render(): JSX.Element;
|
|
72
|
+
private _providerRegistry;
|
|
73
|
+
private _provider;
|
|
74
|
+
private _providerSchema;
|
|
75
|
+
private _useSecretsManager;
|
|
76
|
+
private _rmRegistry;
|
|
77
|
+
private _secretsManager;
|
|
78
|
+
private _currentSettings;
|
|
79
|
+
private _uiSchema;
|
|
80
|
+
private _settings;
|
|
81
|
+
private _formRef;
|
|
82
|
+
private _unsavedFields;
|
|
83
|
+
private _formInputs;
|
|
84
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { FormComponent } from '@jupyterlab/ui-components';
|
|
2
|
+
import { ArrayExt } from '@lumino/algorithm';
|
|
3
|
+
import { JSONExt } from '@lumino/coreutils';
|
|
4
|
+
import validator from '@rjsf/validator-ajv8';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import baseSettings from './base.json';
|
|
7
|
+
const SECRETS_NAMESPACE = '@jupyterlite/ai';
|
|
8
|
+
const MD_MIME_TYPE = 'text/markdown';
|
|
9
|
+
const STORAGE_NAME = '@jupyterlite/ai:settings';
|
|
10
|
+
const INSTRUCTION_CLASS = 'jp-AISettingsInstructions';
|
|
11
|
+
export const aiSettingsRenderer = (options) => {
|
|
12
|
+
return {
|
|
13
|
+
fieldRenderer: (props) => {
|
|
14
|
+
props.formContext = { ...props.formContext, ...options };
|
|
15
|
+
return React.createElement(AiSettings, { ...props });
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
const WrappedFormComponent = (props) => {
|
|
20
|
+
return React.createElement(FormComponent, { ...props, validator: validator });
|
|
21
|
+
};
|
|
22
|
+
export class AiSettings extends React.Component {
|
|
23
|
+
constructor(props) {
|
|
24
|
+
var _a, _b, _c, _d;
|
|
25
|
+
super(props);
|
|
26
|
+
this.updateUseSecretsManager = (value) => {
|
|
27
|
+
var _a;
|
|
28
|
+
this._useSecretsManager = value;
|
|
29
|
+
if (!value) {
|
|
30
|
+
// Detach all the password inputs attached to the secrets manager, and save the
|
|
31
|
+
// 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 = [];
|
|
35
|
+
this.saveSettings(this._currentSettings);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Remove all the keys stored locally and attach the password inputs to the
|
|
39
|
+
// secrets manager.
|
|
40
|
+
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
|
|
41
|
+
Object.keys(settings).forEach(provider => {
|
|
42
|
+
Object.keys(settings[provider])
|
|
43
|
+
.filter(key => key.toLowerCase().includes('key'))
|
|
44
|
+
.forEach(key => {
|
|
45
|
+
delete settings[provider][key];
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
|
|
49
|
+
this.componentDidUpdate();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Triggered when the provider hes changed, to update the schema and values.
|
|
54
|
+
* Update the Jupyterlab settings accordingly.
|
|
55
|
+
*/
|
|
56
|
+
this._onProviderChanged = (e) => {
|
|
57
|
+
const provider = e.formData.provider;
|
|
58
|
+
if (provider === this._currentSettings.provider) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this._provider = provider;
|
|
62
|
+
this.saveCurrentProvider();
|
|
63
|
+
this._currentSettings = this.getSettings();
|
|
64
|
+
this._updateSchema();
|
|
65
|
+
this._renderInstruction();
|
|
66
|
+
this._settings
|
|
67
|
+
.set('AIprovider', { provider: this._provider, ...this._currentSettings })
|
|
68
|
+
.catch(console.error);
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Callback function called when the password input has been programmatically updated
|
|
72
|
+
* with the secret manager.
|
|
73
|
+
*/
|
|
74
|
+
this._onPasswordUpdated = (fieldName, value) => {
|
|
75
|
+
this._currentSettings[fieldName] = value;
|
|
76
|
+
this._settings
|
|
77
|
+
.set('AIprovider', { provider: this._provider, ...this._currentSettings })
|
|
78
|
+
.catch(console.error);
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Triggered when the form value has changed, to update the current settings and save
|
|
82
|
+
* it in local storage.
|
|
83
|
+
* Update the Jupyterlab settings accordingly.
|
|
84
|
+
*/
|
|
85
|
+
this._onFormChange = (e) => {
|
|
86
|
+
this._currentSettings = JSONExt.deepCopy(e.formData);
|
|
87
|
+
this.saveSettings(this._currentSettings);
|
|
88
|
+
this._settings
|
|
89
|
+
.set('AIprovider', { provider: this._provider, ...this._currentSettings })
|
|
90
|
+
.catch(console.error);
|
|
91
|
+
};
|
|
92
|
+
this._currentSettings = { provider: 'None' };
|
|
93
|
+
this._uiSchema = {};
|
|
94
|
+
this._formRef = React.createRef();
|
|
95
|
+
this._unsavedFields = [];
|
|
96
|
+
this._formInputs = [];
|
|
97
|
+
if (!props.formContext.providerRegistry) {
|
|
98
|
+
throw new Error('The provider registry is needed to enable the jupyterlite-ai settings panel');
|
|
99
|
+
}
|
|
100
|
+
this._providerRegistry = props.formContext.providerRegistry;
|
|
101
|
+
this._rmRegistry = (_a = props.formContext.rmRegistry) !== null && _a !== void 0 ? _a : null;
|
|
102
|
+
this._secretsManager = (_b = props.formContext.secretsManager) !== null && _b !== void 0 ? _b : null;
|
|
103
|
+
this._settings = props.formContext.settings;
|
|
104
|
+
this._useSecretsManager =
|
|
105
|
+
(_c = this._settings.get('UseSecretsManager').composite) !== null && _c !== void 0 ? _c : true;
|
|
106
|
+
// Initialize the providers schema.
|
|
107
|
+
const providerSchema = JSONExt.deepCopy(baseSettings);
|
|
108
|
+
providerSchema.properties.provider = {
|
|
109
|
+
type: 'string',
|
|
110
|
+
title: 'Provider',
|
|
111
|
+
description: 'The AI provider to use for chat and completion',
|
|
112
|
+
default: 'None',
|
|
113
|
+
enum: ['None'].concat(this._providerRegistry.providers)
|
|
114
|
+
};
|
|
115
|
+
this._providerSchema = providerSchema;
|
|
116
|
+
// Check if there is saved values in local storage, otherwise use the settings from
|
|
117
|
+
// the setting registry (led to default if there are no user settings).
|
|
118
|
+
const storageSettings = localStorage.getItem(STORAGE_NAME);
|
|
119
|
+
if (storageSettings === null) {
|
|
120
|
+
const labSettings = this._settings.get('AIprovider').composite;
|
|
121
|
+
if (labSettings && Object.keys(labSettings).includes('provider')) {
|
|
122
|
+
// Get the provider name.
|
|
123
|
+
const provider = (_d = Object.entries(labSettings).find(v => v[0] === 'provider')) === null || _d === void 0 ? void 0 : _d[1];
|
|
124
|
+
// Save the settings.
|
|
125
|
+
const settings = {
|
|
126
|
+
_current: provider
|
|
127
|
+
};
|
|
128
|
+
settings[provider] = labSettings;
|
|
129
|
+
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Initialize the settings from the saved ones.
|
|
133
|
+
this._provider = this.getCurrentProvider();
|
|
134
|
+
this._currentSettings = this.getSettings();
|
|
135
|
+
// Initialize the schema.
|
|
136
|
+
const schema = this._buildSchema();
|
|
137
|
+
this.state = { schema, instruction: null };
|
|
138
|
+
this._renderInstruction();
|
|
139
|
+
// Update the setting registry.
|
|
140
|
+
this._settings
|
|
141
|
+
.set('AIprovider', this._currentSettings)
|
|
142
|
+
.catch(console.error);
|
|
143
|
+
this._settings.changed.connect(() => {
|
|
144
|
+
var _a;
|
|
145
|
+
const useSecretsManager = (_a = this._settings.get('UseSecretsManager').composite) !== null && _a !== void 0 ? _a : true;
|
|
146
|
+
if (useSecretsManager !== this._useSecretsManager) {
|
|
147
|
+
this.updateUseSecretsManager(useSecretsManager);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
async componentDidUpdate() {
|
|
152
|
+
var _a;
|
|
153
|
+
if (!this._secretsManager || !this._useSecretsManager) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// Attach the password inputs to the secrets manager only if they have changed.
|
|
157
|
+
const inputs = ((_a = this._formRef.current) === null || _a === void 0 ? void 0 : _a.getElementsByTagName('input')) || [];
|
|
158
|
+
if (ArrayExt.shallowEqual(inputs, this._formInputs)) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
await this._secretsManager.detachAll(SECRETS_NAMESPACE);
|
|
162
|
+
this._formInputs = [...inputs];
|
|
163
|
+
this._unsavedFields = [];
|
|
164
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
165
|
+
if (inputs[i].type.toLowerCase() === 'password') {
|
|
166
|
+
const label = inputs[i].getAttribute('label');
|
|
167
|
+
if (label) {
|
|
168
|
+
const id = `${this._provider}-${label}`;
|
|
169
|
+
this._secretsManager.attach(SECRETS_NAMESPACE, id, inputs[i], (value) => this._onPasswordUpdated(label, value));
|
|
170
|
+
this._unsavedFields.push(label);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get the current provider from the local storage.
|
|
177
|
+
*/
|
|
178
|
+
getCurrentProvider() {
|
|
179
|
+
var _a;
|
|
180
|
+
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
|
|
181
|
+
return (_a = settings['_current']) !== null && _a !== void 0 ? _a : 'None';
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Save the current provider to the local storage.
|
|
185
|
+
*/
|
|
186
|
+
saveCurrentProvider() {
|
|
187
|
+
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
|
|
188
|
+
settings['_current'] = this._provider;
|
|
189
|
+
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Get settings from local storage for a given provider.
|
|
193
|
+
*/
|
|
194
|
+
getSettings() {
|
|
195
|
+
var _a;
|
|
196
|
+
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
|
|
197
|
+
return (_a = settings[this._provider]) !== null && _a !== void 0 ? _a : { provider: this._provider };
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Save settings in local storage for a given provider.
|
|
201
|
+
*/
|
|
202
|
+
saveSettings(value) {
|
|
203
|
+
var _a;
|
|
204
|
+
const currentSettings = { ...value };
|
|
205
|
+
const settings = JSON.parse((_a = localStorage.getItem(STORAGE_NAME)) !== null && _a !== void 0 ? _a : '{}');
|
|
206
|
+
this._unsavedFields.forEach(field => delete currentSettings[field]);
|
|
207
|
+
settings[this._provider] = currentSettings;
|
|
208
|
+
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Update the UI schema of the form.
|
|
212
|
+
* Currently use to hide API keys.
|
|
213
|
+
*/
|
|
214
|
+
_updateUiSchema(key) {
|
|
215
|
+
if (key.toLowerCase().includes('key')) {
|
|
216
|
+
this._uiSchema[key] = { 'ui:widget': 'password' };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Build the schema for a given provider.
|
|
221
|
+
*/
|
|
222
|
+
_buildSchema() {
|
|
223
|
+
const schema = JSONExt.deepCopy(baseSettings);
|
|
224
|
+
this._uiSchema = {};
|
|
225
|
+
const settingsSchema = this._providerRegistry.getSettingsSchema(this._provider);
|
|
226
|
+
if (settingsSchema) {
|
|
227
|
+
Object.entries(settingsSchema).forEach(([key, value]) => {
|
|
228
|
+
schema.properties[key] = value;
|
|
229
|
+
this._updateUiSchema(key);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return schema;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Update the schema state for the given provider, that trigger the re-rendering of
|
|
236
|
+
* the component.
|
|
237
|
+
*/
|
|
238
|
+
_updateSchema() {
|
|
239
|
+
const schema = this._buildSchema();
|
|
240
|
+
this.setState({ schema });
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Render the markdown instructions for the current provider.
|
|
244
|
+
*/
|
|
245
|
+
async _renderInstruction() {
|
|
246
|
+
let instructions = this._providerRegistry.getInstructions(this._provider);
|
|
247
|
+
if (!this._rmRegistry || !instructions) {
|
|
248
|
+
this.setState({ instruction: null });
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
instructions = `---\n\n${instructions}\n\n---`;
|
|
252
|
+
const renderer = this._rmRegistry.createRenderer(MD_MIME_TYPE);
|
|
253
|
+
const model = this._rmRegistry.createModel({
|
|
254
|
+
data: { [MD_MIME_TYPE]: instructions }
|
|
255
|
+
});
|
|
256
|
+
await renderer.renderModel(model);
|
|
257
|
+
this.setState({ instruction: renderer.node });
|
|
258
|
+
}
|
|
259
|
+
render() {
|
|
260
|
+
return (React.createElement("div", { ref: this._formRef },
|
|
261
|
+
React.createElement(WrappedFormComponent, { formData: { provider: this._provider }, schema: this._providerSchema, onChange: this._onProviderChanged }),
|
|
262
|
+
this.state.instruction !== null && (React.createElement("details", null,
|
|
263
|
+
React.createElement("summary", { className: INSTRUCTION_CLASS }, "Instructions"),
|
|
264
|
+
React.createElement("span", { ref: node => node && node.replaceChildren(this.state.instruction) }))),
|
|
265
|
+
React.createElement(WrappedFormComponent, { formData: this._currentSettings, schema: this.state.schema, onChange: this._onFormChange, uiSchema: this._uiSchema })));
|
|
266
|
+
}
|
|
267
|
+
}
|