6
6
import { CancellationToken } from '../../../../../base/common/cancellation.js' ;
7
7
import { MarkdownString } from '../../../../../base/common/htmlContent.js' ;
8
8
import { ResourceSet } from '../../../../../base/common/map.js' ;
9
+ import { extname } from '../../../../../base/common/path.js' ;
9
10
import { URI } from '../../../../../base/common/uri.js' ;
10
11
import { localize } from '../../../../../nls.js' ;
11
12
import { IFileService } from '../../../../../platform/files/common/files.js' ;
12
13
import { IWebContentExtractorService } from '../../../../../platform/webContentExtractor/common/webContentExtractor.js' ;
13
- import { CountTokensCallback , IPreparedToolInvocation , IToolData , IToolImpl , IToolInvocation , IToolInvocationPreparationContext , IToolResult , IToolResultTextPart , ToolDataSource , ToolProgress } from '../../common/languageModelToolsService.js' ;
14
- import { InternalFetchWebPageToolId } from '../../common/tools/tools.js' ;
15
14
import { detectEncodingFromBuffer } from '../../../../services/textfile/common/encoding.js' ;
15
+ import { ChatImageMimeType } from '../../common/languageModels.js' ;
16
+ import { CountTokensCallback , IPreparedToolInvocation , IToolData , IToolImpl , IToolInvocation , IToolInvocationPreparationContext , IToolResult , IToolResultDataPart , IToolResultTextPart , ToolDataSource , ToolProgress } from '../../common/languageModelToolsService.js' ;
17
+ import { InternalFetchWebPageToolId } from '../../common/tools/tools.js' ;
16
18
17
19
export const FetchWebPageToolData : IToolData = {
18
20
id : InternalFetchWebPageToolId ,
@@ -64,22 +66,35 @@ export class FetchWebPageTool implements IToolImpl {
64
66
const webContents = webUris . size > 0 ? await this . _readerModeService . extract ( [ ...webUris . values ( ) ] ) : [ ] ;
65
67
66
68
// Get contents from file URIs
67
- const fileContents : ( string | undefined ) [ ] = [ ] ;
69
+ const fileContents : ( string | IToolResultDataPart | undefined ) [ ] = [ ] ;
68
70
const successfulFileUris : URI [ ] = [ ] ;
69
71
for ( const uri of fileUris . values ( ) ) {
70
72
try {
71
73
const fileContent = await this . _fileService . readFile ( uri , undefined , token ) ;
72
74
73
- // Check if the content is binary
74
- const detected = detectEncodingFromBuffer ( { buffer : fileContent . value , bytesRead : fileContent . value . byteLength } ) ;
75
-
76
- if ( detected . seemsBinary ) {
77
- // For binary files, return a message indicating they're not supported
78
- // We do this for now until the tools that leverage this internal tool can support binary content
79
- fileContents . push ( localize ( 'fetchWebPage.binaryNotSupported' , 'Binary files are not supported at the moment.' ) ) ;
75
+ // Check if this is a supported image type first
76
+ const imageMimeType = this . _getSupportedImageMimeType ( uri ) ;
77
+ if ( imageMimeType ) {
78
+ // For supported image files, return as IToolResultDataPart
79
+ fileContents . push ( {
80
+ kind : 'data' ,
81
+ value : {
82
+ mimeType : imageMimeType ,
83
+ data : fileContent . value
84
+ }
85
+ } ) ;
80
86
} else {
81
- // For text files, convert to string
82
- fileContents . push ( fileContent . value . toString ( ) ) ;
87
+ // Check if the content is binary
88
+ const detected = detectEncodingFromBuffer ( { buffer : fileContent . value , bytesRead : fileContent . value . byteLength } ) ;
89
+
90
+ if ( detected . seemsBinary ) {
91
+ // For binary files, return a message indicating they're not supported
92
+ // We do this for now until the tools that leverage this internal tool can support binary content
93
+ fileContents . push ( localize ( 'fetchWebPage.binaryNotSupported' , 'Binary files are not supported at the moment.' ) ) ;
94
+ } else {
95
+ // For text files, convert to string
96
+ fileContents . push ( fileContent . value . toString ( ) ) ;
97
+ }
83
98
}
84
99
85
100
successfulFileUris . push ( uri ) ;
@@ -90,7 +105,7 @@ export class FetchWebPageTool implements IToolImpl {
90
105
}
91
106
92
107
// Build results array in original order
93
- const results : ( string | undefined ) [ ] = [ ] ;
108
+ const results : ( string | IToolResultDataPart | undefined ) [ ] = [ ] ;
94
109
let webIndex = 0 ;
95
110
let fileIndex = 0 ;
96
111
for ( const url of urls ) {
@@ -112,8 +127,7 @@ export class FetchWebPageTool implements IToolImpl {
112
127
113
128
return {
114
129
content : this . _getPromptPartsForResults ( results ) ,
115
- // Have multiple results show in the dropdown
116
- toolResultDetails : actuallyValidUris . length > 1 ? actuallyValidUris : undefined
130
+ toolResultDetails : actuallyValidUris
117
131
} ;
118
132
}
119
133
@@ -160,8 +174,8 @@ export class FetchWebPageTool implements IToolImpl {
160
174
invocationMessage . appendMarkdown ( localize ( 'fetchWebPage.invocationMessage.plural' , 'Fetching {0} resources' , valid . length ) ) ;
161
175
} else if ( valid . length === 1 ) {
162
176
const url = valid [ 0 ] . toString ( ) ;
163
- // If the URL is too long, show it as a link... otherwise, show it as plain text
164
- if ( url . length > 400 ) {
177
+ // If the URL is too long or it's a file url , show it as a link... otherwise, show it as plain text
178
+ if ( url . length > 400 || validFileUris . length === 1 ) {
165
179
pastTenseMessage . appendMarkdown ( localize ( {
166
180
key : 'fetchWebPage.pastTenseMessageResult.singularAsLink' ,
167
181
comment : [
@@ -228,10 +242,41 @@ export class FetchWebPageTool implements IToolImpl {
228
242
return { webUris, fileUris, invalidUris } ;
229
243
}
230
244
231
- private _getPromptPartsForResults ( results : ( string | undefined ) [ ] ) : IToolResultTextPart [ ] {
232
- return results . map ( value => ( {
233
- kind : 'text' ,
234
- value : value || localize ( 'fetchWebPage.invalidUrl' , 'Invalid URL' )
235
- } ) ) ;
245
+ private _getPromptPartsForResults ( results : ( string | IToolResultDataPart | undefined ) [ ] ) : ( IToolResultTextPart | IToolResultDataPart ) [ ] {
246
+ return results . map ( value => {
247
+ if ( ! value ) {
248
+ return {
249
+ kind : 'text' ,
250
+ value : localize ( 'fetchWebPage.invalidUrl' , 'Invalid URL' )
251
+ } ;
252
+ } else if ( typeof value === 'string' ) {
253
+ return {
254
+ kind : 'text' ,
255
+ value : value
256
+ } ;
257
+ } else {
258
+ // This is an IToolResultDataPart
259
+ return value ;
260
+ }
261
+ } ) ;
262
+ }
263
+
264
+ private _getSupportedImageMimeType ( uri : URI ) : ChatImageMimeType | undefined {
265
+ const ext = extname ( uri . path ) . toLowerCase ( ) ;
266
+ switch ( ext ) {
267
+ case '.png' :
268
+ return ChatImageMimeType . PNG ;
269
+ case '.jpg' :
270
+ case '.jpeg' :
271
+ return ChatImageMimeType . JPEG ;
272
+ case '.gif' :
273
+ return ChatImageMimeType . GIF ;
274
+ case '.webp' :
275
+ return ChatImageMimeType . WEBP ;
276
+ case '.bmp' :
277
+ return ChatImageMimeType . BMP ;
278
+ default :
279
+ return undefined ;
280
+ }
236
281
}
237
282
}
0 commit comments