@evalguard/langchain 1.0.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/LICENSE +201 -0
- package/README.md +334 -0
- package/dist/cost.d.ts +4 -0
- package/dist/cost.d.ts.map +1 -0
- package/dist/cost.js +18 -0
- package/dist/cost.js.map +1 -0
- package/dist/guardrail-client.d.ts +3 -0
- package/dist/guardrail-client.d.ts.map +1 -0
- package/dist/guardrail-client.js +15 -0
- package/dist/guardrail-client.js.map +1 -0
- package/dist/index.d.ts +231 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +592 -0
- package/dist/index.js.map +1 -0
- package/package.json +74 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EvalGuard instrumentation for LangChain + LangGraph.
|
|
3
|
+
*
|
|
4
|
+
* LangChain's instrumentation surface is `@langchain/core/callbacks`. The
|
|
5
|
+
* recommended integration shape is a class that extends `BaseCallbackHandler`
|
|
6
|
+
* and overrides `handleLLMStart` / `handleLLMEnd` (and friends). The same
|
|
7
|
+
* handler works across LangChain.js + LangGraph because LangGraph reuses
|
|
8
|
+
* LangChain's callback dispatching for node-level events.
|
|
9
|
+
*
|
|
10
|
+
* Two usage patterns:
|
|
11
|
+
*
|
|
12
|
+
* // 1. Pass directly to a chain/runnable
|
|
13
|
+
* import { ChatOpenAI } from "@langchain/openai";
|
|
14
|
+
* import { EvalGuardCallbackHandler } from "@evalguard/langchain";
|
|
15
|
+
*
|
|
16
|
+
* const handler = new EvalGuardCallbackHandler({
|
|
17
|
+
* apiKey: process.env.EVALGUARD_API_KEY!,
|
|
18
|
+
* projectId: "proj-123",
|
|
19
|
+
* });
|
|
20
|
+
* const model = new ChatOpenAI({ model: "gpt-4o", callbacks: [handler] });
|
|
21
|
+
*
|
|
22
|
+
* // 2. Or globally via env (auto-installed when present)
|
|
23
|
+
* import { autoInstall } from "@evalguard/langchain";
|
|
24
|
+
* autoInstall(); // reads EVALGUARD_API_KEY + EVALGUARD_PROJECT_ID
|
|
25
|
+
*
|
|
26
|
+
* Implementation notes:
|
|
27
|
+
* - We do NOT depend on `@langchain/core` at install time. The handler
|
|
28
|
+
* extends a duck-typed `BaseCallbackHandler` shape so the package
|
|
29
|
+
* compiles + tests without LangChain present. Customers get full types
|
|
30
|
+
* from their own `@langchain/core` install via TypeScript's module
|
|
31
|
+
* resolution.
|
|
32
|
+
* - Fail-CLOSED semantics match openai-wrapper / anthropic-wrapper /
|
|
33
|
+
* llamaindex-wrapper (changed 2026-05-28): when `blockOnViolation` is
|
|
34
|
+
* on (the default), an UNREACHABLE guardrail blocks the run rather
|
|
35
|
+
* than waving it through. Set `blockOnViolation: false` for
|
|
36
|
+
* monitor-only/availability-first deployments.
|
|
37
|
+
* - `blockOnViolation` raises `EvalguardBlockedError` from `handleLLMStart`,
|
|
38
|
+
* which aborts the run ONLY because this handler sets
|
|
39
|
+
* `raiseError = true` and `awaitHandlers = true` (see the class body).
|
|
40
|
+
* LangChain swallows handler throws by default. The customer catches
|
|
41
|
+
* via `try { await chain.invoke(...) } catch {}`. Verified end-to-end
|
|
42
|
+
* against @langchain/core@1.1.48 through `model.invoke`, `model.stream`
|
|
43
|
+
* and a `RunnableSequence` in
|
|
44
|
+
* `src/__tests__/blocking-enforcement.test.ts` — NOT by inspection.
|
|
45
|
+
* - Because `raiseError = true` makes every throw from this class abort
|
|
46
|
+
* the customer's run, the split is strict: the guardrail path is the
|
|
47
|
+
* ONLY thing allowed to throw. Prompt-extraction failure fails CLOSED
|
|
48
|
+
* (an unscannable prompt is rejected, never silently skipped); model-name
|
|
49
|
+
* resolution degrades to "unknown"; every logging hook is wrapped in
|
|
50
|
+
* `safeLog` so a trace-log failure can never bubble into the chain.
|
|
51
|
+
* - Options that cannot be honoured are rejected in the constructor
|
|
52
|
+
* (`EvalguardConfigError`) rather than accepted and ignored.
|
|
53
|
+
* - **OpenInference compatibility**: every trace emits a `openinference`
|
|
54
|
+
* sidecar with the standard `openinference.span.kind`, `llm.input_messages`,
|
|
55
|
+
* `llm.output_messages`, `llm.model_name`, `llm.token_count.*` attributes.
|
|
56
|
+
* This frees rendering in Phoenix / Arize / any OpenInference-aware viewer.
|
|
57
|
+
* - LangGraph: the same handler emits `chain` + `tool` + `agent` events
|
|
58
|
+
* so node-level activity in a graph shows up as nested spans, mirroring
|
|
59
|
+
* LangSmith's per-node evaluator surface.
|
|
60
|
+
*/
|
|
61
|
+
import { type GuardrailCheckResult } from "./guardrail-client.js";
|
|
62
|
+
/** Minimal LangChain message shape used in chat-model handler args. */
|
|
63
|
+
interface LangChainMessage {
|
|
64
|
+
content: string | unknown;
|
|
65
|
+
type?: string;
|
|
66
|
+
_getType?: () => string;
|
|
67
|
+
}
|
|
68
|
+
interface LangChainSerialized {
|
|
69
|
+
name?: string;
|
|
70
|
+
id?: string[];
|
|
71
|
+
kwargs?: Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
interface LangChainLLMResult {
|
|
74
|
+
generations: Array<Array<{
|
|
75
|
+
text?: string;
|
|
76
|
+
message?: {
|
|
77
|
+
content?: string;
|
|
78
|
+
};
|
|
79
|
+
generationInfo?: Record<string, unknown>;
|
|
80
|
+
}>>;
|
|
81
|
+
llmOutput?: {
|
|
82
|
+
tokenUsage?: {
|
|
83
|
+
promptTokens?: number;
|
|
84
|
+
completionTokens?: number;
|
|
85
|
+
totalTokens?: number;
|
|
86
|
+
};
|
|
87
|
+
modelName?: string;
|
|
88
|
+
model_name?: string;
|
|
89
|
+
model?: string;
|
|
90
|
+
[k: string]: unknown;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export interface EvalGuardHandlerConfig {
|
|
94
|
+
apiKey: string;
|
|
95
|
+
projectId?: string;
|
|
96
|
+
baseUrl?: string;
|
|
97
|
+
blockOnViolation?: boolean;
|
|
98
|
+
disableLogging?: boolean;
|
|
99
|
+
disableGuardrails?: boolean;
|
|
100
|
+
metadata?: Record<string, unknown>;
|
|
101
|
+
}
|
|
102
|
+
export declare class EvalguardBlockedError extends Error {
|
|
103
|
+
readonly violations: GuardrailCheckResult["violations"];
|
|
104
|
+
constructor(message: string, violations: GuardrailCheckResult["violations"]);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Thrown from the constructor when the supplied options cannot all be
|
|
108
|
+
* honoured. A security option that the handler is structurally unable to
|
|
109
|
+
* enforce must fail loudly at wiring time — silently accepting it is how
|
|
110
|
+
* a customer ends up believing they are protected when they are not.
|
|
111
|
+
*/
|
|
112
|
+
export declare class EvalguardConfigError extends Error {
|
|
113
|
+
constructor(message: string);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The handler keeps state per runId so async overlapping calls are tracked
|
|
117
|
+
* correctly. LangChain assigns each LLM/chain/tool invocation a unique runId
|
|
118
|
+
* (UUID) which is passed to every handle* method.
|
|
119
|
+
*/
|
|
120
|
+
export declare class EvalGuardCallbackHandler {
|
|
121
|
+
readonly name = "evalguard-handler";
|
|
122
|
+
readonly awaitHandlers = true;
|
|
123
|
+
readonly raiseError = true;
|
|
124
|
+
private readonly client;
|
|
125
|
+
private readonly config;
|
|
126
|
+
private readonly inflight;
|
|
127
|
+
private readonly blockOnViolation;
|
|
128
|
+
private readonly enableLogging;
|
|
129
|
+
private readonly enableGuardrails;
|
|
130
|
+
constructor(config: EvalGuardHandlerConfig);
|
|
131
|
+
/**
|
|
132
|
+
* Called when an LLM (completion-style) starts. LangChain emits both
|
|
133
|
+
* `handleLLMStart` and `handleChatModelStart` — we cover both so the
|
|
134
|
+
* same handler works across `LLM` and `BaseChatModel` subclasses.
|
|
135
|
+
*/
|
|
136
|
+
handleLLMStart(llm: LangChainSerialized, prompts: string[], runId: string, _parentRunId?: string, extraParams?: Record<string, unknown>): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Called when a chat-style LLM (e.g. ChatOpenAI, ChatAnthropic) starts.
|
|
139
|
+
* `messages` is `BaseMessage[][]` — one inner array per parallel
|
|
140
|
+
* generation. We collapse EVERY role into a single prompt for the
|
|
141
|
+
* guardrail check (A287: user-only collapse hid tool results and
|
|
142
|
+
* retrieved documents from the firewall).
|
|
143
|
+
*/
|
|
144
|
+
handleChatModelStart(llm: LangChainSerialized, messages: LangChainMessage[][], runId: string, _parentRunId?: string, extraParams?: Record<string, unknown>): Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* Called when the LLM finishes (success). Emits a trace with latency,
|
|
147
|
+
* token usage, cost, and OpenInference-compliant span attributes.
|
|
148
|
+
*/
|
|
149
|
+
handleLLMEnd(output: LangChainLLMResult, runId: string): Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* Called when the LLM call errors. We still emit a trace so customers
|
|
152
|
+
* can see failed calls in the dashboard (latency + error type + model).
|
|
153
|
+
*/
|
|
154
|
+
handleLLMError(err: unknown, runId: string): Promise<void>;
|
|
155
|
+
/**
|
|
156
|
+
* Chain start — covers LangChain Runnables + LangGraph node entry.
|
|
157
|
+
* We log these as nested spans via OpenInference `chain` kind so the
|
|
158
|
+
* trace tree mirrors the customer's actual graph topology.
|
|
159
|
+
*/
|
|
160
|
+
handleChainStart(chain: LangChainSerialized, inputs: Record<string, unknown>, runId: string): Promise<void>;
|
|
161
|
+
/**
|
|
162
|
+
* Tool execution start (LangGraph node calling a tool). Treated as its
|
|
163
|
+
* own span so trajectory-style assertions can recover the tool-call
|
|
164
|
+
* sequence from the trace.
|
|
165
|
+
*/
|
|
166
|
+
handleToolStart(tool: LangChainSerialized, input: string, runId: string): Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* Agent action (LangChain agents + LangGraph). Captures the decided
|
|
169
|
+
* action so trajectory-grading metrics can score whether the action
|
|
170
|
+
* was the right next step.
|
|
171
|
+
*/
|
|
172
|
+
handleAgentAction(action: {
|
|
173
|
+
tool: string;
|
|
174
|
+
toolInput: unknown;
|
|
175
|
+
log?: string;
|
|
176
|
+
}, runId: string): Promise<void>;
|
|
177
|
+
/** Shared startup path for both `handleLLMStart` and `handleChatModelStart`. */
|
|
178
|
+
private startRun;
|
|
179
|
+
/**
|
|
180
|
+
* Model name is trace METADATA, not a security input. A failure to
|
|
181
|
+
* resolve it must never abort the customer's run and must never skip the
|
|
182
|
+
* firewall — degrade to "unknown" and carry on to the scan.
|
|
183
|
+
*/
|
|
184
|
+
private safeResolveModel;
|
|
185
|
+
/**
|
|
186
|
+
* The prompt could not be read, so the firewall cannot see the bytes that
|
|
187
|
+
* are about to reach the model. Silently returning here (what this handler
|
|
188
|
+
* did until 2026-07-30) skipped the scan entirely and let the call through
|
|
189
|
+
* unguarded — a total bypass for any customer using a message class whose
|
|
190
|
+
* `_getType()`/content accessor throws.
|
|
191
|
+
*
|
|
192
|
+
* Fail CLOSED instead, with an explicit `prompt_unreadable` marker so the
|
|
193
|
+
* trace records that nothing was scanned. Note this is a REJECTION, not a
|
|
194
|
+
* shortened/partial scan: never hand the scanner less than the real input.
|
|
195
|
+
*/
|
|
196
|
+
private onUnreadablePrompt;
|
|
197
|
+
/**
|
|
198
|
+
* Fire-and-forget trace logging that can NEVER throw into the customer's
|
|
199
|
+
* chain. This class sets `raiseError = true` so LangChain propagates our
|
|
200
|
+
* throws — that is required for a guardrail block to actually block, but
|
|
201
|
+
* it also means an incidental failure in a logging-only hook would abort a
|
|
202
|
+
* perfectly good LLM call. The published contract (README, "Outage
|
|
203
|
+
* semantics") is that a trace-log failure never bubbles into your chain;
|
|
204
|
+
* this is where that contract is enforced.
|
|
205
|
+
*
|
|
206
|
+
* `GuardrailClient.logTrace` already resolves to null on transport failure
|
|
207
|
+
* (wrapper-core `withRetry`), so this guards the synchronous payload
|
|
208
|
+
* construction and any future rejection path.
|
|
209
|
+
*/
|
|
210
|
+
private safeLog;
|
|
211
|
+
private resolveModel;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Auto-install path for the lazy customer. Reads EVALGUARD_API_KEY from env.
|
|
215
|
+
* Returns the handler instance if installed; null if EVALGUARD_API_KEY is
|
|
216
|
+
* not set (no-op safe to call at module load).
|
|
217
|
+
*
|
|
218
|
+
* Usage:
|
|
219
|
+
*
|
|
220
|
+
* import { autoInstall } from "@evalguard/langchain";
|
|
221
|
+
* autoInstall();
|
|
222
|
+
*
|
|
223
|
+
* Then pass the returned handler to a Runnable's `callbacks` array, OR set
|
|
224
|
+
* the global LangChain callbacks via `setGlobalCallbacks([handler])` from
|
|
225
|
+
* `@langchain/core/callbacks/manager`.
|
|
226
|
+
*/
|
|
227
|
+
export declare function autoInstall(): EvalGuardCallbackHandler | null;
|
|
228
|
+
export type { GuardrailCheckResult, GuardrailViolation, TraceLogData } from "./guardrail-client.js";
|
|
229
|
+
export { estimateCost, estimateCostDetailed, isModelPriced } from "./cost.js";
|
|
230
|
+
export type { CostEstimate, PricingSource } from "./cost.js";
|
|
231
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|