-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathlangchain.test.ts
More file actions
87 lines (76 loc) · 3.57 KB
/
langchain.test.ts
File metadata and controls
87 lines (76 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import { describe, expect, test, vi } from 'vitest';
import { _INTERNAL_augmentCallbackHandlers } from '../../../src/integrations/tracing/langchain/instrumentation';
const sentryHandler = { name: 'SentryCallbackHandler' };
/**
* Minimal `CallbackManager` stand-in. We only need the duck-typed shape
* (`addHandler` + `copy`) for the production code to recognize this as a
* `CallbackManager` rather than fall through to the "unknown" branch.
*/
function makeFakeCallbackManager(existingHandlers: unknown[] = []) {
const manager = {
handlers: [...existingHandlers],
addHandler: vi.fn(function (this: any, handler: unknown, _inherit?: boolean) {
this.handlers.push(handler);
}),
copy: vi.fn(function (this: any) {
return makeFakeCallbackManager(this.handlers);
}),
};
return manager;
}
describe('augmentCallbackHandlers', () => {
test('wraps Sentry handler in an array when no callbacks are configured', () => {
const result = _INTERNAL_augmentCallbackHandlers(undefined, sentryHandler);
expect(result).toEqual([sentryHandler]);
});
test('appends Sentry handler when callbacks is already an array', () => {
const other = { name: 'OtherHandler' };
const result = _INTERNAL_augmentCallbackHandlers([other], sentryHandler);
expect(result).toEqual([other, sentryHandler]);
});
test('is idempotent when Sentry handler is already in the array', () => {
const result = _INTERNAL_augmentCallbackHandlers([sentryHandler], sentryHandler);
expect(result).toEqual([sentryHandler]);
});
test('preserves inheritable handlers when callbacks is a CallbackManager', () => {
// Reproduces the LangGraph `streamMode: ['messages']` setup: a
// CallbackManager carrying a StreamMessagesHandler is passed via
// options.callbacks. Without this fix, the manager would be wrapped as
// `[manager, sentryHandler]`, dropping all its inheritable children.
const streamMessagesHandler = {
name: 'StreamMessagesHandler',
lc_prefer_streaming: true,
};
const manager = makeFakeCallbackManager([streamMessagesHandler]);
const result = _INTERNAL_augmentCallbackHandlers(manager, sentryHandler) as {
handlers: unknown[];
};
// The result is a manager (object), not a wrapping array.
expect(Array.isArray(result)).toBe(false);
// The original child handler is still there alongside Sentry's.
expect(result.handlers).toEqual([streamMessagesHandler, sentryHandler]);
});
test('copies the manager rather than mutating the caller-supplied one', () => {
// If we mutated the original manager, repeated invocations would
// accumulate Sentry handlers (and tracers from prior runs would leak
// into subsequent unrelated runs).
const manager = makeFakeCallbackManager([]);
_INTERNAL_augmentCallbackHandlers(manager, sentryHandler);
expect(manager.copy).toHaveBeenCalledTimes(1);
expect(manager.handlers).toEqual([]);
});
test('does not double-register Sentry handler when copy already contains it', () => {
const manager = makeFakeCallbackManager([sentryHandler]);
const result = _INTERNAL_augmentCallbackHandlers(manager, sentryHandler) as {
handlers: unknown[];
addHandler: ReturnType<typeof vi.fn>;
};
expect(result.handlers).toEqual([sentryHandler]);
expect(result.addHandler).not.toHaveBeenCalled();
});
test('returns the value unchanged when it is neither an array nor a CallbackManager', () => {
const opaque = { name: 'NotAManager' };
const result = _INTERNAL_augmentCallbackHandlers(opaque, sentryHandler);
expect(result).toBe(opaque);
});
});