base.cjs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.isBaseMessageChunk = exports.isBaseMessage = exports._isMessageFieldWithRole = exports.BaseMessageChunk = exports._mergeObj = exports._mergeLists = exports._mergeDicts = exports.isOpenAIToolCallArray = exports.BaseMessage = exports._mergeStatus = exports.mergeContent = void 0;
  4. const serializable_js_1 = require("../load/serializable.cjs");
  5. const content_blocks_js_1 = require("./content_blocks.cjs");
  6. function mergeContent(firstContent, secondContent) {
  7. // If first content is a string
  8. if (typeof firstContent === "string") {
  9. if (firstContent === "") {
  10. return secondContent;
  11. }
  12. if (typeof secondContent === "string") {
  13. return firstContent + secondContent;
  14. }
  15. else if (Array.isArray(secondContent) &&
  16. secondContent.some((c) => (0, content_blocks_js_1.isDataContentBlock)(c))) {
  17. return [
  18. {
  19. type: "text",
  20. source_type: "text",
  21. text: firstContent,
  22. },
  23. ...secondContent,
  24. ];
  25. }
  26. else {
  27. return [{ type: "text", text: firstContent }, ...secondContent];
  28. }
  29. // If both are arrays
  30. }
  31. else if (Array.isArray(secondContent)) {
  32. return (_mergeLists(firstContent, secondContent) ?? [
  33. ...firstContent,
  34. ...secondContent,
  35. ]);
  36. }
  37. else {
  38. if (secondContent === "") {
  39. return firstContent;
  40. }
  41. else if (Array.isArray(firstContent) &&
  42. firstContent.some((c) => (0, content_blocks_js_1.isDataContentBlock)(c))) {
  43. return [
  44. ...firstContent,
  45. {
  46. type: "file",
  47. source_type: "text",
  48. text: secondContent,
  49. },
  50. ];
  51. }
  52. else {
  53. return [...firstContent, { type: "text", text: secondContent }];
  54. }
  55. }
  56. }
  57. exports.mergeContent = mergeContent;
  58. /**
  59. * 'Merge' two statuses. If either value passed is 'error', it will return 'error'. Else
  60. * it will return 'success'.
  61. *
  62. * @param {"success" | "error" | undefined} left The existing value to 'merge' with the new value.
  63. * @param {"success" | "error" | undefined} right The new value to 'merge' with the existing value
  64. * @returns {"success" | "error"} The 'merged' value.
  65. */
  66. function _mergeStatus(left, right) {
  67. if (left === "error" || right === "error") {
  68. return "error";
  69. }
  70. return "success";
  71. }
  72. exports._mergeStatus = _mergeStatus;
  73. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  74. function stringifyWithDepthLimit(obj, depthLimit) {
  75. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  76. function helper(obj, currentDepth) {
  77. if (typeof obj !== "object" || obj === null || obj === undefined) {
  78. return obj;
  79. }
  80. if (currentDepth >= depthLimit) {
  81. if (Array.isArray(obj)) {
  82. return "[Array]";
  83. }
  84. return "[Object]";
  85. }
  86. if (Array.isArray(obj)) {
  87. return obj.map((item) => helper(item, currentDepth + 1));
  88. }
  89. const result = {};
  90. for (const key of Object.keys(obj)) {
  91. result[key] = helper(obj[key], currentDepth + 1);
  92. }
  93. return result;
  94. }
  95. return JSON.stringify(helper(obj, 0), null, 2);
  96. }
  97. /**
  98. * Base class for all types of messages in a conversation. It includes
  99. * properties like `content`, `name`, and `additional_kwargs`. It also
  100. * includes methods like `toDict()` and `_getType()`.
  101. */
  102. class BaseMessage extends serializable_js_1.Serializable {
  103. get lc_aliases() {
  104. // exclude snake case conversion to pascal case
  105. return {
  106. additional_kwargs: "additional_kwargs",
  107. response_metadata: "response_metadata",
  108. };
  109. }
  110. /**
  111. * Get text content of the message.
  112. */
  113. get text() {
  114. if (typeof this.content === "string") {
  115. return this.content;
  116. }
  117. if (!Array.isArray(this.content))
  118. return "";
  119. return this.content
  120. .map((c) => {
  121. if (typeof c === "string")
  122. return c;
  123. if (c.type === "text")
  124. return c.text;
  125. return "";
  126. })
  127. .join("");
  128. }
  129. /** The type of the message. */
  130. getType() {
  131. return this._getType();
  132. }
  133. constructor(fields,
  134. /** @deprecated */
  135. kwargs) {
  136. if (typeof fields === "string") {
  137. // eslint-disable-next-line no-param-reassign
  138. fields = {
  139. content: fields,
  140. additional_kwargs: kwargs,
  141. response_metadata: {},
  142. };
  143. }
  144. // Make sure the default value for additional_kwargs is passed into super() for serialization
  145. if (!fields.additional_kwargs) {
  146. // eslint-disable-next-line no-param-reassign
  147. fields.additional_kwargs = {};
  148. }
  149. if (!fields.response_metadata) {
  150. // eslint-disable-next-line no-param-reassign
  151. fields.response_metadata = {};
  152. }
  153. super(fields);
  154. Object.defineProperty(this, "lc_namespace", {
  155. enumerable: true,
  156. configurable: true,
  157. writable: true,
  158. value: ["langchain_core", "messages"]
  159. });
  160. Object.defineProperty(this, "lc_serializable", {
  161. enumerable: true,
  162. configurable: true,
  163. writable: true,
  164. value: true
  165. });
  166. /** The content of the message. */
  167. Object.defineProperty(this, "content", {
  168. enumerable: true,
  169. configurable: true,
  170. writable: true,
  171. value: void 0
  172. });
  173. /** The name of the message sender in a multi-user chat. */
  174. Object.defineProperty(this, "name", {
  175. enumerable: true,
  176. configurable: true,
  177. writable: true,
  178. value: void 0
  179. });
  180. /** Additional keyword arguments */
  181. Object.defineProperty(this, "additional_kwargs", {
  182. enumerable: true,
  183. configurable: true,
  184. writable: true,
  185. value: void 0
  186. });
  187. /** Response metadata. For example: response headers, logprobs, token counts. */
  188. Object.defineProperty(this, "response_metadata", {
  189. enumerable: true,
  190. configurable: true,
  191. writable: true,
  192. value: void 0
  193. });
  194. /**
  195. * An optional unique identifier for the message. This should ideally be
  196. * provided by the provider/model which created the message.
  197. */
  198. Object.defineProperty(this, "id", {
  199. enumerable: true,
  200. configurable: true,
  201. writable: true,
  202. value: void 0
  203. });
  204. this.name = fields.name;
  205. this.content = fields.content;
  206. this.additional_kwargs = fields.additional_kwargs;
  207. this.response_metadata = fields.response_metadata;
  208. this.id = fields.id;
  209. }
  210. toDict() {
  211. return {
  212. type: this._getType(),
  213. data: this.toJSON()
  214. .kwargs,
  215. };
  216. }
  217. static lc_name() {
  218. return "BaseMessage";
  219. }
  220. // Can't be protected for silly reasons
  221. get _printableFields() {
  222. return {
  223. id: this.id,
  224. content: this.content,
  225. name: this.name,
  226. additional_kwargs: this.additional_kwargs,
  227. response_metadata: this.response_metadata,
  228. };
  229. }
  230. // this private method is used to update the ID for the runtime
  231. // value as well as in lc_kwargs for serialisation
  232. _updateId(value) {
  233. this.id = value;
  234. // lc_attributes wouldn't work here, because jest compares the
  235. // whole object
  236. this.lc_kwargs.id = value;
  237. }
  238. get [Symbol.toStringTag]() {
  239. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  240. return this.constructor.lc_name();
  241. }
  242. // Override the default behavior of console.log
  243. [Symbol.for("nodejs.util.inspect.custom")](depth) {
  244. if (depth === null) {
  245. return this;
  246. }
  247. const printable = stringifyWithDepthLimit(this._printableFields, Math.max(4, depth));
  248. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  249. return `${this.constructor.lc_name()} ${printable}`;
  250. }
  251. }
  252. exports.BaseMessage = BaseMessage;
  253. function isOpenAIToolCallArray(value) {
  254. return (Array.isArray(value) &&
  255. value.every((v) => typeof v.index === "number"));
  256. }
  257. exports.isOpenAIToolCallArray = isOpenAIToolCallArray;
  258. function _mergeDicts(
  259. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  260. left,
  261. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  262. right
  263. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  264. ) {
  265. const merged = { ...left };
  266. for (const [key, value] of Object.entries(right)) {
  267. if (merged[key] == null) {
  268. merged[key] = value;
  269. }
  270. else if (value == null) {
  271. continue;
  272. }
  273. else if (typeof merged[key] !== typeof value ||
  274. Array.isArray(merged[key]) !== Array.isArray(value)) {
  275. throw new Error(`field[${key}] already exists in the message chunk, but with a different type.`);
  276. }
  277. else if (typeof merged[key] === "string") {
  278. if (key === "type") {
  279. // Do not merge 'type' fields
  280. continue;
  281. }
  282. merged[key] += value;
  283. }
  284. else if (typeof merged[key] === "object" && !Array.isArray(merged[key])) {
  285. merged[key] = _mergeDicts(merged[key], value);
  286. }
  287. else if (Array.isArray(merged[key])) {
  288. merged[key] = _mergeLists(merged[key], value);
  289. }
  290. else if (merged[key] === value) {
  291. continue;
  292. }
  293. else {
  294. console.warn(`field[${key}] already exists in this message chunk and value has unsupported type.`);
  295. }
  296. }
  297. return merged;
  298. }
  299. exports._mergeDicts = _mergeDicts;
  300. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  301. function _mergeLists(left, right) {
  302. if (left === undefined && right === undefined) {
  303. return undefined;
  304. }
  305. else if (left === undefined || right === undefined) {
  306. return left || right;
  307. }
  308. else {
  309. const merged = [...left];
  310. for (const item of right) {
  311. if (typeof item === "object" &&
  312. "index" in item &&
  313. typeof item.index === "number") {
  314. const toMerge = merged.findIndex((leftItem) => leftItem.index === item.index);
  315. if (toMerge !== -1) {
  316. merged[toMerge] = _mergeDicts(merged[toMerge], item);
  317. }
  318. else {
  319. merged.push(item);
  320. }
  321. }
  322. else if (typeof item === "object" &&
  323. "text" in item &&
  324. item.text === "") {
  325. // No-op - skip empty text blocks
  326. continue;
  327. }
  328. else {
  329. merged.push(item);
  330. }
  331. }
  332. return merged;
  333. }
  334. }
  335. exports._mergeLists = _mergeLists;
  336. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  337. function _mergeObj(left, right) {
  338. if (!left && !right) {
  339. throw new Error("Cannot merge two undefined objects.");
  340. }
  341. if (!left || !right) {
  342. return left || right;
  343. }
  344. else if (typeof left !== typeof right) {
  345. throw new Error(`Cannot merge objects of different types.\nLeft ${typeof left}\nRight ${typeof right}`);
  346. }
  347. else if (typeof left === "string" && typeof right === "string") {
  348. return (left + right);
  349. }
  350. else if (Array.isArray(left) && Array.isArray(right)) {
  351. return _mergeLists(left, right);
  352. }
  353. else if (typeof left === "object" && typeof right === "object") {
  354. return _mergeDicts(left, right);
  355. }
  356. else if (left === right) {
  357. return left;
  358. }
  359. else {
  360. throw new Error(`Can not merge objects of different types.\nLeft ${left}\nRight ${right}`);
  361. }
  362. }
  363. exports._mergeObj = _mergeObj;
  364. /**
  365. * Represents a chunk of a message, which can be concatenated with other
  366. * message chunks. It includes a method `_merge_kwargs_dict()` for merging
  367. * additional keyword arguments from another `BaseMessageChunk` into this
  368. * one. It also overrides the `__add__()` method to support concatenation
  369. * of `BaseMessageChunk` instances.
  370. */
  371. class BaseMessageChunk extends BaseMessage {
  372. }
  373. exports.BaseMessageChunk = BaseMessageChunk;
  374. function _isMessageFieldWithRole(x) {
  375. return typeof x.role === "string";
  376. }
  377. exports._isMessageFieldWithRole = _isMessageFieldWithRole;
  378. function isBaseMessage(messageLike) {
  379. return typeof messageLike?._getType === "function";
  380. }
  381. exports.isBaseMessage = isBaseMessage;
  382. function isBaseMessageChunk(messageLike) {
  383. return (isBaseMessage(messageLike) &&
  384. typeof messageLike.concat === "function");
  385. }
  386. exports.isBaseMessageChunk = isBaseMessageChunk;