@langchain/core
Version:
Core LangChain.js abstractions and schemas
268 lines (266 loc) • 10 kB
JavaScript
import { Serializable } from "../load/serializable.js";
import { isDataContentBlock } from "./content/data.js";
import { convertToV1FromAnthropicInput } from "./block_translators/anthropic.js";
import { convertToV1FromDataContent } from "./block_translators/data.js";
import { convertToV1FromChatCompletionsInput } from "./block_translators/openai.js";
import { isMessage } from "./message.js";
import { convertToFormattedString } from "./format.js";
//#region src/messages/base.ts
/** @internal */
const MESSAGE_SYMBOL = Symbol.for("langchain.message");
function mergeContent(firstContent, secondContent) {
if (typeof firstContent === "string") {
if (firstContent === "") return secondContent;
if (typeof secondContent === "string") return firstContent + secondContent;
else if (Array.isArray(secondContent) && secondContent.some((c) => isDataContentBlock(c))) return [{
type: "text",
source_type: "text",
text: firstContent
}, ...secondContent];
else return [{
type: "text",
text: firstContent
}, ...secondContent];
} else if (Array.isArray(secondContent)) return _mergeLists(firstContent, secondContent) ?? [...firstContent, ...secondContent];
else if (secondContent === "") return firstContent;
else if (Array.isArray(firstContent) && firstContent.some((c) => isDataContentBlock(c))) return [...firstContent, {
type: "file",
source_type: "text",
text: secondContent
}];
else return [...firstContent, {
type: "text",
text: secondContent
}];
}
/**
* 'Merge' two statuses. If either value passed is 'error', it will return 'error'. Else
* it will return 'success'.
*
* @param {"success" | "error" | undefined} left The existing value to 'merge' with the new value.
* @param {"success" | "error" | undefined} right The new value to 'merge' with the existing value
* @returns {"success" | "error"} The 'merged' value.
*/
function _mergeStatus(left, right) {
if (left === "error" || right === "error") return "error";
return "success";
}
function stringifyWithDepthLimit(obj, depthLimit) {
function helper(obj$1, currentDepth) {
if (typeof obj$1 !== "object" || obj$1 === null || obj$1 === void 0) return obj$1;
if (currentDepth >= depthLimit) {
if (Array.isArray(obj$1)) return "[Array]";
return "[Object]";
}
if (Array.isArray(obj$1)) return obj$1.map((item) => helper(item, currentDepth + 1));
const result = {};
for (const key of Object.keys(obj$1)) result[key] = helper(obj$1[key], currentDepth + 1);
return result;
}
return JSON.stringify(helper(obj, 0), null, 2);
}
/**
* Base class for all types of messages in a conversation. It includes
* properties like `content`, `name`, and `additional_kwargs`. It also
* includes methods like `toDict()` and `_getType()`.
*/
var BaseMessage = class extends Serializable {
lc_namespace = ["langchain_core", "messages"];
lc_serializable = true;
get lc_aliases() {
return {
additional_kwargs: "additional_kwargs",
response_metadata: "response_metadata"
};
}
[MESSAGE_SYMBOL] = true;
id;
name;
content;
additional_kwargs;
response_metadata;
/**
* @deprecated Use .getType() instead or import the proper typeguard.
* For example:
*
* ```ts
* import { isAIMessage } from "@langchain/core/messages";
*
* const message = new AIMessage("Hello!");
* isAIMessage(message); // true
* ```
*/
_getType() {
return this.type;
}
/**
* @deprecated Use .type instead
* The type of the message.
*/
getType() {
return this._getType();
}
constructor(arg) {
const fields = typeof arg === "string" || Array.isArray(arg) ? { content: arg } : arg;
if (!fields.additional_kwargs) fields.additional_kwargs = {};
if (!fields.response_metadata) fields.response_metadata = {};
super(fields);
this.name = fields.name;
if (fields.content === void 0 && fields.contentBlocks !== void 0) {
this.content = fields.contentBlocks;
this.response_metadata = {
output_version: "v1",
...fields.response_metadata
};
} else if (fields.content !== void 0) {
this.content = fields.content ?? [];
this.response_metadata = fields.response_metadata;
} else {
this.content = [];
this.response_metadata = fields.response_metadata;
}
this.additional_kwargs = fields.additional_kwargs;
this.id = fields.id;
}
/** Get text content of the message. */
get text() {
if (typeof this.content === "string") return this.content;
if (!Array.isArray(this.content)) return "";
return this.content.map((c) => {
if (typeof c === "string") return c;
if (c.type === "text") return c.text;
return "";
}).join("");
}
get contentBlocks() {
const blocks = typeof this.content === "string" ? [{
type: "text",
text: this.content
}] : this.content;
const parsingSteps = [
convertToV1FromDataContent,
convertToV1FromChatCompletionsInput,
convertToV1FromAnthropicInput
];
const parsedBlocks = parsingSteps.reduce((blocks$1, step) => step(blocks$1), blocks);
return parsedBlocks;
}
toDict() {
return {
type: this.getType(),
data: this.toJSON().kwargs
};
}
static lc_name() {
return "BaseMessage";
}
get _printableFields() {
return {
id: this.id,
content: this.content,
name: this.name,
additional_kwargs: this.additional_kwargs,
response_metadata: this.response_metadata
};
}
static isInstance(obj) {
return typeof obj === "object" && obj !== null && MESSAGE_SYMBOL in obj && obj[MESSAGE_SYMBOL] === true && isMessage(obj);
}
_updateId(value) {
this.id = value;
this.lc_kwargs.id = value;
}
get [Symbol.toStringTag]() {
return this.constructor.lc_name();
}
[Symbol.for("nodejs.util.inspect.custom")](depth) {
if (depth === null) return this;
const printable = stringifyWithDepthLimit(this._printableFields, Math.max(4, depth));
return `${this.constructor.lc_name()} ${printable}`;
}
toFormattedString(format = "pretty") {
return convertToFormattedString(this, format);
}
};
function isOpenAIToolCallArray(value) {
return Array.isArray(value) && value.every((v) => typeof v.index === "number");
}
function _mergeDicts(left = {}, right = {}) {
const merged = { ...left };
for (const [key, value] of Object.entries(right)) if (merged[key] == null) merged[key] = value;
else if (value == null) continue;
else if (typeof merged[key] !== typeof value || Array.isArray(merged[key]) !== Array.isArray(value)) throw new Error(`field[${key}] already exists in the message chunk, but with a different type.`);
else if (typeof merged[key] === "string") if (key === "type") continue;
else if ([
"id",
"name",
"output_version",
"model_provider"
].includes(key)) merged[key] = value;
else merged[key] += value;
else if (typeof merged[key] === "object" && !Array.isArray(merged[key])) merged[key] = _mergeDicts(merged[key], value);
else if (Array.isArray(merged[key])) merged[key] = _mergeLists(merged[key], value);
else if (merged[key] === value) continue;
else console.warn(`field[${key}] already exists in this message chunk and value has unsupported type.`);
return merged;
}
function _mergeLists(left, right) {
if (left === void 0 && right === void 0) return void 0;
else if (left === void 0 || right === void 0) return left || right;
else {
const merged = [...left];
for (const item of right) if (typeof item === "object" && item !== null && "index" in item && typeof item.index === "number") {
const toMerge = merged.findIndex((leftItem) => {
const isObject = typeof leftItem === "object";
const indiciesMatch = "index" in leftItem && leftItem.index === item.index;
const idsMatch = "id" in leftItem && "id" in item && leftItem?.id === item?.id;
const eitherItemMissingID = !("id" in leftItem) || !leftItem?.id || !("id" in item) || !item?.id;
return isObject && indiciesMatch && (idsMatch || eitherItemMissingID);
});
if (toMerge !== -1 && typeof merged[toMerge] === "object" && merged[toMerge] !== null) merged[toMerge] = _mergeDicts(merged[toMerge], item);
else merged.push(item);
} else if (typeof item === "object" && item !== null && "text" in item && item.text === "") continue;
else merged.push(item);
return merged;
}
}
function _mergeObj(left, right) {
if (!left && !right) throw new Error("Cannot merge two undefined objects.");
if (!left || !right) return left || right;
else if (typeof left !== typeof right) throw new Error(`Cannot merge objects of different types.\nLeft ${typeof left}\nRight ${typeof right}`);
else if (typeof left === "string" && typeof right === "string") return left + right;
else if (Array.isArray(left) && Array.isArray(right)) return _mergeLists(left, right);
else if (typeof left === "object" && typeof right === "object") return _mergeDicts(left, right);
else if (left === right) return left;
else throw new Error(`Can not merge objects of different types.\nLeft ${left}\nRight ${right}`);
}
/**
* Represents a chunk of a message, which can be concatenated with other
* message chunks. It includes a method `_merge_kwargs_dict()` for merging
* additional keyword arguments from another `BaseMessageChunk` into this
* one. It also overrides the `__add__()` method to support concatenation
* of `BaseMessageChunk` instances.
*/
var BaseMessageChunk = class extends BaseMessage {
static isInstance(obj) {
return super.isInstance(obj) && "concat" in obj && typeof obj.concat === "function";
}
};
function _isMessageFieldWithRole(x) {
return typeof x.role === "string";
}
/**
* @deprecated Use {@link BaseMessage.isInstance} instead
*/
function isBaseMessage(messageLike) {
return typeof messageLike?._getType === "function";
}
/**
* @deprecated Use {@link BaseMessageChunk.isInstance} instead
*/
function isBaseMessageChunk(messageLike) {
return isBaseMessage(messageLike) && typeof messageLike.concat === "function";
}
//#endregion
export { BaseMessage, BaseMessageChunk, _isMessageFieldWithRole, _mergeDicts, _mergeLists, _mergeObj, _mergeStatus, isBaseMessage, isBaseMessageChunk, isOpenAIToolCallArray, mergeContent };
//# sourceMappingURL=base.js.map