(record: WorkspaceFileRecord)
| 285 | * nests underneath for the image-resize path. |
| 286 | */ |
| 287 | export async function readFileRecord(record: WorkspaceFileRecord): Promise<FileReadResult | null> { |
| 288 | const startedAt = Date.now() |
| 289 | const result = await getVfsTracer().startActiveSpan( |
| 290 | TraceSpan.CopilotVfsReadFile, |
| 291 | { |
| 292 | attributes: { |
| 293 | [TraceAttr.CopilotVfsFileName]: record.name, |
| 294 | [TraceAttr.CopilotVfsFileMediaType]: record.type, |
| 295 | [TraceAttr.CopilotVfsFileSizeBytes]: record.size, |
| 296 | [TraceAttr.CopilotVfsFileExtension]: getExtension(record.name), |
| 297 | }, |
| 298 | }, |
| 299 | async (span) => { |
| 300 | try { |
| 301 | if (isImageFileType(record.type)) { |
| 302 | span.setAttribute(TraceAttr.CopilotVfsReadPath, CopilotVfsReadPath.Image) |
| 303 | const originalBuffer = await fetchWorkspaceFileBuffer(record) |
| 304 | const prepared = await prepareImageForVision(originalBuffer, record.type) |
| 305 | if (!prepared) { |
| 306 | span.setAttribute(TraceAttr.CopilotVfsReadOutcome, CopilotVfsReadOutcome.ImageTooLarge) |
| 307 | return { |
| 308 | content: `[Image too large: ${record.name} (${(record.size / 1024 / 1024).toFixed(1)}MB, limit 5MB after resize/compression)]`, |
| 309 | totalLines: 1, |
| 310 | } |
| 311 | } |
| 312 | const sizeKb = (prepared.buffer.length / 1024).toFixed(1) |
| 313 | const resizeNote = prepared.resized ? ', resized for vision' : '' |
| 314 | span.setAttributes({ |
| 315 | [TraceAttr.CopilotVfsReadOutcome]: CopilotVfsReadOutcome.ImagePrepared, |
| 316 | [TraceAttr.CopilotVfsReadOutputBytes]: prepared.buffer.length, |
| 317 | [TraceAttr.CopilotVfsReadOutputMediaType]: prepared.mediaType, |
| 318 | [TraceAttr.CopilotVfsReadImageResized]: prepared.resized, |
| 319 | }) |
| 320 | return { |
| 321 | content: `Image: ${record.name} (${sizeKb}KB, ${prepared.mediaType}${resizeNote})`, |
| 322 | totalLines: 1, |
| 323 | attachment: { |
| 324 | type: 'image', |
| 325 | name: record.name, |
| 326 | source: { |
| 327 | type: 'base64' as const, |
| 328 | media_type: prepared.mediaType, |
| 329 | data: prepared.buffer.toString('base64'), |
| 330 | }, |
| 331 | }, |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | if (isReadableType(record.type)) { |
| 336 | span.setAttribute(TraceAttr.CopilotVfsReadPath, CopilotVfsReadPath.Text) |
| 337 | if (record.size > MAX_TEXT_READ_BYTES) { |
| 338 | span.setAttribute(TraceAttr.CopilotVfsReadOutcome, CopilotVfsReadOutcome.TextTooLarge) |
| 339 | return { |
| 340 | content: `[File too large to display inline: ${record.name} (${record.size} bytes, limit ${MAX_TEXT_READ_BYTES})]`, |
| 341 | totalLines: 1, |
| 342 | } |
| 343 | } |
| 344 |
no test coverage detected