logquill 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -1
- package/dist/index.cjs +188 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -124
- package/dist/index.d.ts +97 -124
- package/dist/index.mjs +182 -3
- package/dist/index.mjs.map +1 -1
- package/dist/langchain.cjs +114 -0
- package/dist/langchain.cjs.map +1 -0
- package/dist/langchain.d.cts +122 -0
- package/dist/langchain.d.ts +122 -0
- package/dist/langchain.mjs +110 -0
- package/dist/langchain.mjs.map +1 -0
- package/dist/logger-D1_THnBJ.d.cts +163 -0
- package/dist/logger-D1_THnBJ.d.ts +163 -0
- package/package.json +20 -10
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var base = require('@langchain/core/callbacks/base');
|
|
4
|
+
|
|
5
|
+
// src/adapters/adapter.ts
|
|
6
|
+
var LogQuillAdapter = class {
|
|
7
|
+
constructor(log) {
|
|
8
|
+
this.log = log;
|
|
9
|
+
}
|
|
10
|
+
log;
|
|
11
|
+
};
|
|
12
|
+
function spanIds(runId, parentRunId) {
|
|
13
|
+
const ids = { spanId: runId };
|
|
14
|
+
if (parentRunId !== void 0) {
|
|
15
|
+
ids.parentSpanId = parentRunId;
|
|
16
|
+
}
|
|
17
|
+
return ids;
|
|
18
|
+
}
|
|
19
|
+
function serializedName(serialized, fallback) {
|
|
20
|
+
const name = serialized?.name;
|
|
21
|
+
if (typeof name === "string" && name) {
|
|
22
|
+
return name;
|
|
23
|
+
}
|
|
24
|
+
const lastIdSegment = serialized?.id.at(-1);
|
|
25
|
+
return typeof lastIdSegment === "string" && lastIdSegment ? lastIdSegment : fallback;
|
|
26
|
+
}
|
|
27
|
+
function formatError(error) {
|
|
28
|
+
if (error instanceof Error) {
|
|
29
|
+
return `${error.name}: ${error.message}`;
|
|
30
|
+
}
|
|
31
|
+
return String(error);
|
|
32
|
+
}
|
|
33
|
+
var LangChainAdapter = class extends base.BaseCallbackHandler {
|
|
34
|
+
name = "logquill";
|
|
35
|
+
log;
|
|
36
|
+
callStarts = /* @__PURE__ */ new Map();
|
|
37
|
+
chainNames = /* @__PURE__ */ new Map();
|
|
38
|
+
constructor(agentLog) {
|
|
39
|
+
super();
|
|
40
|
+
this.log = agentLog;
|
|
41
|
+
}
|
|
42
|
+
takeDurationMs(runId) {
|
|
43
|
+
const start = this.callStarts.get(runId);
|
|
44
|
+
if (start === void 0) {
|
|
45
|
+
return void 0;
|
|
46
|
+
}
|
|
47
|
+
this.callStarts.delete(runId);
|
|
48
|
+
return Math.round((performance.now() - start) * 1e3) / 1e3;
|
|
49
|
+
}
|
|
50
|
+
// -- chains: one span()-shaped record on end/error --------------------
|
|
51
|
+
// `handleChainEnd`/`handleChainError` don't receive the chain's
|
|
52
|
+
// `Serialized` descriptor (only `handleChainStart` does), so the name is
|
|
53
|
+
// captured at start and looked up again at end/error.
|
|
54
|
+
handleChainStart(chain, _inputs, runId) {
|
|
55
|
+
this.callStarts.set(runId, performance.now());
|
|
56
|
+
this.chainNames.set(runId, serializedName(chain, "chain"));
|
|
57
|
+
}
|
|
58
|
+
handleChainEnd(_outputs, runId, parentRunId) {
|
|
59
|
+
const name = this.chainNames.get(runId) ?? "chain";
|
|
60
|
+
this.chainNames.delete(runId);
|
|
61
|
+
this.log.info(name, { kind: "span", ...spanIds(runId, parentRunId), durationMs: this.takeDurationMs(runId) });
|
|
62
|
+
}
|
|
63
|
+
handleChainError(err, runId, parentRunId) {
|
|
64
|
+
const name = this.chainNames.get(runId) ?? "chain";
|
|
65
|
+
this.chainNames.delete(runId);
|
|
66
|
+
this.log.error(name, {
|
|
67
|
+
kind: "span",
|
|
68
|
+
...spanIds(runId, parentRunId),
|
|
69
|
+
durationMs: this.takeDurationMs(runId),
|
|
70
|
+
error: formatError(err)
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// -- LLM calls: action (start) / observation (end) ---------------------
|
|
74
|
+
handleLLMStart(serialized, _prompts, runId, parentRunId) {
|
|
75
|
+
this.callStarts.set(runId, performance.now());
|
|
76
|
+
this.log.action(serializedName(serialized, "llm_start"), spanIds(runId, parentRunId));
|
|
77
|
+
}
|
|
78
|
+
handleLLMEnd(_output, runId, parentRunId) {
|
|
79
|
+
this.log.observation("llm_end", { ...spanIds(runId, parentRunId), durationMs: this.takeDurationMs(runId) });
|
|
80
|
+
}
|
|
81
|
+
handleLLMError(err, runId, parentRunId) {
|
|
82
|
+
this.takeDurationMs(runId);
|
|
83
|
+
this.log.error("llm_error", { error: formatError(err), ...spanIds(runId, parentRunId) });
|
|
84
|
+
}
|
|
85
|
+
// -- agent-level events --------------------------------------------------
|
|
86
|
+
handleAgentAction(action, runId) {
|
|
87
|
+
this.log.action(action.tool || "agent_action", { parentSpanId: runId });
|
|
88
|
+
}
|
|
89
|
+
handleAgentEnd(_action, runId) {
|
|
90
|
+
this.log.decision("agent_finish", { parentSpanId: runId });
|
|
91
|
+
}
|
|
92
|
+
// -- tools: action (start) / observation (end) / error -------------------
|
|
93
|
+
handleToolStart(serialized, _input, runId, parentRunId) {
|
|
94
|
+
this.callStarts.set(runId, performance.now());
|
|
95
|
+
this.log.action(serializedName(serialized, "tool"), spanIds(runId, parentRunId));
|
|
96
|
+
}
|
|
97
|
+
handleToolEnd(_output, runId, parentRunId) {
|
|
98
|
+
this.log.observation("tool_end", { ...spanIds(runId, parentRunId), durationMs: this.takeDurationMs(runId) });
|
|
99
|
+
}
|
|
100
|
+
handleToolError(err, runId, parentRunId) {
|
|
101
|
+
this.takeDurationMs(runId);
|
|
102
|
+
this.log.error("tool_error", { error: formatError(err), ...spanIds(runId, parentRunId) });
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// src/adapters/langgraph-adapter.ts
|
|
107
|
+
var LangGraphAdapter = class extends LangChainAdapter {
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
exports.LangChainAdapter = LangChainAdapter;
|
|
111
|
+
exports.LangGraphAdapter = LangGraphAdapter;
|
|
112
|
+
exports.LogQuillAdapter = LogQuillAdapter;
|
|
113
|
+
//# sourceMappingURL=langchain.cjs.map
|
|
114
|
+
//# sourceMappingURL=langchain.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/adapter.ts","../src/adapters/langchain-adapter.ts","../src/adapters/langgraph-adapter.ts"],"names":["BaseCallbackHandler"],"mappings":"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","file":"langchain.cjs","sourcesContent":["import type { Logger } from \"../core/logger.js\";\n\n/**\n * Base for framework tracing adapters. A concrete adapter holds a\n * reference to the `Logger` to forward events onto (`this.log`) and\n * overrides only the events its framework actually emits — translating\n * them into `.thought()/.action()/.observation()/.decision()` calls and\n * `span()`-shaped records. Always a thin mapping from the framework's\n * native event shape onto LogQuill's, never a reimplementation of tracing\n * logic per framework.\n *\n * `LangChainAdapter` doesn't literally extend this: it has to extend\n * LangChain's own `BaseCallbackHandler` instead (JS classes support only\n * single inheritance), so it holds the same `log` reference itself rather\n * than inheriting it. This base is for adapters that don't need to\n * subclass a framework SDK class.\n */\nexport abstract class LogQuillAdapter {\n constructor(protected readonly log: Logger) {}\n}\n","import { BaseCallbackHandler } from \"@langchain/core/callbacks/base\";\nimport type { AgentAction, AgentFinish } from \"@langchain/core/agents\";\nimport type { Serialized } from \"@langchain/core/load/serializable\";\nimport type { LLMResult } from \"@langchain/core/outputs\";\nimport type { ChainValues } from \"@langchain/core/utils/types\";\nimport type { Logger } from \"../core/logger.js\";\n\nfunction spanIds(runId: string, parentRunId: string | undefined): Record<string, unknown> {\n const ids: Record<string, unknown> = { spanId: runId };\n if (parentRunId !== undefined) {\n ids.parentSpanId = parentRunId;\n }\n return ids;\n}\n\n/**\n * Prefers `serialized.name`, which most `Serialized` variants carry — but\n * a bare `RunnableLambda` (and other constructless runnables), verified\n * against `@langchain/core` 1.2.9, omits it. `id` (e.g.\n * `[\"langchain_core\", \"runnables\", \"RunnableLambda\"]`) is present on\n * every `Serialized` variant, so its last segment — the class name — is a\n * more useful fallback than the generic `fallback` string.\n */\nfunction serializedName(serialized: Serialized | undefined, fallback: string): string {\n const name = (serialized as { name?: unknown } | undefined)?.name;\n if (typeof name === \"string\" && name) {\n return name;\n }\n const lastIdSegment = serialized?.id.at(-1);\n return typeof lastIdSegment === \"string\" && lastIdSegment ? lastIdSegment : fallback;\n}\n\n// LangChain's `handle*Error` callbacks type their `error` parameter as\n// `Error` (really `any` in the base class — see `type Error = any` in\n// `@langchain/core`'s own `callbacks/base.d.ts`), but nothing enforces that\n// at the actual throw site: a tool's `_call`, an LLM provider, or any\n// chain step can `throw` a bare string or plain object just as validly.\n// Trusting `Error` here would crash this adapter's own error handler on\n// exactly the input it exists to report.\nfunction formatError(error: unknown): string {\n if (error instanceof Error) {\n return `${error.name}: ${error.message}`;\n }\n return String(error);\n}\n\n/**\n * Maps LangChain.js's `BaseCallbackHandler` events onto LogQuill calls —\n * LangGraph.js is covered for free (see `LangGraphAdapter`), since it\n * shares LangChain.js's callback system.\n *\n * Pass an instance into a chain/agent invocation's `callbacks: [...]`, the\n * same way any other LangChain tracing handler (LangSmith, Langfuse, ...)\n * is wired in — no other instrumentation needed:\n *\n * ```ts\n * import { Logger, RunPlugin } from \"logquill\";\n * import { LangChainAdapter } from \"logquill/langchain\";\n *\n * const handler = new LangChainAdapter(log.child(\"agent\").use(new RunPlugin()));\n * const llm = new ChatOpenAI({ callbacks: [handler] });\n * ```\n *\n * Event mapping:\n *\n * | LangChain.js callback | LogQuill call |\n * |---|---|\n * | `handleChainStart` / `handleChainEnd` | one `span()`-shaped record on end/error |\n * | `handleLLMStart` / `handleLLMEnd` | `.action()` / `.observation()` with `durationMs` |\n * | `handleAgentAction` | `.action()` |\n * | `handleAgentEnd` | `.decision()` |\n * | `handleToolStart` / `handleToolEnd` / `handleToolError` | `.action()` / `.observation()` / `.error()` |\n *\n * LangChain's own `runId`/`parentRunId` are written directly onto\n * `meta.spanId`/`meta.parentSpanId` on every event — the shapes already\n * match, so this is field renaming, not translation. Chain start/end is\n * stamped manually (rather than via `Logger.span()`, which wraps a single\n * callback) into the same `{ kind: \"span\", spanId, parentSpanId,\n * durationMs }` shape `Logger.span()` itself produces, since LangChain\n * opens and closes a chain run from two separate, independently-scheduled\n * callback invocations — there's no single function to wrap.\n *\n * `handleAgentAction`/`handleAgentEnd` carry the *enclosing* chain's own\n * `runId` (LangChain doesn't mint a fresh one for these events), so it's\n * written as this record's `parentSpanId`, not `spanId` — using it as\n * `spanId` would make the record indistinguishable from the chain's own\n * span-closing record.\n */\nexport class LangChainAdapter extends BaseCallbackHandler {\n name = \"logquill\";\n\n private readonly log: Logger;\n private readonly callStarts = new Map<string, number>();\n private readonly chainNames = new Map<string, string>();\n\n constructor(agentLog: Logger) {\n super();\n this.log = agentLog;\n }\n\n private takeDurationMs(runId: string): number | undefined {\n const start = this.callStarts.get(runId);\n if (start === undefined) {\n return undefined;\n }\n this.callStarts.delete(runId);\n return Math.round((performance.now() - start) * 1000) / 1000;\n }\n\n // -- chains: one span()-shaped record on end/error --------------------\n // `handleChainEnd`/`handleChainError` don't receive the chain's\n // `Serialized` descriptor (only `handleChainStart` does), so the name is\n // captured at start and looked up again at end/error.\n\n handleChainStart(chain: Serialized, _inputs: ChainValues, runId: string): void {\n this.callStarts.set(runId, performance.now());\n this.chainNames.set(runId, serializedName(chain, \"chain\"));\n }\n\n handleChainEnd(_outputs: ChainValues, runId: string, parentRunId: string | undefined): void {\n const name = this.chainNames.get(runId) ?? \"chain\";\n this.chainNames.delete(runId);\n this.log.info(name, { kind: \"span\", ...spanIds(runId, parentRunId), durationMs: this.takeDurationMs(runId) });\n }\n\n handleChainError(err: unknown, runId: string, parentRunId: string | undefined): void {\n const name = this.chainNames.get(runId) ?? \"chain\";\n this.chainNames.delete(runId);\n this.log.error(name, {\n kind: \"span\",\n ...spanIds(runId, parentRunId),\n durationMs: this.takeDurationMs(runId),\n error: formatError(err),\n });\n }\n\n // -- LLM calls: action (start) / observation (end) ---------------------\n\n handleLLMStart(\n serialized: Serialized,\n _prompts: string[],\n runId: string,\n parentRunId?: string,\n ): void {\n this.callStarts.set(runId, performance.now());\n this.log.action(serializedName(serialized, \"llm_start\"), spanIds(runId, parentRunId));\n }\n\n handleLLMEnd(_output: LLMResult, runId: string, parentRunId: string | undefined): void {\n this.log.observation(\"llm_end\", { ...spanIds(runId, parentRunId), durationMs: this.takeDurationMs(runId) });\n }\n\n handleLLMError(err: unknown, runId: string, parentRunId: string | undefined): void {\n this.takeDurationMs(runId);\n this.log.error(\"llm_error\", { error: formatError(err), ...spanIds(runId, parentRunId) });\n }\n\n // -- agent-level events --------------------------------------------------\n\n handleAgentAction(action: AgentAction, runId: string): void {\n this.log.action(action.tool || \"agent_action\", { parentSpanId: runId });\n }\n\n handleAgentEnd(_action: AgentFinish, runId: string): void {\n this.log.decision(\"agent_finish\", { parentSpanId: runId });\n }\n\n // -- tools: action (start) / observation (end) / error -------------------\n\n handleToolStart(\n serialized: Serialized,\n _input: string,\n runId: string,\n parentRunId?: string,\n ): void {\n this.callStarts.set(runId, performance.now());\n this.log.action(serializedName(serialized, \"tool\"), spanIds(runId, parentRunId));\n }\n\n handleToolEnd(_output: unknown, runId: string, parentRunId: string | undefined): void {\n this.log.observation(\"tool_end\", { ...spanIds(runId, parentRunId), durationMs: this.takeDurationMs(runId) });\n }\n\n handleToolError(err: unknown, runId: string, parentRunId: string | undefined): void {\n this.takeDurationMs(runId);\n this.log.error(\"tool_error\", { error: formatError(err), ...spanIds(runId, parentRunId) });\n }\n}\n","import { LangChainAdapter } from \"./langchain-adapter.js\";\n\n/**\n * `LangChainAdapter`, for use with LangGraph.js graphs — exported under its\n * own name for discoverability and parity with `logquill-python`'s\n * `LangGraphAdapter`. LangGraph.js nodes run as ordinary LangChain.js\n * `Runnable`s, so `LangChainAdapter`'s `handleChainStart`/`handleLLMStart`/\n * `handleToolStart`/etc. already fire exactly as they would for a plain\n * chain — no extra mapping needed.\n *\n * Unlike `logquill-python`, this class adds no extra event handling.\n * Python's `LangGraphAdapter` exists specifically to catch LangGraph\n * Python's own checkpoint pause/resume events (`on_interrupt`/`on_resume`),\n * which that ecosystem dispatches only to handlers implementing its own\n * `GraphCallbackHandler` — a plain `BaseCallbackHandler` subclass never\n * receives them there. LangGraph.js has no equivalent: as of `@langchain/\n * langgraph` 1.x it exposes no distinct callback-handler class or\n * `onInterrupt`/`onResume` hook — an `interrupt()` call instead pauses the\n * graph and surfaces in its state/stream output (under `__interrupt__`),\n * not through the callback-handler system at all. There is nothing this\n * class could subscribe to that `LangChainAdapter` doesn't already cover.\n *\n * ```ts\n * import { Logger, RunPlugin } from \"logquill\";\n * import { LangGraphAdapter } from \"logquill/langchain\";\n *\n * const handler = new LangGraphAdapter(log.child(\"agent\").use(new RunPlugin()));\n * const graph = builder.compile({ checkpointer });\n * await graph.invoke(input, { callbacks: [handler], configurable: { thread_id: \"1\" } });\n * ```\n */\nexport class LangGraphAdapter extends LangChainAdapter {}\n"]}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { d as Logger } from './logger-D1_THnBJ.cjs';
|
|
2
|
+
import { BaseCallbackHandler } from '@langchain/core/callbacks/base';
|
|
3
|
+
import { AgentAction, AgentFinish } from '@langchain/core/agents';
|
|
4
|
+
import { Serialized } from '@langchain/core/load/serializable';
|
|
5
|
+
import { LLMResult } from '@langchain/core/outputs';
|
|
6
|
+
import { ChainValues } from '@langchain/core/utils/types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Base for framework tracing adapters. A concrete adapter holds a
|
|
10
|
+
* reference to the `Logger` to forward events onto (`this.log`) and
|
|
11
|
+
* overrides only the events its framework actually emits — translating
|
|
12
|
+
* them into `.thought()/.action()/.observation()/.decision()` calls and
|
|
13
|
+
* `span()`-shaped records. Always a thin mapping from the framework's
|
|
14
|
+
* native event shape onto LogQuill's, never a reimplementation of tracing
|
|
15
|
+
* logic per framework.
|
|
16
|
+
*
|
|
17
|
+
* `LangChainAdapter` doesn't literally extend this: it has to extend
|
|
18
|
+
* LangChain's own `BaseCallbackHandler` instead (JS classes support only
|
|
19
|
+
* single inheritance), so it holds the same `log` reference itself rather
|
|
20
|
+
* than inheriting it. This base is for adapters that don't need to
|
|
21
|
+
* subclass a framework SDK class.
|
|
22
|
+
*/
|
|
23
|
+
declare abstract class LogQuillAdapter {
|
|
24
|
+
protected readonly log: Logger;
|
|
25
|
+
constructor(log: Logger);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Maps LangChain.js's `BaseCallbackHandler` events onto LogQuill calls —
|
|
30
|
+
* LangGraph.js is covered for free (see `LangGraphAdapter`), since it
|
|
31
|
+
* shares LangChain.js's callback system.
|
|
32
|
+
*
|
|
33
|
+
* Pass an instance into a chain/agent invocation's `callbacks: [...]`, the
|
|
34
|
+
* same way any other LangChain tracing handler (LangSmith, Langfuse, ...)
|
|
35
|
+
* is wired in — no other instrumentation needed:
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { Logger, RunPlugin } from "logquill";
|
|
39
|
+
* import { LangChainAdapter } from "logquill/langchain";
|
|
40
|
+
*
|
|
41
|
+
* const handler = new LangChainAdapter(log.child("agent").use(new RunPlugin()));
|
|
42
|
+
* const llm = new ChatOpenAI({ callbacks: [handler] });
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Event mapping:
|
|
46
|
+
*
|
|
47
|
+
* | LangChain.js callback | LogQuill call |
|
|
48
|
+
* |---|---|
|
|
49
|
+
* | `handleChainStart` / `handleChainEnd` | one `span()`-shaped record on end/error |
|
|
50
|
+
* | `handleLLMStart` / `handleLLMEnd` | `.action()` / `.observation()` with `durationMs` |
|
|
51
|
+
* | `handleAgentAction` | `.action()` |
|
|
52
|
+
* | `handleAgentEnd` | `.decision()` |
|
|
53
|
+
* | `handleToolStart` / `handleToolEnd` / `handleToolError` | `.action()` / `.observation()` / `.error()` |
|
|
54
|
+
*
|
|
55
|
+
* LangChain's own `runId`/`parentRunId` are written directly onto
|
|
56
|
+
* `meta.spanId`/`meta.parentSpanId` on every event — the shapes already
|
|
57
|
+
* match, so this is field renaming, not translation. Chain start/end is
|
|
58
|
+
* stamped manually (rather than via `Logger.span()`, which wraps a single
|
|
59
|
+
* callback) into the same `{ kind: "span", spanId, parentSpanId,
|
|
60
|
+
* durationMs }` shape `Logger.span()` itself produces, since LangChain
|
|
61
|
+
* opens and closes a chain run from two separate, independently-scheduled
|
|
62
|
+
* callback invocations — there's no single function to wrap.
|
|
63
|
+
*
|
|
64
|
+
* `handleAgentAction`/`handleAgentEnd` carry the *enclosing* chain's own
|
|
65
|
+
* `runId` (LangChain doesn't mint a fresh one for these events), so it's
|
|
66
|
+
* written as this record's `parentSpanId`, not `spanId` — using it as
|
|
67
|
+
* `spanId` would make the record indistinguishable from the chain's own
|
|
68
|
+
* span-closing record.
|
|
69
|
+
*/
|
|
70
|
+
declare class LangChainAdapter extends BaseCallbackHandler {
|
|
71
|
+
name: string;
|
|
72
|
+
private readonly log;
|
|
73
|
+
private readonly callStarts;
|
|
74
|
+
private readonly chainNames;
|
|
75
|
+
constructor(agentLog: Logger);
|
|
76
|
+
private takeDurationMs;
|
|
77
|
+
handleChainStart(chain: Serialized, _inputs: ChainValues, runId: string): void;
|
|
78
|
+
handleChainEnd(_outputs: ChainValues, runId: string, parentRunId: string | undefined): void;
|
|
79
|
+
handleChainError(err: unknown, runId: string, parentRunId: string | undefined): void;
|
|
80
|
+
handleLLMStart(serialized: Serialized, _prompts: string[], runId: string, parentRunId?: string): void;
|
|
81
|
+
handleLLMEnd(_output: LLMResult, runId: string, parentRunId: string | undefined): void;
|
|
82
|
+
handleLLMError(err: unknown, runId: string, parentRunId: string | undefined): void;
|
|
83
|
+
handleAgentAction(action: AgentAction, runId: string): void;
|
|
84
|
+
handleAgentEnd(_action: AgentFinish, runId: string): void;
|
|
85
|
+
handleToolStart(serialized: Serialized, _input: string, runId: string, parentRunId?: string): void;
|
|
86
|
+
handleToolEnd(_output: unknown, runId: string, parentRunId: string | undefined): void;
|
|
87
|
+
handleToolError(err: unknown, runId: string, parentRunId: string | undefined): void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* `LangChainAdapter`, for use with LangGraph.js graphs — exported under its
|
|
92
|
+
* own name for discoverability and parity with `logquill-python`'s
|
|
93
|
+
* `LangGraphAdapter`. LangGraph.js nodes run as ordinary LangChain.js
|
|
94
|
+
* `Runnable`s, so `LangChainAdapter`'s `handleChainStart`/`handleLLMStart`/
|
|
95
|
+
* `handleToolStart`/etc. already fire exactly as they would for a plain
|
|
96
|
+
* chain — no extra mapping needed.
|
|
97
|
+
*
|
|
98
|
+
* Unlike `logquill-python`, this class adds no extra event handling.
|
|
99
|
+
* Python's `LangGraphAdapter` exists specifically to catch LangGraph
|
|
100
|
+
* Python's own checkpoint pause/resume events (`on_interrupt`/`on_resume`),
|
|
101
|
+
* which that ecosystem dispatches only to handlers implementing its own
|
|
102
|
+
* `GraphCallbackHandler` — a plain `BaseCallbackHandler` subclass never
|
|
103
|
+
* receives them there. LangGraph.js has no equivalent: as of `@langchain/
|
|
104
|
+
* langgraph` 1.x it exposes no distinct callback-handler class or
|
|
105
|
+
* `onInterrupt`/`onResume` hook — an `interrupt()` call instead pauses the
|
|
106
|
+
* graph and surfaces in its state/stream output (under `__interrupt__`),
|
|
107
|
+
* not through the callback-handler system at all. There is nothing this
|
|
108
|
+
* class could subscribe to that `LangChainAdapter` doesn't already cover.
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* import { Logger, RunPlugin } from "logquill";
|
|
112
|
+
* import { LangGraphAdapter } from "logquill/langchain";
|
|
113
|
+
*
|
|
114
|
+
* const handler = new LangGraphAdapter(log.child("agent").use(new RunPlugin()));
|
|
115
|
+
* const graph = builder.compile({ checkpointer });
|
|
116
|
+
* await graph.invoke(input, { callbacks: [handler], configurable: { thread_id: "1" } });
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
declare class LangGraphAdapter extends LangChainAdapter {
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export { LangChainAdapter, LangGraphAdapter, LogQuillAdapter };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { d as Logger } from './logger-D1_THnBJ.js';
|
|
2
|
+
import { BaseCallbackHandler } from '@langchain/core/callbacks/base';
|
|
3
|
+
import { AgentAction, AgentFinish } from '@langchain/core/agents';
|
|
4
|
+
import { Serialized } from '@langchain/core/load/serializable';
|
|
5
|
+
import { LLMResult } from '@langchain/core/outputs';
|
|
6
|
+
import { ChainValues } from '@langchain/core/utils/types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Base for framework tracing adapters. A concrete adapter holds a
|
|
10
|
+
* reference to the `Logger` to forward events onto (`this.log`) and
|
|
11
|
+
* overrides only the events its framework actually emits — translating
|
|
12
|
+
* them into `.thought()/.action()/.observation()/.decision()` calls and
|
|
13
|
+
* `span()`-shaped records. Always a thin mapping from the framework's
|
|
14
|
+
* native event shape onto LogQuill's, never a reimplementation of tracing
|
|
15
|
+
* logic per framework.
|
|
16
|
+
*
|
|
17
|
+
* `LangChainAdapter` doesn't literally extend this: it has to extend
|
|
18
|
+
* LangChain's own `BaseCallbackHandler` instead (JS classes support only
|
|
19
|
+
* single inheritance), so it holds the same `log` reference itself rather
|
|
20
|
+
* than inheriting it. This base is for adapters that don't need to
|
|
21
|
+
* subclass a framework SDK class.
|
|
22
|
+
*/
|
|
23
|
+
declare abstract class LogQuillAdapter {
|
|
24
|
+
protected readonly log: Logger;
|
|
25
|
+
constructor(log: Logger);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Maps LangChain.js's `BaseCallbackHandler` events onto LogQuill calls —
|
|
30
|
+
* LangGraph.js is covered for free (see `LangGraphAdapter`), since it
|
|
31
|
+
* shares LangChain.js's callback system.
|
|
32
|
+
*
|
|
33
|
+
* Pass an instance into a chain/agent invocation's `callbacks: [...]`, the
|
|
34
|
+
* same way any other LangChain tracing handler (LangSmith, Langfuse, ...)
|
|
35
|
+
* is wired in — no other instrumentation needed:
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { Logger, RunPlugin } from "logquill";
|
|
39
|
+
* import { LangChainAdapter } from "logquill/langchain";
|
|
40
|
+
*
|
|
41
|
+
* const handler = new LangChainAdapter(log.child("agent").use(new RunPlugin()));
|
|
42
|
+
* const llm = new ChatOpenAI({ callbacks: [handler] });
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Event mapping:
|
|
46
|
+
*
|
|
47
|
+
* | LangChain.js callback | LogQuill call |
|
|
48
|
+
* |---|---|
|
|
49
|
+
* | `handleChainStart` / `handleChainEnd` | one `span()`-shaped record on end/error |
|
|
50
|
+
* | `handleLLMStart` / `handleLLMEnd` | `.action()` / `.observation()` with `durationMs` |
|
|
51
|
+
* | `handleAgentAction` | `.action()` |
|
|
52
|
+
* | `handleAgentEnd` | `.decision()` |
|
|
53
|
+
* | `handleToolStart` / `handleToolEnd` / `handleToolError` | `.action()` / `.observation()` / `.error()` |
|
|
54
|
+
*
|
|
55
|
+
* LangChain's own `runId`/`parentRunId` are written directly onto
|
|
56
|
+
* `meta.spanId`/`meta.parentSpanId` on every event — the shapes already
|
|
57
|
+
* match, so this is field renaming, not translation. Chain start/end is
|
|
58
|
+
* stamped manually (rather than via `Logger.span()`, which wraps a single
|
|
59
|
+
* callback) into the same `{ kind: "span", spanId, parentSpanId,
|
|
60
|
+
* durationMs }` shape `Logger.span()` itself produces, since LangChain
|
|
61
|
+
* opens and closes a chain run from two separate, independently-scheduled
|
|
62
|
+
* callback invocations — there's no single function to wrap.
|
|
63
|
+
*
|
|
64
|
+
* `handleAgentAction`/`handleAgentEnd` carry the *enclosing* chain's own
|
|
65
|
+
* `runId` (LangChain doesn't mint a fresh one for these events), so it's
|
|
66
|
+
* written as this record's `parentSpanId`, not `spanId` — using it as
|
|
67
|
+
* `spanId` would make the record indistinguishable from the chain's own
|
|
68
|
+
* span-closing record.
|
|
69
|
+
*/
|
|
70
|
+
declare class LangChainAdapter extends BaseCallbackHandler {
|
|
71
|
+
name: string;
|
|
72
|
+
private readonly log;
|
|
73
|
+
private readonly callStarts;
|
|
74
|
+
private readonly chainNames;
|
|
75
|
+
constructor(agentLog: Logger);
|
|
76
|
+
private takeDurationMs;
|
|
77
|
+
handleChainStart(chain: Serialized, _inputs: ChainValues, runId: string): void;
|
|
78
|
+
handleChainEnd(_outputs: ChainValues, runId: string, parentRunId: string | undefined): void;
|
|
79
|
+
handleChainError(err: unknown, runId: string, parentRunId: string | undefined): void;
|
|
80
|
+
handleLLMStart(serialized: Serialized, _prompts: string[], runId: string, parentRunId?: string): void;
|
|
81
|
+
handleLLMEnd(_output: LLMResult, runId: string, parentRunId: string | undefined): void;
|
|
82
|
+
handleLLMError(err: unknown, runId: string, parentRunId: string | undefined): void;
|
|
83
|
+
handleAgentAction(action: AgentAction, runId: string): void;
|
|
84
|
+
handleAgentEnd(_action: AgentFinish, runId: string): void;
|
|
85
|
+
handleToolStart(serialized: Serialized, _input: string, runId: string, parentRunId?: string): void;
|
|
86
|
+
handleToolEnd(_output: unknown, runId: string, parentRunId: string | undefined): void;
|
|
87
|
+
handleToolError(err: unknown, runId: string, parentRunId: string | undefined): void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* `LangChainAdapter`, for use with LangGraph.js graphs — exported under its
|
|
92
|
+
* own name for discoverability and parity with `logquill-python`'s
|
|
93
|
+
* `LangGraphAdapter`. LangGraph.js nodes run as ordinary LangChain.js
|
|
94
|
+
* `Runnable`s, so `LangChainAdapter`'s `handleChainStart`/`handleLLMStart`/
|
|
95
|
+
* `handleToolStart`/etc. already fire exactly as they would for a plain
|
|
96
|
+
* chain — no extra mapping needed.
|
|
97
|
+
*
|
|
98
|
+
* Unlike `logquill-python`, this class adds no extra event handling.
|
|
99
|
+
* Python's `LangGraphAdapter` exists specifically to catch LangGraph
|
|
100
|
+
* Python's own checkpoint pause/resume events (`on_interrupt`/`on_resume`),
|
|
101
|
+
* which that ecosystem dispatches only to handlers implementing its own
|
|
102
|
+
* `GraphCallbackHandler` — a plain `BaseCallbackHandler` subclass never
|
|
103
|
+
* receives them there. LangGraph.js has no equivalent: as of `@langchain/
|
|
104
|
+
* langgraph` 1.x it exposes no distinct callback-handler class or
|
|
105
|
+
* `onInterrupt`/`onResume` hook — an `interrupt()` call instead pauses the
|
|
106
|
+
* graph and surfaces in its state/stream output (under `__interrupt__`),
|
|
107
|
+
* not through the callback-handler system at all. There is nothing this
|
|
108
|
+
* class could subscribe to that `LangChainAdapter` doesn't already cover.
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* import { Logger, RunPlugin } from "logquill";
|
|
112
|
+
* import { LangGraphAdapter } from "logquill/langchain";
|
|
113
|
+
*
|
|
114
|
+
* const handler = new LangGraphAdapter(log.child("agent").use(new RunPlugin()));
|
|
115
|
+
* const graph = builder.compile({ checkpointer });
|
|
116
|
+
* await graph.invoke(input, { callbacks: [handler], configurable: { thread_id: "1" } });
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
declare class LangGraphAdapter extends LangChainAdapter {
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export { LangChainAdapter, LangGraphAdapter, LogQuillAdapter };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { BaseCallbackHandler } from '@langchain/core/callbacks/base';
|
|
2
|
+
|
|
3
|
+
// src/adapters/adapter.ts
|
|
4
|
+
var LogQuillAdapter = class {
|
|
5
|
+
constructor(log) {
|
|
6
|
+
this.log = log;
|
|
7
|
+
}
|
|
8
|
+
log;
|
|
9
|
+
};
|
|
10
|
+
function spanIds(runId, parentRunId) {
|
|
11
|
+
const ids = { spanId: runId };
|
|
12
|
+
if (parentRunId !== void 0) {
|
|
13
|
+
ids.parentSpanId = parentRunId;
|
|
14
|
+
}
|
|
15
|
+
return ids;
|
|
16
|
+
}
|
|
17
|
+
function serializedName(serialized, fallback) {
|
|
18
|
+
const name = serialized?.name;
|
|
19
|
+
if (typeof name === "string" && name) {
|
|
20
|
+
return name;
|
|
21
|
+
}
|
|
22
|
+
const lastIdSegment = serialized?.id.at(-1);
|
|
23
|
+
return typeof lastIdSegment === "string" && lastIdSegment ? lastIdSegment : fallback;
|
|
24
|
+
}
|
|
25
|
+
function formatError(error) {
|
|
26
|
+
if (error instanceof Error) {
|
|
27
|
+
return `${error.name}: ${error.message}`;
|
|
28
|
+
}
|
|
29
|
+
return String(error);
|
|
30
|
+
}
|
|
31
|
+
var LangChainAdapter = class extends BaseCallbackHandler {
|
|
32
|
+
name = "logquill";
|
|
33
|
+
log;
|
|
34
|
+
callStarts = /* @__PURE__ */ new Map();
|
|
35
|
+
chainNames = /* @__PURE__ */ new Map();
|
|
36
|
+
constructor(agentLog) {
|
|
37
|
+
super();
|
|
38
|
+
this.log = agentLog;
|
|
39
|
+
}
|
|
40
|
+
takeDurationMs(runId) {
|
|
41
|
+
const start = this.callStarts.get(runId);
|
|
42
|
+
if (start === void 0) {
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
this.callStarts.delete(runId);
|
|
46
|
+
return Math.round((performance.now() - start) * 1e3) / 1e3;
|
|
47
|
+
}
|
|
48
|
+
// -- chains: one span()-shaped record on end/error --------------------
|
|
49
|
+
// `handleChainEnd`/`handleChainError` don't receive the chain's
|
|
50
|
+
// `Serialized` descriptor (only `handleChainStart` does), so the name is
|
|
51
|
+
// captured at start and looked up again at end/error.
|
|
52
|
+
handleChainStart(chain, _inputs, runId) {
|
|
53
|
+
this.callStarts.set(runId, performance.now());
|
|
54
|
+
this.chainNames.set(runId, serializedName(chain, "chain"));
|
|
55
|
+
}
|
|
56
|
+
handleChainEnd(_outputs, runId, parentRunId) {
|
|
57
|
+
const name = this.chainNames.get(runId) ?? "chain";
|
|
58
|
+
this.chainNames.delete(runId);
|
|
59
|
+
this.log.info(name, { kind: "span", ...spanIds(runId, parentRunId), durationMs: this.takeDurationMs(runId) });
|
|
60
|
+
}
|
|
61
|
+
handleChainError(err, runId, parentRunId) {
|
|
62
|
+
const name = this.chainNames.get(runId) ?? "chain";
|
|
63
|
+
this.chainNames.delete(runId);
|
|
64
|
+
this.log.error(name, {
|
|
65
|
+
kind: "span",
|
|
66
|
+
...spanIds(runId, parentRunId),
|
|
67
|
+
durationMs: this.takeDurationMs(runId),
|
|
68
|
+
error: formatError(err)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// -- LLM calls: action (start) / observation (end) ---------------------
|
|
72
|
+
handleLLMStart(serialized, _prompts, runId, parentRunId) {
|
|
73
|
+
this.callStarts.set(runId, performance.now());
|
|
74
|
+
this.log.action(serializedName(serialized, "llm_start"), spanIds(runId, parentRunId));
|
|
75
|
+
}
|
|
76
|
+
handleLLMEnd(_output, runId, parentRunId) {
|
|
77
|
+
this.log.observation("llm_end", { ...spanIds(runId, parentRunId), durationMs: this.takeDurationMs(runId) });
|
|
78
|
+
}
|
|
79
|
+
handleLLMError(err, runId, parentRunId) {
|
|
80
|
+
this.takeDurationMs(runId);
|
|
81
|
+
this.log.error("llm_error", { error: formatError(err), ...spanIds(runId, parentRunId) });
|
|
82
|
+
}
|
|
83
|
+
// -- agent-level events --------------------------------------------------
|
|
84
|
+
handleAgentAction(action, runId) {
|
|
85
|
+
this.log.action(action.tool || "agent_action", { parentSpanId: runId });
|
|
86
|
+
}
|
|
87
|
+
handleAgentEnd(_action, runId) {
|
|
88
|
+
this.log.decision("agent_finish", { parentSpanId: runId });
|
|
89
|
+
}
|
|
90
|
+
// -- tools: action (start) / observation (end) / error -------------------
|
|
91
|
+
handleToolStart(serialized, _input, runId, parentRunId) {
|
|
92
|
+
this.callStarts.set(runId, performance.now());
|
|
93
|
+
this.log.action(serializedName(serialized, "tool"), spanIds(runId, parentRunId));
|
|
94
|
+
}
|
|
95
|
+
handleToolEnd(_output, runId, parentRunId) {
|
|
96
|
+
this.log.observation("tool_end", { ...spanIds(runId, parentRunId), durationMs: this.takeDurationMs(runId) });
|
|
97
|
+
}
|
|
98
|
+
handleToolError(err, runId, parentRunId) {
|
|
99
|
+
this.takeDurationMs(runId);
|
|
100
|
+
this.log.error("tool_error", { error: formatError(err), ...spanIds(runId, parentRunId) });
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/adapters/langgraph-adapter.ts
|
|
105
|
+
var LangGraphAdapter = class extends LangChainAdapter {
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export { LangChainAdapter, LangGraphAdapter, LogQuillAdapter };
|
|
109
|
+
//# sourceMappingURL=langchain.mjs.map
|
|
110
|
+
//# sourceMappingURL=langchain.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/adapter.ts","../src/adapters/langchain-adapter.ts","../src/adapters/langgraph-adapter.ts"],"names":[],"mappings":"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","file":"langchain.mjs","sourcesContent":["import type { Logger } from \"../core/logger.js\";\n\n/**\n * Base for framework tracing adapters. A concrete adapter holds a\n * reference to the `Logger` to forward events onto (`this.log`) and\n * overrides only the events its framework actually emits — translating\n * them into `.thought()/.action()/.observation()/.decision()` calls and\n * `span()`-shaped records. Always a thin mapping from the framework's\n * native event shape onto LogQuill's, never a reimplementation of tracing\n * logic per framework.\n *\n * `LangChainAdapter` doesn't literally extend this: it has to extend\n * LangChain's own `BaseCallbackHandler` instead (JS classes support only\n * single inheritance), so it holds the same `log` reference itself rather\n * than inheriting it. This base is for adapters that don't need to\n * subclass a framework SDK class.\n */\nexport abstract class LogQuillAdapter {\n constructor(protected readonly log: Logger) {}\n}\n","import { BaseCallbackHandler } from \"@langchain/core/callbacks/base\";\nimport type { AgentAction, AgentFinish } from \"@langchain/core/agents\";\nimport type { Serialized } from \"@langchain/core/load/serializable\";\nimport type { LLMResult } from \"@langchain/core/outputs\";\nimport type { ChainValues } from \"@langchain/core/utils/types\";\nimport type { Logger } from \"../core/logger.js\";\n\nfunction spanIds(runId: string, parentRunId: string | undefined): Record<string, unknown> {\n const ids: Record<string, unknown> = { spanId: runId };\n if (parentRunId !== undefined) {\n ids.parentSpanId = parentRunId;\n }\n return ids;\n}\n\n/**\n * Prefers `serialized.name`, which most `Serialized` variants carry — but\n * a bare `RunnableLambda` (and other constructless runnables), verified\n * against `@langchain/core` 1.2.9, omits it. `id` (e.g.\n * `[\"langchain_core\", \"runnables\", \"RunnableLambda\"]`) is present on\n * every `Serialized` variant, so its last segment — the class name — is a\n * more useful fallback than the generic `fallback` string.\n */\nfunction serializedName(serialized: Serialized | undefined, fallback: string): string {\n const name = (serialized as { name?: unknown } | undefined)?.name;\n if (typeof name === \"string\" && name) {\n return name;\n }\n const lastIdSegment = serialized?.id.at(-1);\n return typeof lastIdSegment === \"string\" && lastIdSegment ? lastIdSegment : fallback;\n}\n\n// LangChain's `handle*Error` callbacks type their `error` parameter as\n// `Error` (really `any` in the base class — see `type Error = any` in\n// `@langchain/core`'s own `callbacks/base.d.ts`), but nothing enforces that\n// at the actual throw site: a tool's `_call`, an LLM provider, or any\n// chain step can `throw` a bare string or plain object just as validly.\n// Trusting `Error` here would crash this adapter's own error handler on\n// exactly the input it exists to report.\nfunction formatError(error: unknown): string {\n if (error instanceof Error) {\n return `${error.name}: ${error.message}`;\n }\n return String(error);\n}\n\n/**\n * Maps LangChain.js's `BaseCallbackHandler` events onto LogQuill calls —\n * LangGraph.js is covered for free (see `LangGraphAdapter`), since it\n * shares LangChain.js's callback system.\n *\n * Pass an instance into a chain/agent invocation's `callbacks: [...]`, the\n * same way any other LangChain tracing handler (LangSmith, Langfuse, ...)\n * is wired in — no other instrumentation needed:\n *\n * ```ts\n * import { Logger, RunPlugin } from \"logquill\";\n * import { LangChainAdapter } from \"logquill/langchain\";\n *\n * const handler = new LangChainAdapter(log.child(\"agent\").use(new RunPlugin()));\n * const llm = new ChatOpenAI({ callbacks: [handler] });\n * ```\n *\n * Event mapping:\n *\n * | LangChain.js callback | LogQuill call |\n * |---|---|\n * | `handleChainStart` / `handleChainEnd` | one `span()`-shaped record on end/error |\n * | `handleLLMStart` / `handleLLMEnd` | `.action()` / `.observation()` with `durationMs` |\n * | `handleAgentAction` | `.action()` |\n * | `handleAgentEnd` | `.decision()` |\n * | `handleToolStart` / `handleToolEnd` / `handleToolError` | `.action()` / `.observation()` / `.error()` |\n *\n * LangChain's own `runId`/`parentRunId` are written directly onto\n * `meta.spanId`/`meta.parentSpanId` on every event — the shapes already\n * match, so this is field renaming, not translation. Chain start/end is\n * stamped manually (rather than via `Logger.span()`, which wraps a single\n * callback) into the same `{ kind: \"span\", spanId, parentSpanId,\n * durationMs }` shape `Logger.span()` itself produces, since LangChain\n * opens and closes a chain run from two separate, independently-scheduled\n * callback invocations — there's no single function to wrap.\n *\n * `handleAgentAction`/`handleAgentEnd` carry the *enclosing* chain's own\n * `runId` (LangChain doesn't mint a fresh one for these events), so it's\n * written as this record's `parentSpanId`, not `spanId` — using it as\n * `spanId` would make the record indistinguishable from the chain's own\n * span-closing record.\n */\nexport class LangChainAdapter extends BaseCallbackHandler {\n name = \"logquill\";\n\n private readonly log: Logger;\n private readonly callStarts = new Map<string, number>();\n private readonly chainNames = new Map<string, string>();\n\n constructor(agentLog: Logger) {\n super();\n this.log = agentLog;\n }\n\n private takeDurationMs(runId: string): number | undefined {\n const start = this.callStarts.get(runId);\n if (start === undefined) {\n return undefined;\n }\n this.callStarts.delete(runId);\n return Math.round((performance.now() - start) * 1000) / 1000;\n }\n\n // -- chains: one span()-shaped record on end/error --------------------\n // `handleChainEnd`/`handleChainError` don't receive the chain's\n // `Serialized` descriptor (only `handleChainStart` does), so the name is\n // captured at start and looked up again at end/error.\n\n handleChainStart(chain: Serialized, _inputs: ChainValues, runId: string): void {\n this.callStarts.set(runId, performance.now());\n this.chainNames.set(runId, serializedName(chain, \"chain\"));\n }\n\n handleChainEnd(_outputs: ChainValues, runId: string, parentRunId: string | undefined): void {\n const name = this.chainNames.get(runId) ?? \"chain\";\n this.chainNames.delete(runId);\n this.log.info(name, { kind: \"span\", ...spanIds(runId, parentRunId), durationMs: this.takeDurationMs(runId) });\n }\n\n handleChainError(err: unknown, runId: string, parentRunId: string | undefined): void {\n const name = this.chainNames.get(runId) ?? \"chain\";\n this.chainNames.delete(runId);\n this.log.error(name, {\n kind: \"span\",\n ...spanIds(runId, parentRunId),\n durationMs: this.takeDurationMs(runId),\n error: formatError(err),\n });\n }\n\n // -- LLM calls: action (start) / observation (end) ---------------------\n\n handleLLMStart(\n serialized: Serialized,\n _prompts: string[],\n runId: string,\n parentRunId?: string,\n ): void {\n this.callStarts.set(runId, performance.now());\n this.log.action(serializedName(serialized, \"llm_start\"), spanIds(runId, parentRunId));\n }\n\n handleLLMEnd(_output: LLMResult, runId: string, parentRunId: string | undefined): void {\n this.log.observation(\"llm_end\", { ...spanIds(runId, parentRunId), durationMs: this.takeDurationMs(runId) });\n }\n\n handleLLMError(err: unknown, runId: string, parentRunId: string | undefined): void {\n this.takeDurationMs(runId);\n this.log.error(\"llm_error\", { error: formatError(err), ...spanIds(runId, parentRunId) });\n }\n\n // -- agent-level events --------------------------------------------------\n\n handleAgentAction(action: AgentAction, runId: string): void {\n this.log.action(action.tool || \"agent_action\", { parentSpanId: runId });\n }\n\n handleAgentEnd(_action: AgentFinish, runId: string): void {\n this.log.decision(\"agent_finish\", { parentSpanId: runId });\n }\n\n // -- tools: action (start) / observation (end) / error -------------------\n\n handleToolStart(\n serialized: Serialized,\n _input: string,\n runId: string,\n parentRunId?: string,\n ): void {\n this.callStarts.set(runId, performance.now());\n this.log.action(serializedName(serialized, \"tool\"), spanIds(runId, parentRunId));\n }\n\n handleToolEnd(_output: unknown, runId: string, parentRunId: string | undefined): void {\n this.log.observation(\"tool_end\", { ...spanIds(runId, parentRunId), durationMs: this.takeDurationMs(runId) });\n }\n\n handleToolError(err: unknown, runId: string, parentRunId: string | undefined): void {\n this.takeDurationMs(runId);\n this.log.error(\"tool_error\", { error: formatError(err), ...spanIds(runId, parentRunId) });\n }\n}\n","import { LangChainAdapter } from \"./langchain-adapter.js\";\n\n/**\n * `LangChainAdapter`, for use with LangGraph.js graphs — exported under its\n * own name for discoverability and parity with `logquill-python`'s\n * `LangGraphAdapter`. LangGraph.js nodes run as ordinary LangChain.js\n * `Runnable`s, so `LangChainAdapter`'s `handleChainStart`/`handleLLMStart`/\n * `handleToolStart`/etc. already fire exactly as they would for a plain\n * chain — no extra mapping needed.\n *\n * Unlike `logquill-python`, this class adds no extra event handling.\n * Python's `LangGraphAdapter` exists specifically to catch LangGraph\n * Python's own checkpoint pause/resume events (`on_interrupt`/`on_resume`),\n * which that ecosystem dispatches only to handlers implementing its own\n * `GraphCallbackHandler` — a plain `BaseCallbackHandler` subclass never\n * receives them there. LangGraph.js has no equivalent: as of `@langchain/\n * langgraph` 1.x it exposes no distinct callback-handler class or\n * `onInterrupt`/`onResume` hook — an `interrupt()` call instead pauses the\n * graph and surfaces in its state/stream output (under `__interrupt__`),\n * not through the callback-handler system at all. There is nothing this\n * class could subscribe to that `LangChainAdapter` doesn't already cover.\n *\n * ```ts\n * import { Logger, RunPlugin } from \"logquill\";\n * import { LangGraphAdapter } from \"logquill/langchain\";\n *\n * const handler = new LangGraphAdapter(log.child(\"agent\").use(new RunPlugin()));\n * const graph = builder.compile({ checkpointer });\n * await graph.invoke(input, { callbacks: [handler], configurable: { thread_id: \"1\" } });\n * ```\n */\nexport class LangGraphAdapter extends LangChainAdapter {}\n"]}
|