@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.
Files changed (64) hide show
  1. package/lib/agent.d.ts +33 -115
  2. package/lib/agent.js +192 -106
  3. package/lib/chat-model-handler.d.ts +9 -11
  4. package/lib/chat-model-handler.js +9 -4
  5. package/lib/chat-model.d.ts +84 -13
  6. package/lib/chat-model.js +214 -136
  7. package/lib/completion/completion-provider.d.ts +2 -3
  8. package/lib/components/completion-status.d.ts +2 -2
  9. package/lib/components/index.d.ts +1 -1
  10. package/lib/components/index.js +1 -1
  11. package/lib/components/model-select.d.ts +3 -3
  12. package/lib/components/save-button.d.ts +31 -0
  13. package/lib/components/save-button.js +41 -0
  14. package/lib/components/tool-select.d.ts +3 -4
  15. package/lib/components/{token-usage-display.d.ts → usage-display.d.ts} +13 -14
  16. package/lib/components/usage-display.js +109 -0
  17. package/lib/diff-manager.d.ts +2 -3
  18. package/lib/index.d.ts +2 -4
  19. package/lib/index.js +186 -28
  20. package/lib/models/settings-model.d.ts +11 -53
  21. package/lib/models/settings-model.js +38 -22
  22. package/lib/providers/built-in-providers.js +22 -36
  23. package/lib/providers/generated-context-windows.d.ts +8 -0
  24. package/lib/providers/generated-context-windows.js +96 -0
  25. package/lib/providers/model-info.d.ts +3 -0
  26. package/lib/providers/model-info.js +58 -0
  27. package/lib/tokens.d.ts +361 -36
  28. package/lib/tokens.js +18 -13
  29. package/lib/tools/commands.d.ts +2 -3
  30. package/lib/widgets/ai-settings.d.ts +3 -5
  31. package/lib/widgets/ai-settings.js +12 -0
  32. package/lib/widgets/main-area-chat.d.ts +2 -3
  33. package/lib/widgets/main-area-chat.js +12 -12
  34. package/lib/widgets/provider-config-dialog.d.ts +1 -2
  35. package/lib/widgets/provider-config-dialog.js +34 -34
  36. package/package.json +17 -10
  37. package/schema/settings-model.json +18 -1
  38. package/src/agent.ts +275 -248
  39. package/src/chat-model-handler.ts +25 -21
  40. package/src/chat-model.ts +307 -196
  41. package/src/completion/completion-provider.ts +7 -4
  42. package/src/components/completion-status.tsx +3 -3
  43. package/src/components/index.ts +1 -1
  44. package/src/components/model-select.tsx +4 -3
  45. package/src/components/save-button.tsx +84 -0
  46. package/src/components/tool-select.tsx +10 -4
  47. package/src/components/usage-display.tsx +208 -0
  48. package/src/diff-manager.ts +4 -4
  49. package/src/index.ts +250 -58
  50. package/src/models/settings-model.ts +46 -88
  51. package/src/providers/built-in-providers.ts +22 -36
  52. package/src/providers/generated-context-windows.ts +102 -0
  53. package/src/providers/model-info.ts +88 -0
  54. package/src/tokens.ts +438 -58
  55. package/src/tools/commands.ts +2 -3
  56. package/src/widgets/ai-settings.tsx +69 -15
  57. package/src/widgets/main-area-chat.ts +18 -15
  58. package/src/widgets/provider-config-dialog.tsx +96 -61
  59. package/style/base.css +17 -195
  60. package/lib/approval-buttons.d.ts +0 -49
  61. package/lib/approval-buttons.js +0 -79
  62. package/lib/components/token-usage-display.js +0 -72
  63. package/src/approval-buttons.ts +0 -115
  64. package/src/components/token-usage-display.tsx +0 -138
@@ -8,9 +8,12 @@ import { NotebookPanel } from '@jupyterlab/notebook';
8
8
  import { generateText, type LanguageModel } from 'ai';
9
9
  import { ISecretsManager } from 'jupyter-secrets-manager';
10
10
 
11
- import { AISettingsModel } from '../models/settings-model';
12
11
  import { createCompletionModel } from '../providers/models';
