Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(langchain,core): Add shim for hub mustache templates with nested input variables #7581

Merged
merged 2 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"]);
});
23 changes: 23 additions & 0 deletions langchain/src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,29 @@
};
}

// 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)) {
message.kwargs.prompt.kwargs.input_variables =

Check failure on line 127 in langchain/src/hub.ts

View workflow job for this annotation

GitHub Actions / Check linting

Assignment to property of function parameter 'message'
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"),
])
);
});
Loading