diff --git a/js/src/client.ts b/js/src/client.ts index b3f0a1e03..a0ed04506 100644 --- a/js/src/client.ts +++ b/js/src/client.ts @@ -41,6 +41,8 @@ import { UpdateExamplesResponse, RawExample, AttachmentInfo, + AttachmentDescription, + AttachmentData, } from "./schemas.js"; import { convertLangChainMessageToExample, @@ -1128,9 +1130,17 @@ export class Client implements LangSmithTracingClientInterface { const attachments = allAttachments[payload.id]; if (attachments) { delete allAttachments[payload.id]; - for (const [name, [contentType, content]] of Object.entries( - attachments - )) { + for (const [name, attachment] of Object.entries(attachments)) { + let contentType: string; + let content: AttachmentData; + + if (Array.isArray(attachment)) { + [contentType, content] = attachment; + } else { + contentType = attachment.mimeType; + content = attachment.data; + } + // Validate that the attachment name doesn't contain a '.' if (name.includes(".")) { console.warn( @@ -3947,9 +3957,16 @@ export class Client implements LangSmithTracingClientInterface { // Add attachments if present if (example.attachments) { - for (const [name, [mimeType, data]] of Object.entries( - example.attachments - )) { + for (const [name, attachment] of Object.entries(example.attachments)) { + let mimeType: string; + let data: AttachmentData; + + if (Array.isArray(attachment)) { + [mimeType, data] = attachment; + } else { + mimeType = attachment.mimeType; + data = attachment.data; + } const attachmentBlob = new Blob([data], { type: `${mimeType}; length=${data.byteLength}`, }); @@ -4038,9 +4055,16 @@ export class Client implements LangSmithTracingClientInterface { // Add attachments if present if (example.attachments) { - for (const [name, [mimeType, data]] of Object.entries( - example.attachments - )) { + for (const [name, attachment] of Object.entries(example.attachments)) { + let mimeType: string; + let data: AttachmentData; + + if (Array.isArray(attachment)) { + [mimeType, data] = attachment; + } else { + mimeType = attachment.mimeType; + data = attachment.data; + } const attachmentBlob = new Blob([data], { type: `${mimeType}; length=${data.byteLength}`, }); diff --git a/js/src/schemas.ts b/js/src/schemas.ts index af7848e59..9fe4e8e16 100644 --- a/js/src/schemas.ts +++ b/js/src/schemas.ts @@ -68,7 +68,15 @@ export interface AttachmentInfo { } export type AttachmentData = Uint8Array | ArrayBuffer; -export type Attachments = Record; + +export type AttachmentDescription = { + mimeType: string; + data: AttachmentData; +}; +export type Attachments = Record< + string, + [string, AttachmentData] | AttachmentDescription +>; /** * A run can represent either a trace (root run) diff --git a/js/src/tests/client.int.test.ts b/js/src/tests/client.int.test.ts index de8710e1f..137c98c79 100644 --- a/js/src/tests/client.int.test.ts +++ b/js/src/tests/client.int.test.ts @@ -1278,7 +1278,10 @@ test("upload examples multipart", async () => { inputs: { text: "hello world" }, // check that passing no outputs works fine attachments: { - test_file: ["image/png", fs.readFileSync(pathname)], + test_file: { + mimeType: "image/png", + data: fs.readFileSync(pathname), + }, }, };