@jupyterlite/ai 0.15.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 +12 -2
- package/lib/agent.js +112 -17
- package/lib/chat-commands/clear.js +1 -1
- package/lib/chat-model-handler.js +4 -1
- package/lib/chat-model.d.ts +25 -24
- package/lib/chat-model.js +262 -132
- package/lib/components/clear-button.d.ts +1 -1
- package/lib/components/clear-button.js +1 -1
- package/lib/components/index.d.ts +1 -1
- package/lib/components/index.js +1 -1
- package/lib/components/{token-usage-display.d.ts → usage-display.d.ts} +11 -11
- package/lib/components/usage-display.js +109 -0
- package/lib/index.js +205 -20
- package/lib/models/settings-model.js +1 -0
- package/lib/providers/built-in-providers.js +5 -0
- 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 +34 -3
- package/lib/tokens.js +8 -7
- package/lib/widgets/ai-settings.js +9 -0
- package/lib/widgets/main-area-chat.d.ts +1 -0
- package/lib/widgets/main-area-chat.js +10 -4
- package/lib/widgets/provider-config-dialog.js +18 -5
- package/package.json +3 -2
- package/schema/settings-model.json +11 -0
- package/src/agent.ts +151 -21
- package/src/chat-commands/clear.ts +1 -1
- package/src/chat-model-handler.ts +6 -1
- package/src/chat-model.ts +350 -175
- package/src/components/clear-button.tsx +3 -3
- package/src/components/index.ts +1 -1
- package/src/components/usage-display.tsx +208 -0
- package/src/index.ts +250 -26
- package/src/models/settings-model.ts +1 -0
- package/src/providers/built-in-providers.ts +5 -0
- package/src/providers/generated-context-windows.ts +102 -0
- package/src/providers/model-info.ts +88 -0
- package/src/tokens.ts +46 -10
- package/src/widgets/ai-settings.tsx +42 -0
- package/src/widgets/main-area-chat.ts +12 -4
- package/src/widgets/provider-config-dialog.tsx +45 -5
- package/lib/components/token-usage-display.js +0 -72
- package/src/components/token-usage-display.tsx +0 -137
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is generated by `jlpm sync:model-context-windows`.
|
|
3
|
+
* Source: https://models.dev/api.json
|
|
4
|
+
* Backed by: https://github.com/anomalyco/models.dev
|
|
5
|
+
* Generated: 2026-04-08T16:23:34.080Z
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { IProviderModelInfo } from '../tokens';
|
|
9
|
+
|
|
10
|
+
export const BUILT_IN_PROVIDER_MODEL_INFO: Record<
|
|
11
|
+
string,
|
|
12
|
+
Record<string, IProviderModelInfo>
|
|
13
|
+
> = {
|
|
14
|
+
anthropic: {
|
|
15
|
+
'claude-opus-4-6': { contextWindow: 1000000 },
|
|
16
|
+
'claude-sonnet-4-6': { contextWindow: 1000000 },
|
|
17
|
+
'claude-opus-4-5': { contextWindow: 200000 },
|
|
18
|
+
'claude-opus-4-5-20251101': { contextWindow: 200000 },
|
|
19
|
+
'claude-sonnet-4-5': { contextWindow: 200000 },
|
|
20
|
+
'claude-sonnet-4-5-20250929': { contextWindow: 200000 },
|
|
21
|
+
'claude-haiku-4-5': { contextWindow: 200000 },
|
|
22
|
+
'claude-haiku-4-5-20251001': { contextWindow: 200000 },
|
|
23
|
+
'claude-opus-4-1': { contextWindow: 200000 },
|
|
24
|
+
'claude-opus-4-1-20250805': { contextWindow: 200000 },
|
|
25
|
+
'claude-opus-4-0': { contextWindow: 200000 },
|
|
26
|
+
'claude-opus-4-20250514': { contextWindow: 200000 },
|
|
27
|
+
'claude-sonnet-4-0': { contextWindow: 200000 },
|
|
28
|
+
'claude-sonnet-4-20250514': { contextWindow: 200000 }
|
|
29
|
+
},
|
|
30
|
+
google: {
|
|
31
|
+
'gemini-3.1-pro-preview': { contextWindow: 1048576 },
|
|
32
|
+
'gemini-3.1-pro-preview-customtools': { contextWindow: 1048576 },
|
|
33
|
+
'gemini-3.1-flash-image-preview': { contextWindow: 131072 },
|
|
34
|
+
'gemini-3.1-flash-lite-preview': { contextWindow: 1048576 },
|
|
35
|
+
'gemini-3-flash-preview': { contextWindow: 1048576 },
|
|
36
|
+
'gemini-2.5-pro': { contextWindow: 1048576 },
|
|
37
|
+
'gemini-2.5-flash': { contextWindow: 1048576 },
|
|
38
|
+
'gemini-2.5-flash-image': { contextWindow: 32768 },
|
|
39
|
+
'gemini-2.5-flash-lite': { contextWindow: 1048576 },
|
|
40
|
+
'gemini-flash-latest': { contextWindow: 1048576 },
|
|
41
|
+
'gemini-flash-lite-latest': { contextWindow: 1048576 }
|
|
42
|
+
},
|
|
43
|
+
mistral: {
|
|
44
|
+
'mistral-large-latest': { contextWindow: 262144 },
|
|
45
|
+
'mistral-medium-latest': { contextWindow: 128000 },
|
|
46
|
+
'mistral-medium-2508': { contextWindow: 262144 },
|
|
47
|
+
'mistral-small-latest': { contextWindow: 256000 },
|
|
48
|
+
'mistral-small-2506': { contextWindow: 128000 },
|
|
49
|
+
'ministral-3b-latest': { contextWindow: 128000 },
|
|
50
|
+
'ministral-8b-latest': { contextWindow: 128000 },
|
|
51
|
+
'magistral-small-latest': { contextWindow: 128000 },
|
|
52
|
+
'magistral-medium-latest': { contextWindow: 128000 },
|
|
53
|
+
'pixtral-large-latest': { contextWindow: 128000 },
|
|
54
|
+
'codestral-latest': { contextWindow: 256000 },
|
|
55
|
+
'devstral-latest': { contextWindow: 262144 },
|
|
56
|
+
'devstral-2512': { contextWindow: 262144 }
|
|
57
|
+
},
|
|
58
|
+
openai: {
|
|
59
|
+
'gpt-5.4': { contextWindow: 1050000 },
|
|
60
|
+
'gpt-5.4-mini': { contextWindow: 400000 },
|
|
61
|
+
'gpt-5.4-nano': { contextWindow: 400000 },
|
|
62
|
+
'gpt-5.2': { contextWindow: 400000 },
|
|
63
|
+
'gpt-5.2-2025-12-11': { contextWindow: 400000 },
|
|
64
|
+
'gpt-5.2-chat-latest': { contextWindow: 128000 },
|
|
65
|
+
'gpt-5.2-pro': { contextWindow: 400000 },
|
|
66
|
+
'gpt-5.2-pro-2025-12-11': { contextWindow: 400000 },
|
|
67
|
+
'gpt-5.2-codex': { contextWindow: 400000 },
|
|
68
|
+
'gpt-5.1': { contextWindow: 400000 },
|
|
69
|
+
'gpt-5.1-2025-11-13': { contextWindow: 400000 },
|
|
70
|
+
'gpt-5.1-chat-latest': { contextWindow: 128000 },
|
|
71
|
+
'gpt-5': { contextWindow: 400000 },
|
|
72
|
+
'gpt-5-2025-08-07': { contextWindow: 400000 },
|
|
73
|
+
'gpt-5-chat-latest': { contextWindow: 400000 },
|
|
74
|
+
'gpt-5-mini': { contextWindow: 400000 },
|
|
75
|
+
'gpt-5-mini-2025-08-07': { contextWindow: 400000 },
|
|
76
|
+
'gpt-5-nano': { contextWindow: 400000 },
|
|
77
|
+
'gpt-5-nano-2025-08-07': { contextWindow: 400000 },
|
|
78
|
+
'o4-mini': { contextWindow: 200000 },
|
|
79
|
+
'o4-mini-2025-04-16': { contextWindow: 200000 },
|
|
80
|
+
'o3-pro': { contextWindow: 200000 },
|
|
81
|
+
o3: { contextWindow: 200000 },
|
|
82
|
+
'o3-2025-04-16': { contextWindow: 200000 },
|
|
83
|
+
'o3-mini': { contextWindow: 200000 },
|
|
84
|
+
'o3-mini-2025-01-31': { contextWindow: 200000 },
|
|
85
|
+
o1: { contextWindow: 200000 },
|
|
86
|
+
'o1-2024-12-17': { contextWindow: 200000 },
|
|
87
|
+
'gpt-4.1': { contextWindow: 1047576 },
|
|
88
|
+
'gpt-4.1-2025-04-14': { contextWindow: 1047576 },
|
|
89
|
+
'gpt-4.1-mini': { contextWindow: 1047576 },
|
|
90
|
+
'gpt-4.1-mini-2025-04-14': { contextWindow: 1047576 },
|
|
91
|
+
'gpt-4.1-nano': { contextWindow: 1047576 },
|
|
92
|
+
'gpt-4.1-nano-2025-04-14': { contextWindow: 1047576 },
|
|
93
|
+
'gpt-4o': { contextWindow: 128000 },
|
|
94
|
+
'gpt-4o-2024-05-13': { contextWindow: 128000 },
|
|
95
|
+
'gpt-4o-2024-08-06': { contextWindow: 128000 },
|
|
96
|
+
'gpt-4o-2024-11-20': { contextWindow: 128000 },
|
|
97
|
+
'gpt-4o-mini': { contextWindow: 128000 },
|
|
98
|
+
'gpt-4o-mini-2024-07-18': { contextWindow: 128000 },
|
|
99
|
+
'gpt-3.5-turbo': { contextWindow: 16385 },
|
|
100
|
+
'gpt-3.5-turbo-0125': { contextWindow: 16385 }
|
|
101
|
+
}
|
|
102
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IProviderConfig,
|
|
3
|
+
IProviderInfo,
|
|
4
|
+
IProviderModelInfo,
|
|
5
|
+
IProviderRegistry
|
|
6
|
+
} from '../tokens';
|
|
7
|
+
|
|
8
|
+
const DATE_SUFFIX = /^(.*)-\d{4}-\d{2}-\d{2}$/;
|
|
9
|
+
const SHORT_VERSION_SUFFIX = /^(.*)-\d{4}$/;
|
|
10
|
+
|
|
11
|
+
// Treat rolling aliases and dated releases as the same model family so they
|
|
12
|
+
// can share provider metadata such as context windows.
|
|
13
|
+
function normalizeModelId(modelId: string): string {
|
|
14
|
+
if (modelId.endsWith('-latest')) {
|
|
15
|
+
return modelId.slice(0, -7);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const dateSuffixMatch = modelId.match(DATE_SUFFIX);
|
|
19
|
+
if (dateSuffixMatch) {
|
|
20
|
+
return dateSuffixMatch[1];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const shortVersionSuffixMatch = modelId.match(SHORT_VERSION_SUFFIX);
|
|
24
|
+
if (shortVersionSuffixMatch) {
|
|
25
|
+
return shortVersionSuffixMatch[1];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return modelId;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getCandidateModelIds(modelId: string): string[] {
|
|
32
|
+
const candidates = [modelId];
|
|
33
|
+
const normalizedModelId = normalizeModelId(modelId);
|
|
34
|
+
|
|
35
|
+
candidates.push(normalizedModelId);
|
|
36
|
+
|
|
37
|
+
if (normalizedModelId !== modelId) {
|
|
38
|
+
candidates.push(`${normalizedModelId}-latest`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return [...new Set(candidates)];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getProviderModelInfo(
|
|
45
|
+
providerInfo: IProviderInfo | null | undefined,
|
|
46
|
+
model: string | undefined
|
|
47
|
+
): IProviderModelInfo | undefined {
|
|
48
|
+
if (!providerInfo || !model) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const modelInfo = providerInfo.modelInfo;
|
|
53
|
+
if (!modelInfo) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (const candidateId of getCandidateModelIds(model)) {
|
|
58
|
+
if (modelInfo[candidateId]) {
|
|
59
|
+
return modelInfo[candidateId];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const normalizedModelId = normalizeModelId(model);
|
|
64
|
+
// As a last resort, match any known model entry that normalizes to the same
|
|
65
|
+
// base ID, even if the exact alias/version string differs.
|
|
66
|
+
return Object.entries(modelInfo).find(([candidateId]) => {
|
|
67
|
+
return normalizeModelId(candidateId) === normalizedModelId;
|
|
68
|
+
})?.[1];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function getEffectiveContextWindow(
|
|
72
|
+
providerConfig: IProviderConfig | undefined,
|
|
73
|
+
providerRegistry?: IProviderRegistry
|
|
74
|
+
): number | undefined {
|
|
75
|
+
if (!providerConfig) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (providerConfig.parameters?.contextWindow !== undefined) {
|
|
80
|
+
return providerConfig.parameters.contextWindow;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const providerInfo = providerRegistry?.getProviderInfo(
|
|
84
|
+
providerConfig.provider
|
|
85
|
+
);
|
|
86
|
+
return getProviderModelInfo(providerInfo, providerConfig.model)
|
|
87
|
+
?.contextWindow;
|
|
88
|
+
}
|
package/src/tokens.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
|
4
4
|
import { Token } from '@lumino/coreutils';
|
|
5
5
|
import type { IDisposable } from '@lumino/disposable';
|
|
6
6
|
import { ISignal } from '@lumino/signaling';
|
|
7
|
-
import type { Tool, LanguageModel } from 'ai';
|
|
7
|
+
import type { Tool, LanguageModel, UserContent, ModelMessage } from 'ai';
|
|
8
8
|
import { ISecretsManager } from 'jupyter-secrets-manager';
|
|
9
9
|
|
|
10
10
|
import type { IModelOptions } from './providers/models';
|
|
@@ -30,6 +30,7 @@ export namespace CommandIds {
|
|
|
30
30
|
export const openSettings = '@jupyterlite/ai:open-settings';
|
|
31
31
|
export const reposition = '@jupyterlite/ai:reposition';
|
|
32
32
|
export const openChat = '@jupyterlite/ai:open-chat';
|
|
33
|
+
export const openOrRevealChat = '@jupyterlite/ai:open-or-reveal-chat';
|
|
33
34
|
export const moveChat = '@jupyterlite/ai:move-chat';
|
|
34
35
|
export const refreshSkills = '@jupyterlite/ai:refresh-skills';
|
|
35
36
|
export const saveChat = '@jupyterlite/ai:save-chat';
|
|
@@ -102,7 +103,7 @@ export interface IToolRegistry {
|
|
|
102
103
|
* The tool registry token.
|
|
103
104
|
*/
|
|
104
105
|
export const IToolRegistry = new Token<IToolRegistry>(
|
|
105
|
-
'@jupyterlite/ai:
|
|
106
|
+
'@jupyterlite/ai:IToolRegistry',
|
|
106
107
|
'Tool registry for AI agent functionality'
|
|
107
108
|
);
|
|
108
109
|
|
|
@@ -145,7 +146,7 @@ export interface ISkillRegistry {
|
|
|
145
146
|
* The skill registry token.
|
|
146
147
|
*/
|
|
147
148
|
export const ISkillRegistry = new Token<ISkillRegistry>(
|
|
148
|
-
'@jupyterlite/ai:
|
|
149
|
+
'@jupyterlite/ai:ISkillRegistry',
|
|
149
150
|
'Skill registry for AI agent functionality'
|
|
150
151
|
);
|
|
151
152
|
|
|
@@ -204,6 +205,13 @@ export interface IProviderToolCapabilities {
|
|
|
204
205
|
/**
|
|
205
206
|
* Provider information
|
|
206
207
|
*/
|
|
208
|
+
export interface IProviderModelInfo {
|
|
209
|
+
/**
|
|
210
|
+
* Default context window for the model in tokens.
|
|
211
|
+
*/
|
|
212
|
+
contextWindow?: number;
|
|
213
|
+
}
|
|
214
|
+
|
|
207
215
|
export interface IProviderInfo {
|
|
208
216
|
/**
|
|
209
217
|
* Unique identifier for the provider
|
|
@@ -228,6 +236,11 @@ export interface IProviderInfo {
|
|
|
228
236
|
*/
|
|
229
237
|
defaultModels: string[];
|
|
230
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Optional per-model metadata keyed by model ID.
|
|
241
|
+
*/
|
|
242
|
+
modelInfo?: Record<string, IProviderModelInfo>;
|
|
243
|
+
|
|
231
244
|
/**
|
|
232
245
|
* Whether this provider supports custom base URLs
|
|
233
246
|
*/
|
|
@@ -311,7 +324,7 @@ export interface IProviderRegistry {
|
|
|
311
324
|
* Token for the provider registry.
|
|
312
325
|
*/
|
|
313
326
|
export const IProviderRegistry = new Token<IProviderRegistry>(
|
|
314
|
-
'@jupyterlite/ai:
|
|
327
|
+
'@jupyterlite/ai:IProviderRegistry',
|
|
315
328
|
'Registry for AI providers'
|
|
316
329
|
);
|
|
317
330
|
|
|
@@ -321,6 +334,7 @@ export interface IProviderParameters {
|
|
|
321
334
|
temperature?: number;
|
|
322
335
|
maxOutputTokens?: number;
|
|
323
336
|
maxTurns?: number;
|
|
337
|
+
contextWindow?: number;
|
|
324
338
|
supportsFillInMiddle?: boolean;
|
|
325
339
|
useFilterText?: boolean;
|
|
326
340
|
}
|
|
@@ -368,6 +382,8 @@ export interface IAIConfig {
|
|
|
368
382
|
sendWithShiftEnter: boolean;
|
|
369
383
|
// Token usage display setting
|
|
370
384
|
showTokenUsage: boolean;
|
|
385
|
+
// Context usage display setting
|
|
386
|
+
showContextUsage: boolean;
|
|
371
387
|
// Commands that require approval before execution
|
|
372
388
|
commandsRequiringApproval: string[];
|
|
373
389
|
// Commands whose execute_command outputs may auto-render MIME bundles in chat
|
|
@@ -569,7 +585,7 @@ export interface IAgentManager {
|
|
|
569
585
|
/**
|
|
570
586
|
* Clears conversation history and resets agent state.
|
|
571
587
|
*/
|
|
572
|
-
clearHistory(): void
|
|
588
|
+
clearHistory(): Promise<void>;
|
|
573
589
|
/**
|
|
574
590
|
* Sets the conversation history with a list of messages from the chat.
|
|
575
591
|
* @param messages The chat messages to set as history
|
|
@@ -596,7 +612,12 @@ export interface IAgentManager {
|
|
|
596
612
|
* Handles the complete execution cycle including tool calls.
|
|
597
613
|
* @param message The user message to respond to (may include processed attachment content)
|
|
598
614
|
*/
|
|
599
|
-
generateResponse(message:
|
|
615
|
+
generateResponse(message: UserContent): Promise<void>;
|
|
616
|
+
/**
|
|
617
|
+
* Create a transient language model to request a text response, which won't be added to history.
|
|
618
|
+
* @param messages - the messages sequence to send to the model.
|
|
619
|
+
*/
|
|
620
|
+
textResponse(messages: ModelMessage[]): Promise<string>;
|
|
600
621
|
/**
|
|
601
622
|
* Initializes the AI agent with current settings and tools.
|
|
602
623
|
* Sets up the agent with model configuration, tools, and MCP tools.
|
|
@@ -608,7 +629,7 @@ export interface IAgentManager {
|
|
|
608
629
|
* Token for the agent manager.
|
|
609
630
|
*/
|
|
610
631
|
export const IAgentManager = new Token<IAgentManager>(
|
|
611
|
-
'@jupyterlite/ai:
|
|
632
|
+
'@jupyterlite/ai:IAgentManager'
|
|
612
633
|
);
|
|
613
634
|
|
|
614
635
|
/* The AGENT MANAGER FACTORY */
|
|
@@ -640,7 +661,7 @@ export interface IAgentManagerFactory {
|
|
|
640
661
|
* Token for the agent manager factory.
|
|
641
662
|
*/
|
|
642
663
|
export const IAgentManagerFactory = new Token<IAgentManagerFactory>(
|
|
643
|
-
'@jupyterlite/ai:
|
|
664
|
+
'@jupyterlite/ai:IAgentManagerFactory'
|
|
644
665
|
);
|
|
645
666
|
|
|
646
667
|
/* THE CHAT MODELS HANDLER */
|
|
@@ -680,12 +701,16 @@ export interface ICreateChatOptions {
|
|
|
680
701
|
* Whether the chat is autosaved or not.
|
|
681
702
|
*/
|
|
682
703
|
autosave?: boolean;
|
|
704
|
+
/**
|
|
705
|
+
* An optional title to the chat.
|
|
706
|
+
*/
|
|
707
|
+
title?: string | null;
|
|
683
708
|
}
|
|
684
709
|
/**
|
|
685
710
|
* Token for the chat model handler.
|
|
686
711
|
*/
|
|
687
712
|
export const IChatModelHandler = new Token<IChatModelHandler>(
|
|
688
|
-
'@jupyterlite/ai:
|
|
713
|
+
'@jupyterlite/ai:IChatModelHandler'
|
|
689
714
|
);
|
|
690
715
|
|
|
691
716
|
/* THE DIFF MANAGER */
|
|
@@ -760,7 +785,7 @@ export interface IDiffManager {
|
|
|
760
785
|
* Token for the diff manager.
|
|
761
786
|
*/
|
|
762
787
|
export const IDiffManager = new Token<IDiffManager>(
|
|
763
|
-
'@jupyterlite/ai:
|
|
788
|
+
'@jupyterlite/ai:IDiffManager'
|
|
764
789
|
);
|
|
765
790
|
|
|
766
791
|
/**
|
|
@@ -776,6 +801,17 @@ export interface ITokenUsage {
|
|
|
776
801
|
* Number of output tokens generated (completion tokens)
|
|
777
802
|
*/
|
|
778
803
|
outputTokens: number;
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Estimated prompt tokens used by the most recent model request.
|
|
807
|
+
* This is based on the final step of the latest request.
|
|
808
|
+
*/
|
|
809
|
+
lastRequestInputTokens?: number;
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Configured context window size for the active provider/model.
|
|
813
|
+
*/
|
|
814
|
+
contextWindow?: number;
|
|
779
815
|
}
|
|
780
816
|
|
|
781
817
|
/**
|
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
createTheme
|
|
46
46
|
} from '@mui/material';
|
|
47
47
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
48
|
+
import { getEffectiveContextWindow } from '../providers/model-info';
|
|
48
49
|
import {
|
|
49
50
|
type IAgentManagerFactory,
|
|
50
51
|
type IAIConfig,
|
|
@@ -670,6 +671,10 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
670
671
|
const providerToolCapabilities =
|
|
671
672
|
providerInfo?.providerToolCapabilities;
|
|
672
673
|
const params = provider.parameters;
|
|
674
|
+
const effectiveContextWindow = getEffectiveContextWindow(
|
|
675
|
+
provider,
|
|
676
|
+
providerRegistry
|
|
677
|
+
);
|
|
673
678
|
const webSearchEnabled =
|
|
674
679
|
!!providerToolCapabilities?.webSearch &&
|
|
675
680
|
provider.customSettings?.webSearch?.enabled === true;
|
|
@@ -741,6 +746,7 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
741
746
|
{(params?.temperature !== undefined ||
|
|
742
747
|
params?.maxOutputTokens !== undefined ||
|
|
743
748
|
params?.maxTurns !== undefined ||
|
|
749
|
+
effectiveContextWindow !== undefined ||
|
|
744
750
|
webSearchEnabled ||
|
|
745
751
|
webFetchEnabled) && (
|
|
746
752
|
<Box
|
|
@@ -781,6 +787,16 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
781
787
|
variant="outlined"
|
|
782
788
|
/>
|
|
783
789
|
)}
|
|
790
|
+
{effectiveContextWindow !== undefined && (
|
|
791
|
+
<Chip
|
|
792
|
+
label={trans.__(
|
|
793
|
+
'Context: %1',
|
|
794
|
+
effectiveContextWindow
|
|
795
|
+
)}
|
|
796
|
+
size="small"
|
|
797
|
+
variant="outlined"
|
|
798
|
+
/>
|
|
799
|
+
)}
|
|
784
800
|
{webSearchEnabled && (
|
|
785
801
|
<Chip
|
|
786
802
|
label={trans.__('Web Search')}
|
|
@@ -935,6 +951,32 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
935
951
|
}
|
|
936
952
|
/>
|
|
937
953
|
|
|
954
|
+
<FormControlLabel
|
|
955
|
+
control={
|
|
956
|
+
<Switch
|
|
957
|
+
checked={config.showContextUsage}
|
|
958
|
+
onChange={e =>
|
|
959
|
+
handleConfigUpdate({
|
|
960
|
+
showContextUsage: e.target.checked
|
|
961
|
+
})
|
|
962
|
+
}
|
|
963
|
+
color="primary"
|
|
964
|
+
/>
|
|
965
|
+
}
|
|
966
|
+
label={
|
|
967
|
+
<Box>
|
|
968
|
+
<Typography variant="body1">
|
|
969
|
+
{trans.__('Show Context Usage')}
|
|
970
|
+
</Typography>
|
|
971
|
+
<Typography variant="caption" color="text.secondary">
|
|
972
|
+
{trans.__(
|
|
973
|
+
'Display estimated context usage in the chat toolbar'
|
|
974
|
+
)}
|
|
975
|
+
</Typography>
|
|
976
|
+
</Box>
|
|
977
|
+
}
|
|
978
|
+
/>
|
|
979
|
+
|
|
938
980
|
<FormControlLabel
|
|
939
981
|
control={
|
|
940
982
|
<Switch
|
|
@@ -6,7 +6,7 @@ import { CommandRegistry } from '@lumino/commands';
|
|
|
6
6
|
|
|
7
7
|
import { AIChatModel } from '../chat-model';
|
|
8
8
|
import { SaveComponentWidget } from '../components/save-button';
|
|
9
|
-
import {
|
|
9
|
+
import { UsageWidget } from '../components/usage-display';
|
|
10
10
|
import { RenderedMessageOutputAreaCompat } from '../rendered-message-outputarea';
|
|
11
11
|
import { CommandIds, type IAISettingsModel } from '../tokens';
|
|
12
12
|
|
|
@@ -24,7 +24,8 @@ export namespace MainAreaChat {
|
|
|
24
24
|
export class MainAreaChat extends MainAreaWidget<ChatWidget> {
|
|
25
25
|
constructor(options: MainAreaChat.IOptions) {
|
|
26
26
|
super(options);
|
|
27
|
-
this.title.label = this.
|
|
27
|
+
this.title.label = this.model.name;
|
|
28
|
+
this.title.caption = this.model.title ?? this.model.name;
|
|
28
29
|
|
|
29
30
|
const { trans } = options;
|
|
30
31
|
|
|
@@ -54,13 +55,13 @@ export class MainAreaChat extends MainAreaWidget<ChatWidget> {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
// Add the token usage button.
|
|
57
|
-
const
|
|
58
|
+
const usageWidget = new UsageWidget({
|
|
58
59
|
tokenUsageChanged: this.model.tokenUsageChanged,
|
|
59
60
|
settingsModel: options.settingsModel,
|
|
60
61
|
initialTokenUsage: this.model.agentManager.tokenUsage,
|
|
61
62
|
translator: trans
|
|
62
63
|
});
|
|
63
|
-
this.toolbar.addItem('
|
|
64
|
+
this.toolbar.addItem('usage', usageWidget);
|
|
64
65
|
|
|
65
66
|
// Temporary compat: keep output-area CSS context for MIME renderers
|
|
66
67
|
// until jupyter-chat provides it natively.
|
|
@@ -69,6 +70,8 @@ export class MainAreaChat extends MainAreaWidget<ChatWidget> {
|
|
|
69
70
|
});
|
|
70
71
|
|
|
71
72
|
this.model.writersChanged.connect(this._writersChanged);
|
|
73
|
+
|
|
74
|
+
this.model.titleChanged.connect(this._titleChanged);
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
dispose(): void {
|
|
@@ -76,6 +79,7 @@ export class MainAreaChat extends MainAreaWidget<ChatWidget> {
|
|
|
76
79
|
// Dispose of the approval buttons widget when the chat is disposed.
|
|
77
80
|
this._outputAreaCompat.dispose();
|
|
78
81
|
this.model.writersChanged.disconnect(this._writersChanged);
|
|
82
|
+
this.model.titleChanged.disconnect(this._titleChanged);
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
/**
|
|
@@ -107,5 +111,9 @@ export class MainAreaChat extends MainAreaWidget<ChatWidget> {
|
|
|
107
111
|
}
|
|
108
112
|
};
|
|
109
113
|
|
|
114
|
+
private _titleChanged = () => {
|
|
115
|
+
this.title.caption = this.model.title ?? this.model.name;
|
|
116
|
+
};
|
|
117
|
+
|
|
110
118
|
private _outputAreaCompat: RenderedMessageOutputAreaCompat;
|
|
111
119
|
}
|
|
@@ -37,6 +37,7 @@ import type {
|
|
|
37
37
|
IProviderRegistry,
|
|
38
38
|
IProviderToolCapabilities
|
|
39
39
|
} from '../tokens';
|
|
40
|
+
import { getProviderModelInfo } from '../providers/model-info';
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* Default parameter values for provider configuration
|
|
@@ -168,6 +169,10 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
|
|
|
168
169
|
);
|
|
169
170
|
const providerToolCapabilities =
|
|
170
171
|
selectedProviderInfo?.providerToolCapabilities;
|
|
172
|
+
const selectedModelInfo = React.useMemo(
|
|
173
|
+
() => getProviderModelInfo(selectedProviderInfo, model),
|
|
174
|
+
[selectedProviderInfo, model]
|
|
175
|
+
);
|
|
171
176
|
const webSearchImplementation =
|
|
172
177
|
providerToolCapabilities?.webSearch?.implementation;
|
|
173
178
|
const supportsWebSearch = !!providerToolCapabilities?.webSearch;
|
|
@@ -653,7 +658,7 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
|
|
|
653
658
|
}
|
|
654
659
|
placeholder={trans.__('Leave empty for provider default')}
|
|
655
660
|
helperText={trans.__('Maximum length of AI responses')}
|
|
656
|
-
|
|
661
|
+
slotProps={{ htmlInput: { min: 1 } }}
|
|
657
662
|
/>
|
|
658
663
|
|
|
659
664
|
<TextField
|
|
@@ -673,7 +678,42 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
|
|
|
673
678
|
helperText={trans.__(
|
|
674
679
|
'Maximum number of tool execution turns'
|
|
675
680
|
)}
|
|
676
|
-
|
|
681
|
+
slotProps={{ htmlInput: { min: 1, max: 100 } }}
|
|
682
|
+
/>
|
|
683
|
+
|
|
684
|
+
<TextField
|
|
685
|
+
fullWidth
|
|
686
|
+
label={trans.__('Context Window (Optional)')}
|
|
687
|
+
type="number"
|
|
688
|
+
value={parameters.contextWindow ?? ''}
|
|
689
|
+
onChange={e =>
|
|
690
|
+
setParameters({
|
|
691
|
+
...parameters,
|
|
692
|
+
contextWindow: e.target.value
|
|
693
|
+
? Number(e.target.value)
|
|
694
|
+
: undefined
|
|
695
|
+
})
|
|
696
|
+
}
|
|
697
|
+
placeholder={
|
|
698
|
+
selectedModelInfo?.contextWindow !== undefined
|
|
699
|
+
? trans.__(
|
|
700
|
+
'Default: %1',
|
|
701
|
+
selectedModelInfo.contextWindow.toLocaleString()
|
|
702
|
+
)
|
|
703
|
+
: trans.__('e.g., 128000')
|
|
704
|
+
}
|
|
705
|
+
helperText={
|
|
706
|
+
selectedModelInfo?.contextWindow !== undefined &&
|
|
707
|
+
parameters.contextWindow === undefined
|
|
708
|
+
? trans.__(
|
|
709
|
+
'Using provider metadata default of %1 tokens for this model unless you override it here.',
|
|
710
|
+
selectedModelInfo.contextWindow.toLocaleString()
|
|
711
|
+
)
|
|
712
|
+
: trans.__(
|
|
713
|
+
'Model context window size in tokens (used for context usage estimation)'
|
|
714
|
+
)
|
|
715
|
+
}
|
|
716
|
+
slotProps={{ htmlInput: { min: 1 } }}
|
|
677
717
|
/>
|
|
678
718
|
|
|
679
719
|
<Typography
|
|
@@ -830,7 +870,7 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
|
|
|
830
870
|
: undefined
|
|
831
871
|
)
|
|
832
872
|
}
|
|
833
|
-
|
|
873
|
+
slotProps={{ htmlInput: { min: 1 } }}
|
|
834
874
|
/>
|
|
835
875
|
{renderDomainList(
|
|
836
876
|
'webSearch.blockedDomains',
|
|
@@ -888,7 +928,7 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
|
|
|
888
928
|
: undefined
|
|
889
929
|
)
|
|
890
930
|
}
|
|
891
|
-
|
|
931
|
+
slotProps={{ htmlInput: { min: 1 } }}
|
|
892
932
|
/>
|
|
893
933
|
<TextField
|
|
894
934
|
fullWidth
|
|
@@ -904,7 +944,7 @@ export const ProviderConfigDialog: React.FC<IProviderConfigDialogProps> = ({
|
|
|
904
944
|
: undefined
|
|
905
945
|
)
|
|
906
946
|
}
|
|
907
|
-
|
|
947
|
+
slotProps={{ htmlInput: { min: 1 } }}
|
|
908
948
|
/>
|
|
909
949
|
{renderDomainList(
|
|
910
950
|
'webFetch.allowedDomains',
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { ReactWidget, UseSignal } from '@jupyterlab/ui-components';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
/**
|
|
4
|
-
* React component that displays token usage information.
|
|
5
|
-
* Shows input/output token counts with up/down arrows.
|
|
6
|
-
* Only renders when token usage display is enabled in settings.
|
|
7
|
-
*/
|
|
8
|
-
export const TokenUsageDisplay = ({ tokenUsageChanged, settingsModel, initialTokenUsage, translator: trans }) => {
|
|
9
|
-
return (React.createElement(UseSignal, { signal: settingsModel.stateChanged, initialArgs: undefined }, () => {
|
|
10
|
-
const config = settingsModel.config;
|
|
11
|
-
if (!config.showTokenUsage) {
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
return (React.createElement(UseSignal, { signal: tokenUsageChanged, initialArgs: initialTokenUsage }, (_, tokenUsage) => {
|
|
15
|
-
if (!tokenUsage) {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
const total = tokenUsage.inputTokens + tokenUsage.outputTokens;
|
|
19
|
-
if (total === 0) {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
return (React.createElement("div", { style: {
|
|
23
|
-
display: 'flex',
|
|
24
|
-
alignItems: 'center',
|
|
25
|
-
gap: '6px',
|
|
26
|
-
fontSize: '12px',
|
|
27
|
-
color: 'var(--jp-ui-font-color2)',
|
|
28
|
-
padding: '4px 8px',
|
|
29
|
-
backgroundColor: 'var(--jp-layout-color1)',
|
|
30
|
-
border: '1px solid var(--jp-border-color1)',
|
|
31
|
-
borderRadius: '4px',
|
|
32
|
-
whiteSpace: 'nowrap'
|
|
33
|
-
}, title: trans.__('Token Usage - Sent: %1, Received: %2, Total: %3', tokenUsage.inputTokens.toLocaleString(), tokenUsage.outputTokens.toLocaleString(), total.toLocaleString()) },
|
|
34
|
-
React.createElement("span", { style: {
|
|
35
|
-
display: 'flex',
|
|
36
|
-
alignItems: 'center',
|
|
37
|
-
gap: '2px'
|
|
38
|
-
} },
|
|
39
|
-
React.createElement("span", null, "\u2191"),
|
|
40
|
-
React.createElement("span", null, tokenUsage.inputTokens.toLocaleString())),
|
|
41
|
-
React.createElement("span", { style: {
|
|
42
|
-
display: 'flex',
|
|
43
|
-
alignItems: 'center',
|
|
44
|
-
gap: '2px'
|
|
45
|
-
} },
|
|
46
|
-
React.createElement("span", null, "\u2193"),
|
|
47
|
-
React.createElement("span", null, tokenUsage.outputTokens.toLocaleString()))));
|
|
48
|
-
}));
|
|
49
|
-
}));
|
|
50
|
-
};
|
|
51
|
-
/**
|
|
52
|
-
* JupyterLab widget wrapper for the TokenUsageDisplay component.
|
|
53
|
-
* Extends ReactWidget to integrate with the JupyterLab widget system.
|
|
54
|
-
*/
|
|
55
|
-
export class TokenUsageWidget extends ReactWidget {
|
|
56
|
-
/**
|
|
57
|
-
* Creates a new TokenUsageWidget instance.
|
|
58
|
-
* @param options - Configuration options containing required models
|
|
59
|
-
*/
|
|
60
|
-
constructor(options) {
|
|
61
|
-
super();
|
|
62
|
-
this._options = options;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Renders the React component within the widget.
|
|
66
|
-
* @returns The TokenUsageDisplay React element
|
|
67
|
-
*/
|
|
68
|
-
render() {
|
|
69
|
-
return React.createElement(TokenUsageDisplay, { ...this._options });
|
|
70
|
-
}
|
|
71
|
-
_options;
|
|
72
|
-
}
|