@jupyterlite/ai 0.4.0 → 0.6.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.
Files changed (72) hide show
  1. package/lib/chat-handler.d.ts +9 -1
  2. package/lib/chat-handler.js +37 -1
  3. package/lib/completion-provider.d.ts +8 -1
  4. package/lib/components/stop-button.d.ts +19 -0
  5. package/lib/components/stop-button.js +32 -0
  6. package/lib/{llm-models/anthropic-completer.d.ts → default-providers/Anthropic/completer.d.ts} +1 -1
  7. package/lib/{llm-models/anthropic-completer.js → default-providers/Anthropic/completer.js} +1 -1
  8. package/lib/{llm-models/chrome-completer.d.ts → default-providers/ChromeAI/completer.d.ts} +1 -1
  9. package/lib/{llm-models/chrome-completer.js → default-providers/ChromeAI/completer.js} +1 -1
  10. package/lib/default-providers/ChromeAI/instructions.d.ts +2 -0
  11. package/lib/default-providers/ChromeAI/instructions.js +24 -0
  12. package/lib/{llm-models/codestral-completer.d.ts → default-providers/MistralAI/completer.d.ts} +1 -1
  13. package/lib/{llm-models/codestral-completer.js → default-providers/MistralAI/completer.js} +1 -1
  14. package/lib/default-providers/MistralAI/instructions.d.ts +2 -0
  15. package/lib/default-providers/MistralAI/instructions.js +16 -0
  16. package/lib/{llm-models/openai-completer.d.ts → default-providers/OpenAI/completer.d.ts} +1 -1
  17. package/lib/{llm-models/openai-completer.js → default-providers/OpenAI/completer.js} +1 -1
  18. package/lib/default-providers/index.d.ts +2 -0
  19. package/lib/default-providers/index.js +60 -0
  20. package/lib/index.d.ts +3 -2
  21. package/lib/index.js +57 -36
  22. package/lib/provider.d.ts +13 -12
  23. package/lib/provider.js +43 -9
  24. package/lib/settings/index.d.ts +3 -0
  25. package/lib/settings/index.js +3 -0
  26. package/lib/settings/panel.d.ts +17 -0
  27. package/lib/settings/panel.js +92 -5
  28. package/lib/settings/settings-connector.d.ts +31 -0
  29. package/lib/settings/settings-connector.js +61 -0
  30. package/lib/settings/utils.d.ts +3 -0
  31. package/lib/settings/utils.js +5 -0
  32. package/lib/tokens.d.ts +16 -4
  33. package/package.json +14 -7
  34. package/schema/provider-registry.json +6 -0
  35. package/src/chat-handler.ts +43 -1
  36. package/src/completion-provider.ts +8 -1
  37. package/src/components/stop-button.tsx +56 -0
  38. package/src/{llm-models/anthropic-completer.ts → default-providers/Anthropic/completer.ts} +2 -2
  39. package/src/{llm-models/chrome-completer.ts → default-providers/ChromeAI/completer.ts} +3 -2
  40. package/src/default-providers/ChromeAI/instructions.ts +24 -0
  41. package/src/{llm-models/codestral-completer.ts → default-providers/MistralAI/completer.ts} +2 -2
  42. package/src/default-providers/MistralAI/instructions.ts +16 -0
  43. package/src/{llm-models/openai-completer.ts → default-providers/OpenAI/completer.ts} +2 -2
  44. package/src/default-providers/index.ts +71 -0
  45. package/src/index.ts +77 -49
  46. package/src/provider.ts +58 -15
  47. package/src/settings/index.ts +3 -0
  48. package/src/settings/panel.tsx +109 -5
  49. package/src/settings/settings-connector.ts +89 -0
  50. package/src/settings/utils.ts +6 -0
  51. package/src/tokens.ts +17 -4
  52. package/lib/llm-models/index.d.ts +0 -4
  53. package/lib/llm-models/index.js +0 -43
  54. package/lib/settings/instructions.d.ts +0 -2
  55. package/lib/settings/instructions.js +0 -44
  56. package/lib/settings/schemas/index.d.ts +0 -3
  57. package/lib/settings/schemas/index.js +0 -11
  58. package/lib/slash-commands.d.ts +0 -16
  59. package/lib/slash-commands.js +0 -25
  60. package/src/llm-models/index.ts +0 -50
  61. package/src/settings/instructions.ts +0 -48
  62. package/src/settings/schemas/index.ts +0 -15
  63. package/src/slash-commands.tsx +0 -55
  64. /package/lib/{llm-models/base-completer.d.ts → base-completer.d.ts} +0 -0
  65. /package/lib/{llm-models/base-completer.js → base-completer.js} +0 -0
  66. /package/lib/{settings/schemas/_generated/Anthropic.json → default-providers/Anthropic/settings-schema.json} +0 -0
  67. /package/lib/{settings/schemas/_generated/ChromeAI.json → default-providers/ChromeAI/settings-schema.json} +0 -0
  68. /package/lib/{settings/schemas/_generated/MistralAI.json → default-providers/MistralAI/settings-schema.json} +0 -0
  69. /package/lib/{settings/schemas/_generated/OpenAI.json → default-providers/OpenAI/settings-schema.json} +0 -0
  70. /package/lib/settings/{schemas/base.json → base.json} +0 -0
  71. /package/src/{llm-models/base-completer.ts → base-completer.ts} +0 -0
  72. /package/src/{llm-models/svg.d.ts → global.d.ts} +0 -0
