@jupyterlite/ai 0.12.0 → 0.13.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 +24 -2
- package/lib/agent.js +161 -24
- package/lib/{chat-model-registry.d.ts → chat-model-handler.d.ts} +12 -11
- package/lib/{chat-model-registry.js → chat-model-handler.js} +6 -40
- package/lib/chat-model.d.ts +8 -0
- package/lib/chat-model.js +156 -8
- package/lib/completion/completion-provider.d.ts +1 -1
- package/lib/completion/completion-provider.js +14 -2
- package/lib/components/model-select.js +4 -4
- package/lib/components/tool-select.d.ts +11 -2
- package/lib/components/tool-select.js +77 -18
- package/lib/index.d.ts +3 -3
- package/lib/index.js +128 -66
- package/lib/models/settings-model.d.ts +2 -0
- package/lib/models/settings-model.js +2 -0
- package/lib/providers/built-in-providers.js +7 -0
- package/lib/providers/provider-tools.d.ts +36 -0
- package/lib/providers/provider-tools.js +93 -0
- package/lib/rendered-message-outputarea.d.ts +24 -0
- package/lib/rendered-message-outputarea.js +48 -0
- package/lib/tokens.d.ts +44 -7
- package/lib/tokens.js +1 -1
- package/lib/tools/commands.js +4 -2
- package/lib/tools/web.d.ts +8 -0
- package/lib/tools/web.js +196 -0
- package/lib/widgets/ai-settings.d.ts +1 -1
- package/lib/widgets/ai-settings.js +125 -38
- package/lib/widgets/main-area-chat.d.ts +6 -0
- package/lib/widgets/main-area-chat.js +28 -0
- package/lib/widgets/provider-config-dialog.js +207 -4
- package/package.json +10 -4
- package/schema/settings-model.json +89 -1
- package/src/agent.ts +220 -42
- package/src/{chat-model-registry.ts → chat-model-handler.ts} +16 -51
- package/src/chat-model.ts +223 -14
- package/src/completion/completion-provider.ts +26 -12
- package/src/components/model-select.tsx +4 -5
- package/src/components/tool-select.tsx +110 -7
- package/src/index.ts +153 -82
- package/src/models/settings-model.ts +6 -0
- package/src/providers/built-in-providers.ts +7 -0
- package/src/providers/provider-tools.ts +179 -0
- package/src/rendered-message-outputarea.ts +62 -0
- package/src/tokens.ts +53 -9
- package/src/tools/commands.ts +4 -2
- package/src/tools/web.ts +238 -0
- package/src/widgets/ai-settings.tsx +282 -77
- package/src/widgets/main-area-chat.ts +34 -1
- package/src/widgets/provider-config-dialog.tsx +496 -3
|
@@ -31,7 +31,6 @@ import {
|
|
|
31
31
|
InputLabel,
|
|
32
32
|
List,
|
|
33
33
|
ListItem,
|
|
34
|
-
ListItemSecondaryAction,
|
|
35
34
|
ListItemText,
|
|
36
35
|
Menu,
|
|
37
36
|
MenuItem,
|
|
@@ -100,6 +99,12 @@ export class AISettingsWidget extends ReactWidget {
|
|
|
100
99
|
this.title.label = this._trans.__('AI Settings');
|
|
101
100
|
this.title.caption = this._trans.__('Configure AI providers and behavior');
|
|
102
101
|
this.title.closable = true;
|
|
102
|
+
|
|
103
|
+
// Disable the secrets manager if the token is empty.
|
|
104
|
+
if (!options.token) {
|
|
105
|
+
this._settingsModel.updateConfig({ useSecretsManager: false });
|
|
106
|
+
this._secretsManager = undefined;
|
|
107
|
+
}
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
/**
|
|
@@ -282,8 +287,12 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
282
287
|
provider: string,
|
|
283
288
|
fieldName: string
|
|
284
289
|
): Promise<string | undefined> => {
|
|
290
|
+
const token = Private.getToken();
|
|
291
|
+
if (!token) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
285
294
|
const secret = await secretsManager?.get(
|
|
286
|
-
|
|
295
|
+
token,
|
|
287
296
|
SECRETS_NAMESPACE,
|
|
288
297
|
`${provider}:${fieldName}`
|
|
289
298
|
);
|
|
@@ -295,8 +304,12 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
295
304
|
fieldName: string,
|
|
296
305
|
value: string
|
|
297
306
|
): Promise<void> => {
|
|
307
|
+
const token = Private.getToken();
|
|
308
|
+
if (!token) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
298
311
|
await secretsManager?.set(
|
|
299
|
-
|
|
312
|
+
token,
|
|
300
313
|
SECRETS_NAMESPACE,
|
|
301
314
|
`${provider}:${fieldName}`,
|
|
302
315
|
{
|
|
@@ -321,8 +334,12 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
321
334
|
if (!(model.config.useSecretsManager && secretsManager)) {
|
|
322
335
|
return;
|
|
323
336
|
}
|
|
337
|
+
const token = Private.getToken();
|
|
338
|
+
if (!token) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
324
341
|
await secretsManager?.attach(
|
|
325
|
-
|
|
342
|
+
token,
|
|
326
343
|
SECRETS_NAMESPACE,
|
|
327
344
|
`${provider}:${fieldName}`,
|
|
328
345
|
input
|
|
@@ -427,17 +444,25 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
427
444
|
if (updates.useSecretsManager !== undefined) {
|
|
428
445
|
if (updates.useSecretsManager) {
|
|
429
446
|
for (const provider of model.config.providers) {
|
|
430
|
-
|
|
447
|
+
const settingsApiKey = provider.apiKey;
|
|
448
|
+
// If the secrets manager doesn't have the current API key, set the current
|
|
431
449
|
// one from settings.
|
|
450
|
+
// Update the settings value with SECRETS_REPLACEMENT if a key exist in the
|
|
451
|
+
// secrets manager (was already there or a value was set in settings).
|
|
432
452
|
if (!(await getSecretFromManager(provider.provider, 'apiKey'))) {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
453
|
+
if (settingsApiKey !== undefined) {
|
|
454
|
+
setSecretToManager(
|
|
455
|
+
provider.provider,
|
|
456
|
+
'apiKey',
|
|
457
|
+
settingsApiKey !== SECRETS_REPLACEMENT ? settingsApiKey : ''
|
|
458
|
+
);
|
|
459
|
+
provider.apiKey = SECRETS_REPLACEMENT;
|
|
460
|
+
await model.updateProvider(provider.id, provider);
|
|
461
|
+
}
|
|
462
|
+
} else {
|
|
463
|
+
provider.apiKey = SECRETS_REPLACEMENT;
|
|
464
|
+
await model.updateProvider(provider.id, provider);
|
|
438
465
|
}
|
|
439
|
-
provider.apiKey = SECRETS_REPLACEMENT;
|
|
440
|
-
await model.updateProvider(provider.id, provider);
|
|
441
466
|
}
|
|
442
467
|
} else {
|
|
443
468
|
for (const provider of model.config.providers) {
|
|
@@ -445,10 +470,8 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
445
470
|
provider.provider,
|
|
446
471
|
'apiKey'
|
|
447
472
|
);
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
await model.updateProvider(provider.id, provider);
|
|
451
|
-
}
|
|
473
|
+
provider.apiKey = apiKey;
|
|
474
|
+
await model.updateProvider(provider.id, provider);
|
|
452
475
|
}
|
|
453
476
|
}
|
|
454
477
|
}
|
|
@@ -676,7 +699,18 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
676
699
|
config.useSameProviderForChatAndCompleter
|
|
677
700
|
? isActive
|
|
678
701
|
: config.activeCompleterProvider === provider.id;
|
|
702
|
+
const providerInfo = providerRegistry.getProviderInfo(
|
|
703
|
+
provider.provider
|
|
704
|
+
);
|
|
705
|
+
const providerToolCapabilities =
|
|
706
|
+
providerInfo?.providerToolCapabilities;
|
|
679
707
|
const params = provider.parameters;
|
|
708
|
+
const webSearchEnabled =
|
|
709
|
+
!!providerToolCapabilities?.webSearch &&
|
|
710
|
+
provider.customSettings?.webSearch?.enabled === true;
|
|
711
|
+
const webFetchEnabled =
|
|
712
|
+
!!providerToolCapabilities?.webFetch &&
|
|
713
|
+
provider.customSettings?.webFetch?.enabled === true;
|
|
680
714
|
|
|
681
715
|
return (
|
|
682
716
|
<ListItem
|
|
@@ -739,50 +773,67 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
739
773
|
</Typography>
|
|
740
774
|
|
|
741
775
|
{/* Display parameters if set */}
|
|
742
|
-
{params
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
776
|
+
{(params?.temperature !== undefined ||
|
|
777
|
+
params?.maxOutputTokens !== undefined ||
|
|
778
|
+
params?.maxTurns !== undefined ||
|
|
779
|
+
webSearchEnabled ||
|
|
780
|
+
webFetchEnabled) && (
|
|
781
|
+
<Box
|
|
782
|
+
sx={{
|
|
783
|
+
display: 'flex',
|
|
784
|
+
flexWrap: 'wrap',
|
|
785
|
+
gap: 1,
|
|
786
|
+
mt: 1
|
|
787
|
+
}}
|
|
788
|
+
>
|
|
789
|
+
{params?.temperature !== undefined && (
|
|
790
|
+
<Chip
|
|
791
|
+
label={trans.__(
|
|
792
|
+
'Temp: %1',
|
|
793
|
+
params.temperature
|
|
794
|
+
)}
|
|
795
|
+
size="small"
|
|
796
|
+
variant="outlined"
|
|
797
|
+
/>
|
|
798
|
+
)}
|
|
799
|
+
{params?.maxOutputTokens !== undefined && (
|
|
800
|
+
<Chip
|
|
801
|
+
label={trans.__(
|
|
802
|
+
'Tokens: %1',
|
|
803
|
+
params.maxOutputTokens
|
|
804
|
+
)}
|
|
805
|
+
size="small"
|
|
806
|
+
variant="outlined"
|
|
807
|
+
/>
|
|
808
|
+
)}
|
|
809
|
+
{params?.maxTurns !== undefined && (
|
|
810
|
+
<Chip
|
|
811
|
+
label={trans.__(
|
|
812
|
+
'Turns: %1',
|
|
813
|
+
params.maxTurns
|
|
814
|
+
)}
|
|
815
|
+
size="small"
|
|
816
|
+
variant="outlined"
|
|
817
|
+
/>
|
|
818
|
+
)}
|
|
819
|
+
{webSearchEnabled && (
|
|
820
|
+
<Chip
|
|
821
|
+
label={trans.__('Web Search')}
|
|
822
|
+
size="small"
|
|
823
|
+
variant="outlined"
|
|
824
|
+
color="info"
|
|
825
|
+
/>
|
|
826
|
+
)}
|
|
827
|
+
{webFetchEnabled && (
|
|
828
|
+
<Chip
|
|
829
|
+
label={trans.__('Web Fetch')}
|
|
830
|
+
size="small"
|
|
831
|
+
variant="outlined"
|
|
832
|
+
color="info"
|
|
833
|
+
/>
|
|
834
|
+
)}
|
|
835
|
+
</Box>
|
|
836
|
+
)}
|
|
786
837
|
</Box>
|
|
787
838
|
<IconButton
|
|
788
839
|
onClick={e => handleMenuClick(e, provider.id)}
|
|
@@ -1117,9 +1168,10 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
1117
1168
|
|
|
1118
1169
|
<List sx={{ mb: 2, maxHeight: 200, overflow: 'auto' }}>
|
|
1119
1170
|
{config.commandsRequiringApproval.map((command, index) => (
|
|
1120
|
-
<ListItem
|
|
1121
|
-
|
|
1122
|
-
|
|
1171
|
+
<ListItem
|
|
1172
|
+
key={index}
|
|
1173
|
+
divider
|
|
1174
|
+
secondaryAction={
|
|
1123
1175
|
<IconButton
|
|
1124
1176
|
onClick={() => {
|
|
1125
1177
|
const newCommands = [
|
|
@@ -1134,7 +1186,9 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
1134
1186
|
>
|
|
1135
1187
|
<Delete />
|
|
1136
1188
|
</IconButton>
|
|
1137
|
-
|
|
1189
|
+
}
|
|
1190
|
+
>
|
|
1191
|
+
<ListItemText primary={command} />
|
|
1138
1192
|
</ListItem>
|
|
1139
1193
|
))}
|
|
1140
1194
|
</List>
|
|
@@ -1168,6 +1222,154 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
1168
1222
|
)}
|
|
1169
1223
|
/>
|
|
1170
1224
|
</Box>
|
|
1225
|
+
|
|
1226
|
+
<Divider sx={{ my: 2 }} />
|
|
1227
|
+
|
|
1228
|
+
<Box>
|
|
1229
|
+
<Typography variant="body1" gutterBottom>
|
|
1230
|
+
{trans.__('Commands Auto-Rendering MIME Bundles')}
|
|
1231
|
+
</Typography>
|
|
1232
|
+
<Typography
|
|
1233
|
+
variant="caption"
|
|
1234
|
+
color="text.secondary"
|
|
1235
|
+
gutterBottom
|
|
1236
|
+
sx={{ display: 'block' }}
|
|
1237
|
+
>
|
|
1238
|
+
{trans.__(
|
|
1239
|
+
'Only these execute_command command IDs can auto-render MIME bundle outputs in chat'
|
|
1240
|
+
)}
|
|
1241
|
+
</Typography>
|
|
1242
|
+
|
|
1243
|
+
<List sx={{ mb: 2, maxHeight: 200, overflow: 'auto' }}>
|
|
1244
|
+
{(config.commandsAutoRenderMimeBundles ?? []).map(
|
|
1245
|
+
(command, index) => (
|
|
1246
|
+
<ListItem
|
|
1247
|
+
key={index}
|
|
1248
|
+
divider
|
|
1249
|
+
secondaryAction={
|
|
1250
|
+
<IconButton
|
|
1251
|
+
onClick={() => {
|
|
1252
|
+
const newCommands = [
|
|
1253
|
+
...(config.commandsAutoRenderMimeBundles ??
|
|
1254
|
+
[])
|
|
1255
|
+
];
|
|
1256
|
+
newCommands.splice(index, 1);
|
|
1257
|
+
handleConfigUpdate({
|
|
1258
|
+
commandsAutoRenderMimeBundles: newCommands
|
|
1259
|
+
});
|
|
1260
|
+
}}
|
|
1261
|
+
size="small"
|
|
1262
|
+
>
|
|
1263
|
+
<Delete />
|
|
1264
|
+
</IconButton>
|
|
1265
|
+
}
|
|
1266
|
+
>
|
|
1267
|
+
<ListItemText primary={command} />
|
|
1268
|
+
</ListItem>
|
|
1269
|
+
)
|
|
1270
|
+
)}
|
|
1271
|
+
</List>
|
|
1272
|
+
|
|
1273
|
+
<TextField
|
|
1274
|
+
fullWidth
|
|
1275
|
+
label={trans.__('Add Auto-Render Command')}
|
|
1276
|
+
placeholder={trans.__(
|
|
1277
|
+
'e.g., jupyterlab-ai-commands:execute-in-kernel'
|
|
1278
|
+
)}
|
|
1279
|
+
onKeyDown={e => {
|
|
1280
|
+
if (e.key === 'Enter') {
|
|
1281
|
+
const value = (
|
|
1282
|
+
e.target as HTMLInputElement
|
|
1283
|
+
).value.trim();
|
|
1284
|
+
const existingCommands =
|
|
1285
|
+
config.commandsAutoRenderMimeBundles ?? [];
|
|
1286
|
+
if (value && !existingCommands.includes(value)) {
|
|
1287
|
+
const newCommands = [...existingCommands, value];
|
|
1288
|
+
handleConfigUpdate({
|
|
1289
|
+
commandsAutoRenderMimeBundles: newCommands
|
|
1290
|
+
});
|
|
1291
|
+
(e.target as HTMLInputElement).value = '';
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}}
|
|
1295
|
+
helperText={trans.__(
|
|
1296
|
+
'Press Enter to add a command. Default: jupyterlab-ai-commands:execute-in-kernel'
|
|
1297
|
+
)}
|
|
1298
|
+
/>
|
|
1299
|
+
</Box>
|
|
1300
|
+
|
|
1301
|
+
<Divider sx={{ my: 2 }} />
|
|
1302
|
+
|
|
1303
|
+
<Box>
|
|
1304
|
+
<Typography variant="body1" gutterBottom>
|
|
1305
|
+
{trans.__('Trusted MIME Types for Auto-Render')}
|
|
1306
|
+
</Typography>
|
|
1307
|
+
<Typography
|
|
1308
|
+
variant="caption"
|
|
1309
|
+
color="text.secondary"
|
|
1310
|
+
gutterBottom
|
|
1311
|
+
sx={{ display: 'block' }}
|
|
1312
|
+
>
|
|
1313
|
+
{trans.__(
|
|
1314
|
+
'When auto-rendering command outputs, these MIME types are marked trusted in chat'
|
|
1315
|
+
)}
|
|
1316
|
+
</Typography>
|
|
1317
|
+
|
|
1318
|
+
<List sx={{ mb: 2, maxHeight: 200, overflow: 'auto' }}>
|
|
1319
|
+
{(config.trustedMimeTypesForAutoRender ?? []).map(
|
|
1320
|
+
(mimeType, index) => (
|
|
1321
|
+
<ListItem
|
|
1322
|
+
key={index}
|
|
1323
|
+
divider
|
|
1324
|
+
secondaryAction={
|
|
1325
|
+
<IconButton
|
|
1326
|
+
onClick={() => {
|
|
1327
|
+
const newMimeTypes = [
|
|
1328
|
+
...(config.trustedMimeTypesForAutoRender ??
|
|
1329
|
+
[])
|
|
1330
|
+
];
|
|
1331
|
+
newMimeTypes.splice(index, 1);
|
|
1332
|
+
handleConfigUpdate({
|
|
1333
|
+
trustedMimeTypesForAutoRender: newMimeTypes
|
|
1334
|
+
});
|
|
1335
|
+
}}
|
|
1336
|
+
size="small"
|
|
1337
|
+
>
|
|
1338
|
+
<Delete />
|
|
1339
|
+
</IconButton>
|
|
1340
|
+
}
|
|
1341
|
+
>
|
|
1342
|
+
<ListItemText primary={mimeType} />
|
|
1343
|
+
</ListItem>
|
|
1344
|
+
)
|
|
1345
|
+
)}
|
|
1346
|
+
</List>
|
|
1347
|
+
|
|
1348
|
+
<TextField
|
|
1349
|
+
fullWidth
|
|
1350
|
+
label={trans.__('Add Trusted MIME Type')}
|
|
1351
|
+
placeholder={trans.__('e.g., text/html')}
|
|
1352
|
+
onKeyDown={e => {
|
|
1353
|
+
if (e.key === 'Enter') {
|
|
1354
|
+
const value = (
|
|
1355
|
+
e.target as HTMLInputElement
|
|
1356
|
+
).value.trim();
|
|
1357
|
+
const existingMimeTypes =
|
|
1358
|
+
config.trustedMimeTypesForAutoRender ?? [];
|
|
1359
|
+
if (value && !existingMimeTypes.includes(value)) {
|
|
1360
|
+
const newMimeTypes = [...existingMimeTypes, value];
|
|
1361
|
+
handleConfigUpdate({
|
|
1362
|
+
trustedMimeTypesForAutoRender: newMimeTypes
|
|
1363
|
+
});
|
|
1364
|
+
(e.target as HTMLInputElement).value = '';
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
}}
|
|
1368
|
+
helperText={trans.__(
|
|
1369
|
+
'Press Enter to add a MIME type. Default: text/html'
|
|
1370
|
+
)}
|
|
1371
|
+
/>
|
|
1372
|
+
</Box>
|
|
1171
1373
|
</Box>
|
|
1172
1374
|
</CardContent>
|
|
1173
1375
|
</Card>
|
|
@@ -1215,7 +1417,18 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
1215
1417
|
) : (
|
|
1216
1418
|
<List>
|
|
1217
1419
|
{config.mcpServers.map(server => (
|
|
1218
|
-
<ListItem
|
|
1420
|
+
<ListItem
|
|
1421
|
+
key={server.id}
|
|
1422
|
+
divider
|
|
1423
|
+
secondaryAction={
|
|
1424
|
+
<IconButton
|
|
1425
|
+
onClick={e => handleMCPMenuClick(e, server.id)}
|
|
1426
|
+
size="small"
|
|
1427
|
+
>
|
|
1428
|
+
<MoreVert />
|
|
1429
|
+
</IconButton>
|
|
1430
|
+
}
|
|
1431
|
+
>
|
|
1219
1432
|
<ListItemText
|
|
1220
1433
|
primary={
|
|
1221
1434
|
<Box
|
|
@@ -1279,14 +1492,6 @@ const AISettingsComponent: React.FC<IAISettingsComponentProps> = ({
|
|
|
1279
1492
|
</Box>
|
|
1280
1493
|
}
|
|
1281
1494
|
/>
|
|
1282
|
-
<ListItemSecondaryAction>
|
|
1283
|
-
<IconButton
|
|
1284
|
-
onClick={e => handleMCPMenuClick(e, server.id)}
|
|
1285
|
-
size="small"
|
|
1286
|
-
>
|
|
1287
|
-
<MoreVert />
|
|
1288
|
-
</IconButton>
|
|
1289
|
-
</ListItemSecondaryAction>
|
|
1290
1495
|
</ListItem>
|
|
1291
1496
|
))}
|
|
1292
1497
|
</List>
|
|
@@ -1517,7 +1722,7 @@ export namespace AISettingsWidget {
|
|
|
1517
1722
|
/**
|
|
1518
1723
|
* The token used to request the secrets manager.
|
|
1519
1724
|
*/
|
|
1520
|
-
token: symbol;
|
|
1725
|
+
token: symbol | null;
|
|
1521
1726
|
/**
|
|
1522
1727
|
* The application language translation bundle.
|
|
1523
1728
|
*/
|
|
@@ -1529,11 +1734,11 @@ namespace Private {
|
|
|
1529
1734
|
/**
|
|
1530
1735
|
* The token to use with the secrets manager, setter and getter.
|
|
1531
1736
|
*/
|
|
1532
|
-
let secretsToken: symbol;
|
|
1533
|
-
export function setToken(value: symbol): void {
|
|
1737
|
+
let secretsToken: symbol | null;
|
|
1738
|
+
export function setToken(value: symbol | null): void {
|
|
1534
1739
|
secretsToken = value;
|
|
1535
1740
|
}
|
|
1536
|
-
export function getToken(): symbol {
|
|
1741
|
+
export function getToken(): symbol | null {
|
|
1537
1742
|
return secretsToken;
|
|
1538
1743
|
}
|
|
1539
1744
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChatWidget } from '@jupyter/chat';
|
|
1
|
+
import { ChatWidget, IChatModel } from '@jupyter/chat';
|
|
2
2
|
import { CommandToolbarButton, MainAreaWidget } from '@jupyterlab/apputils';
|
|
3
3
|
import { launchIcon } from '@jupyterlab/ui-components';
|
|
4
4
|
import type { TranslationBundle } from '@jupyterlab/translation';
|
|
@@ -8,6 +8,7 @@ import { ApprovalButtons } from '../approval-buttons';
|
|
|
8
8
|
import { AIChatModel } from '../chat-model';
|
|
9
9
|
import { TokenUsageWidget } from '../components/token-usage-display';
|
|
10
10
|
import { AISettingsModel } from '../models/settings-model';
|
|
11
|
+
import { RenderedMessageOutputAreaCompat } from '../rendered-message-outputarea';
|
|
11
12
|
import { CommandIds } from '../tokens';
|
|
12
13
|
|
|
13
14
|
export namespace MainAreaChat {
|
|
@@ -56,12 +57,21 @@ export class MainAreaChat extends MainAreaWidget<ChatWidget> {
|
|
|
56
57
|
chatPanel: this.content,
|
|
57
58
|
agentManager: this.model.agentManager
|
|
58
59
|
});
|
|
60
|
+
// Temporary compat: keep output-area CSS context for MIME renderers
|
|
61
|
+
// until jupyter-chat provides it natively.
|
|
62
|
+
this._outputAreaCompat = new RenderedMessageOutputAreaCompat({
|
|
63
|
+
chatPanel: this.content
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
this.model.writersChanged.connect(this._writersChanged);
|
|
59
67
|
}
|
|
60
68
|
|
|
61
69
|
dispose(): void {
|
|
62
70
|
super.dispose();
|
|
63
71
|
// Dispose of the approval buttons widget when the chat is disposed.
|
|
64
72
|
this._approvalButtons.dispose();
|
|
73
|
+
this._outputAreaCompat.dispose();
|
|
74
|
+
this.model.writersChanged.disconnect(this._writersChanged);
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
/**
|
|
@@ -71,5 +81,28 @@ export class MainAreaChat extends MainAreaWidget<ChatWidget> {
|
|
|
71
81
|
return this.content.model as AIChatModel;
|
|
72
82
|
}
|
|
73
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Get the area of the chat.
|
|
86
|
+
*/
|
|
87
|
+
get area(): string | undefined {
|
|
88
|
+
return this.content.area;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private _writersChanged = (_: IChatModel, writers: IChatModel.IWriter[]) => {
|
|
92
|
+
// Check if AI is currently writing (streaming)
|
|
93
|
+
const aiWriting = writers.some(
|
|
94
|
+
writer => writer.user.username === 'ai-assistant'
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (aiWriting) {
|
|
98
|
+
this.content.inputToolbarRegistry?.hide('send');
|
|
99
|
+
this.content.inputToolbarRegistry?.show('stop');
|
|
100
|
+
} else {
|
|
101
|
+
this.content.inputToolbarRegistry?.hide('stop');
|
|
102
|
+
this.content.inputToolbarRegistry?.show('send');
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
74
106
|
private _approvalButtons: ApprovalButtons;
|
|
107
|
+
private _outputAreaCompat: RenderedMessageOutputAreaCompat;
|
|
75
108
|
}
|