process-images.ts 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. import * as vscode from "vscode"
  2. import fs from "fs/promises"
  3. import sharp from "sharp"
  4. export async function selectAndProcessImages(): Promise<string[]> {
  5. const options: vscode.OpenDialogOptions = {
  6. canSelectMany: true,
  7. openLabel: "Select",
  8. filters: {
  9. Images: ["png", "jpg", "jpeg", "gif", "webp", "tiff", "avif", "svg"], // sharp can convert these to webp which both anthropic and openrouter support
  10. },
  11. }
  12. const fileUris = await vscode.window.showOpenDialog(options)
  13. if (!fileUris || fileUris.length === 0) {
  14. return []
  15. }
  16. return await Promise.all(
  17. fileUris.map(async (uri) => {
  18. const imagePath = uri.fsPath
  19. const originalBuffer = await fs.readFile(imagePath)
  20. return convertToWebpBase64(originalBuffer)
  21. })
  22. )
  23. }
  24. export async function processPastedImages(base64Strings: string[]): Promise<string[]> {
  25. return await Promise.all(
  26. base64Strings.map(async (base64) => {
  27. const buffer = Buffer.from(base64, "base64")
  28. return convertToWebpBase64(buffer)
  29. })
  30. )
  31. }
  32. async function convertToWebpBase64(buffer: Buffer): Promise<string> {
  33. const processedBuffer = await sharp(buffer)
  34. /*
  35. Anthropic docs recommendations:
  36. - To improve time-to-first-token resize images to no more than 1.15 megapixels (and within 1568 pixels in both dimensions)
  37. - WebP is a newer image format that's more efficient than PNG and JPEG, so ideal for keeping token usage low. (ive seen the following compression decrease size by 10x)
  38. */
  39. .resize(1568, 1568, {
  40. fit: "inside", // maintain aspect ratio
  41. withoutEnlargement: true, // don't enlarge smaller images
  42. })
  43. .webp({
  44. // NOTE: consider increasing effort from 4 to 6 (max), this may increase processing time by up to ~500ms
  45. quality: 80,
  46. })
  47. .toBuffer()
  48. const base64 = processedBuffer.toString("base64")
  49. // console.log({
  50. // originalSize: buffer.length,
  51. // processedSize: processedBuffer.length,
  52. // base64,
  53. // })
  54. return base64
  55. }