@jupyterlite/ai 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/agent.d.ts +7 -1
- package/lib/agent.js +59 -10
- 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 +254 -130
- package/lib/components/clear-button.d.ts +1 -1
- package/lib/components/clear-button.js +1 -1
- package/lib/index.js +200 -15
- package/lib/tokens.d.ts +13 -3
- package/lib/tokens.js +1 -0
- package/lib/widgets/main-area-chat.d.ts +1 -0
- package/lib/widgets/main-area-chat.js +7 -1
- package/package.json +1 -1
- package/src/agent.ts +72 -14
- package/src/chat-commands/clear.ts +1 -1
- package/src/chat-model-handler.ts +6 -1
- package/src/chat-model.ts +343 -171
- package/src/components/clear-button.tsx +3 -3
- package/src/index.ts +245 -17
- package/src/tokens.ts +13 -3
- package/src/widgets/main-area-chat.ts +9 -1
package/lib/chat-model.js
CHANGED
|
@@ -49,6 +49,31 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
49
49
|
}
|
|
50
50
|
this.setReady();
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* A signal emitting when the chat name has changed.
|
|
54
|
+
*/
|
|
55
|
+
get nameChanged() {
|
|
56
|
+
return this._nameChanged;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The title of the chat.
|
|
60
|
+
*/
|
|
61
|
+
get title() {
|
|
62
|
+
return this._title;
|
|
63
|
+
}
|
|
64
|
+
set title(value) {
|
|
65
|
+
this._title = value;
|
|
66
|
+
if (this.autosave) {
|
|
67
|
+
this._autosaveDebouncer.invoke();
|
|
68
|
+
}
|
|
69
|
+
this._titleChanged.emit(this._title);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A signal emitting when the chat title has changed.
|
|
73
|
+
*/
|
|
74
|
+
get titleChanged() {
|
|
75
|
+
return this._titleChanged;
|
|
76
|
+
}
|
|
52
77
|
/**
|
|
53
78
|
* Whether to save the chat automatically.
|
|
54
79
|
*/
|
|
@@ -74,12 +99,6 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
74
99
|
get autosaveChanged() {
|
|
75
100
|
return this._autosaveChanged;
|
|
76
101
|
}
|
|
77
|
-
/**
|
|
78
|
-
* A signal emitting when the chat name has changed.
|
|
79
|
-
*/
|
|
80
|
-
get nameChanged() {
|
|
81
|
-
return this._nameChanged;
|
|
82
|
-
}
|
|
83
102
|
/**
|
|
84
103
|
* Gets the current user information.
|
|
85
104
|
*/
|
|
@@ -135,10 +154,10 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
135
154
|
/**
|
|
136
155
|
* Clears all messages from the chat and resets conversation state.
|
|
137
156
|
*/
|
|
138
|
-
clearMessages = () => {
|
|
157
|
+
clearMessages = async () => {
|
|
139
158
|
this.messagesDeleted(0, this.messages.length);
|
|
140
159
|
this._toolContexts.clear();
|
|
141
|
-
this._agentManager.clearHistory();
|
|
160
|
+
await this._agentManager.clearHistory();
|
|
142
161
|
};
|
|
143
162
|
/**
|
|
144
163
|
* Adds a non-user message to the chat (used by chat commands).
|
|
@@ -192,11 +211,18 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
192
211
|
// Process attachments and add their content to the message
|
|
193
212
|
let enhancedMessage = message.body;
|
|
194
213
|
if (this.input.attachments.length > 0) {
|
|
195
|
-
const
|
|
214
|
+
const { textContents, binaryParts } = await Private.processAttachments(this.input.attachments, this.input.documentManager);
|
|
196
215
|
this.input.clearAttachments();
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
216
|
+
let textPart = message.body;
|
|
217
|
+
if (textContents.length > 0) {
|
|
218
|
+
textPart +=
|
|
219
|
+
'\n\n--- Attached Files ---\n' + textContents.join('\n\n');
|
|
220
|
+
}
|
|
221
|
+
if (binaryParts.length > 0) {
|
|
222
|
+
enhancedMessage = [{ type: 'text', text: textPart }, ...binaryParts];
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
enhancedMessage = textPart;
|
|
200
226
|
}
|
|
201
227
|
}
|
|
202
228
|
this.updateWriters([{ user: this._getAIUser() }]);
|
|
@@ -299,12 +325,33 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
299
325
|
attachments
|
|
300
326
|
};
|
|
301
327
|
});
|
|
302
|
-
this.clearMessages();
|
|
328
|
+
await this.clearMessages();
|
|
303
329
|
this.messagesInserted(0, messages);
|
|
304
330
|
this._agentManager.setHistory(messages);
|
|
305
331
|
this.autosave = content.metadata?.autosave ?? false;
|
|
332
|
+
this.title = content.metadata?.title ?? null;
|
|
306
333
|
return true;
|
|
307
334
|
};
|
|
335
|
+
/**
|
|
336
|
+
* Request a title to this chat, regarding the message history.
|
|
337
|
+
*/
|
|
338
|
+
async requestTitle() {
|
|
339
|
+
const history = this.messages
|
|
340
|
+
.filter(msg => msg.body !== '')
|
|
341
|
+
.map(msg => `${msg.sender.username === 'ai-assistant' ? 'assistant' : 'user'}: ${msg.body}`)
|
|
342
|
+
.join('\n');
|
|
343
|
+
const messages = [
|
|
344
|
+
{
|
|
345
|
+
role: 'system',
|
|
346
|
+
content: "Generate a concise title (no more than 10 words) for the following conversation. Do not use formatting. Focus on the user's main intent."
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
role: 'user',
|
|
350
|
+
content: history
|
|
351
|
+
}
|
|
352
|
+
];
|
|
353
|
+
return this.agentManager.textResponse(messages);
|
|
354
|
+
}
|
|
308
355
|
/**
|
|
309
356
|
* Serialize the model for backup
|
|
310
357
|
*/
|
|
@@ -348,7 +395,8 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
348
395
|
attachments,
|
|
349
396
|
metadata: {
|
|
350
397
|
provider,
|
|
351
|
-
autosave: this.autosave
|
|
398
|
+
autosave: this.autosave,
|
|
399
|
+
...(this.title ? { title: this.title } : {})
|
|
352
400
|
}
|
|
353
401
|
};
|
|
354
402
|
}
|
|
@@ -658,49 +706,216 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
658
706
|
}
|
|
659
707
|
});
|
|
660
708
|
}
|
|
709
|
+
// Private fields
|
|
710
|
+
_settingsModel;
|
|
711
|
+
_user;
|
|
712
|
+
_toolContexts = new Map();
|
|
713
|
+
_agentManager;
|
|
714
|
+
_currentStreamingMessage = null;
|
|
715
|
+
_nameChanged = new Signal(this);
|
|
716
|
+
_contentsManager;
|
|
717
|
+
_autosave = false;
|
|
718
|
+
_autosaveChanged = new Signal(this);
|
|
719
|
+
_autosaveDebouncer;
|
|
720
|
+
_title = null;
|
|
721
|
+
_titleChanged = new Signal(this);
|
|
722
|
+
}
|
|
723
|
+
var Private;
|
|
724
|
+
(function (Private) {
|
|
725
|
+
const isPlainObject = (value) => {
|
|
726
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
727
|
+
};
|
|
728
|
+
const isDisplayOutput = (value) => {
|
|
729
|
+
if (!isPlainObject(value)) {
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
const output = value;
|
|
733
|
+
return (nbformat.isDisplayData(output) ||
|
|
734
|
+
nbformat.isDisplayUpdate(output) ||
|
|
735
|
+
nbformat.isExecuteResult(output));
|
|
736
|
+
};
|
|
737
|
+
const toMimeBundle = (value, trustedMimeTypes) => {
|
|
738
|
+
const data = value.data;
|
|
739
|
+
if (!isPlainObject(data) || Object.keys(data).length === 0) {
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
return {
|
|
743
|
+
data: data,
|
|
744
|
+
...(isPlainObject(value.metadata)
|
|
745
|
+
? { metadata: value.metadata }
|
|
746
|
+
: {}),
|
|
747
|
+
// MIME auto-rendering only runs for explicitly configured command IDs.
|
|
748
|
+
// Trust handling is configurable to keep risky MIME execution opt-in.
|
|
749
|
+
...(Object.keys(data).some(m => trustedMimeTypes.has(m))
|
|
750
|
+
? { trusted: true }
|
|
751
|
+
: {})
|
|
752
|
+
};
|
|
753
|
+
};
|
|
754
|
+
/**
|
|
755
|
+
* Normalize arbitrary tool payloads into canonical display outputs.
|
|
756
|
+
*
|
|
757
|
+
* Tool outputs are not guaranteed to be raw Jupyter IOPub messages; they are
|
|
758
|
+
* often wrapped objects (for example `{ success, result: { outputs: [...] } }`).
|
|
759
|
+
*/
|
|
760
|
+
const toDisplayOutputs = (value) => {
|
|
761
|
+
if (isDisplayOutput(value)) {
|
|
762
|
+
return [value];
|
|
763
|
+
}
|
|
764
|
+
if (Array.isArray(value)) {
|
|
765
|
+
return value.filter(isDisplayOutput);
|
|
766
|
+
}
|
|
767
|
+
if (!isPlainObject(value)) {
|
|
768
|
+
return [];
|
|
769
|
+
}
|
|
770
|
+
if (Array.isArray(value.outputs)) {
|
|
771
|
+
return value.outputs.filter(isDisplayOutput);
|
|
772
|
+
}
|
|
773
|
+
if ('result' in value) {
|
|
774
|
+
return toDisplayOutputs(value.result);
|
|
775
|
+
}
|
|
776
|
+
return [];
|
|
777
|
+
};
|
|
661
778
|
/**
|
|
662
|
-
*
|
|
779
|
+
* Extract rendermime-ready mime bundles from arbitrary tool results.
|
|
780
|
+
*/
|
|
781
|
+
function extractMimeBundlesFromUnknown(content, options = {}) {
|
|
782
|
+
const bundles = [];
|
|
783
|
+
const outputs = toDisplayOutputs(content);
|
|
784
|
+
const trustedMimeTypes = new Set(options.trustedMimeTypes ?? []);
|
|
785
|
+
for (const output of outputs) {
|
|
786
|
+
const bundle = toMimeBundle(output, trustedMimeTypes);
|
|
787
|
+
if (bundle) {
|
|
788
|
+
bundles.push(bundle);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return bundles;
|
|
792
|
+
}
|
|
793
|
+
Private.extractMimeBundlesFromUnknown = extractMimeBundlesFromUnknown;
|
|
794
|
+
function formatToolOutput(outputData) {
|
|
795
|
+
if (typeof outputData === 'string') {
|
|
796
|
+
return outputData;
|
|
797
|
+
}
|
|
798
|
+
try {
|
|
799
|
+
return JSON.stringify(outputData, null, 2);
|
|
800
|
+
}
|
|
801
|
+
catch {
|
|
802
|
+
return '[Complex object - cannot serialize]';
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
Private.formatToolOutput = formatToolOutput;
|
|
806
|
+
/**
|
|
807
|
+
* Processes file attachments and returns text contents and binary parts separately.
|
|
663
808
|
* @param attachments Array of file attachments to process
|
|
664
|
-
* @
|
|
809
|
+
* @param documentManager Optional document manager for file operations
|
|
810
|
+
* @returns Text contents and binary parts
|
|
665
811
|
*/
|
|
666
|
-
async
|
|
667
|
-
const
|
|
812
|
+
async function processAttachments(attachments, documentManager) {
|
|
813
|
+
const textContents = [];
|
|
814
|
+
const binaryParts = [];
|
|
815
|
+
if (!documentManager) {
|
|
816
|
+
return { textContents, binaryParts };
|
|
817
|
+
}
|
|
668
818
|
for (const attachment of attachments) {
|
|
669
819
|
try {
|
|
670
820
|
if (attachment.type === 'notebook' && attachment.cells?.length) {
|
|
671
|
-
const cellContents = await
|
|
821
|
+
const cellContents = await readNotebookCells(attachment, documentManager);
|
|
672
822
|
if (cellContents) {
|
|
673
|
-
|
|
823
|
+
textContents.push(cellContents);
|
|
674
824
|
}
|
|
675
825
|
}
|
|
676
826
|
else {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
827
|
+
let mimetype = attachment.mimetype;
|
|
828
|
+
const fileExtension = PathExt.extname(attachment.value).toLowerCase();
|
|
829
|
+
// Fetch mimetype from server metadata if not provided
|
|
830
|
+
if (!mimetype) {
|
|
831
|
+
try {
|
|
832
|
+
const diskModel = await documentManager.services.contents.get(attachment.value, { content: false });
|
|
833
|
+
mimetype = diskModel?.mimetype;
|
|
834
|
+
}
|
|
835
|
+
catch (e) {
|
|
836
|
+
console.warn(`Failed to fetch metadata for ${attachment.value}:`, e);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
if (mimetype?.startsWith('image/')) {
|
|
840
|
+
const data = await readBinaryAttachment(attachment, documentManager);
|
|
841
|
+
if (data) {
|
|
842
|
+
binaryParts.push({
|
|
843
|
+
type: 'image',
|
|
844
|
+
image: data,
|
|
845
|
+
mediaType: mimetype
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
else if (mimetype === 'application/pdf') {
|
|
850
|
+
const data = await readBinaryAttachment(attachment, documentManager);
|
|
851
|
+
if (data) {
|
|
852
|
+
binaryParts.push({
|
|
853
|
+
type: 'file',
|
|
854
|
+
data,
|
|
855
|
+
mediaType: mimetype,
|
|
856
|
+
filename: PathExt.basename(attachment.value)
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
else {
|
|
861
|
+
const fileContent = await readFileAttachment(attachment, documentManager);
|
|
862
|
+
if (fileContent) {
|
|
863
|
+
const language = fileExtension === '.ipynb' ||
|
|
864
|
+
mimetype === 'application/x-ipynb+json'
|
|
865
|
+
? 'json'
|
|
866
|
+
: '';
|
|
867
|
+
textContents.push(`**File: ${attachment.value}**\n\`\`\`${language}\n${fileContent}\n\`\`\``);
|
|
868
|
+
}
|
|
682
869
|
}
|
|
683
870
|
}
|
|
684
871
|
}
|
|
685
872
|
catch (error) {
|
|
686
873
|
console.warn(`Failed to read attachment ${attachment.value}:`, error);
|
|
687
|
-
|
|
874
|
+
textContents.push(`**File: ${attachment.value}** (Could not read file)`);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
return { textContents, binaryParts };
|
|
878
|
+
}
|
|
879
|
+
Private.processAttachments = processAttachments;
|
|
880
|
+
/**
|
|
881
|
+
* Reads a binary attachment and returns its base64-encoded content.
|
|
882
|
+
* @param attachment The attachment to read
|
|
883
|
+
* @param documentManager Optional document manager for file operations
|
|
884
|
+
* @returns Base64 string or null if unable to read
|
|
885
|
+
*/
|
|
886
|
+
async function readBinaryAttachment(attachment, documentManager) {
|
|
887
|
+
if (!documentManager) {
|
|
888
|
+
return null;
|
|
889
|
+
}
|
|
890
|
+
try {
|
|
891
|
+
const diskModel = await documentManager.services.contents.get(attachment.value, { content: true });
|
|
892
|
+
if (diskModel?.content && diskModel.format === 'base64') {
|
|
893
|
+
// Strip whitespace/newlines
|
|
894
|
+
return diskModel.content.replace(/\s/g, '');
|
|
688
895
|
}
|
|
896
|
+
return null;
|
|
897
|
+
}
|
|
898
|
+
catch (error) {
|
|
899
|
+
console.warn(`Failed to read binary attachment ${attachment.value}:`, error);
|
|
900
|
+
return null;
|
|
689
901
|
}
|
|
690
|
-
return contents;
|
|
691
902
|
}
|
|
903
|
+
Private.readBinaryAttachment = readBinaryAttachment;
|
|
692
904
|
/**
|
|
693
905
|
* Reads the content of a notebook cell.
|
|
694
906
|
* @param attachment The notebook attachment to read
|
|
907
|
+
* @param documentManager Optional document manager for file operations
|
|
695
908
|
* @returns Cell content as string or null if unable to read
|
|
696
909
|
*/
|
|
697
|
-
async
|
|
698
|
-
if (attachment.type !== 'notebook' ||
|
|
910
|
+
async function readNotebookCells(attachment, documentManager) {
|
|
911
|
+
if (attachment.type !== 'notebook' ||
|
|
912
|
+
!attachment.cells ||
|
|
913
|
+
!documentManager) {
|
|
699
914
|
return null;
|
|
700
915
|
}
|
|
701
916
|
try {
|
|
702
917
|
// Try reading from live notebook if open
|
|
703
|
-
const widget =
|
|
918
|
+
const widget = documentManager.findWidget(attachment.value);
|
|
704
919
|
let cellData;
|
|
705
920
|
let kernelLang = 'text';
|
|
706
921
|
const ymodel = widget?.context.model.sharedModel;
|
|
@@ -714,7 +929,7 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
714
929
|
}
|
|
715
930
|
else {
|
|
716
931
|
// Fallback: reading from disk
|
|
717
|
-
const model = await
|
|
932
|
+
const model = await documentManager.services.contents.get(attachment.value);
|
|
718
933
|
if (!model || model.type !== 'notebook') {
|
|
719
934
|
return null;
|
|
720
935
|
}
|
|
@@ -829,19 +1044,22 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
829
1044
|
return null;
|
|
830
1045
|
}
|
|
831
1046
|
}
|
|
1047
|
+
Private.readNotebookCells = readNotebookCells;
|
|
832
1048
|
/**
|
|
833
1049
|
* Reads the content of a file attachment.
|
|
834
1050
|
* @param attachment The file attachment to read
|
|
1051
|
+
* @param documentManager Optional document manager for file operations
|
|
835
1052
|
* @returns File content as string or null if unable to read
|
|
836
1053
|
*/
|
|
837
|
-
async
|
|
1054
|
+
async function readFileAttachment(attachment, documentManager) {
|
|
838
1055
|
// Handle both 'file' and 'notebook' types since both have a 'value' path
|
|
839
|
-
if (attachment.type !== 'file' && attachment.type !== 'notebook')
|
|
1056
|
+
if ((attachment.type !== 'file' && attachment.type !== 'notebook') ||
|
|
1057
|
+
!documentManager) {
|
|
840
1058
|
return null;
|
|
841
1059
|
}
|
|
842
1060
|
try {
|
|
843
1061
|
// Try reading from an open widget first
|
|
844
|
-
const widget =
|
|
1062
|
+
const widget = documentManager.findWidget(attachment.value);
|
|
845
1063
|
if (widget && widget.context && widget.context.model) {
|
|
846
1064
|
const model = widget.context.model;
|
|
847
1065
|
const ymodel = model.sharedModel;
|
|
@@ -853,7 +1071,7 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
853
1071
|
}
|
|
854
1072
|
}
|
|
855
1073
|
// If not open, load from disk
|
|
856
|
-
const diskModel = await
|
|
1074
|
+
const diskModel = await documentManager.services.contents.get(attachment.value);
|
|
857
1075
|
if (!diskModel?.content) {
|
|
858
1076
|
return null;
|
|
859
1077
|
}
|
|
@@ -879,99 +1097,5 @@ export class AIChatModel extends AbstractChatModel {
|
|
|
879
1097
|
return null;
|
|
880
1098
|
}
|
|
881
1099
|
}
|
|
882
|
-
|
|
883
|
-
_settingsModel;
|
|
884
|
-
_user;
|
|
885
|
-
_toolContexts = new Map();
|
|
886
|
-
_agentManager;
|
|
887
|
-
_currentStreamingMessage = null;
|
|
888
|
-
_nameChanged = new Signal(this);
|
|
889
|
-
_contentsManager;
|
|
890
|
-
_autosave = false;
|
|
891
|
-
_autosaveChanged = new Signal(this);
|
|
892
|
-
_autosaveDebouncer;
|
|
893
|
-
}
|
|
894
|
-
var Private;
|
|
895
|
-
(function (Private) {
|
|
896
|
-
const isPlainObject = (value) => {
|
|
897
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
898
|
-
};
|
|
899
|
-
const isDisplayOutput = (value) => {
|
|
900
|
-
if (!isPlainObject(value)) {
|
|
901
|
-
return false;
|
|
902
|
-
}
|
|
903
|
-
const output = value;
|
|
904
|
-
return (nbformat.isDisplayData(output) ||
|
|
905
|
-
nbformat.isDisplayUpdate(output) ||
|
|
906
|
-
nbformat.isExecuteResult(output));
|
|
907
|
-
};
|
|
908
|
-
const toMimeBundle = (value, trustedMimeTypes) => {
|
|
909
|
-
const data = value.data;
|
|
910
|
-
if (!isPlainObject(data) || Object.keys(data).length === 0) {
|
|
911
|
-
return null;
|
|
912
|
-
}
|
|
913
|
-
return {
|
|
914
|
-
data: data,
|
|
915
|
-
...(isPlainObject(value.metadata)
|
|
916
|
-
? { metadata: value.metadata }
|
|
917
|
-
: {}),
|
|
918
|
-
// MIME auto-rendering only runs for explicitly configured command IDs.
|
|
919
|
-
// Trust handling is configurable to keep risky MIME execution opt-in.
|
|
920
|
-
...(Object.keys(data).some(m => trustedMimeTypes.has(m))
|
|
921
|
-
? { trusted: true }
|
|
922
|
-
: {})
|
|
923
|
-
};
|
|
924
|
-
};
|
|
925
|
-
/**
|
|
926
|
-
* Normalize arbitrary tool payloads into canonical display outputs.
|
|
927
|
-
*
|
|
928
|
-
* Tool outputs are not guaranteed to be raw Jupyter IOPub messages; they are
|
|
929
|
-
* often wrapped objects (for example `{ success, result: { outputs: [...] } }`).
|
|
930
|
-
*/
|
|
931
|
-
const toDisplayOutputs = (value) => {
|
|
932
|
-
if (isDisplayOutput(value)) {
|
|
933
|
-
return [value];
|
|
934
|
-
}
|
|
935
|
-
if (Array.isArray(value)) {
|
|
936
|
-
return value.filter(isDisplayOutput);
|
|
937
|
-
}
|
|
938
|
-
if (!isPlainObject(value)) {
|
|
939
|
-
return [];
|
|
940
|
-
}
|
|
941
|
-
if (Array.isArray(value.outputs)) {
|
|
942
|
-
return value.outputs.filter(isDisplayOutput);
|
|
943
|
-
}
|
|
944
|
-
if ('result' in value) {
|
|
945
|
-
return toDisplayOutputs(value.result);
|
|
946
|
-
}
|
|
947
|
-
return [];
|
|
948
|
-
};
|
|
949
|
-
/**
|
|
950
|
-
* Extract rendermime-ready mime bundles from arbitrary tool results.
|
|
951
|
-
*/
|
|
952
|
-
function extractMimeBundlesFromUnknown(content, options = {}) {
|
|
953
|
-
const bundles = [];
|
|
954
|
-
const outputs = toDisplayOutputs(content);
|
|
955
|
-
const trustedMimeTypes = new Set(options.trustedMimeTypes ?? []);
|
|
956
|
-
for (const output of outputs) {
|
|
957
|
-
const bundle = toMimeBundle(output, trustedMimeTypes);
|
|
958
|
-
if (bundle) {
|
|
959
|
-
bundles.push(bundle);
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
return bundles;
|
|
963
|
-
}
|
|
964
|
-
Private.extractMimeBundlesFromUnknown = extractMimeBundlesFromUnknown;
|
|
965
|
-
function formatToolOutput(outputData) {
|
|
966
|
-
if (typeof outputData === 'string') {
|
|
967
|
-
return outputData;
|
|
968
|
-
}
|
|
969
|
-
try {
|
|
970
|
-
return JSON.stringify(outputData, null, 2);
|
|
971
|
-
}
|
|
972
|
-
catch {
|
|
973
|
-
return '[Complex object - cannot serialize]';
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
Private.formatToolOutput = formatToolOutput;
|
|
1100
|
+
Private.readFileAttachment = readFileAttachment;
|
|
977
1101
|
})(Private || (Private = {}));
|
|
@@ -21,7 +21,7 @@ export function clearItem(translator) {
|
|
|
21
21
|
return {
|
|
22
22
|
element: (props) => {
|
|
23
23
|
const { model } = props;
|
|
24
|
-
const clearMessages = () => model.chatContext.clearMessages();
|
|
24
|
+
const clearMessages = async () => await model.chatContext.clearMessages();
|
|
25
25
|
const clearProps = {
|
|
26
26
|
...props,
|
|
27
27
|
clearMessages,
|