@jupyterlite/ai 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/agent.d.ts +33 -115
- package/lib/agent.js +192 -106
- package/lib/chat-model-handler.d.ts +9 -11
- package/lib/chat-model-handler.js +9 -4
- package/lib/chat-model.d.ts +84 -13
- package/lib/chat-model.js +214 -136
- package/lib/completion/completion-provider.d.ts +2 -3
- package/lib/components/completion-status.d.ts +2 -2
- package/lib/components/index.d.ts +1 -1
- package/lib/components/index.js +1 -1
- package/lib/components/model-select.d.ts +3 -3
- package/lib/components/save-button.d.ts +31 -0
- package/lib/components/save-button.js +41 -0
- package/lib/components/tool-select.d.ts +3 -4
- package/lib/components/{token-usage-display.d.ts → usage-display.d.ts} +13 -14
- package/lib/components/usage-display.js +109 -0
- package/lib/diff-manager.d.ts +2 -3
- package/lib/index.d.ts +2 -4
- package/lib/index.js +186 -28
- package/lib/models/settings-model.d.ts +11 -53
- package/lib/models/settings-model.js +38 -22
- package/lib/providers/built-in-providers.js +22 -36
- package/lib/providers/generated-context-windows.d.ts +8 -0
- package/lib/providers/generated-context-windows.js +96 -0
- package/lib/providers/model-info.d.ts +3 -0
- package/lib/providers/model-info.js +58 -0
- package/lib/tokens.d.ts +361 -36
- package/lib/tokens.js +18 -13
- package/lib/tools/commands.d.ts +2 -3
- package/lib/widgets/ai-settings.d.ts +3 -5
- package/lib/widgets/ai-settings.js +12 -0
- package/lib/widgets/main-area-chat.d.ts +2 -3
- package/lib/widgets/main-area-chat.js +12 -12
- package/lib/widgets/provider-config-dialog.d.ts +1 -2
- package/lib/widgets/provider-config-dialog.js +34 -34
- package/package.json +17 -10
- package/schema/settings-model.json +18 -1
- package/src/agent.ts +275 -248
- package/src/chat-model-handler.ts +25 -21
- package/src/chat-model.ts +307 -196
- package/src/completion/completion-provider.ts +7 -4
- package/src/components/completion-status.tsx +3 -3
- package/src/components/index.ts +1 -1
- package/src/components/model-select.tsx +4 -3
- package/src/components/save-button.tsx +84 -0
- package/src/components/tool-select.tsx +10 -4
- package/src/components/usage-display.tsx +208 -0
- package/src/diff-manager.ts +4 -4
- package/src/index.ts +250 -58
- package/src/models/settings-model.ts +46 -88
- package/src/providers/built-in-providers.ts +22 -36
- package/src/providers/generated-context-windows.ts +102 -0
- package/src/providers/model-info.ts +88 -0
- package/src/tokens.ts +438 -58
- package/src/tools/commands.ts +2 -3
- package/src/widgets/ai-settings.tsx +69 -15
- package/src/widgets/main-area-chat.ts +18 -15
- package/src/widgets/provider-config-dialog.tsx +96 -61
- package/style/base.css +17 -195
- package/lib/approval-buttons.d.ts +0 -49
- package/lib/approval-buttons.js +0 -79
- package/lib/components/token-usage-display.js +0 -72
- package/src/approval-buttons.ts +0 -115
- package/src/components/token-usage-display.tsx +0 -138
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { ActiveCellManager } from '@jupyter/chat';
|
|
2
|
-
import { TranslationBundle } from '@jupyterlab/translation';
|
|
3
|
-
import { AgentManagerFactory } from './agent';
|
|
4
|
-
import { AIChatModel } from './chat-model';
|
|
5
|
-
import { AISettingsModel } from './models/settings-model';
|
|
6
|
-
import { IChatModelHandler, IProviderRegistry, ITokenUsage, IToolRegistry } from './tokens';
|
|
7
2
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
8
3
|
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
4
|
+
import { Contents } from '@jupyterlab/services';
|
|
5
|
+
import { AIChatModel } from './chat-model';
|
|
6
|
+
import type { IAgentManagerFactory, IAISettingsModel, IChatModelHandler, ICreateChatOptions, IProviderRegistry, IToolRegistry } from './tokens';
|
|
9
7
|
/**
|
|
10
8
|
* The chat model handler.
|
|
11
9
|
*/
|
|
12
10
|
export declare class ChatModelHandler implements IChatModelHandler {
|
|
13
11
|
constructor(options: ChatModelHandler.IOptions);
|
|
14
|
-
createModel(
|
|
12
|
+
createModel(options: ICreateChatOptions): AIChatModel;
|
|
15
13
|
/**
|
|
16
14
|
* Getter/setter for the active cell manager.
|
|
17
15
|
*/
|
|
@@ -24,7 +22,7 @@ export declare class ChatModelHandler implements IChatModelHandler {
|
|
|
24
22
|
private _providerRegistry?;
|
|
25
23
|
private _rmRegistry;
|
|
26
24
|
private _activeCellManager?;
|
|
27
|
-
private
|
|
25
|
+
private _contentsManager?;
|
|
28
26
|
}
|
|
29
27
|
export declare namespace ChatModelHandler {
|
|
30
28
|
interface IOptions {
|
|
@@ -35,11 +33,11 @@ export declare namespace ChatModelHandler {
|
|
|
35
33
|
/**
|
|
36
34
|
* The agent manager factory.
|
|
37
35
|
*/
|
|
38
|
-
agentManagerFactory:
|
|
36
|
+
agentManagerFactory: IAgentManagerFactory;
|
|
39
37
|
/**
|
|
40
38
|
* AI settings model for configuration
|
|
41
39
|
*/
|
|
42
|
-
settingsModel:
|
|
40
|
+
settingsModel: IAISettingsModel;
|
|
43
41
|
/**
|
|
44
42
|
* Optional tool registry for managing available tools
|
|
45
43
|
*/
|
|
@@ -57,8 +55,8 @@ export declare namespace ChatModelHandler {
|
|
|
57
55
|
*/
|
|
58
56
|
activeCellManager?: ActiveCellManager | undefined;
|
|
59
57
|
/**
|
|
60
|
-
* The
|
|
58
|
+
* The contents manager.
|
|
61
59
|
*/
|
|
62
|
-
|
|
60
|
+
contentsManager?: Contents.IManager;
|
|
63
61
|
}
|
|
64
62
|
}
|
|
@@ -11,9 +11,10 @@ export class ChatModelHandler {
|
|
|
11
11
|
this._providerRegistry = options.providerRegistry;
|
|
12
12
|
this._rmRegistry = options.rmRegistry;
|
|
13
13
|
this._activeCellManager = options.activeCellManager;
|
|
14
|
-
this.
|
|
14
|
+
this._contentsManager = options.contentsManager;
|
|
15
15
|
}
|
|
16
|
-
createModel(
|
|
16
|
+
createModel(options) {
|
|
17
|
+
const { name, activeProvider, tokenUsage, messages, autosave } = options;
|
|
17
18
|
// Create Agent Manager first so it can be shared
|
|
18
19
|
const agentManager = this._agentManagerFactory.createAgent({
|
|
19
20
|
settingsModel: this._settingsModel,
|
|
@@ -30,8 +31,12 @@ export class ChatModelHandler {
|
|
|
30
31
|
agentManager,
|
|
31
32
|
activeCellManager: this._activeCellManager,
|
|
32
33
|
documentManager: this._docManager,
|
|
33
|
-
|
|
34
|
+
contentsManager: this._contentsManager
|
|
34
35
|
});
|
|
36
|
+
messages?.forEach(message => {
|
|
37
|
+
model.messageAdded({ ...message.content });
|
|
38
|
+
});
|
|
39
|
+
model.autosave = autosave ?? false;
|
|
35
40
|
model.name = name;
|
|
36
41
|
return model;
|
|
37
42
|
}
|
|
@@ -51,5 +56,5 @@ export class ChatModelHandler {
|
|
|
51
56
|
_providerRegistry;
|
|
52
57
|
_rmRegistry;
|
|
53
58
|
_activeCellManager;
|
|
54
|
-
|
|
59
|
+
_contentsManager;
|
|
55
60
|
}
|
package/lib/chat-model.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { AbstractChatModel, IActiveCellManager, IChatContext, INewMessage, IUser } from '@jupyter/chat';
|
|
1
|
+
import { AbstractChatModel, IActiveCellManager, IAttachment, IChatContext, IMessageContent, INewMessage, IUser } from '@jupyter/chat';
|
|
2
2
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
3
|
-
import {
|
|
3
|
+
import { Contents } from '@jupyterlab/services';
|
|
4
4
|
import { ISignal } from '@lumino/signaling';
|
|
5
|
-
import {
|
|
6
|
-
import { AISettingsModel } from './models/settings-model';
|
|
7
|
-
import { ITokenUsage } from './tokens';
|
|
5
|
+
import type { IAgentManager, IAISettingsModel, ITokenUsage } from './tokens';
|
|
8
6
|
/**
|
|
9
7
|
* AI Chat Model implementation that provides chat functionality tool integration,
|
|
10
8
|
* and MCP server support.
|
|
@@ -20,6 +18,15 @@ export declare class AIChatModel extends AbstractChatModel {
|
|
|
20
18
|
*/
|
|
21
19
|
get name(): string;
|
|
22
20
|
set name(value: string);
|
|
21
|
+
/**
|
|
22
|
+
* Whether to save the chat automatically.
|
|
23
|
+
*/
|
|
24
|
+
get autosave(): boolean;
|
|
25
|
+
set autosave(value: boolean);
|
|
26
|
+
/**
|
|
27
|
+
* A signal emitting when the autosave flag changed.
|
|
28
|
+
*/
|
|
29
|
+
get autosaveChanged(): ISignal<AIChatModel, boolean>;
|
|
23
30
|
/**
|
|
24
31
|
* A signal emitting when the chat name has changed.
|
|
25
32
|
*/
|
|
@@ -31,11 +38,19 @@ export declare class AIChatModel extends AbstractChatModel {
|
|
|
31
38
|
/**
|
|
32
39
|
* A signal emitting when the token usage changed.
|
|
33
40
|
*/
|
|
34
|
-
get tokenUsageChanged(): ISignal<
|
|
41
|
+
get tokenUsageChanged(): ISignal<IAgentManager, ITokenUsage>;
|
|
35
42
|
/**
|
|
36
43
|
* Get the agent manager associated to the model.
|
|
37
44
|
*/
|
|
38
|
-
get agentManager():
|
|
45
|
+
get agentManager(): IAgentManager;
|
|
46
|
+
/**
|
|
47
|
+
* Whether save/restore is available.
|
|
48
|
+
*/
|
|
49
|
+
get saveAvailable(): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Dispose of the model.
|
|
52
|
+
*/
|
|
53
|
+
dispose(): void;
|
|
39
54
|
/**
|
|
40
55
|
* Creates a chat context for the current conversation.
|
|
41
56
|
*/
|
|
@@ -57,6 +72,21 @@ export declare class AIChatModel extends AbstractChatModel {
|
|
|
57
72
|
* @param message The user message to send
|
|
58
73
|
*/
|
|
59
74
|
sendMessage(message: INewMessage): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Save the chat as json file.
|
|
77
|
+
*/
|
|
78
|
+
save: () => Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Restore the chat from a json file.
|
|
81
|
+
*
|
|
82
|
+
* @param silent - Whether a log should be displayed in the console if the
|
|
83
|
+
* restoration is not possible.
|
|
84
|
+
*/
|
|
85
|
+
restore: (filepath: string, silent?: boolean) => Promise<boolean>;
|
|
86
|
+
/**
|
|
87
|
+
* Serialize the model for backup
|
|
88
|
+
*/
|
|
89
|
+
private _serializeModel;
|
|
60
90
|
/**
|
|
61
91
|
* Gets the AI user information for system messages.
|
|
62
92
|
*/
|
|
@@ -149,7 +179,10 @@ export declare class AIChatModel extends AbstractChatModel {
|
|
|
149
179
|
private _agentManager;
|
|
150
180
|
private _currentStreamingMessage;
|
|
151
181
|
private _nameChanged;
|
|
152
|
-
private
|
|
182
|
+
private _contentsManager?;
|
|
183
|
+
private _autosave;
|
|
184
|
+
private _autosaveChanged;
|
|
185
|
+
private _autosaveDebouncer;
|
|
153
186
|
}
|
|
154
187
|
/**
|
|
155
188
|
* Namespace containing types and interfaces for AIChatModel.
|
|
@@ -166,11 +199,11 @@ export declare namespace AIChatModel {
|
|
|
166
199
|
/**
|
|
167
200
|
* Settings model for AI configuration
|
|
168
201
|
*/
|
|
169
|
-
settingsModel:
|
|
202
|
+
settingsModel: IAISettingsModel;
|
|
170
203
|
/**
|
|
171
204
|
* Optional agent manager for handling AI agent lifecycle
|
|
172
205
|
*/
|
|
173
|
-
agentManager:
|
|
206
|
+
agentManager: IAgentManager;
|
|
174
207
|
/**
|
|
175
208
|
* Optional active cell manager for Jupyter integration
|
|
176
209
|
*/
|
|
@@ -180,9 +213,13 @@ export declare namespace AIChatModel {
|
|
|
180
213
|
*/
|
|
181
214
|
documentManager?: IDocumentManager;
|
|
182
215
|
/**
|
|
183
|
-
* The
|
|
216
|
+
* The contents manager.
|
|
217
|
+
*/
|
|
218
|
+
contentsManager?: Contents.IManager;
|
|
219
|
+
/**
|
|
220
|
+
* Whether to restore or not the message (default to true)
|
|
184
221
|
*/
|
|
185
|
-
|
|
222
|
+
restore?: boolean;
|
|
186
223
|
}
|
|
187
224
|
/**
|
|
188
225
|
* The chat context for toolbar buttons.
|
|
@@ -203,6 +240,40 @@ export declare namespace AIChatModel {
|
|
|
203
240
|
/**
|
|
204
241
|
* The agent manager of the chat.
|
|
205
242
|
*/
|
|
206
|
-
agentManager:
|
|
243
|
+
agentManager: IAgentManager;
|
|
207
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* The exported chat format.
|
|
247
|
+
*/
|
|
248
|
+
type ExportedChat = {
|
|
249
|
+
/**
|
|
250
|
+
* Message list (user are only string to avoid duplication).
|
|
251
|
+
*/
|
|
252
|
+
messages: IMessageContent<string, string>[];
|
|
253
|
+
/**
|
|
254
|
+
* The user list.
|
|
255
|
+
*/
|
|
256
|
+
users: {
|
|
257
|
+
[id: string]: IUser;
|
|
258
|
+
};
|
|
259
|
+
/**
|
|
260
|
+
* The attachments of the chat.
|
|
261
|
+
*/
|
|
262
|
+
attachments?: {
|
|
263
|
+
[id: string]: IAttachment;
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* The metadata associated to the chat.
|
|
267
|
+
*/
|
|
268
|
+
metadata?: {
|
|
269
|
+
/**
|
|
270
|
+
* Provider of the chat.
|
|
271
|
+
*/
|
|
272
|
+
provider?: string;
|
|
273
|
+
/**
|
|
274
|
+
* Whether the chat is automatically saved.
|
|
275
|
+
*/
|
|
276
|
+
autosave?: boolean;
|
|
277
|
+
};
|
|
278
|
+
};
|
|
208
279
|
}
|
package/lib/chat-model.js
CHANGED
|
@@ -2,6 +2,7 @@ import { AbstractChatModel } from '@jupyter/chat';
|
|
|
2
2
|
import { PathExt } from '@jupyterlab/coreutils';
|
|
3
3
|
import * as nbformat from '@jupyterlab/nbformat';
|
|
4
4
|
import { UUID } from '@lumino/coreutils';
|
|
5
|
+
import { Debouncer } from '@lumino/polling';
|
|
5
6
|
import { Signal } from '@lumino/signaling';
|
|
6
7
|
import { AI_AVATAR } from './icons';
|
|
7
8
|
/**
|
|
@@ -25,12 +26,12 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
25
26
|
this._settingsModel = options.settingsModel;
|
|
26
27
|
this._user = options.user;
|
|
27
28
|
this._agentManager = options.agentManager;
|
|
28
|
-
this.
|
|
29
|
+
this._contentsManager = options.contentsManager;
|
|
29
30
|
// Listen for agent events
|
|
30
31
|
this._agentManager.agentEvent.connect(this._onAgentEvent, this);
|
|
31
32
|
// Listen for settings changes to update chat behavior
|
|
32
33
|
this._settingsModel.stateChanged.connect(this._onSettingsChanged, this);
|
|
33
|
-
this.
|
|
34
|
+
this._autosaveDebouncer = new Debouncer(this.save, 3000);
|
|
34
35
|
}
|
|
35
36
|
/**
|
|
36
37
|
* Override the getter/setter of the name to add a signal when renaming a chat.
|
|
@@ -41,6 +42,37 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
41
42
|
set name(value) {
|
|
42
43
|
super.name = value;
|
|
43
44
|
this._nameChanged.emit(value);
|
|
45
|
+
if (!this.messages.length) {
|
|
46
|
+
const directory = this._settingsModel.config.chatBackupDirectory;
|
|
47
|
+
const filepath = PathExt.join(directory, `${this.name}.chat`);
|
|
48
|
+
this.restore(filepath, true);
|
|
49
|
+
}
|
|
50
|
+
this.setReady();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Whether to save the chat automatically.
|
|
54
|
+
*/
|
|
55
|
+
get autosave() {
|
|
56
|
+
return this._autosave;
|
|
57
|
+
}
|
|
58
|
+
set autosave(value) {
|
|
59
|
+
this._autosave = value;
|
|
60
|
+
this._autosaveChanged.emit(value);
|
|
61
|
+
if (value) {
|
|
62
|
+
this.messagesUpdated.connect(this._autosaveDebouncer.invoke, this._autosaveDebouncer);
|
|
63
|
+
this.messageChanged.connect(this._autosaveDebouncer.invoke, this._autosaveDebouncer);
|
|
64
|
+
this._autosaveDebouncer.invoke();
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.messagesUpdated.disconnect(this._autosaveDebouncer.invoke, this._autosaveDebouncer);
|
|
68
|
+
this.messageChanged.disconnect(this._autosaveDebouncer.invoke, this._autosaveDebouncer);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A signal emitting when the autosave flag changed.
|
|
73
|
+
*/
|
|
74
|
+
get autosaveChanged() {
|
|
75
|
+
return this._autosaveChanged;
|
|
44
76
|
}
|
|
45
77
|
/**
|
|
46
78
|
* A signal emitting when the chat name has changed.
|
|
@@ -66,6 +98,19 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
66
98
|
get agentManager() {
|
|
67
99
|
return this._agentManager;
|
|
68
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Whether save/restore is available.
|
|
103
|
+
*/
|
|
104
|
+
get saveAvailable() {
|
|
105
|
+
return !!this._contentsManager;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Dispose of the model.
|
|
109
|
+
*/
|
|
110
|
+
dispose() {
|
|
111
|
+
this.messagesUpdated.disconnect(this._autosaveDebouncer.invoke, this._autosaveDebouncer);
|
|
112
|
+
super.dispose();
|
|
113
|
+
}
|
|
69
114
|
/**
|
|
70
115
|
* Creates a chat context for the current conversation.
|
|
71
116
|
*/
|
|
@@ -172,6 +217,141 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
172
217
|
this.updateWriters([]);
|
|
173
218
|
}
|
|
174
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* Save the chat as json file.
|
|
222
|
+
*/
|
|
223
|
+
save = async () => {
|
|
224
|
+
if (!this._contentsManager) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const directory = this._settingsModel.config.chatBackupDirectory;
|
|
228
|
+
const filepath = PathExt.join(directory, `${this.name}.chat`);
|
|
229
|
+
const content = JSON.stringify(this._serializeModel());
|
|
230
|
+
await this._contentsManager
|
|
231
|
+
.get(filepath, { content: false })
|
|
232
|
+
.catch(async () => {
|
|
233
|
+
await this._contentsManager
|
|
234
|
+
?.get(directory, { content: false })
|
|
235
|
+
.catch(async () => {
|
|
236
|
+
const dir = await this._contentsManager.newUntitled({
|
|
237
|
+
type: 'directory'
|
|
238
|
+
});
|
|
239
|
+
await this._contentsManager.rename(dir.path, directory);
|
|
240
|
+
});
|
|
241
|
+
const file = await this._contentsManager.newUntitled({ ext: '.chat' });
|
|
242
|
+
await this._contentsManager?.rename(file.path, filepath);
|
|
243
|
+
});
|
|
244
|
+
await this._contentsManager.save(filepath, {
|
|
245
|
+
content,
|
|
246
|
+
type: 'file',
|
|
247
|
+
format: 'text'
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* Restore the chat from a json file.
|
|
252
|
+
*
|
|
253
|
+
* @param silent - Whether a log should be displayed in the console if the
|
|
254
|
+
* restoration is not possible.
|
|
255
|
+
*/
|
|
256
|
+
restore = async (filepath, silent = false) => {
|
|
257
|
+
if (!this._contentsManager) {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
const contentModel = await this._contentsManager
|
|
261
|
+
.get(filepath, { content: true, type: 'file', format: 'text' })
|
|
262
|
+
.catch(() => {
|
|
263
|
+
if (!silent) {
|
|
264
|
+
console.log(`There is no backup for chat '${this.name}'`);
|
|
265
|
+
}
|
|
266
|
+
return;
|
|
267
|
+
});
|
|
268
|
+
if (!contentModel) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
let content;
|
|
272
|
+
try {
|
|
273
|
+
content = JSON.parse(contentModel.content);
|
|
274
|
+
}
|
|
275
|
+
catch (e) {
|
|
276
|
+
throw `Error when parsing the chat ${filepath}\n${e}`;
|
|
277
|
+
}
|
|
278
|
+
if (content.metadata?.provider) {
|
|
279
|
+
if (this._settingsModel.getProvider(content.metadata.provider)) {
|
|
280
|
+
this._agentManager.activeProvider = content.metadata.provider;
|
|
281
|
+
}
|
|
282
|
+
else if (!silent) {
|
|
283
|
+
console.log(`Provider '${content.metadata.provider}' doesn't exist, it can't be restored.`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
else if (!silent) {
|
|
287
|
+
console.log(`Provider not providing when restoring ${filepath}.`);
|
|
288
|
+
}
|
|
289
|
+
const messages = content.messages.map(message => {
|
|
290
|
+
let attachments = [];
|
|
291
|
+
if (content.attachments && message.attachments) {
|
|
292
|
+
attachments =
|
|
293
|
+
message.attachments.map(index => content.attachments[index]) ?? [];
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
...message,
|
|
297
|
+
sender: content.users[message.sender] ?? { username: 'unknown' },
|
|
298
|
+
mentions: message.mentions?.map(mention => content.users[mention]),
|
|
299
|
+
attachments
|
|
300
|
+
};
|
|
301
|
+
});
|
|
302
|
+
this.clearMessages();
|
|
303
|
+
this.messagesInserted(0, messages);
|
|
304
|
+
this._agentManager.setHistory(messages);
|
|
305
|
+
this.autosave = content.metadata?.autosave ?? false;
|
|
306
|
+
return true;
|
|
307
|
+
};
|
|
308
|
+
/**
|
|
309
|
+
* Serialize the model for backup
|
|
310
|
+
*/
|
|
311
|
+
_serializeModel() {
|
|
312
|
+
const provider = this._agentManager.activeProvider;
|
|
313
|
+
const messages = [];
|
|
314
|
+
const users = {};
|
|
315
|
+
const attachmentMap = new Map(); // JSON → index
|
|
316
|
+
const attachmentsList = []; // Actual attachments
|
|
317
|
+
this.messages.forEach(message => {
|
|
318
|
+
let attachmentIndexes = [];
|
|
319
|
+
if (message.attachments) {
|
|
320
|
+
attachmentIndexes = message.attachments.map(attachment => {
|
|
321
|
+
const attachmentJson = JSON.stringify(attachment);
|
|
322
|
+
let index;
|
|
323
|
+
if (attachmentMap.has(attachmentJson)) {
|
|
324
|
+
index = attachmentMap.get(attachmentJson);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
index = attachmentsList.length;
|
|
328
|
+
attachmentMap.set(attachmentJson, index);
|
|
329
|
+
attachmentsList.push(attachment);
|
|
330
|
+
}
|
|
331
|
+
return index.toString();
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
messages.push({
|
|
335
|
+
...message.content,
|
|
336
|
+
sender: message.sender.username,
|
|
337
|
+
mentions: message.mentions?.map(user => user.username),
|
|
338
|
+
attachments: attachmentIndexes
|
|
339
|
+
});
|
|
340
|
+
if (!users[message.sender.username]) {
|
|
341
|
+
users[message.sender.username] = message.sender;
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
const attachments = Object.fromEntries(attachmentsList.map((item, index) => [index, item]));
|
|
345
|
+
return {
|
|
346
|
+
messages,
|
|
347
|
+
users,
|
|
348
|
+
attachments,
|
|
349
|
+
metadata: {
|
|
350
|
+
provider,
|
|
351
|
+
autosave: this.autosave
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
175
355
|
/**
|
|
176
356
|
* Gets the AI user information for system messages.
|
|
177
357
|
*/
|
|
@@ -352,13 +532,18 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
352
532
|
};
|
|
353
533
|
this._toolContexts.set(event.data.callId, context);
|
|
354
534
|
const toolCallMessage = {
|
|
355
|
-
body:
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
535
|
+
body: '',
|
|
536
|
+
mime_model: {
|
|
537
|
+
data: {
|
|
538
|
+
'application/vnd.jupyter.chat.components': 'tool-call'
|
|
539
|
+
},
|
|
540
|
+
metadata: {
|
|
541
|
+
toolName: context.toolName,
|
|
542
|
+
input: context.input,
|
|
543
|
+
status: context.status,
|
|
544
|
+
summary: context.summary
|
|
545
|
+
}
|
|
546
|
+
},
|
|
362
547
|
sender: this._getAIUser(),
|
|
363
548
|
id: messageId,
|
|
364
549
|
time: Date.now() / 1000,
|
|
@@ -383,7 +568,8 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
383
568
|
});
|
|
384
569
|
for (const bundle of mimeBundles) {
|
|
385
570
|
this.messageAdded({
|
|
386
|
-
body:
|
|
571
|
+
body: '',
|
|
572
|
+
mime_model: bundle,
|
|
387
573
|
sender: this._getAIUser(),
|
|
388
574
|
id: UUID.uuid4(),
|
|
389
575
|
time: Date.now() / 1000,
|
|
@@ -456,15 +642,20 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
456
642
|
}
|
|
457
643
|
context.status = status;
|
|
458
644
|
existingMessage.update({
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
645
|
+
mime_model: {
|
|
646
|
+
data: {
|
|
647
|
+
'application/vnd.jupyter.chat.components': 'tool-call'
|
|
648
|
+
},
|
|
649
|
+
metadata: {
|
|
650
|
+
toolName: context.toolName,
|
|
651
|
+
input: context.input,
|
|
652
|
+
status: context.status,
|
|
653
|
+
summary: context.summary,
|
|
654
|
+
output,
|
|
655
|
+
targetId: this.name,
|
|
656
|
+
approvalId: context.approvalId
|
|
657
|
+
}
|
|
658
|
+
}
|
|
468
659
|
});
|
|
469
660
|
}
|
|
470
661
|
/**
|
|
@@ -695,7 +886,10 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
695
886
|
_agentManager;
|
|
696
887
|
_currentStreamingMessage = null;
|
|
697
888
|
_nameChanged = new Signal(this);
|
|
698
|
-
|
|
889
|
+
_contentsManager;
|
|
890
|
+
_autosave = false;
|
|
891
|
+
_autosaveChanged = new Signal(this);
|
|
892
|
+
_autosaveDebouncer;
|
|
699
893
|
}
|
|
700
894
|
var Private;
|
|
701
895
|
(function (Private) {
|
|
@@ -780,120 +974,4 @@ var Private;
|
|
|
780
974
|
}
|
|
781
975
|
}
|
|
782
976
|
Private.formatToolOutput = formatToolOutput;
|
|
783
|
-
function escapeHtml(value) {
|
|
784
|
-
// Prefer the same native escaping approach used in JupyterLab itself
|
|
785
|
-
// (e.g. `@jupyterlab/completer`).
|
|
786
|
-
if (typeof document !== 'undefined') {
|
|
787
|
-
const node = document.createElement('span');
|
|
788
|
-
node.textContent = value;
|
|
789
|
-
return node.innerHTML;
|
|
790
|
-
}
|
|
791
|
-
// Fallback
|
|
792
|
-
return value
|
|
793
|
-
.replace(/&/g, '&')
|
|
794
|
-
.replace(/</g, '<')
|
|
795
|
-
.replace(/>/g, '>')
|
|
796
|
-
.replace(/"/g, '"')
|
|
797
|
-
.replace(/'/g, ''');
|
|
798
|
-
}
|
|
799
|
-
Private.escapeHtml = escapeHtml;
|
|
800
|
-
const STATUS_CONFIG = {
|
|
801
|
-
pending: {
|
|
802
|
-
cssClass: 'jp-ai-tool-pending',
|
|
803
|
-
statusClass: 'jp-ai-tool-status-pending'
|
|
804
|
-
},
|
|
805
|
-
awaiting_approval: {
|
|
806
|
-
cssClass: 'jp-ai-tool-pending',
|
|
807
|
-
statusClass: 'jp-ai-tool-status-approval',
|
|
808
|
-
open: true
|
|
809
|
-
},
|
|
810
|
-
approved: {
|
|
811
|
-
cssClass: 'jp-ai-tool-pending',
|
|
812
|
-
statusClass: 'jp-ai-tool-status-completed'
|
|
813
|
-
},
|
|
814
|
-
rejected: {
|
|
815
|
-
cssClass: 'jp-ai-tool-error',
|
|
816
|
-
statusClass: 'jp-ai-tool-status-error'
|
|
817
|
-
},
|
|
818
|
-
completed: {
|
|
819
|
-
cssClass: 'jp-ai-tool-completed',
|
|
820
|
-
statusClass: 'jp-ai-tool-status-completed'
|
|
821
|
-
},
|
|
822
|
-
error: {
|
|
823
|
-
cssClass: 'jp-ai-tool-error',
|
|
824
|
-
statusClass: 'jp-ai-tool-status-error'
|
|
825
|
-
}
|
|
826
|
-
};
|
|
827
|
-
/**
|
|
828
|
-
* Returns the translated status text for a given tool status.
|
|
829
|
-
*/
|
|
830
|
-
const getStatusText = (status, trans) => {
|
|
831
|
-
switch (status) {
|
|
832
|
-
case 'pending':
|
|
833
|
-
return trans.__('Running...');
|
|
834
|
-
case 'awaiting_approval':
|
|
835
|
-
return trans.__('Awaiting Approval');
|
|
836
|
-
case 'approved':
|
|
837
|
-
return trans.__('Approved - Executing...');
|
|
838
|
-
case 'rejected':
|
|
839
|
-
return trans.__('Rejected');
|
|
840
|
-
case 'completed':
|
|
841
|
-
return trans.__('Completed');
|
|
842
|
-
case 'error':
|
|
843
|
-
return trans.__('Error');
|
|
844
|
-
}
|
|
845
|
-
};
|
|
846
|
-
/**
|
|
847
|
-
* Builds HTML for a tool call display.
|
|
848
|
-
*/
|
|
849
|
-
function buildToolCallHtml(options) {
|
|
850
|
-
const { toolName, input, status, summary, output, approvalId, trans } = options;
|
|
851
|
-
const config = STATUS_CONFIG[status];
|
|
852
|
-
const statusText = getStatusText(status, trans);
|
|
853
|
-
const escapedToolName = escapeHtml(toolName);
|
|
854
|
-
const escapedInput = escapeHtml(input);
|
|
855
|
-
const openAttr = config.open ? ' open' : '';
|
|
856
|
-
const summaryHtml = summary
|
|
857
|
-
? `<span class="jp-ai-tool-summary">${escapeHtml(summary)}</span>`
|
|
858
|
-
: '';
|
|
859
|
-
let bodyContent = `
|
|
860
|
-
<div class="jp-ai-tool-section">
|
|
861
|
-
<div class="jp-ai-tool-label">${trans.__('Input')}</div>
|
|
862
|
-
<pre class="jp-ai-tool-code"><code>${escapedInput}</code></pre>
|
|
863
|
-
</div>`;
|
|
864
|
-
// Add approval buttons if awaiting approval
|
|
865
|
-
if (status === 'awaiting_approval' && approvalId) {
|
|
866
|
-
bodyContent += `
|
|
867
|
-
<div class="jp-ai-tool-approval-buttons jp-ai-approval-id--${approvalId}">
|
|
868
|
-
<button class="jp-ai-approval-btn jp-ai-approval-approve">${trans.__('Approve')}</button>
|
|
869
|
-
<button class="jp-ai-approval-btn jp-ai-approval-reject">${trans.__('Reject')}</button>
|
|
870
|
-
</div>`;
|
|
871
|
-
}
|
|
872
|
-
// Add output/result section if provided
|
|
873
|
-
if (output !== undefined) {
|
|
874
|
-
const escapedOutput = escapeHtml(output);
|
|
875
|
-
const label = status === 'error' ? trans.__('Error') : trans.__('Result');
|
|
876
|
-
bodyContent += `
|
|
877
|
-
<div class="jp-ai-tool-section">
|
|
878
|
-
<div class="jp-ai-tool-label">${label}</div>
|
|
879
|
-
<pre class="jp-ai-tool-code"><code>${escapedOutput}</code></pre>
|
|
880
|
-
</div>`;
|
|
881
|
-
}
|
|
882
|
-
const HTMLContent = `<details class="jp-ai-tool-call ${config.cssClass}"${openAttr}>
|
|
883
|
-
<summary class="jp-ai-tool-header">
|
|
884
|
-
<div class="jp-ai-tool-icon">⚡</div>
|
|
885
|
-
<div class="jp-ai-tool-title">${escapedToolName}${summaryHtml}</div>
|
|
886
|
-
<div class="jp-ai-tool-status ${config.statusClass}">${statusText}</div>
|
|
887
|
-
</summary>
|
|
888
|
-
<div class="jp-ai-tool-body">${bodyContent}
|
|
889
|
-
</div>
|
|
890
|
-
</details>`;
|
|
891
|
-
return {
|
|
892
|
-
trusted: true,
|
|
893
|
-
data: {
|
|
894
|
-
'text/html': HTMLContent
|
|
895
|
-
}
|
|
896
|
-
};
|
|
897
|
-
}
|
|
898
|
-
Private.buildToolCallHtml = buildToolCallHtml;
|
|
899
977
|
})(Private || (Private = {}));
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { CompletionHandler, IInlineCompletionContext, IInlineCompletionList, IInlineCompletionProvider } from '@jupyterlab/completer';
|
|
2
2
|
import { ISecretsManager } from 'jupyter-secrets-manager';
|
|
3
|
-
import {
|
|
4
|
-
import { type IProviderRegistry } from '../tokens';
|
|
3
|
+
import { type IAISettingsModel, type IProviderRegistry } from '../tokens';
|
|
5
4
|
/**
|
|
6
5
|
* Configuration interface for provider-specific completion behavior
|
|
7
6
|
*/
|
|
@@ -68,7 +67,7 @@ export declare namespace AICompletionProvider {
|
|
|
68
67
|
/**
|
|
69
68
|
* The AI settings model.
|
|
70
69
|
*/
|
|
71
|
-
settingsModel:
|
|
70
|
+
settingsModel: IAISettingsModel;
|
|
72
71
|
/**
|
|
73
72
|
* The provider registry
|
|
74
73
|
*/
|