@jupyterlite/ai 0.16.0 → 0.17.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,5 +1,6 @@
1
1
  import type { IMessageContent } from '@jupyter/chat';
2
2
  import { ISignal } from '@lumino/signaling';
3
+ import { type ModelMessage, type UserContent } from 'ai';
3
4
  import { ISecretsManager } from 'jupyter-secrets-manager';
4
5
  import { type IAgentManager, type IAgentManagerFactory, type IAISettingsModel, type ISkillRegistry, type ITokenUsage, type ToolMap } from './tokens';
5
6
  /**
@@ -158,7 +159,12 @@ export declare class AgentManager implements IAgentManager {
158
159
  * Handles the complete execution cycle including tool calls.
159
160
  * @param message The user message to respond to (may include processed attachment content)
160
161
  */
161
- generateResponse(message: string): Promise<void>;
162
+ generateResponse(message: UserContent): Promise<void>;
163
+ /**
164
+ * Create a transient language model to request a text response which won't be added to history.
165
+ * @param messages - the messages sequence to send to the model.
166
+ */
167
+ textResponse(messages: ModelMessage[]): Promise<string>;
162
168
  /**
163
169
  * Updates cumulative token usage statistics from a completed model step.
164
170
  */
package/lib/agent.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createMCPClient } from '@ai-sdk/mcp';
2
2
  import { PromiseDelegate } from '@lumino/coreutils';
3
3
  import { Signal } from '@lumino/signaling';
4
- import { ToolLoopAgent, stepCountIs } from 'ai';
4
+ import { generateText, ToolLoopAgent, stepCountIs, APICallError } from 'ai';
5
5
  import { createModel } from './providers/models';
6
6
  import { getEffectiveContextWindow } from './providers/model-info';
7
7
  import { createProviderTools } from './providers/provider-tools';
