base.cjs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.BaseTracer = exports.isBaseTracer = void 0;
  4. const base_js_1 = require("../callbacks/base.cjs");
  5. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  6. function _coerceToDict(value, defaultKey) {
  7. return value && !Array.isArray(value) && typeof value === "object"
  8. ? value
  9. : { [defaultKey]: value };
  10. }
  11. function stripNonAlphanumeric(input) {
  12. return input.replace(/[-:.]/g, "");
  13. }
  14. function convertToDottedOrderFormat(epoch, runId, executionOrder) {
  15. const paddedOrder = executionOrder.toFixed(0).slice(0, 3).padStart(3, "0");
  16. return (stripNonAlphanumeric(`${new Date(epoch).toISOString().slice(0, -1)}${paddedOrder}Z`) + runId);
  17. }
  18. function isBaseTracer(x) {
  19. return typeof x._addRunToRunMap === "function";
  20. }
  21. exports.isBaseTracer = isBaseTracer;
  22. class BaseTracer extends base_js_1.BaseCallbackHandler {
  23. constructor(_fields) {
  24. super(...arguments);
  25. Object.defineProperty(this, "runMap", {
  26. enumerable: true,
  27. configurable: true,
  28. writable: true,
  29. value: new Map()
  30. });
  31. }
  32. copy() {
  33. return this;
  34. }
  35. stringifyError(error) {
  36. // eslint-disable-next-line no-instanceof/no-instanceof
  37. if (error instanceof Error) {
  38. return error.message + (error?.stack ? `\n\n${error.stack}` : "");
  39. }
  40. if (typeof error === "string") {
  41. return error;
  42. }
  43. return `${error}`;
  44. }
  45. _addChildRun(parentRun, childRun) {
  46. parentRun.child_runs.push(childRun);
  47. }
  48. _addRunToRunMap(run) {
  49. const currentDottedOrder = convertToDottedOrderFormat(run.start_time, run.id, run.execution_order);
  50. const storedRun = { ...run };
  51. if (storedRun.parent_run_id !== undefined) {
  52. const parentRun = this.runMap.get(storedRun.parent_run_id);
  53. if (parentRun) {
  54. this._addChildRun(parentRun, storedRun);
  55. parentRun.child_execution_order = Math.max(parentRun.child_execution_order, storedRun.child_execution_order);
  56. storedRun.trace_id = parentRun.trace_id;
  57. if (parentRun.dotted_order !== undefined) {
  58. storedRun.dotted_order = [
  59. parentRun.dotted_order,
  60. currentDottedOrder,
  61. ].join(".");
  62. }
  63. else {
  64. // This can happen naturally for callbacks added within a run
  65. // console.debug(`Parent run with UUID ${storedRun.parent_run_id} has no dotted order.`);
  66. }
  67. }
  68. else {
  69. // This can happen naturally for callbacks added within a run
  70. // console.debug(
  71. // `Parent run with UUID ${storedRun.parent_run_id} not found.`
  72. // );
  73. }
  74. }
  75. else {
  76. storedRun.trace_id = storedRun.id;
  77. storedRun.dotted_order = currentDottedOrder;
  78. }
  79. this.runMap.set(storedRun.id, storedRun);
  80. return storedRun;
  81. }
  82. async _endTrace(run) {
  83. const parentRun = run.parent_run_id !== undefined && this.runMap.get(run.parent_run_id);
  84. if (parentRun) {
  85. parentRun.child_execution_order = Math.max(parentRun.child_execution_order, run.child_execution_order);
  86. }
  87. else {
  88. await this.persistRun(run);
  89. }
  90. this.runMap.delete(run.id);
  91. await this.onRunUpdate?.(run);
  92. }
  93. _getExecutionOrder(parentRunId) {
  94. const parentRun = parentRunId !== undefined && this.runMap.get(parentRunId);
  95. // If a run has no parent then execution order is 1
  96. if (!parentRun) {
  97. return 1;
  98. }
  99. return parentRun.child_execution_order + 1;
  100. }
  101. /**
  102. * Create and add a run to the run map for LLM start events.
  103. * This must sometimes be done synchronously to avoid race conditions
  104. * when callbacks are backgrounded, so we expose it as a separate method here.
  105. */
  106. _createRunForLLMStart(llm, prompts, runId, parentRunId, extraParams, tags, metadata, name) {
  107. const execution_order = this._getExecutionOrder(parentRunId);
  108. const start_time = Date.now();
  109. const finalExtraParams = metadata
  110. ? { ...extraParams, metadata }
  111. : extraParams;
  112. const run = {
  113. id: runId,
  114. name: name ?? llm.id[llm.id.length - 1],
  115. parent_run_id: parentRunId,
  116. start_time,
  117. serialized: llm,
  118. events: [
  119. {
  120. name: "start",
  121. time: new Date(start_time).toISOString(),
  122. },
  123. ],
  124. inputs: { prompts },
  125. execution_order,
  126. child_runs: [],
  127. child_execution_order: execution_order,
  128. run_type: "llm",
  129. extra: finalExtraParams ?? {},
  130. tags: tags || [],
  131. };
  132. return this._addRunToRunMap(run);
  133. }
  134. async handleLLMStart(llm, prompts, runId, parentRunId, extraParams, tags, metadata, name) {
  135. const run = this.runMap.get(runId) ??
  136. this._createRunForLLMStart(llm, prompts, runId, parentRunId, extraParams, tags, metadata, name);
  137. await this.onRunCreate?.(run);
  138. await this.onLLMStart?.(run);
  139. return run;
  140. }
  141. /**
  142. * Create and add a run to the run map for chat model start events.
  143. * This must sometimes be done synchronously to avoid race conditions
  144. * when callbacks are backgrounded, so we expose it as a separate method here.
  145. */
  146. _createRunForChatModelStart(llm, messages, runId, parentRunId, extraParams, tags, metadata, name) {
  147. const execution_order = this._getExecutionOrder(parentRunId);
  148. const start_time = Date.now();
  149. const finalExtraParams = metadata
  150. ? { ...extraParams, metadata }
  151. : extraParams;
  152. const run = {
  153. id: runId,
  154. name: name ?? llm.id[llm.id.length - 1],
  155. parent_run_id: parentRunId,
  156. start_time,
  157. serialized: llm,
  158. events: [
  159. {
  160. name: "start",
  161. time: new Date(start_time).toISOString(),
  162. },
  163. ],
  164. inputs: { messages },
  165. execution_order,
  166. child_runs: [],
  167. child_execution_order: execution_order,
  168. run_type: "llm",
  169. extra: finalExtraParams ?? {},
  170. tags: tags || [],
  171. };
  172. return this._addRunToRunMap(run);
  173. }
  174. async handleChatModelStart(llm, messages, runId, parentRunId, extraParams, tags, metadata, name) {
  175. const run = this.runMap.get(runId) ??
  176. this._createRunForChatModelStart(llm, messages, runId, parentRunId, extraParams, tags, metadata, name);
  177. await this.onRunCreate?.(run);
  178. await this.onLLMStart?.(run);
  179. return run;
  180. }
  181. async handleLLMEnd(output, runId, _parentRunId, _tags, extraParams) {
  182. const run = this.runMap.get(runId);
  183. if (!run || run?.run_type !== "llm") {
  184. throw new Error("No LLM run to end.");
  185. }
  186. run.end_time = Date.now();
  187. run.outputs = output;
  188. run.events.push({
  189. name: "end",
  190. time: new Date(run.end_time).toISOString(),
  191. });
  192. run.extra = { ...run.extra, ...extraParams };
  193. await this.onLLMEnd?.(run);
  194. await this._endTrace(run);
  195. return run;
  196. }
  197. async handleLLMError(error, runId, _parentRunId, _tags, extraParams) {
  198. const run = this.runMap.get(runId);
  199. if (!run || run?.run_type !== "llm") {
  200. throw new Error("No LLM run to end.");
  201. }
  202. run.end_time = Date.now();
  203. run.error = this.stringifyError(error);
  204. run.events.push({
  205. name: "error",
  206. time: new Date(run.end_time).toISOString(),
  207. });
  208. run.extra = { ...run.extra, ...extraParams };
  209. await this.onLLMError?.(run);
  210. await this._endTrace(run);
  211. return run;
  212. }
  213. /**
  214. * Create and add a run to the run map for chain start events.
  215. * This must sometimes be done synchronously to avoid race conditions
  216. * when callbacks are backgrounded, so we expose it as a separate method here.
  217. */
  218. _createRunForChainStart(chain, inputs, runId, parentRunId, tags, metadata, runType, name) {
  219. const execution_order = this._getExecutionOrder(parentRunId);
  220. const start_time = Date.now();
  221. const run = {
  222. id: runId,
  223. name: name ?? chain.id[chain.id.length - 1],
  224. parent_run_id: parentRunId,
  225. start_time,
  226. serialized: chain,
  227. events: [
  228. {
  229. name: "start",
  230. time: new Date(start_time).toISOString(),
  231. },
  232. ],
  233. inputs,
  234. execution_order,
  235. child_execution_order: execution_order,
  236. run_type: runType ?? "chain",
  237. child_runs: [],
  238. extra: metadata ? { metadata } : {},
  239. tags: tags || [],
  240. };
  241. return this._addRunToRunMap(run);
  242. }
  243. async handleChainStart(chain, inputs, runId, parentRunId, tags, metadata, runType, name) {
  244. const run = this.runMap.get(runId) ??
  245. this._createRunForChainStart(chain, inputs, runId, parentRunId, tags, metadata, runType, name);
  246. await this.onRunCreate?.(run);
  247. await this.onChainStart?.(run);
  248. return run;
  249. }
  250. async handleChainEnd(outputs, runId, _parentRunId, _tags, kwargs) {
  251. const run = this.runMap.get(runId);
  252. if (!run) {
  253. throw new Error("No chain run to end.");
  254. }
  255. run.end_time = Date.now();
  256. run.outputs = _coerceToDict(outputs, "output");
  257. run.events.push({
  258. name: "end",
  259. time: new Date(run.end_time).toISOString(),
  260. });
  261. if (kwargs?.inputs !== undefined) {
  262. run.inputs = _coerceToDict(kwargs.inputs, "input");
  263. }
  264. await this.onChainEnd?.(run);
  265. await this._endTrace(run);
  266. return run;
  267. }
  268. async handleChainError(error, runId, _parentRunId, _tags, kwargs) {
  269. const run = this.runMap.get(runId);
  270. if (!run) {
  271. throw new Error("No chain run to end.");
  272. }
  273. run.end_time = Date.now();
  274. run.error = this.stringifyError(error);
  275. run.events.push({
  276. name: "error",
  277. time: new Date(run.end_time).toISOString(),
  278. });
  279. if (kwargs?.inputs !== undefined) {
  280. run.inputs = _coerceToDict(kwargs.inputs, "input");
  281. }
  282. await this.onChainError?.(run);
  283. await this._endTrace(run);
  284. return run;
  285. }
  286. /**
  287. * Create and add a run to the run map for tool start events.
  288. * This must sometimes be done synchronously to avoid race conditions
  289. * when callbacks are backgrounded, so we expose it as a separate method here.
  290. */
  291. _createRunForToolStart(tool, input, runId, parentRunId, tags, metadata, name) {
  292. const execution_order = this._getExecutionOrder(parentRunId);
  293. const start_time = Date.now();
  294. const run = {
  295. id: runId,
  296. name: name ?? tool.id[tool.id.length - 1],
  297. parent_run_id: parentRunId,
  298. start_time,
  299. serialized: tool,
  300. events: [
  301. {
  302. name: "start",
  303. time: new Date(start_time).toISOString(),
  304. },
  305. ],
  306. inputs: { input },
  307. execution_order,
  308. child_execution_order: execution_order,
  309. run_type: "tool",
  310. child_runs: [],
  311. extra: metadata ? { metadata } : {},
  312. tags: tags || [],
  313. };
  314. return this._addRunToRunMap(run);
  315. }
  316. async handleToolStart(tool, input, runId, parentRunId, tags, metadata, name) {
  317. const run = this.runMap.get(runId) ??
  318. this._createRunForToolStart(tool, input, runId, parentRunId, tags, metadata, name);
  319. await this.onRunCreate?.(run);
  320. await this.onToolStart?.(run);
  321. return run;
  322. }
  323. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  324. async handleToolEnd(output, runId) {
  325. const run = this.runMap.get(runId);
  326. if (!run || run?.run_type !== "tool") {
  327. throw new Error("No tool run to end");
  328. }
  329. run.end_time = Date.now();
  330. run.outputs = { output };
  331. run.events.push({
  332. name: "end",
  333. time: new Date(run.end_time).toISOString(),
  334. });
  335. await this.onToolEnd?.(run);
  336. await this._endTrace(run);
  337. return run;
  338. }
  339. async handleToolError(error, runId) {
  340. const run = this.runMap.get(runId);
  341. if (!run || run?.run_type !== "tool") {
  342. throw new Error("No tool run to end");
  343. }
  344. run.end_time = Date.now();
  345. run.error = this.stringifyError(error);
  346. run.events.push({
  347. name: "error",
  348. time: new Date(run.end_time).toISOString(),
  349. });
  350. await this.onToolError?.(run);
  351. await this._endTrace(run);
  352. return run;
  353. }
  354. async handleAgentAction(action, runId) {
  355. const run = this.runMap.get(runId);
  356. if (!run || run?.run_type !== "chain") {
  357. return;
  358. }
  359. const agentRun = run;
  360. agentRun.actions = agentRun.actions || [];
  361. agentRun.actions.push(action);
  362. agentRun.events.push({
  363. name: "agent_action",
  364. time: new Date().toISOString(),
  365. kwargs: { action },
  366. });
  367. await this.onAgentAction?.(run);
  368. }
  369. async handleAgentEnd(action, runId) {
  370. const run = this.runMap.get(runId);
  371. if (!run || run?.run_type !== "chain") {
  372. return;
  373. }
  374. run.events.push({
  375. name: "agent_end",
  376. time: new Date().toISOString(),
  377. kwargs: { action },
  378. });
  379. await this.onAgentEnd?.(run);
  380. }
  381. /**
  382. * Create and add a run to the run map for retriever start events.
  383. * This must sometimes be done synchronously to avoid race conditions
  384. * when callbacks are backgrounded, so we expose it as a separate method here.
  385. */
  386. _createRunForRetrieverStart(retriever, query, runId, parentRunId, tags, metadata, name) {
  387. const execution_order = this._getExecutionOrder(parentRunId);
  388. const start_time = Date.now();
  389. const run = {
  390. id: runId,
  391. name: name ?? retriever.id[retriever.id.length - 1],
  392. parent_run_id: parentRunId,
  393. start_time,
  394. serialized: retriever,
  395. events: [
  396. {
  397. name: "start",
  398. time: new Date(start_time).toISOString(),
  399. },
  400. ],
  401. inputs: { query },
  402. execution_order,
  403. child_execution_order: execution_order,
  404. run_type: "retriever",
  405. child_runs: [],
  406. extra: metadata ? { metadata } : {},
  407. tags: tags || [],
  408. };
  409. return this._addRunToRunMap(run);
  410. }
  411. async handleRetrieverStart(retriever, query, runId, parentRunId, tags, metadata, name) {
  412. const run = this.runMap.get(runId) ??
  413. this._createRunForRetrieverStart(retriever, query, runId, parentRunId, tags, metadata, name);
  414. await this.onRunCreate?.(run);
  415. await this.onRetrieverStart?.(run);
  416. return run;
  417. }
  418. async handleRetrieverEnd(documents, runId) {
  419. const run = this.runMap.get(runId);
  420. if (!run || run?.run_type !== "retriever") {
  421. throw new Error("No retriever run to end");
  422. }
  423. run.end_time = Date.now();
  424. run.outputs = { documents };
  425. run.events.push({
  426. name: "end",
  427. time: new Date(run.end_time).toISOString(),
  428. });
  429. await this.onRetrieverEnd?.(run);
  430. await this._endTrace(run);
  431. return run;
  432. }
  433. async handleRetrieverError(error, runId) {
  434. const run = this.runMap.get(runId);
  435. if (!run || run?.run_type !== "retriever") {
  436. throw new Error("No retriever run to end");
  437. }
  438. run.end_time = Date.now();
  439. run.error = this.stringifyError(error);
  440. run.events.push({
  441. name: "error",
  442. time: new Date(run.end_time).toISOString(),
  443. });
  444. await this.onRetrieverError?.(run);
  445. await this._endTrace(run);
  446. return run;
  447. }
  448. async handleText(text, runId) {
  449. const run = this.runMap.get(runId);
  450. if (!run || run?.run_type !== "chain") {
  451. return;
  452. }
  453. run.events.push({
  454. name: "text",
  455. time: new Date().toISOString(),
  456. kwargs: { text },
  457. });
  458. await this.onText?.(run);
  459. }
  460. async handleLLMNewToken(token, idx, runId, _parentRunId, _tags, fields) {
  461. const run = this.runMap.get(runId);
  462. if (!run || run?.run_type !== "llm") {
  463. throw new Error(`Invalid "runId" provided to "handleLLMNewToken" callback.`);
  464. }
  465. run.events.push({
  466. name: "new_token",
  467. time: new Date().toISOString(),
  468. kwargs: { token, idx, chunk: fields?.chunk },
  469. });
  470. await this.onLLMNewToken?.(run, token, { chunk: fields?.chunk });
  471. return run;
  472. }
  473. }
  474. exports.BaseTracer = BaseTracer;