@notebook-intelligence/notebook-intelligence 2.5.0 → 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 +113 -0
- package/lib/api.d.ts +6 -0
- package/lib/api.js +28 -0
- package/lib/chat-sidebar.d.ts +11 -0
- package/lib/chat-sidebar.js +91 -43
- package/lib/index.js +57 -2
- package/lib/tokens.d.ts +3 -1
- package/lib/tokens.js +2 -0
- package/package.json +1 -1
- package/src/api.ts +42 -0
- package/src/chat-sidebar.tsx +149 -51
- package/src/index.ts +64 -1
- package/src/tokens.ts +3 -1
- package/style/base.css +28 -2
- package/style/icons/sparkles-warning.svg +5 -0
package/README.md
CHANGED
|
@@ -175,6 +175,119 @@ If you have multiple servers configured but you would like to disable some for a
|
|
|
175
175
|
}
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
+
### Ruleset System
|
|
179
|
+
|
|
180
|
+
NBI includes a powerful ruleset system that allows you to define custom guidelines and best practices that are automatically injected into AI prompts. This helps ensure consistent coding standards, project-specific conventions, and domain knowledge across all AI interactions.
|
|
181
|
+
|
|
182
|
+
#### How It Works
|
|
183
|
+
|
|
184
|
+
Rules are markdown files with optional YAML frontmatter stored in `~/.jupyter/nbi/rules/`. They are automatically discovered and applied based on context (file type, notebook kernel, chat mode).
|
|
185
|
+
|
|
186
|
+
#### Creating Rules
|
|
187
|
+
|
|
188
|
+
**Global Rules** - Apply to all contexts:
|
|
189
|
+
|
|
190
|
+
Create a file like `~/.jupyter/nbi/rules/01-coding-standards.md`:
|
|
191
|
+
|
|
192
|
+
```markdown
|
|
193
|
+
---
|
|
194
|
+
priority: 10
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
# Coding Standards
|
|
198
|
+
|
|
199
|
+
- Always use type hints in Python functions
|
|
200
|
+
- Prefer list comprehensions over loops when appropriate
|
|
201
|
+
- Add docstrings to all public functions
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Mode-Specific Rules** - Apply only to specific chat modes:
|
|
205
|
+
|
|
206
|
+
NBI supports mode-specific rules for three modes:
|
|
207
|
+
|
|
208
|
+
- **ask** - Question/answer mode
|
|
209
|
+
- **agent** - Autonomous agent mode with tool access
|
|
210
|
+
- **inline-chat** - Inline code generation and editing
|
|
211
|
+
|
|
212
|
+
Create a file like `~/.jupyter/nbi/rules/modes/agent/01-testing.md`:
|
|
213
|
+
|
|
214
|
+
```markdown
|
|
215
|
+
---
|
|
216
|
+
priority: 20
|
|
217
|
+
scope:
|
|
218
|
+
kernels: ['python3']
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
# Testing Guidelines
|
|
222
|
+
|
|
223
|
+
When writing code in agent mode:
|
|
224
|
+
|
|
225
|
+
- Always include error handling
|
|
226
|
+
- Add logging for debugging
|
|
227
|
+
- Test edge cases
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
#### Rule Frontmatter Options
|
|
231
|
+
|
|
232
|
+
```yaml
|
|
233
|
+
---
|
|
234
|
+
apply: always # 'always', 'auto', or 'manual'
|
|
235
|
+
active: true # Enable/disable the rule
|
|
236
|
+
priority: 10 # Lower numbers = higher priority
|
|
237
|
+
scope:
|
|
238
|
+
file_patterns: # Apply to specific file patterns
|
|
239
|
+
- '*.py'
|
|
240
|
+
- 'test_*.ipynb'
|
|
241
|
+
kernels: # Apply to specific notebook kernels
|
|
242
|
+
- 'python3'
|
|
243
|
+
- 'ir'
|
|
244
|
+
directories: # Apply to specific directories
|
|
245
|
+
- '/projects/ml'
|
|
246
|
+
---
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### Configuration
|
|
250
|
+
|
|
251
|
+
**Enable/Disable Rules System:**
|
|
252
|
+
|
|
253
|
+
Edit `~/.jupyter/nbi/config.json`:
|
|
254
|
+
|
|
255
|
+
```json
|
|
256
|
+
{
|
|
257
|
+
"rules_enabled": true
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Auto-Reload Configuration:**
|
|
262
|
+
|
|
263
|
+
Rules are automatically reloaded when changed (enabled by default). This behavior is controlled by the `NBI_RULES_AUTO_RELOAD` environment variable.
|
|
264
|
+
|
|
265
|
+
To disable auto-reload:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
export NBI_RULES_AUTO_RELOAD=false
|
|
269
|
+
jupyter lab
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Or to enable (default):
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
export NBI_RULES_AUTO_RELOAD=true
|
|
276
|
+
jupyter lab
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### Managing Rules
|
|
280
|
+
|
|
281
|
+
Rules are automatically discovered from:
|
|
282
|
+
|
|
283
|
+
- **Global rules**: `~/.jupyter/nbi/rules/*.md`
|
|
284
|
+
- **Mode-specific rules**: `~/.jupyter/nbi/rules/modes/{mode}/*.md` where `{mode}` can be:
|
|
285
|
+
- `ask` - For question/answer interactions
|
|
286
|
+
- `agent` - For autonomous agent operations
|
|
287
|
+
- `inline-chat` - For inline code generation
|
|
288
|
+
|
|
289
|
+
Rules are applied in priority order (lower numbers first) and can be toggled on/off without deleting the files.
|
|
290
|
+
|
|
178
291
|
### Developer documentation
|
|
179
292
|
|
|
180
293
|
For building locally and contributing see the [developer documentatation](CONTRIBUTING.md).
|
package/lib/api.d.ts
CHANGED
|
@@ -22,6 +22,9 @@ export declare class NBIConfig {
|
|
|
22
22
|
get usingGitHubCopilotModel(): boolean;
|
|
23
23
|
get storeGitHubAccessToken(): boolean;
|
|
24
24
|
get toolConfig(): any;
|
|
25
|
+
get mcpServers(): any;
|
|
26
|
+
getMCPServer(serverId: string): any;
|
|
27
|
+
getMCPServerPrompt(serverId: string, promptName: string): any;
|
|
25
28
|
get mcpServerSettings(): any;
|
|
26
29
|
capabilities: any;
|
|
27
30
|
chatParticipants: IChatParticipant[];
|
|
@@ -39,6 +42,9 @@ export declare class NBIAPI {
|
|
|
39
42
|
static initializeWebsocket(): Promise<void>;
|
|
40
43
|
static getLoginStatus(): GitHubCopilotLoginStatus;
|
|
41
44
|
static getDeviceVerificationInfo(): IDeviceVerificationInfo;
|
|
45
|
+
static getGHLoginRequired(): boolean;
|
|
46
|
+
static getChatEnabled(): any;
|
|
47
|
+
static getInlineCompletionEnabled(): any;
|
|
42
48
|
static loginToGitHub(): Promise<unknown>;
|
|
43
49
|
static logoutFromGitHub(): Promise<unknown>;
|
|
44
50
|
static updateGitHubLoginStatus(): Promise<void>;
|
package/lib/api.js
CHANGED
|
@@ -53,6 +53,19 @@ export class NBIConfig {
|
|
|
53
53
|
get toolConfig() {
|
|
54
54
|
return this.capabilities.tool_config;
|
|
55
55
|
}
|
|
56
|
+
get mcpServers() {
|
|
57
|
+
return this.toolConfig.mcpServers;
|
|
58
|
+
}
|
|
59
|
+
getMCPServer(serverId) {
|
|
60
|
+
return this.toolConfig.mcpServers.find((server) => server.id === serverId);
|
|
61
|
+
}
|
|
62
|
+
getMCPServerPrompt(serverId, promptName) {
|
|
63
|
+
const server = this.getMCPServer(serverId);
|
|
64
|
+
if (server) {
|
|
65
|
+
return server.prompts.find((prompt) => prompt.name === promptName);
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
56
69
|
get mcpServerSettings() {
|
|
57
70
|
return this.capabilities.mcp_server_settings;
|
|
58
71
|
}
|
|
@@ -98,6 +111,21 @@ class NBIAPI {
|
|
|
98
111
|
static getDeviceVerificationInfo() {
|
|
99
112
|
return this._deviceVerificationInfo;
|
|
100
113
|
}
|
|
114
|
+
static getGHLoginRequired() {
|
|
115
|
+
return (this.config.usingGitHubCopilotModel &&
|
|
116
|
+
NBIAPI.getLoginStatus() === GitHubCopilotLoginStatus.NotLoggedIn);
|
|
117
|
+
}
|
|
118
|
+
static getChatEnabled() {
|
|
119
|
+
return this.config.chatModel.provider === GITHUB_COPILOT_PROVIDER_ID
|
|
120
|
+
? !this.getGHLoginRequired()
|
|
121
|
+
: this.config.llmProviders.find(provider => provider.id === this.config.chatModel.provider);
|
|
122
|
+
}
|
|
123
|
+
static getInlineCompletionEnabled() {
|
|
124
|
+
return this.config.inlineCompletionModel.provider ===
|
|
125
|
+
GITHUB_COPILOT_PROVIDER_ID
|
|
126
|
+
? !this.getGHLoginRequired()
|
|
127
|
+
: this.config.llmProviders.find(provider => provider.id === this.config.inlineCompletionModel.provider);
|
|
128
|
+
}
|
|
101
129
|
static async loginToGitHub() {
|
|
102
130
|
this._loginStatus = GitHubCopilotLoginStatus.ActivatingDevice;
|
|
103
131
|
return new Promise((resolve, reject) => {
|
package/lib/chat-sidebar.d.ts
CHANGED
|
@@ -42,6 +42,8 @@ export interface IInlinePromptWidgetOptions {
|
|
|
42
42
|
existingCode: string;
|
|
43
43
|
prefix: string;
|
|
44
44
|
suffix: string;
|
|
45
|
+
language?: string;
|
|
46
|
+
filename?: string;
|
|
45
47
|
onRequestSubmitted: (prompt: string) => void;
|
|
46
48
|
onRequestCancelled: () => void;
|
|
47
49
|
onContentStream: (content: string) => void;
|
|
@@ -73,3 +75,12 @@ export declare class GitHubCopilotLoginDialogBody extends ReactWidget {
|
|
|
73
75
|
render(): JSX.Element;
|
|
74
76
|
private _onLoggedIn;
|
|
75
77
|
}
|
|
78
|
+
export declare class FormInputDialogBody extends ReactWidget {
|
|
79
|
+
constructor(options: {
|
|
80
|
+
fields: any;
|
|
81
|
+
onDone: (formData: any) => void;
|
|
82
|
+
});
|
|
83
|
+
render(): JSX.Element;
|
|
84
|
+
private _fields;
|
|
85
|
+
private _onDone;
|
|
86
|
+
}
|
package/lib/chat-sidebar.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ReactWidget } from '@jupyterlab/apputils';
|
|
|
4
4
|
import { UUID } from '@lumino/coreutils';
|
|
5
5
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
|
|
6
6
|
import { NBIAPI, GitHubCopilotLoginStatus } from './api';
|
|
7
|
-
import { BackendMessageType, BuiltinToolsetType, ContextType,
|
|
7
|
+
import { BackendMessageType, BuiltinToolsetType, ContextType, RequestDataType, ResponseStreamDataType, TelemetryEventType } from './tokens';
|
|
8
8
|
import { MarkdownRenderer as OriginalMarkdownRenderer } from './markdown-renderer';
|
|
9
9
|
const MarkdownRenderer = memo(OriginalMarkdownRenderer);
|
|
10
10
|
import copySvgstr from '../style/icons/copy.svg';
|
|
@@ -613,29 +613,36 @@ function SidebarComponent(props) {
|
|
|
613
613
|
setToolSelections(newConfig);
|
|
614
614
|
};
|
|
615
615
|
useEffect(() => {
|
|
616
|
+
var _a;
|
|
616
617
|
const prefixes = [];
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
618
|
+
prefixes.push('/clear');
|
|
619
|
+
if (chatMode === 'ask') {
|
|
620
|
+
const chatParticipants = NBIAPI.config.chatParticipants;
|
|
621
|
+
for (const participant of chatParticipants) {
|
|
622
|
+
const id = participant.id;
|
|
623
|
+
const commands = participant.commands;
|
|
624
|
+
const participantPrefix = id === 'default' ? '' : `@${id}`;
|
|
625
|
+
if (participantPrefix !== '') {
|
|
626
|
+
prefixes.push(participantPrefix);
|
|
627
|
+
}
|
|
628
|
+
const commandPrefix = participantPrefix === '' ? '' : `${participantPrefix} `;
|
|
629
|
+
for (const command of commands) {
|
|
630
|
+
prefixes.push(`${commandPrefix}/${command}`);
|
|
631
|
+
}
|
|
630
632
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
633
|
+
}
|
|
634
|
+
const mcpServers = NBIAPI.config.toolConfig.mcpServers;
|
|
635
|
+
const mcpServerSettings = NBIAPI.config.mcpServerSettings;
|
|
636
|
+
for (const mcpServer of mcpServers) {
|
|
637
|
+
if (((_a = mcpServerSettings[mcpServer.id]) === null || _a === void 0 ? void 0 : _a.disabled) !== true) {
|
|
638
|
+
for (const prompt of mcpServer.prompts) {
|
|
639
|
+
prefixes.push(`/mcp:${mcpServer.id}:${prompt.name}`);
|
|
640
|
+
}
|
|
634
641
|
}
|
|
635
642
|
}
|
|
636
643
|
setOriginalPrefixes(prefixes);
|
|
637
644
|
setPrefixSuggestions(prefixes);
|
|
638
|
-
}, [chatMode]);
|
|
645
|
+
}, [chatMode, renderCount]);
|
|
639
646
|
useEffect(() => {
|
|
640
647
|
const fetchData = () => {
|
|
641
648
|
setGHLoginStatus(NBIAPI.getLoginStatus());
|
|
@@ -664,13 +671,37 @@ function SidebarComponent(props) {
|
|
|
664
671
|
setShowPopover(false);
|
|
665
672
|
}
|
|
666
673
|
};
|
|
667
|
-
const applyPrefixSuggestion = (prefix) => {
|
|
674
|
+
const applyPrefixSuggestion = async (prefix) => {
|
|
668
675
|
var _a;
|
|
676
|
+
let mcpArguments = '';
|
|
677
|
+
if (prefix.startsWith('/mcp:')) {
|
|
678
|
+
mcpArguments = ':';
|
|
679
|
+
const serverId = prefix.split(':')[1];
|
|
680
|
+
const promptName = prefix.split(':')[2];
|
|
681
|
+
const promptConfig = NBIAPI.config.getMCPServerPrompt(serverId, promptName);
|
|
682
|
+
if (promptConfig &&
|
|
683
|
+
promptConfig.arguments &&
|
|
684
|
+
promptConfig.arguments.length > 0) {
|
|
685
|
+
const result = await props
|
|
686
|
+
.getApp()
|
|
687
|
+
.commands.execute('notebook-intelligence:show-form-input-dialog', {
|
|
688
|
+
title: 'Input Parameters',
|
|
689
|
+
fields: promptConfig.arguments
|
|
690
|
+
});
|
|
691
|
+
const argumentValues = [];
|
|
692
|
+
for (const argument of promptConfig.arguments) {
|
|
693
|
+
if (result[argument.name] !== undefined) {
|
|
694
|
+
argumentValues.push(`${argument.name}=${result[argument.name]}`);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
mcpArguments = `(${argumentValues.join(', ')}):`;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
669
700
|
if (prefix.includes(prompt)) {
|
|
670
|
-
setPrompt(`${prefix} `);
|
|
701
|
+
setPrompt(`${prefix}${mcpArguments} `);
|
|
671
702
|
}
|
|
672
703
|
else {
|
|
673
|
-
setPrompt(`${prefix} ${prompt} `);
|
|
704
|
+
setPrompt(`${prefix} ${prompt}${mcpArguments} `);
|
|
674
705
|
}
|
|
675
706
|
setShowPopover(false);
|
|
676
707
|
(_a = promptInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
@@ -721,7 +752,6 @@ function SidebarComponent(props) {
|
|
|
721
752
|
}
|
|
722
753
|
}
|
|
723
754
|
}
|
|
724
|
-
const promptPrefix = promptPrefixParts.length > 0 ? promptPrefixParts.join(' ') + ' ' : '';
|
|
725
755
|
lastMessageId.current = UUID.uuid4();
|
|
726
756
|
lastRequestTime.current = new Date();
|
|
727
757
|
const newList = [
|
|
@@ -780,7 +810,7 @@ function SidebarComponent(props) {
|
|
|
780
810
|
type: RunChatCompletionType.Chat,
|
|
781
811
|
content: extractedPrompt,
|
|
782
812
|
language: activeDocInfo.language,
|
|
783
|
-
filename: activeDocInfo.
|
|
813
|
+
filename: activeDocInfo.filePath,
|
|
784
814
|
additionalContext,
|
|
785
815
|
chatMode,
|
|
786
816
|
toolSelections: toolSelections
|
|
@@ -863,7 +893,7 @@ function SidebarComponent(props) {
|
|
|
863
893
|
]);
|
|
864
894
|
}
|
|
865
895
|
});
|
|
866
|
-
const newPrompt =
|
|
896
|
+
const newPrompt = '';
|
|
867
897
|
setPrompt(newPrompt);
|
|
868
898
|
filterPrefixSuggestions(newPrompt);
|
|
869
899
|
telemetryEmitter.emitTelemetryEvent({
|
|
@@ -1130,27 +1160,17 @@ function SidebarComponent(props) {
|
|
|
1130
1160
|
}
|
|
1131
1161
|
return `${activeDocumentInfo.filename}${cellAndLineIndicator}`;
|
|
1132
1162
|
};
|
|
1133
|
-
const
|
|
1134
|
-
const
|
|
1135
|
-
return (nbiConfig.usingGitHubCopilotModel &&
|
|
1136
|
-
NBIAPI.getLoginStatus() === GitHubCopilotLoginStatus.NotLoggedIn);
|
|
1137
|
-
};
|
|
1138
|
-
const getChatEnabled = () => {
|
|
1139
|
-
return nbiConfig.chatModel.provider === GITHUB_COPILOT_PROVIDER_ID
|
|
1140
|
-
? !getGHLoginRequired()
|
|
1141
|
-
: nbiConfig.llmProviders.find(provider => provider.id === nbiConfig.chatModel.provider);
|
|
1142
|
-
};
|
|
1143
|
-
const [ghLoginRequired, setGHLoginRequired] = useState(getGHLoginRequired());
|
|
1144
|
-
const [chatEnabled, setChatEnabled] = useState(getChatEnabled());
|
|
1163
|
+
const [ghLoginRequired, setGHLoginRequired] = useState(NBIAPI.getGHLoginRequired());
|
|
1164
|
+
const [chatEnabled, setChatEnabled] = useState(NBIAPI.getChatEnabled());
|
|
1145
1165
|
useEffect(() => {
|
|
1146
1166
|
NBIAPI.configChanged.connect(() => {
|
|
1147
|
-
setGHLoginRequired(getGHLoginRequired());
|
|
1148
|
-
setChatEnabled(getChatEnabled());
|
|
1167
|
+
setGHLoginRequired(NBIAPI.getGHLoginRequired());
|
|
1168
|
+
setChatEnabled(NBIAPI.getChatEnabled());
|
|
1149
1169
|
});
|
|
1150
1170
|
}, []);
|
|
1151
1171
|
useEffect(() => {
|
|
1152
|
-
setGHLoginRequired(getGHLoginRequired());
|
|
1153
|
-
setChatEnabled(getChatEnabled());
|
|
1172
|
+
setGHLoginRequired(NBIAPI.getGHLoginRequired());
|
|
1173
|
+
setChatEnabled(NBIAPI.getChatEnabled());
|
|
1154
1174
|
}, [ghLoginStatus]);
|
|
1155
1175
|
return (React.createElement("div", { className: "sidebar" },
|
|
1156
1176
|
React.createElement("div", { className: "sidebar-header" },
|
|
@@ -1242,7 +1262,7 @@ function SidebarComponent(props) {
|
|
|
1242
1262
|
} })))),
|
|
1243
1263
|
renderCount > 0 &&
|
|
1244
1264
|
mcpServerEnabledState.size > 0 &&
|
|
1245
|
-
toolConfigRef.current.mcpServers.length > 0 && (React.createElement("div", { className: "mode-tools-group-header" }, "MCP
|
|
1265
|
+
toolConfigRef.current.mcpServers.length > 0 && (React.createElement("div", { className: "mode-tools-group-header" }, "MCP Server Tools")),
|
|
1246
1266
|
renderCount > 0 &&
|
|
1247
1267
|
toolConfigRef.current.mcpServers
|
|
1248
1268
|
.filter(mcpServer => mcpServerEnabledState.has(mcpServer.id))
|
|
@@ -1350,8 +1370,8 @@ function InlinePromptComponent(props) {
|
|
|
1350
1370
|
chatId: UUID.uuid4(),
|
|
1351
1371
|
type: RunChatCompletionType.GenerateCode,
|
|
1352
1372
|
content: prompt,
|
|
1353
|
-
language:
|
|
1354
|
-
filename:
|
|
1373
|
+
language: props.language || 'python',
|
|
1374
|
+
filename: props.filename || 'Untitled.ipynb',
|
|
1355
1375
|
prefix: props.prefix,
|
|
1356
1376
|
suffix: props.suffix,
|
|
1357
1377
|
existingCode: props.existingCode,
|
|
@@ -1508,3 +1528,31 @@ function GitHubCopilotLoginDialogBodyComponent(props) {
|
|
|
1508
1528
|
React.createElement("button", { className: "jp-Dialog-button jp-mod-reject jp-mod-styled", onClick: handleLogoutClick },
|
|
1509
1529
|
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, "Cancel activation"))))));
|
|
1510
1530
|
}
|
|
1531
|
+
export class FormInputDialogBody extends ReactWidget {
|
|
1532
|
+
constructor(options) {
|
|
1533
|
+
super();
|
|
1534
|
+
this._fields = options.fields || [];
|
|
1535
|
+
this._onDone = options.onDone || (() => { });
|
|
1536
|
+
}
|
|
1537
|
+
render() {
|
|
1538
|
+
return (React.createElement(FormInputDialogBodyComponent, { fields: this._fields, onDone: this._onDone }));
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
function FormInputDialogBodyComponent(props) {
|
|
1542
|
+
const [formData, setFormData] = useState({});
|
|
1543
|
+
const handleInputChange = (event) => {
|
|
1544
|
+
setFormData({ ...formData, [event.target.name]: event.target.value });
|
|
1545
|
+
};
|
|
1546
|
+
return (React.createElement("div", { className: "form-input-dialog-body" },
|
|
1547
|
+
React.createElement("div", { className: "form-input-dialog-body-content" },
|
|
1548
|
+
React.createElement("div", { className: "form-input-dialog-body-content-title" }, props.title),
|
|
1549
|
+
React.createElement("div", { className: "form-input-dialog-body-content-fields" }, props.fields.map((field) => (React.createElement("div", { className: "form-input-dialog-body-content-field", key: field.name },
|
|
1550
|
+
React.createElement("label", { className: "form-input-dialog-body-content-field-label jp-mod-styled", htmlFor: field.name },
|
|
1551
|
+
field.name,
|
|
1552
|
+
field.required ? ' (required)' : ''),
|
|
1553
|
+
React.createElement("input", { className: "form-input-dialog-body-content-field-input jp-mod-styled", type: field.type, id: field.name, name: field.name, onChange: handleInputChange, value: formData[field.name] || '' }))))),
|
|
1554
|
+
React.createElement("div", null,
|
|
1555
|
+
React.createElement("div", { style: { marginTop: '10px' } },
|
|
1556
|
+
React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: () => props.onDone(formData) },
|
|
1557
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, "Done")))))));
|
|
1558
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -14,11 +14,12 @@ 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, GitHubCopilotLoginDialogBody, GitHubCopilotStatusBarItem, InlinePromptWidget, RunChatCompletionType } from './chat-sidebar';
|
|
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';
|
|
@@ -52,6 +53,7 @@ var CommandIDs;
|
|
|
52
53
|
CommandIDs.getCurrentFileContent = 'notebook-intelligence:get-current-file-content';
|
|
53
54
|
CommandIDs.setCurrentFileContent = 'notebook-intelligence:set-current-file-content';
|
|
54
55
|
CommandIDs.openMCPConfigEditor = 'notebook-intelligence:open-mcp-config-editor';
|
|
56
|
+
CommandIDs.showFormInputDialog = 'notebook-intelligence:show-form-input-dialog';
|
|
55
57
|
})(CommandIDs || (CommandIDs = {}));
|
|
56
58
|
const DOCUMENT_WATCH_INTERVAL = 1000;
|
|
57
59
|
const MAX_TOKENS = 4096;
|
|
@@ -63,6 +65,10 @@ const sparkleIcon = new LabIcon({
|
|
|
63
65
|
name: 'notebook-intelligence:sparkles-icon',
|
|
64
66
|
svgstr: sparklesSvgstr
|
|
65
67
|
});
|
|
68
|
+
const sparkleWarningIcon = new LabIcon({
|
|
69
|
+
name: 'notebook-intelligence:sparkles-warning-icon',
|
|
70
|
+
svgstr: sparklesWarningSvgstr
|
|
71
|
+
});
|
|
66
72
|
const emptyNotebookContent = {
|
|
67
73
|
cells: [],
|
|
68
74
|
metadata: {},
|
|
@@ -499,7 +505,7 @@ const plugin = {
|
|
|
499
505
|
panel.id = 'notebook-intelligence-tab';
|
|
500
506
|
panel.title.caption = 'Notebook Intelligence';
|
|
501
507
|
const sidebarIcon = new LabIcon({
|
|
502
|
-
name: '
|
|
508
|
+
name: 'notebook-intelligence:sidebar-icon',
|
|
503
509
|
svgstr: sparklesSvgstr
|
|
504
510
|
});
|
|
505
511
|
panel.title.icon = sidebarIcon;
|
|
@@ -526,6 +532,23 @@ const plugin = {
|
|
|
526
532
|
panel.addWidget(sidebar);
|
|
527
533
|
app.shell.add(panel, 'left', { rank: 1000 });
|
|
528
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);
|
|
529
552
|
app.commands.addCommand(CommandIDs.chatuserInput, {
|
|
530
553
|
execute: args => {
|
|
531
554
|
NBIAPI.sendChatUserInput(args.id, args.data);
|
|
@@ -595,6 +618,36 @@ const plugin = {
|
|
|
595
618
|
return newPyFile;
|
|
596
619
|
}
|
|
597
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
|
+
});
|
|
598
651
|
app.commands.addCommand(CommandIDs.createNewNotebookFromPython, {
|
|
599
652
|
execute: async (args) => {
|
|
600
653
|
var _a;
|
|
@@ -1143,6 +1196,8 @@ const plugin = {
|
|
|
1143
1196
|
existingCode,
|
|
1144
1197
|
prefix: prefix,
|
|
1145
1198
|
suffix: suffix,
|
|
1199
|
+
language: ActiveDocumentWatcher.activeDocumentInfo.language,
|
|
1200
|
+
filename: ActiveDocumentWatcher.activeDocumentInfo.filePath,
|
|
1146
1201
|
onRequestSubmitted: (prompt) => {
|
|
1147
1202
|
userPrompt = prompt;
|
|
1148
1203
|
generatedContent = '';
|
package/lib/tokens.d.ts
CHANGED
|
@@ -51,7 +51,9 @@ export declare enum MCPServerStatus {
|
|
|
51
51
|
FailedToConnect = "failed-to-connect",
|
|
52
52
|
Connected = "connected",
|
|
53
53
|
UpdatingToolList = "updating-tool-list",
|
|
54
|
-
UpdatedToolList = "updated-tool-list"
|
|
54
|
+
UpdatedToolList = "updated-tool-list",
|
|
55
|
+
UpdatingPromptList = "updating-prompt-list",
|
|
56
|
+
UpdatedPromptList = "updated-prompt-list"
|
|
55
57
|
}
|
|
56
58
|
export interface IContextItem {
|
|
57
59
|
type: ContextType;
|
package/lib/tokens.js
CHANGED
|
@@ -45,6 +45,8 @@ export var MCPServerStatus;
|
|
|
45
45
|
MCPServerStatus["Connected"] = "connected";
|
|
46
46
|
MCPServerStatus["UpdatingToolList"] = "updating-tool-list";
|
|
47
47
|
MCPServerStatus["UpdatedToolList"] = "updated-tool-list";
|
|
48
|
+
MCPServerStatus["UpdatingPromptList"] = "updating-prompt-list";
|
|
49
|
+
MCPServerStatus["UpdatedPromptList"] = "updated-prompt-list";
|
|
48
50
|
})(MCPServerStatus || (MCPServerStatus = {}));
|
|
49
51
|
export var BuiltinToolsetType;
|
|
50
52
|
(function (BuiltinToolsetType) {
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -76,6 +76,24 @@ export class NBIConfig {
|
|
|
76
76
|
return this.capabilities.tool_config;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
get mcpServers(): any {
|
|
80
|
+
return this.toolConfig.mcpServers;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
getMCPServer(serverId: string): any {
|
|
84
|
+
return this.toolConfig.mcpServers.find(
|
|
85
|
+
(server: any) => server.id === serverId
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getMCPServerPrompt(serverId: string, promptName: string): any {
|
|
90
|
+
const server = this.getMCPServer(serverId);
|
|
91
|
+
if (server) {
|
|
92
|
+
return server.prompts.find((prompt: any) => prompt.name === promptName);
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
79
97
|
get mcpServerSettings(): any {
|
|
80
98
|
return this.capabilities.mcp_server_settings;
|
|
81
99
|
}
|
|
@@ -152,6 +170,30 @@ export class NBIAPI {
|
|
|
152
170
|
return this._deviceVerificationInfo;
|
|
153
171
|
}
|
|
154
172
|
|
|
173
|
+
static getGHLoginRequired() {
|
|
174
|
+
return (
|
|
175
|
+
this.config.usingGitHubCopilotModel &&
|
|
176
|
+
NBIAPI.getLoginStatus() === GitHubCopilotLoginStatus.NotLoggedIn
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
static getChatEnabled() {
|
|
181
|
+
return this.config.chatModel.provider === GITHUB_COPILOT_PROVIDER_ID
|
|
182
|
+
? !this.getGHLoginRequired()
|
|
183
|
+
: this.config.llmProviders.find(
|
|
184
|
+
provider => provider.id === this.config.chatModel.provider
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
static getInlineCompletionEnabled() {
|
|
189
|
+
return this.config.inlineCompletionModel.provider ===
|
|
190
|
+
GITHUB_COPILOT_PROVIDER_ID
|
|
191
|
+
? !this.getGHLoginRequired()
|
|
192
|
+
: this.config.llmProviders.find(
|
|
193
|
+
provider => provider.id === this.config.inlineCompletionModel.provider
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
155
197
|
static async loginToGitHub() {
|
|
156
198
|
this._loginStatus = GitHubCopilotLoginStatus.ActivatingDevice;
|
|
157
199
|
return new Promise((resolve, reject) => {
|
package/src/chat-sidebar.tsx
CHANGED
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
BackendMessageType,
|
|
21
21
|
BuiltinToolsetType,
|
|
22
22
|
ContextType,
|
|
23
|
-
GITHUB_COPILOT_PROVIDER_ID,
|
|
24
23
|
IActiveDocumentInfo,
|
|
25
24
|
ICellContents,
|
|
26
25
|
IChatCompletionResponseEmitter,
|
|
@@ -118,6 +117,8 @@ export interface IInlinePromptWidgetOptions {
|
|
|
118
117
|
existingCode: string;
|
|
119
118
|
prefix: string;
|
|
120
119
|
suffix: string;
|
|
120
|
+
language?: string;
|
|
121
|
+
filename?: string;
|
|
121
122
|
onRequestSubmitted: (prompt: string) => void;
|
|
122
123
|
onRequestCancelled: () => void;
|
|
123
124
|
onContentStream: (content: string) => void;
|
|
@@ -1078,30 +1079,38 @@ function SidebarComponent(props: any) {
|
|
|
1078
1079
|
|
|
1079
1080
|
useEffect(() => {
|
|
1080
1081
|
const prefixes: string[] = [];
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1082
|
+
prefixes.push('/clear');
|
|
1083
|
+
|
|
1084
|
+
if (chatMode === 'ask') {
|
|
1085
|
+
const chatParticipants = NBIAPI.config.chatParticipants;
|
|
1086
|
+
for (const participant of chatParticipants) {
|
|
1087
|
+
const id = participant.id;
|
|
1088
|
+
const commands = participant.commands;
|
|
1089
|
+
const participantPrefix = id === 'default' ? '' : `@${id}`;
|
|
1090
|
+
if (participantPrefix !== '') {
|
|
1091
|
+
prefixes.push(participantPrefix);
|
|
1092
|
+
}
|
|
1093
|
+
const commandPrefix =
|
|
1094
|
+
participantPrefix === '' ? '' : `${participantPrefix} `;
|
|
1095
|
+
for (const command of commands) {
|
|
1096
|
+
prefixes.push(`${commandPrefix}/${command}`);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1086
1099
|
}
|
|
1087
1100
|
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
}
|
|
1096
|
-
const commandPrefix =
|
|
1097
|
-
participantPrefix === '' ? '' : `${participantPrefix} `;
|
|
1098
|
-
for (const command of commands) {
|
|
1099
|
-
prefixes.push(`${commandPrefix}/${command}`);
|
|
1101
|
+
const mcpServers = NBIAPI.config.toolConfig.mcpServers;
|
|
1102
|
+
const mcpServerSettings = NBIAPI.config.mcpServerSettings;
|
|
1103
|
+
for (const mcpServer of mcpServers) {
|
|
1104
|
+
if (mcpServerSettings[mcpServer.id]?.disabled !== true) {
|
|
1105
|
+
for (const prompt of mcpServer.prompts) {
|
|
1106
|
+
prefixes.push(`/mcp:${mcpServer.id}:${prompt.name}`);
|
|
1107
|
+
}
|
|
1100
1108
|
}
|
|
1101
1109
|
}
|
|
1110
|
+
|
|
1102
1111
|
setOriginalPrefixes(prefixes);
|
|
1103
1112
|
setPrefixSuggestions(prefixes);
|
|
1104
|
-
}, [chatMode]);
|
|
1113
|
+
}, [chatMode, renderCount]);
|
|
1105
1114
|
|
|
1106
1115
|
useEffect(() => {
|
|
1107
1116
|
const fetchData = () => {
|
|
@@ -1137,11 +1146,41 @@ function SidebarComponent(props: any) {
|
|
|
1137
1146
|
}
|
|
1138
1147
|
};
|
|
1139
1148
|
|
|
1140
|
-
const applyPrefixSuggestion = (prefix: string) => {
|
|
1149
|
+
const applyPrefixSuggestion = async (prefix: string) => {
|
|
1150
|
+
let mcpArguments = '';
|
|
1151
|
+
if (prefix.startsWith('/mcp:')) {
|
|
1152
|
+
mcpArguments = ':';
|
|
1153
|
+
const serverId = prefix.split(':')[1];
|
|
1154
|
+
const promptName = prefix.split(':')[2];
|
|
1155
|
+
const promptConfig = NBIAPI.config.getMCPServerPrompt(
|
|
1156
|
+
serverId,
|
|
1157
|
+
promptName
|
|
1158
|
+
);
|
|
1159
|
+
if (
|
|
1160
|
+
promptConfig &&
|
|
1161
|
+
promptConfig.arguments &&
|
|
1162
|
+
promptConfig.arguments.length > 0
|
|
1163
|
+
) {
|
|
1164
|
+
const result = await props
|
|
1165
|
+
.getApp()
|
|
1166
|
+
.commands.execute('notebook-intelligence:show-form-input-dialog', {
|
|
1167
|
+
title: 'Input Parameters',
|
|
1168
|
+
fields: promptConfig.arguments
|
|
1169
|
+
});
|
|
1170
|
+
const argumentValues: string[] = [];
|
|
1171
|
+
for (const argument of promptConfig.arguments) {
|
|
1172
|
+
if (result[argument.name] !== undefined) {
|
|
1173
|
+
argumentValues.push(`${argument.name}=${result[argument.name]}`);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
mcpArguments = `(${argumentValues.join(', ')}):`;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1141
1180
|
if (prefix.includes(prompt)) {
|
|
1142
|
-
setPrompt(`${prefix} `);
|
|
1181
|
+
setPrompt(`${prefix}${mcpArguments} `);
|
|
1143
1182
|
} else {
|
|
1144
|
-
setPrompt(`${prefix} ${prompt} `);
|
|
1183
|
+
setPrompt(`${prefix} ${prompt}${mcpArguments} `);
|
|
1145
1184
|
}
|
|
1146
1185
|
setShowPopover(false);
|
|
1147
1186
|
promptInputRef.current?.focus();
|
|
@@ -1201,9 +1240,6 @@ function SidebarComponent(props: any) {
|
|
|
1201
1240
|
}
|
|
1202
1241
|
}
|
|
1203
1242
|
|
|
1204
|
-
const promptPrefix =
|
|
1205
|
-
promptPrefixParts.length > 0 ? promptPrefixParts.join(' ') + ' ' : '';
|
|
1206
|
-
|
|
1207
1243
|
lastMessageId.current = UUID.uuid4();
|
|
1208
1244
|
lastRequestTime.current = new Date();
|
|
1209
1245
|
|
|
@@ -1275,7 +1311,7 @@ function SidebarComponent(props: any) {
|
|
|
1275
1311
|
type: RunChatCompletionType.Chat,
|
|
1276
1312
|
content: extractedPrompt,
|
|
1277
1313
|
language: activeDocInfo.language,
|
|
1278
|
-
filename: activeDocInfo.
|
|
1314
|
+
filename: activeDocInfo.filePath,
|
|
1279
1315
|
additionalContext,
|
|
1280
1316
|
chatMode,
|
|
1281
1317
|
toolSelections: toolSelections
|
|
@@ -1368,7 +1404,7 @@ function SidebarComponent(props: any) {
|
|
|
1368
1404
|
}
|
|
1369
1405
|
);
|
|
1370
1406
|
|
|
1371
|
-
const newPrompt =
|
|
1407
|
+
const newPrompt = '';
|
|
1372
1408
|
|
|
1373
1409
|
setPrompt(newPrompt);
|
|
1374
1410
|
filterPrefixSuggestions(newPrompt);
|
|
@@ -1697,34 +1733,21 @@ function SidebarComponent(props: any) {
|
|
|
1697
1733
|
return `${activeDocumentInfo.filename}${cellAndLineIndicator}`;
|
|
1698
1734
|
};
|
|
1699
1735
|
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
NBIAPI.getLoginStatus() === GitHubCopilotLoginStatus.NotLoggedIn
|
|
1705
|
-
);
|
|
1706
|
-
};
|
|
1707
|
-
const getChatEnabled = () => {
|
|
1708
|
-
return nbiConfig.chatModel.provider === GITHUB_COPILOT_PROVIDER_ID
|
|
1709
|
-
? !getGHLoginRequired()
|
|
1710
|
-
: nbiConfig.llmProviders.find(
|
|
1711
|
-
provider => provider.id === nbiConfig.chatModel.provider
|
|
1712
|
-
);
|
|
1713
|
-
};
|
|
1714
|
-
|
|
1715
|
-
const [ghLoginRequired, setGHLoginRequired] = useState(getGHLoginRequired());
|
|
1716
|
-
const [chatEnabled, setChatEnabled] = useState(getChatEnabled());
|
|
1736
|
+
const [ghLoginRequired, setGHLoginRequired] = useState(
|
|
1737
|
+
NBIAPI.getGHLoginRequired()
|
|
1738
|
+
);
|
|
1739
|
+
const [chatEnabled, setChatEnabled] = useState(NBIAPI.getChatEnabled());
|
|
1717
1740
|
|
|
1718
1741
|
useEffect(() => {
|
|
1719
1742
|
NBIAPI.configChanged.connect(() => {
|
|
1720
|
-
setGHLoginRequired(getGHLoginRequired());
|
|
1721
|
-
setChatEnabled(getChatEnabled());
|
|
1743
|
+
setGHLoginRequired(NBIAPI.getGHLoginRequired());
|
|
1744
|
+
setChatEnabled(NBIAPI.getChatEnabled());
|
|
1722
1745
|
});
|
|
1723
1746
|
}, []);
|
|
1724
1747
|
|
|
1725
1748
|
useEffect(() => {
|
|
1726
|
-
setGHLoginRequired(getGHLoginRequired());
|
|
1727
|
-
setChatEnabled(getChatEnabled());
|
|
1749
|
+
setGHLoginRequired(NBIAPI.getGHLoginRequired());
|
|
1750
|
+
setChatEnabled(NBIAPI.getChatEnabled());
|
|
1728
1751
|
}, [ghLoginStatus]);
|
|
1729
1752
|
|
|
1730
1753
|
return (
|
|
@@ -1986,7 +2009,9 @@ function SidebarComponent(props: any) {
|
|
|
1986
2009
|
{renderCount > 0 &&
|
|
1987
2010
|
mcpServerEnabledState.size > 0 &&
|
|
1988
2011
|
toolConfigRef.current.mcpServers.length > 0 && (
|
|
1989
|
-
<div className="mode-tools-group-header">
|
|
2012
|
+
<div className="mode-tools-group-header">
|
|
2013
|
+
MCP Server Tools
|
|
2014
|
+
</div>
|
|
1990
2015
|
)}
|
|
1991
2016
|
{renderCount > 0 &&
|
|
1992
2017
|
toolConfigRef.current.mcpServers
|
|
@@ -2236,8 +2261,8 @@ function InlinePromptComponent(props: any) {
|
|
|
2236
2261
|
chatId: UUID.uuid4(),
|
|
2237
2262
|
type: RunChatCompletionType.GenerateCode,
|
|
2238
2263
|
content: prompt,
|
|
2239
|
-
language:
|
|
2240
|
-
filename:
|
|
2264
|
+
language: props.language || 'python',
|
|
2265
|
+
filename: props.filename || 'Untitled.ipynb',
|
|
2241
2266
|
prefix: props.prefix,
|
|
2242
2267
|
suffix: props.suffix,
|
|
2243
2268
|
existingCode: props.existingCode,
|
|
@@ -2509,3 +2534,76 @@ function GitHubCopilotLoginDialogBodyComponent(props: any) {
|
|
|
2509
2534
|
</div>
|
|
2510
2535
|
);
|
|
2511
2536
|
}
|
|
2537
|
+
|
|
2538
|
+
export class FormInputDialogBody extends ReactWidget {
|
|
2539
|
+
constructor(options: { fields: any; onDone: (formData: any) => void }) {
|
|
2540
|
+
super();
|
|
2541
|
+
|
|
2542
|
+
this._fields = options.fields || [];
|
|
2543
|
+
this._onDone = options.onDone || (() => {});
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
render(): JSX.Element {
|
|
2547
|
+
return (
|
|
2548
|
+
<FormInputDialogBodyComponent
|
|
2549
|
+
fields={this._fields}
|
|
2550
|
+
onDone={this._onDone}
|
|
2551
|
+
/>
|
|
2552
|
+
);
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2555
|
+
private _fields: any;
|
|
2556
|
+
private _onDone: (formData: any) => void;
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
function FormInputDialogBodyComponent(props: any) {
|
|
2560
|
+
const [formData, setFormData] = useState<any>({});
|
|
2561
|
+
|
|
2562
|
+
const handleInputChange = (event: any) => {
|
|
2563
|
+
setFormData({ ...formData, [event.target.name]: event.target.value });
|
|
2564
|
+
};
|
|
2565
|
+
|
|
2566
|
+
return (
|
|
2567
|
+
<div className="form-input-dialog-body">
|
|
2568
|
+
<div className="form-input-dialog-body-content">
|
|
2569
|
+
<div className="form-input-dialog-body-content-title">
|
|
2570
|
+
{props.title}
|
|
2571
|
+
</div>
|
|
2572
|
+
<div className="form-input-dialog-body-content-fields">
|
|
2573
|
+
{props.fields.map((field: any) => (
|
|
2574
|
+
<div
|
|
2575
|
+
className="form-input-dialog-body-content-field"
|
|
2576
|
+
key={field.name}
|
|
2577
|
+
>
|
|
2578
|
+
<label
|
|
2579
|
+
className="form-input-dialog-body-content-field-label jp-mod-styled"
|
|
2580
|
+
htmlFor={field.name}
|
|
2581
|
+
>
|
|
2582
|
+
{field.name}
|
|
2583
|
+
{field.required ? ' (required)' : ''}
|
|
2584
|
+
</label>
|
|
2585
|
+
<input
|
|
2586
|
+
className="form-input-dialog-body-content-field-input jp-mod-styled"
|
|
2587
|
+
type={field.type}
|
|
2588
|
+
id={field.name}
|
|
2589
|
+
name={field.name}
|
|
2590
|
+
onChange={handleInputChange}
|
|
2591
|
+
value={formData[field.name] || ''}
|
|
2592
|
+
/>
|
|
2593
|
+
</div>
|
|
2594
|
+
))}
|
|
2595
|
+
</div>
|
|
2596
|
+
<div>
|
|
2597
|
+
<div style={{ marginTop: '10px' }}>
|
|
2598
|
+
<button
|
|
2599
|
+
className="jp-Dialog-button jp-mod-accept jp-mod-styled"
|
|
2600
|
+
onClick={() => props.onDone(formData)}
|
|
2601
|
+
>
|
|
2602
|
+
<div className="jp-Dialog-buttonLabel">Done</div>
|
|
2603
|
+
</button>
|
|
2604
|
+
</div>
|
|
2605
|
+
</div>
|
|
2606
|
+
</div>
|
|
2607
|
+
</div>
|
|
2608
|
+
);
|
|
2609
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -44,6 +44,7 @@ import { IStatusBar } from '@jupyterlab/statusbar';
|
|
|
44
44
|
|
|
45
45
|
import {
|
|
46
46
|
ChatSidebar,
|
|
47
|
+
FormInputDialogBody,
|
|
47
48
|
GitHubCopilotLoginDialogBody,
|
|
48
49
|
GitHubCopilotStatusBarItem,
|
|
49
50
|
InlinePromptWidget,
|
|
@@ -64,6 +65,7 @@ import {
|
|
|
64
65
|
} from './tokens';
|
|
65
66
|
import sparklesSvgstr from '../style/icons/sparkles.svg';
|
|
66
67
|
import copilotSvgstr from '../style/icons/copilot.svg';
|
|
68
|
+
import sparklesWarningSvgstr from '../style/icons/sparkles-warning.svg';
|
|
67
69
|
|
|
68
70
|
import {
|
|
69
71
|
applyCodeToSelectionInEditor,
|
|
@@ -126,6 +128,8 @@ namespace CommandIDs {
|
|
|
126
128
|
'notebook-intelligence:set-current-file-content';
|
|
127
129
|
export const openMCPConfigEditor =
|
|
128
130
|
'notebook-intelligence:open-mcp-config-editor';
|
|
131
|
+
export const showFormInputDialog =
|
|
132
|
+
'notebook-intelligence:show-form-input-dialog';
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
const DOCUMENT_WATCH_INTERVAL = 1000;
|
|
@@ -140,6 +144,10 @@ const sparkleIcon = new LabIcon({
|
|
|
140
144
|
svgstr: sparklesSvgstr
|
|
141
145
|
});
|
|
142
146
|
|
|
147
|
+
const sparkleWarningIcon = new LabIcon({
|
|
148
|
+
name: 'notebook-intelligence:sparkles-warning-icon',
|
|
149
|
+
svgstr: sparklesWarningSvgstr
|
|
150
|
+
});
|
|
143
151
|
const emptyNotebookContent: any = {
|
|
144
152
|
cells: [],
|
|
145
153
|
metadata: {},
|
|
@@ -711,7 +719,7 @@ const plugin: JupyterFrontEndPlugin<INotebookIntelligence> = {
|
|
|
711
719
|
panel.id = 'notebook-intelligence-tab';
|
|
712
720
|
panel.title.caption = 'Notebook Intelligence';
|
|
713
721
|
const sidebarIcon = new LabIcon({
|
|
714
|
-
name: '
|
|
722
|
+
name: 'notebook-intelligence:sidebar-icon',
|
|
715
723
|
svgstr: sparklesSvgstr
|
|
716
724
|
});
|
|
717
725
|
panel.title.icon = sidebarIcon;
|
|
@@ -739,6 +747,26 @@ const plugin: JupyterFrontEndPlugin<INotebookIntelligence> = {
|
|
|
739
747
|
app.shell.add(panel, 'left', { rank: 1000 });
|
|
740
748
|
app.shell.activateById(panel.id);
|
|
741
749
|
|
|
750
|
+
const updateSidebarIcon = () => {
|
|
751
|
+
if (NBIAPI.getChatEnabled()) {
|
|
752
|
+
panel.title.icon = sidebarIcon;
|
|
753
|
+
} else {
|
|
754
|
+
panel.title.icon = sparkleWarningIcon;
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
NBIAPI.githubLoginStatusChanged.connect((_, args) => {
|
|
759
|
+
updateSidebarIcon();
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
NBIAPI.configChanged.connect((_, args) => {
|
|
763
|
+
updateSidebarIcon();
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
setTimeout(() => {
|
|
767
|
+
updateSidebarIcon();
|
|
768
|
+
}, 2000);
|
|
769
|
+
|
|
742
770
|
app.commands.addCommand(CommandIDs.chatuserInput, {
|
|
743
771
|
execute: args => {
|
|
744
772
|
NBIAPI.sendChatUserInput(args.id as string, args.data);
|
|
@@ -820,6 +848,39 @@ const plugin: JupyterFrontEndPlugin<INotebookIntelligence> = {
|
|
|
820
848
|
}
|
|
821
849
|
});
|
|
822
850
|
|
|
851
|
+
app.commands.addCommand(CommandIDs.showFormInputDialog, {
|
|
852
|
+
execute: async args => {
|
|
853
|
+
const title = args.title as string;
|
|
854
|
+
const fields = args.fields;
|
|
855
|
+
|
|
856
|
+
return new Promise<any>((resolve, reject) => {
|
|
857
|
+
let dialog: Dialog<unknown> | null = null;
|
|
858
|
+
const dialogBody = new FormInputDialogBody({
|
|
859
|
+
fields: fields,
|
|
860
|
+
onDone: (formData: any) => {
|
|
861
|
+
dialog.dispose();
|
|
862
|
+
resolve(formData);
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
dialog = new Dialog({
|
|
866
|
+
title: title,
|
|
867
|
+
hasClose: true,
|
|
868
|
+
body: dialogBody,
|
|
869
|
+
buttons: []
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
dialog
|
|
873
|
+
.launch()
|
|
874
|
+
.then((result: any) => {
|
|
875
|
+
reject();
|
|
876
|
+
})
|
|
877
|
+
.catch(() => {
|
|
878
|
+
reject(new Error('Failed to show form input dialog'));
|
|
879
|
+
});
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
|
|
823
884
|
app.commands.addCommand(CommandIDs.createNewNotebookFromPython, {
|
|
824
885
|
execute: async args => {
|
|
825
886
|
let pythonKernelSpec = null;
|
|
@@ -1501,6 +1562,8 @@ const plugin: JupyterFrontEndPlugin<INotebookIntelligence> = {
|
|
|
1501
1562
|
existingCode,
|
|
1502
1563
|
prefix: prefix,
|
|
1503
1564
|
suffix: suffix,
|
|
1565
|
+
language: ActiveDocumentWatcher.activeDocumentInfo.language,
|
|
1566
|
+
filename: ActiveDocumentWatcher.activeDocumentInfo.filePath,
|
|
1504
1567
|
onRequestSubmitted: (prompt: string) => {
|
|
1505
1568
|
userPrompt = prompt;
|
|
1506
1569
|
generatedContent = '';
|
package/src/tokens.ts
CHANGED
|
@@ -60,7 +60,9 @@ export enum MCPServerStatus {
|
|
|
60
60
|
FailedToConnect = 'failed-to-connect',
|
|
61
61
|
Connected = 'connected',
|
|
62
62
|
UpdatingToolList = 'updating-tool-list',
|
|
63
|
-
UpdatedToolList = 'updated-tool-list'
|
|
63
|
+
UpdatedToolList = 'updated-tool-list',
|
|
64
|
+
UpdatingPromptList = 'updating-prompt-list',
|
|
65
|
+
UpdatedPromptList = 'updated-prompt-list'
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
export interface IContextItem {
|
package/style/base.css
CHANGED
|
@@ -890,13 +890,15 @@ svg.access-token-warning {
|
|
|
890
890
|
}
|
|
891
891
|
|
|
892
892
|
.server-status-indicator.connected,
|
|
893
|
-
.server-status-indicator.updated-tool-list
|
|
893
|
+
.server-status-indicator.updated-tool-list,
|
|
894
|
+
.server-status-indicator.updated-prompt-list {
|
|
894
895
|
background-color: var(--jp-success-color1);
|
|
895
896
|
}
|
|
896
897
|
|
|
897
898
|
.server-status-indicator.connecting,
|
|
898
899
|
.server-status-indicator.disconnecting,
|
|
899
|
-
.server-status-indicator.updating-tool-list
|
|
900
|
+
.server-status-indicator.updating-tool-list,
|
|
901
|
+
.server-status-indicator.updating-prompt-list {
|
|
900
902
|
background-color: var(--jp-warn-color1);
|
|
901
903
|
}
|
|
902
904
|
|
|
@@ -904,3 +906,27 @@ svg.access-token-warning {
|
|
|
904
906
|
.server-status-indicator.failed-to-connect {
|
|
905
907
|
background-color: var(--jp-error-color1);
|
|
906
908
|
}
|
|
909
|
+
|
|
910
|
+
.form-input-dialog-body {
|
|
911
|
+
width: 500px;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
.form-input-dialog-body-content-fields {
|
|
915
|
+
display: flex;
|
|
916
|
+
flex-direction: column;
|
|
917
|
+
gap: 10px;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
.form-input-dialog-body-content-field {
|
|
921
|
+
display: flex;
|
|
922
|
+
flex-direction: row;
|
|
923
|
+
align-items: center;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
.form-input-dialog-body-content-field-label {
|
|
927
|
+
width: 50%;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
.form-input-dialog-body-content-field-input {
|
|
931
|
+
width: 50%;
|
|
932
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="var(--jp-inverse-layout-color3)"><path d="M 5.398 10.807 C 5.574 10.931 5.785 10.998 6 10.997 C 6.216 10.998 6.427 10.93 6.602 10.804 C 6.78 10.674 6.915 10.494 6.989 10.286 L 7.436 8.913 C 7.551 8.569 7.744 8.256 8 7.999 C 8.257 7.743 8.569 7.549 8.913 7.434 L 10.304 6.983 C 10.456 6.929 10.594 6.84 10.706 6.724 C 10.817 6.608 10.901 6.467 10.949 6.313 C 10.998 6.159 11.01 5.996 10.985 5.837 C 10.96 5.677 10.898 5.526 10.804 5.394 C 10.67 5.208 10.479 5.071 10.26 5.003 L 8.885 4.556 C 8.541 4.442 8.228 4.249 7.971 3.993 C 7.714 3.736 7.52 3.424 7.405 3.079 L 6.953 1.691 C 6.881 1.489 6.748 1.314 6.571 1.191 C 6.439 1.098 6.286 1.036 6.125 1.012 C 5.965 0.987 5.801 1.001 5.646 1.051 C 5.492 1.101 5.351 1.187 5.236 1.301 C 5.12 1.415 5.033 1.555 4.98 1.708 L 4.523 3.108 C 4.409 3.443 4.22 3.748 3.97 3.999 C 3.721 4.25 3.418 4.441 3.083 4.557 L 1.692 5.005 C 1.541 5.06 1.404 5.149 1.292 5.265 C 1.18 5.381 1.097 5.521 1.048 5.675 C 1 5.829 0.988 5.992 1.013 6.151 C 1.038 6.31 1.099 6.462 1.192 6.593 C 1.32 6.773 1.501 6.908 1.709 6.979 L 3.083 7.424 C 3.524 7.571 3.91 7.845 4.193 8.212 C 4.356 8.423 4.481 8.66 4.564 8.912 L 5.016 10.303 C 5.088 10.507 5.222 10.683 5.398 10.807 Z M 11.535 14.849 C 11.671 14.946 11.834 14.997 12 14.997 C 12.165 14.997 12.326 14.946 12.461 14.851 C 12.601 14.753 12.706 14.613 12.761 14.451 L 13.009 13.689 C 13.063 13.531 13.152 13.387 13.269 13.268 C 13.387 13.15 13.531 13.061 13.689 13.009 L 14.461 12.757 C 14.619 12.703 14.756 12.6 14.852 12.464 C 14.926 12.361 14.974 12.242 14.992 12.117 C 15.011 11.992 14.999 11.865 14.959 11.745 C 14.918 11.625 14.85 11.516 14.76 11.428 C 14.669 11.34 14.559 11.274 14.438 11.236 L 13.674 10.987 C 13.516 10.935 13.372 10.846 13.254 10.729 C 13.136 10.611 13.047 10.467 12.994 10.309 L 12.742 9.536 C 12.689 9.379 12.586 9.242 12.449 9.146 C 12.347 9.073 12.23 9.025 12.106 9.006 C 11.982 8.987 11.855 8.998 11.736 9.037 C 11.616 9.076 11.508 9.142 11.419 9.231 C 11.33 9.319 11.264 9.427 11.224 9.546 L 10.977 10.308 C 10.925 10.466 10.838 10.61 10.721 10.728 C 10.607 10.845 10.467 10.934 10.312 10.987 L 9.539 11.239 C 9.38 11.293 9.242 11.396 9.145 11.533 C 9.047 11.669 8.995 11.833 8.996 12.001 C 8.997 12.169 9.051 12.333 9.15 12.468 C 9.249 12.604 9.388 12.705 9.547 12.757 L 10.31 13.004 C 10.469 13.058 10.614 13.147 10.732 13.265 C 10.851 13.384 10.939 13.528 10.99 13.687 L 11.243 14.461 C 11.298 14.618 11.4 14.753 11.535 14.849 Z"/>
|
|
2
|
+
<g class="layer">
|
|
3
|
+
<circle cx="12" cy="4" fill="#FF0000" r="4" stroke="#FFFFFF" stroke-width="0"/>
|
|
4
|
+
</g>
|
|
5
|
+
</svg>
|