@@ -1,14 +1,20 @@
1
1
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
2
- import { ISettingRegistry } from '@jupyterlab/settingregistry';
2
+ import {
3
+ ISettingConnector,
4
+ ISettingRegistry
5
+ } from '@jupyterlab/settingregistry';
3
6
  import { FormComponent, IFormRenderer } from '@jupyterlab/ui-components';
7
+ import { ArrayExt } from '@lumino/algorithm';
4
8
  import { JSONExt } from '@lumino/coreutils';
5
9
  import { IChangeEvent } from '@rjsf/core';
6
10
  import type { FieldProps } from '@rjsf/utils';
7
11
  import validator from '@rjsf/validator-ajv8';
8
12
  import { JSONSchema7 } from 'json-schema';
13
+ import { ISecretsManager } from 'jupyter-secrets-manager';
9
14
  import React from 'react';
10
15
 
11
- import baseSettings from './schemas/base.json';
16
+ import { getSecretId, SECRETS_NAMESPACE, SettingConnector } from '.';
17
+ import baseSettings from './base.json';
12
18
  import { IAIProviderRegistry, IDict } from '../tokens';
13
19
 
14
20
  const MD_MIME_TYPE = 'text/markdown';
@@ -18,6 +24,8 @@ const INSTRUCTION_CLASS = 'jp-AISettingsInstructions';
18
24
  export const aiSettingsRenderer = (options: {
19
25
  providerRegistry: IAIProviderRegistry;
20
26
  rmRegistry?: IRenderMimeRegistry;
27
+ secretsManager?: ISecretsManager;
28
+ settingConnector?: ISettingConnector;
21
29
  }): IFormRenderer => {
22
30
  return {
23
31
  fieldRenderer: (props: FieldProps) => {
@@ -49,8 +57,13 @@ export class AiSettings extends React.Component<
49
57
  }
50
58
  this._providerRegistry = props.formContext.providerRegistry;
51
59
  this._rmRegistry = props.formContext.rmRegistry ?? null;
60
+ this._secretsManager = props.formContext.secretsManager ?? null;
61
+ this._settingConnector = props.formContext.settingConnector ?? null;
52
62
  this._settings = props.formContext.settings;
53
63
 
64
+ this._useSecretsManager =
65
+ (this._settings.get('UseSecretsManager').composite as boolean) ?? true;
66
+
54
67
  // Initialize the providers schema.
55
68
  const providerSchema = JSONExt.deepCopy(baseSettings) as any;
56
69
  providerSchema.properties.provider = {
@@ -95,6 +108,47 @@ export class AiSettings extends React.Component<
95
108
  this._settings
96
109
  .set('AIprovider', this._currentSettings)
97
110
  .catch(console.error);
111
+
112
+ this._settings.changed.connect(() => {
113
+ const useSecretsManager =
114
+ (this._settings.get('UseSecretsManager').composite as boolean) ?? true;
115
+ if (useSecretsManager !== this._useSecretsManager) {
116
+ this.updateUseSecretsManager(useSecretsManager);
117
+ }
118
+ });
119
+ }
120
+
121
+ async componentDidUpdate(): Promise<void> {
122
+ if (!this._secretsManager || !this._useSecretsManager) {
123
+ return;
124
+ }
125
+ // Attach the password inputs to the secrets manager only if they have changed.
126
+ const inputs = this._formRef.current?.getElementsByTagName('input') || [];
127
+ if (ArrayExt.shallowEqual(inputs, this._formInputs)) {
128
+ return;
129
+ }
130
+
131
+ await this._secretsManager.detachAll(SECRETS_NAMESPACE);
132
+ this._formInputs = [...inputs];
133
+ this._unsavedFields = [];
134
+ for (let i = 0; i < inputs.length; i++) {
135
+ if (inputs[i].type.toLowerCase() === 'password') {
136
+ const label = inputs[i].getAttribute('label');
137
+ if (label) {
138
+ const id = getSecretId(this._provider, label);
139
+ this._secretsManager.attach(
140
+ SECRETS_NAMESPACE,
141
+ id,
142
+ inputs[i],
143
+ (value: string) => this._onPasswordUpdated(label, value)
144
+ );
145
+ this._unsavedFields.push(label);
146
+ }
147
+ }
148
+ }
149
+ if (this._settingConnector instanceof SettingConnector) {
150
+ this._settingConnector.doNotSave = this._unsavedFields;
151
+ }
98
152
  }
99
153
 
100
154
  /**
@@ -126,11 +180,44 @@ export class AiSettings extends React.Component<
126
180
  * Save settings in local storage for a given provider.
127
181
  */
128
182
  saveSettings(value: IDict<any>) {
183
+ const currentSettings = { ...value };
129
184
  const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) ?? '{}');
130
- settings[this._provider] = value;
185
+ this._unsavedFields.forEach(field => delete currentSettings[field]);
186
+ settings[this._provider] = currentSettings;
131
187
  localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
132
188
  }
