@jupyterlite/ai 0.8.1 → 0.9.0-a1

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.
Files changed (162) hide show
  1. package/lib/agent.d.ts +243 -0
  2. package/lib/agent.js +627 -0
  3. package/lib/chat-model.d.ts +195 -0
  4. package/lib/chat-model.js +591 -0
  5. package/lib/completion/completion-provider.d.ts +93 -0
  6. package/lib/completion/completion-provider.js +235 -0
  7. package/lib/completion/index.d.ts +1 -0
  8. package/lib/completion/index.js +1 -0
  9. package/lib/components/clear-button.d.ts +18 -0
  10. package/lib/components/clear-button.js +31 -0
  11. package/lib/components/index.d.ts +3 -0
  12. package/lib/components/index.js +3 -0
  13. package/lib/components/model-select.d.ts +19 -0
  14. package/lib/components/model-select.js +154 -0
  15. package/lib/components/stop-button.d.ts +3 -3
  16. package/lib/components/stop-button.js +8 -9
  17. package/lib/components/token-usage-display.d.ts +45 -0
  18. package/lib/components/token-usage-display.js +74 -0
  19. package/lib/components/tool-select.d.ts +27 -0
  20. package/lib/components/tool-select.js +130 -0
  21. package/lib/icons.d.ts +3 -1
  22. package/lib/icons.js +10 -13
  23. package/lib/index.d.ts +5 -5
  24. package/lib/index.js +341 -169
  25. package/lib/mcp/browser.d.ts +68 -0
  26. package/lib/mcp/browser.js +132 -0
  27. package/lib/models/settings-model.d.ts +70 -0
  28. package/lib/models/settings-model.js +296 -0
  29. package/lib/providers/built-in-providers.d.ts +9 -0
  30. package/lib/providers/built-in-providers.js +266 -0
  31. package/lib/providers/models.d.ts +37 -0
  32. package/lib/providers/models.js +28 -0
  33. package/lib/providers/provider-registry.d.ts +94 -0
  34. package/lib/providers/provider-registry.js +155 -0
  35. package/lib/tokens.d.ts +167 -86
  36. package/lib/tokens.js +25 -12
  37. package/lib/tools/commands.d.ts +11 -0
  38. package/lib/tools/commands.js +126 -0
  39. package/lib/tools/file.d.ts +27 -0
  40. package/lib/tools/file.js +262 -0
  41. package/lib/tools/notebook.d.ts +41 -0
  42. package/lib/tools/notebook.js +779 -0
  43. package/lib/tools/tool-registry.d.ts +35 -0
  44. package/lib/tools/tool-registry.js +55 -0
  45. package/lib/widgets/ai-settings.d.ts +49 -0
  46. package/lib/widgets/ai-settings.js +580 -0
  47. package/lib/widgets/chat-wrapper.d.ts +144 -0
  48. package/lib/widgets/chat-wrapper.js +390 -0
  49. package/lib/widgets/provider-config-dialog.d.ts +14 -0
  50. package/lib/widgets/provider-config-dialog.js +112 -0
  51. package/package.json +151 -40
  52. package/schema/settings-model.json +159 -0
  53. package/src/agent.ts +836 -0
  54. package/src/chat-model.ts +771 -0
  55. package/src/completion/completion-provider.ts +346 -0
  56. package/src/completion/index.ts +1 -0
  57. package/src/components/clear-button.tsx +56 -0
  58. package/src/components/index.ts +3 -0
  59. package/src/components/model-select.tsx +245 -0
  60. package/src/components/stop-button.tsx +11 -11
  61. package/src/components/token-usage-display.tsx +130 -0
  62. package/src/components/tool-select.tsx +218 -0
  63. package/src/icons.ts +12 -14
  64. package/src/index.ts +485 -232
  65. package/src/mcp/browser.ts +213 -0
  66. package/src/models/settings-model.ts +413 -0
  67. package/src/providers/built-in-providers.ts +294 -0
  68. package/src/providers/models.ts +79 -0
  69. package/src/providers/provider-registry.ts +189 -0
  70. package/src/tokens.ts +217 -90
  71. package/src/tools/commands.ts +151 -0
  72. package/src/tools/file.ts +307 -0
  73. package/src/tools/notebook.ts +987 -0
  74. package/src/tools/tool-registry.ts +63 -0
  75. package/src/types.d.ts +4 -0
  76. package/src/widgets/ai-settings.tsx +1233 -0
  77. package/src/widgets/chat-wrapper.tsx +543 -0
  78. package/src/widgets/provider-config-dialog.tsx +272 -0
  79. package/style/base.css +335 -14
  80. package/style/icons/jupyternaut-lite.svg +1 -1
  81. package/lib/base-completer.d.ts +0 -49
  82. package/lib/base-completer.js +0 -14
  83. package/lib/chat-handler.d.ts +0 -56
  84. package/lib/chat-handler.js +0 -201
  85. package/lib/completion-provider.d.ts +0 -34
  86. package/lib/completion-provider.js +0 -32
  87. package/lib/default-prompts.d.ts +0 -2
  88. package/lib/default-prompts.js +0 -31
  89. package/lib/default-providers/Anthropic/completer.d.ts +0 -12
  90. package/lib/default-providers/Anthropic/completer.js +0 -46
  91. package/lib/default-providers/Anthropic/settings-schema.json +0 -70
  92. package/lib/default-providers/ChromeAI/completer.d.ts +0 -12
  93. package/lib/default-providers/ChromeAI/completer.js +0 -56
  94. package/lib/default-providers/ChromeAI/instructions.d.ts +0 -6
  95. package/lib/default-providers/ChromeAI/instructions.js +0 -42
  96. package/lib/default-providers/ChromeAI/settings-schema.json +0 -18
  97. package/lib/default-providers/Gemini/completer.d.ts +0 -12
  98. package/lib/default-providers/Gemini/completer.js +0 -48
  99. package/lib/default-providers/Gemini/instructions.d.ts +0 -2
  100. package/lib/default-providers/Gemini/instructions.js +0 -9
  101. package/lib/default-providers/Gemini/settings-schema.json +0 -64
  102. package/lib/default-providers/MistralAI/completer.d.ts +0 -13
  103. package/lib/default-providers/MistralAI/completer.js +0 -52
  104. package/lib/default-providers/MistralAI/instructions.d.ts +0 -2
  105. package/lib/default-providers/MistralAI/instructions.js +0 -18
  106. package/lib/default-providers/MistralAI/settings-schema.json +0 -75
  107. package/lib/default-providers/Ollama/completer.d.ts +0 -12
  108. package/lib/default-providers/Ollama/completer.js +0 -43
  109. package/lib/default-providers/Ollama/instructions.d.ts +0 -2
  110. package/lib/default-providers/Ollama/instructions.js +0 -70
  111. package/lib/default-providers/Ollama/settings-schema.json +0 -143
  112. package/lib/default-providers/OpenAI/completer.d.ts +0 -12
  113. package/lib/default-providers/OpenAI/completer.js +0 -43
  114. package/lib/default-providers/OpenAI/settings-schema.json +0 -628
  115. package/lib/default-providers/WebLLM/completer.d.ts +0 -21
  116. package/lib/default-providers/WebLLM/completer.js +0 -127
  117. package/lib/default-providers/WebLLM/instructions.d.ts +0 -6
  118. package/lib/default-providers/WebLLM/instructions.js +0 -32
  119. package/lib/default-providers/WebLLM/settings-schema.json +0 -19
  120. package/lib/default-providers/index.d.ts +0 -2
  121. package/lib/default-providers/index.js +0 -179
  122. package/lib/provider.d.ts +0 -144
  123. package/lib/provider.js +0 -412
  124. package/lib/settings/base.json +0 -7
  125. package/lib/settings/index.d.ts +0 -3
  126. package/lib/settings/index.js +0 -3
  127. package/lib/settings/panel.d.ts +0 -226
  128. package/lib/settings/panel.js +0 -510
  129. package/lib/settings/textarea.d.ts +0 -2
  130. package/lib/settings/textarea.js +0 -18
  131. package/lib/settings/utils.d.ts +0 -2
  132. package/lib/settings/utils.js +0 -4
  133. package/lib/types/ai-model.d.ts +0 -24
  134. package/lib/types/ai-model.js +0 -5
  135. package/schema/chat.json +0 -28
  136. package/schema/provider-registry.json +0 -29
  137. package/schema/system-prompts.json +0 -22
  138. package/src/base-completer.ts +0 -75
  139. package/src/chat-handler.ts +0 -262
  140. package/src/completion-provider.ts +0 -64
  141. package/src/default-prompts.ts +0 -33
  142. package/src/default-providers/Anthropic/completer.ts +0 -59
  143. package/src/default-providers/ChromeAI/completer.ts +0 -73
  144. package/src/default-providers/ChromeAI/instructions.ts +0 -45
  145. package/src/default-providers/Gemini/completer.ts +0 -61
  146. package/src/default-providers/Gemini/instructions.ts +0 -9
  147. package/src/default-providers/MistralAI/completer.ts +0 -69
  148. package/src/default-providers/MistralAI/instructions.ts +0 -18
  149. package/src/default-providers/Ollama/completer.ts +0 -54
  150. package/src/default-providers/Ollama/instructions.ts +0 -70
  151. package/src/default-providers/OpenAI/completer.ts +0 -54
  152. package/src/default-providers/WebLLM/completer.ts +0 -151
  153. package/src/default-providers/WebLLM/instructions.ts +0 -33
  154. package/src/default-providers/index.ts +0 -211
  155. package/src/global.d.ts +0 -9
  156. package/src/provider.ts +0 -514
  157. package/src/settings/index.ts +0 -3
  158. package/src/settings/panel.tsx +0 -773
  159. package/src/settings/textarea.tsx +0 -33
  160. package/src/settings/utils.ts +0 -5
  161. package/src/types/ai-model.ts +0 -37
  162. 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,70 @@
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
+ useSecretsManager: boolean;
23
+ providers: IProviderConfig[];
24
+ activeProvider: string;
25
+ activeCompleterProvider?: string;
26
+ useSameProviderForChatAndCompleter: boolean;
27
+ mcpServers: IMCPServerConfig[];
28
+ temperature: number;
29
+ maxTokens?: number;
30
+ contextAwareness: boolean;
31
+ codeExecution: boolean;
32
+ systemPrompt: string;
33
+ toolsEnabled: boolean;
34
+ sendWithShiftEnter: boolean;
35
+ maxTurns: number;
36
+ showTokenUsage: boolean;
37
+ commandsRequiringApproval: string[];
38
+ }
39
+ export declare class AISettingsModel extends VDomModel {
40
+ private _config;
41
+ private _settingRegistry;
42
+ private _settings;
43
+ constructor(options: AISettingsModel.IOptions);
44
+ private initializeSettings;
45
+ private onSettingsChanged;
46
+ private loadFromSettings;
47
+ get config(): IAIConfig;
48
+ get providers(): IProviderConfig[];
49
+ getProvider(id: string): IProviderConfig | undefined;
50
+ getActiveProvider(): IProviderConfig | undefined;
51
+ getCompleterProvider(): IProviderConfig | undefined;
52
+ addProvider(providerConfig: Omit<IProviderConfig, 'id'>): Promise<string>;
53
+ removeProvider(id: string): Promise<void>;
54
+ updateProvider(id: string, updates: Partial<IProviderConfig>): Promise<void>;
55
+ setActiveProvider(id: string): Promise<void>;
56
+ setActiveCompleterProvider(id: string | undefined): Promise<void>;
57
+ get mcpServers(): IMCPServerConfig[];
58
+ getMCPServer(id: string): IMCPServerConfig | undefined;
59
+ addMCPServer(serverConfig: Omit<IMCPServerConfig, 'id'>): Promise<string>;
60
+ removeMCPServer(id: string): Promise<void>;
61
+ updateMCPServer(id: string, updates: Partial<IMCPServerConfig>): Promise<void>;
62
+ updateConfig(updates: Partial<IAIConfig>): Promise<void>;
63
+ getApiKey(id: string): string;
64
+ private saveSetting;
65
+ }
66
+ export declare namespace AISettingsModel {
67
+ interface IOptions {
68
+ settingRegistry: ISettingRegistry;
69
+ }
70
+ }
@@ -0,0 +1,296 @@
1
+ import { VDomModel } from '@jupyterlab/ui-components';
2
+ const PLUGIN_ID = '@jupyterlite/ai:settings-model';
3
+ export class AISettingsModel extends VDomModel {
4
+ _config = {
5
+ useSecretsManager: true,
6
+ providers: [],
7
+ activeProvider: '',
8
+ activeCompleterProvider: undefined,
9
+ useSameProviderForChatAndCompleter: true,
10
+ mcpServers: [],
11
+ temperature: 0.7,
12
+ maxTokens: undefined,
13
+ contextAwareness: true,
14
+ codeExecution: false,
15
+ toolsEnabled: true,
16
+ sendWithShiftEnter: false,
17
+ maxTurns: 25,
18
+ showTokenUsage: false,
19
+ commandsRequiringApproval: [
20
+ 'notebook:restart-run-all',
21
+ 'notebook:run-cell',
22
+ 'notebook:run-cell-and-select-next',
23
+ 'notebook:run-cell-and-insert-below',
24
+ 'notebook:run-all-cells',
25
+ 'notebook:run-all-above',
26
+ 'notebook:run-all-below',
27
+ 'console:execute',
28
+ 'console:execute-forced',
29
+ 'fileeditor:run-code',
30
+ 'kernelmenu:run',
31
+ 'kernelmenu:restart-and-run-all',
32
+ 'runmenu:run-all'
33
+ ],
34
+ systemPrompt: `You are Jupyternaut, an AI coding assistant built specifically for the JupyterLab environment.
35
+
36
+ ## Your Core Mission
37
+ 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.
38
+
39
+ ## Your Capabilities
40
+ **📁 File & Project Management:**
41
+ - Create, read, edit, and organize Python files and notebooks
42
+ - Manage project structure and navigate file systems
43
+ - Help with version control and project organization
44
+
45
+ **📊 Notebook Operations:**
46
+ - Create new notebooks and manage existing ones
47
+ - Add, edit, delete, and run cells (both code and markdown)
48
+ - Help with notebook structure and organization
49
+ - Retrieve and analyze cell outputs and execution results
50
+
51
+ **🧠 Coding & Development:**
52
+ - Write, debug, and optimize Python code
53
+ - Explain complex algorithms and data structures
54
+ - Help with data analysis, visualization, and machine learning
55
+ - Support for scientific computing libraries (numpy, pandas, matplotlib, etc.)
56
+ - Code reviews and best practices recommendations
57
+
58
+ **💡 Adaptive Assistance:**
59
+ - Understand context from your current work environment
60
+ - Provide suggestions tailored to your specific use case
61
+ - Help with both quick fixes and long-term project planning
62
+
63
+ ## How I Work
64
+ I can actively interact with your JupyterLab environment using specialized tools. When you ask me to perform actions, I can:
65
+ - Execute operations directly in your notebooks
66
+ - Create and modify files as needed
67
+ - Run code and analyze results
68
+ - Make systematic changes across multiple files
69
+
70
+ ## My Approach
71
+ - **Context-aware**: I understand you're working in a data science/research environment
72
+ - **Practical**: I focus on actionable solutions that work in your current setup
73
+ - **Educational**: I explain my reasoning and teach best practices along the way
74
+ - **Collaborative**: Think of me as a pair programming partner, not just a code generator
75
+
76
+ ## Communication Style & Agent Behavior
77
+ - **Conversational**: I maintain a friendly, natural conversation flow throughout our interaction
78
+ - **Progress Updates**: I write brief progress messages between tool uses that appear directly in our conversation
79
+ - **No Filler**: I avoid empty acknowledgments like "Sounds good!" or "Okay, I will..." - I get straight to work
80
+ - **Purposeful Communication**: I start with what I'm doing, use tools, then share what I found and what's next
81
+ - **Active Narration**: I actively write progress updates like "Looking at the current code structure..." or "Found the issue in the notebook..." between tool calls
82
+ - **Checkpoint Updates**: After several operations, I summarize what I've accomplished and what remains
83
+ - **Natural Flow**: My explanations and progress reports appear as normal conversation text, not just in tool blocks
84
+
85
+ ## 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.
86
+
87
+ ## Technical Communication
88
+ - Code is formatted in proper markdown blocks with syntax highlighting
89
+ - Mathematical notation uses LaTeX formatting: \\(equations\\) and \\[display math\\]
90
+ - I provide context for my actions and explain my reasoning as I work
91
+ - When creating or modifying multiple files, I give brief summaries of changes
92
+ - I keep users informed of progress while staying focused on the task
93
+
94
+ ## Multi-Step Task Handling
95
+ 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:
96
+ - First use create_notebook to create the notebook
97
+ - Then use add_code_cell or add_markdown_cell to add cells
98
+ - Use set_cell_content to add content to cells as needed
99
+ - Use run_cell to execute code when appropriate
100
+
101
+ Always think through multi-step tasks and use tools to fully complete the user's request rather than stopping after just one action.
102
+
103
+ Ready to help you build something great! What are you working on?`
104
+ };
105
+ _settingRegistry;
106
+ _settings = null;
107
+ constructor(options) {
108
+ super();
109
+ this._settingRegistry = options.settingRegistry;
110
+ this.initializeSettings();
111
+ }
112
+ async initializeSettings() {
113
+ try {
114
+ this._settings = await this._settingRegistry.load(PLUGIN_ID);
115
+ this.loadFromSettings();
116
+ // Listen for settings changes
117
+ this._settings.changed.connect(this.onSettingsChanged, this);
118
+ this.stateChanged.emit(void 0);
119
+ }
120
+ catch (error) {
121
+ console.warn('Failed to load JupyterLab settings:', error);
122
+ this.stateChanged.emit(void 0);
123
+ }
124
+ }
125
+ onSettingsChanged() {
126
+ this.loadFromSettings();
127
+ this.stateChanged.emit(void 0);
128
+ }
129
+ loadFromSettings() {
130
+ if (!this._settings) {
131
+ return;
132
+ }
133
+ // Merge JupyterLab settings with defaults
134
+ const settingsData = this._settings.composite;
135
+ this._config = {
136
+ ...this._config,
137
+ ...settingsData
138
+ };
139
+ }
140
+ get config() {
141
+ return { ...this._config };
142
+ }
143
+ get providers() {
144
+ return [...this._config.providers];
145
+ }
146
+ getProvider(id) {
147
+ return this._config.providers.find(p => p.id === id);
148
+ }
149
+ getActiveProvider() {
150
+ return this.getProvider(this._config.activeProvider);
151
+ }
152
+ getCompleterProvider() {
153
+ if (this._config.useSameProviderForChatAndCompleter) {
154
+ return this.getActiveProvider();
155
+ }
156
+ return this._config.activeCompleterProvider
157
+ ? this.getProvider(this._config.activeCompleterProvider)
158
+ : this.getActiveProvider();
159
+ }
160
+ async addProvider(providerConfig) {
161
+ const id = `${providerConfig.provider}-${Date.now()}`;
162
+ const newProvider = {
163
+ id,
164
+ name: providerConfig.name,
165
+ provider: providerConfig.provider,
166
+ model: providerConfig.model,
167
+ apiKey: providerConfig.apiKey,
168
+ baseURL: providerConfig.baseURL,
169
+ headers: providerConfig.headers,
170
+ customSettings: providerConfig.customSettings
171
+ };
172
+ this._config.providers.push(newProvider);
173
+ // If this is the first provider, make it active
174
+ if (this._config.providers.length === 1) {
175
+ this._config.activeProvider = id;
176
+ // Save both providers and activeProvider
177
+ await this.saveSetting('providers', this._config.providers);
178
+ await this.saveSetting('activeProvider', this._config.activeProvider);
179
+ }
180
+ else {
181
+ // Only save providers
182
+ await this.saveSetting('providers', this._config.providers);
183
+ }
184
+ return id;
185
+ }
186
+ async removeProvider(id) {
187
+ const index = this._config.providers.findIndex(p => p.id === id);
188
+ if (index === -1) {
189
+ return;
190
+ }
191
+ this._config.providers.splice(index, 1);
192
+ await this.saveSetting('providers', this._config.providers);
193
+ // If this was the active provider, select a new one
194
+ if (this._config.activeProvider === id) {
195
+ this._config.activeProvider =
196
+ this._config.providers.length > 0 ? this._config.providers[0].id : '';
197
+ await this.saveSetting('activeProvider', this._config.activeProvider);
198
+ }
199
+ if (this._config.activeCompleterProvider === id) {
200
+ this._config.activeCompleterProvider = undefined;
201
+ await this.saveSetting('activeCompleterProvider', this._config.activeCompleterProvider);
202
+ }
203
+ }
204
+ async updateProvider(id, updates) {
205
+ const provider = this.getProvider(id);
206
+ if (!provider) {
207
+ return;
208
+ }
209
+ Object.assign(provider, updates);
210
+ await this.saveSetting('providers', this._config.providers);
211
+ }
212
+ async setActiveProvider(id) {
213
+ if (this.getProvider(id)) {
214
+ this._config.activeProvider = id;
215
+ await this.saveSetting('activeProvider', this._config.activeProvider);
216
+ }
217
+ }
218
+ async setActiveCompleterProvider(id) {
219
+ this._config.activeCompleterProvider = id;
220
+ await this.saveSetting('activeCompleterProvider', this._config.activeCompleterProvider);
221
+ }
222
+ get mcpServers() {
223
+ return [...this._config.mcpServers];
224
+ }
225
+ getMCPServer(id) {
226
+ return this._config.mcpServers.find(s => s.id === id);
227
+ }
228
+ async addMCPServer(serverConfig) {
229
+ const id = `mcp-${Date.now()}`;
230
+ const newServer = {
231
+ id,
232
+ name: serverConfig.name,
233
+ url: serverConfig.url,
234
+ enabled: serverConfig.enabled
235
+ };
236
+ this._config.mcpServers.push(newServer);
237
+ await this.saveSetting('mcpServers', this._config.mcpServers);
238
+ return id;
239
+ }
240
+ async removeMCPServer(id) {
241
+ const index = this._config.mcpServers.findIndex(s => s.id === id);
242
+ if (index === -1) {
243
+ return;
244
+ }
245
+ this._config.mcpServers.splice(index, 1);
246
+ await this.saveSetting('mcpServers', this._config.mcpServers);
247
+ }
248
+ async updateMCPServer(id, updates) {
249
+ const server = this.getMCPServer(id);
250
+ if (!server) {
251
+ return;
252
+ }
253
+ Object.assign(server, updates);
254
+ await this.saveSetting('mcpServers', this._config.mcpServers);
255
+ }
256
+ async updateConfig(updates) {
257
+ // Update config and save only changed settings
258
+ const promises = [];
259
+ for (const [key, value] of Object.entries(updates)) {
260
+ if (key in this._config &&
261
+ this._config[key] !== value) {
262
+ this._config[key] = value;
263
+ promises.push(this.saveSetting(key, value));
264
+ }
265
+ }
266
+ // Wait for all settings to be saved
267
+ await Promise.all(promises);
268
+ }
269
+ getApiKey(id) {
270
+ // First check the active completer provider
271
+ const activeCompleterProvider = this.getCompleterProvider();
272
+ if (activeCompleterProvider && activeCompleterProvider.id === id) {
273
+ return activeCompleterProvider.apiKey || '';
274
+ }
275
+ // Fallback to active chat provider
276
+ const activeProvider = this.getActiveProvider();
277
+ if (activeProvider && activeProvider.id === id) {
278
+ return activeProvider.apiKey || '';
279
+ }
280
+ return '';
281
+ }
282
+ async saveSetting(key, value) {
283
+ try {
284
+ if (this._settings) {
285
+ // Only save the specific setting that changed
286
+ if (value !== undefined) {
287
+ await this._settings.set(key, value);
288
+ }
289
+ }
290
+ }
291
+ catch (error) {
292
+ console.warn(`Failed to save setting '${key}' to JupyterLab settings, falling back to localStorage:`, error);
293
+ }
294
+ this.stateChanged.emit(void 0);
295
+ }
296
+ }
@@ -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;