@jupyterlite/ai 0.16.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.
Files changed (40) hide show
  1. package/lib/agent.d.ts +19 -10
  2. package/lib/agent.js +82 -46
  3. package/lib/chat-commands/clear.js +1 -1
  4. package/lib/chat-model-handler.d.ts +2 -3
  5. package/lib/chat-model-handler.js +6 -2
  6. package/lib/chat-model.d.ts +129 -26
  7. package/lib/chat-model.js +543 -160
  8. package/lib/components/clear-button.d.ts +1 -1
  9. package/lib/components/clear-button.js +1 -1
  10. package/lib/components/save-button.d.ts +2 -2
  11. package/lib/index.js +224 -59
  12. package/lib/models/settings-model.js +1 -0
  13. package/lib/providers/built-in-providers.js +1 -1
  14. package/lib/providers/{generated-context-windows.d.ts → generated-model-info.d.ts} +2 -2
  15. package/lib/providers/generated-model-info.js +502 -0
  16. package/lib/providers/model-info.d.ts +3 -0
  17. package/lib/providers/model-info.js +33 -0
  18. package/lib/tokens.d.ts +98 -15
  19. package/lib/tokens.js +1 -0
  20. package/lib/widgets/ai-settings.js +5 -0
  21. package/lib/widgets/main-area-chat.d.ts +3 -3
  22. package/lib/widgets/main-area-chat.js +9 -5
  23. package/package.json +3 -3
  24. package/schema/settings-model.json +6 -0
  25. package/src/agent.ts +100 -52
  26. package/src/chat-commands/clear.ts +1 -1
  27. package/src/chat-model-handler.ts +10 -3
  28. package/src/chat-model.ts +727 -210
  29. package/src/components/clear-button.tsx +3 -3
  30. package/src/components/save-button.tsx +3 -3
  31. package/src/index.ts +289 -83
  32. package/src/models/settings-model.ts +1 -0
  33. package/src/providers/built-in-providers.ts +1 -1
  34. package/src/providers/generated-model-info.ts +508 -0
  35. package/src/providers/model-info.ts +57 -0
  36. package/src/tokens.ts +100 -15
  37. package/src/widgets/ai-settings.tsx +26 -0
  38. package/src/widgets/main-area-chat.ts +14 -9
  39. package/lib/providers/generated-context-windows.js +0 -96
  40. package/src/providers/generated-context-windows.ts +0 -102
@@ -16,7 +16,7 @@ export interface IClearButtonProps
16
16
  /**
17
17
  * The function to clear messages.
18
18
  */
19
- clearMessages: () => void;
19
+ clearMessages: () => Promise<void>;
20
20
  /**
21
21
  * The application language translator.
22
22
  */
