From 2b8edc32cc27462a0181088c35545b7995f1cd46 Mon Sep 17 00:00:00 2001 From: ckr-rai73 Date: Sat, 21 Mar 2026 16:17:53 +0530 Subject: [PATCH 1/5] fix(sdk): resolve string vs. object type mismatch in prompt compilation --- packages/client/src/prompt/promptClients.ts | 113 ++++++++++++-------- tests/e2e/prompts.e2e.test.ts | 36 +++++++ 2 files changed, 104 insertions(+), 45 deletions(-) diff --git a/packages/client/src/prompt/promptClients.ts b/packages/client/src/prompt/promptClients.ts index b6d08a49..2be881c0 100644 --- a/packages/client/src/prompt/promptClients.ts +++ b/packages/client/src/prompt/promptClients.ts @@ -263,6 +263,7 @@ export class TextPromptClient extends BasePromptClient { * @public */ export class ChatPromptClient extends BasePromptClient { + private static readonly mustacheTokenCache = new Map(); /** The original prompt response from the API */ public readonly promptResponse: Prompt.Chat; /** The chat messages that make up the prompt */ @@ -328,13 +329,56 @@ export class ChatPromptClient extends BasePromptClient { variables?: Record, placeholders?: Record, ): (ChatMessageOrPlaceholder | any)[] { - const messagesWithPlaceholdersReplaced: (ChatMessageOrPlaceholder | any)[] = - []; - const placeholderValues = placeholders ?? {}; + const vars = variables ?? {}; + const phs = placeholders ?? {}; + + // 4. ZERO-ALLOCATION CHECK: Skip entirely if no injections needed + if (Object.keys(vars).length === 0 && Object.keys(phs).length === 0) { + return this.prompt.map((item) => { + if ("type" in item && item.type === ChatMessageType.ChatMessage) { + return { role: item.role, content: item.content }; + } + return item; + }); + } + + const result: (ChatMessageOrPlaceholder | any)[] = []; + const writer = new mustache.Writer(); for (const item of this.prompt) { - if ("type" in item && item.type === ChatMessageType.Placeholder) { - const placeholderValue = placeholderValues[item.name]; + // 3. REFINED TYPE-GUARD: Check content type as the very first operation + if (item.type === ChatMessageType.ChatMessage) { + if (typeof item.content !== "string") { + result.push({ + role: item.role, + content: item.content, + }); + continue; + } + + // 2. TOKEN CACHING: Use static Map to bypass expensive parsing + let tokens = ChatPromptClient.mustacheTokenCache.get(item.content); + if (!tokens) { + tokens = mustache.parse(item.content); + ChatPromptClient.mustacheTokenCache.set(item.content, tokens); + } + + const rendered = writer.renderTokens( + tokens, + new mustache.Context(vars), + undefined, + item.content, + ); + result.push({ + role: item.role, + content: rendered, + }); + continue; + } + + // Handle Placeholders (preserved logic) + if (item.type === ChatMessageType.Placeholder) { + const placeholderValue = phs[item.name]; if ( Array.isArray(placeholderValue) && placeholderValue.length > 0 && @@ -343,54 +387,27 @@ export class ChatPromptClient extends BasePromptClient { typeof msg === "object" && "role" in msg && "content" in msg, ) ) { - messagesWithPlaceholdersReplaced.push( - ...(placeholderValue as ChatMessage[]), - ); + result.push(...(placeholderValue as ChatMessage[])); } else if ( Array.isArray(placeholderValue) && placeholderValue.length === 0 ) { - // Empty array provided - skip placeholder (don't include it) + // Skip empty placeholder array } else if (placeholderValue !== undefined) { - // Non-standard placeholder value format, just stringfiy - messagesWithPlaceholdersReplaced.push( - JSON.stringify(placeholderValue), - ); + // Stringify non-standard formats + result.push(JSON.stringify(placeholderValue)); } else { - // Keep unresolved placeholder in the output - messagesWithPlaceholdersReplaced.push( - item as { type: ChatMessageType.Placeholder } & typeof item, - ); + // Keep unresolved placeholder + result.push(item); } - } else if ( - "role" in item && - "content" in item && - item.type === ChatMessageType.ChatMessage - ) { - messagesWithPlaceholdersReplaced.push({ - role: item.role, - content: item.content, - }); + continue; } + + // Catch-all for any other message types + result.push(item); } - return messagesWithPlaceholdersReplaced.map((item) => { - if ( - typeof item === "object" && - item !== null && - "role" in item && - "content" in item && - typeof item.content === 'string' - ) { - return { - ...item, - content: mustache.render(item.content, variables ?? {}), - }; - } else { - // Return placeholder or stringified value as-is - return item; - } - }); + return result; } /** @@ -438,7 +455,10 @@ export class ChatPromptClient extends BasePromptClient { ...(placeholderValue as ChatMessage[]).map((msg) => { return { role: msg.role, - content: this._transformToLangchainVariables(msg.content), + content: + typeof msg.content === "string" + ? this._transformToLangchainVariables(msg.content) + : msg.content, }; }), ); @@ -469,7 +489,10 @@ export class ChatPromptClient extends BasePromptClient { ) { messagesWithPlaceholdersReplaced.push({ role: item.role, - content: this._transformToLangchainVariables(item.content), + content: + typeof item.content === "string" + ? this._transformToLangchainVariables(item.content) + : item.content, }); } } diff --git a/tests/e2e/prompts.e2e.test.ts b/tests/e2e/prompts.e2e.test.ts index d81d8d21..278ed161 100644 --- a/tests/e2e/prompts.e2e.test.ts +++ b/tests/e2e/prompts.e2e.test.ts @@ -2151,5 +2151,41 @@ Configuration: expect(formattedMessages[1].content).toBe(expectedUser); }); }); + + it("should handle multimodal array inputs without crashing mustache", () => { + const promptClient = new ChatPromptClient({ + name: "multimodal-test", + type: "chat", + version: 1, + prompt: [ + { + type: ChatMessageType.ChatMessage, + role: "user", + content: { + attachments: [ + { + type: "image_url", + image_url: { url: "https://example.com/image.png" }, + }, + ], + } as any, + }, + ], + config: {}, + labels: [], + tags: [], + }); + + // After fix: Should return the multimodal content as-is without crashing + const compiled = promptClient.compile(); + expect(compiled[0].content).toEqual({ + attachments: [ + { + type: "image_url", + image_url: { url: "https://example.com/image.png" }, + }, + ], + }); + }); }); }); From 7a4d87f74f576f84145231d66402b1f36988291a Mon Sep 17 00:00:00 2001 From: ckr-rai73 Date: Sat, 21 Mar 2026 17:14:13 +0530 Subject: [PATCH 2/5] test: add coverage for multimodal main-loop and langchain paths --- tests/e2e/prompts.e2e.test.ts | 77 +++++++++++++++++++++++++++++++ vitest.workspace.ts | 85 +++++++++++++++-------------------- 2 files changed, 114 insertions(+), 48 deletions(-) diff --git a/tests/e2e/prompts.e2e.test.ts b/tests/e2e/prompts.e2e.test.ts index 278ed161..4019da27 100644 --- a/tests/e2e/prompts.e2e.test.ts +++ b/tests/e2e/prompts.e2e.test.ts @@ -2187,5 +2187,82 @@ Configuration: ], }); }); + + it("should handle multimodal array inputs in the main loop (with variables)", () => { + const promptClient = new ChatPromptClient({ + name: "multimodal-test", + type: "chat", + version: 1, + prompt: [ + { + type: ChatMessageType.ChatMessage, + role: "user", + content: { + attachments: [ + { + type: "image_url", + image_url: { url: "https://example.com/image.png" }, + }, + ], + } as any, + }, + { + type: ChatMessageType.ChatMessage, + role: "user", + content: "Dummy: {{dummy}}", + }, + ], + config: {}, + labels: [], + tags: [], + }); + + // Passing a variable forces execution into the main loop + const compiled = promptClient.compile({ dummy: "test" }); + expect(compiled[0].content).toEqual({ + attachments: [ + { + type: "image_url", + image_url: { url: "https://example.com/image.png" }, + }, + ], + }); + expect(compiled[1].content).toEqual("Dummy: test"); + }); + + it("should handle multimodal content in getLangchainPrompt without crashing", () => { + const promptClient = new ChatPromptClient({ + name: "multimodal-test", + type: "chat", + version: 1, + prompt: [ + { + type: ChatMessageType.ChatMessage, + role: "user", + content: { + attachments: [ + { + type: "image_url", + image_url: { url: "https://example.com/image.png" }, + }, + ], + } as any, + }, + ], + config: {}, + labels: [], + tags: [], + }); + + const result = promptClient.getLangchainPrompt(); + expect(result[0].content).toEqual({ + attachments: [ + { + type: "image_url", + image_url: { url: "https://example.com/image.png" }, + }, + ], + }); + }); }); }); diff --git a/vitest.workspace.ts b/vitest.workspace.ts index 1f6f5b77..976aab94 100644 --- a/vitest.workspace.ts +++ b/vitest.workspace.ts @@ -1,4 +1,5 @@ import { defineWorkspace } from "vitest/config"; +import { fileURLToPath } from "node:url"; export default defineWorkspace([ { @@ -10,30 +11,24 @@ export default defineWorkspace([ }, resolve: { alias: { - "@langfuse/client": new URL( - "./packages/client/dist/index.mjs", - import.meta.url, - ).pathname, - "@langfuse/tracing": new URL( - "./packages/tracing/dist/index.mjs", - import.meta.url, - ).pathname, - "@langfuse/otel": new URL( - "./packages/otel/dist/index.mjs", - import.meta.url, - ).pathname, - "@langfuse/langchain": new URL( - "./packages/langchain/dist/index.mjs", - import.meta.url, - ).pathname, - "@langfuse/openai": new URL( - "./packages/openai/dist/index.mjs", - import.meta.url, - ).pathname, - "@langfuse/core": new URL( - "./packages/core/dist/index.mjs", - import.meta.url, - ).pathname, + "@langfuse/client": fileURLToPath( + new URL("./packages/client/dist/index.mjs", import.meta.url), + ), + "@langfuse/tracing": fileURLToPath( + new URL("./packages/tracing/dist/index.mjs", import.meta.url), + ), + "@langfuse/otel": fileURLToPath( + new URL("./packages/otel/dist/index.mjs", import.meta.url), + ), + "@langfuse/langchain": fileURLToPath( + new URL("./packages/langchain/dist/index.mjs", import.meta.url), + ), + "@langfuse/openai": fileURLToPath( + new URL("./packages/openai/dist/index.mjs", import.meta.url), + ), + "@langfuse/core": fileURLToPath( + new URL("./packages/core/dist/index.mjs", import.meta.url), + ), }, }, }, @@ -47,30 +42,24 @@ export default defineWorkspace([ }, resolve: { alias: { - "@langfuse/client": new URL( - "./packages/client/dist/index.mjs", - import.meta.url, - ).pathname, - "@langfuse/tracing": new URL( - "./packages/tracing/dist/index.mjs", - import.meta.url, - ).pathname, - "@langfuse/otel": new URL( - "./packages/otel/dist/index.mjs", - import.meta.url, - ).pathname, - "@langfuse/langchain": new URL( - "./packages/langchain/dist/index.mjs", - import.meta.url, - ).pathname, - "@langfuse/openai": new URL( - "./packages/openai/dist/index.mjs", - import.meta.url, - ).pathname, - "@langfuse/core": new URL( - "./packages/core/dist/index.mjs", - import.meta.url, - ).pathname, + "@langfuse/client": fileURLToPath( + new URL("./packages/client/dist/index.mjs", import.meta.url), + ), + "@langfuse/tracing": fileURLToPath( + new URL("./packages/tracing/dist/index.mjs", import.meta.url), + ), + "@langfuse/otel": fileURLToPath( + new URL("./packages/otel/dist/index.mjs", import.meta.url), + ), + "@langfuse/langchain": fileURLToPath( + new URL("./packages/langchain/dist/index.mjs", import.meta.url), + ), + "@langfuse/openai": fileURLToPath( + new URL("./packages/openai/dist/index.mjs", import.meta.url), + ), + "@langfuse/core": fileURLToPath( + new URL("./packages/core/dist/index.mjs", import.meta.url), + ), }, }, }, From 28a0e42a472a13dde683795d71a29f315aaceec6 Mon Sep 17 00:00:00 2001 From: ckr-rai73 Date: Sat, 21 Mar 2026 18:33:36 +0530 Subject: [PATCH 3/5] test: add coverage for multimodal main-loop and langchain paths --- tests/e2e/prompts.e2e.test.ts | 57 +++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/e2e/prompts.e2e.test.ts b/tests/e2e/prompts.e2e.test.ts index 4019da27..cdb83883 100644 --- a/tests/e2e/prompts.e2e.test.ts +++ b/tests/e2e/prompts.e2e.test.ts @@ -2263,6 +2263,63 @@ Configuration: }, ], }); + });it("should handle multimodal array inputs in the main loop (with variables)", () => { + const promptClient = new ChatPromptClient({ + name: "multimodal-loop-test", + type: "chat", + version: 1, + prompt: [ + { + role: "user", + content: { + attachments: [ + { + type: "image_url", + image_url: { url: "https://example.com/image.png" }, + }, + ], + } as any, + }, + { + role: "user", + content: "Context: {{dummy}}", + }, + ], + config: {}, + labels: [], + tags: [], + } as any); + + const compiled = promptClient.compile({ dummy: "test-value" }); + + expect(compiled[0].content).toEqual({ + attachments: [{ type: "image_url", image_url: { url: "https://example.com/image.png" } }], + }); + expect(compiled[1].content).toEqual("Context: test-value"); + }); + + it("should handle multimodal content in getLangchainPrompt without crashing", () => { + const promptClient = new ChatPromptClient({ + name: "multimodal-langchain-test", + type: "chat", + version: 1, + prompt: [ + { + role: "user", + content: { + attachments: [{ type: "image_url", image_url: { url: "https://example.com/image.png" } }], + } as any, + }, + ], + config: {}, + labels: [], + tags: [], + } as any); + + const result = promptClient.getLangchainPrompt(); + expect(result[0].content).toEqual({ + attachments: [{ type: "image_url", image_url: { url: "https://example.com/image.png" } }], + }); }); }); }); From 0fc6ae6d1818c730b8fe87375ec342f50b684e4e Mon Sep 17 00:00:00 2001 From: ckr-rai73 Date: Sat, 21 Mar 2026 18:35:26 +0530 Subject: [PATCH 4/5] test: add coverage for multimodal main-loop and langchain paths --- tests/e2e/prompts.e2e.test.ts | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/e2e/prompts.e2e.test.ts b/tests/e2e/prompts.e2e.test.ts index cdb83883..5cc343f1 100644 --- a/tests/e2e/prompts.e2e.test.ts +++ b/tests/e2e/prompts.e2e.test.ts @@ -2263,7 +2263,8 @@ Configuration: }, ], }); - });it("should handle multimodal array inputs in the main loop (with variables)", () => { + }); + it("should handle multimodal array inputs in the main loop (with variables)", () => { const promptClient = new ChatPromptClient({ name: "multimodal-loop-test", type: "chat", @@ -2293,7 +2294,12 @@ Configuration: const compiled = promptClient.compile({ dummy: "test-value" }); expect(compiled[0].content).toEqual({ - attachments: [{ type: "image_url", image_url: { url: "https://example.com/image.png" } }], + attachments: [ + { + type: "image_url", + image_url: { url: "https://example.com/image.png" }, + }, + ], }); expect(compiled[1].content).toEqual("Context: test-value"); }); @@ -2307,7 +2313,12 @@ Configuration: { role: "user", content: { - attachments: [{ type: "image_url", image_url: { url: "https://example.com/image.png" } }], + attachments: [ + { + type: "image_url", + image_url: { url: "https://example.com/image.png" }, + }, + ], } as any, }, ], @@ -2318,7 +2329,12 @@ Configuration: const result = promptClient.getLangchainPrompt(); expect(result[0].content).toEqual({ - attachments: [{ type: "image_url", image_url: { url: "https://example.com/image.png" } }], + attachments: [ + { + type: "image_url", + image_url: { url: "https://example.com/image.png" }, + }, + ], }); }); }); From 49b560943f10f7315765e65331ba1d43ad0241e4 Mon Sep 17 00:00:00 2001 From: ckr-rai73 Date: Sat, 21 Mar 2026 22:00:46 +0530 Subject: [PATCH 5/5] fix(sdk): implement FIFO cache eviction and unique test signatures --- packages/client/src/prompt/promptClients.ts | 17 +++- .../tsup.config.bundled_nv8l3rehalb.mjs | 18 ++++ tests/e2e/prompts.e2e.test.ts | 83 +++---------------- 3 files changed, 43 insertions(+), 75 deletions(-) create mode 100644 packages/client/tsup.config.bundled_nv8l3rehalb.mjs diff --git a/packages/client/src/prompt/promptClients.ts b/packages/client/src/prompt/promptClients.ts index 2be881c0..5e250f9d 100644 --- a/packages/client/src/prompt/promptClients.ts +++ b/packages/client/src/prompt/promptClients.ts @@ -263,6 +263,7 @@ export class TextPromptClient extends BasePromptClient { * @public */ export class ChatPromptClient extends BasePromptClient { + private static readonly MAX_CACHE_SIZE = 500; private static readonly mustacheTokenCache = new Map(); /** The original prompt response from the API */ public readonly promptResponse: Prompt.Chat; @@ -346,8 +347,8 @@ export class ChatPromptClient extends BasePromptClient { const writer = new mustache.Writer(); for (const item of this.prompt) { - // 3. REFINED TYPE-GUARD: Check content type as the very first operation if (item.type === ChatMessageType.ChatMessage) { + // 3. REFINED TYPE-GUARD: Check content type as the very first operation if (typeof item.content !== "string") { result.push({ role: item.role, @@ -356,9 +357,21 @@ export class ChatPromptClient extends BasePromptClient { continue; } - // 2. TOKEN CACHING: Use static Map to bypass expensive parsing + // 2. TOKEN CACHING: Use static Map to bypass expensive parsing (Memory-Safe) let tokens = ChatPromptClient.mustacheTokenCache.get(item.content); if (!tokens) { + if ( + ChatPromptClient.mustacheTokenCache.size >= + ChatPromptClient.MAX_CACHE_SIZE + ) { + // FIFO Eviction: Eject the oldest entry + const oldestKey = ChatPromptClient.mustacheTokenCache + .keys() + .next().value; + if (oldestKey !== undefined) { + ChatPromptClient.mustacheTokenCache.delete(oldestKey); + } + } tokens = mustache.parse(item.content); ChatPromptClient.mustacheTokenCache.set(item.content, tokens); } diff --git a/packages/client/tsup.config.bundled_nv8l3rehalb.mjs b/packages/client/tsup.config.bundled_nv8l3rehalb.mjs new file mode 100644 index 00000000..095398ea --- /dev/null +++ b/packages/client/tsup.config.bundled_nv8l3rehalb.mjs @@ -0,0 +1,18 @@ +// tsup.config.ts +import { defineConfig } from "tsup"; +var tsup_config_default = defineConfig({ + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: true, + splitting: false, + sourcemap: true, + clean: true, + outDir: "dist", + outExtension: ({ format }) => ({ + js: format === "cjs" ? ".cjs" : ".mjs" + }) +}); +export { + tsup_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidHN1cC5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiQzpcXFxcVXNlcnNcXFxcUFJBVkVFTiBSQUlcXFxcbGFuZ2Z1c2UtanNcXFxccGFja2FnZXNcXFxcY2xpZW50XFxcXHRzdXAuY29uZmlnLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIkM6XFxcXFVzZXJzXFxcXFBSQVZFRU4gUkFJXFxcXGxhbmdmdXNlLWpzXFxcXHBhY2thZ2VzXFxcXGNsaWVudFwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vQzovVXNlcnMvUFJBVkVFTiUyMFJBSS9sYW5nZnVzZS1qcy9wYWNrYWdlcy9jbGllbnQvdHN1cC5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tIFwidHN1cFwiO1xuXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xuICBlbnRyeTogW1wic3JjL2luZGV4LnRzXCJdLFxuICBmb3JtYXQ6IFtcImNqc1wiLCBcImVzbVwiXSxcbiAgZHRzOiB0cnVlLFxuICBzcGxpdHRpbmc6IGZhbHNlLFxuICBzb3VyY2VtYXA6IHRydWUsXG4gIGNsZWFuOiB0cnVlLFxuICBvdXREaXI6IFwiZGlzdFwiLFxuICBvdXRFeHRlbnNpb246ICh7IGZvcm1hdCB9KSA9PiAoe1xuICAgIGpzOiBmb3JtYXQgPT09IFwiY2pzXCIgPyBcIi5janNcIiA6IFwiLm1qc1wiLFxuICB9KSxcbn0pO1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUE0UyxTQUFTLG9CQUFvQjtBQUV6VSxJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixPQUFPLENBQUMsY0FBYztBQUFBLEVBQ3RCLFFBQVEsQ0FBQyxPQUFPLEtBQUs7QUFBQSxFQUNyQixLQUFLO0FBQUEsRUFDTCxXQUFXO0FBQUEsRUFDWCxXQUFXO0FBQUEsRUFDWCxPQUFPO0FBQUEsRUFDUCxRQUFRO0FBQUEsRUFDUixjQUFjLENBQUMsRUFBRSxPQUFPLE9BQU87QUFBQSxJQUM3QixJQUFJLFdBQVcsUUFBUSxTQUFTO0FBQUEsRUFDbEM7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo= diff --git a/tests/e2e/prompts.e2e.test.ts b/tests/e2e/prompts.e2e.test.ts index 5cc343f1..ee76abde 100644 --- a/tests/e2e/prompts.e2e.test.ts +++ b/tests/e2e/prompts.e2e.test.ts @@ -2152,7 +2152,7 @@ Configuration: }); }); - it("should handle multimodal array inputs without crashing mustache", () => { + it("should handle multimodal array inputs for ChatMessage types", () => { const promptClient = new ChatPromptClient({ name: "multimodal-test", type: "chat", @@ -2188,7 +2188,7 @@ Configuration: }); }); - it("should handle multimodal array inputs in the main loop (with variables)", () => { + it("should handle multimodal array inputs in the main loop with variables", () => { const promptClient = new ChatPromptClient({ name: "multimodal-test", type: "chat", @@ -2230,9 +2230,9 @@ Configuration: expect(compiled[1].content).toEqual("Dummy: test"); }); - it("should handle multimodal content in getLangchainPrompt without crashing", () => { + it("should handle multimodal content in getLangchainPrompt safely", () => { const promptClient = new ChatPromptClient({ - name: "multimodal-test", + name: "multimodal-langchain-test", type: "chat", version: 1, prompt: [ @@ -2253,89 +2253,26 @@ Configuration: labels: [], tags: [], }); - - const result = promptClient.getLangchainPrompt(); - expect(result[0].content).toEqual({ - attachments: [ - { - type: "image_url", - image_url: { url: "https://example.com/image.png" }, - }, - ], - }); - }); - it("should handle multimodal array inputs in the main loop (with variables)", () => { - const promptClient = new ChatPromptClient({ - name: "multimodal-loop-test", - type: "chat", - version: 1, - prompt: [ - { - role: "user", - content: { - attachments: [ - { - type: "image_url", - image_url: { url: "https://example.com/image.png" }, - }, - ], - } as any, - }, - { - role: "user", - content: "Context: {{dummy}}", - }, - ], - config: {}, - labels: [], - tags: [], - } as any); - - const compiled = promptClient.compile({ dummy: "test-value" }); - - expect(compiled[0].content).toEqual({ - attachments: [ - { - type: "image_url", - image_url: { url: "https://example.com/image.png" }, - }, - ], - }); - expect(compiled[1].content).toEqual("Context: test-value"); }); - it("should handle multimodal content in getLangchainPrompt without crashing", () => { + it("should handle multimodal content in getLangchainPrompt for non-ChatMessage items", () => { const promptClient = new ChatPromptClient({ - name: "multimodal-langchain-test", + name: "multimodal-non-chat-test", type: "chat", version: 1, prompt: [ { - role: "user", - content: { - attachments: [ - { - type: "image_url", - image_url: { url: "https://example.com/image.png" }, - }, - ], - } as any, + type: ChatMessageType.Placeholder, + name: "multimodal_placeholder", }, ], config: {}, labels: [], tags: [], - } as any); + }); const result = promptClient.getLangchainPrompt(); - expect(result[0].content).toEqual({ - attachments: [ - { - type: "image_url", - image_url: { url: "https://example.com/image.png" }, - }, - ], - }); + expect(result[0]).toEqual(["placeholder", "{multimodal_placeholder}"]); }); }); });