@prefactor/langchain 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,252 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
6
+ var __toCommonJS = (from) => {
7
+ var entry = __moduleCache.get(from), desc;
8
+ if (entry)
9
+ return entry;
10
+ entry = __defProp({}, "__esModule", { value: true });
11
+ if (from && typeof from === "object" || typeof from === "function")
12
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
13
+ get: () => from[key],
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ }));
16
+ __moduleCache.set(from, entry);
17
+ return entry;
18
+ };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, {
22
+ get: all[name],
23
+ enumerable: true,
24
+ configurable: true,
25
+ set: (newValue) => all[name] = () => newValue
26
+ });
27
+ };
28
+
29
+ // packages/langchain/src/index.ts
30
+ var exports_src = {};
31
+ __export(exports_src, {
32
+ shutdown: () => shutdown,
33
+ init: () => init,
34
+ getTracer: () => getTracer,
35
+ extractTokenUsage: () => extractTokenUsage,
36
+ SpanType: () => import_core3.SpanType,
37
+ SpanStatus: () => import_core3.SpanStatus,
38
+ PrefactorMiddleware: () => PrefactorMiddleware
39
+ });
40
+ module.exports = __toCommonJS(exports_src);
41
+ var import_core3 = require("@prefactor/core");
42
+
43
+ // packages/langchain/src/init.ts
44
+ var import_core2 = require("@prefactor/core");
45
+ var import_pfid = require("@prefactor/pfid");
46
+ var import_langchain = require("langchain");
47
+
48
+ // packages/langchain/src/middleware.ts
49
+ var import_core = require("@prefactor/core");
50
+
51
+ // packages/langchain/src/metadata-extractor.ts
52
+ function extractTokenUsage(response) {
53
+ try {
54
+ const tokenUsage = response?.token_usage ?? response?.usage;
55
+ if (tokenUsage) {
56
+ return {
57
+ promptTokens: tokenUsage.prompt_tokens ?? 0,
58
+ completionTokens: tokenUsage.completion_tokens ?? 0,
59
+ totalTokens: tokenUsage.total_tokens ?? 0
60
+ };
61
+ }
62
+ const usageMetadata = response?.usage_metadata;
63
+ if (usageMetadata) {
64
+ return {
65
+ promptTokens: usageMetadata.input_tokens ?? 0,
66
+ completionTokens: usageMetadata.output_tokens ?? 0,
67
+ totalTokens: usageMetadata.total_tokens ?? 0
68
+ };
69
+ }
70
+ const responseMetadata = response?.response_metadata;
71
+ if (responseMetadata?.token_usage) {
72
+ return {
73
+ promptTokens: responseMetadata.token_usage.prompt_tokens ?? 0,
74
+ completionTokens: responseMetadata.token_usage.completion_tokens ?? 0,
75
+ totalTokens: responseMetadata.token_usage.total_tokens ?? 0
76
+ };
77
+ }
78
+ return null;
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ // packages/langchain/src/middleware.ts
85
+ class PrefactorMiddleware {
86
+ tracer;
87
+ rootSpan = null;
88
+ constructor(tracer) {
89
+ this.tracer = tracer;
90
+ }
91
+ async beforeAgent(state) {
92
+ const parentSpan = import_core.SpanContext.getCurrent();
93
+ const messages = state?.messages ?? [];
94
+ this.tracer.startAgentInstance();
95
+ const span = this.tracer.startSpan({
96
+ name: "agent",
97
+ spanType: import_core.SpanType.AGENT,
98
+ inputs: { messages: messages.slice(-3).map((m) => String(m)) },
99
+ parentSpanId: parentSpan?.spanId,
100
+ traceId: parentSpan?.traceId
101
+ });
102
+ this.rootSpan = span;
103
+ }
104
+ async afterAgent(state) {
105
+ if (!this.rootSpan) {
106
+ return;
107
+ }
108
+ const messages = state?.messages ?? [];
109
+ this.tracer.endSpan(this.rootSpan, {
110
+ outputs: { messages: messages.slice(-3).map((m) => String(m)) }
111
+ });
112
+ this.tracer.finishAgentInstance();
113
+ import_core.SpanContext.clear();
114
+ this.rootSpan = null;
115
+ }
116
+ async wrapModelCall(request, handler) {
117
+ const parentSpan = import_core.SpanContext.getCurrent();
118
+ const span = this.tracer.startSpan({
119
+ name: this.extractModelName(request),
120
+ spanType: import_core.SpanType.LLM,
121
+ inputs: this.extractModelInputs(request),
122
+ parentSpanId: parentSpan?.spanId,
123
+ traceId: parentSpan?.traceId
124
+ });
125
+ try {
126
+ const response = await import_core.SpanContext.runAsync(span, async () => {
127
+ return handler(request);
128
+ });
129
+ const outputs = this.extractModelOutputs(response);
130
+ const tokenUsage = extractTokenUsage(response);
131
+ this.tracer.endSpan(span, { outputs, tokenUsage: tokenUsage ?? undefined });
132
+ return response;
133
+ } catch (error) {
134
+ this.tracer.endSpan(span, { error });
135
+ throw error;
136
+ }
137
+ }
138
+ async wrapToolCall(request, handler) {
139
+ const parentSpan = import_core.SpanContext.getCurrent();
140
+ const span = this.tracer.startSpan({
141
+ name: this.extractToolName(request),
142
+ spanType: import_core.SpanType.TOOL,
143
+ inputs: this.extractToolInputs(request),
144
+ parentSpanId: parentSpan?.spanId,
145
+ traceId: parentSpan?.traceId
146
+ });
147
+ try {
148
+ const response = await import_core.SpanContext.runAsync(span, async () => {
149
+ return handler(request);
150
+ });
151
+ this.tracer.endSpan(span, {
152
+ outputs: this.extractToolOutputs(response)
153
+ });
154
+ return response;
155
+ } catch (error) {
156
+ this.tracer.endSpan(span, { error });
157
+ throw error;
158
+ }
159
+ }
160
+ extractModelName(request) {
161
+ return request?.model ?? request?.modelName ?? "unknown";
162
+ }
163
+ extractModelInputs(request) {
164
+ const messages = request?.messages ?? [];
165
+ return { messages: messages.slice(-3).map((m) => String(m)) };
166
+ }
167
+ extractModelOutputs(response) {
168
+ const content = response?.content ?? response?.text ?? "";
169
+ return { content: String(content) };
170
+ }
171
+ extractToolName(request) {
172
+ return request?.name ?? request?.tool ?? "unknown";
173
+ }
174
+ extractToolInputs(request) {
175
+ return { input: request?.input ?? request?.args ?? {} };
176
+ }
177
+ extractToolOutputs(response) {
178
+ return { output: response?.output ?? response };
179
+ }
180
+ }
181
+
182
+ // packages/langchain/src/init.ts
183
+ var logger = import_core2.getLogger("init");
184
+ var globalTracer = null;
185
+ var globalMiddleware = null;
186
+ function init(config) {
187
+ import_core2.configureLogging();
188
+ const finalConfig = import_core2.createConfig(config);
189
+ logger.info("Initializing Prefactor SDK", { transport: finalConfig.transportType });
190
+ if (globalMiddleware !== null) {
191
+ return globalMiddleware;
192
+ }
193
+ let transport;
194
+ if (finalConfig.transportType === "stdio") {
195
+ transport = new import_core2.StdioTransport;
196
+ } else {
197
+ if (!finalConfig.httpConfig) {
198
+ throw new Error("HTTP transport requires httpConfig to be provided in configuration");
199
+ }
200
+ const httpConfig = import_core2.HttpTransportConfigSchema.parse(finalConfig.httpConfig);
201
+ transport = new import_core2.HttpTransport(httpConfig);
202
+ }
203
+ let partition;
204
+ if (finalConfig.httpConfig?.agentId) {
205
+ try {
206
+ partition = import_pfid.extractPartition(finalConfig.httpConfig.agentId);
207
+ logger.debug("Extracted partition from agent_id", { partition });
208
+ } catch (error) {
209
+ logger.warn("Failed to extract partition from agent_id, using random partition", { error });
210
+ }
211
+ }
212
+ globalTracer = new import_core2.Tracer(transport, partition);
213
+ const prefactorMiddleware = new PrefactorMiddleware(globalTracer);
214
+ const middleware = import_langchain.createMiddleware({
215
+ name: "prefactor",
216
+ wrapModelCall: async (request, handler) => {
217
+ return prefactorMiddleware.wrapModelCall(request, handler);
218
+ },
219
+ wrapToolCall: async (request, handler) => {
220
+ return prefactorMiddleware.wrapToolCall(request, handler);
221
+ },
222
+ beforeAgent: async (state) => {
223
+ await prefactorMiddleware.beforeAgent(state);
224
+ },
225
+ afterAgent: async (state) => {
226
+ await prefactorMiddleware.afterAgent(state);
227
+ }
228
+ });
229
+ globalMiddleware = middleware;
230
+ return middleware;
231
+ }
232
+ function getTracer() {
233
+ if (!globalTracer) {
234
+ init();
235
+ }
236
+ return globalTracer;
237
+ }
238
+ async function shutdown() {
239
+ if (globalTracer) {
240
+ logger.info("Shutting down Prefactor SDK");
241
+ await globalTracer.close();
242
+ }
243
+ globalTracer = null;
244
+ globalMiddleware = null;
245
+ }
246
+ process.on("beforeExit", () => {
247
+ shutdown().catch((error) => {
248
+ console.error("Error during Prefactor SDK shutdown:", error);
249
+ });
250
+ });
251
+
252
+ //# debugId=46B60F8DD9AA982064756E2164756E21
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/init.ts", "../src/middleware.ts", "../src/metadata-extractor.ts"],
4
+ "sourcesContent": [
5
+ "// Main entry points\n\n// Convenience re-exports from core\nexport {\n type Config,\n type HttpTransportConfig,\n type Span,\n SpanStatus,\n SpanType,\n} from '@prefactor/core';\nexport { getTracer, init, shutdown } from './init.js';\nexport { extractTokenUsage } from './metadata-extractor.js';\n// Middleware\nexport { PrefactorMiddleware } from './middleware.js';\n",
6
+ "import {\n type Config,\n configureLogging,\n createConfig,\n getLogger,\n HttpTransport,\n HttpTransportConfigSchema,\n StdioTransport,\n Tracer,\n type Transport,\n} from '@prefactor/core';\nimport { extractPartition, type Partition } from '@prefactor/pfid';\nimport { type AgentMiddleware, createMiddleware } from 'langchain';\nimport { PrefactorMiddleware } from './middleware.js';\n\nconst logger = getLogger('init');\n\nlet globalTracer: Tracer | null = null;\nlet globalMiddleware: AgentMiddleware | null = null;\n\n/**\n * Initialize the Prefactor SDK and return middleware for LangChain.js\n *\n * This is the main entry point for the SDK. Call this function to create a middleware\n * instance that you can pass to your LangChain.js agents.\n *\n * @param config - Optional configuration object\n * @returns PrefactorMiddleware instance to use with LangChain.js agents\n *\n * @example\n * ```typescript\n * import { init } from '@prefactor/langchain';\n * import { createAgent } from 'langchain';\n *\n * // Initialize with defaults (stdio transport)\n * const middleware = init();\n *\n * // Or configure HTTP transport\n * const middleware = init({\n * transportType: 'http',\n * httpConfig: {\n * apiUrl: 'https://api.prefactor.ai',\n * apiToken: process.env.PREFACTOR_API_TOKEN!,\n * }\n * });\n *\n * const agent = createAgent({\n * model: 'claude-sonnet-4-5-20250929',\n * tools: [myTool],\n * middleware: [middleware],\n * });\n * ```\n */\nexport function init(config?: Partial<Config>): AgentMiddleware {\n configureLogging();\n\n const finalConfig = createConfig(config);\n logger.info('Initializing Prefactor SDK', { transport: finalConfig.transportType });\n\n if (globalMiddleware !== null) {\n return globalMiddleware;\n }\n\n let transport: Transport;\n if (finalConfig.transportType === 'stdio') {\n transport = new StdioTransport();\n } else {\n if (!finalConfig.httpConfig) {\n throw new Error('HTTP transport requires httpConfig to be provided in configuration');\n }\n // Parse httpConfig to apply defaults from schema\n const httpConfig = HttpTransportConfigSchema.parse(finalConfig.httpConfig);\n transport = new HttpTransport(httpConfig);\n }\n\n // Extract partition from agent_id if provided (for HTTP transport)\n let partition: Partition | undefined;\n if (finalConfig.httpConfig?.agentId) {\n try {\n partition = extractPartition(finalConfig.httpConfig.agentId);\n logger.debug('Extracted partition from agent_id', { partition });\n } catch (error) {\n logger.warn('Failed to extract partition from agent_id, using random partition', { error });\n }\n }\n\n globalTracer = new Tracer(transport, partition);\n const prefactorMiddleware = new PrefactorMiddleware(globalTracer);\n\n const middleware = createMiddleware({\n name: 'prefactor',\n // biome-ignore lint/suspicious/noExplicitAny: LangChain middleware hooks use dynamic types\n wrapModelCall: async (request: any, handler: any) => {\n return prefactorMiddleware.wrapModelCall(request, handler);\n },\n // biome-ignore lint/suspicious/noExplicitAny: LangChain middleware hooks use dynamic types\n wrapToolCall: async (request: any, handler: any) => {\n return prefactorMiddleware.wrapToolCall(request, handler);\n },\n // biome-ignore lint/suspicious/noExplicitAny: LangChain middleware hooks use dynamic types\n beforeAgent: async (state: any) => {\n await prefactorMiddleware.beforeAgent(state);\n },\n // biome-ignore lint/suspicious/noExplicitAny: LangChain middleware hooks use dynamic types\n afterAgent: async (state: any) => {\n await prefactorMiddleware.afterAgent(state);\n },\n });\n\n globalMiddleware = middleware;\n return middleware;\n}\n\n/**\n * Get the current tracer instance.\n *\n * If no tracer has been created yet, this will call init() with default configuration.\n *\n * @returns Tracer instance\n *\n * @example\n * ```typescript\n * import { getTracer } from '@prefactor/langchain';\n *\n * const tracer = getTracer();\n * const span = tracer.startSpan({\n * name: 'custom-operation',\n * spanType: SpanType.TOOL,\n * inputs: { data: 'example' }\n * });\n * ```\n */\nexport function getTracer(): Tracer {\n if (!globalTracer) {\n init();\n }\n // Safe because init() always sets globalTracer\n return globalTracer as Tracer;\n}\n\n/**\n * Shutdown the SDK and flush any pending spans.\n *\n * Call this before your application exits to ensure all spans are sent to the transport.\n * This is especially important for HTTP transport which has a queue of pending requests.\n *\n * @returns Promise that resolves when shutdown is complete\n *\n * @example\n * ```typescript\n * import { shutdown } from '@prefactor/langchain';\n *\n * process.on('SIGTERM', async () => {\n * await shutdown();\n * process.exit(0);\n * });\n * ```\n */\nexport async function shutdown(): Promise<void> {\n if (globalTracer) {\n logger.info('Shutting down Prefactor SDK');\n await globalTracer.close();\n }\n globalTracer = null;\n globalMiddleware = null;\n}\n\n// Automatic shutdown on process exit\nprocess.on('beforeExit', () => {\n shutdown().catch((error) => {\n console.error('Error during Prefactor SDK shutdown:', error);\n });\n});\n",
7
+ "import { type Span, SpanContext, SpanType, type Tracer } from '@prefactor/core';\nimport { extractTokenUsage } from './metadata-extractor.js';\n\n/**\n * Prefactor middleware for LangChain.js agents.\n *\n * This middleware automatically traces LLM calls, tool executions, and agent workflows.\n * It integrates with LangChain.js middleware API to provide transparent instrumentation.\n *\n * Features:\n * - Automatic parent-child span relationships via context propagation\n * - Token usage extraction for LLM calls\n * - Error tracking and debugging\n * - Zero-overhead instrumentation (graceful failure)\n *\n * @example\n * ```typescript\n * import { init } from '@prefactor/langchain';\n * import { createReactAgent } from '@langchain/langgraph/prebuilt';\n *\n * const middleware = init();\n * const agent = createReactAgent({\n * llm: model,\n * tools: [myTool],\n * middleware: [middleware],\n * });\n * ```\n */\nexport class PrefactorMiddleware {\n private rootSpan: Span | null = null;\n\n constructor(private tracer: Tracer) {}\n\n /**\n * Called before agent execution starts\n *\n * @param state - Agent state containing messages\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain state can be any structure\n async beforeAgent(state: any): Promise<void> {\n const parentSpan = SpanContext.getCurrent();\n const messages = state?.messages ?? [];\n\n this.tracer.startAgentInstance();\n\n const span = this.tracer.startSpan({\n name: 'agent',\n spanType: SpanType.AGENT,\n inputs: { messages: messages.slice(-3).map((m: unknown) => String(m)) },\n parentSpanId: parentSpan?.spanId,\n traceId: parentSpan?.traceId,\n });\n\n this.rootSpan = span;\n }\n\n /**\n * Called after agent execution completes\n *\n * @param state - Agent state containing messages\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain state can be any structure\n async afterAgent(state: any): Promise<void> {\n if (!this.rootSpan) {\n return;\n }\n\n const messages = state?.messages ?? [];\n this.tracer.endSpan(this.rootSpan, {\n outputs: { messages: messages.slice(-3).map((m: unknown) => String(m)) },\n });\n\n this.tracer.finishAgentInstance();\n SpanContext.clear();\n this.rootSpan = null;\n }\n\n /**\n * Wrap a model call to trace LLM invocations\n *\n * @param request - Model invocation request\n * @param handler - The actual model call function\n * @returns Promise resolving to the model response\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain request/handler types are dynamic\n async wrapModelCall<T>(request: any, handler: (req: any) => Promise<T>): Promise<T> {\n const parentSpan = SpanContext.getCurrent();\n\n const span = this.tracer.startSpan({\n name: this.extractModelName(request),\n spanType: SpanType.LLM,\n inputs: this.extractModelInputs(request),\n parentSpanId: parentSpan?.spanId,\n traceId: parentSpan?.traceId,\n });\n\n try {\n // CRITICAL: Wrap handler in context so child operations see this span\n const response = await SpanContext.runAsync(span, async () => {\n return handler(request);\n });\n\n const outputs = this.extractModelOutputs(response);\n const tokenUsage = extractTokenUsage(response);\n\n this.tracer.endSpan(span, { outputs, tokenUsage: tokenUsage ?? undefined });\n return response;\n } catch (error) {\n this.tracer.endSpan(span, { error: error as Error });\n throw error;\n }\n }\n\n /**\n * Wrap a tool call to trace tool executions\n *\n * @param request - Tool invocation request\n * @param handler - The actual tool call function\n * @returns Promise resolving to the tool response\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain request/handler types are dynamic\n async wrapToolCall<T>(request: any, handler: (req: any) => Promise<T>): Promise<T> {\n const parentSpan = SpanContext.getCurrent();\n\n const span = this.tracer.startSpan({\n name: this.extractToolName(request),\n spanType: SpanType.TOOL,\n inputs: this.extractToolInputs(request),\n parentSpanId: parentSpan?.spanId,\n traceId: parentSpan?.traceId,\n });\n\n try {\n // CRITICAL: Wrap handler in context so child operations see this span\n const response = await SpanContext.runAsync(span, async () => {\n return handler(request);\n });\n\n this.tracer.endSpan(span, {\n outputs: this.extractToolOutputs(response),\n });\n return response;\n } catch (error) {\n this.tracer.endSpan(span, { error: error as Error });\n throw error;\n }\n }\n\n /**\n * Extract model name from request\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain request structure is dynamic\n private extractModelName(request: any): string {\n return request?.model ?? request?.modelName ?? 'unknown';\n }\n\n /**\n * Extract model inputs from request\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain request structure is dynamic\n private extractModelInputs(request: any): Record<string, unknown> {\n const messages = request?.messages ?? [];\n return { messages: messages.slice(-3).map((m: unknown) => String(m)) };\n }\n\n /**\n * Extract model outputs from response\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain response structure is dynamic\n private extractModelOutputs(response: any): Record<string, unknown> {\n const content = response?.content ?? response?.text ?? '';\n return { content: String(content) };\n }\n\n /**\n * Extract tool name from request\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain request structure is dynamic\n private extractToolName(request: any): string {\n return request?.name ?? request?.tool ?? 'unknown';\n }\n\n /**\n * Extract tool inputs from request\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain request structure is dynamic\n private extractToolInputs(request: any): Record<string, unknown> {\n return { input: request?.input ?? request?.args ?? {} };\n }\n\n /**\n * Extract tool outputs from response\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain response structure is dynamic\n private extractToolOutputs(response: any): Record<string, unknown> {\n return { output: response?.output ?? response };\n }\n}\n",
8
+ "import type { TokenUsage } from '@prefactor/core';\n\n/**\n * Extract token usage information from LLM responses.\n *\n * Handles multiple response formats from different LLM providers and LangChain versions.\n *\n * @param response - The LLM response object\n * @returns TokenUsage object or null if no usage data found\n *\n * @example\n * ```typescript\n * const response = await model.invoke(messages);\n * const tokenUsage = extractTokenUsage(response);\n * if (tokenUsage) {\n * console.log(`Tokens used: ${tokenUsage.totalTokens}`);\n * }\n * ```\n */\n// biome-ignore lint/suspicious/noExplicitAny: LLM response structure varies by provider\nexport function extractTokenUsage(response: any): TokenUsage | null {\n try {\n // Try token_usage field (common format)\n const tokenUsage = response?.token_usage ?? response?.usage;\n if (tokenUsage) {\n return {\n promptTokens: tokenUsage.prompt_tokens ?? 0,\n completionTokens: tokenUsage.completion_tokens ?? 0,\n totalTokens: tokenUsage.total_tokens ?? 0,\n };\n }\n\n // Try usage_metadata field (LangChain format)\n const usageMetadata = response?.usage_metadata;\n if (usageMetadata) {\n return {\n promptTokens: usageMetadata.input_tokens ?? 0,\n completionTokens: usageMetadata.output_tokens ?? 0,\n totalTokens: usageMetadata.total_tokens ?? 0,\n };\n }\n\n // Try response_metadata.token_usage (nested format)\n const responseMetadata = response?.response_metadata;\n if (responseMetadata?.token_usage) {\n return {\n promptTokens: responseMetadata.token_usage.prompt_tokens ?? 0,\n completionTokens: responseMetadata.token_usage.completion_tokens ?? 0,\n totalTokens: responseMetadata.token_usage.total_tokens ?? 0,\n };\n }\n\n return null;\n } catch {\n return null;\n }\n}\n"
9
+ ],
10
+ "mappings": "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",
11
+ "debugId": "46B60F8DD9AA982064756E2164756E21",
12
+ "names": []
13
+ }
@@ -0,0 +1,5 @@
1
+ export { type Config, type HttpTransportConfig, type Span, SpanStatus, SpanType, } from '@prefactor/core';
2
+ export { getTracer, init, shutdown } from './init.js';
3
+ export { extractTokenUsage } from './metadata-extractor.js';
4
+ export { PrefactorMiddleware } from './middleware.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,mBAAmB,EACxB,KAAK,IAAI,EACT,UAAU,EACV,QAAQ,GACT,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,233 @@
1
+ // packages/langchain/src/index.ts
2
+ import {
3
+ SpanStatus,
4
+ SpanType as SpanType2
5
+ } from "@prefactor/core";
6
+
7
+ // packages/langchain/src/init.ts
8
+ import {
9
+ configureLogging,
10
+ createConfig,
11
+ getLogger,
12
+ HttpTransport,
13
+ HttpTransportConfigSchema,
14
+ StdioTransport,
15
+ Tracer
16
+ } from "@prefactor/core";
17
+ import { extractPartition } from "@prefactor/pfid";
18
+ import { createMiddleware } from "langchain";
19
+
20
+ // packages/langchain/src/middleware.ts
21
+ import { SpanContext, SpanType } from "@prefactor/core";
22
+
23
+ // packages/langchain/src/metadata-extractor.ts
24
+ function extractTokenUsage(response) {
25
+ try {
26
+ const tokenUsage = response?.token_usage ?? response?.usage;
27
+ if (tokenUsage) {
28
+ return {
29
+ promptTokens: tokenUsage.prompt_tokens ?? 0,
30
+ completionTokens: tokenUsage.completion_tokens ?? 0,
31
+ totalTokens: tokenUsage.total_tokens ?? 0
32
+ };
33
+ }
34
+ const usageMetadata = response?.usage_metadata;
35
+ if (usageMetadata) {
36
+ return {
37
+ promptTokens: usageMetadata.input_tokens ?? 0,
38
+ completionTokens: usageMetadata.output_tokens ?? 0,
39
+ totalTokens: usageMetadata.total_tokens ?? 0
40
+ };
41
+ }
42
+ const responseMetadata = response?.response_metadata;
43
+ if (responseMetadata?.token_usage) {
44
+ return {
45
+ promptTokens: responseMetadata.token_usage.prompt_tokens ?? 0,
46
+ completionTokens: responseMetadata.token_usage.completion_tokens ?? 0,
47
+ totalTokens: responseMetadata.token_usage.total_tokens ?? 0
48
+ };
49
+ }
50
+ return null;
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ // packages/langchain/src/middleware.ts
57
+ class PrefactorMiddleware {
58
+ tracer;
59
+ rootSpan = null;
60
+ constructor(tracer) {
61
+ this.tracer = tracer;
62
+ }
63
+ async beforeAgent(state) {
64
+ const parentSpan = SpanContext.getCurrent();
65
+ const messages = state?.messages ?? [];
66
+ this.tracer.startAgentInstance();
67
+ const span = this.tracer.startSpan({
68
+ name: "agent",
69
+ spanType: SpanType.AGENT,
70
+ inputs: { messages: messages.slice(-3).map((m) => String(m)) },
71
+ parentSpanId: parentSpan?.spanId,
72
+ traceId: parentSpan?.traceId
73
+ });
74
+ this.rootSpan = span;
75
+ }
76
+ async afterAgent(state) {
77
+ if (!this.rootSpan) {
78
+ return;
79
+ }
80
+ const messages = state?.messages ?? [];
81
+ this.tracer.endSpan(this.rootSpan, {
82
+ outputs: { messages: messages.slice(-3).map((m) => String(m)) }
83
+ });
84
+ this.tracer.finishAgentInstance();
85
+ SpanContext.clear();
86
+ this.rootSpan = null;
87
+ }
88
+ async wrapModelCall(request, handler) {
89
+ const parentSpan = SpanContext.getCurrent();
90
+ const span = this.tracer.startSpan({
91
+ name: this.extractModelName(request),
92
+ spanType: SpanType.LLM,
93
+ inputs: this.extractModelInputs(request),
94
+ parentSpanId: parentSpan?.spanId,
95
+ traceId: parentSpan?.traceId
96
+ });
97
+ try {
98
+ const response = await SpanContext.runAsync(span, async () => {
99
+ return handler(request);
100
+ });
101
+ const outputs = this.extractModelOutputs(response);
102
+ const tokenUsage = extractTokenUsage(response);
103
+ this.tracer.endSpan(span, { outputs, tokenUsage: tokenUsage ?? undefined });
104
+ return response;
105
+ } catch (error) {
106
+ this.tracer.endSpan(span, { error });
107
+ throw error;
108
+ }
109
+ }
110
+ async wrapToolCall(request, handler) {
111
+ const parentSpan = SpanContext.getCurrent();
112
+ const span = this.tracer.startSpan({
113
+ name: this.extractToolName(request),
114
+ spanType: SpanType.TOOL,
115
+ inputs: this.extractToolInputs(request),
116
+ parentSpanId: parentSpan?.spanId,
117
+ traceId: parentSpan?.traceId
118
+ });
119
+ try {
120
+ const response = await SpanContext.runAsync(span, async () => {
121
+ return handler(request);
122
+ });
123
+ this.tracer.endSpan(span, {
124
+ outputs: this.extractToolOutputs(response)
125
+ });
126
+ return response;
127
+ } catch (error) {
128
+ this.tracer.endSpan(span, { error });
129
+ throw error;
130
+ }
131
+ }
132
+ extractModelName(request) {
133
+ return request?.model ?? request?.modelName ?? "unknown";
134
+ }
135
+ extractModelInputs(request) {
136
+ const messages = request?.messages ?? [];
137
+ return { messages: messages.slice(-3).map((m) => String(m)) };
138
+ }
139
+ extractModelOutputs(response) {
140
+ const content = response?.content ?? response?.text ?? "";
141
+ return { content: String(content) };
142
+ }
143
+ extractToolName(request) {
144
+ return request?.name ?? request?.tool ?? "unknown";
145
+ }
146
+ extractToolInputs(request) {
147
+ return { input: request?.input ?? request?.args ?? {} };
148
+ }
149
+ extractToolOutputs(response) {
150
+ return { output: response?.output ?? response };
151
+ }
152
+ }
153
+
154
+ // packages/langchain/src/init.ts
155
+ var logger = getLogger("init");
156
+ var globalTracer = null;
157
+ var globalMiddleware = null;
158
+ function init(config) {
159
+ configureLogging();
160
+ const finalConfig = createConfig(config);
161
+ logger.info("Initializing Prefactor SDK", { transport: finalConfig.transportType });
162
+ if (globalMiddleware !== null) {
163
+ return globalMiddleware;
164
+ }
165
+ let transport;
166
+ if (finalConfig.transportType === "stdio") {
167
+ transport = new StdioTransport;
168
+ } else {
169
+ if (!finalConfig.httpConfig) {
170
+ throw new Error("HTTP transport requires httpConfig to be provided in configuration");
171
+ }
172
+ const httpConfig = HttpTransportConfigSchema.parse(finalConfig.httpConfig);
173
+ transport = new HttpTransport(httpConfig);
174
+ }
175
+ let partition;
176
+ if (finalConfig.httpConfig?.agentId) {
177
+ try {
178
+ partition = extractPartition(finalConfig.httpConfig.agentId);
179
+ logger.debug("Extracted partition from agent_id", { partition });
180
+ } catch (error) {
181
+ logger.warn("Failed to extract partition from agent_id, using random partition", { error });
182
+ }
183
+ }
184
+ globalTracer = new Tracer(transport, partition);
185
+ const prefactorMiddleware = new PrefactorMiddleware(globalTracer);
186
+ const middleware = createMiddleware({
187
+ name: "prefactor",
188
+ wrapModelCall: async (request, handler) => {
189
+ return prefactorMiddleware.wrapModelCall(request, handler);
190
+ },
191
+ wrapToolCall: async (request, handler) => {
192
+ return prefactorMiddleware.wrapToolCall(request, handler);
193
+ },
194
+ beforeAgent: async (state) => {
195
+ await prefactorMiddleware.beforeAgent(state);
196
+ },
197
+ afterAgent: async (state) => {
198
+ await prefactorMiddleware.afterAgent(state);
199
+ }
200
+ });
201
+ globalMiddleware = middleware;
202
+ return middleware;
203
+ }
204
+ function getTracer() {
205
+ if (!globalTracer) {
206
+ init();
207
+ }
208
+ return globalTracer;
209
+ }
210
+ async function shutdown() {
211
+ if (globalTracer) {
212
+ logger.info("Shutting down Prefactor SDK");
213
+ await globalTracer.close();
214
+ }
215
+ globalTracer = null;
216
+ globalMiddleware = null;
217
+ }
218
+ process.on("beforeExit", () => {
219
+ shutdown().catch((error) => {
220
+ console.error("Error during Prefactor SDK shutdown:", error);
221
+ });
222
+ });
223
+ export {
224
+ shutdown,
225
+ init,
226
+ getTracer,
227
+ extractTokenUsage,
228
+ SpanType2 as SpanType,
229
+ SpanStatus,
230
+ PrefactorMiddleware
231
+ };
232
+
233
+ //# debugId=B136D336DD9CEBB764756E2164756E21
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/init.ts", "../src/middleware.ts", "../src/metadata-extractor.ts"],
4
+ "sourcesContent": [
5
+ "// Main entry points\n\n// Convenience re-exports from core\nexport {\n type Config,\n type HttpTransportConfig,\n type Span,\n SpanStatus,\n SpanType,\n} from '@prefactor/core';\nexport { getTracer, init, shutdown } from './init.js';\nexport { extractTokenUsage } from './metadata-extractor.js';\n// Middleware\nexport { PrefactorMiddleware } from './middleware.js';\n",
6
+ "import {\n type Config,\n configureLogging,\n createConfig,\n getLogger,\n HttpTransport,\n HttpTransportConfigSchema,\n StdioTransport,\n Tracer,\n type Transport,\n} from '@prefactor/core';\nimport { extractPartition, type Partition } from '@prefactor/pfid';\nimport { type AgentMiddleware, createMiddleware } from 'langchain';\nimport { PrefactorMiddleware } from './middleware.js';\n\nconst logger = getLogger('init');\n\nlet globalTracer: Tracer | null = null;\nlet globalMiddleware: AgentMiddleware | null = null;\n\n/**\n * Initialize the Prefactor SDK and return middleware for LangChain.js\n *\n * This is the main entry point for the SDK. Call this function to create a middleware\n * instance that you can pass to your LangChain.js agents.\n *\n * @param config - Optional configuration object\n * @returns PrefactorMiddleware instance to use with LangChain.js agents\n *\n * @example\n * ```typescript\n * import { init } from '@prefactor/langchain';\n * import { createAgent } from 'langchain';\n *\n * // Initialize with defaults (stdio transport)\n * const middleware = init();\n *\n * // Or configure HTTP transport\n * const middleware = init({\n * transportType: 'http',\n * httpConfig: {\n * apiUrl: 'https://api.prefactor.ai',\n * apiToken: process.env.PREFACTOR_API_TOKEN!,\n * }\n * });\n *\n * const agent = createAgent({\n * model: 'claude-sonnet-4-5-20250929',\n * tools: [myTool],\n * middleware: [middleware],\n * });\n * ```\n */\nexport function init(config?: Partial<Config>): AgentMiddleware {\n configureLogging();\n\n const finalConfig = createConfig(config);\n logger.info('Initializing Prefactor SDK', { transport: finalConfig.transportType });\n\n if (globalMiddleware !== null) {\n return globalMiddleware;\n }\n\n let transport: Transport;\n if (finalConfig.transportType === 'stdio') {\n transport = new StdioTransport();\n } else {\n if (!finalConfig.httpConfig) {\n throw new Error('HTTP transport requires httpConfig to be provided in configuration');\n }\n // Parse httpConfig to apply defaults from schema\n const httpConfig = HttpTransportConfigSchema.parse(finalConfig.httpConfig);\n transport = new HttpTransport(httpConfig);\n }\n\n // Extract partition from agent_id if provided (for HTTP transport)\n let partition: Partition | undefined;\n if (finalConfig.httpConfig?.agentId) {\n try {\n partition = extractPartition(finalConfig.httpConfig.agentId);\n logger.debug('Extracted partition from agent_id', { partition });\n } catch (error) {\n logger.warn('Failed to extract partition from agent_id, using random partition', { error });\n }\n }\n\n globalTracer = new Tracer(transport, partition);\n const prefactorMiddleware = new PrefactorMiddleware(globalTracer);\n\n const middleware = createMiddleware({\n name: 'prefactor',\n // biome-ignore lint/suspicious/noExplicitAny: LangChain middleware hooks use dynamic types\n wrapModelCall: async (request: any, handler: any) => {\n return prefactorMiddleware.wrapModelCall(request, handler);\n },\n // biome-ignore lint/suspicious/noExplicitAny: LangChain middleware hooks use dynamic types\n wrapToolCall: async (request: any, handler: any) => {\n return prefactorMiddleware.wrapToolCall(request, handler);\n },\n // biome-ignore lint/suspicious/noExplicitAny: LangChain middleware hooks use dynamic types\n beforeAgent: async (state: any) => {\n await prefactorMiddleware.beforeAgent(state);\n },\n // biome-ignore lint/suspicious/noExplicitAny: LangChain middleware hooks use dynamic types\n afterAgent: async (state: any) => {\n await prefactorMiddleware.afterAgent(state);\n },\n });\n\n globalMiddleware = middleware;\n return middleware;\n}\n\n/**\n * Get the current tracer instance.\n *\n * If no tracer has been created yet, this will call init() with default configuration.\n *\n * @returns Tracer instance\n *\n * @example\n * ```typescript\n * import { getTracer } from '@prefactor/langchain';\n *\n * const tracer = getTracer();\n * const span = tracer.startSpan({\n * name: 'custom-operation',\n * spanType: SpanType.TOOL,\n * inputs: { data: 'example' }\n * });\n * ```\n */\nexport function getTracer(): Tracer {\n if (!globalTracer) {\n init();\n }\n // Safe because init() always sets globalTracer\n return globalTracer as Tracer;\n}\n\n/**\n * Shutdown the SDK and flush any pending spans.\n *\n * Call this before your application exits to ensure all spans are sent to the transport.\n * This is especially important for HTTP transport which has a queue of pending requests.\n *\n * @returns Promise that resolves when shutdown is complete\n *\n * @example\n * ```typescript\n * import { shutdown } from '@prefactor/langchain';\n *\n * process.on('SIGTERM', async () => {\n * await shutdown();\n * process.exit(0);\n * });\n * ```\n */\nexport async function shutdown(): Promise<void> {\n if (globalTracer) {\n logger.info('Shutting down Prefactor SDK');\n await globalTracer.close();\n }\n globalTracer = null;\n globalMiddleware = null;\n}\n\n// Automatic shutdown on process exit\nprocess.on('beforeExit', () => {\n shutdown().catch((error) => {\n console.error('Error during Prefactor SDK shutdown:', error);\n });\n});\n",
7
+ "import { type Span, SpanContext, SpanType, type Tracer } from '@prefactor/core';\nimport { extractTokenUsage } from './metadata-extractor.js';\n\n/**\n * Prefactor middleware for LangChain.js agents.\n *\n * This middleware automatically traces LLM calls, tool executions, and agent workflows.\n * It integrates with LangChain.js middleware API to provide transparent instrumentation.\n *\n * Features:\n * - Automatic parent-child span relationships via context propagation\n * - Token usage extraction for LLM calls\n * - Error tracking and debugging\n * - Zero-overhead instrumentation (graceful failure)\n *\n * @example\n * ```typescript\n * import { init } from '@prefactor/langchain';\n * import { createReactAgent } from '@langchain/langgraph/prebuilt';\n *\n * const middleware = init();\n * const agent = createReactAgent({\n * llm: model,\n * tools: [myTool],\n * middleware: [middleware],\n * });\n * ```\n */\nexport class PrefactorMiddleware {\n private rootSpan: Span | null = null;\n\n constructor(private tracer: Tracer) {}\n\n /**\n * Called before agent execution starts\n *\n * @param state - Agent state containing messages\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain state can be any structure\n async beforeAgent(state: any): Promise<void> {\n const parentSpan = SpanContext.getCurrent();\n const messages = state?.messages ?? [];\n\n this.tracer.startAgentInstance();\n\n const span = this.tracer.startSpan({\n name: 'agent',\n spanType: SpanType.AGENT,\n inputs: { messages: messages.slice(-3).map((m: unknown) => String(m)) },\n parentSpanId: parentSpan?.spanId,\n traceId: parentSpan?.traceId,\n });\n\n this.rootSpan = span;\n }\n\n /**\n * Called after agent execution completes\n *\n * @param state - Agent state containing messages\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain state can be any structure\n async afterAgent(state: any): Promise<void> {\n if (!this.rootSpan) {\n return;\n }\n\n const messages = state?.messages ?? [];\n this.tracer.endSpan(this.rootSpan, {\n outputs: { messages: messages.slice(-3).map((m: unknown) => String(m)) },\n });\n\n this.tracer.finishAgentInstance();\n SpanContext.clear();\n this.rootSpan = null;\n }\n\n /**\n * Wrap a model call to trace LLM invocations\n *\n * @param request - Model invocation request\n * @param handler - The actual model call function\n * @returns Promise resolving to the model response\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain request/handler types are dynamic\n async wrapModelCall<T>(request: any, handler: (req: any) => Promise<T>): Promise<T> {\n const parentSpan = SpanContext.getCurrent();\n\n const span = this.tracer.startSpan({\n name: this.extractModelName(request),\n spanType: SpanType.LLM,\n inputs: this.extractModelInputs(request),\n parentSpanId: parentSpan?.spanId,\n traceId: parentSpan?.traceId,\n });\n\n try {\n // CRITICAL: Wrap handler in context so child operations see this span\n const response = await SpanContext.runAsync(span, async () => {\n return handler(request);\n });\n\n const outputs = this.extractModelOutputs(response);\n const tokenUsage = extractTokenUsage(response);\n\n this.tracer.endSpan(span, { outputs, tokenUsage: tokenUsage ?? undefined });\n return response;\n } catch (error) {\n this.tracer.endSpan(span, { error: error as Error });\n throw error;\n }\n }\n\n /**\n * Wrap a tool call to trace tool executions\n *\n * @param request - Tool invocation request\n * @param handler - The actual tool call function\n * @returns Promise resolving to the tool response\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain request/handler types are dynamic\n async wrapToolCall<T>(request: any, handler: (req: any) => Promise<T>): Promise<T> {\n const parentSpan = SpanContext.getCurrent();\n\n const span = this.tracer.startSpan({\n name: this.extractToolName(request),\n spanType: SpanType.TOOL,\n inputs: this.extractToolInputs(request),\n parentSpanId: parentSpan?.spanId,\n traceId: parentSpan?.traceId,\n });\n\n try {\n // CRITICAL: Wrap handler in context so child operations see this span\n const response = await SpanContext.runAsync(span, async () => {\n return handler(request);\n });\n\n this.tracer.endSpan(span, {\n outputs: this.extractToolOutputs(response),\n });\n return response;\n } catch (error) {\n this.tracer.endSpan(span, { error: error as Error });\n throw error;\n }\n }\n\n /**\n * Extract model name from request\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain request structure is dynamic\n private extractModelName(request: any): string {\n return request?.model ?? request?.modelName ?? 'unknown';\n }\n\n /**\n * Extract model inputs from request\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain request structure is dynamic\n private extractModelInputs(request: any): Record<string, unknown> {\n const messages = request?.messages ?? [];\n return { messages: messages.slice(-3).map((m: unknown) => String(m)) };\n }\n\n /**\n * Extract model outputs from response\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain response structure is dynamic\n private extractModelOutputs(response: any): Record<string, unknown> {\n const content = response?.content ?? response?.text ?? '';\n return { content: String(content) };\n }\n\n /**\n * Extract tool name from request\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain request structure is dynamic\n private extractToolName(request: any): string {\n return request?.name ?? request?.tool ?? 'unknown';\n }\n\n /**\n * Extract tool inputs from request\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain request structure is dynamic\n private extractToolInputs(request: any): Record<string, unknown> {\n return { input: request?.input ?? request?.args ?? {} };\n }\n\n /**\n * Extract tool outputs from response\n */\n // biome-ignore lint/suspicious/noExplicitAny: LangChain response structure is dynamic\n private extractToolOutputs(response: any): Record<string, unknown> {\n return { output: response?.output ?? response };\n }\n}\n",
8
+ "import type { TokenUsage } from '@prefactor/core';\n\n/**\n * Extract token usage information from LLM responses.\n *\n * Handles multiple response formats from different LLM providers and LangChain versions.\n *\n * @param response - The LLM response object\n * @returns TokenUsage object or null if no usage data found\n *\n * @example\n * ```typescript\n * const response = await model.invoke(messages);\n * const tokenUsage = extractTokenUsage(response);\n * if (tokenUsage) {\n * console.log(`Tokens used: ${tokenUsage.totalTokens}`);\n * }\n * ```\n */\n// biome-ignore lint/suspicious/noExplicitAny: LLM response structure varies by provider\nexport function extractTokenUsage(response: any): TokenUsage | null {\n try {\n // Try token_usage field (common format)\n const tokenUsage = response?.token_usage ?? response?.usage;\n if (tokenUsage) {\n return {\n promptTokens: tokenUsage.prompt_tokens ?? 0,\n completionTokens: tokenUsage.completion_tokens ?? 0,\n totalTokens: tokenUsage.total_tokens ?? 0,\n };\n }\n\n // Try usage_metadata field (LangChain format)\n const usageMetadata = response?.usage_metadata;\n if (usageMetadata) {\n return {\n promptTokens: usageMetadata.input_tokens ?? 0,\n completionTokens: usageMetadata.output_tokens ?? 0,\n totalTokens: usageMetadata.total_tokens ?? 0,\n };\n }\n\n // Try response_metadata.token_usage (nested format)\n const responseMetadata = response?.response_metadata;\n if (responseMetadata?.token_usage) {\n return {\n promptTokens: responseMetadata.token_usage.prompt_tokens ?? 0,\n completionTokens: responseMetadata.token_usage.completion_tokens ?? 0,\n totalTokens: responseMetadata.token_usage.total_tokens ?? 0,\n };\n }\n\n return null;\n } catch {\n return null;\n }\n}\n"
9
+ ],
10
+ "mappings": "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",
11
+ "debugId": "B136D336DD9CEBB764756E2164756E21",
12
+ "names": []
13
+ }
package/dist/init.d.ts ADDED
@@ -0,0 +1,76 @@
1
+ import { type Config, Tracer } from '@prefactor/core';
2
+ import { type AgentMiddleware } from 'langchain';
3
+ /**
4
+ * Initialize the Prefactor SDK and return middleware for LangChain.js
5
+ *
6
+ * This is the main entry point for the SDK. Call this function to create a middleware
7
+ * instance that you can pass to your LangChain.js agents.
8
+ *
9
+ * @param config - Optional configuration object
10
+ * @returns PrefactorMiddleware instance to use with LangChain.js agents
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { init } from '@prefactor/langchain';
15
+ * import { createAgent } from 'langchain';
16
+ *
17
+ * // Initialize with defaults (stdio transport)
18
+ * const middleware = init();
19
+ *
20
+ * // Or configure HTTP transport
21
+ * const middleware = init({
22
+ * transportType: 'http',
23
+ * httpConfig: {
24
+ * apiUrl: 'https://api.prefactor.ai',
25
+ * apiToken: process.env.PREFACTOR_API_TOKEN!,
26
+ * }
27
+ * });
28
+ *
29
+ * const agent = createAgent({
30
+ * model: 'claude-sonnet-4-5-20250929',
31
+ * tools: [myTool],
32
+ * middleware: [middleware],
33
+ * });
34
+ * ```
35
+ */
36
+ export declare function init(config?: Partial<Config>): AgentMiddleware;
37
+ /**
38
+ * Get the current tracer instance.
39
+ *
40
+ * If no tracer has been created yet, this will call init() with default configuration.
41
+ *
42
+ * @returns Tracer instance
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * import { getTracer } from '@prefactor/langchain';
47
+ *
48
+ * const tracer = getTracer();
49
+ * const span = tracer.startSpan({
50
+ * name: 'custom-operation',
51
+ * spanType: SpanType.TOOL,
52
+ * inputs: { data: 'example' }
53
+ * });
54
+ * ```
55
+ */
56
+ export declare function getTracer(): Tracer;
57
+ /**
58
+ * Shutdown the SDK and flush any pending spans.
59
+ *
60
+ * Call this before your application exits to ensure all spans are sent to the transport.
61
+ * This is especially important for HTTP transport which has a queue of pending requests.
62
+ *
63
+ * @returns Promise that resolves when shutdown is complete
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * import { shutdown } from '@prefactor/langchain';
68
+ *
69
+ * process.on('SIGTERM', async () => {
70
+ * await shutdown();
71
+ * process.exit(0);
72
+ * });
73
+ * ```
74
+ */
75
+ export declare function shutdown(): Promise<void>;
76
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EAOX,MAAM,EAEP,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,KAAK,eAAe,EAAoB,MAAM,WAAW,CAAC;AAQnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,eAAe,CA0D9D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAMlC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAO9C"}
package/dist/init.js ADDED
@@ -0,0 +1,153 @@
1
+ import { configureLogging, createConfig, getLogger, HttpTransport, HttpTransportConfigSchema, StdioTransport, Tracer, } from '@prefactor/core';
2
+ import { extractPartition } from '@prefactor/pfid';
3
+ import { createMiddleware } from 'langchain';
4
+ import { PrefactorMiddleware } from './middleware.js';
5
+ const logger = getLogger('init');
6
+ let globalTracer = null;
7
+ let globalMiddleware = null;
8
+ /**
9
+ * Initialize the Prefactor SDK and return middleware for LangChain.js
10
+ *
11
+ * This is the main entry point for the SDK. Call this function to create a middleware
12
+ * instance that you can pass to your LangChain.js agents.
13
+ *
14
+ * @param config - Optional configuration object
15
+ * @returns PrefactorMiddleware instance to use with LangChain.js agents
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { init } from '@prefactor/langchain';
20
+ * import { createAgent } from 'langchain';
21
+ *
22
+ * // Initialize with defaults (stdio transport)
23
+ * const middleware = init();
24
+ *
25
+ * // Or configure HTTP transport
26
+ * const middleware = init({
27
+ * transportType: 'http',
28
+ * httpConfig: {
29
+ * apiUrl: 'https://api.prefactor.ai',
30
+ * apiToken: process.env.PREFACTOR_API_TOKEN!,
31
+ * }
32
+ * });
33
+ *
34
+ * const agent = createAgent({
35
+ * model: 'claude-sonnet-4-5-20250929',
36
+ * tools: [myTool],
37
+ * middleware: [middleware],
38
+ * });
39
+ * ```
40
+ */
41
+ export function init(config) {
42
+ configureLogging();
43
+ const finalConfig = createConfig(config);
44
+ logger.info('Initializing Prefactor SDK', { transport: finalConfig.transportType });
45
+ if (globalMiddleware !== null) {
46
+ return globalMiddleware;
47
+ }
48
+ let transport;
49
+ if (finalConfig.transportType === 'stdio') {
50
+ transport = new StdioTransport();
51
+ }
52
+ else {
53
+ if (!finalConfig.httpConfig) {
54
+ throw new Error('HTTP transport requires httpConfig to be provided in configuration');
55
+ }
56
+ // Parse httpConfig to apply defaults from schema
57
+ const httpConfig = HttpTransportConfigSchema.parse(finalConfig.httpConfig);
58
+ transport = new HttpTransport(httpConfig);
59
+ }
60
+ // Extract partition from agent_id if provided (for HTTP transport)
61
+ let partition;
62
+ if (finalConfig.httpConfig?.agentId) {
63
+ try {
64
+ partition = extractPartition(finalConfig.httpConfig.agentId);
65
+ logger.debug('Extracted partition from agent_id', { partition });
66
+ }
67
+ catch (error) {
68
+ logger.warn('Failed to extract partition from agent_id, using random partition', { error });
69
+ }
70
+ }
71
+ globalTracer = new Tracer(transport, partition);
72
+ const prefactorMiddleware = new PrefactorMiddleware(globalTracer);
73
+ const middleware = createMiddleware({
74
+ name: 'prefactor',
75
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain middleware hooks use dynamic types
76
+ wrapModelCall: async (request, handler) => {
77
+ return prefactorMiddleware.wrapModelCall(request, handler);
78
+ },
79
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain middleware hooks use dynamic types
80
+ wrapToolCall: async (request, handler) => {
81
+ return prefactorMiddleware.wrapToolCall(request, handler);
82
+ },
83
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain middleware hooks use dynamic types
84
+ beforeAgent: async (state) => {
85
+ await prefactorMiddleware.beforeAgent(state);
86
+ },
87
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain middleware hooks use dynamic types
88
+ afterAgent: async (state) => {
89
+ await prefactorMiddleware.afterAgent(state);
90
+ },
91
+ });
92
+ globalMiddleware = middleware;
93
+ return middleware;
94
+ }
95
+ /**
96
+ * Get the current tracer instance.
97
+ *
98
+ * If no tracer has been created yet, this will call init() with default configuration.
99
+ *
100
+ * @returns Tracer instance
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * import { getTracer } from '@prefactor/langchain';
105
+ *
106
+ * const tracer = getTracer();
107
+ * const span = tracer.startSpan({
108
+ * name: 'custom-operation',
109
+ * spanType: SpanType.TOOL,
110
+ * inputs: { data: 'example' }
111
+ * });
112
+ * ```
113
+ */
114
+ export function getTracer() {
115
+ if (!globalTracer) {
116
+ init();
117
+ }
118
+ // Safe because init() always sets globalTracer
119
+ return globalTracer;
120
+ }
121
+ /**
122
+ * Shutdown the SDK and flush any pending spans.
123
+ *
124
+ * Call this before your application exits to ensure all spans are sent to the transport.
125
+ * This is especially important for HTTP transport which has a queue of pending requests.
126
+ *
127
+ * @returns Promise that resolves when shutdown is complete
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * import { shutdown } from '@prefactor/langchain';
132
+ *
133
+ * process.on('SIGTERM', async () => {
134
+ * await shutdown();
135
+ * process.exit(0);
136
+ * });
137
+ * ```
138
+ */
139
+ export async function shutdown() {
140
+ if (globalTracer) {
141
+ logger.info('Shutting down Prefactor SDK');
142
+ await globalTracer.close();
143
+ }
144
+ globalTracer = null;
145
+ globalMiddleware = null;
146
+ }
147
+ // Automatic shutdown on process exit
148
+ process.on('beforeExit', () => {
149
+ shutdown().catch((error) => {
150
+ console.error('Error during Prefactor SDK shutdown:', error);
151
+ });
152
+ });
153
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { TokenUsage } from '@prefactor/core';
2
+ /**
3
+ * Extract token usage information from LLM responses.
4
+ *
5
+ * Handles multiple response formats from different LLM providers and LangChain versions.
6
+ *
7
+ * @param response - The LLM response object
8
+ * @returns TokenUsage object or null if no usage data found
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const response = await model.invoke(messages);
13
+ * const tokenUsage = extractTokenUsage(response);
14
+ * if (tokenUsage) {
15
+ * console.log(`Tokens used: ${tokenUsage.totalTokens}`);
16
+ * }
17
+ * ```
18
+ */
19
+ export declare function extractTokenUsage(response: any): TokenUsage | null;
20
+ //# sourceMappingURL=metadata-extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata-extractor.d.ts","sourceRoot":"","sources":["../src/metadata-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;;;;;;;;;;;;;GAgBG;AAEH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,GAAG,GAAG,UAAU,GAAG,IAAI,CAoClE"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Extract token usage information from LLM responses.
3
+ *
4
+ * Handles multiple response formats from different LLM providers and LangChain versions.
5
+ *
6
+ * @param response - The LLM response object
7
+ * @returns TokenUsage object or null if no usage data found
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const response = await model.invoke(messages);
12
+ * const tokenUsage = extractTokenUsage(response);
13
+ * if (tokenUsage) {
14
+ * console.log(`Tokens used: ${tokenUsage.totalTokens}`);
15
+ * }
16
+ * ```
17
+ */
18
+ // biome-ignore lint/suspicious/noExplicitAny: LLM response structure varies by provider
19
+ export function extractTokenUsage(response) {
20
+ try {
21
+ // Try token_usage field (common format)
22
+ const tokenUsage = response?.token_usage ?? response?.usage;
23
+ if (tokenUsage) {
24
+ return {
25
+ promptTokens: tokenUsage.prompt_tokens ?? 0,
26
+ completionTokens: tokenUsage.completion_tokens ?? 0,
27
+ totalTokens: tokenUsage.total_tokens ?? 0,
28
+ };
29
+ }
30
+ // Try usage_metadata field (LangChain format)
31
+ const usageMetadata = response?.usage_metadata;
32
+ if (usageMetadata) {
33
+ return {
34
+ promptTokens: usageMetadata.input_tokens ?? 0,
35
+ completionTokens: usageMetadata.output_tokens ?? 0,
36
+ totalTokens: usageMetadata.total_tokens ?? 0,
37
+ };
38
+ }
39
+ // Try response_metadata.token_usage (nested format)
40
+ const responseMetadata = response?.response_metadata;
41
+ if (responseMetadata?.token_usage) {
42
+ return {
43
+ promptTokens: responseMetadata.token_usage.prompt_tokens ?? 0,
44
+ completionTokens: responseMetadata.token_usage.completion_tokens ?? 0,
45
+ totalTokens: responseMetadata.token_usage.total_tokens ?? 0,
46
+ };
47
+ }
48
+ return null;
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ //# sourceMappingURL=metadata-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata-extractor.js","sourceRoot":"","sources":["../src/metadata-extractor.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AACH,wFAAwF;AACxF,MAAM,UAAU,iBAAiB,CAAC,QAAa;IAC7C,IAAI,CAAC;QACH,wCAAwC;QACxC,MAAM,UAAU,GAAG,QAAQ,EAAE,WAAW,IAAI,QAAQ,EAAE,KAAK,CAAC;QAC5D,IAAI,UAAU,EAAE,CAAC;YACf,OAAO;gBACL,YAAY,EAAE,UAAU,CAAC,aAAa,IAAI,CAAC;gBAC3C,gBAAgB,EAAE,UAAU,CAAC,iBAAiB,IAAI,CAAC;gBACnD,WAAW,EAAE,UAAU,CAAC,YAAY,IAAI,CAAC;aAC1C,CAAC;QACJ,CAAC;QAED,8CAA8C;QAC9C,MAAM,aAAa,GAAG,QAAQ,EAAE,cAAc,CAAC;QAC/C,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO;gBACL,YAAY,EAAE,aAAa,CAAC,YAAY,IAAI,CAAC;gBAC7C,gBAAgB,EAAE,aAAa,CAAC,aAAa,IAAI,CAAC;gBAClD,WAAW,EAAE,aAAa,CAAC,YAAY,IAAI,CAAC;aAC7C,CAAC;QACJ,CAAC;QAED,oDAAoD;QACpD,MAAM,gBAAgB,GAAG,QAAQ,EAAE,iBAAiB,CAAC;QACrD,IAAI,gBAAgB,EAAE,WAAW,EAAE,CAAC;YAClC,OAAO;gBACL,YAAY,EAAE,gBAAgB,CAAC,WAAW,CAAC,aAAa,IAAI,CAAC;gBAC7D,gBAAgB,EAAE,gBAAgB,CAAC,WAAW,CAAC,iBAAiB,IAAI,CAAC;gBACrE,WAAW,EAAE,gBAAgB,CAAC,WAAW,CAAC,YAAY,IAAI,CAAC;aAC5D,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,84 @@
1
+ import { type Tracer } from '@prefactor/core';
2
+ /**
3
+ * Prefactor middleware for LangChain.js agents.
4
+ *
5
+ * This middleware automatically traces LLM calls, tool executions, and agent workflows.
6
+ * It integrates with LangChain.js middleware API to provide transparent instrumentation.
7
+ *
8
+ * Features:
9
+ * - Automatic parent-child span relationships via context propagation
10
+ * - Token usage extraction for LLM calls
11
+ * - Error tracking and debugging
12
+ * - Zero-overhead instrumentation (graceful failure)
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { init } from '@prefactor/langchain';
17
+ * import { createReactAgent } from '@langchain/langgraph/prebuilt';
18
+ *
19
+ * const middleware = init();
20
+ * const agent = createReactAgent({
21
+ * llm: model,
22
+ * tools: [myTool],
23
+ * middleware: [middleware],
24
+ * });
25
+ * ```
26
+ */
27
+ export declare class PrefactorMiddleware {
28
+ private tracer;
29
+ private rootSpan;
30
+ constructor(tracer: Tracer);
31
+ /**
32
+ * Called before agent execution starts
33
+ *
34
+ * @param state - Agent state containing messages
35
+ */
36
+ beforeAgent(state: any): Promise<void>;
37
+ /**
38
+ * Called after agent execution completes
39
+ *
40
+ * @param state - Agent state containing messages
41
+ */
42
+ afterAgent(state: any): Promise<void>;
43
+ /**
44
+ * Wrap a model call to trace LLM invocations
45
+ *
46
+ * @param request - Model invocation request
47
+ * @param handler - The actual model call function
48
+ * @returns Promise resolving to the model response
49
+ */
50
+ wrapModelCall<T>(request: any, handler: (req: any) => Promise<T>): Promise<T>;
51
+ /**
52
+ * Wrap a tool call to trace tool executions
53
+ *
54
+ * @param request - Tool invocation request
55
+ * @param handler - The actual tool call function
56
+ * @returns Promise resolving to the tool response
57
+ */
58
+ wrapToolCall<T>(request: any, handler: (req: any) => Promise<T>): Promise<T>;
59
+ /**
60
+ * Extract model name from request
61
+ */
62
+ private extractModelName;
63
+ /**
64
+ * Extract model inputs from request
65
+ */
66
+ private extractModelInputs;
67
+ /**
68
+ * Extract model outputs from response
69
+ */
70
+ private extractModelOutputs;
71
+ /**
72
+ * Extract tool name from request
73
+ */
74
+ private extractToolName;
75
+ /**
76
+ * Extract tool inputs from request
77
+ */
78
+ private extractToolInputs;
79
+ /**
80
+ * Extract tool outputs from response
81
+ */
82
+ private extractToolOutputs;
83
+ }
84
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAGhF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,mBAAmB;IAGlB,OAAO,CAAC,MAAM;IAF1B,OAAO,CAAC,QAAQ,CAAqB;gBAEjB,MAAM,EAAE,MAAM;IAElC;;;;OAIG;IAEG,WAAW,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB5C;;;;OAIG;IAEG,UAAU,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAe3C;;;;;;OAMG;IAEG,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA4BnF;;;;;;OAMG;IAEG,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA2BlF;;OAEG;IAEH,OAAO,CAAC,gBAAgB;IAIxB;;OAEG;IAEH,OAAO,CAAC,kBAAkB;IAK1B;;OAEG;IAEH,OAAO,CAAC,mBAAmB;IAK3B;;OAEG;IAEH,OAAO,CAAC,eAAe;IAIvB;;OAEG;IAEH,OAAO,CAAC,iBAAiB;IAIzB;;OAEG;IAEH,OAAO,CAAC,kBAAkB;CAG3B"}
@@ -0,0 +1,180 @@
1
+ import { SpanContext, SpanType } from '@prefactor/core';
2
+ import { extractTokenUsage } from './metadata-extractor.js';
3
+ /**
4
+ * Prefactor middleware for LangChain.js agents.
5
+ *
6
+ * This middleware automatically traces LLM calls, tool executions, and agent workflows.
7
+ * It integrates with LangChain.js middleware API to provide transparent instrumentation.
8
+ *
9
+ * Features:
10
+ * - Automatic parent-child span relationships via context propagation
11
+ * - Token usage extraction for LLM calls
12
+ * - Error tracking and debugging
13
+ * - Zero-overhead instrumentation (graceful failure)
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { init } from '@prefactor/langchain';
18
+ * import { createReactAgent } from '@langchain/langgraph/prebuilt';
19
+ *
20
+ * const middleware = init();
21
+ * const agent = createReactAgent({
22
+ * llm: model,
23
+ * tools: [myTool],
24
+ * middleware: [middleware],
25
+ * });
26
+ * ```
27
+ */
28
+ export class PrefactorMiddleware {
29
+ tracer;
30
+ rootSpan = null;
31
+ constructor(tracer) {
32
+ this.tracer = tracer;
33
+ }
34
+ /**
35
+ * Called before agent execution starts
36
+ *
37
+ * @param state - Agent state containing messages
38
+ */
39
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain state can be any structure
40
+ async beforeAgent(state) {
41
+ const parentSpan = SpanContext.getCurrent();
42
+ const messages = state?.messages ?? [];
43
+ this.tracer.startAgentInstance();
44
+ const span = this.tracer.startSpan({
45
+ name: 'agent',
46
+ spanType: SpanType.AGENT,
47
+ inputs: { messages: messages.slice(-3).map((m) => String(m)) },
48
+ parentSpanId: parentSpan?.spanId,
49
+ traceId: parentSpan?.traceId,
50
+ });
51
+ this.rootSpan = span;
52
+ }
53
+ /**
54
+ * Called after agent execution completes
55
+ *
56
+ * @param state - Agent state containing messages
57
+ */
58
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain state can be any structure
59
+ async afterAgent(state) {
60
+ if (!this.rootSpan) {
61
+ return;
62
+ }
63
+ const messages = state?.messages ?? [];
64
+ this.tracer.endSpan(this.rootSpan, {
65
+ outputs: { messages: messages.slice(-3).map((m) => String(m)) },
66
+ });
67
+ this.tracer.finishAgentInstance();
68
+ SpanContext.clear();
69
+ this.rootSpan = null;
70
+ }
71
+ /**
72
+ * Wrap a model call to trace LLM invocations
73
+ *
74
+ * @param request - Model invocation request
75
+ * @param handler - The actual model call function
76
+ * @returns Promise resolving to the model response
77
+ */
78
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain request/handler types are dynamic
79
+ async wrapModelCall(request, handler) {
80
+ const parentSpan = SpanContext.getCurrent();
81
+ const span = this.tracer.startSpan({
82
+ name: this.extractModelName(request),
83
+ spanType: SpanType.LLM,
84
+ inputs: this.extractModelInputs(request),
85
+ parentSpanId: parentSpan?.spanId,
86
+ traceId: parentSpan?.traceId,
87
+ });
88
+ try {
89
+ // CRITICAL: Wrap handler in context so child operations see this span
90
+ const response = await SpanContext.runAsync(span, async () => {
91
+ return handler(request);
92
+ });
93
+ const outputs = this.extractModelOutputs(response);
94
+ const tokenUsage = extractTokenUsage(response);
95
+ this.tracer.endSpan(span, { outputs, tokenUsage: tokenUsage ?? undefined });
96
+ return response;
97
+ }
98
+ catch (error) {
99
+ this.tracer.endSpan(span, { error: error });
100
+ throw error;
101
+ }
102
+ }
103
+ /**
104
+ * Wrap a tool call to trace tool executions
105
+ *
106
+ * @param request - Tool invocation request
107
+ * @param handler - The actual tool call function
108
+ * @returns Promise resolving to the tool response
109
+ */
110
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain request/handler types are dynamic
111
+ async wrapToolCall(request, handler) {
112
+ const parentSpan = SpanContext.getCurrent();
113
+ const span = this.tracer.startSpan({
114
+ name: this.extractToolName(request),
115
+ spanType: SpanType.TOOL,
116
+ inputs: this.extractToolInputs(request),
117
+ parentSpanId: parentSpan?.spanId,
118
+ traceId: parentSpan?.traceId,
119
+ });
120
+ try {
121
+ // CRITICAL: Wrap handler in context so child operations see this span
122
+ const response = await SpanContext.runAsync(span, async () => {
123
+ return handler(request);
124
+ });
125
+ this.tracer.endSpan(span, {
126
+ outputs: this.extractToolOutputs(response),
127
+ });
128
+ return response;
129
+ }
130
+ catch (error) {
131
+ this.tracer.endSpan(span, { error: error });
132
+ throw error;
133
+ }
134
+ }
135
+ /**
136
+ * Extract model name from request
137
+ */
138
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain request structure is dynamic
139
+ extractModelName(request) {
140
+ return request?.model ?? request?.modelName ?? 'unknown';
141
+ }
142
+ /**
143
+ * Extract model inputs from request
144
+ */
145
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain request structure is dynamic
146
+ extractModelInputs(request) {
147
+ const messages = request?.messages ?? [];
148
+ return { messages: messages.slice(-3).map((m) => String(m)) };
149
+ }
150
+ /**
151
+ * Extract model outputs from response
152
+ */
153
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain response structure is dynamic
154
+ extractModelOutputs(response) {
155
+ const content = response?.content ?? response?.text ?? '';
156
+ return { content: String(content) };
157
+ }
158
+ /**
159
+ * Extract tool name from request
160
+ */
161
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain request structure is dynamic
162
+ extractToolName(request) {
163
+ return request?.name ?? request?.tool ?? 'unknown';
164
+ }
165
+ /**
166
+ * Extract tool inputs from request
167
+ */
168
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain request structure is dynamic
169
+ extractToolInputs(request) {
170
+ return { input: request?.input ?? request?.args ?? {} };
171
+ }
172
+ /**
173
+ * Extract tool outputs from response
174
+ */
175
+ // biome-ignore lint/suspicious/noExplicitAny: LangChain response structure is dynamic
176
+ extractToolOutputs(response) {
177
+ return { output: response?.output ?? response };
178
+ }
179
+ }
180
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@prefactor/langchain",
3
+ "version": "0.1.0",
4
+ "description": "LangChain.js integration for Prefactor observability",
5
+ "type": "module",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "keywords": [
20
+ "prefactor",
21
+ "observability",
22
+ "tracing",
23
+ "langchain",
24
+ "llm",
25
+ "agent",
26
+ "monitoring"
27
+ ],
28
+ "author": "Prefactor",
29
+ "license": "MIT",
30
+ "dependencies": {
31
+ "@prefactor/core": "0.1.0",
32
+ "@prefactor/pfid": "^0.1.0"
33
+ },
34
+ "peerDependencies": {
35
+ "langchain": "^1.0.0"
36
+ },
37
+ "engines": {
38
+ "node": ">=24.0.0"
39
+ }
40
+ }