utils.cjs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.convertToChunk = exports.mapChatMessagesToStoredMessages = exports.mapStoredMessagesToChatMessages = exports.mapStoredMessageToChatMessage = exports.getBufferString = exports.coerceMessageLikeToMessage = void 0;
  4. const index_js_1 = require("../errors/index.cjs");
  5. const utils_js_1 = require("../tools/utils.cjs");
  6. const ai_js_1 = require("./ai.cjs");
  7. const base_js_1 = require("./base.cjs");
  8. const chat_js_1 = require("./chat.cjs");
  9. const function_js_1 = require("./function.cjs");
  10. const human_js_1 = require("./human.cjs");
  11. const system_js_1 = require("./system.cjs");
  12. const tool_js_1 = require("./tool.cjs");
  13. function _coerceToolCall(toolCall) {
  14. if ((0, utils_js_1._isToolCall)(toolCall)) {
  15. return toolCall;
  16. }
  17. else if (typeof toolCall.id === "string" &&
  18. toolCall.type === "function" &&
  19. typeof toolCall.function === "object" &&
  20. toolCall.function !== null &&
  21. "arguments" in toolCall.function &&
  22. typeof toolCall.function.arguments === "string" &&
  23. "name" in toolCall.function &&
  24. typeof toolCall.function.name === "string") {
  25. // Handle OpenAI tool call format
  26. return {
  27. id: toolCall.id,
  28. args: JSON.parse(toolCall.function.arguments),
  29. name: toolCall.function.name,
  30. type: "tool_call",
  31. };
  32. }
  33. else {
  34. // TODO: Throw an error?
  35. return toolCall;
  36. }
  37. }
  38. function isSerializedConstructor(x) {
  39. return (typeof x === "object" &&
  40. x != null &&
  41. x.lc === 1 &&
  42. Array.isArray(x.id) &&
  43. x.kwargs != null &&
  44. typeof x.kwargs === "object");
  45. }
  46. function _constructMessageFromParams(params) {
  47. let type;
  48. let rest;
  49. // Support serialized messages
  50. if (isSerializedConstructor(params)) {
  51. const className = params.id.at(-1);
  52. if (className === "HumanMessage" || className === "HumanMessageChunk") {
  53. type = "user";
  54. }
  55. else if (className === "AIMessage" || className === "AIMessageChunk") {
  56. type = "assistant";
  57. }
  58. else if (className === "SystemMessage" ||
  59. className === "SystemMessageChunk") {
  60. type = "system";
  61. }
  62. else if (className === "FunctionMessage" ||
  63. className === "FunctionMessageChunk") {
  64. type = "function";
  65. }
  66. else if (className === "ToolMessage" ||
  67. className === "ToolMessageChunk") {
  68. type = "tool";
  69. }
  70. else {
  71. type = "unknown";
  72. }
  73. rest = params.kwargs;
  74. }
  75. else {
  76. const { type: extractedType, ...otherParams } = params;
  77. type = extractedType;
  78. rest = otherParams;
  79. }
  80. if (type === "human" || type === "user") {
  81. return new human_js_1.HumanMessage(rest);
  82. }
  83. else if (type === "ai" || type === "assistant") {
  84. const { tool_calls: rawToolCalls, ...other } = rest;
  85. if (!Array.isArray(rawToolCalls)) {
  86. return new ai_js_1.AIMessage(rest);
  87. }
  88. const tool_calls = rawToolCalls.map(_coerceToolCall);
  89. return new ai_js_1.AIMessage({ ...other, tool_calls });
  90. }
  91. else if (type === "system") {
  92. return new system_js_1.SystemMessage(rest);
  93. }
  94. else if (type === "developer") {
  95. return new system_js_1.SystemMessage({
  96. ...rest,
  97. additional_kwargs: {
  98. ...rest.additional_kwargs,
  99. __openai_role__: "developer",
  100. },
  101. });
  102. }
  103. else if (type === "tool" && "tool_call_id" in rest) {
  104. return new tool_js_1.ToolMessage({
  105. ...rest,
  106. content: rest.content,
  107. tool_call_id: rest.tool_call_id,
  108. name: rest.name,
  109. });
  110. }
  111. else {
  112. const error = (0, index_js_1.addLangChainErrorFields)(new Error(`Unable to coerce message from array: only human, AI, system, developer, or tool message coercion is currently supported.\n\nReceived: ${JSON.stringify(params, null, 2)}`), "MESSAGE_COERCION_FAILURE");
  113. throw error;
  114. }
  115. }
  116. function coerceMessageLikeToMessage(messageLike) {
  117. if (typeof messageLike === "string") {
  118. return new human_js_1.HumanMessage(messageLike);
  119. }
  120. else if ((0, base_js_1.isBaseMessage)(messageLike)) {
  121. return messageLike;
  122. }
  123. if (Array.isArray(messageLike)) {
  124. const [type, content] = messageLike;
  125. return _constructMessageFromParams({ type, content });
  126. }
  127. else if ((0, base_js_1._isMessageFieldWithRole)(messageLike)) {
  128. const { role: type, ...rest } = messageLike;
  129. return _constructMessageFromParams({ ...rest, type });
  130. }
  131. else {
  132. return _constructMessageFromParams(messageLike);
  133. }
  134. }
  135. exports.coerceMessageLikeToMessage = coerceMessageLikeToMessage;
  136. /**
  137. * This function is used by memory classes to get a string representation
  138. * of the chat message history, based on the message content and role.
  139. */
  140. function getBufferString(messages, humanPrefix = "Human", aiPrefix = "AI") {
  141. const string_messages = [];
  142. for (const m of messages) {
  143. let role;
  144. if (m._getType() === "human") {
  145. role = humanPrefix;
  146. }
  147. else if (m._getType() === "ai") {
  148. role = aiPrefix;
  149. }
  150. else if (m._getType() === "system") {
  151. role = "System";
  152. }
  153. else if (m._getType() === "function") {
  154. role = "Function";
  155. }
  156. else if (m._getType() === "tool") {
  157. role = "Tool";
  158. }
  159. else if (m._getType() === "generic") {
  160. role = m.role;
  161. }
  162. else {
  163. throw new Error(`Got unsupported message type: ${m._getType()}`);
  164. }
  165. const nameStr = m.name ? `${m.name}, ` : "";
  166. const readableContent = typeof m.content === "string"
  167. ? m.content
  168. : JSON.stringify(m.content, null, 2);
  169. string_messages.push(`${role}: ${nameStr}${readableContent}`);
  170. }
  171. return string_messages.join("\n");
  172. }
  173. exports.getBufferString = getBufferString;
  174. /**
  175. * Maps messages from an older format (V1) to the current `StoredMessage`
  176. * format. If the message is already in the `StoredMessage` format, it is
  177. * returned as is. Otherwise, it transforms the V1 message into a
  178. * `StoredMessage`. This function is important for maintaining
  179. * compatibility with older message formats.
  180. */
  181. function mapV1MessageToStoredMessage(message) {
  182. // TODO: Remove this mapper when we deprecate the old message format.
  183. if (message.data !== undefined) {
  184. return message;
  185. }
  186. else {
  187. const v1Message = message;
  188. return {
  189. type: v1Message.type,
  190. data: {
  191. content: v1Message.text,
  192. role: v1Message.role,
  193. name: undefined,
  194. tool_call_id: undefined,
  195. },
  196. };
  197. }
  198. }
  199. function mapStoredMessageToChatMessage(message) {
  200. const storedMessage = mapV1MessageToStoredMessage(message);
  201. switch (storedMessage.type) {
  202. case "human":
  203. return new human_js_1.HumanMessage(storedMessage.data);
  204. case "ai":
  205. return new ai_js_1.AIMessage(storedMessage.data);
  206. case "system":
  207. return new system_js_1.SystemMessage(storedMessage.data);
  208. case "function":
  209. if (storedMessage.data.name === undefined) {
  210. throw new Error("Name must be defined for function messages");
  211. }
  212. return new function_js_1.FunctionMessage(storedMessage.data);
  213. case "tool":
  214. if (storedMessage.data.tool_call_id === undefined) {
  215. throw new Error("Tool call ID must be defined for tool messages");
  216. }
  217. return new tool_js_1.ToolMessage(storedMessage.data);
  218. case "generic": {
  219. if (storedMessage.data.role === undefined) {
  220. throw new Error("Role must be defined for chat messages");
  221. }
  222. return new chat_js_1.ChatMessage(storedMessage.data);
  223. }
  224. default:
  225. throw new Error(`Got unexpected type: ${storedMessage.type}`);
  226. }
  227. }
  228. exports.mapStoredMessageToChatMessage = mapStoredMessageToChatMessage;
  229. /**
  230. * Transforms an array of `StoredMessage` instances into an array of
  231. * `BaseMessage` instances. It uses the `mapV1MessageToStoredMessage`
  232. * function to ensure all messages are in the `StoredMessage` format, then
  233. * creates new instances of the appropriate `BaseMessage` subclass based
  234. * on the type of each message. This function is used to prepare stored
  235. * messages for use in a chat context.
  236. */
  237. function mapStoredMessagesToChatMessages(messages) {
  238. return messages.map(mapStoredMessageToChatMessage);
  239. }
  240. exports.mapStoredMessagesToChatMessages = mapStoredMessagesToChatMessages;
  241. /**
  242. * Transforms an array of `BaseMessage` instances into an array of
  243. * `StoredMessage` instances. It does this by calling the `toDict` method
  244. * on each `BaseMessage`, which returns a `StoredMessage`. This function
  245. * is used to prepare chat messages for storage.
  246. */
  247. function mapChatMessagesToStoredMessages(messages) {
  248. return messages.map((message) => message.toDict());
  249. }
  250. exports.mapChatMessagesToStoredMessages = mapChatMessagesToStoredMessages;
  251. function convertToChunk(message) {
  252. const type = message._getType();
  253. if (type === "human") {
  254. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  255. return new human_js_1.HumanMessageChunk({ ...message });
  256. }
  257. else if (type === "ai") {
  258. let aiChunkFields = {
  259. ...message,
  260. };
  261. if ("tool_calls" in aiChunkFields) {
  262. aiChunkFields = {
  263. ...aiChunkFields,
  264. tool_call_chunks: aiChunkFields.tool_calls?.map((tc) => ({
  265. ...tc,
  266. type: "tool_call_chunk",
  267. index: undefined,
  268. args: JSON.stringify(tc.args),
  269. })),
  270. };
  271. }
  272. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  273. return new ai_js_1.AIMessageChunk({ ...aiChunkFields });
  274. }
  275. else if (type === "system") {
  276. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  277. return new system_js_1.SystemMessageChunk({ ...message });
  278. }
  279. else if (type === "function") {
  280. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  281. return new function_js_1.FunctionMessageChunk({ ...message });
  282. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  283. }
  284. else if (chat_js_1.ChatMessage.isInstance(message)) {
  285. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  286. return new chat_js_1.ChatMessageChunk({ ...message });
  287. }
  288. else {
  289. throw new Error("Unknown message type.");
  290. }
  291. }
  292. exports.convertToChunk = convertToChunk;