@@ -347,9 +347,9 @@ export class AgentManager {
347
347
  this._pendingApprovals.clear();
348
348
  // Convert chat messages to model messages
349
349
  const modelMessages = messages.map(msg => {
350
- const isAIMessage = msg.sender.username === 'ai-assistant';
350
+ const role = msg.sender.username === 'ai-assistant' ? 'assistant' : 'user';
351
351
  return {
352
- role: isAIMessage ? 'assistant' : 'user',
352
+ role,
353
353
  content: msg.body
354
354
  };
355
355
  });
@@ -412,6 +412,11 @@ export class AgentManager {
412
412
  this._streaming = new PromiseDelegate();
413
413
  this._controller = new AbortController();
414
414
  const responseHistory = [];
415
+ // Add user message to history
416
+ responseHistory.push({
417
+ role: 'user',
418
+ content: message
419
+ });
415
420
  try {
416
421
  // Ensure we have an agent
417
422
  if (!this._agent) {
@@ -420,11 +425,6 @@ export class AgentManager {
420
425
  if (!this._agent) {
421
426
  throw new Error('Failed to initialize agent');
422
427
  }
423
- // Add user message to history
424
- responseHistory.push({
425
- role: 'user',
426
- content: message
427
- });
428
428
  let continueLoop = true;
429
429
  while (continueLoop) {
430
430
  const result = await this._agent.stream({
@@ -473,9 +473,38 @@ export class AgentManager {
473
473
  }
474
474
  catch (error) {
475
475
  if (error.name !== 'AbortError') {
476
+ let helpMessage = `${error.message}`;
477
+ // Remove attachments from history on payload rejection errors
478
+ if (APICallError.isInstance(error) &&
479
+ (error.statusCode === 400 ||
480
+ error.statusCode === 404 ||
481
+ error.statusCode === 413 ||
482
+ error.statusCode === 415 ||
483
+ 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
+ }
497
+ helpMessage +=
498
+ '\n\nAttachments have been removed from history. Please send your prompt again.';
499
+ }
476
500
  this._agentEvent.emit({
477
501
  type: 'error',
478
- data: { error: error }
502
+ data: { error: new Error(helpMessage) }
503
+ });
504
+ this._history.push(...Private.sanitizeModelMessages(responseHistory));
505
+ this._history.push({
506
+ role: 'assistant',
507
+ content: helpMessage
479
508
  });
480
509
  }
481
510
  }
@@ -484,6 +513,24 @@ export class AgentManager {
484
513
  this._streaming.resolve();
485
514
  }
486
515
  }
516
+ /**
517
+ * Create a transient language model to request a text response which won't be added to history.
518
+ * @param messages - the messages sequence to send to the model.
519
+ */
520
+ async textResponse(messages) {
521
+ try {
522
+ const model = await this._createModel();
523
+ const result = await generateText({
524
+ model,
525
+ messages
526
+ });
527
+ this._updateTokenUsage(result.totalUsage, result.totalUsage.inputTokens);
528
+ return result.text;
529
+ }
530
+ catch (e) {
531
+ throw `Error while getting the topic of the chat\n${e}`;
532
+ }
533
+ }
487
534
  /**
488
535
  * Updates cumulative token usage statistics from a completed model step.
489
536
  */
@@ -694,13 +741,15 @@ ${richOutputWorkflowInstruction}`;
694
741
  }
695
742
  await this._handleApprovalRequest(part, processResult);
696
743
  break;
744
+ case 'error':
745
+ throw part.error;
697
746
  case 'finish-step':
698
747
  this._updateTokenUsage(part.usage, part.usage.inputTokens);
699
748
  break;
700
749
  case 'abort':
701
750
  processResult.aborted = true;
702
751
  break;
703
- // Ignore: text-start, text-end, finish, error, and others
752
+ // Ignore: text-start, text-end, finish, and others
704
753
  default:
705
754
  break;
706
755
  }
@@ -16,7 +16,7 @@ export class ClearCommandProvider {
16
16
  return;
17
17
  }
18
18
  const context = inputModel.chatContext;
19
- context?.clearMessages?.();
19
+ await context?.clearMessages?.();
20
20
  inputModel.value = '';
21
21
  inputModel.clearAttachments();
22
22
  inputModel.clearMentions();
@@ -14,7 +14,7 @@ export class ChatModelHandler {
14
14
  this._contentsManager = options.contentsManager;
15
15
  }
16
16
  createModel(options) {
17
- const { name, activeProvider, tokenUsage, messages, autosave } = options;
17
+ const { name, activeProvider, tokenUsage, messages, autosave, title } = options;
18
18
  // Create Agent Manager first so it can be shared
19
19
  const agentManager = this._agentManagerFactory.createAgent({
20
20
  settingsModel: this._settingsModel,
@@ -38,6 +38,9 @@ export class ChatModelHandler {
38
38
  });
39
39
  model.autosave = autosave ?? false;
40
40
  model.name = name;
41
+ if (title) {
42
+ model.title = title;
43
+ }
41
44
  return model;
42
45
  }
43
46
  /**
@@ -18,6 +18,19 @@ export declare class AIChatModel extends AbstractChatModel {
18
18
  */
19
19
  get name(): string;
20
20
  set name(value: string);
21
+ /**
22
+ * A signal emitting when the chat name has changed.
23
+ */
24
+ get nameChanged(): ISignal<AIChatModel, string>;
25
+ /**
26
+ * The title of the chat.
27
+ */
28
+ get title(): string | null;
29
+ set title(value: string | null);
30
+ /**
31
+ * A signal emitting when the chat title has changed.
32
+ */
33
+ get titleChanged(): ISignal<AIChatModel, string | null>;
21
34
  /**
22
35
  * Whether to save the chat automatically.
23
36
  */
@@ -27,10 +40,6 @@ export declare class AIChatModel extends AbstractChatModel {
27
40
  * A signal emitting when the autosave flag changed.
28
41
  */
29
42
  get autosaveChanged(): ISignal<AIChatModel, boolean>;
30
- /**
31
- * A signal emitting when the chat name has changed.
32
- */
33
- get nameChanged(): ISignal<AIChatModel, string>;
34
43
  /**
35
44
  * Gets the current user information.
36
45
  */
@@ -62,7 +71,7 @@ export declare class AIChatModel extends AbstractChatModel {
62
71
  /**
63
72
  * Clears all messages from the chat and resets conversation state.
64
73
  */
65
- clearMessages: () => void;
74
+ clearMessages: () => Promise<void>;
66
75
  /**
67
76
  * Adds a non-user message to the chat (used by chat commands).
68
77
  */
@@ -83,6 +92,10 @@ export declare class AIChatModel extends AbstractChatModel {
83
92
  * restoration is not possible.
84
93
  */
85
94
  restore: (filepath: string, silent?: boolean) => Promise<boolean>;
95
+ /**
96
+ * Request a title to this chat, regarding the message history.
97
+ */
98
+ requestTitle(): Promise<string>;
86
99
  /**
87
100
  * Serialize the model for backup
88
101
  */
@@ -155,24 +168,6 @@ export declare class AIChatModel extends AbstractChatModel {
155
168
  * Updates a tool call's UI with new status and optional output.
156
169
  */
157
170
  private _updateToolCallUI;
158
- /**
159
- * Processes file attachments and returns their content as formatted strings.
160
- * @param attachments Array of file attachments to process
161
- * @returns Array of formatted attachment contents
162
- */
163
- private _processAttachments;
164
- /**
165
- * Reads the content of a notebook cell.
166
- * @param attachment The notebook attachment to read
167
- * @returns Cell content as string or null if unable to read
168
- */
169
- private _readNotebookCells;
170
- /**
171
- * Reads the content of a file attachment.
172
- * @param attachment The file attachment to read
173
- * @returns File content as string or null if unable to read
174
- */
175
- private _readFileAttachment;
176
171
  private _settingsModel;
177
172
  private _user;
178
173
  private _toolContexts;
@@ -183,6 +178,8 @@ export declare class AIChatModel extends AbstractChatModel {
183
178
  private _autosave;
184
179
  private _autosaveChanged;
185
180
  private _autosaveDebouncer;
181
+ private _title;
182
+ private _titleChanged;
186
183
  }
187
184
  /**
188
185
  * Namespace containing types and interfaces for AIChatModel.
@@ -232,7 +229,7 @@ export declare namespace AIChatModel {
232
229
  /**
233
230
  * The clear messages callback.
234
231
  */
235
- clearMessages: () => void;
232
+ clearMessages: () => Promise<void>;
236
233
  /**
237
234
  * Adds an assistant/system message to the chat.
238
235
  */
@@ -274,6 +271,10 @@ export declare namespace AIChatModel {
274
271
  * Whether the chat is automatically saved.
275
272
  */
276
273
  autosave?: boolean;
274
+ /**
275
+ * An optional title of the chat.
276
+ */
277
+ title?: string;
277
278
  };
278
279
  };
279
280
  }