@langchain/core
Version:
Core LangChain.js abstractions and schemas
341 lines (340 loc) • 13.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AIMessageChunk = exports.AIMessage = void 0;
exports.isAIMessage = isAIMessage;
exports.isAIMessageChunk = isAIMessageChunk;
const json_js_1 = require("../utils/json.cjs");
const base_js_1 = require("./base.cjs");
const tool_js_1 = require("./tool.cjs");
/**
* Represents an AI message in a conversation.
*/
class AIMessage extends base_js_1.BaseMessage {
get lc_aliases() {
// exclude snake case conversion to pascal case
return {
...super.lc_aliases,
tool_calls: "tool_calls",
invalid_tool_calls: "invalid_tool_calls",
};
}
constructor(fields,
/** @deprecated */
kwargs) {
let initParams;
if (typeof fields === "string") {
initParams = {
content: fields,
tool_calls: [],
invalid_tool_calls: [],
additional_kwargs: kwargs ?? {},
};
}
else {
initParams = fields;
const rawToolCalls = initParams.additional_kwargs?.tool_calls;
const toolCalls = initParams.tool_calls;
if (!(rawToolCalls == null) &&
rawToolCalls.length > 0 &&
(toolCalls === undefined || toolCalls.length === 0)) {
console.warn([
"New LangChain packages are available that more efficiently handle",
"tool calling.\n\nPlease upgrade your packages to versions that set",
"message tool calls. e.g., `yarn add @langchain/anthropic`,",
"yarn add @langchain/openai`, etc.",
].join(" "));
}
try {
if (!(rawToolCalls == null) && toolCalls === undefined) {
const [toolCalls, invalidToolCalls] = (0, tool_js_1.defaultToolCallParser)(rawToolCalls);
initParams.tool_calls = toolCalls ?? [];
initParams.invalid_tool_calls = invalidToolCalls ?? [];
}
else {
initParams.tool_calls = initParams.tool_calls ?? [];
initParams.invalid_tool_calls = initParams.invalid_tool_calls ?? [];
}
}
catch (e) {
// Do nothing if parsing fails
initParams.tool_calls = [];
initParams.invalid_tool_calls = [];
}
}
// Sadly, TypeScript only allows super() calls at root if the class has
// properties with initializers, so we have to check types twice.
super(initParams);
// These are typed as optional to avoid breaking changes and allow for casting
// from BaseMessage.
Object.defineProperty(this, "tool_calls", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "invalid_tool_calls", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
/**
* If provided, token usage information associated with the message.
*/
Object.defineProperty(this, "usage_metadata", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
if (typeof initParams !== "string") {
this.tool_calls = initParams.tool_calls ?? this.tool_calls;
this.invalid_tool_calls =
initParams.invalid_tool_calls ?? this.invalid_tool_calls;
}
this.usage_metadata = initParams.usage_metadata;
}
static lc_name() {
return "AIMessage";
}
_getType() {
return "ai";
}
get _printableFields() {
return {
...super._printableFields,
tool_calls: this.tool_calls,
invalid_tool_calls: this.invalid_tool_calls,
usage_metadata: this.usage_metadata,
};
}
}
exports.AIMessage = AIMessage;
function isAIMessage(x) {
return x._getType() === "ai";
}
function isAIMessageChunk(x) {
return x._getType() === "ai";
}
/**
* Represents a chunk of an AI message, which can be concatenated with
* other AI message chunks.
*/
class AIMessageChunk extends base_js_1.BaseMessageChunk {
constructor(fields) {
let initParams;
if (typeof fields === "string") {
initParams = {
content: fields,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
};
}
else if (fields.tool_call_chunks === undefined) {
initParams = {
...fields,
tool_calls: fields.tool_calls ?? [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: fields.usage_metadata !== undefined
? fields.usage_metadata
: undefined,
};
}
else {
const groupedToolCallChunk = fields.tool_call_chunks.reduce((acc, chunk) => {
// Assign a fallback ID if the chunk doesn't have one
// This can happen with tools that have empty schemas
const chunkId = chunk.id || `fallback-${chunk.index || 0}`;
acc[chunkId] = acc[chunkId] ?? [];
acc[chunkId].push(chunk);
return acc;
}, {});
const toolCalls = [];
const invalidToolCalls = [];
for (const [id, chunks] of Object.entries(groupedToolCallChunk)) {
let parsedArgs = {};
const name = chunks[0]?.name ?? "";
const joinedArgs = chunks.map((c) => c.args || "").join("");
const argsStr = joinedArgs.length ? joinedArgs : "{}";
// Use the original ID from the first chunk if it exists, otherwise use the grouped ID
const originalId = chunks[0]?.id || id;
try {
parsedArgs = (0, json_js_1.parsePartialJson)(argsStr);
if (parsedArgs === null ||
typeof parsedArgs !== "object" ||
Array.isArray(parsedArgs)) {
throw new Error("Malformed tool call chunk args.");
}
toolCalls.push({
name,
args: parsedArgs,
id: originalId,
type: "tool_call",
});
}
catch (e) {
invalidToolCalls.push({
name,
args: argsStr,
id: originalId,
error: "Malformed args.",
type: "invalid_tool_call",
});
}
}
initParams = {
...fields,
tool_calls: toolCalls,
invalid_tool_calls: invalidToolCalls,
usage_metadata: fields.usage_metadata !== undefined
? fields.usage_metadata
: undefined,
};
}
// Sadly, TypeScript only allows super() calls at root if the class has
// properties with initializers, so we have to check types twice.
super(initParams);
// Must redeclare tool call fields since there is no multiple inheritance in JS.
// These are typed as optional to avoid breaking changes and allow for casting
// from BaseMessage.
Object.defineProperty(this, "tool_calls", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "invalid_tool_calls", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "tool_call_chunks", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
/**
* If provided, token usage information associated with the message.
*/
Object.defineProperty(this, "usage_metadata", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.tool_call_chunks =
initParams.tool_call_chunks ?? this.tool_call_chunks;
this.tool_calls = initParams.tool_calls ?? this.tool_calls;
this.invalid_tool_calls =
initParams.invalid_tool_calls ?? this.invalid_tool_calls;
this.usage_metadata = initParams.usage_metadata;
}
get lc_aliases() {
// exclude snake case conversion to pascal case
return {
...super.lc_aliases,
tool_calls: "tool_calls",
invalid_tool_calls: "invalid_tool_calls",
tool_call_chunks: "tool_call_chunks",
};
}
static lc_name() {
return "AIMessageChunk";
}
_getType() {
return "ai";
}
get _printableFields() {
return {
...super._printableFields,
tool_calls: this.tool_calls,
tool_call_chunks: this.tool_call_chunks,
invalid_tool_calls: this.invalid_tool_calls,
usage_metadata: this.usage_metadata,
};
}
concat(chunk) {
const combinedFields = {
content: (0, base_js_1.mergeContent)(this.content, chunk.content),
additional_kwargs: (0, base_js_1._mergeDicts)(this.additional_kwargs, chunk.additional_kwargs),
response_metadata: (0, base_js_1._mergeDicts)(this.response_metadata, chunk.response_metadata),
tool_call_chunks: [],
id: this.id ?? chunk.id,
};
if (this.tool_call_chunks !== undefined ||
chunk.tool_call_chunks !== undefined) {
const rawToolCalls = (0, base_js_1._mergeLists)(this.tool_call_chunks, chunk.tool_call_chunks);
if (rawToolCalls !== undefined && rawToolCalls.length > 0) {
combinedFields.tool_call_chunks = rawToolCalls;
}
}
if (this.usage_metadata !== undefined ||
chunk.usage_metadata !== undefined) {
const inputTokenDetails = {
...((this.usage_metadata?.input_token_details?.audio !== undefined ||
chunk.usage_metadata?.input_token_details?.audio !== undefined) && {
audio: (this.usage_metadata?.input_token_details?.audio ?? 0) +
(chunk.usage_metadata?.input_token_details?.audio ?? 0),
}),
...((this.usage_metadata?.input_token_details?.cache_read !==
undefined ||
chunk.usage_metadata?.input_token_details?.cache_read !==
undefined) && {
cache_read: (this.usage_metadata?.input_token_details?.cache_read ?? 0) +
(chunk.usage_metadata?.input_token_details?.cache_read ?? 0),
}),
...((this.usage_metadata?.input_token_details?.cache_creation !==
undefined ||
chunk.usage_metadata?.input_token_details?.cache_creation !==
undefined) && {
cache_creation: (this.usage_metadata?.input_token_details?.cache_creation ?? 0) +
(chunk.usage_metadata?.input_token_details?.cache_creation ?? 0),
}),
};
const outputTokenDetails = {
...((this.usage_metadata?.output_token_details?.audio !== undefined ||
chunk.usage_metadata?.output_token_details?.audio !== undefined) && {
audio: (this.usage_metadata?.output_token_details?.audio ?? 0) +
(chunk.usage_metadata?.output_token_details?.audio ?? 0),
}),
...((this.usage_metadata?.output_token_details?.reasoning !==
undefined ||
chunk.usage_metadata?.output_token_details?.reasoning !==
undefined) && {
reasoning: (this.usage_metadata?.output_token_details?.reasoning ?? 0) +
(chunk.usage_metadata?.output_token_details?.reasoning ?? 0),
}),
};
const left = this.usage_metadata ?? {
input_tokens: 0,
output_tokens: 0,
total_tokens: 0,
};
const right = chunk.usage_metadata ?? {
input_tokens: 0,
output_tokens: 0,
total_tokens: 0,
};
const usage_metadata = {
input_tokens: left.input_tokens + right.input_tokens,
output_tokens: left.output_tokens + right.output_tokens,
total_tokens: left.total_tokens + right.total_tokens,
// Do not include `input_token_details` / `output_token_details` keys in combined fields
// unless their values are defined.
...(Object.keys(inputTokenDetails).length > 0 && {
input_token_details: inputTokenDetails,
}),
...(Object.keys(outputTokenDetails).length > 0 && {
output_token_details: outputTokenDetails,
}),
};
combinedFields.usage_metadata = usage_metadata;
}
return new AIMessageChunk(combinedFields);
}
}
exports.AIMessageChunk = AIMessageChunk;