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.
@@ -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"]}