From 221ba5f8ea06450d6df9383277483479d5a8cc5f Mon Sep 17 00:00:00 2001 From: Damien BUTY Date: Tue, 16 Jul 2024 12:18:20 +0200 Subject: [PATCH 1/6] wip --- src/api.ts | 81 +++++++++++++++++++++++++++++++++++---- src/openai.ts | 25 +++++------- tests/api.test.ts | 45 +--------------------- tests/attachments.test.ts | 70 +++++++++++++++++++++++++++++++++ 4 files changed, 155 insertions(+), 66 deletions(-) create mode 100644 tests/attachments.test.ts diff --git a/src/api.ts b/src/api.ts index dd4426c..f8646a6 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,6 +1,7 @@ import axios, { AxiosError } from 'axios'; import FormData from 'form-data'; import { createReadStream } from 'fs'; +import { ReadStream } from 'fs'; import { v4 as uuidv4 } from 'uuid'; import { LiteralClient } from '.'; @@ -21,6 +22,7 @@ import { PersistedGeneration } from './generation'; import { + Attachment, CleanThreadFields, Dataset, DatasetExperiment, @@ -327,6 +329,28 @@ function addGenerationsToDatasetQueryBuilder(generationIds: string[]) { `; } +type UploadFileBaseParams = { + id?: Maybe; + threadId?: string; + mime?: Maybe; +}; +type UploadFileParamsWithPath = UploadFileBaseParams & { + path: string; +}; +type UploadFileParamsWithContent = UploadFileBaseParams & { + content: + | ReadableStream + | ReadStream + | Buffer + | File + | Blob + | ArrayBuffer; +}; +type CreateAttachmentParams = { + name?: string; + metadata?: Maybe>; +}; + export class API { /** @ignore */ private client: LiteralClient; @@ -596,19 +620,25 @@ export class API { * @returns An object containing the `objectKey` of the uploaded file and the signed `url`, or `null` values if the upload fails. * @throws {Error} Throws an error if neither `content` nor `path` is provided, or if the server response is invalid. */ + + async uploadFile(params: UploadFileParamsWithContent): Promise<{ + objectKey: Maybe; + url: Maybe; + }>; + async uploadFile(params: UploadFileParamsWithPath): Promise<{ + objectKey: Maybe; + url: Maybe; + }>; async uploadFile({ content, path, id, threadId, mime - }: { - content?: Maybe; - path?: Maybe; - id?: Maybe; - threadId: string; - mime?: Maybe; - }) { + }: UploadFileParamsWithContent & UploadFileParamsWithPath): Promise<{ + objectKey: Maybe; + url: Maybe; + }> { if (!content && !path) { throw new Error('Either content or path must be provided'); } @@ -662,6 +692,13 @@ export class API { }; } + console.log({ + method, + url, + headers + // data: uploadType === 'raw' ? content || createReadStream(path!) : formData + }); + try { await axios({ method, @@ -673,11 +710,41 @@ export class API { return { objectKey, url: signedUrl }; } catch (e) { + console.error(e); console.error(`Failed to upload file: ${e}`); return { objectKey: null, url: null }; } } + async createAttachment( + params: UploadFileParamsWithContent & CreateAttachmentParams + ): Promise; + async createAttachment( + params: UploadFileParamsWithPath & CreateAttachmentParams + ): Promise; + async createAttachment( + params: UploadFileParamsWithContent & + UploadFileParamsWithPath & + CreateAttachmentParams + ): Promise { + if (params.content instanceof Blob) { + params.content = Buffer.from(await params.content.arrayBuffer()); + } + if (params.content instanceof ArrayBuffer) { + params.content = Buffer.from(params.content); + } + + const { objectKey, url } = await this.uploadFile(params); + + return new Attachment({ + name: params.name, + objectKey, + mime: params.mime, + metadata: params.metadata, + url + }); + } + // Generation /** * Retrieves a paginated list of Generations based on the provided filters and sorting order. diff --git a/src/openai.ts b/src/openai.ts index 359c135..48c5bec 100644 --- a/src/openai.ts +++ b/src/openai.ts @@ -42,21 +42,16 @@ class OpenAIAssistantSyncer { ); const mime = 'image/png'; - const { objectKey } = await this.client.api.uploadFile({ - threadId: litThreadId, - id: attachmentId, - content: file.body, - mime - }); - - const attachment = new Attachment({ - name: content.image_file.file_id, - id: attachmentId, - objectKey, - mime - }); - - attachments.push(attachment); + if (file.body) { + const attachment = await this.client.api.createAttachment({ + threadId: litThreadId, + id: attachmentId, + content: file.body, + mime + }); + + attachments.push(attachment); + } } else if (content.type === 'text') { output.content += content.text.value; } diff --git a/tests/api.test.ts b/tests/api.test.ts index 490573f..3c436be 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -1,14 +1,7 @@ import 'dotenv/config'; -import { createReadStream } from 'fs'; import { v4 as uuidv4 } from 'uuid'; -import { - Attachment, - ChatGeneration, - Dataset, - LiteralClient, - Score -} from '../src'; +import { ChatGeneration, Dataset, LiteralClient, Score } from '../src'; describe('End to end tests for the SDK', function () { let client: LiteralClient; @@ -336,42 +329,6 @@ describe('End to end tests for the SDK', function () { expect(scores[1].scorer).toBe('openai:gpt-3.5-turbo'); }); - it('should test attachment', async function () { - const thread = await client.thread({ id: uuidv4() }); - // Upload an attachment - const fileStream = createReadStream('./tests/chainlit-logo.png'); - const mime = 'image/png'; - - const { objectKey } = await client.api.uploadFile({ - threadId: thread.id, - content: fileStream, - mime - }); - - const attachment = new Attachment({ - name: 'test', - objectKey, - mime - }); - - const step = await thread - .step({ - name: 'test', - type: 'run', - attachments: [attachment] - }) - .send(); - - await new Promise((resolve) => setTimeout(resolve, 1000)); - - const fetchedStep = await client.api.getStep(step.id!); - expect(fetchedStep?.attachments?.length).toBe(1); - expect(fetchedStep?.attachments![0].objectKey).toBe(objectKey); - expect(fetchedStep?.attachments![0].url).toBeDefined(); - - await client.api.deleteThread(thread.id); - }); - it('should get project id', async () => { const projectId = await client.api.getProjectId(); expect(projectId).toEqual(expect.any(String)); diff --git a/tests/attachments.test.ts b/tests/attachments.test.ts new file mode 100644 index 0000000..203627e --- /dev/null +++ b/tests/attachments.test.ts @@ -0,0 +1,70 @@ +import 'dotenv/config'; +import { createReadStream, readFileSync } from 'fs'; + +import { LiteralClient, Thread } from '../src'; + +const url = process.env.LITERAL_API_URL; +const apiKey = process.env.LITERAL_API_KEY; +if (!url || !apiKey) { + throw new Error('Missing environment variables'); +} +const client = new LiteralClient(apiKey, url); + +const filePath = './tests/chainlit-logo.png'; +const mime = 'image/png'; + +describe('Attachments', () => { + describe('Uploading a file', () => { + const stream = createReadStream(filePath); + const buffer = readFileSync(filePath); + const arrayBuffer = buffer.buffer; + const blob = new Blob([buffer]); + // We wrap the blob in a blob and simulate the structure of a File + const file = new Blob([blob], { type: 'image/jpeg' }); + + let thread: Thread; + beforeAll(async () => { + thread = await client.thread({ name: 'Attachment test ' }).upsert(); + }); + + it.each([ + { type: 'Stream', content: stream! } + // { type: 'Buffer', content: buffer! }, + // { type: 'ArrayBuffer', content: arrayBuffer! }, + // { type: 'Blob', content: blob! }, + // { type: 'File', content: file! } + ])('handles $type objects', async function ({ type, content }) { + const attachment = await client.api.createAttachment({ + content, + mime, + name: `Attachment ${type}`, + metadata: { type } + }); + + const step = await thread + .step({ + name: `Test ${type}`, + type: 'run', + attachments: [attachment] + }) + .send(); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + const urlWithoutSignature = attachment.url!.split('X-Amz-Signature')[0]; + const fetchedUrlWithoutSignature = + attachment.url!.split('X-Amz-Signature')[0]; + + const fetchedStep = await client.api.getStep(step.id!); + expect(fetchedStep?.attachments?.length).toBe(1); + expect(fetchedStep?.attachments![0].objectKey).toEqual( + attachment.objectKey + ); + expect(fetchedStep?.attachments![0].name).toEqual(attachment.name); + expect(fetchedStep?.attachments![0].metadata).toEqual( + attachment.metadata + ); + expect(urlWithoutSignature).toEqual(fetchedUrlWithoutSignature); + }); + }); +}); From dc0de992d9b5ae5f6b5eb37d18889157668cd3ba Mon Sep 17 00:00:00 2001 From: Damien BUTY Date: Wed, 17 Jul 2024 16:15:16 +0200 Subject: [PATCH 2/6] wip --- src/api.ts | 7 ------- tests/attachments.test.ts | 10 +++++----- tests/chainlit-logo.png | Bin 13175 -> 2957 bytes 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/api.ts b/src/api.ts index f8646a6..4b967bb 100644 --- a/src/api.ts +++ b/src/api.ts @@ -692,13 +692,6 @@ export class API { }; } - console.log({ - method, - url, - headers - // data: uploadType === 'raw' ? content || createReadStream(path!) : formData - }); - try { await axios({ method, diff --git a/tests/attachments.test.ts b/tests/attachments.test.ts index 203627e..f2c0e48 100644 --- a/tests/attachments.test.ts +++ b/tests/attachments.test.ts @@ -28,11 +28,11 @@ describe('Attachments', () => { }); it.each([ - { type: 'Stream', content: stream! } - // { type: 'Buffer', content: buffer! }, - // { type: 'ArrayBuffer', content: arrayBuffer! }, - // { type: 'Blob', content: blob! }, - // { type: 'File', content: file! } + { type: 'Stream', content: stream! }, + { type: 'Buffer', content: buffer! }, + { type: 'ArrayBuffer', content: arrayBuffer! }, + { type: 'Blob', content: blob! }, + { type: 'File', content: file! } ])('handles $type objects', async function ({ type, content }) { const attachment = await client.api.createAttachment({ content, diff --git a/tests/chainlit-logo.png b/tests/chainlit-logo.png index ee5cccba764edd314bcadb953edef4c19bcdddcc..30c4a4f598327dcf67160267760975d3f00d9c54 100644 GIT binary patch literal 2957 zcmV;83v%>{P)+&J<801G)uL_t(|+U;0vdLt_J6eM$`CHETi*Z!ggSYH(+I}x+yXMv(T_&(S1 zy_%kNs!^%8BF}Fx(In5A|>f}bU@zG^LY?OZU~$hh=tdrHLy^$E6w1x z1-Yz5uBV6!3_VaDQ4n=Xq+QoD2jn~iG%k5uiKwi}bT?XsmJYS#vj?(hNDYCb8VyNM zF&^^+U5)d_1A){-udK>|Bjq7hH}}YnZ~pK=zBF$gh37VZi9qbMSlu-i)zTj{k_23k zEB6#nS>X&f8k3@RRVGnP!o5>4E9X}qq|_<0U>Id}qtNsyf}orj^7;d-yKB+zyE~{% zLiY!=YvRoZi4Q=;4U_q@k3Eow20f5TgZ41ad2d+h>%XQ+`87b^0l5^@&wXKWu|6qo zc$r?0u7fYtS|x4>^eY5t<*EmpS|%y`>^BeOy$nR^xh_U%iQDd@T$%5bp19=z?eq7YJ z@<5>F;mP~tElluQx<^!`QcQQbO8^BERYyDy%(9i_t8lfrBPAbwm5E6-ChV;OvH27O z^f%SIeDUO)e8YqCQrs;5Qc4npN*?Q300h&^%+uzF39mls_sFa+91UFLl;e_x%Enh1 zi#s>%URmUs-z8d51jxl~%7f&-xZN6-rBUG141pkntuI$NX+=d+M>C4M`=sHb%$ws^ zS8Brkj;KzVG}U{AtEe6x=Ly~Zsd!a^^QL*e;~Y3!z9?7jG7R(DPcSLq$v!{IFe$B! z{cY9hmFgfpcioy>v~Ory$6`IMjmrPF&YnwE1`Auc@&NaCvu&`BS3QeE6-)LuO?Qx5 zH&5vEy38L@n{o0!k=HEBAbc6iHti$Qju)p2KfGUflNdOsO@6D+Uf4w&ilv{P-=9pARs3eW=Vbp3lEBajJig zCb!=J+4+^nkwa=PVG=h}Xp$s|tK~=2v8|9wwGs&RokjtR2Gb_e#G)?V z`Q;X7D(POMM42cK6DAB9klCI6D9F+v;)Brho=sE`KA~XwiFtNF0%{y|=KamSJ#Up_ zC}EOBeZk$azLGbeRFP^FcXHD#C3S2FqKUR>+TB^zjR7bL%=|tH<|Hstr{NfGdllf! zg|%3xF}v;6HUOios+t78SCC+Gt$z)JaK@wtI=LHUfbzx!awbVImB02HMJ=eBaKf+N zTx1;r2TiS9T7V>QcN!&dDv}R7Id~qva^E5%mxy2)unRj{UYJQ+(tKHhDx2I7>mcQ1 zChV@jStZ#w+Hzl%E?SUdlKO6aa!EZ8%%y;VR2pw>(fUiR?9=QgY}v^)k!^;- zvcG%rd?^>+!f8AEYUuq%l==m#=t>{K6m$8In_X8wH1G+n!T6+ge2031eK zj+r-#q|oEF>&gTQCPho`$Hc`-Eyp_E;JW8&`$6_PL=)J$!?X*>#=1hyC=41wve{Qk zNIrz&jR=tgvXl$j{*T}llQdCmz_FR(8UJB~dTTZXY)Bp1lfrhd9+2%t1f(-_b*xe^gUx+;N%Zdy$4y#2>fI@e= z4{O_9rVOP3dQGSyg)M_Qm4N=|jJMAIBq^*$wRbt?epoRn!FY7YCoVnJteli2>HL}ePkn$yYuDMHc*Y$2@SgUA9XJD*Bv8(T+pzr7OoR!Y5Dq$!eKCDjB7I=wBF zKfv$=|G!a4mxyDgfi4b!*;d0@Z*`yEe1oIvU; z^f%Cyn)X8HW{x16c5^p+E!9wNEXNKrp;$D{A(7fcCY(HwzCg=ELmdVpL2*(~g1QlP z$zSe9->LKw+|xwe=*d2PRCUzu*)!L$KIpbfQ_3;$@$gt3%q$0##*_F$gS7) zKL0mIx~Kn>@2%B};9ve45#x13B#_f5zfEW^*v(PopN}-t&HKgr7svBI!3d8|l>a)o zywIN=VOjn(#P6^^#xag@jAI<*7{@rqF^+MJ|G)7+1VMs)tcH*j00000NkvXXu0mjf DJXoJ; literal 13175 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 From e66cf65c933812d4195ecf333f6e8ac7020e6294 Mon Sep 17 00:00:00 2001 From: Damien BUTY Date: Wed, 17 Jul 2024 16:34:55 +0200 Subject: [PATCH 3/6] feat(attachments): create context-aware helper for attachments --- src/api.ts | 30 +++++++++++++++++++++++++++++- tests/attachments.test.ts | 28 +++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/api.ts b/src/api.ts index 4b967bb..ce91af0 100644 --- a/src/api.ts +++ b/src/api.ts @@ -727,15 +727,43 @@ export class API { params.content = Buffer.from(params.content); } + let threadFromStore: Thread | null = null; + try { + threadFromStore = this.client.getCurrentThread(); + } catch (error) { + // Ignore error thrown if getCurrentThread is called outside of a context + } + + let stepFromStore: Step | null = null; + try { + stepFromStore = this.client.getCurrentStep(); + } catch (error) { + // Ignore error thrown if getCurrentStep is called outside of a context + } + + if (threadFromStore) { + params.threadId = threadFromStore.id; + } + const { objectKey, url } = await this.uploadFile(params); - return new Attachment({ + const attachment = new Attachment({ name: params.name, objectKey, mime: params.mime, metadata: params.metadata, url }); + + if (stepFromStore) { + if (!stepFromStore.attachments) { + stepFromStore.attachments = []; + } + + stepFromStore.attachments.push(attachment); + } + + return attachment; } // Generation diff --git a/tests/attachments.test.ts b/tests/attachments.test.ts index f2c0e48..8370567 100644 --- a/tests/attachments.test.ts +++ b/tests/attachments.test.ts @@ -1,7 +1,7 @@ import 'dotenv/config'; import { createReadStream, readFileSync } from 'fs'; -import { LiteralClient, Thread } from '../src'; +import { Attachment, LiteralClient, Maybe, Thread } from '../src'; const url = process.env.LITERAL_API_URL; const apiKey = process.env.LITERAL_API_KEY; @@ -67,4 +67,30 @@ describe('Attachments', () => { expect(urlWithoutSignature).toEqual(fetchedUrlWithoutSignature); }); }); + + describe('Handling context', () => { + it('attaches the attachment to the step in the context', async () => { + const stream = createReadStream(filePath); + + let stepId: Maybe; + let attachment: Maybe; + + await client.run({ name: 'Attachment test ' }).wrap(async () => { + stepId = client.getCurrentStep().id!; + attachment = await client.api.createAttachment({ + content: stream!, + mime, + name: 'Attachment', + metadata: { type: 'Stream' } + }); + }); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + const fetchedStep = await client.api.getStep(stepId!); + + expect(fetchedStep?.attachments?.length).toBe(1); + expect(fetchedStep?.attachments![0].id).toEqual(attachment!.id); + }); + }); }); From 235b65d4a4fc9b5364e37ad685d6d9042e2b85bb Mon Sep 17 00:00:00 2001 From: Damien BUTY Date: Wed, 17 Jul 2024 17:02:08 +0200 Subject: [PATCH 4/6] fix: tests --- src/api.ts | 1 - tests/attachments.test.ts | 23 +++++++++-------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/api.ts b/src/api.ts index ce91af0..06f49f0 100644 --- a/src/api.ts +++ b/src/api.ts @@ -703,7 +703,6 @@ export class API { return { objectKey, url: signedUrl }; } catch (e) { - console.error(e); console.error(`Failed to upload file: ${e}`); return { objectKey: null, url: null }; } diff --git a/tests/attachments.test.ts b/tests/attachments.test.ts index 8370567..62319d9 100644 --- a/tests/attachments.test.ts +++ b/tests/attachments.test.ts @@ -1,7 +1,7 @@ import 'dotenv/config'; import { createReadStream, readFileSync } from 'fs'; -import { Attachment, LiteralClient, Maybe, Thread } from '../src'; +import { Attachment, LiteralClient, Maybe } from '../src'; const url = process.env.LITERAL_API_URL; const apiKey = process.env.LITERAL_API_KEY; @@ -22,11 +22,6 @@ describe('Attachments', () => { // We wrap the blob in a blob and simulate the structure of a File const file = new Blob([blob], { type: 'image/jpeg' }); - let thread: Thread; - beforeAll(async () => { - thread = await client.thread({ name: 'Attachment test ' }).upsert(); - }); - it.each([ { type: 'Stream', content: stream! }, { type: 'Buffer', content: buffer! }, @@ -41,21 +36,21 @@ describe('Attachments', () => { metadata: { type } }); - const step = await thread - .step({ + const step = await client + .run({ name: `Test ${type}`, - type: 'run', attachments: [attachment] }) .send(); await new Promise((resolve) => setTimeout(resolve, 1000)); - const urlWithoutSignature = attachment.url!.split('X-Amz-Signature')[0]; - const fetchedUrlWithoutSignature = - attachment.url!.split('X-Amz-Signature')[0]; - const fetchedStep = await client.api.getStep(step.id!); + + const urlWithoutAmzVariables = attachment.url!.split('X-Amz-Date')[0]; + const fetchedUrlWithoutAmzVariables = + fetchedStep?.attachments![0].url!.split('X-Amz-Date')[0]; + expect(fetchedStep?.attachments?.length).toBe(1); expect(fetchedStep?.attachments![0].objectKey).toEqual( attachment.objectKey @@ -64,7 +59,7 @@ describe('Attachments', () => { expect(fetchedStep?.attachments![0].metadata).toEqual( attachment.metadata ); - expect(urlWithoutSignature).toEqual(fetchedUrlWithoutSignature); + expect(urlWithoutAmzVariables).toEqual(fetchedUrlWithoutAmzVariables); }); }); From ceca78ca15a904ce759025691022f7b9ef25fce9 Mon Sep 17 00:00:00 2001 From: Damien BUTY Date: Wed, 17 Jul 2024 18:20:54 +0200 Subject: [PATCH 5/6] fix: tests --- tests/attachments.test.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/attachments.test.ts b/tests/attachments.test.ts index 62319d9..bce12bb 100644 --- a/tests/attachments.test.ts +++ b/tests/attachments.test.ts @@ -13,6 +13,10 @@ const client = new LiteralClient(apiKey, url); const filePath = './tests/chainlit-logo.png'; const mime = 'image/png'; +function removeVariableParts(url: string) { + return url.split('X-Amz-Date')[0].split('X-Goog-Date')[0]; +} + describe('Attachments', () => { describe('Uploading a file', () => { const stream = createReadStream(filePath); @@ -47,9 +51,10 @@ describe('Attachments', () => { const fetchedStep = await client.api.getStep(step.id!); - const urlWithoutAmzVariables = attachment.url!.split('X-Amz-Date')[0]; - const fetchedUrlWithoutAmzVariables = - fetchedStep?.attachments![0].url!.split('X-Amz-Date')[0]; + const urlWithoutVariables = removeVariableParts(attachment.url!); + const fetchedUrlWithoutVariables = removeVariableParts( + fetchedStep?.attachments![0].url as string + ); expect(fetchedStep?.attachments?.length).toBe(1); expect(fetchedStep?.attachments![0].objectKey).toEqual( @@ -59,7 +64,7 @@ describe('Attachments', () => { expect(fetchedStep?.attachments![0].metadata).toEqual( attachment.metadata ); - expect(urlWithoutAmzVariables).toEqual(fetchedUrlWithoutAmzVariables); + expect(urlWithoutVariables).toEqual(fetchedUrlWithoutVariables); }); }); From 26b8687164f28c929cbb700f19e66a5bf2a36738 Mon Sep 17 00:00:00 2001 From: Damien BUTY Date: Thu, 25 Jul 2024 10:01:57 +0200 Subject: [PATCH 6/6] fix: review --- src/api.ts | 15 ++------------- src/index.ts | 12 ++++++++++++ src/instrumentation/openai.ts | 20 +++----------------- tests/attachments.test.ts | 2 +- 4 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/api.ts b/src/api.ts index 06f49f0..5dc9787 100644 --- a/src/api.ts +++ b/src/api.ts @@ -726,19 +726,8 @@ export class API { params.content = Buffer.from(params.content); } - let threadFromStore: Thread | null = null; - try { - threadFromStore = this.client.getCurrentThread(); - } catch (error) { - // Ignore error thrown if getCurrentThread is called outside of a context - } - - let stepFromStore: Step | null = null; - try { - stepFromStore = this.client.getCurrentStep(); - } catch (error) { - // Ignore error thrown if getCurrentStep is called outside of a context - } + const threadFromStore = this.client._currentThread(); + const stepFromStore = this.client._currentStep(); if (threadFromStore) { params.threadId = threadFromStore.id; diff --git a/src/index.ts b/src/index.ts index 9750dfa..5f44b65 100644 --- a/src/index.ts +++ b/src/index.ts @@ -49,6 +49,18 @@ export class LiteralClient { return this.step({ ...data, type: 'run' }); } + _currentThread(): Thread | null { + const store = storage.getStore(); + + return store?.currentThread || null; + } + + _currentStep(): Step | null { + const store = storage.getStore(); + + return store?.currentStep || null; + } + /** * Gets the current thread from the context. * WARNING : this will throw if run outside of a thread context. diff --git a/src/instrumentation/openai.ts b/src/instrumentation/openai.ts index f489c95..786dea0 100644 --- a/src/instrumentation/openai.ts +++ b/src/instrumentation/openai.ts @@ -13,9 +13,7 @@ import { IGenerationMessage, LiteralClient, Maybe, - Step, - StepConstructor, - Thread + StepConstructor } from '..'; // Define a generic type for the original function to be wrapped @@ -310,25 +308,13 @@ const processOpenAIOutput = async ( tags: tags }; - let threadFromStore: Thread | null = null; - try { - threadFromStore = client.getCurrentThread(); - } catch (error) { - // Ignore error thrown if getCurrentThread is called outside of a context - } - - let stepFromStore: Step | null = null; - try { - stepFromStore = client.getCurrentStep(); - } catch (error) { - // Ignore error thrown if getCurrentStep is called outside of a context - } + const threadFromStore = client._currentThread(); + const stepFromStore = client._currentStep(); const parent = stepFromStore || threadFromStore; if ('data' in output) { // Image Generation - const stepData: StepConstructor = { name: inputs.model || 'openai', type: 'llm', diff --git a/tests/attachments.test.ts b/tests/attachments.test.ts index bce12bb..8877b8d 100644 --- a/tests/attachments.test.ts +++ b/tests/attachments.test.ts @@ -24,7 +24,7 @@ describe('Attachments', () => { const arrayBuffer = buffer.buffer; const blob = new Blob([buffer]); // We wrap the blob in a blob and simulate the structure of a File - const file = new Blob([blob], { type: 'image/jpeg' }); + const file = new Blob([blob], { type: 'image/png' }); it.each([ { type: 'Stream', content: stream! },