UNPKG

@langchain/core

Version:
190 lines (189 loc) 7.07 kB
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."); } }