@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
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
2
|
+
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
3
|
+
import { FormComponent, IFormRenderer } from '@jupyterlab/ui-components';
|
|
4
|
+
import { ArrayExt } from '@lumino/algorithm';
|
|
5
|
+
import { JSONExt } from '@lumino/coreutils';
|
|
6
|
+
import { IChangeEvent } from '@rjsf/core';
|
|
7
|
+
import type { FieldProps } from '@rjsf/utils';
|
|
8
|
+
import validator from '@rjsf/validator-ajv8';
|
|
9
|
+
import { JSONSchema7 } from 'json-schema';
|
|
10
|
+
import { ISecretsManager } from 'jupyter-secrets-manager';
|
|
11
|
+
import React from 'react';
|
|
12
|
+
|
|
13
|
+
import baseSettings from './base.json';
|
|
14
|
+
import { IAIProviderRegistry, IDict } from '../tokens';
|
|
15
|
+
|
|
16
|
+
const SECRETS_NAMESPACE = '@jupyterlite/ai';
|
|
17
|
+
const MD_MIME_TYPE = 'text/markdown';
|
|
18
|
+
const STORAGE_NAME = '@jupyterlite/ai:settings';
|
|
19
|
+
const INSTRUCTION_CLASS = 'jp-AISettingsInstructions';
|
|
20
|
+
|
|
21
|
+
export const aiSettingsRenderer = (options: {
|
|
22
|
+
providerRegistry: IAIProviderRegistry;
|
|
23
|
+
rmRegistry?: IRenderMimeRegistry;
|
|
24
|
+
secretsManager?: ISecretsManager;
|
|
25
|
+
}): IFormRenderer => {
|
|
26
|
+
return {
|
|
27
|
+
fieldRenderer: (props: FieldProps) => {
|
|
28
|
+
props.formContext = { ...props.formContext, ...options };
|
|
29
|
+
return <AiSettings {...props} />;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export interface ISettingsFormStates {
|
|
35
|
+
schema: JSONSchema7;
|
|
36
|
+
instruction: HTMLElement | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const WrappedFormComponent = (props: any): JSX.Element => {
|
|
40
|
+
return <FormComponent {...props} validator={validator} />;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export class AiSettings extends React.Component<
|
|
44
|
+
FieldProps,
|
|
45
|
+
ISettingsFormStates
|
|
46
|
+
> {
|
|
47
|
+
constructor(props: FieldProps) {
|
|
48
|
+
super(props);
|
|
49
|
+
if (!props.formContext.providerRegistry) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
'The provider registry is needed to enable the jupyterlite-ai settings panel'
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
this._providerRegistry = props.formContext.providerRegistry;
|
|
55
|
+
this._rmRegistry = props.formContext.rmRegistry ?? null;
|
|
56
|
+
this._secretsManager = props.formContext.secretsManager ?? null;
|
|
57
|
+
this._settings = props.formContext.settings;
|
|
58
|
+
|
|
59
|
+
this._useSecretsManager =
|
|
60
|
+
(this._settings.get('UseSecretsManager').composite as boolean) ?? true;
|
|
61
|
+
|
|
62
|
+
// Initialize the providers schema.
|
|
63
|
+
const providerSchema = JSONExt.deepCopy(baseSettings) as any;
|
|
64
|
+
providerSchema.properties.provider = {
|
|
65
|
+
type: 'string',
|
|
66
|
+
title: 'Provider',
|
|
67
|
+
description: 'The AI provider to use for chat and completion',
|
|
68
|
+
default: 'None',
|
|
69
|
+
enum: ['None'].concat(this._providerRegistry.providers)
|
|
70
|
+
};
|
|
71
|
+
this._providerSchema = providerSchema as JSONSchema7;
|
|
72
|
+
|
|
73
|
+
// Check if there is saved values in local storage, otherwise use the settings from
|
|
74
|
+
// the setting registry (led to default if there are no user settings).
|
|
75
|
+
const storageSettings = localStorage.getItem(STORAGE_NAME);
|
|
76
|
+
if (storageSettings === null) {
|
|
77
|
+
const labSettings = this._settings.get('AIprovider').composite;
|
|
78
|
+
if (labSettings && Object.keys(labSettings).includes('provider')) {
|
|
79
|
+
// Get the provider name.
|
|
80
|
+
const provider = Object.entries(labSettings).find(
|
|
81
|
+
v => v[0] === 'provider'
|
|
82
|
+
)?.[1] as string;
|
|
83
|
+
// Save the settings.
|
|
84
|
+
const settings: any = {
|
|
85
|
+
_current: provider
|
|
86
|
+
};
|
|
87
|
+
settings[provider] = labSettings;
|
|
88
|
+
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Initialize the settings from the saved ones.
|
|
93
|
+
this._provider = this.getCurrentProvider();
|
|
94
|
+
this._currentSettings = this.getSettings();
|
|
95
|
+
|
|
96
|
+
// Initialize the schema.
|
|
97
|
+
const schema = this._buildSchema();
|
|
98
|
+
this.state = { schema, instruction: null };
|
|
99
|
+
|
|
100
|
+
this._renderInstruction();
|
|
101
|
+
|
|
102
|
+
// Update the setting registry.
|
|
103
|
+
this._settings
|
|
104
|
+
.set('AIprovider', this._currentSettings)
|
|
105
|
+
.catch(console.error);
|
|
106
|
+
|
|
107
|
+
this._settings.changed.connect(() => {
|
|
108
|
+
const useSecretsManager =
|
|
109
|
+
(this._settings.get('UseSecretsManager').composite as boolean) ?? true;
|
|
110
|
+
if (useSecretsManager !== this._useSecretsManager) {
|
|
111
|
+
this.updateUseSecretsManager(useSecretsManager);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async componentDidUpdate(): Promise<void> {
|
|
117
|
+
if (!this._secretsManager || !this._useSecretsManager) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// Attach the password inputs to the secrets manager only if they have changed.
|
|
121
|
+
const inputs = this._formRef.current?.getElementsByTagName('input') || [];
|
|
122
|
+
if (ArrayExt.shallowEqual(inputs, this._formInputs)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
await this._secretsManager.detachAll(SECRETS_NAMESPACE);
|
|
127
|
+
this._formInputs = [...inputs];
|
|
128
|
+
this._unsavedFields = [];
|
|
129
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
130
|
+
if (inputs[i].type.toLowerCase() === 'password') {
|
|
131
|
+
const label = inputs[i].getAttribute('label');
|
|
132
|
+
if (label) {
|
|
133
|
+
const id = `${this._provider}-${label}`;
|
|
134
|
+
this._secretsManager.attach(
|
|
135
|
+
SECRETS_NAMESPACE,
|
|
136
|
+
id,
|
|
137
|
+
inputs[i],
|
|
138
|
+
(value: string) => this._onPasswordUpdated(label, value)
|
|
139
|
+
);
|
|
140
|
+
this._unsavedFields.push(label);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get the current provider from the local storage.
|
|
148
|
+
*/
|
|
149
|
+
getCurrentProvider(): string {
|
|
150
|
+
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
|
|
151
|
+
return settings['_current'] ?? 'None';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Save the current provider to the local storage.
|
|
156
|
+
*/
|
|
157
|
+
saveCurrentProvider(): void {
|
|
158
|
+
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
|
|
159
|
+
settings['_current'] = this._provider;
|
|
160
|
+
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get settings from local storage for a given provider.
|
|
165
|
+
*/
|
|
166
|
+
getSettings(): IDict<any> {
|
|
167
|
+
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
|
|
168
|
+
return settings[this._provider] ?? { provider: this._provider };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Save settings in local storage for a given provider.
|
|
173
|
+
*/
|
|
174
|
+
saveSettings(value: IDict<any>) {
|
|
175
|
+
const currentSettings = { ...value };
|
|
176
|
+
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) ?? '{}');
|
|
177
|
+
this._unsavedFields.forEach(field => delete currentSettings[field]);
|
|
178
|
+
settings[this._provider] = currentSettings;
|
|
179
|
+
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private updateUseSecretsManager = (value: boolean) => {
|
|
183
|
+
this._useSecretsManager = value;
|
|
184
|
+
if (!value) {
|
|
185
|
+
// Detach all the password inputs attached to the secrets manager, and save the
|
|
186
|
+
// current settings to the local storage to save the password.
|
|
187
|
+
this._secretsManager?.detachAll(SECRETS_NAMESPACE);
|
|
188
|
+
this._formInputs = [];
|
|
189
|
+
this._unsavedFields = [];
|
|
190
|
+
this.saveSettings(this._currentSettings);
|
|
191
|
+
} else {
|
|
192
|
+
// Remove all the keys stored locally and attach the password inputs to the
|
|
193
|
+
// secrets manager.
|
|
194
|
+
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
|
|
195
|
+
Object.keys(settings).forEach(provider => {
|
|
196
|
+
Object.keys(settings[provider])
|
|
197
|
+
.filter(key => key.toLowerCase().includes('key'))
|
|
198
|
+
.forEach(key => {
|
|
199
|
+
delete settings[provider][key];
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
|
|
203
|
+
this.componentDidUpdate();
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Update the UI schema of the form.
|
|
209
|
+
* Currently use to hide API keys.
|
|
210
|
+
*/
|
|
211
|
+
private _updateUiSchema(key: string) {
|
|
212
|
+
if (key.toLowerCase().includes('key')) {
|
|
213
|
+
this._uiSchema[key] = { 'ui:widget': 'password' };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Build the schema for a given provider.
|
|
219
|
+
*/
|
|
220
|
+
private _buildSchema(): JSONSchema7 {
|
|
221
|
+
const schema = JSONExt.deepCopy(baseSettings) as any;
|
|
222
|
+
this._uiSchema = {};
|
|
223
|
+
const settingsSchema = this._providerRegistry.getSettingsSchema(
|
|
224
|
+
this._provider
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
if (settingsSchema) {
|
|
228
|
+
Object.entries(settingsSchema).forEach(([key, value]) => {
|
|
229
|
+
schema.properties[key] = value;
|
|
230
|
+
this._updateUiSchema(key);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
return schema as JSONSchema7;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Update the schema state for the given provider, that trigger the re-rendering of
|
|
238
|
+
* the component.
|
|
239
|
+
*/
|
|
240
|
+
private _updateSchema() {
|
|
241
|
+
const schema = this._buildSchema();
|
|
242
|
+
this.setState({ schema });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Render the markdown instructions for the current provider.
|
|
247
|
+
*/
|
|
248
|
+
private async _renderInstruction(): Promise<void> {
|
|
249
|
+
let instructions = this._providerRegistry.getInstructions(this._provider);
|
|
250
|
+
if (!this._rmRegistry || !instructions) {
|
|
251
|
+
this.setState({ instruction: null });
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
instructions = `---\n\n${instructions}\n\n---`;
|
|
255
|
+
const renderer = this._rmRegistry.createRenderer(MD_MIME_TYPE);
|
|
256
|
+
const model = this._rmRegistry.createModel({
|
|
257
|
+
data: { [MD_MIME_TYPE]: instructions }
|
|
258
|
+
});
|
|
259
|
+
await renderer.renderModel(model);
|
|
260
|
+
this.setState({ instruction: renderer.node });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Triggered when the provider hes changed, to update the schema and values.
|
|
265
|
+
* Update the Jupyterlab settings accordingly.
|
|
266
|
+
*/
|
|
267
|
+
private _onProviderChanged = (e: IChangeEvent) => {
|
|
268
|
+
const provider = e.formData.provider;
|
|
269
|
+
if (provider === this._currentSettings.provider) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
this._provider = provider;
|
|
273
|
+
this.saveCurrentProvider();
|
|
274
|
+
this._currentSettings = this.getSettings();
|
|
275
|
+
this._updateSchema();
|
|
276
|
+
this._renderInstruction();
|
|
277
|
+
this._settings
|
|
278
|
+
.set('AIprovider', { provider: this._provider, ...this._currentSettings })
|
|
279
|
+
.catch(console.error);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Callback function called when the password input has been programmatically updated
|
|
284
|
+
* with the secret manager.
|
|
285
|
+
*/
|
|
286
|
+
private _onPasswordUpdated = (fieldName: string, value: string) => {
|
|
287
|
+
this._currentSettings[fieldName] = value;
|
|
288
|
+
this._settings
|
|
289
|
+
.set('AIprovider', { provider: this._provider, ...this._currentSettings })
|
|
290
|
+
.catch(console.error);
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Triggered when the form value has changed, to update the current settings and save
|
|
295
|
+
* it in local storage.
|
|
296
|
+
* Update the Jupyterlab settings accordingly.
|
|
297
|
+
*/
|
|
298
|
+
private _onFormChange = (e: IChangeEvent) => {
|
|
299
|
+
this._currentSettings = JSONExt.deepCopy(e.formData);
|
|
300
|
+
this.saveSettings(this._currentSettings);
|
|
301
|
+
this._settings
|
|
302
|
+
.set('AIprovider', { provider: this._provider, ...this._currentSettings })
|
|
303
|
+
.catch(console.error);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
render(): JSX.Element {
|
|
307
|
+
return (
|
|
308
|
+
<div ref={this._formRef}>
|
|
309
|
+
<WrappedFormComponent
|
|
310
|
+
formData={{ provider: this._provider }}
|
|
311
|
+
schema={this._providerSchema}
|
|
312
|
+
onChange={this._onProviderChanged}
|
|
313
|
+
/>
|
|
314
|
+
{this.state.instruction !== null && (
|
|
315
|
+
<details>
|
|
316
|
+
<summary className={INSTRUCTION_CLASS}>Instructions</summary>
|
|
317
|
+
<span
|
|
318
|
+
ref={node =>
|
|
319
|
+
node && node.replaceChildren(this.state.instruction!)
|
|
320
|
+
}
|
|
321
|
+
/>
|
|
322
|
+
</details>
|
|
323
|
+
)}
|
|
324
|
+
<WrappedFormComponent
|
|
325
|
+
formData={this._currentSettings}
|
|
326
|
+
schema={this.state.schema}
|
|
327
|
+
onChange={this._onFormChange}
|
|
328
|
+
uiSchema={this._uiSchema}
|
|
329
|
+
/>
|
|
330
|
+
</div>
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private _providerRegistry: IAIProviderRegistry;
|
|
335
|
+
private _provider: string;
|
|
336
|
+
private _providerSchema: JSONSchema7;
|
|
337
|
+
private _useSecretsManager: boolean;
|
|
338
|
+
private _rmRegistry: IRenderMimeRegistry | null;
|
|
339
|
+
private _secretsManager: ISecretsManager | null;
|
|
340
|
+
private _currentSettings: IDict<any> = { provider: 'None' };
|
|
341
|
+
private _uiSchema: IDict<any> = {};
|
|
342
|
+
private _settings: ISettingRegistry.ISettings;
|
|
343
|
+
private _formRef = React.createRef<HTMLDivElement>();
|
|
344
|
+
private _unsavedFields: string[] = [];
|
|
345
|
+
private _formInputs: HTMLInputElement[] = [];
|
|
346
|
+
}
|
package/src/tokens.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
2
|
+
import { ReadonlyPartialJSONObject, Token } from '@lumino/coreutils';
|
|
3
|
+
import { ISignal } from '@lumino/signaling';
|
|
4
|
+
import { JSONSchema7 } from 'json-schema';
|
|
5
|
+
|
|
6
|
+
import { IBaseCompleter } from './base-completer';
|
|
7
|
+
|
|
8
|
+
export interface IDict<T = any> {
|
|
9
|
+
[key: string]: T;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface IType<T> {
|
|
13
|
+
new (...args: any[]): T;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The provider interface.
|
|
18
|
+
*/
|
|
19
|
+
export interface IAIProvider {
|
|
20
|
+
/**
|
|
21
|
+
* The name of the provider.
|
|
22
|
+
*/
|
|
23
|
+
name: string;
|
|
24
|
+
/**
|
|
25
|
+
* The chat model class to use.
|
|
26
|
+
*/
|
|
27
|
+
chatModel?: IType<BaseChatModel>;
|
|
28
|
+
/**
|
|
29
|
+
* The completer class to use.
|
|
30
|
+
*/
|
|
31
|
+
completer?: IType<IBaseCompleter>;
|
|
32
|
+
/**
|
|
33
|
+
* the settings schema for the provider.
|
|
34
|
+
*/
|
|
35
|
+
settingsSchema?: any;
|
|
36
|
+
/**
|
|
37
|
+
* The instructions to be displayed in the settings, as helper to use the provider.
|
|
38
|
+
* A markdown renderer is used to render the instructions.
|
|
39
|
+
*/
|
|
40
|
+
instructions?: string;
|
|
41
|
+
/**
|
|
42
|
+
* A function that extract the error message from the provider API error.
|
|
43
|
+
* Default to `(error) => error.message`.
|
|
44
|
+
*/
|
|
45
|
+
errorMessage?: (error: any) => string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The provider registry interface.
|
|
50
|
+
*/
|
|
51
|
+
export interface IAIProviderRegistry {
|
|
52
|
+
/**
|
|
53
|
+
* Get the list of provider names.
|
|
54
|
+
*/
|
|
55
|
+
readonly providers: string[];
|
|
56
|
+
/**
|
|
57
|
+
* Add a new provider.
|
|
58
|
+
*/
|
|
59
|
+
add(provider: IAIProvider): void;
|
|
60
|
+
/**
|
|
61
|
+
* Get the current provider name.
|
|
62
|
+
*/
|
|
63
|
+
currentName: string;
|
|
64
|
+
/**
|
|
65
|
+
* Get the current completer of the completion provider.
|
|
66
|
+
*/
|
|
67
|
+
currentCompleter: IBaseCompleter | null;
|
|
68
|
+
/**
|
|
69
|
+
* Get the current llm chat model.
|
|
70
|
+
*/
|
|
71
|
+
currentChatModel: BaseChatModel | null;
|
|
72
|
+
/**
|
|
73
|
+
* Get the settings schema of a given provider.
|
|
74
|
+
*/
|
|
75
|
+
getSettingsSchema(provider: string): JSONSchema7;
|
|
76
|
+
/**
|
|
77
|
+
* Get the instructions of a given provider.
|
|
78
|
+
*/
|
|
79
|
+
getInstructions(provider: string): string | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Format an error message from the current provider.
|
|
82
|
+
*/
|
|
83
|
+
formatErrorMessage(error: any): string;
|
|
84
|
+
/**
|
|
85
|
+
* Set the providers (chat model and completer).
|
|
86
|
+
* Creates the providers if the name has changed, otherwise only updates their config.
|
|
87
|
+
*
|
|
88
|
+
* @param name - the name of the provider to use.
|
|
89
|
+
* @param settings - the settings for the models.
|
|
90
|
+
*/
|
|
91
|
+
setProvider(name: string, settings: ReadonlyPartialJSONObject): void;
|
|
92
|
+
/**
|
|
93
|
+
* A signal emitting when the provider or its settings has changed.
|
|
94
|
+
*/
|
|
95
|
+
readonly providerChanged: ISignal<IAIProviderRegistry, void>;
|
|
96
|
+
/**
|
|
97
|
+
* Get the current chat error;
|
|
98
|
+
*/
|
|
99
|
+
readonly chatError: string;
|
|
100
|
+
/**
|
|
101
|
+
* get the current completer error.
|
|
102
|
+
*/
|
|
103
|
+
readonly completerError: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The provider registry token.
|
|
108
|
+
*/
|
|
109
|
+
export const IAIProviderRegistry = new Token<IAIProviderRegistry>(
|
|
110
|
+
'@jupyterlite/ai:provider-registry',
|
|
111
|
+
'Provider for chat and completion LLM provider'
|
|
112
|
+
);
|
package/style/base.css
CHANGED
package/lib/llm-models/index.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
2
|
-
import { IBaseCompleter } from './base-completer';
|
|
3
|
-
import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
|
|
4
|
-
/**
|
|
5
|
-
* Get an LLM completer from the name.
|
|
6
|
-
*/
|
|
7
|
-
export declare function getCompleter(name: string, settings: ReadonlyPartialJSONObject): IBaseCompleter | null;
|
|
8
|
-
/**
|
|
9
|
-
* Get an LLM chat model from the name.
|
|
10
|
-
*/
|
|
11
|
-
export declare function getChatModel(name: string, settings: ReadonlyPartialJSONObject): BaseChatModel | null;
|
|
12
|
-
/**
|
|
13
|
-
* Get the error message from provider.
|
|
14
|
-
*/
|
|
15
|
-
export declare function getErrorMessage(name: string, error: any): string;
|
|
16
|
-
export declare function getSettings(name: string): any;
|
package/lib/llm-models/utils.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { ChatAnthropic } from '@langchain/anthropic';
|
|
2
|
-
import { ChromeAI } from '@langchain/community/experimental/llms/chrome_ai';
|
|
3
|
-
import { ChatMistralAI } from '@langchain/mistralai';
|
|
4
|
-
import { ChatOpenAI } from '@langchain/openai';
|
|
5
|
-
import { AnthropicCompleter } from './anthropic-completer';
|
|
6
|
-
import { CodestralCompleter } from './codestral-completer';
|
|
7
|
-
import { ChromeCompleter } from './chrome-completer';
|
|
8
|
-
import { OpenAICompleter } from './openai-completer';
|
|
9
|
-
import chromeAI from '../_provider-settings/chromeAI.json';
|
|
10
|
-
import mistralAI from '../_provider-settings/mistralAI.json';
|
|
11
|
-
import anthropic from '../_provider-settings/anthropic.json';
|
|
12
|
-
import openAI from '../_provider-settings/openAI.json';
|
|
13
|
-
/**
|
|
14
|
-
* Get an LLM completer from the name.
|
|
15
|
-
*/
|
|
16
|
-
export function getCompleter(name, settings) {
|
|
17
|
-
if (name === 'MistralAI') {
|
|
18
|
-
return new CodestralCompleter({ settings });
|
|
19
|
-
}
|
|
20
|
-
else if (name === 'Anthropic') {
|
|
21
|
-
return new AnthropicCompleter({ settings });
|
|
22
|
-
}
|
|
23
|
-
else if (name === 'ChromeAI') {
|
|
24
|
-
return new ChromeCompleter({ settings });
|
|
25
|
-
}
|
|
26
|
-
else if (name === 'OpenAI') {
|
|
27
|
-
return new OpenAICompleter({ settings });
|
|
28
|
-
}
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Get an LLM chat model from the name.
|
|
33
|
-
*/
|
|
34
|
-
export function getChatModel(name, settings) {
|
|
35
|
-
if (name === 'MistralAI') {
|
|
36
|
-
return new ChatMistralAI({ ...settings });
|
|
37
|
-
}
|
|
38
|
-
else if (name === 'Anthropic') {
|
|
39
|
-
return new ChatAnthropic({ ...settings });
|
|
40
|
-
}
|
|
41
|
-
else if (name === 'ChromeAI') {
|
|
42
|
-
// TODO: fix
|
|
43
|
-
// @ts-expect-error: missing properties
|
|
44
|
-
return new ChromeAI({ ...settings });
|
|
45
|
-
}
|
|
46
|
-
else if (name === 'OpenAI') {
|
|
47
|
-
return new ChatOpenAI({ ...settings });
|
|
48
|
-
}
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Get the error message from provider.
|
|
53
|
-
*/
|
|
54
|
-
export function getErrorMessage(name, error) {
|
|
55
|
-
if (name === 'MistralAI') {
|
|
56
|
-
return error.message;
|
|
57
|
-
}
|
|
58
|
-
else if (name === 'Anthropic') {
|
|
59
|
-
return error.error.error.message;
|
|
60
|
-
}
|
|
61
|
-
else if (name === 'ChromeAI') {
|
|
62
|
-
return error.message;
|
|
63
|
-
}
|
|
64
|
-
else if (name === 'OpenAI') {
|
|
65
|
-
return error.message;
|
|
66
|
-
}
|
|
67
|
-
return 'Unknown provider';
|
|
68
|
-
}
|
|
69
|
-
/*
|
|
70
|
-
* Get an LLM completer from the name.
|
|
71
|
-
*/
|
|
72
|
-
export function getSettings(name) {
|
|
73
|
-
if (name === 'MistralAI') {
|
|
74
|
-
return mistralAI.properties;
|
|
75
|
-
}
|
|
76
|
-
else if (name === 'Anthropic') {
|
|
77
|
-
return anthropic.properties;
|
|
78
|
-
}
|
|
79
|
-
else if (name === 'ChromeAI') {
|
|
80
|
-
return chromeAI.properties;
|
|
81
|
-
}
|
|
82
|
-
else if (name === 'OpenAI') {
|
|
83
|
-
return openAI.properties;
|
|
84
|
-
}
|
|
85
|
-
return null;
|
|
86
|
-
}
|
package/lib/slash-commands.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TODO: reuse from Jupyter AI instead of copying?
|
|
3
|
-
* https://github.com/jupyterlab/jupyter-ai/blob/main/packages/jupyter-ai/src/slash-autocompletion.tsx
|
|
4
|
-
*/
|
|
5
|
-
/// <reference types="react-addons-linked-state-mixin" />
|
|
6
|
-
import { AutocompleteCommand } from '@jupyter/chat';
|
|
7
|
-
import React from 'react';
|
|
8
|
-
type SlashCommandOption = AutocompleteCommand & {
|
|
9
|
-
id: string;
|
|
10
|
-
description: string;
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Renders an option shown in the slash command autocomplete.
|
|
14
|
-
*/
|
|
15
|
-
export declare function renderSlashCommandOption(optionProps: React.HTMLAttributes<HTMLLIElement>, option: SlashCommandOption): JSX.Element;
|
|
16
|
-
export {};
|
package/lib/slash-commands.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TODO: reuse from Jupyter AI instead of copying?
|
|
3
|
-
* https://github.com/jupyterlab/jupyter-ai/blob/main/packages/jupyter-ai/src/slash-autocompletion.tsx
|
|
4
|
-
*/
|
|
5
|
-
import { Box, Typography } from '@mui/material';
|
|
6
|
-
import HideSource from '@mui/icons-material/HideSource';
|
|
7
|
-
import React from 'react';
|
|
8
|
-
const DEFAULT_SLASH_COMMAND_ICONS = {
|
|
9
|
-
clear: React.createElement(HideSource, null)
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Renders an option shown in the slash command autocomplete.
|
|
13
|
-
*/
|
|
14
|
-
export function renderSlashCommandOption(optionProps, option) {
|
|
15
|
-
const icon = option.id in DEFAULT_SLASH_COMMAND_ICONS
|
|
16
|
-
? DEFAULT_SLASH_COMMAND_ICONS[option.id]
|
|
17
|
-
: DEFAULT_SLASH_COMMAND_ICONS.unknown;
|
|
18
|
-
return (React.createElement("li", { ...optionProps },
|
|
19
|
-
React.createElement(Box, { sx: { lineHeight: 0, marginRight: 4, opacity: 0.618 } }, icon),
|
|
20
|
-
React.createElement(Box, { sx: { flexGrow: 1 } },
|
|
21
|
-
React.createElement(Typography, { component: "span", sx: {
|
|
22
|
-
fontSize: 'var(--jp-ui-font-size1)'
|
|
23
|
-
} }, option.label),
|
|
24
|
-
React.createElement(Typography, { component: "span", sx: { opacity: 0.618, fontSize: 'var(--jp-ui-font-size0)' } }, ' — ' + option.description))));
|
|
25
|
-
}
|
package/lib/token.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
2
|
-
import { Token } from '@lumino/coreutils';
|
|
3
|
-
import { ISignal } from '@lumino/signaling';
|
|
4
|
-
import { IBaseCompleter } from './llm-models';
|
|
5
|
-
export interface IAIProvider {
|
|
6
|
-
name: string;
|
|
7
|
-
completer: IBaseCompleter | null;
|
|
8
|
-
chatModel: BaseChatModel | null;
|
|
9
|
-
modelChange: ISignal<IAIProvider, void>;
|
|
10
|
-
chatError: string;
|
|
11
|
-
completerError: string;
|
|
12
|
-
}
|
|
13
|
-
export declare const IAIProvider: Token<IAIProvider>;
|
package/lib/token.js
DELETED
package/schema/ai-provider.json
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"title": "AI provider",
|
|
3
|
-
"description": "Provider settings",
|
|
4
|
-
"jupyter.lab.setting-icon": "@jupyterlite/ai:jupyternaut-lite",
|
|
5
|
-
"jupyter.lab.setting-icon-label": "JupyterLite AI Chat",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"properties": {
|
|
8
|
-
"provider": {
|
|
9
|
-
"type": "string",
|
|
10
|
-
"title": "The AI provider",
|
|
11
|
-
"description": "The AI provider to use for chat and completion",
|
|
12
|
-
"default": "None",
|
|
13
|
-
"enum": ["None", "Anthropic", "ChromeAI", "MistralAI", "OpenAI"]
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"additionalProperties": true
|
|
17
|
-
}
|
package/src/llm-models/index.ts
DELETED