@jupyterlite/ai 0.8.0 → 0.9.0-a0
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/agent.d.ts +233 -0
- package/lib/agent.js +604 -0
- package/lib/chat-model.d.ts +195 -0
- package/lib/chat-model.js +590 -0
- package/lib/completion/completion-provider.d.ts +83 -0
- package/lib/completion/completion-provider.js +209 -0
- package/lib/completion/index.d.ts +1 -0
- package/lib/completion/index.js +1 -0
- package/lib/components/clear-button.d.ts +18 -0
- package/lib/components/clear-button.js +31 -0
- package/lib/components/index.d.ts +3 -0
- package/lib/components/index.js +3 -0
- package/lib/components/model-select.d.ts +19 -0
- package/lib/components/model-select.js +154 -0
- package/lib/components/stop-button.d.ts +3 -3
- package/lib/components/stop-button.js +8 -9
- package/lib/components/token-usage-display.d.ts +45 -0
- package/lib/components/token-usage-display.js +74 -0
- package/lib/components/tool-select.d.ts +27 -0
- package/lib/components/tool-select.js +130 -0
- package/lib/icons.d.ts +3 -1
- package/lib/icons.js +10 -13
- package/lib/index.d.ts +4 -5
- package/lib/index.js +322 -167
- package/lib/mcp/browser.d.ts +68 -0
- package/lib/mcp/browser.js +132 -0
- package/lib/models/settings-model.d.ts +69 -0
- package/lib/models/settings-model.js +295 -0
- package/lib/providers/built-in-providers.d.ts +9 -0
- package/lib/providers/built-in-providers.js +192 -0
- package/lib/providers/models.d.ts +37 -0
- package/lib/providers/models.js +28 -0
- package/lib/providers/provider-registry.d.ts +94 -0
- package/lib/providers/provider-registry.js +155 -0
- package/lib/tokens.d.ts +157 -86
- package/lib/tokens.js +16 -12
- package/lib/tools/commands.d.ts +11 -0
- package/lib/tools/commands.js +126 -0
- package/lib/tools/file.d.ts +27 -0
- package/lib/tools/file.js +262 -0
- package/lib/tools/notebook.d.ts +40 -0
- package/lib/tools/notebook.js +762 -0
- package/lib/tools/tool-registry.d.ts +35 -0
- package/lib/tools/tool-registry.js +55 -0
- package/lib/widgets/ai-settings.d.ts +39 -0
- package/lib/widgets/ai-settings.js +506 -0
- package/lib/widgets/chat-wrapper.d.ts +144 -0
- package/lib/widgets/chat-wrapper.js +390 -0
- package/lib/widgets/provider-config-dialog.d.ts +13 -0
- package/lib/widgets/provider-config-dialog.js +104 -0
- package/package.json +150 -41
- package/schema/settings-model.json +153 -0
- package/src/agent.ts +800 -0
- package/src/chat-model.ts +770 -0
- package/src/completion/completion-provider.ts +308 -0
- package/src/completion/index.ts +1 -0
- package/src/components/clear-button.tsx +56 -0
- package/src/components/index.ts +3 -0
- package/src/components/model-select.tsx +245 -0
- package/src/components/stop-button.tsx +11 -11
- package/src/components/token-usage-display.tsx +130 -0
- package/src/components/tool-select.tsx +218 -0
- package/src/icons.ts +12 -14
- package/src/index.ts +468 -238
- package/src/mcp/browser.ts +213 -0
- package/src/models/settings-model.ts +409 -0
- package/src/providers/built-in-providers.ts +216 -0
- package/src/providers/models.ts +79 -0
- package/src/providers/provider-registry.ts +189 -0
- package/src/tokens.ts +203 -90
- package/src/tools/commands.ts +151 -0
- package/src/tools/file.ts +307 -0
- package/src/tools/notebook.ts +964 -0
- package/src/tools/tool-registry.ts +63 -0
- package/src/types.d.ts +4 -0
- package/src/widgets/ai-settings.tsx +1100 -0
- package/src/widgets/chat-wrapper.tsx +543 -0
- package/src/widgets/provider-config-dialog.tsx +256 -0
- package/style/base.css +335 -14
- package/style/icons/jupyternaut-lite.svg +1 -1
- package/lib/base-completer.d.ts +0 -49
- package/lib/base-completer.js +0 -14
- package/lib/chat-handler.d.ts +0 -56
- package/lib/chat-handler.js +0 -201
- package/lib/completion-provider.d.ts +0 -34
- package/lib/completion-provider.js +0 -32
- package/lib/default-prompts.d.ts +0 -2
- package/lib/default-prompts.js +0 -31
- package/lib/default-providers/Anthropic/completer.d.ts +0 -12
- package/lib/default-providers/Anthropic/completer.js +0 -46
- package/lib/default-providers/Anthropic/settings-schema.json +0 -70
- package/lib/default-providers/ChromeAI/completer.d.ts +0 -12
- package/lib/default-providers/ChromeAI/completer.js +0 -56
- package/lib/default-providers/ChromeAI/instructions.d.ts +0 -6
- package/lib/default-providers/ChromeAI/instructions.js +0 -42
- package/lib/default-providers/ChromeAI/settings-schema.json +0 -18
- package/lib/default-providers/Gemini/completer.d.ts +0 -12
- package/lib/default-providers/Gemini/completer.js +0 -48
- package/lib/default-providers/Gemini/instructions.d.ts +0 -2
- package/lib/default-providers/Gemini/instructions.js +0 -9
- package/lib/default-providers/Gemini/settings-schema.json +0 -64
- package/lib/default-providers/MistralAI/completer.d.ts +0 -13
- package/lib/default-providers/MistralAI/completer.js +0 -52
- package/lib/default-providers/MistralAI/instructions.d.ts +0 -2
- package/lib/default-providers/MistralAI/instructions.js +0 -18
- package/lib/default-providers/MistralAI/settings-schema.json +0 -75
- package/lib/default-providers/Ollama/completer.d.ts +0 -12
- package/lib/default-providers/Ollama/completer.js +0 -43
- package/lib/default-providers/Ollama/instructions.d.ts +0 -2
- package/lib/default-providers/Ollama/instructions.js +0 -70
- package/lib/default-providers/Ollama/settings-schema.json +0 -143
- package/lib/default-providers/OpenAI/completer.d.ts +0 -12
- package/lib/default-providers/OpenAI/completer.js +0 -43
- package/lib/default-providers/OpenAI/settings-schema.json +0 -628
- package/lib/default-providers/WebLLM/completer.d.ts +0 -21
- package/lib/default-providers/WebLLM/completer.js +0 -127
- package/lib/default-providers/WebLLM/instructions.d.ts +0 -6
- package/lib/default-providers/WebLLM/instructions.js +0 -32
- package/lib/default-providers/WebLLM/settings-schema.json +0 -19
- package/lib/default-providers/index.d.ts +0 -2
- package/lib/default-providers/index.js +0 -179
- package/lib/provider.d.ts +0 -144
- package/lib/provider.js +0 -412
- package/lib/settings/base.json +0 -7
- package/lib/settings/index.d.ts +0 -3
- package/lib/settings/index.js +0 -3
- package/lib/settings/panel.d.ts +0 -226
- package/lib/settings/panel.js +0 -510
- package/lib/settings/textarea.d.ts +0 -2
- package/lib/settings/textarea.js +0 -18
- package/lib/settings/utils.d.ts +0 -2
- package/lib/settings/utils.js +0 -4
- package/lib/types/ai-model.d.ts +0 -24
- package/lib/types/ai-model.js +0 -5
- package/schema/chat.json +0 -28
- package/schema/provider-registry.json +0 -29
- package/schema/system-prompts.json +0 -22
- package/src/base-completer.ts +0 -75
- package/src/chat-handler.ts +0 -262
- package/src/completion-provider.ts +0 -64
- package/src/default-prompts.ts +0 -33
- package/src/default-providers/Anthropic/completer.ts +0 -59
- package/src/default-providers/ChromeAI/completer.ts +0 -73
- package/src/default-providers/ChromeAI/instructions.ts +0 -45
- package/src/default-providers/Gemini/completer.ts +0 -61
- package/src/default-providers/Gemini/instructions.ts +0 -9
- package/src/default-providers/MistralAI/completer.ts +0 -69
- package/src/default-providers/MistralAI/instructions.ts +0 -18
- package/src/default-providers/Ollama/completer.ts +0 -54
- package/src/default-providers/Ollama/instructions.ts +0 -70
- package/src/default-providers/OpenAI/completer.ts +0 -54
- package/src/default-providers/WebLLM/completer.ts +0 -151
- package/src/default-providers/WebLLM/instructions.ts +0 -33
- package/src/default-providers/index.ts +0 -211
- package/src/global.d.ts +0 -9
- package/src/provider.ts +0 -514
- package/src/settings/index.ts +0 -3
- package/src/settings/panel.tsx +0 -773
- package/src/settings/textarea.tsx +0 -33
- package/src/settings/utils.ts +0 -5
- package/src/types/ai-model.ts +0 -37
- package/src/types/service-worker.d.ts +0 -6
package/src/settings/panel.tsx
DELETED
|
@@ -1,773 +0,0 @@
|
|
|
1
|
-
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
2
|
-
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
3
|
-
import {
|
|
4
|
-
Button,
|
|
5
|
-
FormComponent,
|
|
6
|
-
IFormRenderer
|
|
7
|
-
} from '@jupyterlab/ui-components';
|
|
8
|
-
import { JSONExt, ReadonlyPartialJSONObject } from '@lumino/coreutils';
|
|
9
|
-
import { IChangeEvent } from '@rjsf/core';
|
|
10
|
-
import type { FieldProps } from '@rjsf/utils';
|
|
11
|
-
import validator from '@rjsf/validator-ajv8';
|
|
12
|
-
import { JSONSchema7 } from 'json-schema';
|
|
13
|
-
import { ISecretsManager } from 'jupyter-secrets-manager';
|
|
14
|
-
import React from 'react';
|
|
15
|
-
|
|
16
|
-
import { getSecretId, SECRETS_REPLACEMENT } from '.';
|
|
17
|
-
import baseSettings from './base.json';
|
|
18
|
-
import { IAIProviderRegistry, IDict, ModelRole, PLUGIN_IDS } from '../tokens';
|
|
19
|
-
|
|
20
|
-
const MD_MIME_TYPE = 'text/markdown';
|
|
21
|
-
const INSTRUCTION_CLASS = 'jp-AISettingsInstructions';
|
|
22
|
-
const ERROR_CLASS = 'jp-AISettingsError';
|
|
23
|
-
const SECRETS_NAMESPACE = PLUGIN_IDS.providerRegistry;
|
|
24
|
-
const STORAGE_KEYS = {
|
|
25
|
-
chat: '@jupyterlite/ai:chat-settings',
|
|
26
|
-
completer: '@jupyterlite/ai:completer-settings'
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export const aiSettingsRenderer = (options: {
|
|
30
|
-
providerRegistry: IAIProviderRegistry;
|
|
31
|
-
secretsToken?: symbol;
|
|
32
|
-
rmRegistry?: IRenderMimeRegistry;
|
|
33
|
-
secretsManager?: ISecretsManager;
|
|
34
|
-
}): IFormRenderer => {
|
|
35
|
-
const { secretsToken } = options;
|
|
36
|
-
delete options.secretsToken;
|
|
37
|
-
if (secretsToken) {
|
|
38
|
-
Private.setToken(secretsToken);
|
|
39
|
-
}
|
|
40
|
-
return {
|
|
41
|
-
fieldRenderer: (props: FieldProps) => {
|
|
42
|
-
props.formContext = { ...props.formContext, ...options };
|
|
43
|
-
return <AiSettings {...props} />;
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const WrappedFormComponent = (props: any): JSX.Element => {
|
|
49
|
-
return <FormComponent {...props} validator={validator} />;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export interface IAiSettings {
|
|
53
|
-
/**
|
|
54
|
-
* Get the local storage settings for a specific role (chat or completer).
|
|
55
|
-
*/
|
|
56
|
-
getLocalStorage(role: ModelRole): IDict<any>;
|
|
57
|
-
/**
|
|
58
|
-
* Set the local storage item for a specific role (chat or completer).
|
|
59
|
-
* If the key is not provider (null) we assume the value should replace the whole
|
|
60
|
-
* local storage for this role.
|
|
61
|
-
*/
|
|
62
|
-
setLocalStorageItem(role: ModelRole, key: string | null, value: any): void;
|
|
63
|
-
/**
|
|
64
|
-
* Get the settings from the registry (jupyterlab settings system) for a given role.
|
|
65
|
-
*/
|
|
66
|
-
getSettingsFromRegistry(role: ModelRole): IDict<any>;
|
|
67
|
-
/**
|
|
68
|
-
* Save the settings to the setting registry.
|
|
69
|
-
*/
|
|
70
|
-
saveSettingsToRegistry(role: ModelRole, settings: IDict<any>): void;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export class AiSettings
|
|
74
|
-
extends React.Component<FieldProps, AiSettings.states>
|
|
75
|
-
implements IAiSettings
|
|
76
|
-
{
|
|
77
|
-
constructor(props: FieldProps) {
|
|
78
|
-
super(props);
|
|
79
|
-
this._settings = props.formContext.settings;
|
|
80
|
-
const uniqueProvider =
|
|
81
|
-
(this._settings.get('UniqueProvider').composite as boolean) ?? true;
|
|
82
|
-
|
|
83
|
-
this.state = { uniqueProvider };
|
|
84
|
-
|
|
85
|
-
this._settings.changed.connect(this._settingsChanged);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
private _settingsChanged = () => {
|
|
89
|
-
const uniqueProvider =
|
|
90
|
-
(this._settings.get('UniqueProvider').composite as boolean) ?? true;
|
|
91
|
-
if (this.state.uniqueProvider === uniqueProvider) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
if (uniqueProvider) {
|
|
95
|
-
// Copy chat settings to the completer settings if there should be a unique
|
|
96
|
-
// provider for both.
|
|
97
|
-
this.setLocalStorageItem('completer', null, this.getLocalStorage('chat'));
|
|
98
|
-
this.saveSettingsToRegistry(
|
|
99
|
-
'completer',
|
|
100
|
-
this.getSettingsFromRegistry('chat')
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
this.setState({ uniqueProvider });
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Get the local storage settings for a specific role (chat or completer).
|
|
108
|
-
*/
|
|
109
|
-
getLocalStorage = (role: ModelRole): IDict<any> => {
|
|
110
|
-
const storageKey = STORAGE_KEYS[role];
|
|
111
|
-
return JSON.parse(localStorage.getItem(storageKey) ?? '{}');
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Set the local storage item for a specific role (chat or completer).
|
|
116
|
-
* If the key is not provider (null) we assume the value should replace the whole
|
|
117
|
-
* local storage for this role.
|
|
118
|
-
*/
|
|
119
|
-
setLocalStorageItem = (
|
|
120
|
-
role: ModelRole,
|
|
121
|
-
key: string | null,
|
|
122
|
-
value: any
|
|
123
|
-
): void => {
|
|
124
|
-
const storageKey = STORAGE_KEYS[role];
|
|
125
|
-
let settings: IDict<any>;
|
|
126
|
-
|
|
127
|
-
if (key !== null) {
|
|
128
|
-
settings = JSON.parse(localStorage.getItem(storageKey) ?? '{}');
|
|
129
|
-
settings[key] = value;
|
|
130
|
-
} else {
|
|
131
|
-
settings = value;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
localStorage.setItem(storageKey, JSON.stringify(settings));
|
|
135
|
-
|
|
136
|
-
// If both chat and completer use the same settings, only the chat settings should
|
|
137
|
-
// be editable for user, so we should duplicate its values to the completer
|
|
138
|
-
// local storage.
|
|
139
|
-
if (this.state.uniqueProvider && role === 'chat') {
|
|
140
|
-
const storageKeyCompleter = STORAGE_KEYS['completer'];
|
|
141
|
-
localStorage.setItem(storageKeyCompleter, JSON.stringify(settings));
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Get the settings from the registry (jupyterlab settings system) for a given role.
|
|
147
|
-
*/
|
|
148
|
-
getSettingsFromRegistry = (role: ModelRole): IDict<any> => {
|
|
149
|
-
const settings = this._settings.get('AIproviders')
|
|
150
|
-
.composite as ReadonlyPartialJSONObject;
|
|
151
|
-
return settings && Object.keys(settings).includes(role)
|
|
152
|
-
? (settings[role] as IDict<any>)
|
|
153
|
-
: { provider: 'None' };
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Save the settings to the setting registry.
|
|
158
|
-
*/
|
|
159
|
-
saveSettingsToRegistry = (role: ModelRole, settings: IDict<any>): void => {
|
|
160
|
-
const fullSettings = this._settings.get('AIproviders')
|
|
161
|
-
.composite as IDict<any>;
|
|
162
|
-
fullSettings[role] = { ...settings };
|
|
163
|
-
|
|
164
|
-
// If both chat and completer use the same settings, only the chat settings should
|
|
165
|
-
// be editable for user, so we should duplicate its values to the completer
|
|
166
|
-
// settings.
|
|
167
|
-
if (this.state.uniqueProvider && role === 'chat') {
|
|
168
|
-
fullSettings['completer'] = { ...settings };
|
|
169
|
-
}
|
|
170
|
-
this._settings.set('AIproviders', { ...fullSettings }).catch(console.error);
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
render(): JSX.Element {
|
|
174
|
-
return (
|
|
175
|
-
<div>
|
|
176
|
-
<h3>
|
|
177
|
-
{this.state.uniqueProvider
|
|
178
|
-
? 'Chat and completer provider'
|
|
179
|
-
: 'Chat provider'}
|
|
180
|
-
</h3>
|
|
181
|
-
<AiProviderSettings {...this.props} role={'chat'} aiSettings={this} />
|
|
182
|
-
{!this.state.uniqueProvider && (
|
|
183
|
-
<>
|
|
184
|
-
<h3>Completer provider</h3>
|
|
185
|
-
<AiProviderSettings
|
|
186
|
-
{...this.props}
|
|
187
|
-
role={'completer'}
|
|
188
|
-
aiSettings={this}
|
|
189
|
-
/>
|
|
190
|
-
</>
|
|
191
|
-
)}
|
|
192
|
-
</div>
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
private _settings: ISettingRegistry.ISettings;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* The AI settings component namespace.
|
|
201
|
-
*/
|
|
202
|
-
namespace AiSettings {
|
|
203
|
-
/**
|
|
204
|
-
* The AI settings component states.
|
|
205
|
-
*/
|
|
206
|
-
export type states = {
|
|
207
|
-
/**
|
|
208
|
-
* Whether there is only one provider for chat and completion.
|
|
209
|
-
*/
|
|
210
|
-
uniqueProvider: boolean;
|
|
211
|
-
};
|
|
212
|
-
/**
|
|
213
|
-
* The provider names object.
|
|
214
|
-
*/
|
|
215
|
-
export type providers = {
|
|
216
|
-
[key in ModelRole]: string;
|
|
217
|
-
};
|
|
218
|
-
/**
|
|
219
|
-
* The provider schemas object.
|
|
220
|
-
*/
|
|
221
|
-
export type schemas = {
|
|
222
|
-
[key in ModelRole]: JSONSchema7;
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
export class AiProviderSettings extends React.Component<
|
|
227
|
-
AiProviderSettings.props,
|
|
228
|
-
AiProviderSettings.states
|
|
229
|
-
> {
|
|
230
|
-
constructor(props: AiProviderSettings.props) {
|
|
231
|
-
super(props);
|
|
232
|
-
if (!props.formContext.providerRegistry) {
|
|
233
|
-
throw new Error(
|
|
234
|
-
'The provider registry is needed to enable the jupyterlite-ai settings panel'
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
this._role = props.role;
|
|
238
|
-
this._providerRegistry = props.formContext.providerRegistry;
|
|
239
|
-
this._rmRegistry = props.formContext.rmRegistry ?? null;
|
|
240
|
-
this._secretsManager = props.formContext.secretsManager ?? null;
|
|
241
|
-
this._settings = props.formContext.settings;
|
|
242
|
-
|
|
243
|
-
const useSecretsManagerSetting =
|
|
244
|
-
(this._settings.get('UseSecretsManager').composite as boolean) ?? true;
|
|
245
|
-
this._useSecretsManager =
|
|
246
|
-
useSecretsManagerSetting && this._secretsManager !== null;
|
|
247
|
-
|
|
248
|
-
// Initialize the providers schema.
|
|
249
|
-
const providerSchema = JSONExt.deepCopy(baseSettings) as any;
|
|
250
|
-
providerSchema.properties.provider = {
|
|
251
|
-
type: 'string',
|
|
252
|
-
title: 'Provider',
|
|
253
|
-
description: 'The AI provider to use for chat and completion',
|
|
254
|
-
default: 'None',
|
|
255
|
-
enum: ['None'].concat(this._providerRegistry.providers)
|
|
256
|
-
};
|
|
257
|
-
this._providerSchema = providerSchema as JSONSchema7;
|
|
258
|
-
|
|
259
|
-
// Check if there is saved values in local storage, otherwise use the settings from
|
|
260
|
-
// the setting registry (leads to default if there are no user settings).
|
|
261
|
-
const storageKey = STORAGE_KEYS[this._role];
|
|
262
|
-
const storageSettings = localStorage.getItem(storageKey);
|
|
263
|
-
if (storageSettings === null) {
|
|
264
|
-
const labSettings = this.props.aiSettings.getSettingsFromRegistry(
|
|
265
|
-
this._role
|
|
266
|
-
);
|
|
267
|
-
if (Object.keys(labSettings).includes('provider')) {
|
|
268
|
-
// Get the provider name.
|
|
269
|
-
const provider = Object.entries(labSettings).find(
|
|
270
|
-
v => v[0] === 'provider'
|
|
271
|
-
)?.[1] as string;
|
|
272
|
-
// Save the settings.
|
|
273
|
-
const settings: any = {
|
|
274
|
-
_current: provider
|
|
275
|
-
};
|
|
276
|
-
settings[provider] = labSettings;
|
|
277
|
-
this.props.aiSettings.setLocalStorageItem(this._role, null, settings);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Initialize the settings from the saved ones.
|
|
282
|
-
this._provider = this.getCurrentProvider();
|
|
283
|
-
|
|
284
|
-
// Initialize the schema.
|
|
285
|
-
const schema = this._buildSchema();
|
|
286
|
-
|
|
287
|
-
// Initialize the current settings.
|
|
288
|
-
const isModified = this._updatedFormData(
|
|
289
|
-
this.getSettingsFromLocalStorage()
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
this.state = {
|
|
293
|
-
schema,
|
|
294
|
-
instruction: null,
|
|
295
|
-
compatibilityError: null,
|
|
296
|
-
isModified: isModified
|
|
297
|
-
};
|
|
298
|
-
this._renderInstruction();
|
|
299
|
-
|
|
300
|
-
this._checkProviderCompatibility();
|
|
301
|
-
|
|
302
|
-
// Update the setting registry.
|
|
303
|
-
this.saveSettingsToRegistry();
|
|
304
|
-
|
|
305
|
-
this._secretsManager?.fieldVisibilityChanged.connect(
|
|
306
|
-
this._fieldVisibilityChanged
|
|
307
|
-
);
|
|
308
|
-
|
|
309
|
-
this._settings.changed.connect(this._settingsChanged);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
componentDidMount(): void {
|
|
313
|
-
this.componentDidUpdate();
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
async componentDidUpdate(): Promise<void> {
|
|
317
|
-
if (!this._secretsManager || !this._useSecretsManager) {
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// Attach the password inputs to the secrets manager.
|
|
322
|
-
await this._secretsManager.detachAll(Private.getToken(), SECRETS_NAMESPACE);
|
|
323
|
-
const inputs = this._formRef.current?.getElementsByTagName('input') || [];
|
|
324
|
-
for (let i = 0; i < inputs.length; i++) {
|
|
325
|
-
if (inputs[i].type.toLowerCase() === 'password') {
|
|
326
|
-
const label = inputs[i].getAttribute('label');
|
|
327
|
-
if (label) {
|
|
328
|
-
const id = getSecretId(this._provider, label);
|
|
329
|
-
this._secretsManager.attach(
|
|
330
|
-
Private.getToken(),
|
|
331
|
-
SECRETS_NAMESPACE,
|
|
332
|
-
id,
|
|
333
|
-
inputs[i],
|
|
334
|
-
(value: string) => this._onPasswordUpdated(label, value)
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
componentWillUnmount(): void {
|
|
342
|
-
this._settings.changed.disconnect(this._settingsChanged);
|
|
343
|
-
this._secretsManager?.fieldVisibilityChanged.disconnect(
|
|
344
|
-
this._fieldVisibilityChanged
|
|
345
|
-
);
|
|
346
|
-
if (!this._secretsManager || !this._useSecretsManager) {
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
this._secretsManager.detachAll(Private.getToken(), SECRETS_NAMESPACE);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Get the current provider from the local storage.
|
|
354
|
-
*/
|
|
355
|
-
getCurrentProvider(): string {
|
|
356
|
-
const settings = this.props.aiSettings.getLocalStorage(this._role);
|
|
357
|
-
return settings['_current'] ?? 'None';
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Save the current provider to the local storage.
|
|
362
|
-
*/
|
|
363
|
-
saveCurrentProvider(): void {
|
|
364
|
-
this.props.aiSettings.setLocalStorageItem(
|
|
365
|
-
this._role,
|
|
366
|
-
'_current',
|
|
367
|
-
this._provider
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Get settings from local storage for the current provider provider.
|
|
373
|
-
*/
|
|
374
|
-
getSettingsFromLocalStorage(): IDict<any> {
|
|
375
|
-
const settings = this.props.aiSettings.getLocalStorage(this._role);
|
|
376
|
-
return settings[this._provider] ?? { provider: this._provider };
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Save settings in local storage for a given provider.
|
|
381
|
-
*/
|
|
382
|
-
saveSettingsToLocalStorage() {
|
|
383
|
-
const currentSettings = { ...this._currentSettings };
|
|
384
|
-
// Do not save secrets in local storage if using the secrets manager.
|
|
385
|
-
if (this._useSecretsManager) {
|
|
386
|
-
this._secretFields.forEach(field => delete currentSettings[field]);
|
|
387
|
-
}
|
|
388
|
-
this.props.aiSettings.setLocalStorageItem(
|
|
389
|
-
this._role,
|
|
390
|
-
this._provider,
|
|
391
|
-
currentSettings
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Save the settings to the setting registry.
|
|
397
|
-
*/
|
|
398
|
-
saveSettingsToRegistry(): void {
|
|
399
|
-
const sanitizedSettings = { ...this._currentSettings };
|
|
400
|
-
if (this._useSecretsManager) {
|
|
401
|
-
this._secretFields.forEach(field => {
|
|
402
|
-
sanitizedSettings[field] = SECRETS_REPLACEMENT;
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
this.props.aiSettings.saveSettingsToRegistry(this._role, {
|
|
407
|
-
provider: this._provider,
|
|
408
|
-
...sanitizedSettings
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Triggered when the settings has changed.
|
|
414
|
-
*/
|
|
415
|
-
private _settingsChanged = (settings: ISettingRegistry.ISettings) => {
|
|
416
|
-
this._updateUseSecretsManager(
|
|
417
|
-
(this._settings.get('UseSecretsManager').composite as boolean) ?? true
|
|
418
|
-
);
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Triggered when the secret fields visibility has changed.
|
|
423
|
-
*/
|
|
424
|
-
private _fieldVisibilityChanged = (
|
|
425
|
-
_: ISecretsManager,
|
|
426
|
-
value: boolean
|
|
427
|
-
): void => {
|
|
428
|
-
if (this._useSecretsManager) {
|
|
429
|
-
this._updateSchema();
|
|
430
|
-
}
|
|
431
|
-
};
|
|
432
|
-
|
|
433
|
-
/**
|
|
434
|
-
* Update the settings whether the secrets manager is used or not.
|
|
435
|
-
*
|
|
436
|
-
* @param value - whether to use the secrets manager or not.
|
|
437
|
-
*/
|
|
438
|
-
private _updateUseSecretsManager = (value: boolean) => {
|
|
439
|
-
// No-op if the value did not change or the secrets manager has not been provided.
|
|
440
|
-
if (value === this._useSecretsManager || this._secretsManager === null) {
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// Update the secrets manager.
|
|
445
|
-
this._useSecretsManager = value;
|
|
446
|
-
if (!value) {
|
|
447
|
-
// Detach all the password inputs attached to the secrets manager, and save the
|
|
448
|
-
// current settings to the local storage to save the password.
|
|
449
|
-
this._secretsManager.detachAll(Private.getToken(), SECRETS_NAMESPACE);
|
|
450
|
-
} else {
|
|
451
|
-
// Remove all the keys stored locally.
|
|
452
|
-
const settings = this.props.aiSettings.getLocalStorage(this._role);
|
|
453
|
-
Object.keys(settings).forEach(provider => {
|
|
454
|
-
Object.keys(settings[provider])
|
|
455
|
-
.filter(key => key.toLowerCase().includes('key'))
|
|
456
|
-
.forEach(key => {
|
|
457
|
-
delete settings[provider][key];
|
|
458
|
-
});
|
|
459
|
-
});
|
|
460
|
-
this.props.aiSettings.setLocalStorageItem(this._role, null, settings);
|
|
461
|
-
}
|
|
462
|
-
this._updateSchema();
|
|
463
|
-
this.saveSettingsToLocalStorage();
|
|
464
|
-
this.saveSettingsToRegistry();
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Build the schema for a given provider.
|
|
469
|
-
*/
|
|
470
|
-
private _buildSchema(): JSONSchema7 {
|
|
471
|
-
const schema = JSONExt.deepCopy(baseSettings) as any;
|
|
472
|
-
this._uiSchema = {};
|
|
473
|
-
const settingsSchema = this._providerRegistry.getSettingsSchema(
|
|
474
|
-
this._provider
|
|
475
|
-
);
|
|
476
|
-
|
|
477
|
-
this._secretFields = [];
|
|
478
|
-
this._defaultFormData = {};
|
|
479
|
-
if (settingsSchema) {
|
|
480
|
-
Object.entries(settingsSchema).forEach(([key, value]) => {
|
|
481
|
-
if (key.toLowerCase().includes('key')) {
|
|
482
|
-
this._secretFields.push(key);
|
|
483
|
-
|
|
484
|
-
// If the secrets manager is not used, show the secrets fields.
|
|
485
|
-
// If the secrets manager is used, check if the fields should be visible.
|
|
486
|
-
const showSecretFields =
|
|
487
|
-
!this._useSecretsManager ||
|
|
488
|
-
(this._secretsManager?.secretFieldsVisibility ?? true);
|
|
489
|
-
if (!showSecretFields) {
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
this._uiSchema[key] = { 'ui:widget': 'password' };
|
|
494
|
-
}
|
|
495
|
-
schema.properties[key] = value;
|
|
496
|
-
if (value.default !== undefined) {
|
|
497
|
-
this._defaultFormData[key] = value.default;
|
|
498
|
-
}
|
|
499
|
-
});
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
return schema as JSONSchema7;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
/**
|
|
506
|
-
* Update the schema state for the given provider, that trigger the re-rendering of
|
|
507
|
-
* the component.
|
|
508
|
-
*/
|
|
509
|
-
private _updateSchema() {
|
|
510
|
-
const schema = this._buildSchema();
|
|
511
|
-
this.setState({ schema });
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
/**
|
|
515
|
-
* Render the markdown instructions for the current provider.
|
|
516
|
-
*/
|
|
517
|
-
private async _renderInstruction(): Promise<void> {
|
|
518
|
-
let instructions = this._providerRegistry.getInstructions(this._provider);
|
|
519
|
-
if (!this._rmRegistry || !instructions) {
|
|
520
|
-
this.setState({ instruction: null });
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
instructions = `---\n\n${instructions}\n\n---`;
|
|
524
|
-
const renderer = this._rmRegistry.createRenderer(MD_MIME_TYPE);
|
|
525
|
-
const model = this._rmRegistry.createModel({
|
|
526
|
-
data: { [MD_MIME_TYPE]: instructions }
|
|
527
|
-
});
|
|
528
|
-
await renderer.renderModel(model);
|
|
529
|
-
this.setState({ instruction: renderer.node });
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Check for compatibility of the provider with the current environment.
|
|
534
|
-
* If the provider is not compatible, display an error message.
|
|
535
|
-
*/
|
|
536
|
-
private async _checkProviderCompatibility(): Promise<void> {
|
|
537
|
-
const compatibilityCheck = this._providerRegistry.getCompatibilityCheck(
|
|
538
|
-
this._provider
|
|
539
|
-
);
|
|
540
|
-
if (!compatibilityCheck) {
|
|
541
|
-
this.setState({ compatibilityError: null });
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
const error = await compatibilityCheck();
|
|
545
|
-
if (!error) {
|
|
546
|
-
this.setState({ compatibilityError: null });
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
const errorDiv = document.createElement('div');
|
|
550
|
-
errorDiv.className = ERROR_CLASS;
|
|
551
|
-
errorDiv.innerHTML = error;
|
|
552
|
-
this.setState({ compatibilityError: error });
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
/**
|
|
556
|
-
* Triggered when the provider has changed, to update the schema and values.
|
|
557
|
-
* Update the Jupyterlab settings accordingly.
|
|
558
|
-
*/
|
|
559
|
-
private _onProviderChanged = (e: IChangeEvent) => {
|
|
560
|
-
const provider = e.formData.provider;
|
|
561
|
-
if (provider === this._currentSettings.provider) {
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
this._provider = provider;
|
|
565
|
-
this.saveCurrentProvider();
|
|
566
|
-
this._updateSchema();
|
|
567
|
-
this._renderInstruction();
|
|
568
|
-
this._checkProviderCompatibility();
|
|
569
|
-
|
|
570
|
-
// Initialize the current settings.
|
|
571
|
-
const isModified = this._updatedFormData(
|
|
572
|
-
this.getSettingsFromLocalStorage()
|
|
573
|
-
);
|
|
574
|
-
if (isModified !== this.state.isModified) {
|
|
575
|
-
this.setState({ isModified });
|
|
576
|
-
}
|
|
577
|
-
this.saveSettingsToRegistry();
|
|
578
|
-
};
|
|
579
|
-
|
|
580
|
-
/**
|
|
581
|
-
* Callback function called when the password input has been programmatically updated
|
|
582
|
-
* with the secret manager.
|
|
583
|
-
*/
|
|
584
|
-
private _onPasswordUpdated = (fieldName: string, value: string) => {
|
|
585
|
-
this._currentSettings[fieldName] = value;
|
|
586
|
-
this.saveSettingsToRegistry();
|
|
587
|
-
};
|
|
588
|
-
|
|
589
|
-
/**
|
|
590
|
-
* Update the current settings with the new values from the form.
|
|
591
|
-
*
|
|
592
|
-
* @param data - The form data to update.
|
|
593
|
-
* @returns - Boolean whether the form is not the default one.
|
|
594
|
-
*/
|
|
595
|
-
private _updatedFormData(data: IDict): boolean {
|
|
596
|
-
let isModified = false;
|
|
597
|
-
Object.entries(data).forEach(([key, value]) => {
|
|
598
|
-
if (this._defaultFormData[key] !== undefined) {
|
|
599
|
-
if (value === undefined) {
|
|
600
|
-
const schemaProperty = this.state.schema.properties?.[
|
|
601
|
-
key
|
|
602
|
-
] as JSONSchema7;
|
|
603
|
-
if (schemaProperty.type === 'string') {
|
|
604
|
-
data[key] = '';
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
if (value !== this._defaultFormData[key]) {
|
|
608
|
-
isModified = true;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
});
|
|
612
|
-
this._currentSettings = JSONExt.deepCopy(data);
|
|
613
|
-
return isModified;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
/**
|
|
617
|
-
* Triggered when the form value has changed, to update the current settings and save
|
|
618
|
-
* it in local storage.
|
|
619
|
-
* Update the Jupyterlab settings accordingly.
|
|
620
|
-
*/
|
|
621
|
-
private _onFormChanged = (e: IChangeEvent): void => {
|
|
622
|
-
const { formData } = e;
|
|
623
|
-
const isModified = this._updatedFormData(formData);
|
|
624
|
-
this.saveSettingsToLocalStorage();
|
|
625
|
-
this.saveSettingsToRegistry();
|
|
626
|
-
if (isModified !== this.state.isModified) {
|
|
627
|
-
this.setState({ isModified });
|
|
628
|
-
}
|
|
629
|
-
};
|
|
630
|
-
|
|
631
|
-
/**
|
|
632
|
-
* Handler for the "Restore to defaults" button - clears all
|
|
633
|
-
* modified settings then calls `setFormData` to restore the
|
|
634
|
-
* values.
|
|
635
|
-
*/
|
|
636
|
-
private _reset = async (event: React.MouseEvent): Promise<void> => {
|
|
637
|
-
event.stopPropagation();
|
|
638
|
-
this._currentSettings = {
|
|
639
|
-
...this._currentSettings,
|
|
640
|
-
...this._defaultFormData
|
|
641
|
-
};
|
|
642
|
-
this.saveSettingsToLocalStorage();
|
|
643
|
-
this.saveSettingsToRegistry();
|
|
644
|
-
this.setState({ isModified: false });
|
|
645
|
-
};
|
|
646
|
-
|
|
647
|
-
render(): JSX.Element {
|
|
648
|
-
return (
|
|
649
|
-
<div ref={this._formRef}>
|
|
650
|
-
<WrappedFormComponent
|
|
651
|
-
formData={{ provider: this._provider }}
|
|
652
|
-
schema={this._providerSchema}
|
|
653
|
-
onChange={this._onProviderChanged}
|
|
654
|
-
idPrefix={`jp-SettingsEditor-${PLUGIN_IDS.providerRegistry}-${this._role}`}
|
|
655
|
-
/>
|
|
656
|
-
{this.state.compatibilityError !== null && (
|
|
657
|
-
<div className={ERROR_CLASS}>
|
|
658
|
-
<i className={'fas fa-exclamation-triangle'}></i>
|
|
659
|
-
<span>{this.state.compatibilityError}</span>
|
|
660
|
-
</div>
|
|
661
|
-
)}
|
|
662
|
-
{this.state.instruction !== null && (
|
|
663
|
-
<details>
|
|
664
|
-
<summary className={INSTRUCTION_CLASS}>Instructions</summary>
|
|
665
|
-
<span
|
|
666
|
-
ref={node =>
|
|
667
|
-
node && node.replaceChildren(this.state.instruction!)
|
|
668
|
-
}
|
|
669
|
-
/>
|
|
670
|
-
</details>
|
|
671
|
-
)}
|
|
672
|
-
<div className="jp-SettingsHeader">
|
|
673
|
-
<h3 title={this._provider}>{this._provider}</h3>
|
|
674
|
-
<div className="jp-SettingsHeader-buttonbar">
|
|
675
|
-
{this.state.isModified && (
|
|
676
|
-
<Button className="jp-RestoreButton" onClick={this._reset}>
|
|
677
|
-
Restore to Defaults
|
|
678
|
-
</Button>
|
|
679
|
-
)}
|
|
680
|
-
</div>
|
|
681
|
-
</div>
|
|
682
|
-
<WrappedFormComponent
|
|
683
|
-
formData={this._currentSettings}
|
|
684
|
-
schema={this.state.schema}
|
|
685
|
-
onChange={this._onFormChanged}
|
|
686
|
-
uiSchema={this._uiSchema}
|
|
687
|
-
idPrefix={`jp-SettingsEditor-${PLUGIN_IDS.providerRegistry}-${this._role}`}
|
|
688
|
-
formContext={{
|
|
689
|
-
...this.props.formContext,
|
|
690
|
-
defaultFormData: this._defaultFormData
|
|
691
|
-
}}
|
|
692
|
-
/>
|
|
693
|
-
</div>
|
|
694
|
-
);
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
private _role: ModelRole;
|
|
698
|
-
private _providerRegistry: IAIProviderRegistry;
|
|
699
|
-
private _provider: string;
|
|
700
|
-
private _providerSchema: JSONSchema7;
|
|
701
|
-
private _useSecretsManager: boolean;
|
|
702
|
-
private _rmRegistry: IRenderMimeRegistry | null;
|
|
703
|
-
private _secretsManager: ISecretsManager | null;
|
|
704
|
-
private _currentSettings: IDict<any> = { provider: 'None' };
|
|
705
|
-
private _uiSchema: IDict<any> = {};
|
|
706
|
-
private _settings: ISettingRegistry.ISettings;
|
|
707
|
-
private _formRef = React.createRef<HTMLDivElement>();
|
|
708
|
-
private _secretFields: string[] = [];
|
|
709
|
-
private _defaultFormData: IDict<any> = {};
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
/**
|
|
713
|
-
* The AI provider settings component namespace.
|
|
714
|
-
*/
|
|
715
|
-
export namespace AiProviderSettings {
|
|
716
|
-
/**
|
|
717
|
-
* The AI provider settings component props.
|
|
718
|
-
*/
|
|
719
|
-
export type props = FieldProps & {
|
|
720
|
-
/**
|
|
721
|
-
* Why this model is used for (chat or completion).
|
|
722
|
-
*/
|
|
723
|
-
role: ModelRole;
|
|
724
|
-
/**
|
|
725
|
-
* The parent component which should handle:
|
|
726
|
-
* - the get/set functions for local storage
|
|
727
|
-
* - save settings using jupyter settings system
|
|
728
|
-
*/
|
|
729
|
-
aiSettings: IAiSettings;
|
|
730
|
-
};
|
|
731
|
-
/**
|
|
732
|
-
* The AI provider settings component states.
|
|
733
|
-
*/
|
|
734
|
-
export type states = {
|
|
735
|
-
/**
|
|
736
|
-
* The schema of the settings.
|
|
737
|
-
*/
|
|
738
|
-
schema: JSONSchema7;
|
|
739
|
-
/**
|
|
740
|
-
* The instructions for this provider.
|
|
741
|
-
*/
|
|
742
|
-
instruction: HTMLElement | null;
|
|
743
|
-
/**
|
|
744
|
-
* An error if the model in not compatible with the current environment.
|
|
745
|
-
*/
|
|
746
|
-
compatibilityError: string | null;
|
|
747
|
-
/**
|
|
748
|
-
* Whether the settings are modified from default or not.
|
|
749
|
-
*/
|
|
750
|
-
isModified?: boolean;
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
namespace Private {
|
|
755
|
-
/**
|
|
756
|
-
* The token to use with the secrets manager.
|
|
757
|
-
*/
|
|
758
|
-
let secretsToken: symbol;
|
|
759
|
-
|
|
760
|
-
/**
|
|
761
|
-
* Set of the token.
|
|
762
|
-
*/
|
|
763
|
-
export function setToken(value: symbol): void {
|
|
764
|
-
secretsToken = value;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
/**
|
|
768
|
-
* get the token.
|
|
769
|
-
*/
|
|
770
|
-
export function getToken(): symbol {
|
|
771
|
-
return secretsToken;
|
|
772
|
-
}
|
|
773
|
-
}
|