@langchain/core
Version:
Core LangChain.js abstractions and schemas
190 lines (189 loc) • 7.07 kB
JavaScript
import { AIMessage, AIMessageChunk } from "./ai.js";
import { isBaseMessage, } from "./base.js";
import { ChatMessage, ChatMessageChunk, } from "./chat.js";
import { FunctionMessage, FunctionMessageChunk, } from "./function.js";
import { HumanMessage, HumanMessageChunk } from "./human.js";
import { SystemMessage, SystemMessageChunk } from "./system.js";
import { ToolMessage } from "./tool.js";
function _constructMessageFromParams(params) {
const { type, ...rest } = params;
if (type === "human" || type === "user") {
return new HumanMessage(rest);
}
else if (type === "ai" || type === "assistant") {
return new AIMessage(rest);
}
else if (type === "system") {
return new SystemMessage(rest);
}
else {
throw new Error(`Unable to coerce message from array: only human, AI, or system message coercion is currently supported.`);
}
}
export function coerceMessageLikeToMessage(messageLike) {
if (typeof messageLike === "string") {
return new HumanMessage(messageLike);
}
else if (isBaseMessage(messageLike)) {
return messageLike;
}
if (Array.isArray(messageLike)) {
const [type, content] = messageLike;
return _constructMessageFromParams({ type, content });
}
else {
return _constructMessageFromParams(messageLike);
}
}
/**
* This function is used by memory classes to get a string representation
* of the chat message history, based on the message content and role.
*/
export function getBufferString(messages, humanPrefix = "Human", aiPrefix = "AI") {
const string_messages = [];
for (const m of messages) {
let role;
if (m._getType() === "human") {
role = humanPrefix;
}
else if (m._getType() === "ai") {
role = aiPrefix;
}
else if (m._getType() === "system") {
role = "System";
}
else if (m._getType() === "function") {
role = "Function";
}
else if (m._getType() === "tool") {
role = "Tool";
}
else if (m._getType() === "generic") {
role = m.role;
}
else {
throw new Error(`Got unsupported message type: ${m._getType()}`);
}
const nameStr = m.name ? `${m.name}, ` : "";
const readableContent = typeof m.content === "string"
? m.content
: JSON.stringify(m.content, null, 2);
string_messages.push(`${role}: ${nameStr}${readableContent}`);
}
return string_messages.join("\n");
}
/**
* Maps messages from an older format (V1) to the current `StoredMessage`
* format. If the message is already in the `StoredMessage` format, it is
* returned as is. Otherwise, it transforms the V1 message into a
* `StoredMessage`. This function is important for maintaining
* compatibility with older message formats.
*/
function mapV1MessageToStoredMessage(message) {
// TODO: Remove this mapper when we deprecate the old message format.
if (message.data !== undefined) {
return message;
}
else {
const v1Message = message;
return {
type: v1Message.type,
data: {
content: v1Message.text,
role: v1Message.role,
name: undefined,
tool_call_id: undefined,
},
};
}
}
export function mapStoredMessageToChatMessage(message) {
const storedMessage = mapV1MessageToStoredMessage(message);
switch (storedMessage.type) {
case "human":
return new HumanMessage(storedMessage.data);
case "ai":
return new AIMessage(storedMessage.data);
case "system":
return new SystemMessage(storedMessage.data);
case "function":
if (storedMessage.data.name === undefined) {
throw new Error("Name must be defined for function messages");
}
return new FunctionMessage(storedMessage.data);
case "tool":
if (storedMessage.data.tool_call_id === undefined) {
throw new Error("Tool call ID must be defined for tool messages");
}
return new ToolMessage(storedMessage.data);
case "generic": {
if (storedMessage.data.role === undefined) {
throw new Error("Role must be defined for chat messages");
}
return new ChatMessage(storedMessage.data);
}
default:
throw new Error(`Got unexpected type: ${storedMessage.type}`);
}
}
/**
* Transforms an array of `StoredMessage` instances into an array of
* `BaseMessage` instances. It uses the `mapV1MessageToStoredMessage`
* function to ensure all messages are in the `StoredMessage` format, then
* creates new instances of the appropriate `BaseMessage` subclass based
* on the type of each message. This function is used to prepare stored
* messages for use in a chat context.
*/
export function mapStoredMessagesToChatMessages(messages) {
return messages.map(mapStoredMessageToChatMessage);
}
/**
* Transforms an array of `BaseMessage` instances into an array of
* `StoredMessage` instances. It does this by calling the `toDict` method
* on each `BaseMessage`, which returns a `StoredMessage`. This function
* is used to prepare chat messages for storage.
*/
export function mapChatMessagesToStoredMessages(messages) {
return messages.map((message) => message.toDict());
}
export function convertToChunk(message) {
const type = message._getType();
if (type === "human") {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return new HumanMessageChunk({ ...message });
}
else if (type === "ai") {
let aiChunkFields = {
...message,
};
if ("tool_calls" in aiChunkFields) {
aiChunkFields = {
...aiChunkFields,
tool_call_chunks: aiChunkFields.tool_calls?.map((tc) => ({
...tc,
type: "tool_call_chunk",
index: undefined,
args: JSON.stringify(tc.args),
})),
};
}
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return new AIMessageChunk({ ...aiChunkFields });
}
else if (type === "system") {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return new SystemMessageChunk({ ...message });
}
else if (type === "function") {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return new FunctionMessageChunk({ ...message });
// eslint-disable-next-line @typescript-eslint/no-use-before-define
}
else if (ChatMessage.isInstance(message)) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return new ChatMessageChunk({ ...message });
}
else {
throw new Error("Unknown message type.");
}
}