@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.
Files changed (162) hide show
  1. package/lib/agent.d.ts +233 -0
  2. package/lib/agent.js +604 -0
  3. package/lib/chat-model.d.ts +195 -0
  4. package/lib/chat-model.js +590 -0
  5. package/lib/completion/completion-provider.d.ts +83 -0
  6. package/lib/completion/completion-provider.js +209 -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 +4 -5
  24. package/lib/index.js +322 -167
  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 +69 -0
  28. package/lib/models/settings-model.js +295 -0
  29. package/lib/providers/built-in-providers.d.ts +9 -0
  30. package/lib/providers/built-in-providers.js +192 -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 +157 -86
  36. package/lib/tokens.js +16 -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 +40 -0
  42. package/lib/tools/notebook.js +762 -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 +39 -0
  46. package/lib/widgets/ai-settings.js +506 -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 +13 -0
  50. package/lib/widgets/provider-config-dialog.js +104 -0
  51. package/package.json +150 -41
  52. package/schema/settings-model.json +153 -0
  53. package/src/agent.ts +800 -0
  54. package/src/chat-model.ts +770 -0
  55. package/src/completion/completion-provider.ts +308 -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 +468 -238
  65. package/src/mcp/browser.ts +213 -0
  66. package/src/models/settings-model.ts +409 -0
  67. package/src/providers/built-in-providers.ts +216 -0
  68. package/src/providers/models.ts +79 -0
  69. package/src/providers/provider-registry.ts +189 -0
  70. package/src/tokens.ts +203 -90
  71. package/src/tools/commands.ts +151 -0
  72. package/src/tools/file.ts +307 -0
  73. package/src/tools/notebook.ts +964 -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 +1100 -0
  77. package/src/widgets/chat-wrapper.tsx +543 -0
  78. package/src/widgets/provider-config-dialog.tsx +256 -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,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
+ }