@jupyterlite/ai 0.6.1 → 0.7.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/README.md +1 -1
- package/lib/base-completer.d.ts +0 -5
- package/lib/chat-handler.d.ts +19 -5
- package/lib/chat-handler.js +47 -26
- package/lib/completion-provider.d.ts +2 -2
- package/lib/completion-provider.js +4 -3
- package/lib/components/stop-button.d.ts +0 -1
- package/lib/default-providers/Anthropic/completer.d.ts +1 -3
- package/lib/default-providers/Anthropic/completer.js +4 -6
- package/lib/default-providers/ChromeAI/completer.d.ts +1 -3
- package/lib/default-providers/ChromeAI/completer.js +4 -6
- package/lib/default-providers/ChromeAI/instructions.d.ts +4 -0
- package/lib/default-providers/ChromeAI/instructions.js +18 -0
- package/lib/default-providers/MistralAI/completer.d.ts +1 -3
- package/lib/default-providers/MistralAI/completer.js +5 -6
- package/lib/default-providers/Ollama/completer.d.ts +17 -0
- package/lib/default-providers/Ollama/completer.js +49 -0
- package/lib/default-providers/Ollama/instructions.d.ts +2 -0
- package/lib/default-providers/Ollama/instructions.js +70 -0
- package/lib/default-providers/Ollama/settings-schema.json +146 -0
- package/lib/default-providers/OpenAI/completer.d.ts +1 -3
- package/lib/default-providers/OpenAI/completer.js +4 -6
- package/lib/default-providers/WebLLM/completer.d.ts +27 -0
- package/lib/default-providers/WebLLM/completer.js +136 -0
- package/lib/default-providers/WebLLM/instructions.d.ts +6 -0
- package/lib/default-providers/WebLLM/instructions.js +32 -0
- package/lib/default-providers/WebLLM/settings-schema.json +21 -0
- package/lib/default-providers/index.js +119 -4
- package/lib/index.d.ts +2 -2
- package/lib/index.js +16 -26
- package/lib/provider.d.ts +11 -13
- package/lib/provider.js +124 -54
- package/lib/settings/index.d.ts +0 -1
- package/lib/settings/index.js +0 -1
- package/lib/settings/panel.d.ts +44 -11
- package/lib/settings/panel.js +231 -130
- package/lib/tokens.d.ts +21 -2
- package/lib/types/ai-model.d.ts +24 -0
- package/lib/types/ai-model.js +5 -0
- package/package.json +15 -12
- package/schema/provider-registry.json +0 -6
- package/src/base-completer.ts +0 -6
- package/src/chat-handler.ts +40 -7
- package/src/completion-provider.ts +2 -2
- package/src/default-providers/Anthropic/completer.ts +3 -8
- package/src/default-providers/ChromeAI/completer.ts +3 -8
- package/src/default-providers/ChromeAI/instructions.ts +21 -0
- package/src/default-providers/MistralAI/completer.ts +3 -8
- package/src/default-providers/Ollama/completer.ts +62 -0
- package/src/default-providers/Ollama/instructions.ts +70 -0
- package/src/default-providers/OpenAI/completer.ts +3 -8
- package/src/default-providers/WebLLM/completer.ts +162 -0
- package/src/default-providers/WebLLM/instructions.ts +33 -0
- package/src/default-providers/index.ts +151 -14
- package/src/index.ts +17 -29
- package/src/provider.ts +135 -46
- package/src/settings/index.ts +0 -1
- package/src/settings/panel.tsx +223 -79
- package/src/tokens.ts +23 -2
- package/src/types/ai-model.ts +37 -0
- package/src/types/service-worker.d.ts +6 -0
- package/style/base.css +5 -0
- package/lib/settings/settings-connector.d.ts +0 -31
- package/lib/settings/settings-connector.js +0 -61
- package/src/settings/settings-connector.ts +0 -89
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"numa": {
|
|
6
|
+
"type": "boolean"
|
|
7
|
+
},
|
|
8
|
+
"numCtx": {
|
|
9
|
+
"type": "number"
|
|
10
|
+
},
|
|
11
|
+
"numBatch": {
|
|
12
|
+
"type": "number"
|
|
13
|
+
},
|
|
14
|
+
"numGpu": {
|
|
15
|
+
"type": "number"
|
|
16
|
+
},
|
|
17
|
+
"mainGpu": {
|
|
18
|
+
"type": "number"
|
|
19
|
+
},
|
|
20
|
+
"lowVram": {
|
|
21
|
+
"type": "boolean"
|
|
22
|
+
},
|
|
23
|
+
"f16Kv": {
|
|
24
|
+
"type": "boolean"
|
|
25
|
+
},
|
|
26
|
+
"logitsAll": {
|
|
27
|
+
"type": "boolean"
|
|
28
|
+
},
|
|
29
|
+
"vocabOnly": {
|
|
30
|
+
"type": "boolean"
|
|
31
|
+
},
|
|
32
|
+
"useMmap": {
|
|
33
|
+
"type": "boolean"
|
|
34
|
+
},
|
|
35
|
+
"useMlock": {
|
|
36
|
+
"type": "boolean"
|
|
37
|
+
},
|
|
38
|
+
"embeddingOnly": {
|
|
39
|
+
"type": "boolean"
|
|
40
|
+
},
|
|
41
|
+
"numThread": {
|
|
42
|
+
"type": "number"
|
|
43
|
+
},
|
|
44
|
+
"numKeep": {
|
|
45
|
+
"type": "number"
|
|
46
|
+
},
|
|
47
|
+
"seed": {
|
|
48
|
+
"type": "number"
|
|
49
|
+
},
|
|
50
|
+
"numPredict": {
|
|
51
|
+
"type": "number"
|
|
52
|
+
},
|
|
53
|
+
"topK": {
|
|
54
|
+
"type": "number"
|
|
55
|
+
},
|
|
56
|
+
"topP": {
|
|
57
|
+
"type": "number"
|
|
58
|
+
},
|
|
59
|
+
"tfsZ": {
|
|
60
|
+
"type": "number"
|
|
61
|
+
},
|
|
62
|
+
"typicalP": {
|
|
63
|
+
"type": "number"
|
|
64
|
+
},
|
|
65
|
+
"repeatLastN": {
|
|
66
|
+
"type": "number"
|
|
67
|
+
},
|
|
68
|
+
"temperature": {
|
|
69
|
+
"type": "number"
|
|
70
|
+
},
|
|
71
|
+
"repeatPenalty": {
|
|
72
|
+
"type": "number"
|
|
73
|
+
},
|
|
74
|
+
"presencePenalty": {
|
|
75
|
+
"type": "number"
|
|
76
|
+
},
|
|
77
|
+
"frequencyPenalty": {
|
|
78
|
+
"type": "number"
|
|
79
|
+
},
|
|
80
|
+
"mirostat": {
|
|
81
|
+
"type": "number"
|
|
82
|
+
},
|
|
83
|
+
"mirostatTau": {
|
|
84
|
+
"type": "number"
|
|
85
|
+
},
|
|
86
|
+
"mirostatEta": {
|
|
87
|
+
"type": "number"
|
|
88
|
+
},
|
|
89
|
+
"penalizeNewline": {
|
|
90
|
+
"type": "boolean"
|
|
91
|
+
},
|
|
92
|
+
"keepAlive": {
|
|
93
|
+
"type": [
|
|
94
|
+
"string",
|
|
95
|
+
"number"
|
|
96
|
+
],
|
|
97
|
+
"default": "5m"
|
|
98
|
+
},
|
|
99
|
+
"stop": {
|
|
100
|
+
"type": "array",
|
|
101
|
+
"items": {
|
|
102
|
+
"type": "string"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"disableStreaming": {
|
|
106
|
+
"type": "boolean",
|
|
107
|
+
"description": "Whether to disable streaming.\n\nIf streaming is bypassed, then `stream()` will defer to `invoke()`.\n\n- If true, will always bypass streaming case.\n- If false (default), will always use streaming case if available."
|
|
108
|
+
},
|
|
109
|
+
"model": {
|
|
110
|
+
"type": "string",
|
|
111
|
+
"description": "The model to invoke. If the model does not exist, it will be pulled.",
|
|
112
|
+
"default": ""
|
|
113
|
+
},
|
|
114
|
+
"baseUrl": {
|
|
115
|
+
"type": "string",
|
|
116
|
+
"description": "The host URL of the Ollama server.",
|
|
117
|
+
"default": ""
|
|
118
|
+
},
|
|
119
|
+
"headers": {
|
|
120
|
+
"type": "object",
|
|
121
|
+
"additionalProperties": false,
|
|
122
|
+
"description": "Optional HTTP Headers to include in the request."
|
|
123
|
+
},
|
|
124
|
+
"checkOrPullModel": {
|
|
125
|
+
"type": "boolean",
|
|
126
|
+
"description": "Whether or not to check the model exists on the local machine before invoking it. If set to `true`, the model will be pulled if it does not exist.",
|
|
127
|
+
"default": false
|
|
128
|
+
},
|
|
129
|
+
"streaming": {
|
|
130
|
+
"type": "boolean"
|
|
131
|
+
},
|
|
132
|
+
"format": {
|
|
133
|
+
"anyOf": [
|
|
134
|
+
{
|
|
135
|
+
"type": "string"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"type": "object"
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"additionalProperties": false,
|
|
144
|
+
"description": "Input to chat model class.",
|
|
145
|
+
"definitions": {}
|
|
146
|
+
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { CompletionHandler, IInlineCompletionContext } from '@jupyterlab/completer';
|
|
2
|
-
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
3
2
|
import { BaseCompleter, IBaseCompleter } from '../../base-completer';
|
|
4
3
|
export declare class OpenAICompleter implements IBaseCompleter {
|
|
5
4
|
constructor(options: BaseCompleter.IOptions);
|
|
6
|
-
get provider(): BaseChatModel;
|
|
7
5
|
/**
|
|
8
6
|
* Getter and setter for the initial prompt.
|
|
9
7
|
*/
|
|
@@ -14,6 +12,6 @@ export declare class OpenAICompleter implements IBaseCompleter {
|
|
|
14
12
|
insertText: string;
|
|
15
13
|
}[];
|
|
16
14
|
}>;
|
|
17
|
-
private
|
|
15
|
+
private _completer;
|
|
18
16
|
private _prompt;
|
|
19
17
|
}
|
|
@@ -3,11 +3,7 @@ import { ChatOpenAI } from '@langchain/openai';
|
|
|
3
3
|
import { COMPLETION_SYSTEM_PROMPT } from '../../provider';
|
|
4
4
|
export class OpenAICompleter {
|
|
5
5
|
constructor(options) {
|
|
6
|
-
this.
|
|
7
|
-
this._openAIProvider = new ChatOpenAI({ ...options.settings });
|
|
8
|
-
}
|
|
9
|
-
get provider() {
|
|
10
|
-
return this._openAIProvider;
|
|
6
|
+
this._completer = new ChatOpenAI({ ...options.settings });
|
|
11
7
|
}
|
|
12
8
|
/**
|
|
13
9
|
* Getter and setter for the initial prompt.
|
|
@@ -23,7 +19,7 @@ export class OpenAICompleter {
|
|
|
23
19
|
const prompt = text.slice(0, cursorOffset);
|
|
24
20
|
const messages = [new SystemMessage(this._prompt), new AIMessage(prompt)];
|
|
25
21
|
try {
|
|
26
|
-
const response = await this.
|
|
22
|
+
const response = await this._completer.invoke(messages);
|
|
27
23
|
const items = [];
|
|
28
24
|
if (typeof response.content === 'string') {
|
|
29
25
|
items.push({
|
|
@@ -48,4 +44,6 @@ export class OpenAICompleter {
|
|
|
48
44
|
return { items: [] };
|
|
49
45
|
}
|
|
50
46
|
}
|
|
47
|
+
_completer;
|
|
48
|
+
_prompt = COMPLETION_SYSTEM_PROMPT;
|
|
51
49
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CompletionHandler, IInlineCompletionContext } from '@jupyterlab/completer';
|
|
2
|
+
import { ChatWebLLM } from '@langchain/community/chat_models/webllm';
|
|
3
|
+
import { BaseCompleter, IBaseCompleter } from '../../base-completer';
|
|
4
|
+
export declare class WebLLMCompleter implements IBaseCompleter {
|
|
5
|
+
constructor(options: BaseCompleter.IOptions);
|
|
6
|
+
/**
|
|
7
|
+
* Initialize the WebLLM model
|
|
8
|
+
*/
|
|
9
|
+
private _initializeModel;
|
|
10
|
+
/**
|
|
11
|
+
* Getter and setter for the initial prompt.
|
|
12
|
+
*/
|
|
13
|
+
get prompt(): string;
|
|
14
|
+
set prompt(value: string);
|
|
15
|
+
get provider(): ChatWebLLM;
|
|
16
|
+
fetch(request: CompletionHandler.IRequest, context: IInlineCompletionContext): Promise<{
|
|
17
|
+
items: {
|
|
18
|
+
insertText: string;
|
|
19
|
+
}[];
|
|
20
|
+
}>;
|
|
21
|
+
private _completer;
|
|
22
|
+
private _prompt;
|
|
23
|
+
private _isInitialized;
|
|
24
|
+
private _isInitializing;
|
|
25
|
+
private _initError;
|
|
26
|
+
private _abortController;
|
|
27
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
|
|
2
|
+
import { ChatWebLLM } from '@langchain/community/chat_models/webllm';
|
|
3
|
+
import { COMPLETION_SYSTEM_PROMPT } from '../../provider';
|
|
4
|
+
/**
|
|
5
|
+
* Regular expression to match the '```' string at the start of a string.
|
|
6
|
+
* So the completions returned by the LLM can still be kept after removing the code block formatting.
|
|
7
|
+
*
|
|
8
|
+
* For example, if the response contains the following content after typing `import pandas`:
|
|
9
|
+
*
|
|
10
|
+
* ```python
|
|
11
|
+
* as pd
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* The formatting string after removing the code block delimiters will be:
|
|
15
|
+
*
|
|
16
|
+
* as pd
|
|
17
|
+
*/
|
|
18
|
+
const CODE_BLOCK_START_REGEX = /^```(?:[a-zA-Z]+)?\n?/;
|
|
19
|
+
/**
|
|
20
|
+
* Regular expression to match the '```' string at the end of a string.
|
|
21
|
+
*/
|
|
22
|
+
const CODE_BLOCK_END_REGEX = /```$/;
|
|
23
|
+
export class WebLLMCompleter {
|
|
24
|
+
constructor(options) {
|
|
25
|
+
const model = options.settings.model;
|
|
26
|
+
// provide model separately since ChatWebLLM expects it
|
|
27
|
+
this._completer = new ChatWebLLM({
|
|
28
|
+
...options.settings,
|
|
29
|
+
model
|
|
30
|
+
});
|
|
31
|
+
// Initialize the model and track its status
|
|
32
|
+
this._isInitialized = false;
|
|
33
|
+
this._isInitializing = false;
|
|
34
|
+
this._initError = null;
|
|
35
|
+
void this._initializeModel();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Initialize the WebLLM model
|
|
39
|
+
*/
|
|
40
|
+
async _initializeModel() {
|
|
41
|
+
if (this._isInitialized || this._isInitializing) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this._isInitializing = true;
|
|
45
|
+
try {
|
|
46
|
+
await this._completer.initialize((progress) => {
|
|
47
|
+
console.log('WebLLM initialization progress:', progress);
|
|
48
|
+
});
|
|
49
|
+
this._isInitialized = true;
|
|
50
|
+
this._isInitializing = false;
|
|
51
|
+
console.log('WebLLM model successfully initialized');
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
this._initError =
|
|
55
|
+
error instanceof Error ? error : new Error(String(error));
|
|
56
|
+
this._isInitializing = false;
|
|
57
|
+
console.error('Failed to initialize WebLLM model:', error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Getter and setter for the initial prompt.
|
|
62
|
+
*/
|
|
63
|
+
get prompt() {
|
|
64
|
+
return this._prompt;
|
|
65
|
+
}
|
|
66
|
+
set prompt(value) {
|
|
67
|
+
this._prompt = value;
|
|
68
|
+
}
|
|
69
|
+
get provider() {
|
|
70
|
+
return this._completer;
|
|
71
|
+
}
|
|
72
|
+
async fetch(request, context) {
|
|
73
|
+
// Abort any pending request
|
|
74
|
+
if (this._abortController) {
|
|
75
|
+
this._abortController.abort();
|
|
76
|
+
}
|
|
77
|
+
// Create a new abort controller for this request
|
|
78
|
+
this._abortController = new AbortController();
|
|
79
|
+
const signal = this._abortController.signal;
|
|
80
|
+
if (!this._isInitialized) {
|
|
81
|
+
if (this._initError) {
|
|
82
|
+
console.error('WebLLM model failed to initialize:', this._initError);
|
|
83
|
+
return { items: [] };
|
|
84
|
+
}
|
|
85
|
+
if (!this._isInitializing) {
|
|
86
|
+
// Try to initialize again if it's not currently initializing
|
|
87
|
+
await this._initializeModel();
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.log('WebLLM model is still initializing, please try again later');
|
|
91
|
+
return { items: [] };
|
|
92
|
+
}
|
|
93
|
+
// Return empty if still not initialized
|
|
94
|
+
if (!this._isInitialized) {
|
|
95
|
+
return { items: [] };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const { text, offset: cursorOffset } = request;
|
|
99
|
+
const prompt = text.slice(0, cursorOffset);
|
|
100
|
+
const trimmedPrompt = prompt.trim();
|
|
101
|
+
const messages = [
|
|
102
|
+
new SystemMessage(this._prompt),
|
|
103
|
+
new HumanMessage(trimmedPrompt)
|
|
104
|
+
];
|
|
105
|
+
try {
|
|
106
|
+
console.log('Trigger invoke');
|
|
107
|
+
const response = await this._completer.invoke(messages, { signal });
|
|
108
|
+
let content = response.content;
|
|
109
|
+
console.log('Response content:', content);
|
|
110
|
+
if (CODE_BLOCK_START_REGEX.test(content)) {
|
|
111
|
+
content = content
|
|
112
|
+
.replace(CODE_BLOCK_START_REGEX, '')
|
|
113
|
+
.replace(CODE_BLOCK_END_REGEX, '');
|
|
114
|
+
}
|
|
115
|
+
const items = [{ insertText: content }];
|
|
116
|
+
return {
|
|
117
|
+
items
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
if (error instanceof Error) {
|
|
122
|
+
console.error('Error fetching completion from WebLLM:', error.message);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.error('Unknown error fetching completion from WebLLM:', error);
|
|
126
|
+
}
|
|
127
|
+
return { items: [] };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
_completer;
|
|
131
|
+
_prompt = COMPLETION_SYSTEM_PROMPT;
|
|
132
|
+
_isInitialized = false;
|
|
133
|
+
_isInitializing = false;
|
|
134
|
+
_initError = null;
|
|
135
|
+
_abortController = null;
|
|
136
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
declare const _default: "\nWebLLM enables running LLMs directly in your browser, making it possible to use AI features without sending data to external servers.\n\n<i class=\"fas fa-info-circle\"></i> WebLLM runs models entirely in your browser, so initial model download may be large (100MB-2GB depending on the model).\n\n<i class=\"fas fa-exclamation-triangle\"></i> <strong>Requirements:</strong> WebLLM requires a browser with WebGPU support (Chrome 113+, Edge 113+, or Safari 17+). It will not work on older browsers or browsers without WebGPU enabled.\n\n1. Enter a model in the JupyterLab settings under the **Ai providers** section. Select the `WebLLM` provider and type the model you want to use.\n2. When you first use WebLLM, your browser will download the model. A progress notification will appear:\n3. Once loaded, use the chat\n4. Example of available models:\n - Llama-3.2-1B-Instruct-q4f32_1-MLC\n - Mistral-7B-Instruct-v0.3-q4f32_1-MLC\n - Qwen3-0.6B-q4f32_1-MLC\n5. See the full list of models: https://github.com/mlc-ai/web-llm/blob/632d34725629b480b5b2772379ef5c150b1286f0/src/config.ts#L303-L309\n\n<i class=\"fas fa-exclamation-triangle\"></i> Model performance depends on your device's hardware capabilities. More powerful devices will run models faster. Some larger models may not work well on devices with limited GPU memory or may experience slow response times.\n";
|
|
2
|
+
export default _default;
|
|
3
|
+
/**
|
|
4
|
+
* Check if the browser supports WebLLM.
|
|
5
|
+
*/
|
|
6
|
+
export declare function compatibilityCheck(): Promise<string | null>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export default `
|
|
2
|
+
WebLLM enables running LLMs directly in your browser, making it possible to use AI features without sending data to external servers.
|
|
3
|
+
|
|
4
|
+
<i class="fas fa-info-circle"></i> WebLLM runs models entirely in your browser, so initial model download may be large (100MB-2GB depending on the model).
|
|
5
|
+
|
|
6
|
+
<i class="fas fa-exclamation-triangle"></i> <strong>Requirements:</strong> WebLLM requires a browser with WebGPU support (Chrome 113+, Edge 113+, or Safari 17+). It will not work on older browsers or browsers without WebGPU enabled.
|
|
7
|
+
|
|
8
|
+
1. Enter a model in the JupyterLab settings under the **Ai providers** section. Select the \`WebLLM\` provider and type the model you want to use.
|
|
9
|
+
2. When you first use WebLLM, your browser will download the model. A progress notification will appear:
|
|
10
|
+
3. Once loaded, use the chat
|
|
11
|
+
4. Example of available models:
|
|
12
|
+
- Llama-3.2-1B-Instruct-q4f32_1-MLC
|
|
13
|
+
- Mistral-7B-Instruct-v0.3-q4f32_1-MLC
|
|
14
|
+
- Qwen3-0.6B-q4f32_1-MLC
|
|
15
|
+
5. See the full list of models: https://github.com/mlc-ai/web-llm/blob/632d34725629b480b5b2772379ef5c150b1286f0/src/config.ts#L303-L309
|
|
16
|
+
|
|
17
|
+
<i class="fas fa-exclamation-triangle"></i> Model performance depends on your device's hardware capabilities. More powerful devices will run models faster. Some larger models may not work well on devices with limited GPU memory or may experience slow response times.
|
|
18
|
+
`;
|
|
19
|
+
/**
|
|
20
|
+
* Check if the browser supports WebLLM.
|
|
21
|
+
*/
|
|
22
|
+
export async function compatibilityCheck() {
|
|
23
|
+
// Check if the browser supports the ChromeAI model
|
|
24
|
+
if (typeof navigator === 'undefined' || !('gpu' in navigator)) {
|
|
25
|
+
return 'Your browser does not support WebLLM, it does not support required WebGPU.';
|
|
26
|
+
}
|
|
27
|
+
if ((await navigator.gpu.requestAdapter()) === null) {
|
|
28
|
+
return 'You may need to enable WebGPU, `await navigator.gpu.requestAdapter()` is null.';
|
|
29
|
+
}
|
|
30
|
+
// If the model is available, return null to indicate compatibility
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"disableStreaming": {
|
|
6
|
+
"type": "boolean",
|
|
7
|
+
"description": "Whether to disable streaming.\n\nIf streaming is bypassed, then `stream()` will defer to `invoke()`.\n\n- If true, will always bypass streaming case.\n- If false (default), will always use streaming case if available."
|
|
8
|
+
},
|
|
9
|
+
"temperature": {
|
|
10
|
+
"type": "number"
|
|
11
|
+
},
|
|
12
|
+
"model": {
|
|
13
|
+
"type": "string"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"required": [
|
|
17
|
+
"model"
|
|
18
|
+
],
|
|
19
|
+
"additionalProperties": false,
|
|
20
|
+
"definitions": {}
|
|
21
|
+
}
|
|
@@ -1,21 +1,31 @@
|
|
|
1
|
+
import { Notification } from '@jupyterlab/apputils';
|
|
1
2
|
import { ChatAnthropic } from '@langchain/anthropic';
|
|
3
|
+
import { ChatWebLLM } from '@langchain/community/chat_models/webllm';
|
|
2
4
|
import { ChromeAI } from '@langchain/community/experimental/llms/chrome_ai';
|
|
3
5
|
import { ChatMistralAI } from '@langchain/mistralai';
|
|
6
|
+
import { ChatOllama } from '@langchain/ollama';
|
|
4
7
|
import { ChatOpenAI } from '@langchain/openai';
|
|
5
|
-
import { IAIProviderRegistry } from '../tokens';
|
|
6
8
|
// Import completers
|
|
7
9
|
import { AnthropicCompleter } from './Anthropic/completer';
|
|
8
10
|
import { ChromeCompleter } from './ChromeAI/completer';
|
|
9
11
|
import { CodestralCompleter } from './MistralAI/completer';
|
|
12
|
+
import { OllamaCompleter } from './Ollama/completer';
|
|
10
13
|
import { OpenAICompleter } from './OpenAI/completer';
|
|
14
|
+
import { WebLLMCompleter } from './WebLLM/completer';
|
|
11
15
|
// Import Settings
|
|
12
16
|
import AnthropicSettings from './Anthropic/settings-schema.json';
|
|
13
17
|
import ChromeAISettings from './ChromeAI/settings-schema.json';
|
|
14
18
|
import MistralAISettings from './MistralAI/settings-schema.json';
|
|
19
|
+
import OllamaAISettings from './Ollama/settings-schema.json';
|
|
15
20
|
import OpenAISettings from './OpenAI/settings-schema.json';
|
|
21
|
+
import WebLLMSettings from './WebLLM/settings-schema.json';
|
|
16
22
|
// Import instructions
|
|
17
|
-
import ChromeAIInstructions from './ChromeAI/instructions';
|
|
23
|
+
import ChromeAIInstructions, { compatibilityCheck as chromeAICompatibilityCheck } from './ChromeAI/instructions';
|
|
18
24
|
import MistralAIInstructions from './MistralAI/instructions';
|
|
25
|
+
import OllamaInstructions from './Ollama/instructions';
|
|
26
|
+
import WebLLMInstructions, { compatibilityCheck as webLLMCompatibilityCheck } from './WebLLM/instructions';
|
|
27
|
+
import { prebuiltAppConfig } from '@mlc-ai/web-llm';
|
|
28
|
+
import { IAIProviderRegistry } from '../tokens';
|
|
19
29
|
// Build the AIProvider list
|
|
20
30
|
const AIProviders = [
|
|
21
31
|
{
|
|
@@ -32,7 +42,8 @@ const AIProviders = [
|
|
|
32
42
|
chatModel: ChromeAI,
|
|
33
43
|
completer: ChromeCompleter,
|
|
34
44
|
instructions: ChromeAIInstructions,
|
|
35
|
-
settingsSchema: ChromeAISettings
|
|
45
|
+
settingsSchema: ChromeAISettings,
|
|
46
|
+
compatibilityCheck: chromeAICompatibilityCheck
|
|
36
47
|
},
|
|
37
48
|
{
|
|
38
49
|
name: 'MistralAI',
|
|
@@ -41,6 +52,13 @@ const AIProviders = [
|
|
|
41
52
|
instructions: MistralAIInstructions,
|
|
42
53
|
settingsSchema: MistralAISettings
|
|
43
54
|
},
|
|
55
|
+
{
|
|
56
|
+
name: 'Ollama',
|
|
57
|
+
chatModel: ChatOllama,
|
|
58
|
+
completer: OllamaCompleter,
|
|
59
|
+
instructions: OllamaInstructions,
|
|
60
|
+
settingsSchema: OllamaAISettings
|
|
61
|
+
},
|
|
44
62
|
{
|
|
45
63
|
name: 'OpenAI',
|
|
46
64
|
chatModel: ChatOpenAI,
|
|
@@ -48,7 +66,100 @@ const AIProviders = [
|
|
|
48
66
|
settingsSchema: OpenAISettings
|
|
49
67
|
}
|
|
50
68
|
];
|
|
51
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Register the WebLLM provider in a separate plugin since it creates notifications
|
|
71
|
+
* when the model is changed in the settings.
|
|
72
|
+
*/
|
|
73
|
+
const webLLMProviderPlugin = {
|
|
74
|
+
id: '@jupyterlite/ai:webllm',
|
|
75
|
+
description: 'Register the WebLLM provider',
|
|
76
|
+
autoStart: true,
|
|
77
|
+
requires: [IAIProviderRegistry],
|
|
78
|
+
activate: (app, registry) => {
|
|
79
|
+
registry.add({
|
|
80
|
+
name: 'WebLLM',
|
|
81
|
+
chatModel: ChatWebLLM,
|
|
82
|
+
completer: WebLLMCompleter,
|
|
83
|
+
settingsSchema: WebLLMSettings,
|
|
84
|
+
instructions: WebLLMInstructions,
|
|
85
|
+
compatibilityCheck: webLLMCompatibilityCheck,
|
|
86
|
+
exposeChatModel: true
|
|
87
|
+
});
|
|
88
|
+
registry.providerChanged.connect(async (sender, args) => {
|
|
89
|
+
const { currentName, currentChatModel, chatError } = registry;
|
|
90
|
+
if (currentChatModel === null) {
|
|
91
|
+
Notification.emit(chatError, 'error', {
|
|
92
|
+
autoClose: 2000
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// TODO: implement a proper way to handle models that may need to be initialized before being used.
|
|
97
|
+
// Mostly applies to WebLLM and ChromeAI as they may need to download the model in the browser first.
|
|
98
|
+
if (currentName === 'WebLLM') {
|
|
99
|
+
const compatibilityError = await webLLMCompatibilityCheck();
|
|
100
|
+
if (compatibilityError) {
|
|
101
|
+
Notification.dismiss();
|
|
102
|
+
Notification.emit(compatibilityError, 'error', {
|
|
103
|
+
autoClose: 2000
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const model = currentChatModel;
|
|
108
|
+
if (model === null || !model.model) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Find if the model is part of the prebuiltAppConfig
|
|
112
|
+
const modelRecord = prebuiltAppConfig.model_list.find(modelRecord => modelRecord.model_id === model.model);
|
|
113
|
+
if (!modelRecord) {
|
|
114
|
+
Notification.dismiss();
|
|
115
|
+
Notification.emit(`Model ${model.model} not found in the prebuiltAppConfig`, 'error', {
|
|
116
|
+
autoClose: 2000
|
|
117
|
+
});
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// create a notification
|
|
121
|
+
const notification = Notification.emit('Loading model...', 'in-progress', {
|
|
122
|
+
autoClose: false,
|
|
123
|
+
progress: 0
|
|
124
|
+
});
|
|
125
|
+
try {
|
|
126
|
+
void model.initialize(report => {
|
|
127
|
+
const { progress, text } = report;
|
|
128
|
+
if (progress === 1) {
|
|
129
|
+
Notification.update({
|
|
130
|
+
id: notification,
|
|
131
|
+
progress: 1,
|
|
132
|
+
message: `Model ${model.model} loaded successfully`,
|
|
133
|
+
type: 'success',
|
|
134
|
+
autoClose: 2000
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
Notification.update({
|
|
139
|
+
id: notification,
|
|
140
|
+
progress: progress / 1,
|
|
141
|
+
message: text,
|
|
142
|
+
type: 'in-progress'
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
Notification.update({
|
|
148
|
+
id: notification,
|
|
149
|
+
progress: 1,
|
|
150
|
+
message: `Error loading model ${model.model}`,
|
|
151
|
+
type: 'error',
|
|
152
|
+
autoClose: 2000
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Register all default AI providers.
|
|
161
|
+
*/
|
|
162
|
+
const aiProviderPlugins = AIProviders.map(provider => {
|
|
52
163
|
return {
|
|
53
164
|
id: `@jupyterlite/ai:${provider.name}`,
|
|
54
165
|
autoStart: true,
|
|
@@ -58,3 +169,7 @@ export const defaultProviderPlugins = AIProviders.map(provider => {
|
|
|
58
169
|
}
|
|
59
170
|
};
|
|
60
171
|
});
|
|
172
|
+
export const defaultProviderPlugins = [
|
|
173
|
+
webLLMProviderPlugin,
|
|
174
|
+
...aiProviderPlugins
|
|
175
|
+
];
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IChatCommandRegistry } from '@jupyter/chat';
|
|
2
2
|
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
3
|
-
import { ISettingConnector } from '@jupyterlab/settingregistry';
|
|
4
3
|
import { IAIProviderRegistry } from './tokens';
|
|
5
|
-
declare const _default: (JupyterFrontEndPlugin<void> | JupyterFrontEndPlugin<IChatCommandRegistry> | JupyterFrontEndPlugin<IAIProviderRegistry>
|
|
4
|
+
declare const _default: (JupyterFrontEndPlugin<void> | JupyterFrontEndPlugin<IChatCommandRegistry> | JupyterFrontEndPlugin<IAIProviderRegistry>)[];
|
|
6
5
|
export default _default;
|
|
6
|
+
export { IAIProviderRegistry } from './tokens';
|