@traccia2/sdk 0.0.1
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 +23 -0
- package/README.md +503 -0
- package/dist/auto.d.ts +27 -0
- package/dist/auto.d.ts.map +1 -0
- package/dist/auto.js +171 -0
- package/dist/auto.js.map +1 -0
- package/dist/config/env-config.d.ts +21 -0
- package/dist/config/env-config.d.ts.map +1 -0
- package/dist/config/env-config.js +111 -0
- package/dist/config/env-config.js.map +1 -0
- package/dist/config/pricing-config.d.ts +27 -0
- package/dist/config/pricing-config.d.ts.map +1 -0
- package/dist/config/pricing-config.js +74 -0
- package/dist/config/pricing-config.js.map +1 -0
- package/dist/config/runtime-config.d.ts +65 -0
- package/dist/config/runtime-config.d.ts.map +1 -0
- package/dist/config/runtime-config.js +97 -0
- package/dist/config/runtime-config.js.map +1 -0
- package/dist/context/context.d.ts +29 -0
- package/dist/context/context.d.ts.map +1 -0
- package/dist/context/context.js +48 -0
- package/dist/context/context.js.map +1 -0
- package/dist/exporter/console-exporter.d.ts +18 -0
- package/dist/exporter/console-exporter.d.ts.map +1 -0
- package/dist/exporter/console-exporter.js +39 -0
- package/dist/exporter/console-exporter.js.map +1 -0
- package/dist/exporter/http-exporter.d.ts +57 -0
- package/dist/exporter/http-exporter.d.ts.map +1 -0
- package/dist/exporter/http-exporter.js +181 -0
- package/dist/exporter/http-exporter.js.map +1 -0
- package/dist/exporter/index.d.ts +7 -0
- package/dist/exporter/index.d.ts.map +1 -0
- package/dist/exporter/index.js +12 -0
- package/dist/exporter/index.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/index.d.ts +9 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +16 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/langchain-callback.d.ts +72 -0
- package/dist/integrations/langchain-callback.d.ts.map +1 -0
- package/dist/integrations/langchain-callback.js +201 -0
- package/dist/integrations/langchain-callback.js.map +1 -0
- package/dist/integrations/langgraph-instrumentation.d.ts +57 -0
- package/dist/integrations/langgraph-instrumentation.d.ts.map +1 -0
- package/dist/integrations/langgraph-instrumentation.js +162 -0
- package/dist/integrations/langgraph-instrumentation.js.map +1 -0
- package/dist/processor/batch-processor.d.ts +68 -0
- package/dist/processor/batch-processor.d.ts.map +1 -0
- package/dist/processor/batch-processor.js +150 -0
- package/dist/processor/batch-processor.js.map +1 -0
- package/dist/processor/cost-processor.d.ts +16 -0
- package/dist/processor/cost-processor.d.ts.map +1 -0
- package/dist/processor/cost-processor.js +50 -0
- package/dist/processor/cost-processor.js.map +1 -0
- package/dist/processor/index.d.ts +9 -0
- package/dist/processor/index.d.ts.map +1 -0
- package/dist/processor/index.js +18 -0
- package/dist/processor/index.js.map +1 -0
- package/dist/processor/logging-processor.d.ts +13 -0
- package/dist/processor/logging-processor.d.ts.map +1 -0
- package/dist/processor/logging-processor.js +26 -0
- package/dist/processor/logging-processor.js.map +1 -0
- package/dist/processor/sampler.d.ts +20 -0
- package/dist/processor/sampler.d.ts.map +1 -0
- package/dist/processor/sampler.js +33 -0
- package/dist/processor/sampler.js.map +1 -0
- package/dist/processor/token-counter.d.ts +13 -0
- package/dist/processor/token-counter.d.ts.map +1 -0
- package/dist/processor/token-counter.js +40 -0
- package/dist/processor/token-counter.js.map +1 -0
- package/dist/tracer/index.d.ts +8 -0
- package/dist/tracer/index.d.ts.map +1 -0
- package/dist/tracer/index.js +15 -0
- package/dist/tracer/index.js.map +1 -0
- package/dist/tracer/provider.d.ts +59 -0
- package/dist/tracer/provider.d.ts.map +1 -0
- package/dist/tracer/provider.js +114 -0
- package/dist/tracer/provider.js.map +1 -0
- package/dist/tracer/span-context.d.ts +23 -0
- package/dist/tracer/span-context.d.ts.map +1 -0
- package/dist/tracer/span-context.js +34 -0
- package/dist/tracer/span-context.js.map +1 -0
- package/dist/tracer/span.d.ts +49 -0
- package/dist/tracer/span.d.ts.map +1 -0
- package/dist/tracer/span.js +118 -0
- package/dist/tracer/span.js.map +1 -0
- package/dist/tracer/tracer.d.ts +28 -0
- package/dist/tracer/tracer.d.ts.map +1 -0
- package/dist/tracer/tracer.js +75 -0
- package/dist/tracer/tracer.js.map +1 -0
- package/dist/types.d.ts +135 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/package.json +79 -0
- package/src/__tests__/exporter.test.ts +62 -0
- package/src/__tests__/integrations-langchain.test.ts +384 -0
- package/src/__tests__/integrations-langgraph.test.ts +479 -0
- package/src/__tests__/processor.test.ts +89 -0
- package/src/__tests__/span.test.ts +103 -0
- package/src/__tests__/tracer.test.ts +89 -0
- package/src/auto.ts +198 -0
- package/src/config/env-config.ts +93 -0
- package/src/config/pricing-config.ts +84 -0
- package/src/config/runtime-config.ts +108 -0
- package/src/context/context.ts +52 -0
- package/src/exporter/console-exporter.ts +38 -0
- package/src/exporter/http-exporter.ts +188 -0
- package/src/exporter/index.ts +7 -0
- package/src/index.ts +51 -0
- package/src/integrations/README.md +287 -0
- package/src/integrations/index.ts +13 -0
- package/src/integrations/langchain-callback.ts +229 -0
- package/src/integrations/langgraph-instrumentation.ts +174 -0
- package/src/processor/batch-processor.ts +180 -0
- package/src/processor/cost-processor.ts +57 -0
- package/src/processor/index.ts +9 -0
- package/src/processor/logging-processor.ts +26 -0
- package/src/processor/sampler.ts +35 -0
- package/src/processor/token-counter.ts +42 -0
- package/src/tracer/index.ts +8 -0
- package/src/tracer/provider.ts +130 -0
- package/src/tracer/span-context.ts +46 -0
- package/src/tracer/span.ts +145 -0
- package/src/tracer/tracer.ts +100 -0
- package/src/types.ts +155 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for LangChain integration (TraciaCallbackHandler)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { TraciaCallbackHandler } from '../integrations/langchain-callback';
|
|
6
|
+
import { startTracing, stopTracing } from '../auto';
|
|
7
|
+
|
|
8
|
+
describe('TraciaCallbackHandler', () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
startTracing({
|
|
11
|
+
enableTokenCounting: false,
|
|
12
|
+
enableCostTracking: false,
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
stopTracing();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('handleLLMStart', () => {
|
|
21
|
+
it('should create a span for LLM invocation', async () => {
|
|
22
|
+
const handler = new TraciaCallbackHandler();
|
|
23
|
+
const mockLLM = {
|
|
24
|
+
name: 'gpt-4',
|
|
25
|
+
_modelType: 'openai',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
await handler.handleLLMStart(mockLLM, ['prompt1', 'prompt2'], 'run-1');
|
|
29
|
+
|
|
30
|
+
// Verify span was created in the handler's span map
|
|
31
|
+
expect(handler['spanStack'].has('run-1')).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should capture LLM model name in attributes', async () => {
|
|
35
|
+
const handler = new TraciaCallbackHandler();
|
|
36
|
+
const mockLLM = {
|
|
37
|
+
name: 'claude-3',
|
|
38
|
+
_modelType: 'anthropic',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
await handler.handleLLMStart(mockLLM, ['test prompt'], 'run-2');
|
|
42
|
+
|
|
43
|
+
const span = handler['spanStack'].get('run-2');
|
|
44
|
+
expect(span?.attributes?.model).toMatch(/claude-3|anthropic/);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should capture prompt count', async () => {
|
|
48
|
+
const handler = new TraciaCallbackHandler();
|
|
49
|
+
const mockLLM = { name: 'test-model' };
|
|
50
|
+
const prompts = ['p1', 'p2', 'p3'];
|
|
51
|
+
|
|
52
|
+
await handler.handleLLMStart(mockLLM, prompts, 'run-3');
|
|
53
|
+
|
|
54
|
+
const span = handler['spanStack'].get('run-3');
|
|
55
|
+
expect(span?.attributes?.prompt_count).toBe(3);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should handle missing model name gracefully', async () => {
|
|
59
|
+
const handler = new TraciaCallbackHandler();
|
|
60
|
+
const mockLLM = {}; // No name or _modelType
|
|
61
|
+
|
|
62
|
+
await handler.handleLLMStart(mockLLM, ['prompt'], 'run-4');
|
|
63
|
+
|
|
64
|
+
const span = handler['spanStack'].get('run-4');
|
|
65
|
+
expect(span?.attributes?.model).toBe('unknown');
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('handleLLMEnd', () => {
|
|
70
|
+
it('should end LLM span and record token counts', async () => {
|
|
71
|
+
const handler = new TraciaCallbackHandler();
|
|
72
|
+
const mockLLM = { name: 'gpt-4' };
|
|
73
|
+
|
|
74
|
+
// Start span
|
|
75
|
+
await handler.handleLLMStart(mockLLM, ['prompt'], 'run-5');
|
|
76
|
+
|
|
77
|
+
// End span
|
|
78
|
+
const mockOutput = {
|
|
79
|
+
generations: [[{ text: 'response', generation_info: { usage: { prompt_tokens: 10, completion_tokens: 5 } } }]],
|
|
80
|
+
};
|
|
81
|
+
await handler.handleLLMEnd(mockOutput, 'run-5');
|
|
82
|
+
|
|
83
|
+
// Verify span was removed after ending
|
|
84
|
+
expect(handler['spanStack'].has('run-5')).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should record output length', async () => {
|
|
88
|
+
const handler = new TraciaCallbackHandler();
|
|
89
|
+
const mockLLM = { name: 'test' };
|
|
90
|
+
|
|
91
|
+
await handler.handleLLMStart(mockLLM, ['prompt'], 'run-6');
|
|
92
|
+
|
|
93
|
+
const output = {
|
|
94
|
+
generations: [[{ text: 'This is a test response that has some length to it' }]],
|
|
95
|
+
};
|
|
96
|
+
await handler.handleLLMEnd(output, 'run-6');
|
|
97
|
+
|
|
98
|
+
// Span should be ended
|
|
99
|
+
expect(handler['spanStack'].has('run-6')).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('handleChainStart', () => {
|
|
104
|
+
it('should create a span for chain execution', async () => {
|
|
105
|
+
const handler = new TraciaCallbackHandler();
|
|
106
|
+
const mockChain = {
|
|
107
|
+
name: 'test-chain',
|
|
108
|
+
_chainType: 'stuff',
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
await handler.handleChainStart(mockChain, { input: 'test' }, 'chain-1');
|
|
112
|
+
|
|
113
|
+
expect(handler['spanStack'].has('chain-1')).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should capture chain name and type', async () => {
|
|
117
|
+
const handler = new TraciaCallbackHandler();
|
|
118
|
+
const mockChain = {
|
|
119
|
+
name: 'qa-chain',
|
|
120
|
+
_chainType: 'retrieval_qa',
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
await handler.handleChainStart(mockChain, { query: 'test' }, 'chain-2');
|
|
124
|
+
|
|
125
|
+
const span = handler['spanStack'].get('chain-2');
|
|
126
|
+
expect(span?.attributes?.chain_name).toBe('qa-chain');
|
|
127
|
+
expect(span?.attributes?.chain_type).toBe('retrieval_qa');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should record input keys', async () => {
|
|
131
|
+
const handler = new TraciaCallbackHandler();
|
|
132
|
+
const mockChain = { name: 'chain' };
|
|
133
|
+
const inputs = { key1: 'value1', key2: 'value2' };
|
|
134
|
+
|
|
135
|
+
await handler.handleChainStart(mockChain, inputs, 'chain-3');
|
|
136
|
+
|
|
137
|
+
const span = handler['spanStack'].get('chain-3');
|
|
138
|
+
expect(span?.attributes?.input_keys).toContain('key1');
|
|
139
|
+
expect(span?.attributes?.input_keys).toContain('key2');
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('handleChainEnd', () => {
|
|
144
|
+
it('should end chain span and record output', async () => {
|
|
145
|
+
const handler = new TraciaCallbackHandler();
|
|
146
|
+
const mockChain = { name: 'chain' };
|
|
147
|
+
|
|
148
|
+
await handler.handleChainStart(mockChain, { input: 'test' }, 'chain-4');
|
|
149
|
+
|
|
150
|
+
const output = { output: 'result' };
|
|
151
|
+
await handler.handleChainEnd(output, 'chain-4');
|
|
152
|
+
|
|
153
|
+
expect(handler['spanStack'].has('chain-4')).toBe(false);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('handleToolStart', () => {
|
|
158
|
+
it('should create a span for tool invocation', async () => {
|
|
159
|
+
const handler = new TraciaCallbackHandler();
|
|
160
|
+
const mockTool = {
|
|
161
|
+
name: 'search-api',
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
await handler.handleToolStart(mockTool, 'search query', 'tool-1');
|
|
165
|
+
|
|
166
|
+
expect(handler['spanStack'].has('tool-1')).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should capture tool name', async () => {
|
|
170
|
+
const handler = new TraciaCallbackHandler();
|
|
171
|
+
const mockTool = { name: 'calculator' };
|
|
172
|
+
|
|
173
|
+
await handler.handleToolStart(mockTool, '2+2', 'tool-2');
|
|
174
|
+
|
|
175
|
+
const span = handler['spanStack'].get('tool-2');
|
|
176
|
+
expect(span?.attributes?.tool_name).toBe('calculator');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should record input length', async () => {
|
|
180
|
+
const handler = new TraciaCallbackHandler();
|
|
181
|
+
const mockTool = { name: 'tool' };
|
|
182
|
+
const input = 'test input with some length';
|
|
183
|
+
|
|
184
|
+
await handler.handleToolStart(mockTool, input, 'tool-3');
|
|
185
|
+
|
|
186
|
+
const span = handler['spanStack'].get('tool-3');
|
|
187
|
+
expect(span?.attributes?.input_length).toBeGreaterThan(0);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe('handleToolEnd', () => {
|
|
192
|
+
it('should end tool span', async () => {
|
|
193
|
+
const handler = new TraciaCallbackHandler();
|
|
194
|
+
const mockTool = { name: 'tool' };
|
|
195
|
+
|
|
196
|
+
await handler.handleToolStart(mockTool, 'input', 'tool-4');
|
|
197
|
+
await handler.handleToolEnd('output', 'tool-4');
|
|
198
|
+
|
|
199
|
+
expect(handler['spanStack'].has('tool-4')).toBe(false);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should record output length', async () => {
|
|
203
|
+
const handler = new TraciaCallbackHandler();
|
|
204
|
+
const mockTool = { name: 'tool' };
|
|
205
|
+
|
|
206
|
+
await handler.handleToolStart(mockTool, 'input', 'tool-5');
|
|
207
|
+
const output = 'This is the tool output response';
|
|
208
|
+
await handler.handleToolEnd(output, 'tool-5');
|
|
209
|
+
|
|
210
|
+
expect(handler['spanStack'].has('tool-5')).toBe(false);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('handleAgentAction', () => {
|
|
215
|
+
it('should update existing span on agent action', async () => {
|
|
216
|
+
const handler = new TraciaCallbackHandler();
|
|
217
|
+
const mockChain = { name: 'chain' };
|
|
218
|
+
|
|
219
|
+
// Start a span first
|
|
220
|
+
await handler.handleChainStart(mockChain, {}, 'agent-1');
|
|
221
|
+
|
|
222
|
+
// Then handle agent action
|
|
223
|
+
const mockAction = {
|
|
224
|
+
tool: 'search',
|
|
225
|
+
toolInput: 'query',
|
|
226
|
+
};
|
|
227
|
+
await handler.handleAgentAction(mockAction, 'agent-1');
|
|
228
|
+
|
|
229
|
+
// Span should still exist
|
|
230
|
+
expect(handler['spanStack'].has('agent-1')).toBe(true);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should record agent action details', async () => {
|
|
234
|
+
const handler = new TraciaCallbackHandler();
|
|
235
|
+
const mockChain = { name: 'chain' };
|
|
236
|
+
|
|
237
|
+
// Start a span first
|
|
238
|
+
await handler.handleChainStart(mockChain, {}, 'agent-2');
|
|
239
|
+
|
|
240
|
+
// Then handle agent action
|
|
241
|
+
const mockAction = {
|
|
242
|
+
tool: 'calculator',
|
|
243
|
+
toolInput: '42 / 7',
|
|
244
|
+
};
|
|
245
|
+
await handler.handleAgentAction(mockAction, 'agent-2');
|
|
246
|
+
|
|
247
|
+
// Span should have agent action recorded
|
|
248
|
+
const span = handler['spanStack'].get('agent-2');
|
|
249
|
+
expect(span).toBeDefined();
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
describe('handleAgentFinish', () => {
|
|
254
|
+
it('should end agent span on finish', async () => {
|
|
255
|
+
const handler = new TraciaCallbackHandler();
|
|
256
|
+
|
|
257
|
+
// Create a span first
|
|
258
|
+
const mockAction = { tool: 'test', toolInput: 'input' };
|
|
259
|
+
await handler.handleAgentAction(mockAction, 'agent-3');
|
|
260
|
+
|
|
261
|
+
// Finish agent
|
|
262
|
+
const finish = { output: 'final result' };
|
|
263
|
+
await handler.handleAgentFinish(finish, 'agent-3');
|
|
264
|
+
|
|
265
|
+
expect(handler['spanStack'].has('agent-3')).toBe(false);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should record final output', async () => {
|
|
269
|
+
const handler = new TraciaCallbackHandler();
|
|
270
|
+
const mockAction = { tool: 'test', toolInput: 'input' };
|
|
271
|
+
|
|
272
|
+
await handler.handleAgentAction(mockAction, 'agent-4');
|
|
273
|
+
|
|
274
|
+
const finish = { output: 'completed result' };
|
|
275
|
+
await handler.handleAgentFinish(finish, 'agent-4');
|
|
276
|
+
|
|
277
|
+
expect(handler['spanStack'].has('agent-4')).toBe(false);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
describe('Error Handling', () => {
|
|
282
|
+
it('should handle LLM errors gracefully', async () => {
|
|
283
|
+
const handler = new TraciaCallbackHandler();
|
|
284
|
+
const mockLLM = { name: 'llm' };
|
|
285
|
+
|
|
286
|
+
await handler.handleLLMStart(mockLLM, ['prompt'], 'error-1');
|
|
287
|
+
|
|
288
|
+
const error = new Error('LLM failed');
|
|
289
|
+
await handler.handleLLMError?.(error, 'error-1');
|
|
290
|
+
|
|
291
|
+
// Span should still be cleaned up
|
|
292
|
+
expect(handler['spanStack'].has('error-1')).toBe(false);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('should handle chain errors gracefully', async () => {
|
|
296
|
+
const handler = new TraciaCallbackHandler();
|
|
297
|
+
const mockChain = { name: 'chain' };
|
|
298
|
+
|
|
299
|
+
await handler.handleChainStart(mockChain, {}, 'error-2');
|
|
300
|
+
|
|
301
|
+
const error = new Error('Chain failed');
|
|
302
|
+
await handler.handleChainError?.(error, 'error-2');
|
|
303
|
+
|
|
304
|
+
expect(handler['spanStack'].has('error-2')).toBe(false);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should handle tool errors gracefully', async () => {
|
|
308
|
+
const handler = new TraciaCallbackHandler();
|
|
309
|
+
const mockTool = { name: 'tool' };
|
|
310
|
+
|
|
311
|
+
await handler.handleToolStart(mockTool, 'input', 'error-3');
|
|
312
|
+
|
|
313
|
+
const error = new Error('Tool failed');
|
|
314
|
+
await handler.handleToolError?.(error, 'error-3');
|
|
315
|
+
|
|
316
|
+
expect(handler['spanStack'].has('error-3')).toBe(false);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe('Span Nesting', () => {
|
|
321
|
+
it('should handle nested spans (chain containing LLM)', async () => {
|
|
322
|
+
const handler = new TraciaCallbackHandler();
|
|
323
|
+
|
|
324
|
+
// Start chain
|
|
325
|
+
const chain = { name: 'chain' };
|
|
326
|
+
await handler.handleChainStart(chain, {}, 'chain-outer');
|
|
327
|
+
|
|
328
|
+
// Start LLM inside chain
|
|
329
|
+
const llm = { name: 'llm' };
|
|
330
|
+
await handler.handleLLMStart(llm, ['prompt'], 'llm-inner');
|
|
331
|
+
|
|
332
|
+
// Both should exist
|
|
333
|
+
expect(handler['spanStack'].has('chain-outer')).toBe(true);
|
|
334
|
+
expect(handler['spanStack'].has('llm-inner')).toBe(true);
|
|
335
|
+
|
|
336
|
+
// End LLM first
|
|
337
|
+
await handler.handleLLMEnd({ generations: [[{ text: 'response' }]] }, 'llm-inner');
|
|
338
|
+
expect(handler['spanStack'].has('llm-inner')).toBe(false);
|
|
339
|
+
|
|
340
|
+
// Chain should still exist
|
|
341
|
+
expect(handler['spanStack'].has('chain-outer')).toBe(true);
|
|
342
|
+
|
|
343
|
+
// End chain
|
|
344
|
+
await handler.handleChainEnd({ output: 'result' }, 'chain-outer');
|
|
345
|
+
expect(handler['spanStack'].has('chain-outer')).toBe(false);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should handle multiple concurrent spans', async () => {
|
|
349
|
+
const handler = new TraciaCallbackHandler();
|
|
350
|
+
|
|
351
|
+
// Start multiple spans
|
|
352
|
+
const chain1 = { name: 'chain1' };
|
|
353
|
+
const chain2 = { name: 'chain2' };
|
|
354
|
+
|
|
355
|
+
await handler.handleChainStart(chain1, {}, 'c1');
|
|
356
|
+
await handler.handleChainStart(chain2, {}, 'c2');
|
|
357
|
+
|
|
358
|
+
expect(handler['spanStack'].has('c1')).toBe(true);
|
|
359
|
+
expect(handler['spanStack'].has('c2')).toBe(true);
|
|
360
|
+
|
|
361
|
+
// End one
|
|
362
|
+
await handler.handleChainEnd({ output: 'r1' }, 'c1');
|
|
363
|
+
|
|
364
|
+
// Other should still exist
|
|
365
|
+
expect(handler['spanStack'].has('c1')).toBe(false);
|
|
366
|
+
expect(handler['spanStack'].has('c2')).toBe(true);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
describe('Integration with Tracer', () => {
|
|
371
|
+
it('should use the SDK tracer', () => {
|
|
372
|
+
const handler = new TraciaCallbackHandler();
|
|
373
|
+
expect(handler['tracer']).toBeDefined();
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('should handle unavailable tracer gracefully', async () => {
|
|
377
|
+
const handler = new TraciaCallbackHandler();
|
|
378
|
+
|
|
379
|
+
// Should not throw even if operations fail
|
|
380
|
+
const mockLLM = { name: 'llm' };
|
|
381
|
+
await expect(handler.handleLLMStart(mockLLM, ['prompt'], 'test')).resolves.not.toThrow();
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
});
|