@struktur/telemetry 2.1.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/package.json +36 -0
- package/src/adapters/langfuse/LangfuseAdapter.ts +319 -0
- package/src/adapters/langfuse/index.ts +6 -0
- package/src/adapters/phoenix/PhoenixAdapter.ts +338 -0
- package/src/adapters/phoenix/index.ts +6 -0
- package/src/factory.ts +133 -0
- package/src/index.ts +55 -0
- package/src/types.ts +453 -0
- package/tests/adapters/langfuse.test.ts +118 -0
- package/tests/adapters/phoenix.test.ts +132 -0
- package/tests/factory.test.ts +93 -0
- package/tests/types.test.ts +248 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for LangfuseAdapter
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { test, expect, describe } from "bun:test";
|
|
6
|
+
import {
|
|
7
|
+
LangfuseAdapter,
|
|
8
|
+
createLangfuseAdapter,
|
|
9
|
+
} from "../../src/adapters/langfuse/index.js";
|
|
10
|
+
import type {
|
|
11
|
+
SpanContext,
|
|
12
|
+
LangfuseConfig,
|
|
13
|
+
} from "../../src/types.js";
|
|
14
|
+
|
|
15
|
+
describe("LangfuseAdapter", () => {
|
|
16
|
+
test("should have correct name and version", () => {
|
|
17
|
+
const adapter = new LangfuseAdapter({
|
|
18
|
+
publicKey: "pk-test",
|
|
19
|
+
secretKey: "sk-test",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
expect(adapter.name).toBe("langfuse");
|
|
23
|
+
expect(adapter.version).toBe("1.0.0");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("should apply default baseUrl", () => {
|
|
27
|
+
const adapter = new LangfuseAdapter({
|
|
28
|
+
publicKey: "pk-test",
|
|
29
|
+
secretKey: "sk-test",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(adapter).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("createLangfuseAdapter should create adapter", () => {
|
|
36
|
+
const config: LangfuseConfig = {
|
|
37
|
+
publicKey: "pk-test",
|
|
38
|
+
secretKey: "sk-test",
|
|
39
|
+
baseUrl: "https://cloud.langfuse.com",
|
|
40
|
+
projectName: "my-project",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const adapter = createLangfuseAdapter(config);
|
|
44
|
+
|
|
45
|
+
expect(adapter).toBeDefined();
|
|
46
|
+
expect(adapter.name).toBe("langfuse");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("should throw when startSpan called before initialize", () => {
|
|
50
|
+
const adapter = new LangfuseAdapter({
|
|
51
|
+
publicKey: "pk-test",
|
|
52
|
+
secretKey: "sk-test",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const context: SpanContext = {
|
|
56
|
+
name: "test-span",
|
|
57
|
+
kind: "CHAIN",
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
expect(() => adapter.startSpan(context)).toThrow("not initialized");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("should accept all span kinds", () => {
|
|
64
|
+
const adapter = new LangfuseAdapter({
|
|
65
|
+
publicKey: "pk-test",
|
|
66
|
+
secretKey: "sk-test",
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const kinds = ["CHAIN", "LLM", "TOOL", "AGENT", "RETRIEVER"] as const;
|
|
70
|
+
|
|
71
|
+
for (const _kind of kinds) {
|
|
72
|
+
expect(adapter).toBeDefined();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("LangfuseAdapter config", () => {
|
|
78
|
+
test("should require public and secret keys", () => {
|
|
79
|
+
const config: LangfuseConfig = {
|
|
80
|
+
publicKey: "pk-lf-123",
|
|
81
|
+
secretKey: "sk-lf-456",
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const adapter = createLangfuseAdapter(config);
|
|
85
|
+
|
|
86
|
+
expect(adapter).toBeDefined();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("should accept all config options", () => {
|
|
90
|
+
const config: LangfuseConfig = {
|
|
91
|
+
publicKey: "pk-test",
|
|
92
|
+
secretKey: "sk-test",
|
|
93
|
+
baseUrl: "https://us.cloud.langfuse.com",
|
|
94
|
+
projectName: "test-project",
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const adapter = createLangfuseAdapter(config);
|
|
98
|
+
|
|
99
|
+
expect(adapter).toBeDefined();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("LangfuseAdapter interface", () => {
|
|
104
|
+
test("should expose required methods", () => {
|
|
105
|
+
const adapter = new LangfuseAdapter({
|
|
106
|
+
publicKey: "pk-test",
|
|
107
|
+
secretKey: "sk-test",
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(typeof adapter.initialize).toBe("function");
|
|
111
|
+
expect(typeof adapter.shutdown).toBe("function");
|
|
112
|
+
expect(typeof adapter.startSpan).toBe("function");
|
|
113
|
+
expect(typeof adapter.endSpan).toBe("function");
|
|
114
|
+
expect(typeof adapter.recordEvent).toBe("function");
|
|
115
|
+
expect(typeof adapter.setAttributes).toBe("function");
|
|
116
|
+
expect(typeof adapter.setContext).toBe("function");
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for PhoenixAdapter
|
|
3
|
+
*
|
|
4
|
+
* These tests verify that the PhoenixAdapter correctly implements
|
|
5
|
+
* the TelemetryAdapter interface. Since we don't have the actual
|
|
6
|
+
* dependencies installed, these tests focus on the adapter's logic.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { test, expect, describe } from "bun:test";
|
|
10
|
+
import {
|
|
11
|
+
PhoenixAdapter,
|
|
12
|
+
createPhoenixAdapter,
|
|
13
|
+
} from "../../src/adapters/phoenix/index.js";
|
|
14
|
+
import type {
|
|
15
|
+
SpanContext,
|
|
16
|
+
SpanResult,
|
|
17
|
+
LLMCallEvent,
|
|
18
|
+
ValidationEvent,
|
|
19
|
+
ChunkEvent,
|
|
20
|
+
ToolCallEvent,
|
|
21
|
+
MergeEvent,
|
|
22
|
+
ParseEvent,
|
|
23
|
+
PhoenixConfig,
|
|
24
|
+
} from "../../src/types.js";
|
|
25
|
+
|
|
26
|
+
describe("PhoenixAdapter", () => {
|
|
27
|
+
test("should have correct name and version", () => {
|
|
28
|
+
const adapter = new PhoenixAdapter({ projectName: "test" });
|
|
29
|
+
|
|
30
|
+
expect(adapter.name).toBe("phoenix");
|
|
31
|
+
expect(adapter.version).toBe("1.0.0");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("should apply default config values", () => {
|
|
35
|
+
const adapter = new PhoenixAdapter({ projectName: "test" });
|
|
36
|
+
|
|
37
|
+
// Default values should be set
|
|
38
|
+
expect(adapter).toBeDefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("createPhoenixAdapter should create adapter", () => {
|
|
42
|
+
const config: PhoenixConfig = {
|
|
43
|
+
projectName: "my-project",
|
|
44
|
+
url: "http://localhost:6006",
|
|
45
|
+
apiKey: "test-key",
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const adapter = createPhoenixAdapter(config);
|
|
49
|
+
|
|
50
|
+
expect(adapter).toBeDefined();
|
|
51
|
+
expect(adapter.name).toBe("phoenix");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("should throw when startSpan called before initialize", () => {
|
|
55
|
+
const adapter = new PhoenixAdapter({ projectName: "test" });
|
|
56
|
+
|
|
57
|
+
const context: SpanContext = {
|
|
58
|
+
name: "test-span",
|
|
59
|
+
kind: "CHAIN",
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Should throw because OTel API isn't loaded yet
|
|
63
|
+
expect(() => adapter.startSpan(context)).toThrow("not initialized");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("should accept all span kinds", () => {
|
|
67
|
+
const adapter = new PhoenixAdapter({ projectName: "test" });
|
|
68
|
+
|
|
69
|
+
// Verify adapter is created for all span kinds
|
|
70
|
+
const kinds = ["CHAIN", "LLM", "TOOL", "AGENT", "RETRIEVER", "EMBEDDING", "RERANKER"] as const;
|
|
71
|
+
|
|
72
|
+
for (const kind of kinds) {
|
|
73
|
+
expect(adapter).toBeDefined();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("PhoenixAdapter config", () => {
|
|
79
|
+
test("should merge config with defaults", () => {
|
|
80
|
+
const config: PhoenixConfig = {
|
|
81
|
+
projectName: "test-project",
|
|
82
|
+
url: "https://app.phoenix.arize.com",
|
|
83
|
+
apiKey: "phx-api-key",
|
|
84
|
+
batch: false,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const adapter = createPhoenixAdapter(config);
|
|
88
|
+
|
|
89
|
+
expect(adapter).toBeDefined();
|
|
90
|
+
expect(adapter.name).toBe("phoenix");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("should work with minimal config", () => {
|
|
94
|
+
const config: PhoenixConfig = {
|
|
95
|
+
projectName: "minimal",
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const adapter = createPhoenixAdapter(config);
|
|
99
|
+
|
|
100
|
+
expect(adapter).toBeDefined();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("should handle all optional config fields", () => {
|
|
104
|
+
const config: PhoenixConfig = {
|
|
105
|
+
projectName: "full-config",
|
|
106
|
+
url: "http://localhost:6006",
|
|
107
|
+
apiKey: "secret-key",
|
|
108
|
+
batch: true,
|
|
109
|
+
headers: {
|
|
110
|
+
"X-Custom-Header": "value",
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const adapter = createPhoenixAdapter(config);
|
|
115
|
+
|
|
116
|
+
expect(adapter).toBeDefined();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("PhoenixAdapter interface", () => {
|
|
121
|
+
test("should expose required methods", () => {
|
|
122
|
+
const adapter = new PhoenixAdapter({ projectName: "test" });
|
|
123
|
+
|
|
124
|
+
expect(typeof adapter.initialize).toBe("function");
|
|
125
|
+
expect(typeof adapter.shutdown).toBe("function");
|
|
126
|
+
expect(typeof adapter.startSpan).toBe("function");
|
|
127
|
+
expect(typeof adapter.endSpan).toBe("function");
|
|
128
|
+
expect(typeof adapter.recordEvent).toBe("function");
|
|
129
|
+
expect(typeof adapter.setAttributes).toBe("function");
|
|
130
|
+
expect(typeof adapter.setContext).toBe("function");
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for telemetry factory functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { test, expect, describe } from "bun:test";
|
|
6
|
+
import {
|
|
7
|
+
createTelemetry,
|
|
8
|
+
createPhoenixTelemetry,
|
|
9
|
+
createLangfuseTelemetry,
|
|
10
|
+
createNoopTelemetry
|
|
11
|
+
} from "../src/factory.js";
|
|
12
|
+
import type { TelemetryOptions, PhoenixConfig, LangfuseConfig } from "../src/types.js";
|
|
13
|
+
|
|
14
|
+
describe("createTelemetry", () => {
|
|
15
|
+
test("should return null when disabled", async () => {
|
|
16
|
+
const options: TelemetryOptions = {
|
|
17
|
+
provider: "phoenix",
|
|
18
|
+
config: { projectName: "test" },
|
|
19
|
+
enabled: false,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const telemetry = await createTelemetry(options);
|
|
23
|
+
expect(telemetry).toBeNull();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("should throw error for unknown provider", async () => {
|
|
27
|
+
const options: TelemetryOptions = {
|
|
28
|
+
provider: "unknown" as any,
|
|
29
|
+
config: {},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
await expect(createTelemetry(options)).rejects.toThrow("Unknown telemetry provider");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("createNoopTelemetry", () => {
|
|
37
|
+
test("should create noop adapter", () => {
|
|
38
|
+
const adapter = createNoopTelemetry();
|
|
39
|
+
|
|
40
|
+
expect(adapter).toBeDefined();
|
|
41
|
+
expect(adapter.name).toBe("noop");
|
|
42
|
+
expect(adapter.version).toBe("1.0.0");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("should work without initialization", async () => {
|
|
46
|
+
const adapter = createNoopTelemetry();
|
|
47
|
+
|
|
48
|
+
// Should not require initialize
|
|
49
|
+
const span = adapter.startSpan({
|
|
50
|
+
name: "test",
|
|
51
|
+
kind: "CHAIN",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(span).toBeDefined();
|
|
55
|
+
expect(span.name).toBe("test");
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("createPhoenixTelemetry", () => {
|
|
60
|
+
test("should create Phoenix adapter when dependencies available", async () => {
|
|
61
|
+
const config: PhoenixConfig = {
|
|
62
|
+
projectName: "test",
|
|
63
|
+
url: "http://localhost:6006",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const adapter = await createPhoenixTelemetry(config);
|
|
68
|
+
expect(adapter).toBeDefined();
|
|
69
|
+
expect(adapter.name).toBe("phoenix");
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// Expected if dependencies not installed
|
|
72
|
+
expect(error).toBeInstanceOf(Error);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("createLangfuseTelemetry", () => {
|
|
78
|
+
test("should create Langfuse adapter when dependencies available", async () => {
|
|
79
|
+
const config: LangfuseConfig = {
|
|
80
|
+
publicKey: "pk-test",
|
|
81
|
+
secretKey: "sk-test",
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const adapter = await createLangfuseTelemetry(config);
|
|
86
|
+
expect(adapter).toBeDefined();
|
|
87
|
+
expect(adapter.name).toBe("langfuse");
|
|
88
|
+
} catch (error) {
|
|
89
|
+
// Expected if dependencies not installed
|
|
90
|
+
expect(error).toBeInstanceOf(Error);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for core telemetry types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { test, expect, describe } from "bun:test";
|
|
6
|
+
import {
|
|
7
|
+
NoopTelemetryAdapter,
|
|
8
|
+
type SpanContext,
|
|
9
|
+
type SpanResult,
|
|
10
|
+
type LLMCallEvent,
|
|
11
|
+
type ValidationEvent,
|
|
12
|
+
type ChunkEvent,
|
|
13
|
+
type ToolCallEvent,
|
|
14
|
+
type MergeEvent,
|
|
15
|
+
type ParseEvent,
|
|
16
|
+
} from "../src/types.js";
|
|
17
|
+
|
|
18
|
+
describe("NoopTelemetryAdapter", () => {
|
|
19
|
+
test("should create noop adapter", () => {
|
|
20
|
+
const adapter = new NoopTelemetryAdapter();
|
|
21
|
+
|
|
22
|
+
expect(adapter.name).toBe("noop");
|
|
23
|
+
expect(adapter.version).toBe("1.0.0");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("should initialize without error", async () => {
|
|
27
|
+
const adapter = new NoopTelemetryAdapter();
|
|
28
|
+
await expect(adapter.initialize()).resolves.toBeUndefined();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("should shutdown without error", async () => {
|
|
32
|
+
const adapter = new NoopTelemetryAdapter();
|
|
33
|
+
await expect(adapter.shutdown()).resolves.toBeUndefined();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("should create spans", () => {
|
|
37
|
+
const adapter = new NoopTelemetryAdapter();
|
|
38
|
+
|
|
39
|
+
const context: SpanContext = {
|
|
40
|
+
name: "test-span",
|
|
41
|
+
kind: "CHAIN",
|
|
42
|
+
attributes: { test: true },
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const span = adapter.startSpan(context);
|
|
46
|
+
|
|
47
|
+
expect(span.id).toBeDefined();
|
|
48
|
+
expect(span.name).toBe("test-span");
|
|
49
|
+
expect(span.kind).toBe("CHAIN");
|
|
50
|
+
expect(span.traceId).toBeDefined();
|
|
51
|
+
expect(span.startTime).toBeGreaterThan(0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("should create child spans", () => {
|
|
55
|
+
const adapter = new NoopTelemetryAdapter();
|
|
56
|
+
|
|
57
|
+
const parentContext: SpanContext = {
|
|
58
|
+
name: "parent",
|
|
59
|
+
kind: "CHAIN",
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const parentSpan = adapter.startSpan(parentContext);
|
|
63
|
+
|
|
64
|
+
const childContext: SpanContext = {
|
|
65
|
+
name: "child",
|
|
66
|
+
kind: "LLM",
|
|
67
|
+
parentSpan,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const childSpan = adapter.startSpan(childContext);
|
|
71
|
+
|
|
72
|
+
expect(childSpan.parentId).toBe(parentSpan.id);
|
|
73
|
+
// Noop adapter creates new traceIds for each span
|
|
74
|
+
expect(childSpan).toBeDefined();
|
|
75
|
+
expect(parentSpan).toBeDefined();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("should end spans without error", () => {
|
|
79
|
+
const adapter = new NoopTelemetryAdapter();
|
|
80
|
+
|
|
81
|
+
const span = adapter.startSpan({
|
|
82
|
+
name: "test",
|
|
83
|
+
kind: "CHAIN",
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const result: SpanResult = {
|
|
87
|
+
status: "ok",
|
|
88
|
+
output: { data: "test" },
|
|
89
|
+
latencyMs: 100,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
expect(() => adapter.endSpan(span, result)).not.toThrow();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("should record events without error", () => {
|
|
96
|
+
const adapter = new NoopTelemetryAdapter();
|
|
97
|
+
|
|
98
|
+
const span = adapter.startSpan({
|
|
99
|
+
name: "test",
|
|
100
|
+
kind: "LLM",
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const event: LLMCallEvent = {
|
|
104
|
+
type: "llm_call",
|
|
105
|
+
model: "gpt-4o",
|
|
106
|
+
provider: "openai",
|
|
107
|
+
input: {
|
|
108
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
109
|
+
temperature: 0.5,
|
|
110
|
+
maxTokens: 100,
|
|
111
|
+
},
|
|
112
|
+
output: {
|
|
113
|
+
content: "Hi there!",
|
|
114
|
+
usage: { input: 10, output: 5, total: 15 },
|
|
115
|
+
},
|
|
116
|
+
latencyMs: 250,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
expect(() => adapter.recordEvent(span, event)).not.toThrow();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("should set attributes without error", () => {
|
|
123
|
+
const adapter = new NoopTelemetryAdapter();
|
|
124
|
+
|
|
125
|
+
const span = adapter.startSpan({
|
|
126
|
+
name: "test",
|
|
127
|
+
kind: "CHAIN",
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
expect(() =>
|
|
131
|
+
adapter.setAttributes(span, { key: "value", num: 42 })
|
|
132
|
+
).not.toThrow();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("should set context without error", () => {
|
|
136
|
+
const adapter = new NoopTelemetryAdapter();
|
|
137
|
+
|
|
138
|
+
expect(() =>
|
|
139
|
+
adapter.setContext({
|
|
140
|
+
sessionId: "session-123",
|
|
141
|
+
userId: "user-456",
|
|
142
|
+
metadata: { source: "test" },
|
|
143
|
+
tags: ["test", "unit"],
|
|
144
|
+
})
|
|
145
|
+
).not.toThrow();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("should handle error results", () => {
|
|
149
|
+
const adapter = new NoopTelemetryAdapter();
|
|
150
|
+
|
|
151
|
+
const span = adapter.startSpan({
|
|
152
|
+
name: "test",
|
|
153
|
+
kind: "LLM",
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const result: SpanResult = {
|
|
157
|
+
status: "error",
|
|
158
|
+
error: new Error("Test error"),
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
expect(() => adapter.endSpan(span, result)).not.toThrow();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("should handle all event types", () => {
|
|
165
|
+
const adapter = new NoopTelemetryAdapter();
|
|
166
|
+
|
|
167
|
+
const span = adapter.startSpan({
|
|
168
|
+
name: "test",
|
|
169
|
+
kind: "CHAIN",
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// LLM call event
|
|
173
|
+
const llmEvent: LLMCallEvent = {
|
|
174
|
+
type: "llm_call",
|
|
175
|
+
model: "gpt-4o",
|
|
176
|
+
provider: "openai",
|
|
177
|
+
input: {
|
|
178
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
179
|
+
},
|
|
180
|
+
output: {
|
|
181
|
+
content: "Hi",
|
|
182
|
+
usage: { input: 5, output: 2, total: 7 },
|
|
183
|
+
},
|
|
184
|
+
latencyMs: 100,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
expect(() => adapter.recordEvent(span, llmEvent)).not.toThrow();
|
|
188
|
+
|
|
189
|
+
// Validation event
|
|
190
|
+
const validationEvent: ValidationEvent = {
|
|
191
|
+
type: "validation",
|
|
192
|
+
attempt: 1,
|
|
193
|
+
maxAttempts: 3,
|
|
194
|
+
schema: { type: "object" },
|
|
195
|
+
input: { data: "test" },
|
|
196
|
+
success: true,
|
|
197
|
+
latencyMs: 50,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
expect(() => adapter.recordEvent(span, validationEvent)).not.toThrow();
|
|
201
|
+
|
|
202
|
+
// Chunk event
|
|
203
|
+
const chunkEvent: ChunkEvent = {
|
|
204
|
+
type: "chunk",
|
|
205
|
+
chunkIndex: 0,
|
|
206
|
+
totalChunks: 5,
|
|
207
|
+
tokens: 1000,
|
|
208
|
+
images: 2,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
expect(() => adapter.recordEvent(span, chunkEvent)).not.toThrow();
|
|
212
|
+
|
|
213
|
+
// Tool call event
|
|
214
|
+
const toolEvent: ToolCallEvent = {
|
|
215
|
+
type: "tool_call",
|
|
216
|
+
toolName: "read",
|
|
217
|
+
args: { file_path: "/test.txt" },
|
|
218
|
+
result: "file contents",
|
|
219
|
+
latencyMs: 25,
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
expect(() => adapter.recordEvent(span, toolEvent)).not.toThrow();
|
|
223
|
+
|
|
224
|
+
// Merge event
|
|
225
|
+
const mergeEvent: MergeEvent = {
|
|
226
|
+
type: "merge",
|
|
227
|
+
strategy: "parallel",
|
|
228
|
+
inputCount: 5,
|
|
229
|
+
outputCount: 4,
|
|
230
|
+
deduped: 1,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
expect(() => adapter.recordEvent(span, mergeEvent)).not.toThrow();
|
|
234
|
+
|
|
235
|
+
// Parse event
|
|
236
|
+
const parseEvent: ParseEvent = {
|
|
237
|
+
type: "parse",
|
|
238
|
+
mimeType: "application/pdf",
|
|
239
|
+
parser: "pdf-parse",
|
|
240
|
+
inputSize: 1024,
|
|
241
|
+
outputTokens: 500,
|
|
242
|
+
outputImages: 0,
|
|
243
|
+
latencyMs: 200,
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
expect(() => adapter.recordEvent(span, parseEvent)).not.toThrow();
|
|
247
|
+
});
|
|
248
|
+
});
|