@jupyterlite/ai 0.8.0 → 0.9.0-a0
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/lib/agent.d.ts +233 -0
- package/lib/agent.js +604 -0
- package/lib/chat-model.d.ts +195 -0
- package/lib/chat-model.js +590 -0
- package/lib/completion/completion-provider.d.ts +83 -0
- package/lib/completion/completion-provider.js +209 -0
- package/lib/completion/index.d.ts +1 -0
- package/lib/completion/index.js +1 -0
- package/lib/components/clear-button.d.ts +18 -0
- package/lib/components/clear-button.js +31 -0
- package/lib/components/index.d.ts +3 -0
- package/lib/components/index.js +3 -0
- package/lib/components/model-select.d.ts +19 -0
- package/lib/components/model-select.js +154 -0
- package/lib/components/stop-button.d.ts +3 -3
- package/lib/components/stop-button.js +8 -9
- package/lib/components/token-usage-display.d.ts +45 -0
- package/lib/components/token-usage-display.js +74 -0
- package/lib/components/tool-select.d.ts +27 -0
- package/lib/components/tool-select.js +130 -0
- package/lib/icons.d.ts +3 -1
- package/lib/icons.js +10 -13
- package/lib/index.d.ts +4 -5
- package/lib/index.js +322 -167
- package/lib/mcp/browser.d.ts +68 -0
- package/lib/mcp/browser.js +132 -0
- package/lib/models/settings-model.d.ts +69 -0
- package/lib/models/settings-model.js +295 -0
- package/lib/providers/built-in-providers.d.ts +9 -0
- package/lib/providers/built-in-providers.js +192 -0
- package/lib/providers/models.d.ts +37 -0
- package/lib/providers/models.js +28 -0
- package/lib/providers/provider-registry.d.ts +94 -0
- package/lib/providers/provider-registry.js +155 -0
- package/lib/tokens.d.ts +157 -86
- package/lib/tokens.js +16 -12
- package/lib/tools/commands.d.ts +11 -0
- package/lib/tools/commands.js +126 -0
- package/lib/tools/file.d.ts +27 -0
- package/lib/tools/file.js +262 -0
- package/lib/tools/notebook.d.ts +40 -0
- package/lib/tools/notebook.js +762 -0
- package/lib/tools/tool-registry.d.ts +35 -0
- package/lib/tools/tool-registry.js +55 -0
- package/lib/widgets/ai-settings.d.ts +39 -0
- package/lib/widgets/ai-settings.js +506 -0
- package/lib/widgets/chat-wrapper.d.ts +144 -0
- package/lib/widgets/chat-wrapper.js +390 -0
- package/lib/widgets/provider-config-dialog.d.ts +13 -0
- package/lib/widgets/provider-config-dialog.js +104 -0
- package/package.json +150 -41
- package/schema/settings-model.json +153 -0
- package/src/agent.ts +800 -0
- package/src/chat-model.ts +770 -0
- package/src/completion/completion-provider.ts +308 -0
- package/src/completion/index.ts +1 -0
- package/src/components/clear-button.tsx +56 -0
- package/src/components/index.ts +3 -0
- package/src/components/model-select.tsx +245 -0
- package/src/components/stop-button.tsx +11 -11
- package/src/components/token-usage-display.tsx +130 -0
- package/src/components/tool-select.tsx +218 -0
- package/src/icons.ts +12 -14
- package/src/index.ts +468 -238
- package/src/mcp/browser.ts +213 -0
- package/src/models/settings-model.ts +409 -0
- package/src/providers/built-in-providers.ts +216 -0
- package/src/providers/models.ts +79 -0
- package/src/providers/provider-registry.ts +189 -0
- package/src/tokens.ts +203 -90
- package/src/tools/commands.ts +151 -0
- package/src/tools/file.ts +307 -0
- package/src/tools/notebook.ts +964 -0
- package/src/tools/tool-registry.ts +63 -0
- package/src/types.d.ts +4 -0
- package/src/widgets/ai-settings.tsx +1100 -0
- package/src/widgets/chat-wrapper.tsx +543 -0
- package/src/widgets/provider-config-dialog.tsx +256 -0
- package/style/base.css +335 -14
- package/style/icons/jupyternaut-lite.svg +1 -1
- package/lib/base-completer.d.ts +0 -49
- package/lib/base-completer.js +0 -14
- package/lib/chat-handler.d.ts +0 -56
- package/lib/chat-handler.js +0 -201
- package/lib/completion-provider.d.ts +0 -34
- package/lib/completion-provider.js +0 -32
- package/lib/default-prompts.d.ts +0 -2
- package/lib/default-prompts.js +0 -31
- package/lib/default-providers/Anthropic/completer.d.ts +0 -12
- package/lib/default-providers/Anthropic/completer.js +0 -46
- package/lib/default-providers/Anthropic/settings-schema.json +0 -70
- package/lib/default-providers/ChromeAI/completer.d.ts +0 -12
- package/lib/default-providers/ChromeAI/completer.js +0 -56
- package/lib/default-providers/ChromeAI/instructions.d.ts +0 -6
- package/lib/default-providers/ChromeAI/instructions.js +0 -42
- package/lib/default-providers/ChromeAI/settings-schema.json +0 -18
- package/lib/default-providers/Gemini/completer.d.ts +0 -12
- package/lib/default-providers/Gemini/completer.js +0 -48
- package/lib/default-providers/Gemini/instructions.d.ts +0 -2
- package/lib/default-providers/Gemini/instructions.js +0 -9
- package/lib/default-providers/Gemini/settings-schema.json +0 -64
- package/lib/default-providers/MistralAI/completer.d.ts +0 -13
- package/lib/default-providers/MistralAI/completer.js +0 -52
- package/lib/default-providers/MistralAI/instructions.d.ts +0 -2
- package/lib/default-providers/MistralAI/instructions.js +0 -18
- package/lib/default-providers/MistralAI/settings-schema.json +0 -75
- package/lib/default-providers/Ollama/completer.d.ts +0 -12
- package/lib/default-providers/Ollama/completer.js +0 -43
- package/lib/default-providers/Ollama/instructions.d.ts +0 -2
- package/lib/default-providers/Ollama/instructions.js +0 -70
- package/lib/default-providers/Ollama/settings-schema.json +0 -143
- package/lib/default-providers/OpenAI/completer.d.ts +0 -12
- package/lib/default-providers/OpenAI/completer.js +0 -43
- package/lib/default-providers/OpenAI/settings-schema.json +0 -628
- package/lib/default-providers/WebLLM/completer.d.ts +0 -21
- package/lib/default-providers/WebLLM/completer.js +0 -127
- package/lib/default-providers/WebLLM/instructions.d.ts +0 -6
- package/lib/default-providers/WebLLM/instructions.js +0 -32
- package/lib/default-providers/WebLLM/settings-schema.json +0 -19
- package/lib/default-providers/index.d.ts +0 -2
- package/lib/default-providers/index.js +0 -179
- package/lib/provider.d.ts +0 -144
- package/lib/provider.js +0 -412
- package/lib/settings/base.json +0 -7
- package/lib/settings/index.d.ts +0 -3
- package/lib/settings/index.js +0 -3
- package/lib/settings/panel.d.ts +0 -226
- package/lib/settings/panel.js +0 -510
- package/lib/settings/textarea.d.ts +0 -2
- package/lib/settings/textarea.js +0 -18
- package/lib/settings/utils.d.ts +0 -2
- package/lib/settings/utils.js +0 -4
- package/lib/types/ai-model.d.ts +0 -24
- package/lib/types/ai-model.js +0 -5
- package/schema/chat.json +0 -28
- package/schema/provider-registry.json +0 -29
- package/schema/system-prompts.json +0 -22
- package/src/base-completer.ts +0 -75
- package/src/chat-handler.ts +0 -262
- package/src/completion-provider.ts +0 -64
- package/src/default-prompts.ts +0 -33
- package/src/default-providers/Anthropic/completer.ts +0 -59
- package/src/default-providers/ChromeAI/completer.ts +0 -73
- package/src/default-providers/ChromeAI/instructions.ts +0 -45
- package/src/default-providers/Gemini/completer.ts +0 -61
- package/src/default-providers/Gemini/instructions.ts +0 -9
- package/src/default-providers/MistralAI/completer.ts +0 -69
- package/src/default-providers/MistralAI/instructions.ts +0 -18
- package/src/default-providers/Ollama/completer.ts +0 -54
- package/src/default-providers/Ollama/instructions.ts +0 -70
- package/src/default-providers/OpenAI/completer.ts +0 -54
- package/src/default-providers/WebLLM/completer.ts +0 -151
- package/src/default-providers/WebLLM/instructions.ts +0 -33
- package/src/default-providers/index.ts +0 -211
- package/src/global.d.ts +0 -9
- package/src/provider.ts +0 -514
- package/src/settings/index.ts +0 -3
- package/src/settings/panel.tsx +0 -773
- package/src/settings/textarea.tsx +0 -33
- package/src/settings/utils.ts +0 -5
- package/src/types/ai-model.ts +0 -37
- package/src/types/service-worker.d.ts +0 -6
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
|
+
/**
|
|
3
|
+
* Browser-compatible MCP Server implementation
|
|
4
|
+
*
|
|
5
|
+
* This is a custom implementation that works around the limitation in
|
|
6
|
+
* @openai/agents where MCPServerStreamableHttp doesn't work in browsers
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Browser-compatible MCP Server implementation that works around limitations
|
|
10
|
+
* in @openai/agents where MCPServerStreamableHttp doesn't work in browsers.
|
|
11
|
+
*
|
|
12
|
+
* This class provides a streamable HTTP client transport for MCP (Model Context Protocol)
|
|
13
|
+
* servers that can be used in browser environments.
|
|
14
|
+
*/
|
|
15
|
+
export class BrowserMCPServerStreamableHttp {
|
|
16
|
+
name;
|
|
17
|
+
cacheToolsList;
|
|
18
|
+
toolFilter = undefined;
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this._options = options;
|
|
21
|
+
this.name = options.name || `browser-mcp-server: ${options.url}`;
|
|
22
|
+
this.cacheToolsList = options.cacheToolsList ?? false;
|
|
23
|
+
}
|
|
24
|
+
async connect() {
|
|
25
|
+
try {
|
|
26
|
+
// Dynamic import to handle cases where MCP SDK isn't available
|
|
27
|
+
const { StreamableHTTPClientTransport } = await import('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
28
|
+
const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
|
|
29
|
+
this._transport = new StreamableHTTPClientTransport(new URL(this._options.url), {
|
|
30
|
+
authProvider: this._options.authProvider,
|
|
31
|
+
requestInit: this._options.requestInit,
|
|
32
|
+
fetch: this._options.fetch || fetch,
|
|
33
|
+
reconnectionOptions: this._options.reconnectionOptions,
|
|
34
|
+
sessionId: this._options.sessionId
|
|
35
|
+
});
|
|
36
|
+
this._session = new Client({
|
|
37
|
+
name: this.name,
|
|
38
|
+
version: '1.0.0'
|
|
39
|
+
});
|
|
40
|
+
await this._session.connect(this._transport);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error('Error initializing MCP server:', error);
|
|
44
|
+
await this.close();
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async close() {
|
|
49
|
+
if (this._session) {
|
|
50
|
+
try {
|
|
51
|
+
await this._session.close();
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error('Error closing MCP server session:', error);
|
|
55
|
+
}
|
|
56
|
+
this._session = null;
|
|
57
|
+
}
|
|
58
|
+
if (this._transport) {
|
|
59
|
+
try {
|
|
60
|
+
await this._transport.close();
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
console.error('Error closing MCP server transport:', error);
|
|
64
|
+
}
|
|
65
|
+
this._transport = null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async listTools() {
|
|
69
|
+
if (!this._session) {
|
|
70
|
+
throw new Error('Server not initialized. Call connect() first.');
|
|
71
|
+
}
|
|
72
|
+
if (this.cacheToolsList &&
|
|
73
|
+
!this._cacheDirty &&
|
|
74
|
+
this._toolsList.length > 0) {
|
|
75
|
+
return this._toolsList;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const { ListToolsResultSchema } = await import('@modelcontextprotocol/sdk/types.js');
|
|
79
|
+
const response = await this._session.listTools();
|
|
80
|
+
const parsedResponse = ListToolsResultSchema.parse(response);
|
|
81
|
+
// Map to openai/agents MCPTool type
|
|
82
|
+
this._toolsList = parsedResponse.tools.map((tool) => ({
|
|
83
|
+
name: tool.name,
|
|
84
|
+
description: tool.description,
|
|
85
|
+
inputSchema: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: tool.inputSchema?.properties || {},
|
|
88
|
+
required: tool.inputSchema?.required || [],
|
|
89
|
+
additionalProperties: tool.inputSchema?.additionalProperties ?? false
|
|
90
|
+
}
|
|
91
|
+
}));
|
|
92
|
+
this._cacheDirty = false;
|
|
93
|
+
return this._toolsList;
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
console.error(`Error listing tools from ${this.name}:`, error);
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async callTool(toolName, args) {
|
|
101
|
+
if (!this._session) {
|
|
102
|
+
throw new Error('Server not initialized. Call connect() first.');
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const { CallToolResultSchema } = await import('@modelcontextprotocol/sdk/types.js');
|
|
106
|
+
const response = await this._session.callTool({
|
|
107
|
+
name: toolName,
|
|
108
|
+
arguments: args ?? {}
|
|
109
|
+
}, undefined, {
|
|
110
|
+
timeout: this._options.timeout ?? 30000
|
|
111
|
+
});
|
|
112
|
+
// Parse and validate using MCP SDK schema
|
|
113
|
+
const parsed = CallToolResultSchema.parse(response);
|
|
114
|
+
const result = parsed.content;
|
|
115
|
+
// Return the content array as expected by openai/agents
|
|
116
|
+
// CallToolResultContent is { type: string; text: string }[]
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
console.error(`Error calling tool ${toolName}:`, error);
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async invalidateToolsCache() {
|
|
125
|
+
this._cacheDirty = true;
|
|
126
|
+
}
|
|
127
|
+
_session = null;
|
|
128
|
+
_toolsList = [];
|
|
129
|
+
_cacheDirty = true;
|
|
130
|
+
_transport = null;
|
|
131
|
+
_options;
|
|
132
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { VDomModel } from '@jupyterlab/ui-components';
|
|
2
|
+
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
3
|
+
export interface IProviderConfig {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
provider: 'anthropic' | 'mistral' | 'ollama';
|
|
7
|
+
model: string;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
baseURL?: string;
|
|
10
|
+
headers?: Record<string, string>;
|
|
11
|
+
customSettings?: Record<string, any>;
|
|
12
|
+
[key: string]: any;
|
|
13
|
+
}
|
|
14
|
+
export interface IMCPServerConfig {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
url: string;
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
}
|
|
21
|
+
export interface IAIConfig {
|
|
22
|
+
providers: IProviderConfig[];
|
|
23
|
+
activeProvider: string;
|
|
24
|
+
activeCompleterProvider?: string;
|
|
25
|
+
useSameProviderForChatAndCompleter: boolean;
|
|
26
|
+
mcpServers: IMCPServerConfig[];
|
|
27
|
+
temperature: number;
|
|
28
|
+
maxTokens?: number;
|
|
29
|
+
contextAwareness: boolean;
|
|
30
|
+
codeExecution: boolean;
|
|
31
|
+
systemPrompt: string;
|
|
32
|
+
toolsEnabled: boolean;
|
|
33
|
+
sendWithShiftEnter: boolean;
|
|
34
|
+
maxTurns: number;
|
|
35
|
+
showTokenUsage: boolean;
|
|
36
|
+
commandsRequiringApproval: string[];
|
|
37
|
+
}
|
|
38
|
+
export declare class AISettingsModel extends VDomModel {
|
|
39
|
+
private _config;
|
|
40
|
+
private _settingRegistry;
|
|
41
|
+
private _settings;
|
|
42
|
+
constructor(options: AISettingsModel.IOptions);
|
|
43
|
+
private initializeSettings;
|
|
44
|
+
private onSettingsChanged;
|
|
45
|
+
private loadFromSettings;
|
|
46
|
+
get config(): IAIConfig;
|
|
47
|
+
get providers(): IProviderConfig[];
|
|
48
|
+
getProvider(id: string): IProviderConfig | undefined;
|
|
49
|
+
getActiveProvider(): IProviderConfig | undefined;
|
|
50
|
+
getCompleterProvider(): IProviderConfig | undefined;
|
|
51
|
+
addProvider(providerConfig: Omit<IProviderConfig, 'id'>): Promise<string>;
|
|
52
|
+
removeProvider(id: string): Promise<void>;
|
|
53
|
+
updateProvider(id: string, updates: Partial<IProviderConfig>): Promise<void>;
|
|
54
|
+
setActiveProvider(id: string): Promise<void>;
|
|
55
|
+
setActiveCompleterProvider(id: string | undefined): Promise<void>;
|
|
56
|
+
get mcpServers(): IMCPServerConfig[];
|
|
57
|
+
getMCPServer(id: string): IMCPServerConfig | undefined;
|
|
58
|
+
addMCPServer(serverConfig: Omit<IMCPServerConfig, 'id'>): Promise<string>;
|
|
59
|
+
removeMCPServer(id: string): Promise<void>;
|
|
60
|
+
updateMCPServer(id: string, updates: Partial<IMCPServerConfig>): Promise<void>;
|
|
61
|
+
updateConfig(updates: Partial<IAIConfig>): Promise<void>;
|
|
62
|
+
getApiKey(id: string): string;
|
|
63
|
+
private saveSetting;
|
|
64
|
+
}
|
|
65
|
+
export declare namespace AISettingsModel {
|
|
66
|
+
interface IOptions {
|
|
67
|
+
settingRegistry: ISettingRegistry;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { VDomModel } from '@jupyterlab/ui-components';
|
|
2
|
+
const PLUGIN_ID = '@jupyterlite/ai:settings-model';
|
|
3
|
+
export class AISettingsModel extends VDomModel {
|
|
4
|
+
_config = {
|
|
5
|
+
providers: [],
|
|
6
|
+
activeProvider: '',
|
|
7
|
+
activeCompleterProvider: undefined,
|
|
8
|
+
useSameProviderForChatAndCompleter: true,
|
|
9
|
+
mcpServers: [],
|
|
10
|
+
temperature: 0.7,
|
|
11
|
+
maxTokens: undefined,
|
|
12
|
+
contextAwareness: true,
|
|
13
|
+
codeExecution: false,
|
|
14
|
+
toolsEnabled: true,
|
|
15
|
+
sendWithShiftEnter: false,
|
|
16
|
+
maxTurns: 25,
|
|
17
|
+
showTokenUsage: false,
|
|
18
|
+
commandsRequiringApproval: [
|
|
19
|
+
'notebook:restart-run-all',
|
|
20
|
+
'notebook:run-cell',
|
|
21
|
+
'notebook:run-cell-and-select-next',
|
|
22
|
+
'notebook:run-cell-and-insert-below',
|
|
23
|
+
'notebook:run-all-cells',
|
|
24
|
+
'notebook:run-all-above',
|
|
25
|
+
'notebook:run-all-below',
|
|
26
|
+
'console:execute',
|
|
27
|
+
'console:execute-forced',
|
|
28
|
+
'fileeditor:run-code',
|
|
29
|
+
'kernelmenu:run',
|
|
30
|
+
'kernelmenu:restart-and-run-all',
|
|
31
|
+
'runmenu:run-all'
|
|
32
|
+
],
|
|
33
|
+
systemPrompt: `You are Jupyternaut, an AI coding assistant built specifically for the JupyterLab environment.
|
|
34
|
+
|
|
35
|
+
## Your Core Mission
|
|
36
|
+
You're designed to be a capable partner for data science, research, and development work in Jupyter notebooks. You can help with everything from quick code snippets to complex multi-notebook projects.
|
|
37
|
+
|
|
38
|
+
## Your Capabilities
|
|
39
|
+
**📁 File & Project Management:**
|
|
40
|
+
- Create, read, edit, and organize Python files and notebooks
|
|
41
|
+
- Manage project structure and navigate file systems
|
|
42
|
+
- Help with version control and project organization
|
|
43
|
+
|
|
44
|
+
**📊 Notebook Operations:**
|
|
45
|
+
- Create new notebooks and manage existing ones
|
|
46
|
+
- Add, edit, delete, and run cells (both code and markdown)
|
|
47
|
+
- Help with notebook structure and organization
|
|
48
|
+
- Retrieve and analyze cell outputs and execution results
|
|
49
|
+
|
|
50
|
+
**🧠 Coding & Development:**
|
|
51
|
+
- Write, debug, and optimize Python code
|
|
52
|
+
- Explain complex algorithms and data structures
|
|
53
|
+
- Help with data analysis, visualization, and machine learning
|
|
54
|
+
- Support for scientific computing libraries (numpy, pandas, matplotlib, etc.)
|
|
55
|
+
- Code reviews and best practices recommendations
|
|
56
|
+
|
|
57
|
+
**💡 Adaptive Assistance:**
|
|
58
|
+
- Understand context from your current work environment
|
|
59
|
+
- Provide suggestions tailored to your specific use case
|
|
60
|
+
- Help with both quick fixes and long-term project planning
|
|
61
|
+
|
|
62
|
+
## How I Work
|
|
63
|
+
I can actively interact with your JupyterLab environment using specialized tools. When you ask me to perform actions, I can:
|
|
64
|
+
- Execute operations directly in your notebooks
|
|
65
|
+
- Create and modify files as needed
|
|
66
|
+
- Run code and analyze results
|
|
67
|
+
- Make systematic changes across multiple files
|
|
68
|
+
|
|
69
|
+
## My Approach
|
|
70
|
+
- **Context-aware**: I understand you're working in a data science/research environment
|
|
71
|
+
- **Practical**: I focus on actionable solutions that work in your current setup
|
|
72
|
+
- **Educational**: I explain my reasoning and teach best practices along the way
|
|
73
|
+
- **Collaborative**: Think of me as a pair programming partner, not just a code generator
|
|
74
|
+
|
|
75
|
+
## Communication Style & Agent Behavior
|
|
76
|
+
- **Conversational**: I maintain a friendly, natural conversation flow throughout our interaction
|
|
77
|
+
- **Progress Updates**: I write brief progress messages between tool uses that appear directly in our conversation
|
|
78
|
+
- **No Filler**: I avoid empty acknowledgments like "Sounds good!" or "Okay, I will..." - I get straight to work
|
|
79
|
+
- **Purposeful Communication**: I start with what I'm doing, use tools, then share what I found and what's next
|
|
80
|
+
- **Active Narration**: I actively write progress updates like "Looking at the current code structure..." or "Found the issue in the notebook..." between tool calls
|
|
81
|
+
- **Checkpoint Updates**: After several operations, I summarize what I've accomplished and what remains
|
|
82
|
+
- **Natural Flow**: My explanations and progress reports appear as normal conversation text, not just in tool blocks
|
|
83
|
+
|
|
84
|
+
## IMPORTANT: Always write progress messages between tools that explain what you're doing and what you found. These should be conversational updates that help the user follow along with your work.
|
|
85
|
+
|
|
86
|
+
## Technical Communication
|
|
87
|
+
- Code is formatted in proper markdown blocks with syntax highlighting
|
|
88
|
+
- Mathematical notation uses LaTeX formatting: \\(equations\\) and \\[display math\\]
|
|
89
|
+
- I provide context for my actions and explain my reasoning as I work
|
|
90
|
+
- When creating or modifying multiple files, I give brief summaries of changes
|
|
91
|
+
- I keep users informed of progress while staying focused on the task
|
|
92
|
+
|
|
93
|
+
## Multi-Step Task Handling
|
|
94
|
+
When users request complex tasks that require multiple steps (like "create a notebook with example cells"), I use tools in sequence to accomplish the complete task. For example:
|
|
95
|
+
- First use create_notebook to create the notebook
|
|
96
|
+
- Then use add_code_cell or add_markdown_cell to add cells
|
|
97
|
+
- Use set_cell_content to add content to cells as needed
|
|
98
|
+
- Use run_cell to execute code when appropriate
|
|
99
|
+
|
|
100
|
+
Always think through multi-step tasks and use tools to fully complete the user's request rather than stopping after just one action.
|
|
101
|
+
|
|
102
|
+
Ready to help you build something great! What are you working on?`
|
|
103
|
+
};
|
|
104
|
+
_settingRegistry;
|
|
105
|
+
_settings = null;
|
|
106
|
+
constructor(options) {
|
|
107
|
+
super();
|
|
108
|
+
this._settingRegistry = options.settingRegistry;
|
|
109
|
+
this.initializeSettings();
|
|
110
|
+
}
|
|
111
|
+
async initializeSettings() {
|
|
112
|
+
try {
|
|
113
|
+
this._settings = await this._settingRegistry.load(PLUGIN_ID);
|
|
114
|
+
this.loadFromSettings();
|
|
115
|
+
// Listen for settings changes
|
|
116
|
+
this._settings.changed.connect(this.onSettingsChanged, this);
|
|
117
|
+
this.stateChanged.emit(void 0);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
console.warn('Failed to load JupyterLab settings:', error);
|
|
121
|
+
this.stateChanged.emit(void 0);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
onSettingsChanged() {
|
|
125
|
+
this.loadFromSettings();
|
|
126
|
+
this.stateChanged.emit(void 0);
|
|
127
|
+
}
|
|
128
|
+
loadFromSettings() {
|
|
129
|
+
if (!this._settings) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
// Merge JupyterLab settings with defaults
|
|
133
|
+
const settingsData = this._settings.composite;
|
|
134
|
+
this._config = {
|
|
135
|
+
...this._config,
|
|
136
|
+
...settingsData
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
get config() {
|
|
140
|
+
return { ...this._config };
|
|
141
|
+
}
|
|
142
|
+
get providers() {
|
|
143
|
+
return [...this._config.providers];
|
|
144
|
+
}
|
|
145
|
+
getProvider(id) {
|
|
146
|
+
return this._config.providers.find(p => p.id === id);
|
|
147
|
+
}
|
|
148
|
+
getActiveProvider() {
|
|
149
|
+
return this.getProvider(this._config.activeProvider);
|
|
150
|
+
}
|
|
151
|
+
getCompleterProvider() {
|
|
152
|
+
if (this._config.useSameProviderForChatAndCompleter) {
|
|
153
|
+
return this.getActiveProvider();
|
|
154
|
+
}
|
|
155
|
+
return this._config.activeCompleterProvider
|
|
156
|
+
? this.getProvider(this._config.activeCompleterProvider)
|
|
157
|
+
: this.getActiveProvider();
|
|
158
|
+
}
|
|
159
|
+
async addProvider(providerConfig) {
|
|
160
|
+
const id = `${providerConfig.provider}-${Date.now()}`;
|
|
161
|
+
const newProvider = {
|
|
162
|
+
id,
|
|
163
|
+
name: providerConfig.name,
|
|
164
|
+
provider: providerConfig.provider,
|
|
165
|
+
model: providerConfig.model,
|
|
166
|
+
apiKey: providerConfig.apiKey,
|
|
167
|
+
baseURL: providerConfig.baseURL,
|
|
168
|
+
headers: providerConfig.headers,
|
|
169
|
+
customSettings: providerConfig.customSettings
|
|
170
|
+
};
|
|
171
|
+
this._config.providers.push(newProvider);
|
|
172
|
+
// If this is the first provider, make it active
|
|
173
|
+
if (this._config.providers.length === 1) {
|
|
174
|
+
this._config.activeProvider = id;
|
|
175
|
+
// Save both providers and activeProvider
|
|
176
|
+
await this.saveSetting('providers', this._config.providers);
|
|
177
|
+
await this.saveSetting('activeProvider', this._config.activeProvider);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// Only save providers
|
|
181
|
+
await this.saveSetting('providers', this._config.providers);
|
|
182
|
+
}
|
|
183
|
+
return id;
|
|
184
|
+
}
|
|
185
|
+
async removeProvider(id) {
|
|
186
|
+
const index = this._config.providers.findIndex(p => p.id === id);
|
|
187
|
+
if (index === -1) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
this._config.providers.splice(index, 1);
|
|
191
|
+
await this.saveSetting('providers', this._config.providers);
|
|
192
|
+
// If this was the active provider, select a new one
|
|
193
|
+
if (this._config.activeProvider === id) {
|
|
194
|
+
this._config.activeProvider =
|
|
195
|
+
this._config.providers.length > 0 ? this._config.providers[0].id : '';
|
|
196
|
+
await this.saveSetting('activeProvider', this._config.activeProvider);
|
|
197
|
+
}
|
|
198
|
+
if (this._config.activeCompleterProvider === id) {
|
|
199
|
+
this._config.activeCompleterProvider = undefined;
|
|
200
|
+
await this.saveSetting('activeCompleterProvider', this._config.activeCompleterProvider);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async updateProvider(id, updates) {
|
|
204
|
+
const provider = this.getProvider(id);
|
|
205
|
+
if (!provider) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
Object.assign(provider, updates);
|
|
209
|
+
await this.saveSetting('providers', this._config.providers);
|
|
210
|
+
}
|
|
211
|
+
async setActiveProvider(id) {
|
|
212
|
+
if (this.getProvider(id)) {
|
|
213
|
+
this._config.activeProvider = id;
|
|
214
|
+
await this.saveSetting('activeProvider', this._config.activeProvider);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async setActiveCompleterProvider(id) {
|
|
218
|
+
this._config.activeCompleterProvider = id;
|
|
219
|
+
await this.saveSetting('activeCompleterProvider', this._config.activeCompleterProvider);
|
|
220
|
+
}
|
|
221
|
+
get mcpServers() {
|
|
222
|
+
return [...this._config.mcpServers];
|
|
223
|
+
}
|
|
224
|
+
getMCPServer(id) {
|
|
225
|
+
return this._config.mcpServers.find(s => s.id === id);
|
|
226
|
+
}
|
|
227
|
+
async addMCPServer(serverConfig) {
|
|
228
|
+
const id = `mcp-${Date.now()}`;
|
|
229
|
+
const newServer = {
|
|
230
|
+
id,
|
|
231
|
+
name: serverConfig.name,
|
|
232
|
+
url: serverConfig.url,
|
|
233
|
+
enabled: serverConfig.enabled
|
|
234
|
+
};
|
|
235
|
+
this._config.mcpServers.push(newServer);
|
|
236
|
+
await this.saveSetting('mcpServers', this._config.mcpServers);
|
|
237
|
+
return id;
|
|
238
|
+
}
|
|
239
|
+
async removeMCPServer(id) {
|
|
240
|
+
const index = this._config.mcpServers.findIndex(s => s.id === id);
|
|
241
|
+
if (index === -1) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
this._config.mcpServers.splice(index, 1);
|
|
245
|
+
await this.saveSetting('mcpServers', this._config.mcpServers);
|
|
246
|
+
}
|
|
247
|
+
async updateMCPServer(id, updates) {
|
|
248
|
+
const server = this.getMCPServer(id);
|
|
249
|
+
if (!server) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
Object.assign(server, updates);
|
|
253
|
+
await this.saveSetting('mcpServers', this._config.mcpServers);
|
|
254
|
+
}
|
|
255
|
+
async updateConfig(updates) {
|
|
256
|
+
// Update config and save only changed settings
|
|
257
|
+
const promises = [];
|
|
258
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
259
|
+
if (key in this._config &&
|
|
260
|
+
this._config[key] !== value) {
|
|
261
|
+
this._config[key] = value;
|
|
262
|
+
promises.push(this.saveSetting(key, value));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Wait for all settings to be saved
|
|
266
|
+
await Promise.all(promises);
|
|
267
|
+
}
|
|
268
|
+
getApiKey(id) {
|
|
269
|
+
// First check the active completer provider
|
|
270
|
+
const activeCompleterProvider = this.getCompleterProvider();
|
|
271
|
+
if (activeCompleterProvider && activeCompleterProvider.id === id) {
|
|
272
|
+
return activeCompleterProvider.apiKey || '';
|
|
273
|
+
}
|
|
274
|
+
// Fallback to active chat provider
|
|
275
|
+
const activeProvider = this.getActiveProvider();
|
|
276
|
+
if (activeProvider && activeProvider.id === id) {
|
|
277
|
+
return activeProvider.apiKey || '';
|
|
278
|
+
}
|
|
279
|
+
return '';
|
|
280
|
+
}
|
|
281
|
+
async saveSetting(key, value) {
|
|
282
|
+
try {
|
|
283
|
+
if (this._settings) {
|
|
284
|
+
// Only save the specific setting that changed
|
|
285
|
+
if (value !== undefined) {
|
|
286
|
+
await this._settings.set(key, value);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
console.warn(`Failed to save setting '${key}' to JupyterLab settings, falling back to localStorage:`, error);
|
|
292
|
+
}
|
|
293
|
+
this.stateChanged.emit(void 0);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IChatProviderRegistry, ICompletionProviderRegistry } from '../tokens';
|
|
2
|
+
/**
|
|
3
|
+
* Register all built-in chat providers
|
|
4
|
+
*/
|
|
5
|
+
export declare function registerBuiltInChatProviders(registry: IChatProviderRegistry): void;
|
|
6
|
+
/**
|
|
7
|
+
* Register all built-in completion providers
|
|
8
|
+
*/
|
|
9
|
+
export declare function registerBuiltInCompletionProviders(registry: ICompletionProviderRegistry): void;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
2
|
+
import { createMistral } from '@ai-sdk/mistral';
|
|
3
|
+
import { aisdk } from '@openai/agents-extensions';
|
|
4
|
+
import { createOllama } from 'ollama-ai-provider-v2';
|
|
5
|
+
/**
|
|
6
|
+
* Register all built-in chat providers
|
|
7
|
+
*/
|
|
8
|
+
export function registerBuiltInChatProviders(registry) {
|
|
9
|
+
// Anthropic provider
|
|
10
|
+
const anthropicInfo = {
|
|
11
|
+
id: 'anthropic',
|
|
12
|
+
name: 'Anthropic Claude',
|
|
13
|
+
requiresApiKey: true,
|
|
14
|
+
defaultModels: [
|
|
15
|
+
'claude-sonnet-4-20250514',
|
|
16
|
+
'claude-opus-4-20250514',
|
|
17
|
+
'claude-opus-4-1-20250805',
|
|
18
|
+
'claude-3-5-haiku-latest'
|
|
19
|
+
],
|
|
20
|
+
supportsBaseURL: true,
|
|
21
|
+
supportsHeaders: true,
|
|
22
|
+
factory: (options) => {
|
|
23
|
+
if (!options.apiKey) {
|
|
24
|
+
throw new Error('API key required for Anthropic');
|
|
25
|
+
}
|
|
26
|
+
const anthropic = createAnthropic({
|
|
27
|
+
apiKey: options.apiKey,
|
|
28
|
+
headers: {
|
|
29
|
+
'anthropic-dangerous-direct-browser-access': 'true',
|
|
30
|
+
...options.headers
|
|
31
|
+
},
|
|
32
|
+
...(options.baseURL && { baseURL: options.baseURL })
|
|
33
|
+
});
|
|
34
|
+
const modelName = options.model ?? '';
|
|
35
|
+
return aisdk(anthropic(modelName));
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
registry.registerProvider(anthropicInfo);
|
|
39
|
+
// Mistral provider
|
|
40
|
+
const mistralInfo = {
|
|
41
|
+
id: 'mistral',
|
|
42
|
+
name: 'Mistral AI',
|
|
43
|
+
requiresApiKey: true,
|
|
44
|
+
defaultModels: [
|
|
45
|
+
'mistral-medium-latest',
|
|
46
|
+
'mistral-large-latest',
|
|
47
|
+
'mistral-small-latest',
|
|
48
|
+
'codestral-latest'
|
|
49
|
+
],
|
|
50
|
+
supportsBaseURL: true,
|
|
51
|
+
factory: (options) => {
|
|
52
|
+
if (!options.apiKey) {
|
|
53
|
+
throw new Error('API key required for Mistral');
|
|
54
|
+
}
|
|
55
|
+
const mistral = createMistral({
|
|
56
|
+
apiKey: options.apiKey,
|
|
57
|
+
...(options.baseURL && { baseURL: options.baseURL })
|
|
58
|
+
});
|
|
59
|
+
const modelName = options.model || 'mistral-large-latest';
|
|
60
|
+
return aisdk(mistral(modelName));
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
registry.registerProvider(mistralInfo);
|
|
64
|
+
// Ollama provider
|
|
65
|
+
const ollamaInfo = {
|
|
66
|
+
id: 'ollama',
|
|
67
|
+
name: 'Ollama',
|
|
68
|
+
requiresApiKey: false,
|
|
69
|
+
defaultModels: [],
|
|
70
|
+
supportsBaseURL: true,
|
|
71
|
+
supportsHeaders: true,
|
|
72
|
+
factory: (options) => {
|
|
73
|
+
const ollama = createOllama({
|
|
74
|
+
baseURL: options.baseURL || 'http://localhost:11434/api',
|
|
75
|
+
...(options.headers && { headers: options.headers })
|
|
76
|
+
});
|
|
77
|
+
const modelName = options.model || 'phi3';
|
|
78
|
+
return aisdk(ollama(modelName));
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
registry.registerProvider(ollamaInfo);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Register all built-in completion providers
|
|
85
|
+
*/
|
|
86
|
+
export function registerBuiltInCompletionProviders(registry) {
|
|
87
|
+
// Anthropic provider
|
|
88
|
+
const anthropicInfo = {
|
|
89
|
+
id: 'anthropic',
|
|
90
|
+
name: 'Anthropic Claude',
|
|
91
|
+
requiresApiKey: true,
|
|
92
|
+
defaultModels: [
|
|
93
|
+
'claude-sonnet-4-20250514',
|
|
94
|
+
'claude-opus-4-20250514',
|
|
95
|
+
'claude-opus-4-1-20250805',
|
|
96
|
+
'claude-3-5-haiku-latest'
|
|
97
|
+
],
|
|
98
|
+
supportsBaseURL: true,
|
|
99
|
+
supportsHeaders: true,
|
|
100
|
+
customSettings: {
|
|
101
|
+
completionConfig: {
|
|
102
|
+
temperature: 0.3,
|
|
103
|
+
supportsFillInMiddle: false,
|
|
104
|
+
useFilterText: true
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
factory: (options) => {
|
|
108
|
+
if (!options.apiKey) {
|
|
109
|
+
throw new Error('API key required for Anthropic');
|
|
110
|
+
}
|
|
111
|
+
const anthropic = createAnthropic({
|
|
112
|
+
apiKey: options.apiKey,
|
|
113
|
+
headers: {
|
|
114
|
+
'anthropic-dangerous-direct-browser-access': 'true',
|
|
115
|
+
...options.headers
|
|
116
|
+
},
|
|
117
|
+
...(options.baseURL && { baseURL: options.baseURL })
|
|
118
|
+
});
|
|
119
|
+
const modelName = options.model ?? '';
|
|
120
|
+
return anthropic(modelName);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
registry.registerProvider(anthropicInfo);
|
|
124
|
+
// Mistral provider
|
|
125
|
+
const mistralInfo = {
|
|
126
|
+
id: 'mistral',
|
|
127
|
+
name: 'Mistral AI',
|
|
128
|
+
requiresApiKey: true,
|
|
129
|
+
defaultModels: [
|
|
130
|
+
'mistral-medium-latest',
|
|
131
|
+
'mistral-large-latest',
|
|
132
|
+
'mistral-small-latest',
|
|
133
|
+
'codestral-latest'
|
|
134
|
+
],
|
|
135
|
+
supportsBaseURL: true,
|
|
136
|
+
customSettings: {
|
|
137
|
+
completionConfig: {
|
|
138
|
+
temperature: 0.2,
|
|
139
|
+
supportsFillInMiddle: true,
|
|
140
|
+
customPromptFormat: (prompt, suffix) => {
|
|
141
|
+
return suffix.trim() ? `<PRE>${prompt}<SUF>${suffix}<MID>` : prompt;
|
|
142
|
+
},
|
|
143
|
+
cleanupCompletion: (completion) => {
|
|
144
|
+
return completion
|
|
145
|
+
.replace(/<PRE>/g, '')
|
|
146
|
+
.replace(/<SUF>/g, '')
|
|
147
|
+
.replace(/<MID>/g, '')
|
|
148
|
+
.replace(/```[\s\S]*?```/g, '')
|
|
149
|
+
.trim();
|
|
150
|
+
},
|
|
151
|
+
useFilterText: false
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
factory: (options) => {
|
|
155
|
+
if (!options.apiKey) {
|
|
156
|
+
throw new Error('API key required for Mistral');
|
|
157
|
+
}
|
|
158
|
+
const mistral = createMistral({
|
|
159
|
+
apiKey: options.apiKey,
|
|
160
|
+
...(options.baseURL && { baseURL: options.baseURL })
|
|
161
|
+
});
|
|
162
|
+
const modelName = options.model || 'mistral-large-latest';
|
|
163
|
+
return mistral(modelName);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
registry.registerProvider(mistralInfo);
|
|
167
|
+
// Ollama provider
|
|
168
|
+
const ollamaInfo = {
|
|
169
|
+
id: 'ollama',
|
|
170
|
+
name: 'Ollama',
|
|
171
|
+
requiresApiKey: false,
|
|
172
|
+
defaultModels: ['phi3'],
|
|
173
|
+
supportsBaseURL: true,
|
|
174
|
+
supportsHeaders: true,
|
|
175
|
+
customSettings: {
|
|
176
|
+
completionConfig: {
|
|
177
|
+
temperature: 0.3,
|
|
178
|
+
supportsFillInMiddle: false,
|
|
179
|
+
useFilterText: false
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
factory: (options) => {
|
|
183
|
+
const ollama = createOllama({
|
|
184
|
+
baseURL: options.baseURL || 'http://localhost:11434/api',
|
|
185
|
+
...(options.headers && { headers: options.headers })
|
|
186
|
+
});
|
|
187
|
+
const modelName = options.model || 'phi3';
|
|
188
|
+
return ollama(modelName);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
registry.registerProvider(ollamaInfo);
|
|
192
|
+
}
|