langwatch 0.3.0-prerelease.1 → 0.3.0-prerelease.2
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/chunk-4BZATFKJ.mjs +181 -0
- package/dist/chunk-4BZATFKJ.mjs.map +1 -0
- package/dist/chunk-CSC3CMIT.mjs +118 -0
- package/dist/chunk-CSC3CMIT.mjs.map +1 -0
- package/dist/chunk-F63YKTXA.mjs +47 -0
- package/dist/chunk-F63YKTXA.mjs.map +1 -0
- package/dist/chunk-G3AUABT7.js +4 -0
- package/dist/chunk-G3AUABT7.js.map +1 -0
- package/dist/chunk-HPC6Z7J4.js +118 -0
- package/dist/chunk-HPC6Z7J4.js.map +1 -0
- package/dist/chunk-KGDAENGD.js +50 -0
- package/dist/chunk-KGDAENGD.js.map +1 -0
- package/dist/chunk-LD74LVRU.js +47 -0
- package/dist/chunk-LD74LVRU.js.map +1 -0
- package/dist/chunk-OM7VY3XT.mjs +4 -0
- package/dist/chunk-OM7VY3XT.mjs.map +1 -0
- package/dist/chunk-PCQVQ7SB.js +45 -0
- package/dist/chunk-PCQVQ7SB.js.map +1 -0
- package/dist/chunk-PMBEK6YE.mjs +424 -0
- package/dist/chunk-PMBEK6YE.mjs.map +1 -0
- package/dist/chunk-PR3JDWC3.mjs +50 -0
- package/dist/chunk-PR3JDWC3.mjs.map +1 -0
- package/dist/chunk-PTJ6AAI7.js +360 -0
- package/dist/chunk-PTJ6AAI7.js.map +1 -0
- package/dist/chunk-QEWDG5QE.mjs +45 -0
- package/dist/chunk-QEWDG5QE.mjs.map +1 -0
- package/dist/chunk-REUCVT7A.mjs +39 -0
- package/dist/chunk-REUCVT7A.mjs.map +1 -0
- package/dist/chunk-SVJ7SCGB.js +424 -0
- package/dist/chunk-SVJ7SCGB.js.map +1 -0
- package/dist/chunk-VJSOCNPA.js +181 -0
- package/dist/chunk-VJSOCNPA.js.map +1 -0
- package/dist/chunk-WM2GRSRW.js +39 -0
- package/dist/chunk-WM2GRSRW.js.map +1 -0
- package/dist/chunk-Z5J5UI5E.mjs +360 -0
- package/dist/chunk-Z5J5UI5E.mjs.map +1 -0
- package/dist/client-B2HqIKg6.d.ts +51 -0
- package/dist/client-XyCqclCi.d.mts +51 -0
- package/dist/client-browser.d.mts +8 -0
- package/dist/client-browser.d.ts +8 -0
- package/dist/client-browser.js +83 -0
- package/dist/client-browser.js.map +1 -0
- package/dist/client-browser.mjs +83 -0
- package/dist/client-browser.mjs.map +1 -0
- package/dist/client-node.d.mts +8 -0
- package/dist/client-node.d.ts +8 -0
- package/dist/client-node.js +90 -0
- package/dist/client-node.js.map +1 -0
- package/dist/client-node.mjs +90 -0
- package/dist/client-node.mjs.map +1 -0
- package/dist/evaluation/index.d.mts +897 -0
- package/dist/evaluation/index.d.ts +897 -0
- package/dist/evaluation/index.js +13 -0
- package/dist/evaluation/index.js.map +1 -0
- package/dist/evaluation/index.mjs +13 -0
- package/dist/evaluation/index.mjs.map +1 -0
- package/dist/filterable-batch-span-processor-zO5kcjBY.d.mts +64 -0
- package/dist/filterable-batch-span-processor-zO5kcjBY.d.ts +64 -0
- package/dist/index.d.mts +48 -0
- package/{src/observability/exporters/langwatch-exporter.ts → dist/index.d.ts} +13 -18
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +30 -0
- package/dist/index.mjs.map +1 -0
- package/dist/observability/index.d.mts +260 -0
- package/dist/observability/index.d.ts +260 -0
- package/dist/observability/index.js +20 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/index.mjs +20 -0
- package/dist/observability/index.mjs.map +1 -0
- package/dist/observability/instrumentation/langchain/index.d.mts +40 -0
- package/dist/observability/instrumentation/langchain/index.d.ts +40 -0
- package/dist/observability/instrumentation/langchain/index.js +666 -0
- package/dist/observability/instrumentation/langchain/index.js.map +1 -0
- package/dist/observability/instrumentation/langchain/index.mjs +666 -0
- package/dist/observability/instrumentation/langchain/index.mjs.map +1 -0
- package/dist/prompt/index.d.mts +10 -0
- package/dist/prompt/index.d.ts +10 -0
- package/dist/prompt/index.js +18 -0
- package/dist/prompt/index.js.map +1 -0
- package/dist/prompt/index.mjs +18 -0
- package/dist/prompt/index.mjs.map +1 -0
- package/dist/prompt-BXJWdbQp.d.mts +1967 -0
- package/dist/prompt-BXJWdbQp.d.ts +1967 -0
- package/dist/record-evaluation-CmxMXa-3.d.mts +25 -0
- package/dist/record-evaluation-CmxMXa-3.d.ts +25 -0
- package/dist/trace-D-bZOuqb.d.mts +622 -0
- package/dist/trace-G2312klE.d.ts +622 -0
- package/package.json +9 -4
- package/.editorconfig +0 -16
- package/.eslintrc.cjs +0 -37
- package/copy-types.sh +0 -28
- package/examples/langchain/.env.example +0 -2
- package/examples/langchain/README.md +0 -42
- package/examples/langchain/package-lock.json +0 -2930
- package/examples/langchain/package.json +0 -27
- package/examples/langchain/src/cli-markdown.d.ts +0 -137
- package/examples/langchain/src/index.ts +0 -109
- package/examples/langchain/tsconfig.json +0 -25
- package/examples/langgraph/.env.example +0 -2
- package/examples/langgraph/README.md +0 -42
- package/examples/langgraph/package-lock.json +0 -3031
- package/examples/langgraph/package.json +0 -28
- package/examples/langgraph/src/cli-markdown.d.ts +0 -137
- package/examples/langgraph/src/index.ts +0 -196
- package/examples/langgraph/tsconfig.json +0 -25
- package/examples/mastra/.env.example +0 -2
- package/examples/mastra/README.md +0 -57
- package/examples/mastra/package-lock.json +0 -5296
- package/examples/mastra/package.json +0 -32
- package/examples/mastra/src/cli-markdown.d.ts +0 -137
- package/examples/mastra/src/index.ts +0 -120
- package/examples/mastra/src/mastra/agents/weather-agent.ts +0 -30
- package/examples/mastra/src/mastra/index.ts +0 -21
- package/examples/mastra/src/mastra/tools/weather-tool.ts +0 -102
- package/examples/mastra/tsconfig.json +0 -25
- package/examples/vercel-ai/.env.example +0 -2
- package/examples/vercel-ai/README.md +0 -38
- package/examples/vercel-ai/package-lock.json +0 -2571
- package/examples/vercel-ai/package.json +0 -27
- package/examples/vercel-ai/src/cli-markdown.d.ts +0 -137
- package/examples/vercel-ai/src/index.ts +0 -110
- package/examples/vercel-ai/src/instrumentation.ts +0 -9
- package/examples/vercel-ai/tsconfig.json +0 -25
- package/src/__tests__/client-browser.test.ts +0 -92
- package/src/__tests__/client-node.test.ts +0 -76
- package/src/__tests__/client.test.ts +0 -71
- package/src/__tests__/integration/client-browser.test.ts +0 -46
- package/src/__tests__/integration/client-node.test.ts +0 -46
- package/src/client-browser.ts +0 -70
- package/src/client-node.ts +0 -82
- package/src/client-shared.ts +0 -72
- package/src/client.ts +0 -119
- package/src/evaluation/__tests__/record-evaluation.test.ts +0 -112
- package/src/evaluation/__tests__/run-evaluation.test.ts +0 -171
- package/src/evaluation/index.ts +0 -2
- package/src/evaluation/record-evaluation.ts +0 -101
- package/src/evaluation/run-evaluation.ts +0 -133
- package/src/evaluation/tracer.ts +0 -3
- package/src/evaluation/types.ts +0 -23
- package/src/index.ts +0 -13
- package/src/internal/api/__tests__/errors.test.ts +0 -98
- package/src/internal/api/client.ts +0 -30
- package/src/internal/api/errors.ts +0 -32
- package/src/internal/generated/openapi/.gitkeep +0 -0
- package/src/internal/generated/types/.gitkeep +0 -0
- package/src/observability/__tests__/integration/base.test.ts +0 -74
- package/src/observability/__tests__/integration/browser-setup-ordering.test.ts +0 -60
- package/src/observability/__tests__/integration/complex-nested-spans.test.ts +0 -29
- package/src/observability/__tests__/integration/error-handling.test.ts +0 -24
- package/src/observability/__tests__/integration/langwatch-disabled-otel.test.ts +0 -24
- package/src/observability/__tests__/integration/langwatch-first-then-vercel.test.ts +0 -24
- package/src/observability/__tests__/integration/multiple-setup-attempts.test.ts +0 -27
- package/src/observability/__tests__/integration/otel-ordering.test.ts +0 -27
- package/src/observability/__tests__/integration/vercel-configurations.test.ts +0 -20
- package/src/observability/__tests__/integration/vercel-first-then-langwatch.test.ts +0 -27
- package/src/observability/__tests__/span.test.ts +0 -214
- package/src/observability/__tests__/trace.test.ts +0 -180
- package/src/observability/exporters/index.ts +0 -1
- package/src/observability/index.ts +0 -4
- package/src/observability/instrumentation/langchain/__tests__/integration/langchain-chatbot.test.ts +0 -112
- package/src/observability/instrumentation/langchain/__tests__/langchain.test.ts +0 -284
- package/src/observability/instrumentation/langchain/index.ts +0 -624
- package/src/observability/processors/__tests__/filterable-batch-span-exporter.test.ts +0 -98
- package/src/observability/processors/filterable-batch-span-processor.ts +0 -99
- package/src/observability/processors/index.ts +0 -1
- package/src/observability/semconv/attributes.ts +0 -185
- package/src/observability/semconv/events.ts +0 -42
- package/src/observability/semconv/index.ts +0 -16
- package/src/observability/semconv/values.ts +0 -159
- package/src/observability/span.ts +0 -728
- package/src/observability/trace.ts +0 -301
- package/src/prompt/__tests__/prompt.test.ts +0 -139
- package/src/prompt/get-prompt-version.ts +0 -49
- package/src/prompt/get-prompt.ts +0 -44
- package/src/prompt/index.ts +0 -3
- package/src/prompt/prompt.ts +0 -133
- package/src/prompt/service.ts +0 -221
- package/src/prompt/tracer.ts +0 -3
- package/src/prompt/types.ts +0 -0
- package/ts-to-zod.config.js +0 -35
- package/tsconfig.json +0 -26
- package/tsup.config.ts +0 -20
- package/vitest.config.ts +0 -9
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { EvaluationRESTResult } from "../internal/generated/types/evaluations";
|
|
2
|
-
import * as intSemconv from "../observability/semconv";
|
|
3
|
-
import { Attributes, SpanStatusCode } from "@opentelemetry/api";
|
|
4
|
-
import { generate } from "xksuid";
|
|
5
|
-
import { tracer } from "./tracer";
|
|
6
|
-
|
|
7
|
-
export interface RecordedEvaluationDetails {
|
|
8
|
-
evaluationId?: string;
|
|
9
|
-
name: string;
|
|
10
|
-
type?: string;
|
|
11
|
-
isGuardrail?: boolean;
|
|
12
|
-
status?: "processed" | "skipped" | "error";
|
|
13
|
-
passed?: boolean;
|
|
14
|
-
score?: number;
|
|
15
|
-
label?: string;
|
|
16
|
-
details?: string;
|
|
17
|
-
cost?: number | { currency: string; amount: number };
|
|
18
|
-
error?: Error;
|
|
19
|
-
timestamps?: {
|
|
20
|
-
startedAtUnixMs: number;
|
|
21
|
-
finishedAtUnixMs: number;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function recordEvaluation(
|
|
26
|
-
details: RecordedEvaluationDetails,
|
|
27
|
-
attributes?: Attributes,
|
|
28
|
-
) {
|
|
29
|
-
let result: EvaluationRESTResult;
|
|
30
|
-
const status = details.status || "processed";
|
|
31
|
-
|
|
32
|
-
if (status === "skipped") {
|
|
33
|
-
result = {
|
|
34
|
-
status: "skipped",
|
|
35
|
-
details: details.details,
|
|
36
|
-
};
|
|
37
|
-
} else if (status === "error") {
|
|
38
|
-
result = {
|
|
39
|
-
status: "error",
|
|
40
|
-
error_type: details.error?.name || "Unknown",
|
|
41
|
-
details: details.details || details.error?.message || "Unknown error",
|
|
42
|
-
};
|
|
43
|
-
} else {
|
|
44
|
-
result = {
|
|
45
|
-
status: "processed",
|
|
46
|
-
passed: details.passed,
|
|
47
|
-
score: details.score,
|
|
48
|
-
label: details.label,
|
|
49
|
-
details: details.details,
|
|
50
|
-
};
|
|
51
|
-
if (details.cost) {
|
|
52
|
-
(result as any).cost =
|
|
53
|
-
typeof details.cost === "number"
|
|
54
|
-
? { currency: "USD", amount: details.cost }
|
|
55
|
-
: details.cost;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
tracer.startActiveSpan("record evaluation", (span) => {
|
|
60
|
-
try {
|
|
61
|
-
span.setType(details.isGuardrail ? "guardrail" : "evaluation");
|
|
62
|
-
span.addEvent(intSemconv.ATTR_LANGWATCH_EVALUATION_CUSTOM, {
|
|
63
|
-
json_encoded_event: JSON.stringify({
|
|
64
|
-
evaluation_id: details.evaluationId ?? `eval_${generate()}`,
|
|
65
|
-
name: details.name,
|
|
66
|
-
type: details.type,
|
|
67
|
-
is_guardrail: details.isGuardrail,
|
|
68
|
-
status: result.status,
|
|
69
|
-
passed: details.passed,
|
|
70
|
-
score: details.score,
|
|
71
|
-
label: details.label,
|
|
72
|
-
details: details.details,
|
|
73
|
-
cost: details.cost,
|
|
74
|
-
error: details.error,
|
|
75
|
-
timestamps: details.timestamps,
|
|
76
|
-
}),
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
span.setOutput(result);
|
|
80
|
-
|
|
81
|
-
if (attributes) {
|
|
82
|
-
span.setAttributes(attributes);
|
|
83
|
-
}
|
|
84
|
-
if (details.cost) {
|
|
85
|
-
span.setMetrics({
|
|
86
|
-
cost:
|
|
87
|
-
typeof details.cost === "number"
|
|
88
|
-
? details.cost
|
|
89
|
-
: details.cost.amount,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
} catch (error) {
|
|
93
|
-
span.recordException(error as Error);
|
|
94
|
-
span.setStatus({ code: SpanStatusCode.ERROR, message: (error as Error)?.message });
|
|
95
|
-
} finally {
|
|
96
|
-
span.end();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return;
|
|
100
|
-
});
|
|
101
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { LangWatchApiError } from "../internal/api/errors";
|
|
2
|
-
import { canAutomaticallyCaptureInput, getApiKey, getEndpoint } from "../client";
|
|
3
|
-
import { Conversation } from "../internal/generated/types/evaluations";
|
|
4
|
-
import {
|
|
5
|
-
Evaluators,
|
|
6
|
-
EvaluatorTypes,
|
|
7
|
-
SingleEvaluationResult,
|
|
8
|
-
} from "../internal/generated/types/evaluators.generated";
|
|
9
|
-
import { RAGChunk } from "../internal/generated/types/tracer";
|
|
10
|
-
import { tracer } from "./tracer";
|
|
11
|
-
import { EvaluationResultModel } from "./types";
|
|
12
|
-
import { SpanStatusCode } from "@opentelemetry/api";
|
|
13
|
-
|
|
14
|
-
export interface BasicEvaluationData {
|
|
15
|
-
input?: string;
|
|
16
|
-
output?: string;
|
|
17
|
-
expected_output?: unknown;
|
|
18
|
-
contexts?: RAGChunk[] | string[];
|
|
19
|
-
expected_contexts?: RAGChunk[] | string[];
|
|
20
|
-
conversation?: Conversation;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface EvaluationDetailsBase {
|
|
24
|
-
name?: string;
|
|
25
|
-
data: BasicEvaluationData | Record<string, unknown>;
|
|
26
|
-
contexts?: RAGChunk[] | string[];
|
|
27
|
-
conversation?: Conversation;
|
|
28
|
-
asGuardrail?: boolean;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface SavedEvaluationDetails extends EvaluationDetailsBase {
|
|
32
|
-
slug: string;
|
|
33
|
-
settings?: Record<string, unknown>;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface LangEvalsEvaluationDetails<T extends EvaluatorTypes>
|
|
37
|
-
extends EvaluationDetailsBase {
|
|
38
|
-
evaluator: T;
|
|
39
|
-
settings?: Evaluators[T]["settings"];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export type EvaluationDetails =
|
|
43
|
-
| SavedEvaluationDetails
|
|
44
|
-
| LangEvalsEvaluationDetails<EvaluatorTypes>;
|
|
45
|
-
|
|
46
|
-
export async function runEvaluation(
|
|
47
|
-
details: EvaluationDetails,
|
|
48
|
-
): Promise<SingleEvaluationResult> {
|
|
49
|
-
return await tracer.startActiveSpan("run evaluation", async (span) => {
|
|
50
|
-
span.setType(details.asGuardrail ? "guardrail" : "evaluation");
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
const evaluatorId =
|
|
54
|
-
"slug" in details ? details.slug : details.evaluator;
|
|
55
|
-
const request = {
|
|
56
|
-
trace_id: span.spanContext().traceId,
|
|
57
|
-
span_id: span.spanContext().spanId,
|
|
58
|
-
data: details.data,
|
|
59
|
-
name: details.name,
|
|
60
|
-
settings: details.settings,
|
|
61
|
-
as_guardrail: details.asGuardrail,
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
if (canAutomaticallyCaptureInput()) {
|
|
65
|
-
span.setInput(request);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const url = new URL(
|
|
69
|
-
`/api/evaluations/${evaluatorId}/evaluate`,
|
|
70
|
-
getEndpoint(),
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
const response = await fetch(url.toString(), {
|
|
74
|
-
method: "POST",
|
|
75
|
-
headers: {
|
|
76
|
-
"X-Auth-Token": getApiKey(),
|
|
77
|
-
"Content-Type": "application/json",
|
|
78
|
-
},
|
|
79
|
-
body: JSON.stringify(request),
|
|
80
|
-
});
|
|
81
|
-
if (!response.ok) {
|
|
82
|
-
const err = new LangWatchApiError("Unable to run evaluation", response);
|
|
83
|
-
await err.safeParseBody(response);
|
|
84
|
-
|
|
85
|
-
throw err;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const result: EvaluationResultModel = await response.json();
|
|
89
|
-
|
|
90
|
-
span.setMetrics({
|
|
91
|
-
cost: result.cost?.amount,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
span.setOutputEvaluation(details.asGuardrail ?? false, result);
|
|
95
|
-
|
|
96
|
-
if (result.status === "processed") {
|
|
97
|
-
return {
|
|
98
|
-
status: "processed",
|
|
99
|
-
passed: result.passed,
|
|
100
|
-
score: result.score,
|
|
101
|
-
details: result.details,
|
|
102
|
-
label: result.label,
|
|
103
|
-
cost: result.cost,
|
|
104
|
-
} as SingleEvaluationResult;
|
|
105
|
-
} else if (result.status === "skipped") {
|
|
106
|
-
return {
|
|
107
|
-
status: "skipped",
|
|
108
|
-
details: result.details,
|
|
109
|
-
} as SingleEvaluationResult;
|
|
110
|
-
} else if (result.status === "error") {
|
|
111
|
-
return {
|
|
112
|
-
status: "error",
|
|
113
|
-
error_type: (result as any).error_type || "Unknown",
|
|
114
|
-
details: result.details || "Unknown error",
|
|
115
|
-
traceback: (result as any).traceback || [],
|
|
116
|
-
} as SingleEvaluationResult;
|
|
117
|
-
} else {
|
|
118
|
-
return {
|
|
119
|
-
status: "error",
|
|
120
|
-
error_type: "UnknownStatus",
|
|
121
|
-
details: `Unknown evaluation status: ${result.status}`,
|
|
122
|
-
traceback: [],
|
|
123
|
-
} as SingleEvaluationResult;
|
|
124
|
-
}
|
|
125
|
-
} catch (error) {
|
|
126
|
-
span.recordException(error as Error);
|
|
127
|
-
span.setStatus({ code: SpanStatusCode.ERROR, message: (error as Error)?.message });
|
|
128
|
-
throw error;
|
|
129
|
-
} finally {
|
|
130
|
-
span.end();
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
}
|
package/src/evaluation/tracer.ts
DELETED
package/src/evaluation/types.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export class EvaluationError extends Error {
|
|
2
|
-
readonly httpStatus: number;
|
|
3
|
-
readonly body: unknown;
|
|
4
|
-
|
|
5
|
-
constructor(message: string, httpStatus: number, body: unknown) {
|
|
6
|
-
super(message);
|
|
7
|
-
this.name = "EvaluationError";
|
|
8
|
-
this.httpStatus = httpStatus;
|
|
9
|
-
this.body = body;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface EvaluationResultModel {
|
|
14
|
-
status: "processed" | "skipped" | "error";
|
|
15
|
-
passed?: boolean;
|
|
16
|
-
score?: number;
|
|
17
|
-
details?: string;
|
|
18
|
-
label?: string;
|
|
19
|
-
cost?: {
|
|
20
|
-
currency: string;
|
|
21
|
-
amount: number;
|
|
22
|
-
};
|
|
23
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export { getLangWatchTracer, type LangWatchSpan } from "./observability";
|
|
2
|
-
export {
|
|
3
|
-
FilterableBatchSpanProcessor,
|
|
4
|
-
type SpanProcessingExcludeRule,
|
|
5
|
-
} from "./observability/processors";
|
|
6
|
-
export { LangWatchExporter } from "./observability/exporters";
|
|
7
|
-
|
|
8
|
-
export { recordEvaluation, runEvaluation } from "./evaluation";
|
|
9
|
-
|
|
10
|
-
export {
|
|
11
|
-
getPrompt,
|
|
12
|
-
getPromptVersion,
|
|
13
|
-
} from "./prompt";
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { LangWatchApiError } from '../errors';
|
|
3
|
-
|
|
4
|
-
describe('LangWatchApiError', () => {
|
|
5
|
-
it('creates error with message and response', () => {
|
|
6
|
-
const mockResponse = {
|
|
7
|
-
status: 400,
|
|
8
|
-
statusText: 'Bad Request'
|
|
9
|
-
} as Response;
|
|
10
|
-
|
|
11
|
-
const error = new LangWatchApiError('Test error message', mockResponse);
|
|
12
|
-
expect(error.message).toBe('Test error message');
|
|
13
|
-
expect(error.name).toBe('Error');
|
|
14
|
-
expect(error.httpStatus).toBe(400);
|
|
15
|
-
expect(error.httpStatusText).toBe('Bad Request');
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('inherits from Error', () => {
|
|
19
|
-
const mockResponse = { status: 500, statusText: 'Internal Server Error' } as Response;
|
|
20
|
-
const error = new LangWatchApiError('Test error', mockResponse);
|
|
21
|
-
expect(error).toBeInstanceOf(Error);
|
|
22
|
-
expect(error).toBeInstanceOf(LangWatchApiError);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('has stack trace', () => {
|
|
26
|
-
const mockResponse = { status: 404, statusText: 'Not Found' } as Response;
|
|
27
|
-
const error = new LangWatchApiError('Test error', mockResponse);
|
|
28
|
-
expect(error.stack).toBeDefined();
|
|
29
|
-
expect(typeof error.stack).toBe('string');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('parses JSON response body', async () => {
|
|
33
|
-
const mockResponse = {
|
|
34
|
-
status: 400,
|
|
35
|
-
statusText: 'Bad Request',
|
|
36
|
-
headers: {
|
|
37
|
-
get: vi.fn().mockReturnValue('application/json')
|
|
38
|
-
},
|
|
39
|
-
json: vi.fn().mockResolvedValue({ error: 'Invalid API key' })
|
|
40
|
-
} as unknown as Response;
|
|
41
|
-
|
|
42
|
-
const error = new LangWatchApiError('Bad request', mockResponse);
|
|
43
|
-
await error.safeParseBody(mockResponse);
|
|
44
|
-
|
|
45
|
-
expect(error.body).toEqual({ error: 'Invalid API key' });
|
|
46
|
-
expect(error.apiError).toBe('Invalid API key');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('parses text response body for non-JSON content', async () => {
|
|
50
|
-
const mockResponse = {
|
|
51
|
-
status: 500,
|
|
52
|
-
statusText: 'Internal Server Error',
|
|
53
|
-
headers: {
|
|
54
|
-
get: vi.fn().mockReturnValue('text/plain')
|
|
55
|
-
},
|
|
56
|
-
text: vi.fn().mockResolvedValue('Server error occurred')
|
|
57
|
-
} as unknown as Response;
|
|
58
|
-
|
|
59
|
-
const error = new LangWatchApiError('Server error', mockResponse);
|
|
60
|
-
await error.safeParseBody(mockResponse);
|
|
61
|
-
|
|
62
|
-
expect(error.body).toBe('Server error occurred');
|
|
63
|
-
expect(error.apiError).toBeUndefined();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('handles parsing errors gracefully', async () => {
|
|
67
|
-
const mockResponse = {
|
|
68
|
-
status: 500,
|
|
69
|
-
statusText: 'Internal Server Error',
|
|
70
|
-
headers: {
|
|
71
|
-
get: vi.fn().mockReturnValue('application/json')
|
|
72
|
-
},
|
|
73
|
-
json: vi.fn().mockRejectedValue(new Error('Parse error'))
|
|
74
|
-
} as unknown as Response;
|
|
75
|
-
|
|
76
|
-
const error = new LangWatchApiError('Server error', mockResponse);
|
|
77
|
-
await error.safeParseBody(mockResponse);
|
|
78
|
-
|
|
79
|
-
expect(error.body).toBe(null);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('handles JSON without error field', async () => {
|
|
83
|
-
const mockResponse = {
|
|
84
|
-
status: 400,
|
|
85
|
-
statusText: 'Bad Request',
|
|
86
|
-
headers: {
|
|
87
|
-
get: vi.fn().mockReturnValue('application/json')
|
|
88
|
-
},
|
|
89
|
-
json: vi.fn().mockResolvedValue({ message: 'Something went wrong' })
|
|
90
|
-
} as unknown as Response;
|
|
91
|
-
|
|
92
|
-
const error = new LangWatchApiError('Bad request', mockResponse);
|
|
93
|
-
await error.safeParseBody(mockResponse);
|
|
94
|
-
|
|
95
|
-
expect(error.body).toEqual({ message: 'Something went wrong' });
|
|
96
|
-
expect(error.apiError).toBeUndefined();
|
|
97
|
-
});
|
|
98
|
-
});
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import openApiCreateClient from "openapi-fetch";
|
|
2
|
-
import type { paths } from "../generated/openapi/api-client";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { getApiKey, getEndpoint } from "../../client";
|
|
5
|
-
|
|
6
|
-
// Define the client type explicitly to avoid naming issues
|
|
7
|
-
export type LangwatchApiClient = ReturnType<typeof openApiCreateClient<paths>>;
|
|
8
|
-
|
|
9
|
-
const configSchema = z.object({
|
|
10
|
-
apiKey: z.string().min(1, "API key is required"),
|
|
11
|
-
endpoint: z.string().url("Endpoint must be a valid URL"),
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
export function createLangWatchApiClient(apiKey?: string | undefined, endpoint?: string | undefined ): LangwatchApiClient {
|
|
15
|
-
// This will error if the config is invalid
|
|
16
|
-
const config = configSchema.parse({
|
|
17
|
-
apiKey: apiKey ?? getApiKey(),
|
|
18
|
-
endpoint: endpoint ?? getEndpoint(),
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
return openApiCreateClient<paths>({
|
|
22
|
-
baseUrl: config.endpoint,
|
|
23
|
-
headers: {
|
|
24
|
-
"X-Auth-Token": config.apiKey,
|
|
25
|
-
"Content-Type": "application/json",
|
|
26
|
-
},
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export class LangWatchApiError extends Error {
|
|
2
|
-
public readonly httpStatus: number;
|
|
3
|
-
public readonly httpStatusText: string;
|
|
4
|
-
public apiError: string | undefined;
|
|
5
|
-
public body: unknown;
|
|
6
|
-
|
|
7
|
-
constructor(message: string, response: Response) {
|
|
8
|
-
super(message);
|
|
9
|
-
this.httpStatus = response.status;
|
|
10
|
-
this.httpStatusText = response.statusText;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async safeParseBody(response: Response): Promise<void> {
|
|
14
|
-
try {
|
|
15
|
-
if (response.headers.get("Content-Type")?.includes("application/json")) {
|
|
16
|
-
const json = await response.json();
|
|
17
|
-
|
|
18
|
-
this.body = json;
|
|
19
|
-
|
|
20
|
-
if (json.error && typeof json.error === "string") {
|
|
21
|
-
this.apiError = json.error;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
this.body = await response.text();
|
|
28
|
-
} catch {
|
|
29
|
-
this.body = null;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
File without changes
|
|
File without changes
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { beforeAll, describe, expect, it } from "vitest";
|
|
2
|
-
import { setupLangWatch } from "../../../client-node";
|
|
3
|
-
import { getLangWatchTracer } from "../../trace";
|
|
4
|
-
|
|
5
|
-
const tracerName = "basic-observability.test";
|
|
6
|
-
|
|
7
|
-
describe("basic observability tests around tracing", () => {
|
|
8
|
-
beforeAll(async () => {
|
|
9
|
-
await setupLangWatch();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it("traces should be sent", async () => {
|
|
13
|
-
const tracer = getLangWatchTracer(tracerName);
|
|
14
|
-
await tracer.withActiveSpan(
|
|
15
|
-
"basic trace",
|
|
16
|
-
async () => { },
|
|
17
|
-
);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("traces should be sent with complex arguments", async () => {
|
|
21
|
-
const tracer = getLangWatchTracer(tracerName);
|
|
22
|
-
await tracer.withActiveSpan(
|
|
23
|
-
"complex argument trace",
|
|
24
|
-
{ attributes: { foo: "bar" }, root: true },
|
|
25
|
-
async (span) => {
|
|
26
|
-
span.setAttributes({
|
|
27
|
-
bar: "bas",
|
|
28
|
-
});
|
|
29
|
-
span.addEvent("test event", {
|
|
30
|
-
foo: "bar",
|
|
31
|
-
});
|
|
32
|
-
},
|
|
33
|
-
);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("traces exceptions", async () => {
|
|
37
|
-
const tracer = getLangWatchTracer(tracerName);
|
|
38
|
-
|
|
39
|
-
await expect(
|
|
40
|
-
tracer.withActiveSpan(
|
|
41
|
-
"trace exception",
|
|
42
|
-
async () => {
|
|
43
|
-
throw new Error("this is meant to error");
|
|
44
|
-
},
|
|
45
|
-
)
|
|
46
|
-
).rejects.toThrow("this is meant to error");
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("traces handle complex nesting", async () => {
|
|
50
|
-
const tracer = getLangWatchTracer(tracerName);
|
|
51
|
-
await tracer.withActiveSpan(
|
|
52
|
-
"complex nesting trace",
|
|
53
|
-
async () => {
|
|
54
|
-
await tracer.withActiveSpan(
|
|
55
|
-
"nested trace alpha",
|
|
56
|
-
async () => { },
|
|
57
|
-
);
|
|
58
|
-
await tracer.withActiveSpan(
|
|
59
|
-
"nested trace beta",
|
|
60
|
-
async () => { },
|
|
61
|
-
);
|
|
62
|
-
await tracer.withActiveSpan(
|
|
63
|
-
"nested trace gamma",
|
|
64
|
-
async () => {
|
|
65
|
-
await tracer.withActiveSpan(
|
|
66
|
-
"nested trace gamma child",
|
|
67
|
-
async () => { },
|
|
68
|
-
);
|
|
69
|
-
},
|
|
70
|
-
);
|
|
71
|
-
},
|
|
72
|
-
);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { setupLangWatch as setupBrowser } from "../../../client-browser";
|
|
3
|
-
import { getLangWatchTracer } from "../../trace";
|
|
4
|
-
|
|
5
|
-
// Mock window object for Node.js test environment
|
|
6
|
-
const mockWindow = {
|
|
7
|
-
addEventListener: vi.fn(),
|
|
8
|
-
removeEventListener: vi.fn(),
|
|
9
|
-
};
|
|
10
|
-
global.window = mockWindow as any;
|
|
11
|
-
|
|
12
|
-
describe("Browser SDK setup ordering", () => {
|
|
13
|
-
it("should work when LangWatch browser setup is called multiple times", async () => {
|
|
14
|
-
// First setup
|
|
15
|
-
await setupBrowser({
|
|
16
|
-
apiKey: "test-key-1",
|
|
17
|
-
endpoint: "http://localhost:9999",
|
|
18
|
-
skipOpenTelemetrySetup: false,
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// Second setup should work (browser doesn't have the same restriction as node)
|
|
22
|
-
await setupBrowser({
|
|
23
|
-
apiKey: "test-key-2",
|
|
24
|
-
endpoint: "http://localhost:9999",
|
|
25
|
-
skipOpenTelemetrySetup: false,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Test that LangWatch still works
|
|
29
|
-
const tracer = getLangWatchTracer("browser-multiple-test");
|
|
30
|
-
await tracer.withActiveSpan("test span", async () => {
|
|
31
|
-
expect(true).toBe(true);
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("should work when LangWatch is set up with skipOpenTelemetrySetup=true", async () => {
|
|
36
|
-
// Setup LangWatch with automatic setup disabled
|
|
37
|
-
await setupBrowser({
|
|
38
|
-
apiKey: "test-key",
|
|
39
|
-
endpoint: "http://localhost:9999",
|
|
40
|
-
skipOpenTelemetrySetup: true,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// Test that LangWatch still works
|
|
44
|
-
const tracer = getLangWatchTracer("browser-disabled-test");
|
|
45
|
-
await tracer.withActiveSpan("test span", async () => {
|
|
46
|
-
expect(true).toBe(true);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("should handle window event listeners correctly", async () => {
|
|
51
|
-
await setupBrowser({
|
|
52
|
-
apiKey: "test-key",
|
|
53
|
-
endpoint: "http://localhost:9999",
|
|
54
|
-
skipOpenTelemetrySetup: false,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// Check that window event listeners were added
|
|
58
|
-
expect(mockWindow.addEventListener).toHaveBeenCalled();
|
|
59
|
-
});
|
|
60
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { registerOTel } from '@vercel/otel';
|
|
3
|
-
import { setupLangWatch } from "../../../client-node";
|
|
4
|
-
import { getLangWatchTracer } from "../../trace";
|
|
5
|
-
|
|
6
|
-
describe("Complex nested spans with Vercel AI", () => {
|
|
7
|
-
it("should work with complex nested spans when Vercel AI is set up first", async () => {
|
|
8
|
-
registerOTel({ serviceName: 'complex-test' });
|
|
9
|
-
await setupLangWatch({
|
|
10
|
-
apiKey: "test-key",
|
|
11
|
-
endpoint: "http://localhost:9999",
|
|
12
|
-
skipOpenTelemetrySetup: false,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
const tracer = getLangWatchTracer("complex-otel-test");
|
|
16
|
-
|
|
17
|
-
await tracer.withActiveSpan("root span", async () => {
|
|
18
|
-
await tracer.withActiveSpan("child span 1", async () => {
|
|
19
|
-
await tracer.withActiveSpan("grandchild span", async () => {
|
|
20
|
-
expect(true).toBe(true);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
await tracer.withActiveSpan("child span 2", async () => {
|
|
25
|
-
expect(true).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { registerOTel } from '@vercel/otel';
|
|
3
|
-
import { setupLangWatch } from "../../../client-node";
|
|
4
|
-
import { getLangWatchTracer } from "../../trace";
|
|
5
|
-
|
|
6
|
-
describe("Error handling with Vercel AI", () => {
|
|
7
|
-
it("should handle errors gracefully when both are set up", async () => {
|
|
8
|
-
registerOTel({ serviceName: 'error-test' });
|
|
9
|
-
await setupLangWatch({
|
|
10
|
-
apiKey: "test-key",
|
|
11
|
-
endpoint: "http://localhost:9999",
|
|
12
|
-
skipOpenTelemetrySetup: false,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
const tracer = getLangWatchTracer("error-otel-test");
|
|
16
|
-
|
|
17
|
-
// Test that exceptions in spans are properly handled
|
|
18
|
-
await expect(
|
|
19
|
-
tracer.withActiveSpan("error span", async () => {
|
|
20
|
-
throw new Error("Test error");
|
|
21
|
-
})
|
|
22
|
-
).rejects.toThrow("Test error");
|
|
23
|
-
});
|
|
24
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { registerOTel } from '@vercel/otel';
|
|
3
|
-
import { setupLangWatch } from "../../../client-node";
|
|
4
|
-
import { getLangWatchTracer } from "../../trace";
|
|
5
|
-
|
|
6
|
-
describe("LangWatch with skipOpenTelemetrySetup=true", () => {
|
|
7
|
-
it("should work when LangWatch is set up with skipOpenTelemetrySetup=true", async () => {
|
|
8
|
-
// Setup Vercel AI first
|
|
9
|
-
registerOTel({ serviceName: 'vercel-first' });
|
|
10
|
-
|
|
11
|
-
// Then setup LangWatch with automatic setup disabled
|
|
12
|
-
await setupLangWatch({
|
|
13
|
-
apiKey: "test-key",
|
|
14
|
-
endpoint: "http://localhost:9999",
|
|
15
|
-
skipOpenTelemetrySetup: true,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
// Test that LangWatch still works
|
|
19
|
-
const tracer = getLangWatchTracer("disabled-otel-test");
|
|
20
|
-
await tracer.withActiveSpan("test span", async () => {
|
|
21
|
-
expect(true).toBe(true);
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { registerOTel } from '@vercel/otel';
|
|
3
|
-
import { setupLangWatch } from "../../../client-node";
|
|
4
|
-
import { getLangWatchTracer } from "../../trace";
|
|
5
|
-
|
|
6
|
-
describe("LangWatch setup first, then Vercel AI", () => {
|
|
7
|
-
it("should work when LangWatch is set up first, then Vercel AI", async () => {
|
|
8
|
-
// Setup LangWatch first
|
|
9
|
-
await setupLangWatch({
|
|
10
|
-
apiKey: "test-key",
|
|
11
|
-
endpoint: "http://localhost:9999",
|
|
12
|
-
skipOpenTelemetrySetup: false,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
// Then setup Vercel AI
|
|
16
|
-
registerOTel({ serviceName: 'langwatch-first' });
|
|
17
|
-
|
|
18
|
-
// Test that LangWatch still works
|
|
19
|
-
const tracer = getLangWatchTracer("langwatch-first-test");
|
|
20
|
-
await tracer.withActiveSpan("test span", async () => {
|
|
21
|
-
expect(true).toBe(true);
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
});
|