133
189
 
190
+ private updateUseSecretsManager = (value: boolean) => {
191
+ this._useSecretsManager = value;
192
+ if (!value) {
193
+ // Detach all the password inputs attached to the secrets manager, and save the
194
+ // current settings to the local storage to save the password.
195
+ this._secretsManager?.detachAll(SECRETS_NAMESPACE);
196
+ this._formInputs = [];
197
+ this._unsavedFields = [];
198
+ if (this._settingConnector instanceof SettingConnector) {
199
+ this._settingConnector.doNotSave = [];
200
+ }
201
+ this.saveSettings(this._currentSettings);
202
+ } else {
203
+ // Remove all the keys stored locally and attach the password inputs to the
204
+ // secrets manager.
205
+ const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
206
+ Object.keys(settings).forEach(provider => {
207
+ Object.keys(settings[provider])
208
+ .filter(key => key.toLowerCase().includes('key'))
209
+ .forEach(key => {
210
+ delete settings[provider][key];
211
+ });
212
+ });
213
+ localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
214
+ this.componentDidUpdate();
215
+ }
216
+ this._settings
217
+ .set('AIprovider', { provider: this._provider, ...this._currentSettings })
218
+ .catch(console.error);
219
+ };
220
+
134
221
  /**
135
222
  * Update the UI schema of the form.
136
223
  * Currently use to hide API keys.
@@ -206,6 +293,17 @@ export class AiSettings extends React.Component<
206
293
  .catch(console.error);
207
294
  };
208
295
 
296
+ /**
297
+ * Callback function called when the password input has been programmatically updated
298
+ * with the secret manager.
299
+ */
300
+ private _onPasswordUpdated = (fieldName: string, value: string) => {
301
+ this._currentSettings[fieldName] = value;
302
+ this._settings
303
+ .set('AIprovider', { provider: this._provider, ...this._currentSettings })
304
+ .catch(console.error);
305
+ };
306
+
209
307
  /**
210
308
  * Triggered when the form value has changed, to update the current settings and save
211
309
  * it in local storage.
@@ -221,7 +319,7 @@ export class AiSettings extends React.Component<
221
319
 
222
320
  render(): JSX.Element {
223
321
  return (
224
- <>
322
+ <div ref={this._formRef}>
225
323
  <WrappedFormComponent
226
324
  formData={{ provider: this._provider }}
227
325
  schema={this._providerSchema}
@@ -243,15 +341,21 @@ export class AiSettings extends React.Component<
243
341
  onChange={this._onFormChange}
244
342
  uiSchema={this._uiSchema}
245
343
  />
246
- </>
344
+ </div>
247
345
  );
248
346
  }
249
347
 
250
348
  private _providerRegistry: IAIProviderRegistry;
251
349
  private _provider: string;
252
350
  private _providerSchema: JSONSchema7;
351
+ private _useSecretsManager: boolean;
253
352
  private _rmRegistry: IRenderMimeRegistry | null;
353
+ private _secretsManager: ISecretsManager | null;
354
+ private _settingConnector: ISettingConnector | null;
254
355
  private _currentSettings: IDict<any> = { provider: 'None' };
255
356
  private _uiSchema: IDict<any> = {};
256
357
  private _settings: ISettingRegistry.ISettings;
358
+ private _formRef = React.createRef<HTMLDivElement>();
359
+ private _unsavedFields: string[] = [];
360
+ private _formInputs: HTMLInputElement[] = [];
257
361
  }
@@ -0,0 +1,89 @@
1
+ import { PageConfig } from '@jupyterlab/coreutils';
2
+ import {
3
+ ISettingConnector,
4
+ ISettingRegistry
5
+ } from '@jupyterlab/settingregistry';
6
+ import { DataConnector, IDataConnector } from '@jupyterlab/statedb';
7
+ import { Throttler } from '@lumino/polling';
8
+ import * as json5 from 'json5';
9
+
10
+ import { SECRETS_REPLACEMENT } from '.';
11
+
12
+ /**
13
+ * A data connector for fetching settings.
14
+ *
15
+ * #### Notes
16
+ * This connector adds a query parameter to the base services setting manager.
17
+ */
18
+ export class SettingConnector
19
+ extends DataConnector<ISettingRegistry.IPlugin, string>
20
+ implements ISettingConnector
21
+ {
22
+ constructor(connector: IDataConnector<ISettingRegistry.IPlugin, string>) {
23
+ super();
24
+ this._connector = connector;
25
+ }
26
+
27
+ set doNotSave(fields: string[]) {
28
+ this._doNotSave = [...fields];
29
+ }
30
+
31
+ /**
32
+ * Fetch settings for a plugin.
33
+ * @param id - The plugin ID
34
+ *
35
+ * #### Notes
36
+ * The REST API requests are throttled at one request per plugin per 100ms.
37
+ */
38
+ fetch(id: string): Promise<ISettingRegistry.IPlugin | undefined> {
39
+ const throttlers = this._throttlers;
40
+ if (!(id in throttlers)) {
41
+ throttlers[id] = new Throttler(() => this._connector.fetch(id), 100);
42
+ }
43
+ return throttlers[id].invoke();
44
+ }
45
+
46
+ async list(query: 'ids'): Promise<{ ids: string[] }>;
47
+ async list(
48
+ query: 'active' | 'all'
49
+ ): Promise<{ ids: string[]; values: ISettingRegistry.IPlugin[] }>;
50
+ async list(
51
+ query: 'active' | 'all' | 'ids' = 'all'
52
+ ): Promise<{ ids: string[]; values?: ISettingRegistry.IPlugin[] }> {
53
+ const { isDisabled } = PageConfig.Extension;
54
+ const { ids, values } = await this._connector.list(
55
+ query === 'ids' ? 'ids' : undefined
56
+ );
57
+
58
+ if (query === 'all') {
59
+ return { ids, values };
60
+ }
61
+
62
+ if (query === 'ids') {
63
+ return { ids };
64
+ }
65
+
66
+ return {
67
+ ids: ids.filter(id => !isDisabled(id)),
68
+ values: values.filter(({ id }) => !isDisabled(id))
69
+ };
70
+ }
71
+
72
+ async save(id: string, raw: string): Promise<void> {
73
+ const settings = json5.parse(raw);
74
+ this._doNotSave.forEach(field => {
75
+ if (
76
+ settings['AIprovider'] !== undefined &&
77
+ settings['AIprovider'][field] !== undefined &&
78
+ settings['AIprovider'][field] !== ''
79
+ ) {
80
+ settings['AIprovider'][field] = SECRETS_REPLACEMENT;
81
+ }
82
+ });
83
+ await this._connector.save(id, json5.stringify(settings, null, 2));
84
+ }
85
+
86
+ private _connector: IDataConnector<ISettingRegistry.IPlugin, string>;
87
+ private _doNotSave: string[] = [];
88
+ private _throttlers: { [key: string]: Throttler } = Object.create(null);
89
+ }
@@ -0,0 +1,6 @@
1
+ export const SECRETS_NAMESPACE = '@jupyterlite/ai';
2
+ export const SECRETS_REPLACEMENT = '***';
3
+
4
+ export function getSecretId(provider: string, label: string) {
5
+ return `${provider}-${label}`;
6
+ }
package/src/tokens.ts CHANGED
@@ -3,7 +3,7 @@ import { ReadonlyPartialJSONObject, Token } from '@lumino/coreutils';
3
3
  import { ISignal } from '@lumino/signaling';