13
- import { SECRETS_NAMESPACE, type IProviderRegistry } from '../tokens';
12
+ import {
13
+ type IAISettingsModel,
14
+ SECRETS_NAMESPACE,
15
+ type IProviderRegistry
16
+ } from '../tokens';
14
17
 
15
18
  /**
16
19
  * Configuration interface for provider-specific completion behavior
@@ -304,7 +307,7 @@ export class AICompletionProvider implements IInlineCompletionProvider {
304
307
  };
305
308
  }
306
309
 
307
- private _settingsModel: AISettingsModel;
310
+ private _settingsModel: IAISettingsModel;
308
311
  private _providerRegistry?: IProviderRegistry;
309
312
  private _model: LanguageModel | null = null;
310
313
  private _secretsManager?: ISecretsManager;
@@ -318,7 +321,7 @@ export namespace AICompletionProvider {
318
321
  /**
319
322
  * The AI settings model.
320
323
  */
321
- settingsModel: AISettingsModel;
324
+ settingsModel: IAISettingsModel;
322
325
  /**
323
326
  * The provider registry
324
327
  */
@@ -1,8 +1,8 @@
1
1
  import React, { useEffect, useState } from 'react';
2
- import { AISettingsModel } from '../models/settings-model';
3
2
  import { ReactWidget } from '@jupyterlab/ui-components';
4
3
  import type { TranslationBundle } from '@jupyterlab/translation';
5
4
  import { jupyternautIcon } from '../icons';
5
+ import type { IAISettingsModel } from '../tokens';
6
6
 
7
7
  const COMPLETION_STATUS_CLASS = 'jp-ai-completion-status';
8
8
  const COMPLETION_DISABLED_CLASS = 'jp-ai-completion-disabled';
