@jupyterlite/ai 0.17.0 → 0.18.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 CHANGED
@@ -1,4 +1,3 @@
1
- import type { IMessageContent } from '@jupyter/chat';
2
1
  import { ISignal } from '@lumino/signaling';
3
2
  import { type ModelMessage, type UserContent } from 'ai';
4
3
  import { ISecretsManager } from 'jupyter-secrets-manager';
@@ -133,10 +132,10 @@ export declare class AgentManager implements IAgentManager {
133
132
  */
134
133
  clearHistory(): Promise<void>;
135
134
  /**
136
- * Sets the history with a list of messages from the chat.
137
- * @param messages The chat messages to set as history
135
+ * Sets the history from already-processed model messages.
136
+ * @param messages Pre-built model messages (may include binary content)
138
137
  */
139
- setHistory(messages: IMessageContent[]): void;
138
+ setHistory(messages: ModelMessage[]): void;
140
139
  /**
141
140
  * Stops the current streaming response by aborting the request.
142
141
  * Resolve any pending approval.
@@ -144,16 +143,16 @@ export declare class AgentManager implements IAgentManager {
144
143
  stopStreaming(reason?: string): void;
145
144
  /**
146
145
  * Approves a pending tool call.
147
- * @param approvalId The approval ID to approve
146
+ * @param toolCallId The tool call ID to approve
148
147
  * @param reason Optional reason for approval
149
148
  */
150
- approveToolCall(approvalId: string, reason?: string): void;
149
+ approveToolCall(toolCallId: string, reason?: string): void;
151
150
  /**
152
151
  * Rejects a pending tool call.
153
- * @param approvalId The approval ID to reject
152
+ * @param toolCallId The tool call ID to reject
154
153
  * @param reason Optional reason for rejection
155
154
  */
156
- rejectToolCall(approvalId: string, reason?: string): void;
155
+ rejectToolCall(toolCallId: string, reason?: string): void;
157
156
  /**
158
157
  * Generates AI response to user message using the agent.
159
158
  * Handles the complete execution cycle including tool calls.
@@ -169,6 +168,10 @@ export declare class AgentManager implements IAgentManager {
169
168
  * Updates cumulative token usage statistics from a completed model step.
170
169
  */
171
170
  private _updateTokenUsage;
171
+ /**
172
+ * Removes image and file parts from all user messages in the given list.
173
+ */
174
+ private _stripAttachments;
172
175
  /**
173
176
  * Gets the configured context window for the active provider.
174
177
  */
@@ -223,7 +226,7 @@ export declare class AgentManager implements IAgentManager {
223
226
  private _handleApprovalRequest;
224
227
  /**
225
228
  * Waits for user approval of a tool call.
226
- * @param approvalId The approval ID to wait for
229
+ * @param toolCallId The tool call ID to wait for approval
227
230
  * @returns Promise that resolves to true if approved, false if rejected
228
231
  */
229
232
  private _waitForApproval;
package/lib/agent.js CHANGED
@@ -331,29 +331,12 @@ export class AgentManager {
331
331
  this._tokenUsageChanged.emit(this._tokenUsage);
332
332
  }
333
333
  /**
334
- * Sets the history with a list of messages from the chat.
335
- * @param messages The chat messages to set as history
334
+ * Sets the history from already-processed model messages.
335
+ * @param messages Pre-built model messages (may include binary content)
336
336
  */
337
337
  setHistory(messages) {
338
- // Stop any ongoing streaming and reject awaiting approvals
339
- this.stopStreaming();
340
- for (const [approvalId, pending] of this._pendingApprovals) {
341
- pending.resolve(false, 'Chat history changed');
342
- this._agentEvent.emit({
343
- type: 'tool_approval_resolved',
344
- data: { approvalId, approved: false }
345
- });
346
- }
347
- this._pendingApprovals.clear();
348
- // Convert chat messages to model messages
349
- const modelMessages = messages.map(msg => {
350
- const role = msg.sender.username === 'ai-assistant' ? 'assistant' : 'user';
351
- return {
352
- role,
353
- content: msg.body
354
- };
355
- });
356
- this._history = Private.sanitizeModelMessages(modelMessages);
338
+ this.stopStreaming('Chat history changed');
339
+ this._history = Private.sanitizeModelMessages(messages);
357
340
  }
358
341
  /**
359
342
  * Stops the current streaming response by aborting the request.
@@ -362,44 +345,44 @@ export class AgentManager {
362
345
  stopStreaming(reason) {
363
346
  this._controller?.abort();
364
347
  // Reject any pending approvals
365
- for (const [approvalId, pending] of this._pendingApprovals) {
348
+ for (const [toolCallId, pending] of this._pendingApprovals) {
366
349
  pending.resolve(false, reason ?? 'Stream ended by user');
367
350
  this._agentEvent.emit({
368
351
  type: 'tool_approval_resolved',
369
- data: { approvalId, approved: false }
352
+ data: { toolCallId, approved: false }
370
353
  });
371
354
  }
372
355
  this._pendingApprovals.clear();
373
356
  }
374
357
  /**
375
358
  * Approves a pending tool call.
376
- * @param approvalId The approval ID to approve
359
+ * @param toolCallId The tool call ID to approve
377
360
  * @param reason Optional reason for approval
378
361
  */
379
- approveToolCall(approvalId, reason) {
380
- const pending = this._pendingApprovals.get(approvalId);
362
+ approveToolCall(toolCallId, reason) {
363
+ const pending = this._pendingApprovals.get(toolCallId);
381
364
  if (pending) {
382
365
  pending.resolve(true, reason);
383
- this._pendingApprovals.delete(approvalId);
366
+ this._pendingApprovals.delete(toolCallId);
384
367
  this._agentEvent.emit({
385
368
  type: 'tool_approval_resolved',
386
- data: { approvalId, approved: true }
369
+ data: { toolCallId, approved: true }
387
370
  });
388
371
  }
389
372
  }
390
373
  /**
391
374
  * Rejects a pending tool call.
392
- * @param approvalId The approval ID to reject
375
+ * @param toolCallId The tool call ID to reject
393
376
  * @param reason Optional reason for rejection
394
377
  */
395
- rejectToolCall(approvalId, reason) {
396
- const pending = this._pendingApprovals.get(approvalId);
378
+ rejectToolCall(toolCallId, reason) {
379
+ const pending = this._pendingApprovals.get(toolCallId);
397
380
  if (pending) {
398
381
  pending.resolve(false, reason);
399
- this._pendingApprovals.delete(approvalId);
382
+ this._pendingApprovals.delete(toolCallId);
400
383
  this._agentEvent.emit({
401
384
  type: 'tool_approval_resolved',
402
- data: { approvalId, approved: false }
385
+ data: { toolCallId, approved: false }
403
386
  });
404
387
  }
405
388
  }
@@ -481,19 +464,7 @@ export class AgentManager {
481
464
  error.statusCode === 413 ||
482
465
  error.statusCode === 415 ||
483
466
  error.statusCode === 422)) {
484
- for (const msg of [...this._history, ...responseHistory]) {
485
- if (msg.role === 'user' && Array.isArray(msg.content)) {
486
- const hasMedia = msg.content.some(p => p.type !== 'text');
487
- if (hasMedia) {
488
- const textContent = msg.content
489
- .filter(p => p.type === 'text')
490
- .map(p => p.text)
491
- .join('\n');
492
- msg.content =
493
- textContent || '_Attachment removed due to error_';
494
- }
495
- }
496
- }
467
+ this._stripAttachments([...this._history, ...responseHistory], '_Attachment removed due to error_');
497
468
  helpMessage +=
498
469
  '\n\nAttachments have been removed from history. Please send your prompt again.';
499
470
  }
@@ -545,6 +516,23 @@ export class AgentManager {
545
516
  this._tokenUsage.contextWindow = contextWindow;
546
517
  this._tokenUsageChanged.emit(this._tokenUsage);
547
518
  }
519
+ /**
520
+ * Removes image and file parts from all user messages in the given list.
521
+ */
522
+ _stripAttachments(messages, placeholder) {
523
+ for (const msg of messages) {
524
+ if (msg.role === 'user' && Array.isArray(msg.content)) {
525
+ const hasMedia = msg.content.some(p => p.type !== 'text');
526
+ if (hasMedia) {
527
+ const textContent = msg.content
528
+ .filter(p => p.type === 'text')
529
+ .map(p => p.text)
530
+ .join('\n');
531
+ msg.content = textContent || placeholder;
532
+ }
533
+ }
534
+ }
535
+ }
548
536
  /**
549
537
  * Gets the configured context window for the active provider.
550
538
  */
@@ -828,13 +816,12 @@ ${richOutputWorkflowInstruction}`;
828
816
  this._agentEvent.emit({
829
817
  type: 'tool_approval_request',
830
818
  data: {
831
- approvalId,
832
819
  toolCallId: toolCall.toolCallId,
833
820
  toolName: toolCall.toolName,
834
821
  args: toolCall.input
835
822
  }
836
823
  });
837
- const approved = await this._waitForApproval(approvalId);
824
+ const approved = await this._waitForApproval(toolCall.toolCallId);
838
825
  result.approvalProcessed = true;
839
826
  result.approvalResponse = {
840
827
  role: 'tool',
@@ -849,12 +836,12 @@ ${richOutputWorkflowInstruction}`;
849
836
  }
850
837
  /**
851
838
  * Waits for user approval of a tool call.
852
- * @param approvalId The approval ID to wait for
839
+ * @param toolCallId The tool call ID to wait for approval
853
840
  * @returns Promise that resolves to true if approved, false if rejected
854
841
  */
855
- _waitForApproval(approvalId) {
842
+ _waitForApproval(toolCallId) {
856
843
  return new Promise(resolve => {
857
- this._pendingApprovals.set(approvalId, {
844
+ this._pendingApprovals.set(toolCallId, {
858
845
  resolve: (approved) => {
859
846
  resolve(approved);
860
847
  }
@@ -2,14 +2,13 @@ import { ActiveCellManager } from '@jupyter/chat';
2
2
  import { IDocumentManager } from '@jupyterlab/docmanager';
3
3
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
4
4
  import { Contents } from '@jupyterlab/services';
5
- import { AIChatModel } from './chat-model';
6
- import type { IAgentManagerFactory, IAISettingsModel, IChatModelHandler, ICreateChatOptions, IProviderRegistry, IToolRegistry } from './tokens';
5
+ import type { IAgentManagerFactory, IAIChatModel, IAISettingsModel, IChatModelHandler, ICreateChatOptions, IProviderRegistry, IToolRegistry } from './tokens';
7
6
  /**
8
7
  * The chat model handler.
9
8
  */
10
9
  export declare class ChatModelHandler implements IChatModelHandler {
11
10
  constructor(options: ChatModelHandler.IOptions);
12
- createModel(options: ICreateChatOptions): AIChatModel;
11
+ createModel(options: ICreateChatOptions): IAIChatModel;
13
12
  /**
14
13
  * Getter/setter for the active cell manager.
15
14
  */
@@ -31,7 +31,8 @@ export class ChatModelHandler {
31
31
  agentManager,
32
32
  activeCellManager: this._activeCellManager,
33
33
  documentManager: this._docManager,
34
- contentsManager: this._contentsManager
34
+ contentsManager: this._contentsManager,
35
+ providerRegistry: this._providerRegistry
35
36
  });
36
37
  messages?.forEach(message => {
37
38
  model.messageAdded({ ...message.content });
@@ -1,13 +1,14 @@
1
- import { AbstractChatModel, IActiveCellManager, IAttachment, IChatContext, IMessageContent, INewMessage, IUser } from '@jupyter/chat';
1
+ import { AbstractChatModel, IActiveCellManager, IAttachment, IChatContext, IMessageContent, IMimeModelBody, INewMessage, IUser } from '@jupyter/chat';
2
2
  import { IDocumentManager } from '@jupyterlab/docmanager';
3
3
  import { Contents } from '@jupyterlab/services';
4
4
  import { ISignal } from '@lumino/signaling';
5
- import type { IAgentManager, IAISettingsModel, ITokenUsage } from './tokens';
5
+ import type { UserContent } from 'ai';
6
+ import type { IAgentManager, IAIChatModel, IAISettingsModel, IProviderRegistry, ITokenUsage } from './tokens';
6
7
  /**
7
8
  * AI Chat Model implementation that provides chat functionality tool integration,
8
9
  * and MCP server support.
9
10
  */
10
- export declare class AIChatModel extends AbstractChatModel {
11
+ export declare class AIChatModel extends AbstractChatModel implements IAIChatModel {
11
12
  /**
12
13
  * Constructs a new AIChatModel instance.
13
14
  * @param options Configuration options for the chat model
@@ -21,7 +22,7 @@ export declare class AIChatModel extends AbstractChatModel {
21
22
  /**
22
23
  * A signal emitting when the chat name has changed.
23
24
  */
24
- get nameChanged(): ISignal<AIChatModel, string>;
25
+ get nameChanged(): ISignal<IAIChatModel, string>;
25
26
  /**
26
27
  * The title of the chat.
27
28
  */
@@ -30,7 +31,7 @@ export declare class AIChatModel extends AbstractChatModel {
30
31
  /**
31
32
  * A signal emitting when the chat title has changed.
32
33
  */
33
- get titleChanged(): ISignal<AIChatModel, string | null>;
34
+ get titleChanged(): ISignal<IAIChatModel, string | null>;
34
35
  /**
35
36
  * Whether to save the chat automatically.
36
37
  */
@@ -39,7 +40,7 @@ export declare class AIChatModel extends AbstractChatModel {
39
40
  /**
40
41
  * A signal emitting when the autosave flag changed.
41
42
  */
42
- get autosaveChanged(): ISignal<AIChatModel, boolean>;
43
+ get autosaveChanged(): ISignal<IAIChatModel, boolean>;
43
44
  /**
44
45
  * Gets the current user information.
45
46
  */
@@ -49,7 +50,7 @@ export declare class AIChatModel extends AbstractChatModel {
49
50
  */
50
51
  get tokenUsageChanged(): ISignal<IAgentManager, ITokenUsage>;
51
52
  /**
52
- * Get the agent manager associated to the model.
53
+ * The agent manager used in the model.
53
54
  */
54
55
  get agentManager(): IAgentManager;
55
56
  /**
@@ -72,15 +73,40 @@ export declare class AIChatModel extends AbstractChatModel {
72
73
  * Clears all messages from the chat and resets conversation state.
73
74
  */
74
75
  clearMessages: () => Promise<void>;
76
+ /**
77
+ * Overrides messageAdded to ensure queued messages stay at the bottom.
78
+ */
79
+ messageAdded(message: IMessageContent): void;
75
80
  /**
76
81
  * Adds a non-user message to the chat (used by chat commands).
77
82
  */
78
- addSystemMessage(body: string): void;
83
+ private _addSystemMessage;
79
84
  /**
80
85
  * Sends a message to the AI and generates a response.
81
86
  * @param message The user message to send
82
87
  */
83
88
  sendMessage(message: INewMessage): Promise<void>;
89
+ /**
90
+ * Internal method to process attachments and send the message to the agent.
91
+ */
92
+ private _processMessage;
93
+ /**
94
+ * Removes the message-queue chat component.
95
+ */
96
+ private _removeQueueUI;
97
+ /**
98
+ * Creates or updates the message-queue chat component.
99
+ */
100
+ private _updateQueueUI;
101
+ /**
102
+ * Processes the next message in the queue, or marks the agent as idle.
103
+ */
104
+ private _drainQueue;
105
+ /**
106
+ * Removes a queued message by its ID.
107
+ * @param messageId The ID of the queued message to remove
108
+ */
109
+ removeQueuedMessage(messageId: string): void;
84
110
  /**
85
111
  * Save the chat as json file.
86
112
  */
@@ -108,6 +134,16 @@ export declare class AIChatModel extends AbstractChatModel {
108
134
  * Handles settings changes and updates chat configuration accordingly.
109
135
  */
110
136
  private _onSettingsChanged;
137
+ /**
138
+ * Rebuild history when the active model changes.
139
+ */
140
+ private _onModelChanged;
141
+ /**
142
+ * Rebuilds the agent history from the current messages.
143
+ * For vision-capable models, re-reads binary attachments from disk.
144
+ * For text-only models, uses message text only.
145
+ */
146
+ private _rebuildHistory;
111
147
  /**
112
148
  * Handles events emitted by the agent manager.
113
149
  * @param event The event data containing type and payload
@@ -168,19 +204,80 @@ export declare class AIChatModel extends AbstractChatModel {
168
204
  * Updates a tool call's UI with new status and optional output.
169
205
  */
170
206
  private _updateToolCallUI;
207
+ /**
208
+ * The current message queue
209
+ */
210
+ get messageQueue(): Private.IQueuedItem[];
211
+ set messageQueue(value: Private.IQueuedItem[]);
212
+ /**
213
+ * Whether the chat is busy
214
+ */
215
+ get isBusy(): boolean;
216
+ set isBusy(value: boolean);
171
217
  private _settingsModel;
172
218
  private _user;
173
219
  private _toolContexts;
174
220
  private _agentManager;
221
+ private _providerRegistry?;
222
+ private _currentModelKey;
175
223
  private _currentStreamingMessage;
176
224
  private _nameChanged;
177
225
  private _contentsManager?;
178
226
  private _autosave;
179
227
  private _autosaveChanged;
180
228
  private _autosaveDebouncer;
229
+ private _messageQueue;
230
+ private _isBusy;
231
+ private _queueMessageId;
181
232
  private _title;
182
233
  private _titleChanged;
183
234
  }
235
+ declare namespace Private {
236
+ interface IQueuedItem {
237
+ id: string;
238
+ body: string;
239
+ _originalMsg: IMessageContent;
240
+ }
241
+ /**
242
+ * Extract rendermime-ready mime bundles from arbitrary tool results.
243
+ */
244
+ function extractMimeBundlesFromUnknown(content: unknown, options?: {
245
+ trustedMimeTypes?: ReadonlyArray<string>;
246
+ }): IMimeModelBody[];
247
+ function formatToolOutput(outputData: unknown): string;
248
+ /**
249
+ * Processes file attachments and returns the message content with the attachments.
250
+ * @param attachments Array of file attachments to process
251
+ * @param documentManager Optional document manager for file operations
252
+ * @param body The message body
253
+ * @param supportsImages Whether the model supports images
254
+ * @param supportsPdf Whether the model supports pdfs
255
+ * @param supportsAudio Whether the model supports audio
256
+ * @returns Enhanced message content
257
+ */
258
+ function processAttachments(attachments: IAttachment[], documentManager: IDocumentManager | null | undefined, body: string, supportsImages: boolean, supportsPdf: boolean, supportsAudio: boolean): Promise<UserContent>;
259
+ /**
260
+ * Reads a binary attachment and returns its base64-encoded content.
261
+ * @param attachment The attachment to read
262
+ * @param documentManager Optional document manager for file operations
263
+ * @returns Base64 string or null if unable to read
264
+ */
265
+ function readBinaryAttachment(attachment: IAttachment, documentManager: IDocumentManager | null | undefined): Promise<string | null>;
266
+ /**
267
+ * Reads the content of a notebook cell.
268
+ * @param attachment The notebook attachment to read
269
+ * @param documentManager Optional document manager for file operations
270
+ * @returns Cell content as string or null if unable to read
271
+ */
272
+ function readNotebookCells(attachment: IAttachment, documentManager: IDocumentManager | null | undefined): Promise<string | null>;
273
+ /**
274
+ * Reads the content of a file attachment.
275
+ * @param attachment The file attachment to read
276
+ * @param documentManager Optional document manager for file operations
277
+ * @returns File content as string or null if unable to read
278
+ */
279
+ function readFileAttachment(attachment: IAttachment, documentManager: IDocumentManager | null | undefined): Promise<string | null>;
280
+ }
184
281
  /**
185
282
  * Namespace containing types and interfaces for AIChatModel.
186
283
  */
@@ -213,6 +310,10 @@ export declare namespace AIChatModel {
213
310
  * The contents manager.
214
311
  */
215
312
  contentsManager?: Contents.IManager;
313
+ /**
314
+ * Optional provider registry for model capability lookups.
315
+ */
316
+ providerRegistry?: IProviderRegistry;
216
317
  /**
217
318
  * Whether to restore or not the message (default to true)
218
319
  */
@@ -278,3 +379,4 @@ export declare namespace AIChatModel {
278
379
  };
279
380
  };
280
381
  }
382
+ export {};