Skip to content

Commit

Permalink
fix(langchain,core): Add shim for hub mustache templates with nested …
Browse files Browse the repository at this point in the history
…input variables (#7581)
  • Loading branch information
jacoblee93 authored Jan 24, 2025
1 parent 5bdbb75 commit 71dd696
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 8 deletions.
30 changes: 22 additions & 8 deletions langchain-core/src/prompts/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -771,16 +771,30 @@ function _coerceMessagePromptTemplateLike<
) {
const messageContent = messagePromptTemplateLike[1];
if (
typeof messageContent !== "string" ||
messageContent[0] !== "{" ||
messageContent[messageContent.length - 1] !== "}"
extra?.templateFormat === "mustache" &&
typeof messageContent === "string" &&
messageContent.slice(0, 2) === "{{" &&
messageContent.slice(-2) === "}}"
) {
throw new Error(
`Invalid placeholder template: "${messagePromptTemplateLike[1]}". Expected a variable name surrounded by curly braces.`
);
const variableName = messageContent.slice(2, -2);
return new MessagesPlaceholder({ variableName, optional: true });
} else if (
typeof messageContent === "string" &&
messageContent[0] === "{" &&
messageContent[messageContent.length - 1] === "}"
) {
const variableName = messageContent.slice(1, -1);
return new MessagesPlaceholder({ variableName, optional: true });
}
const variableName = messageContent.slice(1, -1);
return new MessagesPlaceholder({ variableName, optional: true });
throw new Error(
`Invalid placeholder template for format ${
extra?.templateFormat ?? `"f-string"`
}: "${
messagePromptTemplateLike[1]
}". Expected a variable name surrounded by ${
extra?.templateFormat === "mustache" ? "double" : "single"
} curly braces.`
);
}
const message = coerceMessageLikeToMessage(messagePromptTemplateLike);
let templateData:
Expand Down
31 changes: 31 additions & 0 deletions langchain-core/src/prompts/tests/chat.mustache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,34 @@ test("Mustache image template with nested URL and chat prompts HumanMessagePromp

expect(template.inputVariables.sort()).toEqual(["image_url", "name"]);
});

test("Mustache image template with nested props", async () => {
const template = ChatPromptTemplate.fromMessages(
[
["human", "{{agent.name}}"],
["placeholder", "{{messages}}"],
],
{
templateFormat: "mustache",
}
);

const messages = await template.formatMessages({
agent: { name: "testing" },
messages: [
{
role: "assistant",
content: "hi there!",
},
],
});

expect(messages).toEqual([
new HumanMessage({
content: "testing",
}),
new AIMessage("hi there!"),
]);

expect(template.inputVariables.sort()).toEqual(["agent", "messages"]);
});
24 changes: 24 additions & 0 deletions langchain/src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,30 @@ export async function pull<T extends Runnable>(
};
}

// Some nested mustache prompts have improperly parsed variables that include a dot.
if (promptObject.manifest.kwargs.template_format === "mustache") {
const stripDotNotation = (varName: string) => varName.split(".")[0];

const { input_variables } = promptObject.manifest.kwargs;
if (Array.isArray(input_variables)) {
promptObject.manifest.kwargs.input_variables =
input_variables.map(stripDotNotation);
}

const { messages } = promptObject.manifest.kwargs;
if (Array.isArray(messages)) {
promptObject.manifest.kwargs.messages = messages.map((message: any) => {
const nestedVars = message?.kwargs?.prompt?.kwargs?.input_variables;
if (Array.isArray(nestedVars)) {
// eslint-disable-next-line no-param-reassign
message.kwargs.prompt.kwargs.input_variables =
nestedVars.map(stripDotNotation);
}
return message;
});
}
}

try {
const loadedPrompt = await load<T>(
JSON.stringify(promptObject.manifest),
Expand Down
21 changes: 21 additions & 0 deletions langchain/src/tests/hub.int.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
/* eslint-disable no-process-env */

import { ChatPromptTemplate } from "@langchain/core/prompts";
import {
AIMessage,
HumanMessage,
SystemMessage,
} from "@langchain/core/messages";
import { ChatPromptValue } from "@langchain/core/prompt_values";
import * as hub from "../hub.js";

test("Test LangChain Hub client pushing a new repo", async () => {
Expand All @@ -18,3 +24,18 @@ test("Test LangChain Hub client pushing a new repo", async () => {
await pulledPrompt.invoke({ input: "testing" })
);
});

test("Test LangChain Hub with a faulty mustache prompt", async () => {
const pulledPrompt = await hub.pull("jacob/lahzo-testing");
const res = await pulledPrompt.invoke({
agent: { name: "testing" },
messages: [new AIMessage("foo")],
});
expect(res).toEqual(
new ChatPromptValue([
new SystemMessage("You are a chatbot."),
new HumanMessage("testing"),
new AIMessage("foo"),
])
);
});

0 comments on commit 71dd696

Please sign in to comment.