@@ -14,7 +14,7 @@ interface ICompletionStatusProps {
14
14
  /**
15
15
  * The settings model.
16
16
  */
17
- settingsModel: AISettingsModel;
17
+ settingsModel: IAISettingsModel;
18
18
  /**
19
19
  * The application language translator.
20
20
  */
@@ -33,7 +33,7 @@ function CompletionStatus(props: ICompletionStatusProps): JSX.Element {
33
33
  * Handle changes in the settings.
34
34
  */
35
35
  useEffect(() => {
36
- const stateChanged = (model: AISettingsModel) => {
36
+ const stateChanged = (model: IAISettingsModel) => {
37
37
  if (model.config.useSameProviderForChatAndCompleter) {
38
38
  setDisabled(false);
39
39
  setTitle(
@@ -2,5 +2,5 @@ export * from './clear-button';
2
2
  export * from './completion-status';
3
3
  export * from './model-select';
4
4
  export * from './stop-button';
5
- export * from './token-usage-display';
5
+ export * from './usage-display';
6
6
  export * from './tool-select';
@@ -4,7 +4,8 @@ import CheckIcon from '@mui/icons-material/Check';
4
4
  import { Menu, MenuItem, Typography } from '@mui/material';
5
5
  import React, { useCallback, useEffect, useState } from 'react';
6
6
  import { AIChatModel } from '../chat-model';
7
- import { AISettingsModel } from '../models/settings-model';
7
+ import type { IAISettingsModel } from '../tokens';
8
+
8
9
  /**
9
10
  * Properties for the model select component.
10
11
  */
@@ -13,7 +14,7 @@ export interface IModelSelectProps
13
14
  /**
14
15
  * The settings model to get available models and current selection from.
15
16
  */
16
- settingsModel: AISettingsModel;
17
+ settingsModel: IAISettingsModel;
17
18
  /**
18
19
  * The application language translator.
19
20
  */
@@ -243,7 +244,7 @@ export function ModelSelect(props: IModelSelectProps): JSX.Element {
243
244
  * Factory function returning the toolbar item for model selection.
244
245
  */
245
246
  export function createModelSelectItem(
246
- settingsModel: AISettingsModel,
247
+ settingsModel: IAISettingsModel,
247
248
  translator: TranslationBundle
248
249
  ): InputToolbarRegistry.IToolbarItem {
249
250
  return {
@@ -0,0 +1,84 @@
1
+ import {
2
+ historyIcon,
3
+ ReactWidget,
4
+ saveIcon,
5
+ ToolbarButtonComponent
6
+ } from '@jupyterlab/ui-components';
7
+ import type { TranslationBundle } from '@jupyterlab/translation';
8
+ import React, { useEffect, useState } from 'react';
9
+
10
+ import { AIChatModel } from '../chat-model';
11
+
12
+ const COMPONENT_CLASS = 'jp-ai-SaveButton';
13
+ const AUTOSAVE_BUTTON_CLASS = 'jp-ai-AutoSaveButton';
14
+
15
+ /**
16
+ * Properties for the SaveButton component.
17
+ */
18
+ export interface ISaveButtonProps {
19
+ /**
20
+ * The chat model, used to listen for message changes for auto-save.
21
+ */
22
+ model: AIChatModel;
23
+ /**
24
+ * The application language translator.
25
+ */
26
+ translator: TranslationBundle;
27
+ }
28
+
29
+ /**
30
+ * A split button for saving the chat, with a button to toggle auto-save.
31
+ * When auto-save is active, the save button displays the JupyterLab
32
+ * toggled-on appearance (inset box-shadow all around).
33
+ */
34
+ export function SaveComponent(props: ISaveButtonProps): JSX.Element {
35
+ const { model, translator: trans } = props;
36
+
37
+ const [autosave, setAutosave] = useState(model.autosave);
38
+
39
+ /**
40
+ * Effect that update the autosave state when it is updated on the model.
41
+ */
42
+ useEffect(() => {
43
+ const updateAutosave = (_: AIChatModel, value: boolean) => {
44
+ setAutosave(value);
45
+ };
46
+
47
+ model.autosaveChanged.connect(updateAutosave);
48
+ return () => {
49
+ model.autosaveChanged.disconnect(updateAutosave);
50
+ };
51
+ }, [model]);
52
+
53
+ return (
54
+ <div className={`${COMPONENT_CLASS}${autosave ? ' lm-mod-toggled' : ''}`}>
55
+ <ToolbarButtonComponent
56
+ icon={saveIcon}
57
+ onClick={() => model.save()}
58
+ tooltip={trans.__('Save chat')}
59
+ />
60
+ <ToolbarButtonComponent
61
+ className={AUTOSAVE_BUTTON_CLASS}
62
+ icon={historyIcon}
63
+ onClick={() => (model.autosave = !model.autosave)}
64
+ tooltip={trans.__('Auto-save')}
65
+ />
66
+ </div>
67
+ );
68
+ }
69
+
70
+ /**
71
+ * A Lumino widget wrapping the SaveButton React component.
72
+ */
73
+ export class SaveComponentWidget extends ReactWidget {
74
+ constructor(options: ISaveButtonProps) {
75
+ super();
76
+ this._options = options;
77
+ }
78
+
79
+ protected render(): React.ReactElement {
80
+ return <SaveComponent {...this._options} />;
81
+ }
82
+
83
+ private _options: ISaveButtonProps;
84
+ }
@@ -10,11 +10,17 @@ import { Divider, Menu, MenuItem, Tooltip, Typography } from '@mui/material';
10
10
 
11
11
  import React, { useCallback, useEffect, useState } from 'react';
12
12
 
13
- import { INamedTool, IProviderRegistry, IToolRegistry } from '../tokens';
14
13
  import { AIChatModel } from '../chat-model';
15
- import { AISettingsModel } from '../models/settings-model';
14
+
16
15
  import { createProviderTools } from '../providers/provider-tools';
17
16
 
17
+ import type {
18
+ IAISettingsModel,
19
+ INamedTool,
20
+ IProviderRegistry,
21
+ IToolRegistry
22
+ } from '../tokens';
23
+
18
24
  const SELECT_ITEM_CLASS = 'jp-AIToolSelect-item';
19
25
 
20
26
  /**
@@ -40,7 +46,7 @@ export interface IToolSelectProps
40
46
  /**
41
47
  * The settings model to compute provider-level web tools.
42
48
  */
43
- settingsModel: AISettingsModel;
49
+ settingsModel: IAISettingsModel;
44
50
 
45
51
  /**
46
52
  * Registry for provider metadata used to resolve provider tool capabilities.
@@ -307,7 +313,7 @@ export function ToolSelect(props: IToolSelectProps): JSX.Element {
307
313
  */
308
314
  export function createToolSelectItem(
309
315
  toolRegistry: IToolRegistry,
310
- settingsModel: AISettingsModel,
316
+ settingsModel: IAISettingsModel,
311
317
  providerRegistry: IProviderRegistry,
312
318
  toolsEnabled: boolean = true,
313
319
  translator: TranslationBundle
@@ -0,0 +1,208 @@
1
+ import { ReactWidget, UseSignal } from '@jupyterlab/ui-components';
2
+ import type { TranslationBundle } from '@jupyterlab/translation';
3
+ import React from 'react';
4
+ import { ISignal } from '@lumino/signaling';
5
+ import type { IAISettingsModel, ITokenUsage } from '../tokens';
6
+
7
+ /**
8
+ * Props for the UsageDisplay component.
9
+ */
10
+ export interface IUsageDisplayProps {
11
+ /**
12
+ * The token usage changed signal
13
+ */
14
+ tokenUsageChanged: ISignal<any, ITokenUsage>;
15
+
16
+ /**
17
+ * The settings model instance for configuration options
18
+ */
19
+ settingsModel: IAISettingsModel;
20
+
21
+ /**
22
+ * Initial token usage.
23
+ */
24
+ initialTokenUsage?: ITokenUsage;
25
+
26
+ /**
27
+ * The application language translator.
28
+ */
29
+ translator: TranslationBundle;
30
+ }
31
+
32
+ /**
33
+ * React component that displays usage information.
34
+ * Shows input/output token counts and optional estimated context usage.
35
+ * Only renders when token or context usage display is enabled in settings.
36
+ */
37
+ export const UsageDisplay: React.FC<IUsageDisplayProps> = ({
38
+ tokenUsageChanged,
39
+ settingsModel,
40
+ initialTokenUsage,
41
+ translator: trans
42
+ }) => {
43
+ const formatContextPercent = (value: number): string => {
44
+ return Math.round(value).toLocaleString();
45
+ };
46
+
47
+ const badgeStyle: React.CSSProperties = {
48
+ display: 'flex',
49
+ alignItems: 'center',
50
+ gap: '6px',
51
+ fontSize: '12px',
52
+ color: 'var(--jp-ui-font-color2)',
53
+ padding: '4px 8px',
54
+ backgroundColor: 'var(--jp-layout-color1)',
55
+ border: '1px solid var(--jp-border-color1)',
56
+ borderRadius: '4px',
57
+ whiteSpace: 'nowrap'
58
+ };
59
+
60
+ return (
61
+ <UseSignal signal={settingsModel.stateChanged} initialArgs={undefined}>
62
+ {() => {
63
+ const config = settingsModel.config;
64
+ const showTokenUsage = config.showTokenUsage;
65
+ const showContextUsage = config.showContextUsage;
66
+ if (!showTokenUsage && !showContextUsage) {
67
+ return null;
68
+ }
69
+
70
+ return (
71
+ <UseSignal signal={tokenUsageChanged} initialArgs={initialTokenUsage}>
72
+ {(_, tokenUsage: ITokenUsage | null | undefined) => {
73
+ if (!tokenUsage) {
74
+ return null;
75
+ }
76
+
77
+ const total = tokenUsage.inputTokens + tokenUsage.outputTokens;
78
+ const hasKnownContextWindow =
79
+ showContextUsage && tokenUsage.contextWindow !== undefined;
80
+ const contextUsagePercent =
81
+ tokenUsage.lastRequestInputTokens !== undefined &&
82
+ tokenUsage.contextWindow !== undefined &&
83
+ tokenUsage.contextWindow > 0
84
+ ? Math.max(
85
+ 0,
86
+ Math.min(
87
+ 100,
88
+ (tokenUsage.lastRequestInputTokens /
89
+ tokenUsage.contextWindow) *
90
+ 100
91
+ )
92
+ )
93
+ : undefined;
94
+ const hasContextEstimate =
95
+ hasKnownContextWindow &&
96
+ contextUsagePercent !== undefined &&
97
+ tokenUsage.lastRequestInputTokens !== undefined;
98
+
99
+ const contextLabel = hasContextEstimate
100
+ ? `${formatContextPercent(contextUsagePercent)}%`
101
+ : hasKnownContextWindow
102
+ ? '0%'
103
+ : '?';
104
+
105
+ const contextTitle = hasContextEstimate
106
+ ? trans.__(
107
+ 'Context Usage (estimated): %1% (%2 / %3 tokens)',
108
+ formatContextPercent(contextUsagePercent),
109
+ tokenUsage.lastRequestInputTokens!.toLocaleString(),
110
+ tokenUsage.contextWindow!.toLocaleString()
111
+ )
112
+ : hasKnownContextWindow
113
+ ? trans.__(
114
+ 'Context usage estimate will appear after the next request. Showing 0% until then. Context window: %1 tokens',
115
+ tokenUsage.contextWindow!.toLocaleString()
116
+ )
117
+ : trans.__(
118
+ 'Context Usage unavailable. Configure a context window for the active provider/model to enable estimation.'
119
+ );
120
+
121
+ return (
122
+ <div
123
+ style={{
124
+ display: 'flex',
125
+ alignItems: 'center',
126
+ gap: '6px'
127
+ }}
128
+ >
129
+ {showTokenUsage && (
130
+ <span
131
+ style={badgeStyle}
132
+ title={trans.__(
133
+ 'Token Usage - Sent: %1, Received: %2, Total: %3',
134
+ tokenUsage.inputTokens.toLocaleString(),
135
+ tokenUsage.outputTokens.toLocaleString(),
136
+ total.toLocaleString()
137
+ )}
138
+ >
139
+ <span
140
+ style={{
141
+ display: 'flex',
142
+ alignItems: 'center',
143
+ gap: '2px'
144
+ }}
145
+ >
146
+ <span>↑</span>
147
+ <span>{tokenUsage.inputTokens.toLocaleString()}</span>
148
+ </span>
149
+ <span
150
+ style={{
151
+ display: 'flex',
152
+ alignItems: 'center',
153
+ gap: '2px'
154
+ }}
155
+ >
156
+ <span>↓</span>
157
+ <span>{tokenUsage.outputTokens.toLocaleString()}</span>
158
+ </span>
159
+ </span>
160
+ )}
161
+ {showContextUsage && (
162
+ <span style={badgeStyle} title={contextTitle}>
163
+ <span
164
+ style={{
165
+ display: 'flex',
166
+ alignItems: 'center',
167
+ gap: '2px'
168
+ }}
169
+ >
170
+ <span>ctx</span>
171
+ <span>{contextLabel}</span>
172
+ </span>
173
+ </span>
174
+ )}
175
+ </div>
176
+ );
177
+ }}
178
+ </UseSignal>
179
+ );
180
+ }}
181
+ </UseSignal>
182
+ );
183
+ };
184
+
185
+ /**
186
+ * JupyterLab widget wrapper for the UsageDisplay component.
187
+ * Extends ReactWidget to integrate with the JupyterLab widget system.
188
+ */
189
+ export class UsageWidget extends ReactWidget {
190
+ /**
191
+ * Creates a new UsageWidget instance.
192
+ * @param options - Configuration options containing required models
193
+ */
194
+ constructor(options: IUsageDisplayProps) {
195
+ super();
196
+ this._options = options;
197
+ }
198
+
199
+ /**
200
+ * Renders the React component within the widget.
201
+ * @returns The UsageDisplay React element
202
+ */
203
+ protected render(): React.ReactElement {
204
+ return <UsageDisplay {...this._options} />;
205
+ }
206
+
207
+ private _options: IUsageDisplayProps;
208
+ }
@@ -1,6 +1,6 @@
1
1
  import { CommandRegistry } from '@lumino/commands';
2
- import { AISettingsModel } from './models/settings-model';
3
- import {
2
+ import type {
3
+ IAISettingsModel,
4
4
  IDiffManager,
5
5
  IShowCellDiffParams,
6
6
  IShowFileDiffParams
@@ -30,7 +30,7 @@ export class DiffManager implements IDiffManager {
30
30
  */
31
31
  constructor(options: {
32
32
  commands: CommandRegistry;
33
- settingsModel: AISettingsModel;
33
+ settingsModel: IAISettingsModel;
34
34
  }) {
35
35
  this._commands = options.commands;
36
36
  this._settingsModel = options.settingsModel;
@@ -77,5 +77,5 @@ export class DiffManager implements IDiffManager {
77
77
  }
78
78
 
79
79
  private _commands: CommandRegistry;
80
- private _settingsModel: AISettingsModel;
80
+ private _settingsModel: IAISettingsModel;
81
81
  }