@jupyterlite/ai 0.6.2 → 0.8.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 +22 -5
- package/lib/base-completer.js +14 -1
- package/lib/chat-handler.d.ts +23 -11
- package/lib/chat-handler.js +66 -45
- package/lib/completion-provider.d.ts +2 -2
- package/lib/completion-provider.js +5 -4
- package/lib/components/stop-button.d.ts +0 -1
- package/lib/default-prompts.d.ts +2 -0
- package/lib/default-prompts.js +31 -0
- package/lib/default-providers/Anthropic/completer.d.ts +4 -11
- package/lib/default-providers/Anthropic/completer.js +5 -16
- package/lib/default-providers/ChromeAI/completer.d.ts +4 -11
- package/lib/default-providers/ChromeAI/completer.js +5 -16
- package/lib/default-providers/ChromeAI/instructions.d.ts +4 -0
- package/lib/default-providers/ChromeAI/instructions.js +18 -0
- package/lib/default-providers/ChromeAI/settings-schema.json +0 -3
- package/lib/default-providers/Gemini/completer.d.ts +12 -0
- package/lib/default-providers/Gemini/completer.js +48 -0
- package/lib/default-providers/Gemini/instructions.d.ts +2 -0
- package/lib/default-providers/Gemini/instructions.js +9 -0
- package/lib/default-providers/Gemini/settings-schema.json +64 -0
- package/lib/default-providers/MistralAI/completer.d.ts +10 -13
- package/lib/default-providers/MistralAI/completer.js +42 -52
- package/lib/default-providers/MistralAI/instructions.d.ts +1 -1
- package/lib/default-providers/MistralAI/instructions.js +2 -0
- package/lib/default-providers/Ollama/completer.d.ts +12 -0
- package/lib/default-providers/Ollama/completer.js +43 -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 +143 -0
- package/lib/default-providers/OpenAI/completer.d.ts +4 -11
- package/lib/default-providers/OpenAI/completer.js +8 -16
- package/lib/default-providers/OpenAI/settings-schema.json +88 -128
- package/lib/default-providers/WebLLM/completer.d.ts +21 -0
- package/lib/default-providers/WebLLM/completer.js +127 -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 +19 -0
- package/lib/default-providers/index.js +127 -8
- package/lib/index.d.ts +3 -2
- package/lib/index.js +80 -36
- package/lib/provider.d.ts +48 -22
- package/lib/provider.js +254 -101
- package/lib/settings/index.d.ts +1 -1
- package/lib/settings/index.js +1 -1
- package/lib/settings/panel.d.ts +151 -14
- package/lib/settings/panel.js +334 -145
- package/lib/settings/textarea.d.ts +2 -0
- package/lib/settings/textarea.js +18 -0
- package/lib/tokens.d.ts +45 -22
- package/lib/tokens.js +2 -1
- package/lib/types/ai-model.d.ts +24 -0
- package/lib/types/ai-model.js +5 -0
- package/package.json +19 -15
- package/schema/chat.json +1 -1
- package/schema/provider-registry.json +8 -8
- package/schema/system-prompts.json +22 -0
- package/src/base-completer.ts +38 -6
- package/src/chat-handler.ts +62 -31
- package/src/completion-provider.ts +3 -3
- package/src/default-prompts.ts +33 -0
- package/src/default-providers/Anthropic/completer.ts +5 -21
- package/src/default-providers/ChromeAI/completer.ts +5 -21
- package/src/default-providers/ChromeAI/instructions.ts +21 -0
- package/src/default-providers/Gemini/completer.ts +61 -0
- package/src/default-providers/Gemini/instructions.ts +9 -0
- package/src/default-providers/MistralAI/completer.ts +47 -65
- package/src/default-providers/MistralAI/instructions.ts +2 -0
- package/src/default-providers/Ollama/completer.ts +54 -0
- package/src/default-providers/Ollama/instructions.ts +70 -0
- package/src/default-providers/OpenAI/completer.ts +8 -21
- package/src/default-providers/WebLLM/completer.ts +151 -0
- package/src/default-providers/WebLLM/instructions.ts +33 -0
- package/src/default-providers/index.ts +158 -18
- package/src/index.ts +108 -40
- package/src/provider.ts +300 -109
- package/src/settings/index.ts +1 -1
- package/src/settings/panel.tsx +463 -101
- package/src/settings/textarea.tsx +33 -0
- package/src/tokens.ts +49 -24
- package/src/types/ai-model.ts +37 -0
- package/src/types/service-worker.d.ts +6 -0
- package/style/base.css +34 -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 -88
|
@@ -3,31 +3,16 @@ import {
|
|
|
3
3
|
IInlineCompletionContext
|
|
4
4
|
} from '@jupyterlab/completer';
|
|
5
5
|
import { ChatAnthropic } from '@langchain/anthropic';
|
|
6
|
-
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
7
6
|
import { AIMessage, SystemMessage } from '@langchain/core/messages';
|
|
8
7
|
|
|
9
|
-
import { BaseCompleter
|
|
10
|
-
import { COMPLETION_SYSTEM_PROMPT } from '../../provider';
|
|
8
|
+
import { BaseCompleter } from '../../base-completer';
|
|
11
9
|
|
|
12
|
-
export class AnthropicCompleter
|
|
10
|
+
export class AnthropicCompleter extends BaseCompleter {
|
|
13
11
|
constructor(options: BaseCompleter.IOptions) {
|
|
12
|
+
super(options);
|
|
14
13
|
this._completer = new ChatAnthropic({ ...options.settings });
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
get completer(): BaseChatModel {
|
|
18
|
-
return this._completer;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Getter and setter for the initial prompt.
|
|
23
|
-
*/
|
|
24
|
-
get prompt(): string {
|
|
25
|
-
return this._prompt;
|
|
26
|
-
}
|
|
27
|
-
set prompt(value: string) {
|
|
28
|
-
this._prompt = value;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
16
|
async fetch(
|
|
32
17
|
request: CompletionHandler.IRequest,
|
|
33
18
|
context: IInlineCompletionContext
|
|
@@ -39,7 +24,7 @@ export class AnthropicCompleter implements IBaseCompleter {
|
|
|
39
24
|
const trimmedPrompt = prompt.trim();
|
|
40
25
|
|
|
41
26
|
const messages = [
|
|
42
|
-
new SystemMessage(this.
|
|
27
|
+
new SystemMessage(this.systemPrompt),
|
|
43
28
|
new AIMessage(trimmedPrompt)
|
|
44
29
|
];
|
|
45
30
|
|
|
@@ -70,6 +55,5 @@ export class AnthropicCompleter implements IBaseCompleter {
|
|
|
70
55
|
}
|
|
71
56
|
}
|
|
72
57
|
|
|
73
|
-
|
|
74
|
-
private _prompt: string = COMPLETION_SYSTEM_PROMPT;
|
|
58
|
+
protected _completer: ChatAnthropic;
|
|
75
59
|
}
|
|
@@ -3,11 +3,9 @@ import {
|
|
|
3
3
|
IInlineCompletionContext
|
|
4
4
|
} from '@jupyterlab/completer';
|
|
5
5
|
import { ChromeAI } from '@langchain/community/experimental/llms/chrome_ai';
|
|
6
|
-
import { LLM } from '@langchain/core/language_models/llms';
|
|
7
6
|
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
|
|
8
7
|
|
|
9
|
-
import { BaseCompleter
|
|
10
|
-
import { COMPLETION_SYSTEM_PROMPT } from '../../provider';
|
|
8
|
+
import { BaseCompleter } from '../../base-completer';
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
11
|
* Regular expression to match the '```' string at the start of a string.
|
|
@@ -30,25 +28,12 @@ const CODE_BLOCK_START_REGEX = /^```(?:[a-zA-Z]+)?\n?/;
|
|
|
30
28
|
*/
|
|
31
29
|
const CODE_BLOCK_END_REGEX = /```$/;
|
|
32
30
|
|
|
33
|
-
export class ChromeCompleter
|
|
31
|
+
export class ChromeCompleter extends BaseCompleter {
|
|
34
32
|
constructor(options: BaseCompleter.IOptions) {
|
|
33
|
+
super(options);
|
|
35
34
|
this._completer = new ChromeAI({ ...options.settings });
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
/**
|
|
39
|
-
* Getter and setter for the initial prompt.
|
|
40
|
-
*/
|
|
41
|
-
get prompt(): string {
|
|
42
|
-
return this._prompt;
|
|
43
|
-
}
|
|
44
|
-
set prompt(value: string) {
|
|
45
|
-
this._prompt = value;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
get completer(): LLM {
|
|
49
|
-
return this._completer;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
37
|
async fetch(
|
|
53
38
|
request: CompletionHandler.IRequest,
|
|
54
39
|
context: IInlineCompletionContext
|
|
@@ -59,7 +44,7 @@ export class ChromeCompleter implements IBaseCompleter {
|
|
|
59
44
|
const trimmedPrompt = prompt.trim();
|
|
60
45
|
|
|
61
46
|
const messages = [
|
|
62
|
-
new SystemMessage(this.
|
|
47
|
+
new SystemMessage(this.systemPrompt),
|
|
63
48
|
new HumanMessage(trimmedPrompt)
|
|
64
49
|
];
|
|
65
50
|
|
|
@@ -84,6 +69,5 @@ export class ChromeCompleter implements IBaseCompleter {
|
|
|
84
69
|
}
|
|
85
70
|
}
|
|
86
71
|
|
|
87
|
-
|
|
88
|
-
private _prompt: string = COMPLETION_SYSTEM_PROMPT;
|
|
72
|
+
protected _completer: ChromeAI;
|
|
89
73
|
}
|
|
@@ -22,3 +22,24 @@ During the download, ChromeAI may not be available via the extension.
|
|
|
22
22
|
|
|
23
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
24
|
`;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if the browser supports ChromeAI and the model is available.
|
|
28
|
+
*/
|
|
29
|
+
export async function compatibilityCheck(): Promise<string | null> {
|
|
30
|
+
// Check if the browser supports the ChromeAI model
|
|
31
|
+
if (
|
|
32
|
+
typeof window === 'undefined' ||
|
|
33
|
+
!('LanguageModel' in window) ||
|
|
34
|
+
window.LanguageModel === undefined ||
|
|
35
|
+
(window.LanguageModel as any).availability === undefined
|
|
36
|
+
) {
|
|
37
|
+
return 'Your browser does not support ChromeAI. Please use an updated chrome based browser like Google Chrome, and follow the instructions in settings to enable it.';
|
|
38
|
+
}
|
|
39
|
+
const languageModel = window.LanguageModel as any;
|
|
40
|
+
if (!(await languageModel.availability())) {
|
|
41
|
+
return 'The ChromeAI model is not available in your browser. Please ensure you have enabled the necessary flags in Google Chrome as described in the instructions in settings.';
|
|
42
|
+
}
|
|
43
|
+
// If the model is available, return null to indicate compatibility
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CompletionHandler,
|
|
3
|
+
IInlineCompletionContext
|
|
4
|
+
} from '@jupyterlab/completer';
|
|
5
|
+
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
|
|
6
|
+
import { AIMessage, SystemMessage } from '@langchain/core/messages';
|
|
7
|
+
|
|
8
|
+
import { BaseCompleter } from '../../base-completer';
|
|
9
|
+
|
|
10
|
+
export class GeminiCompleter extends BaseCompleter {
|
|
11
|
+
constructor(options: BaseCompleter.IOptions) {
|
|
12
|
+
super(options);
|
|
13
|
+
this._completer = new ChatGoogleGenerativeAI({
|
|
14
|
+
model: 'gemini-pro',
|
|
15
|
+
...options.settings
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async fetch(
|
|
20
|
+
request: CompletionHandler.IRequest,
|
|
21
|
+
context: IInlineCompletionContext
|
|
22
|
+
) {
|
|
23
|
+
const { text, offset: cursorOffset } = request;
|
|
24
|
+
const prompt = text.slice(0, cursorOffset);
|
|
25
|
+
|
|
26
|
+
const trimmedPrompt = prompt.trim();
|
|
27
|
+
|
|
28
|
+
const messages = [
|
|
29
|
+
new SystemMessage(this.systemPrompt),
|
|
30
|
+
new AIMessage(trimmedPrompt)
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const response = await this._completer.invoke(messages);
|
|
35
|
+
const items = [];
|
|
36
|
+
|
|
37
|
+
// Gemini can return string or complex content, a list of string/images/other.
|
|
38
|
+
if (typeof response.content === 'string') {
|
|
39
|
+
items.push({
|
|
40
|
+
insertText: response.content
|
|
41
|
+
});
|
|
42
|
+
} else {
|
|
43
|
+
response.content.forEach(content => {
|
|
44
|
+
if (content.type !== 'text') {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
items.push({
|
|
48
|
+
insertText: content.text,
|
|
49
|
+
filterText: prompt.substring(trimmedPrompt.length)
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return { items };
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error('Error fetching completions', error);
|
|
56
|
+
return { items: [] };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected _completer: ChatGoogleGenerativeAI;
|
|
61
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export default `
|
|
2
|
+
<i class="fas fa-exclamation-triangle"></i> This extension is still very much experimental. It is not an official Google extension.
|
|
3
|
+
|
|
4
|
+
1. Go to <https://aistudio.google.com> and create an API key.
|
|
5
|
+
|
|
6
|
+
2. Open the JupyterLab settings and go to the **Ai providers** section to select the \`Gemini\`
|
|
7
|
+
provider and add your API key (required).
|
|
8
|
+
3. Open the chat, or use the inline completer.
|
|
9
|
+
`;
|
|
@@ -2,86 +2,68 @@ import {
|
|
|
2
2
|
CompletionHandler,
|
|
3
3
|
IInlineCompletionContext
|
|
4
4
|
} from '@jupyterlab/completer';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
BaseMessage,
|
|
8
|
-
HumanMessage,
|
|
9
|
-
SystemMessage
|
|
10
|
-
} from '@langchain/core/messages';
|
|
11
|
-
import { ChatMistralAI } from '@langchain/mistralai';
|
|
12
|
-
import { Throttler } from '@lumino/polling';
|
|
5
|
+
import { MistralAI } from '@langchain/mistralai';
|
|
13
6
|
|
|
14
|
-
import { BaseCompleter
|
|
15
|
-
import { COMPLETION_SYSTEM_PROMPT } from '../../provider';
|
|
7
|
+
import { BaseCompleter } from '../../base-completer';
|
|
16
8
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*/
|
|
20
|
-
const INTERVAL = 1000;
|
|
9
|
+
const CODE_BLOCK_START_REGEX = /^```(?:[a-zA-Z]+)?\n?/;
|
|
10
|
+
const CODE_BLOCK_END_REGEX = /```$/;
|
|
21
11
|
|
|
22
|
-
export class CodestralCompleter
|
|
12
|
+
export class CodestralCompleter extends BaseCompleter {
|
|
23
13
|
constructor(options: BaseCompleter.IOptions) {
|
|
24
|
-
|
|
25
|
-
this.
|
|
26
|
-
async (messages: BaseMessage[]) => {
|
|
27
|
-
const response = await this._completer.invoke(messages);
|
|
28
|
-
// Extract results of completion request.
|
|
29
|
-
const items = [];
|
|
30
|
-
if (typeof response.content === 'string') {
|
|
31
|
-
items.push({
|
|
32
|
-
insertText: response.content
|
|
33
|
-
});
|
|
34
|
-
} else {
|
|
35
|
-
response.content.forEach(content => {
|
|
36
|
-
if (content.type !== 'text') {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
items.push({
|
|
40
|
-
insertText: content.text
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
return { items };
|
|
45
|
-
},
|
|
46
|
-
{ limit: INTERVAL }
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
get completer(): BaseChatModel {
|
|
51
|
-
return this._completer;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Getter and setter for the initial prompt.
|
|
56
|
-
*/
|
|
57
|
-
get prompt(): string {
|
|
58
|
-
return this._prompt;
|
|
59
|
-
}
|
|
60
|
-
set prompt(value: string) {
|
|
61
|
-
this._prompt = value;
|
|
14
|
+
super(options);
|
|
15
|
+
this._completer = new MistralAI({ ...options.settings });
|
|
62
16
|
}
|
|
63
17
|
|
|
64
18
|
async fetch(
|
|
65
19
|
request: CompletionHandler.IRequest,
|
|
66
20
|
context: IInlineCompletionContext
|
|
67
21
|
) {
|
|
68
|
-
|
|
69
|
-
|
|
22
|
+
try {
|
|
23
|
+
const { text, offset: cursorOffset } = request;
|
|
24
|
+
const prompt = this.systemPrompt.concat(text.slice(0, cursorOffset));
|
|
25
|
+
const suffix = text.slice(cursorOffset);
|
|
26
|
+
this._controller.abort();
|
|
27
|
+
this._controller = new AbortController();
|
|
70
28
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
29
|
+
const response = await this._completer.completionWithRetry(
|
|
30
|
+
{
|
|
31
|
+
prompt,
|
|
32
|
+
model: this._completer.model,
|
|
33
|
+
suffix
|
|
34
|
+
},
|
|
35
|
+
{ signal: this._controller.signal },
|
|
36
|
+
false
|
|
37
|
+
);
|
|
38
|
+
const items = response.choices.map(choice => {
|
|
39
|
+
const messageContent = choice.message.content;
|
|
40
|
+
let content = '';
|
|
75
41
|
|
|
76
|
-
|
|
77
|
-
|
|
42
|
+
if (typeof messageContent === 'string') {
|
|
43
|
+
content = messageContent
|
|
44
|
+
.replace(CODE_BLOCK_START_REGEX, '')
|
|
45
|
+
.replace(CODE_BLOCK_END_REGEX, '');
|
|
46
|
+
} else if (Array.isArray(messageContent)) {
|
|
47
|
+
// Handle ContentChunk[] case - extract text content
|
|
48
|
+
content = messageContent
|
|
49
|
+
.filter(chunk => chunk.type === 'text')
|
|
50
|
+
.map(chunk => chunk.text || '')
|
|
51
|
+
.join('')
|
|
52
|
+
.replace(CODE_BLOCK_START_REGEX, '')
|
|
53
|
+
.replace(CODE_BLOCK_END_REGEX, '');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
insertText: content
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
return { items };
|
|
78
61
|
} catch (error) {
|
|
79
|
-
|
|
62
|
+
// the request may be aborted
|
|
80
63
|
return { items: [] };
|
|
81
64
|
}
|
|
82
65
|
}
|
|
83
66
|
|
|
84
|
-
private
|
|
85
|
-
|
|
86
|
-
private _prompt: string = COMPLETION_SYSTEM_PROMPT;
|
|
67
|
+
private _controller = new AbortController();
|
|
68
|
+
protected _completer: MistralAI;
|
|
87
69
|
}
|
|
@@ -10,6 +10,8 @@ export default `
|
|
|
10
10
|
|
|
11
11
|
<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">
|
|
12
12
|
|
|
13
|
+
**Note:** When using MistralAI for completions, only a subset of models are available. Please check [this resource](https://docs.mistral.ai/api/#tag/fim) to see the list of supported models for completions.
|
|
14
|
+
|
|
13
15
|
3. Open the chat, or use the inline completer
|
|
14
16
|
|
|
15
17
|
<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">
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CompletionHandler,
|
|
3
|
+
IInlineCompletionContext
|
|
4
|
+
} from '@jupyterlab/completer';
|
|
5
|
+
import { AIMessage, SystemMessage } from '@langchain/core/messages';
|
|
6
|
+
import { ChatOllama } from '@langchain/ollama';
|
|
7
|
+
|
|
8
|
+
import { BaseCompleter } from '../../base-completer';
|
|
9
|
+
|
|
10
|
+
export class OllamaCompleter extends BaseCompleter {
|
|
11
|
+
constructor(options: BaseCompleter.IOptions) {
|
|
12
|
+
super(options);
|
|
13
|
+
this._completer = new ChatOllama({ ...options.settings });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async fetch(
|
|
17
|
+
request: CompletionHandler.IRequest,
|
|
18
|
+
context: IInlineCompletionContext
|
|
19
|
+
) {
|
|
20
|
+
const { text, offset: cursorOffset } = request;
|
|
21
|
+
const prompt = text.slice(0, cursorOffset);
|
|
22
|
+
|
|
23
|
+
const messages = [
|
|
24
|
+
new SystemMessage(this.systemPrompt),
|
|
25
|
+
new AIMessage(prompt)
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const response = await this._completer.invoke(messages);
|
|
30
|
+
const items = [];
|
|
31
|
+
if (typeof response.content === 'string') {
|
|
32
|
+
items.push({
|
|
33
|
+
insertText: response.content
|
|
34
|
+
});
|
|
35
|
+
} else {
|
|
36
|
+
response.content.forEach(content => {
|
|
37
|
+
if (content.type !== 'text') {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
items.push({
|
|
41
|
+
insertText: content.text,
|
|
42
|
+
filterText: prompt.substring(prompt.length)
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return { items };
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error('Error fetching completions', error);
|
|
49
|
+
return { items: [] };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected _completer: ChatOllama;
|
|
54
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export default `
|
|
2
|
+
Ollama allows to run large language models locally on your machine.
|
|
3
|
+
To use it you need to install the Ollama CLI and pull the model you want to use.
|
|
4
|
+
|
|
5
|
+
1. Install the Ollama CLI by following the instructions at <https://ollama.com/download>
|
|
6
|
+
|
|
7
|
+
2. Pull the model you want to use by running the following command in your terminal:
|
|
8
|
+
|
|
9
|
+
\`\`\`bash
|
|
10
|
+
ollama pull <model-name>
|
|
11
|
+
\`\`\`
|
|
12
|
+
|
|
13
|
+
For example, to pull the Llama 2 model, run:
|
|
14
|
+
|
|
15
|
+
\`\`\`bash
|
|
16
|
+
ollama pull llama2
|
|
17
|
+
\`\`\`
|
|
18
|
+
|
|
19
|
+
3. Once the model is pulled, you can use it in your application by running the following command:
|
|
20
|
+
|
|
21
|
+
\`\`\`bash
|
|
22
|
+
ollama serve
|
|
23
|
+
\`\`\`
|
|
24
|
+
|
|
25
|
+
4. This model will be available in the extension, using the model name you used in the command above.
|
|
26
|
+
|
|
27
|
+
<details>
|
|
28
|
+
<summary>Deploying Lite/Lab on external server</summary>
|
|
29
|
+
|
|
30
|
+
See https://objectgraph.com/blog/ollama-cors/ for more details.
|
|
31
|
+
|
|
32
|
+
On Linux, you can run the following commands:
|
|
33
|
+
|
|
34
|
+
1. Check if CORS is enabled on the server. You can do this by running the following command in your terminal:
|
|
35
|
+
|
|
36
|
+
\`\`\`bash
|
|
37
|
+
curl -X OPTIONS http://localhost:11434 -H "Origin: http://example.com" -H "Access-Control-Request-Method: GET" -I
|
|
38
|
+
\`\`\`
|
|
39
|
+
|
|
40
|
+
If CORS is disabled, you will see a response like this:
|
|
41
|
+
|
|
42
|
+
\`\`\`bash
|
|
43
|
+
HTTP/1.1 403 Forbidden
|
|
44
|
+
Date: Wed, 09 Oct 2024 10:12:15 GMT
|
|
45
|
+
Content-Length: 0
|
|
46
|
+
\`\`\`
|
|
47
|
+
|
|
48
|
+
2. If CORS is not enabled, update _/etc/systemd/system/ollama.service_ with:
|
|
49
|
+
|
|
50
|
+
\`\`\`bash
|
|
51
|
+
[Service]
|
|
52
|
+
Environment="OLLAMA_HOST=0.0.0.0"
|
|
53
|
+
Environment="OLLAMA_ORIGINS=*"
|
|
54
|
+
\`\`\`
|
|
55
|
+
|
|
56
|
+
3. Restart the service:
|
|
57
|
+
|
|
58
|
+
\`\`\`bash
|
|
59
|
+
sudo systemctl daemon-reload
|
|
60
|
+
sudo systemctl restart ollama
|
|
61
|
+
\`\`\`
|
|
62
|
+
|
|
63
|
+
4. Check if CORS is enabled on the server again by running the following command in your terminal:
|
|
64
|
+
|
|
65
|
+
\`\`\`bash
|
|
66
|
+
curl -X OPTIONS http://localhost:11434 -H "Origin: http://example.com" -H "Access-Control-Request-Method: GET" -I
|
|
67
|
+
\`\`\`
|
|
68
|
+
|
|
69
|
+
</details>
|
|
70
|
+
`;
|
|
@@ -2,32 +2,17 @@ import {
|
|
|
2
2
|
CompletionHandler,
|
|
3
3
|
IInlineCompletionContext
|
|
4
4
|
} from '@jupyterlab/completer';
|
|
5
|
-
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
6
5
|
import { AIMessage, SystemMessage } from '@langchain/core/messages';
|
|
7
6
|
import { ChatOpenAI } from '@langchain/openai';
|
|
8
7
|
|
|
9
|
-
import { BaseCompleter
|
|
10
|
-
import { COMPLETION_SYSTEM_PROMPT } from '../../provider';
|
|
8
|
+
import { BaseCompleter } from '../../base-completer';
|
|
11
9
|
|
|
12
|
-
export class OpenAICompleter
|
|
10
|
+
export class OpenAICompleter extends BaseCompleter {
|
|
13
11
|
constructor(options: BaseCompleter.IOptions) {
|
|
12
|
+
super(options);
|
|
14
13
|
this._completer = new ChatOpenAI({ ...options.settings });
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
get completer(): BaseChatModel {
|
|
18
|
-
return this._completer;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Getter and setter for the initial prompt.
|
|
23
|
-
*/
|
|
24
|
-
get prompt(): string {
|
|
25
|
-
return this._prompt;
|
|
26
|
-
}
|
|
27
|
-
set prompt(value: string) {
|
|
28
|
-
this._prompt = value;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
16
|
async fetch(
|
|
32
17
|
request: CompletionHandler.IRequest,
|
|
33
18
|
context: IInlineCompletionContext
|
|
@@ -35,7 +20,10 @@ export class OpenAICompleter implements IBaseCompleter {
|
|
|
35
20
|
const { text, offset: cursorOffset } = request;
|
|
36
21
|
const prompt = text.slice(0, cursorOffset);
|
|
37
22
|
|
|
38
|
-
const messages = [
|
|
23
|
+
const messages = [
|
|
24
|
+
new SystemMessage(this.systemPrompt),
|
|
25
|
+
new AIMessage(prompt)
|
|
26
|
+
];
|
|
39
27
|
|
|
40
28
|
try {
|
|
41
29
|
const response = await this._completer.invoke(messages);
|
|
@@ -62,6 +50,5 @@ export class OpenAICompleter implements IBaseCompleter {
|
|
|
62
50
|
}
|
|
63
51
|
}
|
|
64
52
|
|
|
65
|
-
|
|
66
|
-
private _prompt: string = COMPLETION_SYSTEM_PROMPT;
|
|
53
|
+
protected _completer: ChatOpenAI;
|
|
67
54
|
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CompletionHandler,
|
|
3
|
+
IInlineCompletionContext
|
|
4
|
+
} from '@jupyterlab/completer';
|
|
5
|
+
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
|
|
6
|
+
import { ChatWebLLM } from '@langchain/community/chat_models/webllm';
|
|
7
|
+
|
|
8
|
+
import { BaseCompleter } from '../../base-completer';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Regular expression to match the '```' string at the start of a string.
|
|
12
|
+
* So the completions returned by the LLM can still be kept after removing the code block formatting.
|
|
13
|
+
*
|
|
14
|
+
* For example, if the response contains the following content after typing `import pandas`:
|
|
15
|
+
*
|
|
16
|
+
* ```python
|
|
17
|
+
* as pd
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* The formatting string after removing the code block delimiters will be:
|
|
21
|
+
*
|
|
22
|
+
* as pd
|
|
23
|
+
*/
|
|
24
|
+
const CODE_BLOCK_START_REGEX = /^```(?:[a-zA-Z]+)?\n?/;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Regular expression to match the '```' string at the end of a string.
|
|
28
|
+
*/
|
|
29
|
+
const CODE_BLOCK_END_REGEX = /```$/;
|
|
30
|
+
|
|
31
|
+
export class WebLLMCompleter extends BaseCompleter {
|
|
32
|
+
constructor(options: BaseCompleter.IOptions) {
|
|
33
|
+
super(options);
|
|
34
|
+
const model = options.settings.model as string;
|
|
35
|
+
// provide model separately since ChatWebLLM expects it
|
|
36
|
+
this._completer = new ChatWebLLM({
|
|
37
|
+
...options.settings,
|
|
38
|
+
model
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Initialize the model and track its status
|
|
42
|
+
this._isInitialized = false;
|
|
43
|
+
this._isInitializing = false;
|
|
44
|
+
this._initError = null;
|
|
45
|
+
void this._initializeModel();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Initialize the WebLLM model
|
|
50
|
+
*/
|
|
51
|
+
private async _initializeModel(): Promise<void> {
|
|
52
|
+
if (this._isInitialized || this._isInitializing) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this._isInitializing = true;
|
|
57
|
+
try {
|
|
58
|
+
await this._completer.initialize((progress: any) => {
|
|
59
|
+
console.log('WebLLM initialization progress:', progress);
|
|
60
|
+
});
|
|
61
|
+
this._isInitialized = true;
|
|
62
|
+
this._isInitializing = false;
|
|
63
|
+
console.log('WebLLM model successfully initialized');
|
|
64
|
+
} catch (error) {
|
|
65
|
+
this._initError =
|
|
66
|
+
error instanceof Error ? error : new Error(String(error));
|
|
67
|
+
this._isInitializing = false;
|
|
68
|
+
console.error('Failed to initialize WebLLM model:', error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get provider(): ChatWebLLM {
|
|
73
|
+
return this._completer;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async fetch(
|
|
77
|
+
request: CompletionHandler.IRequest,
|
|
78
|
+
context: IInlineCompletionContext
|
|
79
|
+
) {
|
|
80
|
+
// Abort any pending request
|
|
81
|
+
if (this._abortController) {
|
|
82
|
+
this._abortController.abort();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Create a new abort controller for this request
|
|
86
|
+
this._abortController = new AbortController();
|
|
87
|
+
const signal = this._abortController.signal;
|
|
88
|
+
|
|
89
|
+
if (!this._isInitialized) {
|
|
90
|
+
if (this._initError) {
|
|
91
|
+
console.error('WebLLM model failed to initialize:', this._initError);
|
|
92
|
+
return { items: [] };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!this._isInitializing) {
|
|
96
|
+
// Try to initialize again if it's not currently initializing
|
|
97
|
+
await this._initializeModel();
|
|
98
|
+
} else {
|
|
99
|
+
console.log(
|
|
100
|
+
'WebLLM model is still initializing, please try again later'
|
|
101
|
+
);
|
|
102
|
+
return { items: [] };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Return empty if still not initialized
|
|
106
|
+
if (!this._isInitialized) {
|
|
107
|
+
return { items: [] };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const { text, offset: cursorOffset } = request;
|
|
112
|
+
const prompt = text.slice(0, cursorOffset);
|
|
113
|
+
const trimmedPrompt = prompt.trim();
|
|
114
|
+
|
|
115
|
+
const messages = [
|
|
116
|
+
new SystemMessage(this.systemPrompt),
|
|
117
|
+
new HumanMessage(trimmedPrompt)
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
console.log('Trigger invoke');
|
|
122
|
+
const response = await this._completer.invoke(messages, { signal });
|
|
123
|
+
let content = response.content as string;
|
|
124
|
+
console.log('Response content:', content);
|
|
125
|
+
|
|
126
|
+
if (CODE_BLOCK_START_REGEX.test(content)) {
|
|
127
|
+
content = content
|
|
128
|
+
.replace(CODE_BLOCK_START_REGEX, '')
|
|
129
|
+
.replace(CODE_BLOCK_END_REGEX, '');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const items = [{ insertText: content }];
|
|
133
|
+
return {
|
|
134
|
+
items
|
|
135
|
+
};
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (error instanceof Error) {
|
|
138
|
+
console.error('Error fetching completion from WebLLM:', error.message);
|
|
139
|
+
} else {
|
|
140
|
+
console.error('Unknown error fetching completion from WebLLM:', error);
|
|
141
|
+
}
|
|
142
|
+
return { items: [] };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
protected _completer: ChatWebLLM;
|
|
147
|
+
private _isInitialized: boolean = false;
|
|
148
|
+
private _isInitializing: boolean = false;
|
|
149
|
+
private _initError: Error | null = null;
|
|
150
|
+
private _abortController: AbortController | null = null;
|
|
151
|
+
}
|