4
4
  import { JSONSchema7 } from 'json-schema';
5
5
 
6
- import { IBaseCompleter } from './llm-models';
6
+ import { IBaseCompleter } from './base-completer';
7
7
 
8
8
  export interface IDict<T = any> {
9
9
  [key: string]: T;
@@ -85,10 +85,9 @@ export interface IAIProviderRegistry {
85
85
  * Set the providers (chat model and completer).
86
86
  * Creates the providers if the name has changed, otherwise only updates their config.
87
87
  *
88
- * @param name - the name of the provider to use.
89
- * @param settings - the settings for the models.
88
+ * @param options - an object with the name and the settings of the provider to use.
90
89
  */
91
- setProvider(name: string, settings: ReadonlyPartialJSONObject): void;
90
+ setProvider(options: ISetProviderOptions): void;
92
91
  /**
93
92
  * A signal emitting when the provider or its settings has changed.
94
93
  */
@@ -103,6 +102,20 @@ export interface IAIProviderRegistry {
103
102
  readonly completerError: string;
104
103
  }
105
104
 
105
+ /**
106
+ * The set provider options.
107
+ */
108
+ export interface ISetProviderOptions {
109
+ /**
110
+ * The name of the provider.
111
+ */
112
+ name: string;
113
+ /**
114
+ * The settings of the provider.
115
+ */
116
+ settings: ReadonlyPartialJSONObject;
117
+ }
118
+
106
119
  /**
107
120
  * The provider registry token.
108
121
  */
@@ -1,4 +0,0 @@
1
- import { IAIProvider } from '../tokens';
2
- export * from './base-completer';
3
- declare const AIProviders: IAIProvider[];
4
- export { AIProviders };
@@ -1,43 +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 { instructions } from '../settings/instructions';
10
- import { ProviderSettings } from '../settings/schemas';
11
- export * from './base-completer';
12
- const AIProviders = [
13
- {
14
- name: 'Anthropic',
15
- chatModel: ChatAnthropic,
16
- completer: AnthropicCompleter,
17
- settingsSchema: ProviderSettings.Anthropic,
18
- errorMessage: (error) => error.error.error.message
19
- },
20
- {
21
- name: 'ChromeAI',
22
- // TODO: fix
23
- // @ts-expect-error: missing properties
24
- chatModel: ChromeAI,
25
- completer: ChromeCompleter,
26
- instructions: instructions.ChromeAI,
27
- settingsSchema: ProviderSettings.ChromeAI
28
- },
29
- {
30
- name: 'MistralAI',
31
- chatModel: ChatMistralAI,
32
- completer: CodestralCompleter,
33
- instructions: instructions.MistralAI,
34
- settingsSchema: ProviderSettings.MistralAI
35
- },
36
- {
37
- name: 'OpenAI',
38
- chatModel: ChatOpenAI,
39
- completer: OpenAICompleter,
40
- settingsSchema: ProviderSettings.OpenAI
41
- }
42
- ];
43
- export { AIProviders };
@@ -1,2 +0,0 @@
1
- import { IDict } from '../tokens';
2
- export declare const instructions: IDict;
@@ -1,44 +0,0 @@
1
- const chromeAiInstructions = `
2
- <i class="fas fa-exclamation-triangle"></i> Support for ChromeAI is still experimental and only available in Google Chrome.
3
-
4
- You can test ChromeAI is enabled in your browser by going to the following URL: https://chromeai.org/
5
-
6
- Enable the proper flags in Google Chrome.
7
-
8
- - chrome://flags/#prompt-api-for-gemini-nano
9
- - Select: \`Enabled\`
10
- - chrome://flags/#optimization-guide-on-device-model
11
- - Select: \`Enabled BypassPrefRequirement\`
12
- - chrome://components
13
- - Click \`Check for Update\` on Optimization Guide On Device Model to download the model
14
- - [Optional] chrome://flags/#text-safety-classifier
15
-
16
- <img src="https://github.com/user-attachments/assets/d48f46cc-52ee-4ce5-9eaf-c763cdbee04c" alt="A screenshot showing how to enable the ChromeAI flag in Google Chrome" width="500px">
17
-
18
- Then restart Chrome for these changes to take effect.
19
-
20
- <i class="fas fa-exclamation-triangle"></i> On first use, Chrome will download the on-device model, which can be as large as 22GB (according to their docs and at the time of writing).
21
- During the download, ChromeAI may not be available via the extension.
22
-
23
- <i class="fa fa-info-circle" aria-hidden="true"></i> For more information about Chrome Built-in AI: https://developer.chrome.com/docs/ai/get-started
24
- `;
25
- const mistralAIInstructions = `
26
- <i class="fas fa-exclamation-triangle"></i> This extension is still very much experimental. It is not an official MistralAI extension.
27
-
28
- 1. Go to https://console.mistral.ai/api-keys/ and create an API key.
29
-
30
- <img src="https://raw.githubusercontent.com/jupyterlite/ai/refs/heads/main/img/1-api-key.png" alt="Screenshot showing how to create an API key" width="500px">
31
-
32
- 2. Open the JupyterLab settings and go to the **Ai providers** section to select the \`MistralAI\`
33
- provider and the API key (required).
34
-
35
- <img src="https://raw.githubusercontent.com/jupyterlite/ai/refs/heads/main/img/2-jupyterlab-settings.png" alt="Screenshot showing how to add the API key to the settings" width="500px">
36
-
37
- 3. Open the chat, or use the inline completer
38
-
39
- <img src="https://raw.githubusercontent.com/jupyterlite/ai/refs/heads/main/img/3-usage.png" alt="Screenshot showing how to use the chat" width="500px">
40
- `;
41
- export const instructions = {
42
- ChromeAI: chromeAiInstructions,
43
- MistralAI: mistralAIInstructions
44
- };
@@ -1,3 +0,0 @@
1
- import { IDict } from '../../tokens';
2
- declare const ProviderSettings: IDict<any>;
3
- export { ProviderSettings };
@@ -1,11 +0,0 @@
1
- import ChromeAI from './_generated/ChromeAI.json';
2
- import MistralAI from './_generated/MistralAI.json';
3
- import Anthropic from './_generated/Anthropic.json';
4
- import OpenAI from './_generated/OpenAI.json';
5
- const ProviderSettings = {
6
- ChromeAI,
7
- MistralAI,
8
- Anthropic,
9
- OpenAI
10
- };
11
- export { ProviderSettings };
@@ -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 {};
@@ -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
- }
@@ -1,50 +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
-
6
- import { AnthropicCompleter } from './anthropic-completer';
7
- import { CodestralCompleter } from './codestral-completer';
8
- import { ChromeCompleter } from './chrome-completer';
9
- import { OpenAICompleter } from './openai-completer';
10
-
11
- import { instructions } from '../settings/instructions';
12
- import { ProviderSettings } from '../settings/schemas';
13
-
14
- import { IAIProvider } from '../tokens';
15
-
16
- export * from './base-completer';
17
-
18
- const AIProviders: IAIProvider[] = [
19
- {
20
- name: 'Anthropic',
21
- chatModel: ChatAnthropic,
22
- completer: AnthropicCompleter,
23
- settingsSchema: ProviderSettings.Anthropic,
24
- errorMessage: (error: any) => error.error.error.message
25
- },
26
- {
27
- name: 'ChromeAI',
28
- // TODO: fix
29
- // @ts-expect-error: missing properties
30
- chatModel: ChromeAI,
31
- completer: ChromeCompleter,
32
- instructions: instructions.ChromeAI,
33
- settingsSchema: ProviderSettings.ChromeAI
34
- },
35
- {
36
- name: 'MistralAI',
37
- chatModel: ChatMistralAI,
38
- completer: CodestralCompleter,
39
- instructions: instructions.MistralAI,
40
- settingsSchema: ProviderSettings.MistralAI
41
- },
42
- {
43
- name: 'OpenAI',
44
- chatModel: ChatOpenAI,
45
- completer: OpenAICompleter,
46
- settingsSchema: ProviderSettings.OpenAI
47
- }
48
- ];
49
-
50
- export { AIProviders };
@@ -1,48 +0,0 @@
1
- import { IDict } from '../tokens';
2
-
3
- const chromeAiInstructions = `
4
- <i class="fas fa-exclamation-triangle"></i> Support for ChromeAI is still experimental and only available in Google Chrome.
5
-
6
- You can test ChromeAI is enabled in your browser by going to the following URL: https://chromeai.org/
7
-
8
- Enable the proper flags in Google Chrome.
9
-
10
- - chrome://flags/#prompt-api-for-gemini-nano
11
- - Select: \`Enabled\`
12
- - chrome://flags/#optimization-guide-on-device-model
13
- - Select: \`Enabled BypassPrefRequirement\`
14
- - chrome://components
15
- - Click \`Check for Update\` on Optimization Guide On Device Model to download the model
16
- - [Optional] chrome://flags/#text-safety-classifier
17
-
18
- <img src="https://github.com/user-attachments/assets/d48f46cc-52ee-4ce5-9eaf-c763cdbee04c" alt="A screenshot showing how to enable the ChromeAI flag in Google Chrome" width="500px">
19
-
20
- Then restart Chrome for these changes to take effect.
21
-
22
- <i class="fas fa-exclamation-triangle"></i> On first use, Chrome will download the on-device model, which can be as large as 22GB (according to their docs and at the time of writing).
23
- During the download, ChromeAI may not be available via the extension.
24
-
25
- <i class="fa fa-info-circle" aria-hidden="true"></i> For more information about Chrome Built-in AI: https://developer.chrome.com/docs/ai/get-started
26
- `;
27
-
28
- const mistralAIInstructions = `
29
- <i class="fas fa-exclamation-triangle"></i> This extension is still very much experimental. It is not an official MistralAI extension.
30
-
31
- 1. Go to https://console.mistral.ai/api-keys/ and create an API key.
32
-
33
- <img src="https://raw.githubusercontent.com/jupyterlite/ai/refs/heads/main/img/1-api-key.png" alt="Screenshot showing how to create an API key" width="500px">
34
-
35
- 2. Open the JupyterLab settings and go to the **Ai providers** section to select the \`MistralAI\`
36
- provider and the API key (required).
37
-
38
- <img src="https://raw.githubusercontent.com/jupyterlite/ai/refs/heads/main/img/2-jupyterlab-settings.png" alt="Screenshot showing how to add the API key to the settings" width="500px">
39
-
40
- 3. Open the chat, or use the inline completer
41
-
42
- <img src="https://raw.githubusercontent.com/jupyterlite/ai/refs/heads/main/img/3-usage.png" alt="Screenshot showing how to use the chat" width="500px">
43
- `;
44
-
45
- export const instructions: IDict = {
46
- ChromeAI: chromeAiInstructions,
47
- MistralAI: mistralAIInstructions
48
- };
@@ -1,15 +0,0 @@
1
- import { IDict } from '../../tokens';
2
-
3
- import ChromeAI from './_generated/ChromeAI.json';
4
- import MistralAI from './_generated/MistralAI.json';
5
- import Anthropic from './_generated/Anthropic.json';
6
- import OpenAI from './_generated/OpenAI.json';
7
-
8
- const ProviderSettings: IDict<any> = {
9
- ChromeAI,
10
- MistralAI,
11
- Anthropic,
12
- OpenAI
13
- };
14
-
15
- export { ProviderSettings };
@@ -1,55 +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
-
6
- import { Box, Typography } from '@mui/material';
7
- import { AutocompleteCommand } from '@jupyter/chat';
8
-
9
- import HideSource from '@mui/icons-material/HideSource';
10
-
11
- import React from 'react';
12
-
13
- const DEFAULT_SLASH_COMMAND_ICONS: Record<string, JSX.Element> = {
14
- clear: <HideSource />
15
- };
16
-
17
- type SlashCommandOption = AutocompleteCommand & {
18
- id: string;
19
- description: string;
20
- };
21
-
22
- /**
23
- * Renders an option shown in the slash command autocomplete.
24
- */
25
- export function renderSlashCommandOption(
26
- optionProps: React.HTMLAttributes<HTMLLIElement>,
27
- option: SlashCommandOption
28
- ): JSX.Element {
29
- const icon =
30
- option.id in DEFAULT_SLASH_COMMAND_ICONS
31
- ? DEFAULT_SLASH_COMMAND_ICONS[option.id]
32
- : DEFAULT_SLASH_COMMAND_ICONS.unknown;
33
-
34
- return (
35
- <li {...optionProps}>
36
- <Box sx={{ lineHeight: 0, marginRight: 4, opacity: 0.618 }}>{icon}</Box>
37
- <Box sx={{ flexGrow: 1 }}>
38
- <Typography
39
- component="span"
40
- sx={{
41
- fontSize: 'var(--jp-ui-font-size1)'
42
- }}
43
- >
44
- {option.label}
45
- </Typography>
46
- <Typography
47
- component="span"
48
- sx={{ opacity: 0.618, fontSize: 'var(--jp-ui-font-size0)' }}
49
- >
50
- {' — ' + option.description}
51
- </Typography>
52
- </Box>
53
- </li>
54
- );
55
- }