@notebook-intelligence/notebook-intelligence 2.4.2 → 2.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.
- package/README.md +114 -95
- package/lib/api.d.ts +8 -1
- package/lib/api.js +59 -15
- package/lib/chat-sidebar.d.ts +7 -5
- package/lib/chat-sidebar.js +144 -331
- package/lib/components/checkbox.d.ts +2 -0
- package/lib/components/checkbox.js +11 -0
- package/lib/components/mcp-util.d.ts +2 -0
- package/lib/components/mcp-util.js +37 -0
- package/lib/components/pill.d.ts +2 -0
- package/lib/components/pill.js +5 -0
- package/lib/components/settings-panel.d.ts +11 -0
- package/lib/components/settings-panel.js +374 -0
- package/lib/index.js +82 -22
- package/lib/tokens.d.ts +13 -1
- package/lib/tokens.js +13 -0
- package/package.json +1 -1
- package/src/api.ts +76 -16
- package/src/chat-sidebar.tsx +286 -700
- package/src/components/checkbox.tsx +29 -0
- package/src/components/mcp-util.ts +53 -0
- package/src/components/pill.tsx +15 -0
- package/src/components/settings-panel.tsx +770 -0
- package/src/index.ts +93 -25
- package/src/tokens.ts +14 -1
- package/style/base.css +103 -2
- package/style/icons/sparkles-warning.svg +5 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { MdOutlineCheckBoxOutlineBlank, MdCheckBox } from 'react-icons/md';
|
|
4
|
+
export function CheckBoxItem(props) {
|
|
5
|
+
const indent = props.indent || 0;
|
|
6
|
+
return (React.createElement("div", { className: `checkbox-item checkbox-item-indent-${indent} ${props.header ? 'checkbox-item-header' : ''}`, title: props.title, onClick: event => props.onClick(event) },
|
|
7
|
+
React.createElement("div", { className: "checkbox-item-toggle" },
|
|
8
|
+
props.checked ? (React.createElement(MdCheckBox, { className: "checkbox-icon" })) : (React.createElement(MdOutlineCheckBoxOutlineBlank, { className: "checkbox-icon" })),
|
|
9
|
+
props.label),
|
|
10
|
+
props.title && (React.createElement("div", { className: "checkbox-item-description" }, props.title))));
|
|
11
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
|
|
2
|
+
export function mcpServerSettingsToEnabledState(mcpServers, mcpServerSettings) {
|
|
3
|
+
const mcpServerEnabledState = new Map();
|
|
4
|
+
for (const server of mcpServers) {
|
|
5
|
+
const mcpServerToolEnabledState = mcpServerSettingsToServerToolEnabledState(mcpServers, mcpServerSettings, server.id);
|
|
6
|
+
if (mcpServerToolEnabledState) {
|
|
7
|
+
mcpServerEnabledState.set(server.id, mcpServerToolEnabledState);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return mcpServerEnabledState;
|
|
11
|
+
}
|
|
12
|
+
export function mcpServerSettingsToServerToolEnabledState(mcpServers, mcpServerSettings, serverId) {
|
|
13
|
+
var _a;
|
|
14
|
+
const server = mcpServers.find((server) => server.id === serverId);
|
|
15
|
+
let mcpServerToolEnabledState = null;
|
|
16
|
+
if (!server) {
|
|
17
|
+
return mcpServerToolEnabledState;
|
|
18
|
+
}
|
|
19
|
+
if (mcpServerSettings[server.id]) {
|
|
20
|
+
const serverSettings = mcpServerSettings[server.id];
|
|
21
|
+
if (!serverSettings.disabled) {
|
|
22
|
+
mcpServerToolEnabledState = new Set();
|
|
23
|
+
for (const tool of server.tools) {
|
|
24
|
+
if (!((_a = serverSettings.disabled_tools) === null || _a === void 0 ? void 0 : _a.includes(tool.name))) {
|
|
25
|
+
mcpServerToolEnabledState.add(tool.name);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
mcpServerToolEnabledState = new Set();
|
|
32
|
+
for (const tool of server.tools) {
|
|
33
|
+
mcpServerToolEnabledState.add(tool.name);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return mcpServerToolEnabledState;
|
|
37
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export function PillItem(props) {
|
|
4
|
+
return (React.createElement("div", { className: `pill-item ${props.checked ? 'checked' : ''}`, title: props.title, onClick: event => props.onClick(event) }, props.label));
|
|
5
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { ReactWidget } from '@jupyterlab/apputils';
|
|
3
|
+
export declare class SettingsPanel extends ReactWidget {
|
|
4
|
+
constructor(options: {
|
|
5
|
+
onSave: () => void;
|
|
6
|
+
onEditMCPConfigClicked: () => void;
|
|
7
|
+
});
|
|
8
|
+
render(): JSX.Element;
|
|
9
|
+
private _onSave;
|
|
10
|
+
private _onEditMCPConfigClicked;
|
|
11
|
+
}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
// Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
|
|
2
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { ReactWidget } from '@jupyterlab/apputils';
|
|
4
|
+
import { VscWarning } from 'react-icons/vsc';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import copySvgstr from '../../style/icons/copy.svg';
|
|
7
|
+
import { NBIAPI } from '../api';
|
|
8
|
+
import { CheckBoxItem } from './checkbox';
|
|
9
|
+
import { PillItem } from './pill';
|
|
10
|
+
import { mcpServerSettingsToEnabledState } from './mcp-util';
|
|
11
|
+
const OPENAI_COMPATIBLE_CHAT_MODEL_ID = 'openai-compatible-chat-model';
|
|
12
|
+
const LITELLM_COMPATIBLE_CHAT_MODEL_ID = 'litellm-compatible-chat-model';
|
|
13
|
+
const OPENAI_COMPATIBLE_INLINE_COMPLETION_MODEL_ID = 'openai-compatible-inline-completion-model';
|
|
14
|
+
const LITELLM_COMPATIBLE_INLINE_COMPLETION_MODEL_ID = 'litellm-compatible-inline-completion-model';
|
|
15
|
+
export class SettingsPanel extends ReactWidget {
|
|
16
|
+
constructor(options) {
|
|
17
|
+
super();
|
|
18
|
+
this._onSave = options.onSave;
|
|
19
|
+
this._onEditMCPConfigClicked = options.onEditMCPConfigClicked;
|
|
20
|
+
}
|
|
21
|
+
render() {
|
|
22
|
+
return (React.createElement(SettingsPanelComponent, { onSave: this._onSave, onEditMCPConfigClicked: this._onEditMCPConfigClicked }));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function SettingsPanelComponent(props) {
|
|
26
|
+
const [activeTab, setActiveTab] = useState('general');
|
|
27
|
+
const onTabSelected = (tab) => {
|
|
28
|
+
setActiveTab(tab);
|
|
29
|
+
};
|
|
30
|
+
return (React.createElement("div", { className: "nbi-settings-panel" },
|
|
31
|
+
React.createElement("div", { className: "nbi-settings-panel-tabs" },
|
|
32
|
+
React.createElement(SettingsPanelTabsComponent, { onTabSelected: onTabSelected, activeTab: activeTab })),
|
|
33
|
+
React.createElement("div", { className: "nbi-settings-panel-tab-content" },
|
|
34
|
+
activeTab === 'general' && (React.createElement(SettingsPanelComponentGeneral, { onSave: props.onSave, onEditMCPConfigClicked: props.onEditMCPConfigClicked })),
|
|
35
|
+
activeTab === 'mcp-servers' && (React.createElement(SettingsPanelComponentMCPServers, { onEditMCPConfigClicked: props.onEditMCPConfigClicked })))));
|
|
36
|
+
}
|
|
37
|
+
function SettingsPanelTabsComponent(props) {
|
|
38
|
+
const [activeTab, setActiveTab] = useState(props.activeTab);
|
|
39
|
+
return (React.createElement("div", null,
|
|
40
|
+
React.createElement("div", { className: `nbi-settings-panel-tab ${activeTab === 'general' ? 'active' : ''}`, onClick: () => {
|
|
41
|
+
setActiveTab('general');
|
|
42
|
+
props.onTabSelected('general');
|
|
43
|
+
} }, "General"),
|
|
44
|
+
React.createElement("div", { className: `nbi-settings-panel-tab ${activeTab === 'mcp-servers' ? 'active' : ''}`, onClick: () => {
|
|
45
|
+
setActiveTab('mcp-servers');
|
|
46
|
+
props.onTabSelected('mcp-servers');
|
|
47
|
+
} }, "MCP Servers")));
|
|
48
|
+
}
|
|
49
|
+
function SettingsPanelComponentGeneral(props) {
|
|
50
|
+
const nbiConfig = NBIAPI.config;
|
|
51
|
+
const llmProviders = nbiConfig.llmProviders;
|
|
52
|
+
const [chatModels, setChatModels] = useState([]);
|
|
53
|
+
const [inlineCompletionModels, setInlineCompletionModels] = useState([]);
|
|
54
|
+
const handleSaveSettings = async () => {
|
|
55
|
+
const config = {
|
|
56
|
+
default_chat_mode: defaultChatMode,
|
|
57
|
+
chat_model: {
|
|
58
|
+
provider: chatModelProvider,
|
|
59
|
+
model: chatModel,
|
|
60
|
+
properties: chatModelProperties
|
|
61
|
+
},
|
|
62
|
+
inline_completion_model: {
|
|
63
|
+
provider: inlineCompletionModelProvider,
|
|
64
|
+
model: inlineCompletionModel,
|
|
65
|
+
properties: inlineCompletionModelProperties
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
if (chatModelProvider === 'github-copilot' ||
|
|
69
|
+
inlineCompletionModelProvider === 'github-copilot') {
|
|
70
|
+
config.store_github_access_token = storeGitHubAccessToken;
|
|
71
|
+
}
|
|
72
|
+
await NBIAPI.setConfig(config);
|
|
73
|
+
props.onSave();
|
|
74
|
+
};
|
|
75
|
+
const handleRefreshOllamaModelListClick = async () => {
|
|
76
|
+
await NBIAPI.updateOllamaModelList();
|
|
77
|
+
updateModelOptionsForProvider(chatModelProvider, 'chat');
|
|
78
|
+
};
|
|
79
|
+
const [chatModelProvider, setChatModelProvider] = useState(nbiConfig.chatModel.provider || 'none');
|
|
80
|
+
const [inlineCompletionModelProvider, setInlineCompletionModelProvider] = useState(nbiConfig.inlineCompletionModel.provider || 'none');
|
|
81
|
+
const [defaultChatMode, setDefaultChatMode] = useState(nbiConfig.defaultChatMode);
|
|
82
|
+
const [chatModel, setChatModel] = useState(nbiConfig.chatModel.model);
|
|
83
|
+
const [chatModelProperties, setChatModelProperties] = useState([]);
|
|
84
|
+
const [inlineCompletionModelProperties, setInlineCompletionModelProperties] = useState([]);
|
|
85
|
+
const [inlineCompletionModel, setInlineCompletionModel] = useState(nbiConfig.inlineCompletionModel.model);
|
|
86
|
+
const [storeGitHubAccessToken, setStoreGitHubAccessToken] = useState(nbiConfig.storeGitHubAccessToken);
|
|
87
|
+
const updateModelOptionsForProvider = (providerId, modelType) => {
|
|
88
|
+
if (modelType === 'chat') {
|
|
89
|
+
setChatModelProvider(providerId);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
setInlineCompletionModelProvider(providerId);
|
|
93
|
+
}
|
|
94
|
+
const models = modelType === 'chat'
|
|
95
|
+
? nbiConfig.chatModels
|
|
96
|
+
: nbiConfig.inlineCompletionModels;
|
|
97
|
+
const selectedModelId = modelType === 'chat'
|
|
98
|
+
? nbiConfig.chatModel.model
|
|
99
|
+
: nbiConfig.inlineCompletionModel.model;
|
|
100
|
+
const providerModels = models.filter((model) => model.provider === providerId);
|
|
101
|
+
if (modelType === 'chat') {
|
|
102
|
+
setChatModels(providerModels);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
setInlineCompletionModels(providerModels);
|
|
106
|
+
}
|
|
107
|
+
let selectedModel = providerModels.find((model) => model.id === selectedModelId);
|
|
108
|
+
if (!selectedModel) {
|
|
109
|
+
selectedModel = providerModels === null || providerModels === void 0 ? void 0 : providerModels[0];
|
|
110
|
+
}
|
|
111
|
+
if (selectedModel) {
|
|
112
|
+
if (modelType === 'chat') {
|
|
113
|
+
setChatModel(selectedModel.id);
|
|
114
|
+
setChatModelProperties(selectedModel.properties);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
setInlineCompletionModel(selectedModel.id);
|
|
118
|
+
setInlineCompletionModelProperties(selectedModel.properties);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
if (modelType === 'chat') {
|
|
123
|
+
setChatModelProperties([]);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
setInlineCompletionModelProperties([]);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const onModelPropertyChange = (modelType, propertyId, value) => {
|
|
131
|
+
const modelProperties = modelType === 'chat'
|
|
132
|
+
? chatModelProperties
|
|
133
|
+
: inlineCompletionModelProperties;
|
|
134
|
+
const updatedProperties = modelProperties.map((property) => {
|
|
135
|
+
if (property.id === propertyId) {
|
|
136
|
+
return { ...property, value };
|
|
137
|
+
}
|
|
138
|
+
return property;
|
|
139
|
+
});
|
|
140
|
+
if (modelType === 'chat') {
|
|
141
|
+
setChatModelProperties(updatedProperties);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
setInlineCompletionModelProperties(updatedProperties);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
updateModelOptionsForProvider(chatModelProvider, 'chat');
|
|
149
|
+
updateModelOptionsForProvider(inlineCompletionModelProvider, 'inline-completion');
|
|
150
|
+
}, []);
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
handleSaveSettings();
|
|
153
|
+
}, [
|
|
154
|
+
defaultChatMode,
|
|
155
|
+
chatModelProvider,
|
|
156
|
+
chatModel,
|
|
157
|
+
chatModelProperties,
|
|
158
|
+
inlineCompletionModelProvider,
|
|
159
|
+
inlineCompletionModel,
|
|
160
|
+
inlineCompletionModelProperties,
|
|
161
|
+
storeGitHubAccessToken
|
|
162
|
+
]);
|
|
163
|
+
return (React.createElement("div", { className: "config-dialog" },
|
|
164
|
+
React.createElement("div", { className: "config-dialog-body" },
|
|
165
|
+
React.createElement("div", { className: "model-config-section" },
|
|
166
|
+
React.createElement("div", { className: "model-config-section-header" }, "Default chat mode"),
|
|
167
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
168
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
169
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
170
|
+
React.createElement("div", null,
|
|
171
|
+
React.createElement("select", { className: "jp-mod-styled", value: defaultChatMode, onChange: event => setDefaultChatMode(event.target.value) },
|
|
172
|
+
React.createElement("option", { value: "ask" }, "Ask"),
|
|
173
|
+
React.createElement("option", { value: "agent" }, "Agent")))),
|
|
174
|
+
React.createElement("div", { className: "model-config-section-column" }, " ")))),
|
|
175
|
+
React.createElement("div", { className: "model-config-section" },
|
|
176
|
+
React.createElement("div", { className: "model-config-section-header" }, "Chat model"),
|
|
177
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
178
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
179
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
180
|
+
React.createElement("div", null, "Provider"),
|
|
181
|
+
React.createElement("div", null,
|
|
182
|
+
React.createElement("select", { className: "jp-mod-styled", onChange: event => updateModelOptionsForProvider(event.target.value, 'chat') },
|
|
183
|
+
llmProviders.map((provider, index) => (React.createElement("option", { key: index, value: provider.id, selected: provider.id === chatModelProvider }, provider.name))),
|
|
184
|
+
React.createElement("option", { key: -1, value: "none", selected: chatModelProvider === 'none' ||
|
|
185
|
+
!llmProviders.find(provider => provider.id === chatModelProvider) }, "None")))),
|
|
186
|
+
!['openai-compatible', 'litellm-compatible', 'none'].includes(chatModelProvider) &&
|
|
187
|
+
chatModels.length > 0 && (React.createElement("div", { className: "model-config-section-column" },
|
|
188
|
+
React.createElement("div", null, "Model"),
|
|
189
|
+
![
|
|
190
|
+
OPENAI_COMPATIBLE_CHAT_MODEL_ID,
|
|
191
|
+
LITELLM_COMPATIBLE_CHAT_MODEL_ID
|
|
192
|
+
].includes(chatModel) &&
|
|
193
|
+
chatModels.length > 0 && (React.createElement("div", null,
|
|
194
|
+
React.createElement("select", { className: "jp-mod-styled", onChange: event => setChatModel(event.target.value) }, chatModels.map((model, index) => (React.createElement("option", { key: index, value: model.id, selected: model.id === chatModel }, model.name))))))))),
|
|
195
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
196
|
+
React.createElement("div", { className: "model-config-section-column" }, chatModelProvider === 'ollama' && chatModels.length === 0 && (React.createElement("div", { className: "ollama-warning-message" },
|
|
197
|
+
"No Ollama models found! Make sure",
|
|
198
|
+
' ',
|
|
199
|
+
React.createElement("a", { href: "https://ollama.com/", target: "_blank" }, "Ollama"),
|
|
200
|
+
' ',
|
|
201
|
+
"is running and models are downloaded to your computer.",
|
|
202
|
+
' ',
|
|
203
|
+
React.createElement("a", { href: "javascript:void(0)", onClick: handleRefreshOllamaModelListClick }, "Try again"),
|
|
204
|
+
' ',
|
|
205
|
+
"once ready.")))),
|
|
206
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
207
|
+
React.createElement("div", { className: "model-config-section-column" }, chatModelProperties.map((property, index) => (React.createElement("div", { className: "form-field-row", key: index },
|
|
208
|
+
React.createElement("div", { className: "form-field-description" },
|
|
209
|
+
property.name,
|
|
210
|
+
" ",
|
|
211
|
+
property.optional ? '(optional)' : ''),
|
|
212
|
+
React.createElement("input", { name: "chat-model-id-input", placeholder: property.description, className: "jp-mod-styled", spellCheck: false, value: property.value, onChange: event => onModelPropertyChange('chat', property.id, event.target.value) })))))))),
|
|
213
|
+
React.createElement("div", { className: "model-config-section" },
|
|
214
|
+
React.createElement("div", { className: "model-config-section-header" }, "Auto-complete model"),
|
|
215
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
216
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
217
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
218
|
+
React.createElement("div", null, "Provider"),
|
|
219
|
+
React.createElement("div", null,
|
|
220
|
+
React.createElement("select", { className: "jp-mod-styled", onChange: event => updateModelOptionsForProvider(event.target.value, 'inline-completion') },
|
|
221
|
+
llmProviders.map((provider, index) => (React.createElement("option", { key: index, value: provider.id, selected: provider.id === inlineCompletionModelProvider }, provider.name))),
|
|
222
|
+
React.createElement("option", { key: -1, value: "none", selected: inlineCompletionModelProvider === 'none' ||
|
|
223
|
+
!llmProviders.find(provider => provider.id === inlineCompletionModelProvider) }, "None")))),
|
|
224
|
+
!['openai-compatible', 'litellm-compatible', 'none'].includes(inlineCompletionModelProvider) && (React.createElement("div", { className: "model-config-section-column" },
|
|
225
|
+
React.createElement("div", null, "Model"),
|
|
226
|
+
![
|
|
227
|
+
OPENAI_COMPATIBLE_INLINE_COMPLETION_MODEL_ID,
|
|
228
|
+
LITELLM_COMPATIBLE_INLINE_COMPLETION_MODEL_ID
|
|
229
|
+
].includes(inlineCompletionModel) && (React.createElement("div", null,
|
|
230
|
+
React.createElement("select", { className: "jp-mod-styled", onChange: event => setInlineCompletionModel(event.target.value) }, inlineCompletionModels.map((model, index) => (React.createElement("option", { key: index, value: model.id, selected: model.id === inlineCompletionModel }, model.name))))))))),
|
|
231
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
232
|
+
React.createElement("div", { className: "model-config-section-column" }, inlineCompletionModelProperties.map((property, index) => (React.createElement("div", { className: "form-field-row", key: index },
|
|
233
|
+
React.createElement("div", { className: "form-field-description" },
|
|
234
|
+
property.name,
|
|
235
|
+
" ",
|
|
236
|
+
property.optional ? '(optional)' : ''),
|
|
237
|
+
React.createElement("input", { name: "inline-completion-model-id-input", placeholder: property.description, className: "jp-mod-styled", spellCheck: false, value: property.value, onChange: event => onModelPropertyChange('inline-completion', property.id, event.target.value) })))))))),
|
|
238
|
+
(chatModelProvider === 'github-copilot' ||
|
|
239
|
+
inlineCompletionModelProvider === 'github-copilot') && (React.createElement("div", { className: "model-config-section" },
|
|
240
|
+
React.createElement("div", { className: "model-config-section-header access-token-config-header" },
|
|
241
|
+
"GitHub Copilot login",
|
|
242
|
+
' ',
|
|
243
|
+
React.createElement("a", { href: "https://github.com/notebook-intelligence/notebook-intelligence/blob/main/README.md#remembering-github-copilot-login", target: "_blank" },
|
|
244
|
+
' ',
|
|
245
|
+
React.createElement(VscWarning, { className: "access-token-warning", title: "Click to learn more about security implications" }))),
|
|
246
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
247
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
248
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
249
|
+
React.createElement("label", null,
|
|
250
|
+
React.createElement("input", { type: "checkbox", checked: storeGitHubAccessToken, onChange: event => {
|
|
251
|
+
setStoreGitHubAccessToken(event.target.checked);
|
|
252
|
+
} }),
|
|
253
|
+
"Remember my GitHub Copilot access token")))))),
|
|
254
|
+
React.createElement("div", { className: "model-config-section" },
|
|
255
|
+
React.createElement("div", { className: "model-config-section-header" }, "Config file path"),
|
|
256
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
257
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
258
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
259
|
+
React.createElement("span", { className: "user-code-span", onClick: () => {
|
|
260
|
+
navigator.clipboard.writeText(path.join(NBIAPI.config.userConfigDir, 'config.json'));
|
|
261
|
+
return true;
|
|
262
|
+
} },
|
|
263
|
+
path.join(NBIAPI.config.userConfigDir, 'config.json'),
|
|
264
|
+
' ',
|
|
265
|
+
React.createElement("span", { className: "copy-icon", dangerouslySetInnerHTML: { __html: copySvgstr } })))))))));
|
|
266
|
+
}
|
|
267
|
+
function SettingsPanelComponentMCPServers(props) {
|
|
268
|
+
const nbiConfig = NBIAPI.config;
|
|
269
|
+
const mcpServersRef = useRef(nbiConfig.toolConfig.mcpServers);
|
|
270
|
+
const mcpServerSettingsRef = useRef(nbiConfig.mcpServerSettings);
|
|
271
|
+
const [renderCount, setRenderCount] = useState(1);
|
|
272
|
+
const [mcpServerEnabledState, setMCPServerEnabledState] = useState(new Map(mcpServerSettingsToEnabledState(mcpServersRef.current, mcpServerSettingsRef.current)));
|
|
273
|
+
const mcpServerEnabledStateToMcpServerSettings = () => {
|
|
274
|
+
const mcpServerSettings = {};
|
|
275
|
+
for (const mcpServer of mcpServersRef.current) {
|
|
276
|
+
if (mcpServerEnabledState.has(mcpServer.id)) {
|
|
277
|
+
const disabledTools = [];
|
|
278
|
+
for (const tool of mcpServer.tools) {
|
|
279
|
+
if (!mcpServerEnabledState.get(mcpServer.id).has(tool.name)) {
|
|
280
|
+
disabledTools.push(tool.name);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
mcpServerSettings[mcpServer.id] = {
|
|
284
|
+
disabled: false,
|
|
285
|
+
disabled_tools: disabledTools
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
mcpServerSettings[mcpServer.id] = { disabled: true };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return mcpServerSettings;
|
|
293
|
+
};
|
|
294
|
+
const syncSettingsToServerState = () => {
|
|
295
|
+
NBIAPI.setConfig({
|
|
296
|
+
mcp_server_settings: mcpServerSettingsRef.current
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
const handleReloadMCPServersClick = async () => {
|
|
300
|
+
await NBIAPI.reloadMCPServers();
|
|
301
|
+
};
|
|
302
|
+
useEffect(() => {
|
|
303
|
+
syncSettingsToServerState();
|
|
304
|
+
}, [mcpServerSettingsRef.current]);
|
|
305
|
+
useEffect(() => {
|
|
306
|
+
mcpServerSettingsRef.current = mcpServerEnabledStateToMcpServerSettings();
|
|
307
|
+
setRenderCount(renderCount => renderCount + 1);
|
|
308
|
+
}, [mcpServerEnabledState]);
|
|
309
|
+
const setMCPServerEnabled = (serverId, enabled) => {
|
|
310
|
+
var _a;
|
|
311
|
+
const currentState = new Map(mcpServerEnabledState);
|
|
312
|
+
if (enabled) {
|
|
313
|
+
if (!(serverId in currentState)) {
|
|
314
|
+
currentState.set(serverId, new Set((_a = mcpServersRef.current
|
|
315
|
+
.find((server) => server.id === serverId)) === null || _a === void 0 ? void 0 : _a.tools.map((tool) => tool.name)));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
currentState.delete(serverId);
|
|
320
|
+
}
|
|
321
|
+
setMCPServerEnabledState(currentState);
|
|
322
|
+
};
|
|
323
|
+
const getMCPServerEnabled = (serverId) => {
|
|
324
|
+
return mcpServerEnabledState.has(serverId);
|
|
325
|
+
};
|
|
326
|
+
const getMCPServerToolEnabled = (serverId, toolName) => {
|
|
327
|
+
return (mcpServerEnabledState.has(serverId) &&
|
|
328
|
+
mcpServerEnabledState.get(serverId).has(toolName));
|
|
329
|
+
};
|
|
330
|
+
const setMCPServerToolEnabled = (serverId, toolName, enabled) => {
|
|
331
|
+
const currentState = new Map(mcpServerEnabledState);
|
|
332
|
+
const serverState = currentState.get(serverId);
|
|
333
|
+
if (enabled) {
|
|
334
|
+
serverState.add(toolName);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
serverState.delete(toolName);
|
|
338
|
+
}
|
|
339
|
+
setMCPServerEnabledState(currentState);
|
|
340
|
+
};
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
NBIAPI.configChanged.connect(() => {
|
|
343
|
+
mcpServersRef.current = nbiConfig.toolConfig.mcpServers;
|
|
344
|
+
mcpServerSettingsRef.current = nbiConfig.mcpServerSettings;
|
|
345
|
+
setRenderCount(renderCount => renderCount + 1);
|
|
346
|
+
});
|
|
347
|
+
}, []);
|
|
348
|
+
return (React.createElement("div", { className: "config-dialog" },
|
|
349
|
+
React.createElement("div", { className: "config-dialog-body" },
|
|
350
|
+
React.createElement("div", { className: "model-config-section" },
|
|
351
|
+
React.createElement("div", { className: "model-config-section-header", style: { display: 'flex' } },
|
|
352
|
+
React.createElement("div", { style: { flexGrow: 1 } }, "MCP Servers"),
|
|
353
|
+
React.createElement("div", null,
|
|
354
|
+
React.createElement("button", { className: "jp-toast-button jp-mod-small jp-Button", onClick: handleReloadMCPServersClick },
|
|
355
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, "Reload")))),
|
|
356
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
357
|
+
mcpServersRef.current.length === 0 && renderCount > 0 && (React.createElement("div", { className: "model-config-section-row" },
|
|
358
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
359
|
+
React.createElement("div", null, "No MCP servers found. Add MCP servers in the configuration file.")))),
|
|
360
|
+
mcpServersRef.current.length > 0 && renderCount > 0 && (React.createElement("div", { className: "model-config-section-row" },
|
|
361
|
+
React.createElement("div", { className: "model-config-section-column" }, mcpServersRef.current.map((server) => (React.createElement("div", { key: server.id },
|
|
362
|
+
React.createElement("div", { style: { display: 'flex', alignItems: 'center' } },
|
|
363
|
+
React.createElement(CheckBoxItem, { header: true, label: server.id, checked: getMCPServerEnabled(server.id), onClick: () => {
|
|
364
|
+
setMCPServerEnabled(server.id, !getMCPServerEnabled(server.id));
|
|
365
|
+
} }),
|
|
366
|
+
React.createElement("div", { className: `server-status-indicator ${server.status}`, title: server.status })),
|
|
367
|
+
getMCPServerEnabled(server.id) && (React.createElement("div", null, server.tools.map((tool) => (React.createElement(PillItem, { label: tool.name, title: tool.description, checked: getMCPServerToolEnabled(server.id, tool.name), onClick: () => {
|
|
368
|
+
setMCPServerToolEnabled(server.id, tool.name, !getMCPServerToolEnabled(server.id, tool.name));
|
|
369
|
+
} }))))))))))),
|
|
370
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
371
|
+
React.createElement("div", { className: "model-config-section-column", style: { flexGrow: 'initial' } },
|
|
372
|
+
React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", style: { width: 'max-content' }, onClick: props.onEditMCPConfigClicked },
|
|
373
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, "Add / Edit")))))))));
|
|
374
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
|
|
2
2
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
3
|
-
import { Dialog, ICommandPalette } from '@jupyterlab/apputils';
|
|
3
|
+
import { Dialog, ICommandPalette, MainAreaWidget } from '@jupyterlab/apputils';
|
|
4
4
|
import { IMainMenu } from '@jupyterlab/mainmenu';
|
|
5
5
|
import { IEditorLanguageRegistry } from '@jupyterlab/codemirror';
|
|
6
6
|
import { CodeCell } from '@jupyterlab/cells';
|
|
@@ -14,14 +14,16 @@ import { LabIcon } from '@jupyterlab/ui-components';
|
|
|
14
14
|
import { Menu, Panel, Widget } from '@lumino/widgets';
|
|
15
15
|
import { CommandRegistry } from '@lumino/commands';
|
|
16
16
|
import { IStatusBar } from '@jupyterlab/statusbar';
|
|
17
|
-
import { ChatSidebar,
|
|
17
|
+
import { ChatSidebar, FormInputDialogBody, GitHubCopilotLoginDialogBody, GitHubCopilotStatusBarItem, InlinePromptWidget, RunChatCompletionType } from './chat-sidebar';
|
|
18
18
|
import { NBIAPI, GitHubCopilotLoginStatus } from './api';
|
|
19
19
|
import { BackendMessageType, GITHUB_COPILOT_PROVIDER_ID, INotebookIntelligence, RequestDataType, TelemetryEventType } from './tokens';
|
|
20
20
|
import sparklesSvgstr from '../style/icons/sparkles.svg';
|
|
21
21
|
import copilotSvgstr from '../style/icons/copilot.svg';
|
|
22
|
+
import sparklesWarningSvgstr from '../style/icons/sparkles-warning.svg';
|
|
22
23
|
import { applyCodeToSelectionInEditor, cellOutputAsText, compareSelections, extractLLMGeneratedCode, getSelectionInEditor, getTokenCount, getWholeNotebookContent, isSelectionEmpty, markdownToComment, waitForDuration } from './utils';
|
|
23
24
|
import { UUID } from '@lumino/coreutils';
|
|
24
25
|
import * as path from 'path';
|
|
26
|
+
import { SettingsPanel } from './components/settings-panel';
|
|
25
27
|
var CommandIDs;
|
|
26
28
|
(function (CommandIDs) {
|
|
27
29
|
CommandIDs.chatuserInput = 'notebook-intelligence:chat-user-input';
|
|
@@ -51,6 +53,7 @@ var CommandIDs;
|
|
|
51
53
|
CommandIDs.getCurrentFileContent = 'notebook-intelligence:get-current-file-content';
|
|
52
54
|
CommandIDs.setCurrentFileContent = 'notebook-intelligence:set-current-file-content';
|
|
53
55
|
CommandIDs.openMCPConfigEditor = 'notebook-intelligence:open-mcp-config-editor';
|
|
56
|
+
CommandIDs.showFormInputDialog = 'notebook-intelligence:show-form-input-dialog';
|
|
54
57
|
})(CommandIDs || (CommandIDs = {}));
|
|
55
58
|
const DOCUMENT_WATCH_INTERVAL = 1000;
|
|
56
59
|
const MAX_TOKENS = 4096;
|
|
@@ -62,6 +65,10 @@ const sparkleIcon = new LabIcon({
|
|
|
62
65
|
name: 'notebook-intelligence:sparkles-icon',
|
|
63
66
|
svgstr: sparklesSvgstr
|
|
64
67
|
});
|
|
68
|
+
const sparkleWarningIcon = new LabIcon({
|
|
69
|
+
name: 'notebook-intelligence:sparkles-warning-icon',
|
|
70
|
+
svgstr: sparklesWarningSvgstr
|
|
71
|
+
});
|
|
65
72
|
const emptyNotebookContent = {
|
|
66
73
|
cells: [],
|
|
67
74
|
metadata: {},
|
|
@@ -498,7 +505,7 @@ const plugin = {
|
|
|
498
505
|
panel.id = 'notebook-intelligence-tab';
|
|
499
506
|
panel.title.caption = 'Notebook Intelligence';
|
|
500
507
|
const sidebarIcon = new LabIcon({
|
|
501
|
-
name: '
|
|
508
|
+
name: 'notebook-intelligence:sidebar-icon',
|
|
502
509
|
svgstr: sparklesSvgstr
|
|
503
510
|
});
|
|
504
511
|
panel.title.icon = sidebarIcon;
|
|
@@ -525,6 +532,23 @@ const plugin = {
|
|
|
525
532
|
panel.addWidget(sidebar);
|
|
526
533
|
app.shell.add(panel, 'left', { rank: 1000 });
|
|
527
534
|
app.shell.activateById(panel.id);
|
|
535
|
+
const updateSidebarIcon = () => {
|
|
536
|
+
if (NBIAPI.getChatEnabled()) {
|
|
537
|
+
panel.title.icon = sidebarIcon;
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
panel.title.icon = sparkleWarningIcon;
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
NBIAPI.githubLoginStatusChanged.connect((_, args) => {
|
|
544
|
+
updateSidebarIcon();
|
|
545
|
+
});
|
|
546
|
+
NBIAPI.configChanged.connect((_, args) => {
|
|
547
|
+
updateSidebarIcon();
|
|
548
|
+
});
|
|
549
|
+
setTimeout(() => {
|
|
550
|
+
updateSidebarIcon();
|
|
551
|
+
}, 2000);
|
|
528
552
|
app.commands.addCommand(CommandIDs.chatuserInput, {
|
|
529
553
|
execute: args => {
|
|
530
554
|
NBIAPI.sendChatUserInput(args.id, args.data);
|
|
@@ -594,6 +618,36 @@ const plugin = {
|
|
|
594
618
|
return newPyFile;
|
|
595
619
|
}
|
|
596
620
|
});
|
|
621
|
+
app.commands.addCommand(CommandIDs.showFormInputDialog, {
|
|
622
|
+
execute: async (args) => {
|
|
623
|
+
const title = args.title;
|
|
624
|
+
const fields = args.fields;
|
|
625
|
+
return new Promise((resolve, reject) => {
|
|
626
|
+
let dialog = null;
|
|
627
|
+
const dialogBody = new FormInputDialogBody({
|
|
628
|
+
fields: fields,
|
|
629
|
+
onDone: (formData) => {
|
|
630
|
+
dialog.dispose();
|
|
631
|
+
resolve(formData);
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
dialog = new Dialog({
|
|
635
|
+
title: title,
|
|
636
|
+
hasClose: true,
|
|
637
|
+
body: dialogBody,
|
|
638
|
+
buttons: []
|
|
639
|
+
});
|
|
640
|
+
dialog
|
|
641
|
+
.launch()
|
|
642
|
+
.then((result) => {
|
|
643
|
+
reject();
|
|
644
|
+
})
|
|
645
|
+
.catch(() => {
|
|
646
|
+
reject(new Error('Failed to show form input dialog'));
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
});
|
|
597
651
|
app.commands.addCommand(CommandIDs.createNewNotebookFromPython, {
|
|
598
652
|
execute: async (args) => {
|
|
599
653
|
var _a;
|
|
@@ -922,28 +976,32 @@ const plugin = {
|
|
|
922
976
|
dialog.launch();
|
|
923
977
|
}
|
|
924
978
|
});
|
|
979
|
+
const createNewSettingsWidget = () => {
|
|
980
|
+
const settingsPanel = new SettingsPanel({
|
|
981
|
+
onSave: () => {
|
|
982
|
+
NBIAPI.fetchCapabilities();
|
|
983
|
+
},
|
|
984
|
+
onEditMCPConfigClicked: () => {
|
|
985
|
+
app.commands.execute('notebook-intelligence:open-mcp-config-editor');
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
const widget = new MainAreaWidget({ content: settingsPanel });
|
|
989
|
+
widget.id = 'nbi-settings';
|
|
990
|
+
widget.title.label = 'NBI Settings';
|
|
991
|
+
widget.title.closable = true;
|
|
992
|
+
return widget;
|
|
993
|
+
};
|
|
994
|
+
let settingsWidget = createNewSettingsWidget();
|
|
925
995
|
app.commands.addCommand(CommandIDs.openConfigurationDialog, {
|
|
926
996
|
label: 'Notebook Intelligence Settings',
|
|
927
997
|
execute: args => {
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
dialog === null || dialog === void 0 ? void 0 : dialog.dispose();
|
|
936
|
-
app.commands.execute('notebook-intelligence:open-mcp-config-editor');
|
|
937
|
-
}
|
|
938
|
-
});
|
|
939
|
-
dialog = new Dialog({
|
|
940
|
-
title: 'Notebook Intelligence Settings',
|
|
941
|
-
hasClose: true,
|
|
942
|
-
body: dialogBody,
|
|
943
|
-
buttons: []
|
|
944
|
-
});
|
|
945
|
-
dialog.node.classList.add('config-dialog-container');
|
|
946
|
-
dialog.launch();
|
|
998
|
+
if (settingsWidget.isDisposed) {
|
|
999
|
+
settingsWidget = createNewSettingsWidget();
|
|
1000
|
+
}
|
|
1001
|
+
if (!settingsWidget.isAttached) {
|
|
1002
|
+
app.shell.add(settingsWidget, 'main');
|
|
1003
|
+
}
|
|
1004
|
+
app.shell.activateById(settingsWidget.id);
|
|
947
1005
|
}
|
|
948
1006
|
});
|
|
949
1007
|
app.commands.addCommand(CommandIDs.openMCPConfigEditor, {
|
|
@@ -1138,6 +1196,8 @@ const plugin = {
|
|
|
1138
1196
|
existingCode,
|
|
1139
1197
|
prefix: prefix,
|
|
1140
1198
|
suffix: suffix,
|
|
1199
|
+
language: ActiveDocumentWatcher.activeDocumentInfo.language,
|
|
1200
|
+
filename: ActiveDocumentWatcher.activeDocumentInfo.filePath,
|
|
1141
1201
|
onRequestSubmitted: (prompt) => {
|
|
1142
1202
|
userPrompt = prompt;
|
|
1143
1203
|
generatedContent = '';
|