@@ -53,8 +53,8 @@ export function clearItem(
53
53
  return {
54
54
  element: (props: InputToolbarRegistry.IToolbarItemProps) => {
55
55
  const { model } = props;
56
- const clearMessages = () =>
57
- (model.chatContext as AIChatModel.IAIChatContext).clearMessages();
56
+ const clearMessages = async () =>
57
+ await (model.chatContext as AIChatModel.IAIChatContext).clearMessages();
58
58
  const clearProps: IClearButtonProps = {
59
59
  ...props,
60
60
  clearMessages,
@@ -7,7 +7,7 @@ import {
7
7
  import type { TranslationBundle } from '@jupyterlab/translation';
8
8
  import React, { useEffect, useState } from 'react';
9
9
 
10
- import { AIChatModel } from '../chat-model';
10
+ import { IAIChatModel } from '../tokens';
11
11
 
12
12
  const COMPONENT_CLASS = 'jp-ai-SaveButton';
13
13
  const AUTOSAVE_BUTTON_CLASS = 'jp-ai-AutoSaveButton';
@@ -19,7 +19,7 @@ export interface ISaveButtonProps {
19
19
  /**
20
20
  * The chat model, used to listen for message changes for auto-save.
21
21
  */
22
- model: AIChatModel;
22
+ model: IAIChatModel;
23
23
  /**
24
24
  * The application language translator.
25
25
  */
@@ -40,7 +40,7 @@ export function SaveComponent(props: ISaveButtonProps): JSX.Element {
40
40
  * Effect that update the autosave state when it is updated on the model.
41
41
  */
42
42
  useEffect(() => {
43
- const updateAutosave = (_: AIChatModel, value: boolean) => {
43
+ const updateAutosave = (_: IAIChatModel, value: boolean) => {
44
44
  setAutosave(value);
45
45
  };
46
46
 
package/src/index.ts CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  ToolbarButton
59
59
  } from '@jupyterlab/ui-components';
60
60
 
61
- import { PromiseDelegate, UUID } from '@lumino/coreutils';
61
+ import { UUID } from '@lumino/coreutils';
62
62
 
63
63
  import { DisposableSet } from '@lumino/disposable';
64
64
 
@@ -70,8 +70,6 @@ import { ISecretsManager, SecretsManager } from 'jupyter-secrets-manager';
70
70
 
71
71
  import { AgentManagerFactory } from './agent';
72
72
 
73
- import { AIChatModel } from './chat-model';
74
-
75
73
  import { RenderedMessageOutputAreaCompat } from './rendered-message-outputarea';
76
74
 
77
75
  import { ClearCommandProvider } from './chat-commands/clear';
@@ -95,7 +93,8 @@ import {
95
93
  IProviderRegistry,
96
94
  IToolRegistry,
97
95
  ISkillRegistry,
98
- SECRETS_NAMESPACE
96
+ SECRETS_NAMESPACE,
97
+ IAIChatModel
99
98
  } from './tokens';
100
99
 
101
100
  import {
@@ -357,6 +356,27 @@ const chatModelHandler: JupyterFrontEndPlugin<IChatModelHandler> = {
357
356
  }
358
357
  };
359
358
 
359
+ /**
360
+ * The active cell manager plugin, to allow copying code from chat to notebook.
361
+ */
362
+ const activeCellManager: JupyterFrontEndPlugin<void> = {
363
+ id: '@jupyterlite/ai:activeCellManager',
364
+ description: 'Add the active cell manager to the model handler',
365
+ autoStart: true,
366
+ requires: [IChatModelHandler, INotebookTracker],
367
+ activate: (
368
+ app: JupyterFrontEnd,
369
+ modelHandler: IChatModelHandler,
370
+ notebookTracker: INotebookTracker
371
+ ) => {
372
+ const activeCellManager = new ActiveCellManager({
373
+ tracker: notebookTracker,
374
+ shell: app.shell
375
+ });
376
+ modelHandler.activeCellManager = activeCellManager;
377
+ }
378
+ };
379
+
360
380
  /**
361
381
  * Initialization data for the extension.
362
382
  */
@@ -376,7 +396,6 @@ const plugin: JupyterFrontEndPlugin<IChatTracker> = {
376
396
  IThemeManager,
377
397
  ILayoutRestorer,
378
398
  ILabShell,
379
- INotebookTracker,
380
399
  ITranslator,
381
400
  IComponentsRendererFactory,
382
401
  ICommandPalette,
@@ -392,7 +411,6 @@ const plugin: JupyterFrontEndPlugin<IChatTracker> = {
392
411
  themeManager?: IThemeManager,
393
412
  restorer?: ILayoutRestorer,
394
413
  labShell?: ILabShell,
395
- notebookTracker?: INotebookTracker,
396
414
  translator?: ITranslator,
397
415
  chatComponentsFactory?: IComponentsRendererFactory,
398
416
  palette?: ICommandPalette,
@@ -416,17 +434,6 @@ const plugin: JupyterFrontEndPlugin<IChatTracker> = {
416
434
  }
417
435
  };
418
436
 
419
- // Create ActiveCellManager if notebook tracker is available, and add it to the
420
- // model registry.
421
- let activeCellManager: ActiveCellManager | undefined;
422
- if (notebookTracker) {
423
- activeCellManager = new ActiveCellManager({
424
- tracker: notebookTracker,
425
- shell: app.shell
426
- });
427
- }
428
- modelHandler.activeCellManager = activeCellManager;
429
-
430
437
  // Creating the tracker for the chat widgets
431
438
  const namespace = 'ai-chat';
432
439
  const tracker = new WidgetTracker<MainAreaChat | ChatWidget>({ namespace });
@@ -518,7 +525,7 @@ const plugin: JupyterFrontEndPlugin<IChatTracker> = {
518
525
 
519
526
  let usageWidget: UsageWidget | null = null;
520
527
  chatPanel.chatOpened.connect((_, widget) => {
521
- const model = widget.model as AIChatModel;
528
+ const model = widget.model as IAIChatModel;
522
529
 
523
530
  // Add the widget to the tracker.
524
531
  tracker.add(widget);
@@ -527,6 +534,18 @@ const plugin: JupyterFrontEndPlugin<IChatTracker> = {
527
534
  tracker.save(widget);
528
535
  }
529
536
 
537
+ function updateToolbarTitleOverlay() {
538
+ const titleNode = chatPanel.current?.toolbar.node
539
+ .getElementsByClassName('jp-chat-sidepanel-widget-title')
540
+ .item(0);
541
+ if (titleNode) {
542
+ titleNode.setAttribute('title', model.title ?? model.name);
543
+ }
544
+ }
545
+
546
+ model.titleChanged.connect(updateToolbarTitleOverlay);
547
+ updateToolbarTitleOverlay();
548
+
530
549
  // Update the tracker if the model name changed.
531
550
  model.nameChanged.connect(saveTracker);
532
551
 
@@ -565,11 +584,9 @@ const plugin: JupyterFrontEndPlugin<IChatTracker> = {
565
584
  );
566
585
 
567
586
  if (aiWriting) {
568
- widget.inputToolbarRegistry?.hide('send');
569
587
  widget.inputToolbarRegistry?.show('stop');
570
588
  } else {
571
589
  widget.inputToolbarRegistry?.hide('stop');
572
- widget.inputToolbarRegistry?.show('send');
573
590
  }
574
591
  }
575
592
 
@@ -582,6 +599,7 @@ const plugin: JupyterFrontEndPlugin<IChatTracker> = {
582
599
  });
583
600
 
584
601
  widget.disposed.connect(() => {
602
+ model.titleChanged.disconnect(updateToolbarTitleOverlay);
585
603
  model.nameChanged.disconnect(saveTracker);
586
604
  model.agentManager.activeProviderChanged.disconnect(saveTracker);
587
605
  model.writersChanged?.disconnect(writersChanged);
@@ -607,7 +625,7 @@ const plugin: JupyterFrontEndPlugin<IChatTracker> = {
607
625
  args: widget => ({
608
626
  name: widget.model.name,
609
627
  area: widget instanceof MainAreaChat ? 'main' : 'side',
610
- provider: (widget.model as AIChatModel).agentManager.activeProvider
628
+ provider: (widget.model as IAIChatModel).agentManager.activeProvider
611
629
  }),
612
630
  name: widget => {
613
631
  const area = widget instanceof MainAreaChat ? 'main' : 'side';
@@ -641,24 +659,39 @@ const plugin: JupyterFrontEndPlugin<IChatTracker> = {
641
659
  );
642
660
 
643
661
  /**
644
- * The callback to approve or reject a tool.
662
+ * The callback for grouped tool calls permission decisions.
645
663
  */
646
- function toolCallApproval(
647
- targetId: string,
648
- approvalId: string,
649
- isApproved: boolean
664
+ function toolCallPermissionDecision(
665
+ sessionId: string,
666
+ toolCallId: string,
667
+ optionId: string
650
668
  ) {
651
- const model = tracker.find(chat => chat.model.name === targetId)?.model;
669
+ const model = tracker.find(chat => chat.model.name === sessionId)
670
+ ?.model as IAIChatModel;
652
671
  if (!model) {
653
672
  return;
654
673
  }
674
+
675
+ const isApproved = optionId === 'approve';
655
676
  isApproved
656
- ? (model as AIChatModel).agentManager.approveToolCall(approvalId)
657
- : (model as AIChatModel).agentManager.rejectToolCall(approvalId);
677
+ ? model.agentManager.approveToolCall(toolCallId)
678
+ : model.agentManager.rejectToolCall(toolCallId);
658
679
  }
659
680
 
660
681
  if (chatComponentsFactory) {
661
- chatComponentsFactory.toolCallApproval = toolCallApproval;
682
+ chatComponentsFactory.toolCallPermissionDecision =
683
+ toolCallPermissionDecision;
684
+
685
+ chatComponentsFactory.removeQueuedMessage = (
686
+ targetId: string,
687
+ messageId: string
688
+ ) => {
689
+ const model = tracker.find(chat => chat.model.name === targetId)?.model;
690
+ if (!model) {
691
+ return;
692
+ }
693
+ (model as IAIChatModel).removeQueuedMessage(messageId);
694
+ };
662
695
  }
663
696
 
664
697
  return tracker;
@@ -734,12 +767,13 @@ function registerCommands(
734
767
  }
735
768
  });
736
769
 
737
- const openInMain = (model: AIChatModel) => {
770
+ const openInMain = (model: IAIChatModel): MainAreaChat => {
771
+ const inputToolbarRegistry = inputToolbarFactory.create();
738
772
  const content = new ChatWidget({
739
773
  model,
740
774
  rmRegistry,
741
775
  themeManager: themeManager ?? null,
742
- inputToolbarRegistry: inputToolbarFactory.create(),
776
+ inputToolbarRegistry,
743
777
  attachmentOpenerRegistry,
744
778
  chatCommandRegistry
745
779
  });
@@ -767,6 +801,64 @@ function registerCommands(
767
801
  model.nameChanged.disconnect(saveTracker);
768
802
  model.agentManager.activeProviderChanged.disconnect(saveTracker);
769
803
  });
804
+
805
+ return widget;
806
+ };
807
+
808
+ const focusOnChat = (
809
+ area: 'main' | 'side',
810
+ widget?: ChatWidget | MainAreaChat
811
+ ) => {
812
+ if (area === 'main' && widget) {
813
+ app.shell.activateById(widget.id);
814
+ } else {
815
+ app.shell.activateById(chatPanel.id);
816
+ }
817
+ };
818
+
819
+ const applyInputArgs = (model: IChatModel, args: any) => {
820
+ const input = typeof args.input === 'string' ? args.input : undefined;
821
+ const autoSend = args.autoSend === true;
822
+ const shouldFocus = args.focus !== false;
823
+
824
+ if (input !== undefined) {
825
+ model.input.value = input;
826
+ }
827
+ if (autoSend && input !== undefined) {
828
+ model.input.send(model.input.value);
829
+ }
830
+ if (shouldFocus) {
831
+ model.input.focus();
832
+ }
833
+ };
834
+
835
+ const findChatWidget = (
836
+ name?: string,
837
+ provider?: string
838
+ ): ChatWidget | MainAreaChat | undefined => {
839
+ if (!name && !provider) {
840
+ return;
841
+ }
842
+ return tracker.find(widget => {
843
+ const model = widget.model as IAIChatModel;
844
+ return (
845
+ (!name || widget.model.name === name) &&
846
+ (!provider || model.agentManager.activeProvider === provider)
847
+ );
848
+ });
849
+ };
850
+
851
+ const disposeSideChatModel = (model: IChatModel): boolean => {
852
+ const loadedName = chatPanel
853
+ .getLoadedModelNames()
854
+ .find(name => chatPanel.getLoadedModel(name) === model);
855
+
856
+ if (!loadedName) {
857
+ return false;
858
+ }
859
+
860
+ chatPanel.disposeLoadedModel(loadedName);
861
+ return true;
770
862
  };
771
863
 
772
864
  commands.addCommand(CommandIds.openChat, {
@@ -801,11 +893,18 @@ function registerCommands(
801
893
  return false;
802
894
  }
803
895
 
896
+ const shouldFocus = args.focus === true;
897
+ let widget: ChatWidget | MainAreaChat | undefined;
804
898
  if (area === 'main') {
805
- openInMain(model);
899
+ widget = openInMain(model);
806
900
  } else {
807
- chatPanel.open({ model });
901
+ widget = chatPanel.open({ model });
902
+ }
903
+ if (shouldFocus) {
904
+ focusOnChat(area, widget);
808
905
  }
906
+ applyInputArgs(model, { ...args, focus: shouldFocus });
907
+
809
908
  return true;
810
909
  },
811
910
  describedBy: {
@@ -824,6 +923,137 @@ function registerCommands(
824
923
  provider: {
825
924
  type: 'string',
826
925
  description: trans.__('The provider/model to use with this chat')
926
+ },
927
+ input: {
928
+ type: 'string',
929
+ description: trans.__('The input text to prefill in the chat')
930
+ },
931
+ focus: {
932
+ type: 'boolean',
933
+ description: trans.__(
934
+ 'Whether to focus the chat input after opening it'
935
+ )
936
+ },
937
+ autoSend: {
938
+ type: 'boolean',
939
+ description: trans.__(
940
+ 'Whether to auto-send the provided input after opening the chat'
941
+ )
942
+ }
943
+ }
944
+ }
945
+ }
946
+ });
947
+
948
+ commands.addCommand(CommandIds.openOrRevealChat, {
949
+ label: trans.__('Open or reveal the chat panel'),
950
+ execute: async (args): Promise<boolean> => {
951
+ const area = (args.area as string) === 'main' ? 'main' : 'side';
952
+ const provider = (args.provider as string) ?? undefined;
953
+ const name = (args.name as string) ?? undefined;
954
+ const shouldFocus = args.focus === true;
955
+
956
+ let existingWidget = findChatWidget(name, provider);
957
+ if (!existingWidget && !name) {
958
+ const providerConfig = provider
959
+ ? settingsModel.getProvider(provider)
960
+ : settingsModel.getDefaultProvider();
961
+ existingWidget = findChatWidget(undefined, providerConfig?.id);
962
+ }
963
+
964
+ // If the side chat model is loaded but not currently displayed, reveal it first.
965
+ if (!existingWidget && name) {
966
+ const loadedModel = chatPanel.getLoadedModel(name);
967
+ if (loadedModel) {
968
+ existingWidget = chatPanel.open({ model: loadedModel });
969
+ }
970
+ }
971
+
972
+ if (!existingWidget) {
973
+ return commands.execute(CommandIds.openChat, {
974
+ ...args,
975
+ focus: shouldFocus
976
+ }) as Promise<boolean>;
977
+ }
978
+
979
+ const currentArea =
980
+ existingWidget instanceof MainAreaChat ? 'main' : 'side';
981
+ if (currentArea !== area) {
982
+ const targetName = existingWidget.model.name;
983
+ const moved = (await commands.execute(CommandIds.moveChat, {
984
+ name: targetName,
985
+ area
986
+ })) as boolean;
987
+ if (!moved) {
988
+ return false;
989
+ }
990
+
991
+ const movedWidget = findChatWidget(targetName);
992
+ if (!movedWidget) {
993
+ return false;
994
+ }
995
+
996
+ if (area === 'side') {
997
+ chatPanel.open({ model: movedWidget.model });
998
+ }
999
+ if (shouldFocus) {
1000
+ focusOnChat(area, movedWidget);
1001
+ }
1002
+ applyInputArgs(movedWidget.model, {
1003
+ ...args,
1004
+ focus: shouldFocus
1005
+ });
1006
+
1007
+ return true;
1008
+ }
1009
+
1010
+ if (area === 'side') {
1011
+ chatPanel.open({ model: existingWidget.model });
1012
+ }
1013
+ if (shouldFocus) {
1014
+ focusOnChat(area, existingWidget);
1015
+ }
1016
+ applyInputArgs(existingWidget.model, {
1017
+ ...args,
1018
+ focus: shouldFocus
1019
+ });
1020
+
1021
+ return true;
1022
+ },
1023
+ describedBy: {
1024
+ args: {
1025
+ type: 'object',
1026
+ properties: {
1027
+ area: {
1028
+ type: 'string',
1029
+ enum: ['main', 'side'],
1030
+ description: trans.__(
1031
+ 'The name of the area to open or reveal the chat in'
1032
+ )
1033
+ },
1034
+ name: {
1035
+ type: 'string',
1036
+ description: trans.__('The name of the chat')
1037
+ },
1038
+ provider: {
1039
+ type: 'string',
1040
+ description: trans.__('The provider/model to use with this chat')
1041
+ },
1042
+ input: {
1043
+ type: 'string',
1044
+ description: trans.__('The input text to prefill in the chat')
1045
+ },
1046
+ focus: {
1047
+ type: 'boolean',
1048
+ description: trans.__(
1049
+ 'Whether to focus the chat input after opening it'
1050
+ )
1051
+ },
1052
+ autoSend: {
1053
+ type: 'boolean',
1054
+ description: trans.__(
1055
+ 'Whether to auto-send the provided input after opening the chat'
1056
+ )
827
1057
  }
828
1058
  }
829
1059
  }
@@ -847,11 +1077,11 @@ function registerCommands(
847
1077
  return false;
848
1078
  }
849
1079
  let previousWidget: ChatWidget | MainAreaChat | undefined;
850
- let previousModel: AIChatModel | undefined;
1080
+ let previousModel: IAIChatModel | undefined;
851
1081
  tracker.forEach(widget => {
852
1082
  if (widget.model.name === args.name) {
853
1083
  previousWidget = widget;
854
- previousModel = widget.model as AIChatModel;
1084
+ previousModel = widget.model as IAIChatModel;
855
1085
  }
856
1086
  });
857
1087
 
@@ -862,51 +1092,26 @@ function registerCommands(
862
1092
  return false;
863
1093
  }
864
1094
 
865
- // Listen for the widget updated in tracker, to ensure the previous model name
866
- // has been updated. This is required to remove the widget from the restorer
867
- // when the previous widget is disposed.
868
- const trackerUpdated = new PromiseDelegate<boolean>();
869
- const widgetUpdated = (_: any, widget: ChatWidget | MainAreaChat) => {
870
- if (widget.model === previousModel) {
871
- trackerUpdated.resolve(true);
1095
+ if (area === 'main') {
1096
+ // Temporarily bypass model disposal to transport model to main view
1097
+ // to keep the conversation when switching views
1098
+ // TODO: Remove this code when jupyter-chat PR #423 is merged and released
1099
+ const originalDispose = previousModel.dispose.bind(previousModel);
1100
+ previousModel.dispose = () => {};
1101
+
1102
+ if (previousWidget instanceof ChatWidget) {
1103
+ if (!disposeSideChatModel(previousModel)) {
1104
+ previousWidget.dispose();
1105
+ }
872
1106
  }
873
- };
874
- tracker.widgetUpdated.connect(widgetUpdated);
875
1107
 
876
- // Rename temporary the previous model to be able to reuse this name for the new
877
- // model. The previous is intended to be disposed anyway.
878
- previousModel.name = UUID.uuid4();
879
-
880
- // Create a new model by duplicating the previous model attributes.
881
- const model = modelRegistry.createModel({
882
- name: args.name as string,
883
- activeProvider: previousModel.agentManager.activeProvider,
884
- tokenUsage: previousModel.agentManager.tokenUsage,
885
- messages: previousModel.messages,
886
- autosave: previousModel.autosave
887
- });
888
-
889
- // Wait (with timeout) for the tracker to have updated the previous widget.
890
- const status = await Promise.any([
891
- trackerUpdated.promise,
892
- new Promise<boolean>(r =>
893
- setTimeout(() => {
894
- r(false);
895
- }, 2000)
896
- )
897
- ]);
898
- tracker.widgetUpdated.disconnect(widgetUpdated);
899
-
900
- if (!status) {
901
- return false;
902
- }
903
-
904
- if (area === 'main') {
905
- openInMain(model);
1108
+ // Restore model disposal and transport to main view
1109
+ previousModel.dispose = originalDispose;
1110
+ openInMain(previousModel);
906
1111
  } else {
1112
+ // MainAreaChat disposal does not dispose the model internally, so this is safe.
907
1113
  previousWidget?.dispose();
908
- previousModel.dispose();
909
- chatPanel.open({ model });
1114
+ chatPanel.open({ model: previousModel });
910
1115
  }
911
1116
 
912
1117
  return true;
@@ -935,15 +1140,15 @@ function registerCommands(
935
1140
  caption: trans.__('Save the chat as local file'),
936
1141
  icon: saveIcon,
937
1142
  execute: async (args): Promise<boolean> => {
938
- let model: AIChatModel | null = null;
1143
+ let model: IAIChatModel | null = null;
939
1144
  if (args.name) {
940
1145
  tracker.forEach(widget => {
941
1146
  if (widget.model.name === args.name) {
942
- model = widget.model as AIChatModel;
1147
+ model = widget.model as IAIChatModel;
943
1148
  }
944
1149
  });
945
1150
  } else {
946
- model = (tracker.currentWidget?.model as AIChatModel) ?? null;
1151
+ model = (tracker.currentWidget?.model as IAIChatModel) ?? null;
947
1152
  }
948
1153
  if (model === null) {
949
1154
  console.log('No chat to save');
@@ -980,15 +1185,15 @@ function registerCommands(
980
1185
  console.warn('The restoration is not possible');
981
1186
  return false;
982
1187
  }
983
- let model: AIChatModel | null = null;
1188
+ let model: IAIChatModel | null = null;
984
1189
  if (args.name) {
985
1190
  tracker.forEach(widget => {
986
1191
  if (widget.model.name === args.name) {
987
- model = widget.model as AIChatModel;
1192
+ model = widget.model as IAIChatModel;
988
1193
  }
989
1194
  });
990
1195
  } else {
991
- model = (tracker.currentWidget?.model as AIChatModel) ?? null;
1196
+ model = (tracker.currentWidget?.model as IAIChatModel) ?? null;
992
1197
  }
993
1198
  if (model === null) {
994
1199
  console.warn('There is no chat to restore');
@@ -1512,6 +1717,7 @@ export default [
1512
1717
  skillRegistryPlugin,
1513
1718
  skillsCommandPlugin,
1514
1719
  chatModelHandler,
1720
+ activeCellManager,
1515
1721
  plugin,
1516
1722
  toolRegistry,
1517
1723
  agentManagerFactory,
@@ -29,6 +29,7 @@ export class AISettingsModel extends VDomModel implements IAISettingsModel {
29
29
  diffDisplayMode: 'split',
30
30
  skillsPaths: ['.agents/skills', '_agents/skills'],
31
31
  chatBackupDirectory: '',
32
+ autoTitle: false,
32
33
  commandsRequiringApproval: [
33
34
  'notebook:restart-run-all',
34
35
  'notebook:run-cell',
@@ -4,7 +4,7 @@ import { createMistral } from '@ai-sdk/mistral';
4
4
  import { createOpenAI } from '@ai-sdk/openai';
5
5
  import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
6
6
 
7
- import { BUILT_IN_PROVIDER_MODEL_INFO } from './generated-context-windows';
7
+ import { BUILT_IN_PROVIDER_MODEL_INFO } from './generated-model-info';
8
8
  import type { IProviderInfo } from '../tokens';
9
9
  import type { IModelOptions } from './models';
10
10