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,301 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
trace as otelTrace,
|
|
3
|
-
Tracer,
|
|
4
|
-
Span,
|
|
5
|
-
SpanOptions,
|
|
6
|
-
Context,
|
|
7
|
-
SpanStatusCode,
|
|
8
|
-
} from "@opentelemetry/api";
|
|
9
|
-
import { LangWatchSpan, createLangWatchSpan } from "./span";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* LangWatch OpenTelemetry Tracing Extensions
|
|
13
|
-
*
|
|
14
|
-
* This module provides wrappers and helpers for OpenTelemetry Tracer and Span objects,
|
|
15
|
-
* adding ergonomic methods for LLM/GenAI observability and structured tracing.
|
|
16
|
-
*
|
|
17
|
-
* @module trace
|
|
18
|
-
*/
|
|
19
|
-
export interface LangWatchTracer extends Tracer {
|
|
20
|
-
/**
|
|
21
|
-
* Starts a new {@link LangWatchSpan}. Start the span without setting it on context.
|
|
22
|
-
*
|
|
23
|
-
* This method does NOT modify the current Context.
|
|
24
|
-
*
|
|
25
|
-
* @param name The name of the span
|
|
26
|
-
* @param [options] SpanOptions used for span creation
|
|
27
|
-
* @param [context] Context to use to extract parent
|
|
28
|
-
* @returns LangWatchSpan The newly created span
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* const span = tracer.startSpan('op');
|
|
32
|
-
* span.setAttribute('key', 'value');
|
|
33
|
-
* span.end();
|
|
34
|
-
*/
|
|
35
|
-
startSpan(
|
|
36
|
-
name: string,
|
|
37
|
-
options?: SpanOptions,
|
|
38
|
-
context?: Context,
|
|
39
|
-
): LangWatchSpan;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Starts a new {@link LangWatchSpan} and calls the given function passing it the
|
|
43
|
-
* created span as first argument.
|
|
44
|
-
* Additionally the new span gets set in context and this context is activated
|
|
45
|
-
* for the duration of the function call.
|
|
46
|
-
*
|
|
47
|
-
* @param name The name of the span
|
|
48
|
-
* @param [options] SpanOptions used for span creation
|
|
49
|
-
* @param [context] Context to use to extract parent
|
|
50
|
-
* @param fn function called in the context of the span and receives the newly created span as an argument
|
|
51
|
-
* @returns return value of fn
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* const result = tracer.startActiveSpan('op', span => {
|
|
55
|
-
* try {
|
|
56
|
-
* // do some work
|
|
57
|
-
* span.setStatus({code: SpanStatusCode.OK});
|
|
58
|
-
* return something;
|
|
59
|
-
* } catch (err) {
|
|
60
|
-
* span.setStatus({
|
|
61
|
-
* code: SpanStatusCode.ERROR,
|
|
62
|
-
* message: err.message,
|
|
63
|
-
* });
|
|
64
|
-
* throw err;
|
|
65
|
-
* } finally {
|
|
66
|
-
* span.end();
|
|
67
|
-
* }
|
|
68
|
-
* });
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* const span = tracer.startActiveSpan('op', span => {
|
|
72
|
-
* try {
|
|
73
|
-
* do some work
|
|
74
|
-
* return span;
|
|
75
|
-
* } catch (err) {
|
|
76
|
-
* span.setStatus({
|
|
77
|
-
* code: SpanStatusCode.ERROR,
|
|
78
|
-
* message: err.message,
|
|
79
|
-
* });
|
|
80
|
-
* throw err;
|
|
81
|
-
* }
|
|
82
|
-
* });
|
|
83
|
-
* do some more work
|
|
84
|
-
* span.end();
|
|
85
|
-
*/
|
|
86
|
-
startActiveSpan<F extends (span: LangWatchSpan) => unknown>(
|
|
87
|
-
name: string,
|
|
88
|
-
fn: F,
|
|
89
|
-
): ReturnType<F>;
|
|
90
|
-
startActiveSpan<F extends (span: LangWatchSpan) => unknown>(
|
|
91
|
-
name: string,
|
|
92
|
-
options: SpanOptions,
|
|
93
|
-
fn: F,
|
|
94
|
-
): ReturnType<F>;
|
|
95
|
-
startActiveSpan<F extends (span: LangWatchSpan) => unknown>(
|
|
96
|
-
name: string,
|
|
97
|
-
options: SpanOptions,
|
|
98
|
-
context: Context,
|
|
99
|
-
fn: F,
|
|
100
|
-
): ReturnType<F>;
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Starts a new {@link LangWatchSpan}, runs the provided async function, and automatically handles
|
|
104
|
-
* error recording, status setting, and span ending. This is a safer and more ergonomic alternative
|
|
105
|
-
* to manually using try/catch/finally blocks with startActiveSpan.
|
|
106
|
-
*
|
|
107
|
-
* Overloads:
|
|
108
|
-
* - withActiveSpan(name, fn)
|
|
109
|
-
* - withActiveSpan(name, options, fn)
|
|
110
|
-
* - withActiveSpan(name, options, context, fn)
|
|
111
|
-
*
|
|
112
|
-
* @param name The name of the span
|
|
113
|
-
* @param options Optional SpanOptions for span creation
|
|
114
|
-
* @param context Optional Context to use to extract parent
|
|
115
|
-
* @param fn The async function to execute within the span context. Receives the span as its first argument.
|
|
116
|
-
* @returns The return value of the provided function
|
|
117
|
-
*
|
|
118
|
-
* @example
|
|
119
|
-
* await tracer.withActiveSpan('my-operation', async (span) => {
|
|
120
|
-
* // ... your code ...
|
|
121
|
-
* });
|
|
122
|
-
*
|
|
123
|
-
* await tracer.withActiveSpan('my-operation', { attributes: { foo: 'bar' } }, async (span) => {
|
|
124
|
-
* // ... your code ...
|
|
125
|
-
* });
|
|
126
|
-
*
|
|
127
|
-
* await tracer.withActiveSpan('my-operation', { attributes: { foo: 'bar' } }, myContext, async (span) => {
|
|
128
|
-
* // ... your code ...
|
|
129
|
-
* });
|
|
130
|
-
*/
|
|
131
|
-
withActiveSpan<T>(
|
|
132
|
-
name: string,
|
|
133
|
-
fn: (span: LangWatchSpan) => Promise<T> | T
|
|
134
|
-
): Promise<T>;
|
|
135
|
-
withActiveSpan<T>(
|
|
136
|
-
name: string,
|
|
137
|
-
options: SpanOptions,
|
|
138
|
-
fn: (span: LangWatchSpan) => Promise<T> | T
|
|
139
|
-
): Promise<T>;
|
|
140
|
-
withActiveSpan<T>(
|
|
141
|
-
name: string,
|
|
142
|
-
options: SpanOptions,
|
|
143
|
-
context: Context,
|
|
144
|
-
fn: (span: LangWatchSpan) => Promise<T> | T
|
|
145
|
-
): Promise<T>;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Extension of OpenTelemetry's Tracer with LangWatch-specific helpers.
|
|
150
|
-
*
|
|
151
|
-
* This interface provides methods for starting spans and active spans that return LangWatchSpan objects,
|
|
152
|
-
* which include ergonomic helpers for LLM/GenAI tracing.
|
|
153
|
-
*
|
|
154
|
-
* @example
|
|
155
|
-
* import { getLangWatchTracer } from 'langwatch';
|
|
156
|
-
* const tracer = getLangWatchTracer('my-service');
|
|
157
|
-
* const span = tracer.startSpan('llm-call');
|
|
158
|
-
* span.setType('llm').setInput('Prompt').setOutput('Completion');
|
|
159
|
-
* span.end();
|
|
160
|
-
*
|
|
161
|
-
* tracer.startActiveSpan('llm-call', (span) => {
|
|
162
|
-
* span.setType('llm');
|
|
163
|
-
* // ...
|
|
164
|
-
* span.end();
|
|
165
|
-
* });
|
|
166
|
-
*/
|
|
167
|
-
export function getLangWatchTracer(name: string, version?: string): LangWatchTracer {
|
|
168
|
-
const tracer = otelTrace.getTracer(name, version);
|
|
169
|
-
|
|
170
|
-
// Create a proxy for the tracer that intercepts the calls to startActiveSpan and
|
|
171
|
-
// startSpan, and wraps the span object with our custom LangWatchSpan.
|
|
172
|
-
const handler: ProxyHandler<LangWatchTracer> = {
|
|
173
|
-
get(target, prop, _receiver) {
|
|
174
|
-
switch (prop) {
|
|
175
|
-
case "startActiveSpan": {
|
|
176
|
-
const startActiveSpan: StartActiveSpanOverloads = (
|
|
177
|
-
...args: [
|
|
178
|
-
string,
|
|
179
|
-
SpanOptions?,
|
|
180
|
-
Context?,
|
|
181
|
-
((span: Span) => unknown)?,
|
|
182
|
-
]
|
|
183
|
-
) => {
|
|
184
|
-
// Find the span callback function (usually the last argument!)
|
|
185
|
-
const fnIndex = args.findIndex((arg) => typeof arg === "function");
|
|
186
|
-
if (fnIndex === -1) {
|
|
187
|
-
throw new Error(
|
|
188
|
-
"startActiveSpan requires a function as the last argument",
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// A type assertion is safe here due to the check above, but still sad 😥
|
|
193
|
-
const userFn = args[fnIndex] as (
|
|
194
|
-
span: Span,
|
|
195
|
-
...rest: unknown[]
|
|
196
|
-
) => unknown;
|
|
197
|
-
|
|
198
|
-
// Replace the function with one that wraps the span first
|
|
199
|
-
const spanWrapFunc = (...fnArgs: unknown[]) => {
|
|
200
|
-
const [span, ...rest] = fnArgs;
|
|
201
|
-
return userFn(createLangWatchSpan(span as Span), ...rest);
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const newArgs = [...args];
|
|
205
|
-
newArgs[fnIndex] = spanWrapFunc;
|
|
206
|
-
|
|
207
|
-
// TypeScript can't infer the overload, but this is safe
|
|
208
|
-
return (
|
|
209
|
-
target.startActiveSpan as unknown as (
|
|
210
|
-
...args: unknown[]
|
|
211
|
-
) => unknown
|
|
212
|
-
)(...newArgs);
|
|
213
|
-
};
|
|
214
|
-
return startActiveSpan;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
case "startSpan": {
|
|
218
|
-
return function (
|
|
219
|
-
...args: Parameters<Tracer["startSpan"]>
|
|
220
|
-
): ReturnType<Tracer["startSpan"]> {
|
|
221
|
-
const span = target.startSpan(...args);
|
|
222
|
-
return createLangWatchSpan(span);
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
case "withActiveSpan": {
|
|
227
|
-
/**
|
|
228
|
-
* Implementation of withActiveSpan: supports all overloads like startActiveSpan.
|
|
229
|
-
* Uses startActiveSpan to ensure context propagation for nested spans.
|
|
230
|
-
*/
|
|
231
|
-
return async function withActiveSpan(...args: any[]): Promise<any> {
|
|
232
|
-
// Find the function argument (should be the last argument)
|
|
233
|
-
const fnIndex = args.findIndex((arg) => typeof arg === "function");
|
|
234
|
-
if (fnIndex === -1) {
|
|
235
|
-
throw new Error("withActiveSpan requires a function as the last argument");
|
|
236
|
-
}
|
|
237
|
-
const userFn = args[fnIndex] as (span: LangWatchSpan) => Promise<any> | any;
|
|
238
|
-
// The preceding arguments are: name, options?, context?
|
|
239
|
-
const name = args[0];
|
|
240
|
-
const options = args.length > 2 ? args[1] : undefined;
|
|
241
|
-
const context = args.length > 3 ? args[2] : undefined;
|
|
242
|
-
|
|
243
|
-
return await new Promise((resolve, reject) => {
|
|
244
|
-
// Use startActiveSpan to ensure context propagation
|
|
245
|
-
const cb = async (span: Span) => {
|
|
246
|
-
const wrappedSpan = createLangWatchSpan(span);
|
|
247
|
-
try {
|
|
248
|
-
resolve(await userFn(wrappedSpan));
|
|
249
|
-
} catch (err: any) {
|
|
250
|
-
wrappedSpan.setStatus({
|
|
251
|
-
code: SpanStatusCode.ERROR,
|
|
252
|
-
message: err && err.message ? err.message : String(err),
|
|
253
|
-
});
|
|
254
|
-
wrappedSpan.recordException(err);
|
|
255
|
-
reject(err);
|
|
256
|
-
} finally {
|
|
257
|
-
wrappedSpan.end();
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
// Call the correct overload of startActiveSpan
|
|
261
|
-
if (context !== undefined) {
|
|
262
|
-
target.startActiveSpan(name, options, context, cb);
|
|
263
|
-
} else if (options !== undefined) {
|
|
264
|
-
target.startActiveSpan(name, options, cb);
|
|
265
|
-
} else {
|
|
266
|
-
target.startActiveSpan(name, cb);
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
default: {
|
|
273
|
-
const value = target[prop as keyof Tracer];
|
|
274
|
-
return typeof value === "function" ? value.bind(target) : value;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
},
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
return new Proxy(tracer, handler) as LangWatchTracer;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Helper type for the function overloads of startActiveSpan.
|
|
285
|
-
*
|
|
286
|
-
* This matches OpenTelemetry's Tracer interface and is used internally for type safety.
|
|
287
|
-
*/
|
|
288
|
-
type StartActiveSpanOverloads = {
|
|
289
|
-
<F extends (span: Span) => unknown>(name: string, fn: F): ReturnType<F>;
|
|
290
|
-
<F extends (span: Span) => unknown>(
|
|
291
|
-
name: string,
|
|
292
|
-
options: SpanOptions,
|
|
293
|
-
fn: F,
|
|
294
|
-
): ReturnType<F>;
|
|
295
|
-
<F extends (span: Span) => unknown>(
|
|
296
|
-
name: string,
|
|
297
|
-
options: SpanOptions,
|
|
298
|
-
context: Context,
|
|
299
|
-
fn: F,
|
|
300
|
-
): ReturnType<F>;
|
|
301
|
-
};
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterEach, vi } from "vitest";
|
|
2
|
-
import { PromptService } from "../service";
|
|
3
|
-
import { Prompt, PromptCompilationError } from "../prompt";
|
|
4
|
-
import type { LangwatchApiClient } from "../../internal/api/client";
|
|
5
|
-
|
|
6
|
-
// Mock the client with proper Vitest mock methods
|
|
7
|
-
const mockClient = {
|
|
8
|
-
GET: vi.fn(),
|
|
9
|
-
POST: vi.fn(),
|
|
10
|
-
PUT: vi.fn(),
|
|
11
|
-
DELETE: vi.fn(),
|
|
12
|
-
} as unknown as LangwatchApiClient & {
|
|
13
|
-
GET: ReturnType<typeof vi.fn>;
|
|
14
|
-
POST: ReturnType<typeof vi.fn>;
|
|
15
|
-
PUT: ReturnType<typeof vi.fn>;
|
|
16
|
-
DELETE: ReturnType<typeof vi.fn>;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// Mock the createLangWatchApiClient function
|
|
20
|
-
vi.mock("../../internal/api/client", () => ({
|
|
21
|
-
createLangWatchApiClient: vi.fn(() => mockClient),
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
|
-
describe("Prompt", () => {
|
|
25
|
-
let promptService: PromptService;
|
|
26
|
-
|
|
27
|
-
beforeAll(async () => {
|
|
28
|
-
promptService = new PromptService({ client: mockClient });
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
afterEach(() => {
|
|
32
|
-
vi.clearAllMocks();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("should fetch and compile a prompt", async () => {
|
|
36
|
-
// Mock the API response
|
|
37
|
-
const mockPromptData = {
|
|
38
|
-
id: "prompt_123",
|
|
39
|
-
name: "Test Prompt",
|
|
40
|
-
prompt: "Hello {{user_name}}, how is the {{topic}} today?",
|
|
41
|
-
messages: [
|
|
42
|
-
{
|
|
43
|
-
role: "user",
|
|
44
|
-
content: "Tell me about {{topic}}",
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
model: "gpt-4",
|
|
48
|
-
version: 1,
|
|
49
|
-
updatedAt: "2024-01-01T00:00:00Z",
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
mockClient.GET.mockResolvedValueOnce({
|
|
53
|
-
data: mockPromptData,
|
|
54
|
-
error: null,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const prompt = await promptService.get("prompt_123");
|
|
58
|
-
|
|
59
|
-
expect(prompt.id).toBe("prompt_123");
|
|
60
|
-
expect(prompt.name).toBe("Test Prompt");
|
|
61
|
-
|
|
62
|
-
// Test template compilation
|
|
63
|
-
const compiled = prompt.compile({
|
|
64
|
-
user_name: "Alice",
|
|
65
|
-
topic: "weather",
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
expect(compiled.prompt).toContain("Alice");
|
|
69
|
-
expect(JSON.stringify(compiled.messages)).toContain("weather");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("should handle missing template variables gracefully", async () => {
|
|
73
|
-
// Mock the API response
|
|
74
|
-
const mockPromptData = {
|
|
75
|
-
id: "prompt_123",
|
|
76
|
-
name: "Test Prompt",
|
|
77
|
-
prompt: "Hello {{user_name}}, how is the {{topic}} today?",
|
|
78
|
-
messages: [
|
|
79
|
-
{
|
|
80
|
-
role: "user",
|
|
81
|
-
content: "Tell me about {{topic}}",
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
model: "gpt-4",
|
|
85
|
-
version: 1,
|
|
86
|
-
updatedAt: "2024-01-01T00:00:00Z",
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
mockClient.GET.mockResolvedValueOnce({
|
|
90
|
-
data: mockPromptData,
|
|
91
|
-
error: null,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
const prompt = await promptService.get("prompt_123");
|
|
95
|
-
|
|
96
|
-
// Lenient compilation should not throw and should replace missing variables with empty strings
|
|
97
|
-
const compiled = prompt.compile({ user_name: "Alice", topic: "weather" });
|
|
98
|
-
expect(compiled).toBeInstanceOf(Prompt);
|
|
99
|
-
expect(compiled.prompt).toBe("Hello Alice, how is the weather today?");
|
|
100
|
-
expect(compiled.messages[0]?.content).toBe("Tell me about weather");
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("should throw on strict compilation with missing variables", async () => {
|
|
104
|
-
// Mock the API response
|
|
105
|
-
const mockPromptData = {
|
|
106
|
-
id: "prompt_123",
|
|
107
|
-
name: "Test Prompt",
|
|
108
|
-
prompt: "Hello {{user_name}}, how is the {{topic}} today?",
|
|
109
|
-
messages: [
|
|
110
|
-
{
|
|
111
|
-
role: "user",
|
|
112
|
-
content: "Tell me about {{topic}}",
|
|
113
|
-
},
|
|
114
|
-
],
|
|
115
|
-
model: "gpt-4",
|
|
116
|
-
version: 1,
|
|
117
|
-
updatedAt: "2024-01-01T00:00:00Z",
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
mockClient.GET.mockResolvedValueOnce({
|
|
121
|
-
data: mockPromptData,
|
|
122
|
-
error: null,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
const prompt = await promptService.get("prompt_123");
|
|
126
|
-
|
|
127
|
-
expect(() => {
|
|
128
|
-
prompt.compileStrict({ });
|
|
129
|
-
}).toThrow(PromptCompilationError);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it.todo("should create a prompt");
|
|
133
|
-
it.todo("should update a prompt");
|
|
134
|
-
it.todo("should delete a prompt");
|
|
135
|
-
it.todo("should create a prompt version");
|
|
136
|
-
it.todo("should get a prompt version");
|
|
137
|
-
it.todo("should list prompt versions");
|
|
138
|
-
it.todo("should delete a prompt version");
|
|
139
|
-
});
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { PromptService } from "./service";
|
|
2
|
-
import { CompiledPrompt, Prompt, TemplateVariables } from "./prompt";
|
|
3
|
-
import * as intSemconv from "../observability/semconv";
|
|
4
|
-
import { tracer } from "./tracer";
|
|
5
|
-
import { canAutomaticallyCaptureInput, canAutomaticallyCaptureOutput } from "../client";
|
|
6
|
-
|
|
7
|
-
export async function getPromptVersion(id: string, versionId: string, variables: TemplateVariables): Promise<CompiledPrompt>;
|
|
8
|
-
export async function getPromptVersion(id: string, versionId: string): Promise<Prompt>;
|
|
9
|
-
|
|
10
|
-
export async function getPromptVersion(id: string, versionId: string, variables?: TemplateVariables): Promise<Prompt | CompiledPrompt> {
|
|
11
|
-
return tracer.withActiveSpan("retrieve prompt version", async (span) => {
|
|
12
|
-
span.setType("prompt");
|
|
13
|
-
span.setAttribute(intSemconv.ATTR_LANGWATCH_PROMPT_ID, id);
|
|
14
|
-
|
|
15
|
-
const service = PromptService.getInstance();
|
|
16
|
-
const prompt = await service.getVersions(id);
|
|
17
|
-
const promptVersion = prompt[versionId];
|
|
18
|
-
|
|
19
|
-
if (!promptVersion) {
|
|
20
|
-
throw new Error(`Prompt version ${versionId} not found for prompt ${id}`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (canAutomaticallyCaptureOutput()) {
|
|
24
|
-
span.setOutput(prompt);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
span.setAttributes({
|
|
28
|
-
[intSemconv.ATTR_LANGWATCH_PROMPT_ID]: id,
|
|
29
|
-
[intSemconv.ATTR_LANGWATCH_PROMPT_VERSION_ID]: promptVersion.id,
|
|
30
|
-
[intSemconv.ATTR_LANGWATCH_PROMPT_VERSION_NUMBER]: promptVersion.version,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
if (variables) {
|
|
34
|
-
if (canAutomaticallyCaptureInput()) {
|
|
35
|
-
span.setAttribute(
|
|
36
|
-
intSemconv.ATTR_LANGWATCH_PROMPT_VARIABLES,
|
|
37
|
-
JSON.stringify({
|
|
38
|
-
type: "json",
|
|
39
|
-
value: variables,
|
|
40
|
-
}),
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return promptVersion.compile(variables);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return promptVersion;
|
|
48
|
-
});
|
|
49
|
-
}
|
package/src/prompt/get-prompt.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { PromptService } from "./service";
|
|
2
|
-
import { CompiledPrompt, Prompt, TemplateVariables } from "./prompt";
|
|
3
|
-
import * as intSemconv from "../observability/semconv";
|
|
4
|
-
import { tracer } from "./tracer";
|
|
5
|
-
import { canAutomaticallyCaptureInput, canAutomaticallyCaptureOutput } from "../client";
|
|
6
|
-
|
|
7
|
-
export async function getPrompt(id: string, variables: TemplateVariables): Promise<CompiledPrompt>;
|
|
8
|
-
export async function getPrompt(id: string): Promise<Prompt>;
|
|
9
|
-
|
|
10
|
-
export async function getPrompt(id: string, variables?: TemplateVariables): Promise<Prompt | CompiledPrompt> {
|
|
11
|
-
return tracer.withActiveSpan("retrieve prompt", async (span) => {
|
|
12
|
-
span.setType("prompt");
|
|
13
|
-
span.setAttribute(intSemconv.ATTR_LANGWATCH_PROMPT_ID, id);
|
|
14
|
-
|
|
15
|
-
const service = PromptService.getInstance();
|
|
16
|
-
const prompt = await service.get(id);
|
|
17
|
-
|
|
18
|
-
if (canAutomaticallyCaptureOutput()) {
|
|
19
|
-
span.setOutput(prompt);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
span.setAttributes({
|
|
23
|
-
[intSemconv.ATTR_LANGWATCH_PROMPT_ID]: id,
|
|
24
|
-
[intSemconv.ATTR_LANGWATCH_PROMPT_VERSION_ID]: prompt.versionId,
|
|
25
|
-
[intSemconv.ATTR_LANGWATCH_PROMPT_VERSION_NUMBER]: prompt.version,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
if (variables) {
|
|
29
|
-
if (canAutomaticallyCaptureInput()) {
|
|
30
|
-
span.setAttribute(
|
|
31
|
-
intSemconv.ATTR_LANGWATCH_PROMPT_VARIABLES,
|
|
32
|
-
JSON.stringify({
|
|
33
|
-
type: "json",
|
|
34
|
-
value: variables,
|
|
35
|
-
}),
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return prompt.compile(variables);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return prompt;
|
|
43
|
-
});
|
|
44
|
-
}
|
package/src/prompt/index.ts
DELETED
package/src/prompt/prompt.ts
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { Liquid } from "liquidjs";
|
|
2
|
-
import type { paths } from "../internal/generated/openapi/api-client";
|
|
3
|
-
|
|
4
|
-
// Extract the prompt response type from OpenAPI schema
|
|
5
|
-
export type PromptResponse = NonNullable<
|
|
6
|
-
paths["/api/prompts/{id}"]["get"]["responses"]["200"]["content"]["application/json"]
|
|
7
|
-
>;
|
|
8
|
-
|
|
9
|
-
// Type for template variables - supporting common data types
|
|
10
|
-
export type TemplateVariables = Record<
|
|
11
|
-
string,
|
|
12
|
-
string | number | boolean | object | null
|
|
13
|
-
>;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Error class for template compilation issues
|
|
17
|
-
*/
|
|
18
|
-
export class PromptCompilationError extends Error {
|
|
19
|
-
constructor(
|
|
20
|
-
message: string,
|
|
21
|
-
public readonly template: string,
|
|
22
|
-
public readonly originalError?: any
|
|
23
|
-
) {
|
|
24
|
-
super(message);
|
|
25
|
-
this.name = "PromptCompilationError";
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Global Liquid instance - shared across all prompts for efficiency
|
|
30
|
-
const liquid = new Liquid({
|
|
31
|
-
strictFilters: true,
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
export class Prompt implements PromptResponse {
|
|
35
|
-
public readonly id!: string;
|
|
36
|
-
public readonly name!: string;
|
|
37
|
-
public readonly updatedAt!: string;
|
|
38
|
-
public readonly version!: number;
|
|
39
|
-
public readonly versionId!: string;
|
|
40
|
-
public readonly versionCreatedAt!: string;
|
|
41
|
-
public readonly model!: string;
|
|
42
|
-
public readonly prompt!: string;
|
|
43
|
-
public readonly messages!: PromptResponse["messages"];
|
|
44
|
-
public readonly response_format!: PromptResponse["response_format"];
|
|
45
|
-
|
|
46
|
-
constructor(promptData: PromptResponse) {
|
|
47
|
-
this.id = promptData.id;
|
|
48
|
-
this.name = promptData.name;
|
|
49
|
-
this.updatedAt = promptData.updatedAt;
|
|
50
|
-
this.version = promptData.version;
|
|
51
|
-
this.versionId = promptData.versionId;
|
|
52
|
-
this.versionCreatedAt = promptData.versionCreatedAt;
|
|
53
|
-
this.model = promptData.model;
|
|
54
|
-
this.prompt = promptData.prompt;
|
|
55
|
-
this.messages = promptData.messages;
|
|
56
|
-
this.response_format = promptData.response_format;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Get the raw prompt data from the API
|
|
61
|
-
*/
|
|
62
|
-
get raw(): PromptResponse {
|
|
63
|
-
return this;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Compile the prompt template with provided variables (lenient - missing variables become empty)
|
|
68
|
-
* @param variables - Object containing variable values for template compilation
|
|
69
|
-
* @returns CompiledPrompt instance with compiled content
|
|
70
|
-
*/
|
|
71
|
-
private _compile(variables: TemplateVariables, strict: boolean): CompiledPrompt {
|
|
72
|
-
try {
|
|
73
|
-
// Compile main prompt
|
|
74
|
-
const compiledPrompt = this.prompt
|
|
75
|
-
? liquid.parseAndRenderSync(this.prompt, variables, {
|
|
76
|
-
strictVariables: strict,
|
|
77
|
-
})
|
|
78
|
-
: "";
|
|
79
|
-
|
|
80
|
-
// Compile messages
|
|
81
|
-
const compiledMessages = (this.messages || []).map((message) => ({
|
|
82
|
-
...message,
|
|
83
|
-
content: message.content
|
|
84
|
-
? liquid.parseAndRenderSync(message.content, variables, {
|
|
85
|
-
strictVariables: strict,
|
|
86
|
-
})
|
|
87
|
-
: message.content,
|
|
88
|
-
}));
|
|
89
|
-
|
|
90
|
-
// Create new prompt data with compiled content
|
|
91
|
-
const compiledData: PromptResponse = {
|
|
92
|
-
...this,
|
|
93
|
-
prompt: compiledPrompt,
|
|
94
|
-
messages: compiledMessages,
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
return new CompiledPrompt(compiledData, this);
|
|
98
|
-
} catch (error) {
|
|
99
|
-
const templateStr = this.prompt || JSON.stringify(this.messages);
|
|
100
|
-
throw new PromptCompilationError(
|
|
101
|
-
`Failed to compile prompt template: ${error instanceof Error ? error.message : "Unknown error"
|
|
102
|
-
}`,
|
|
103
|
-
templateStr,
|
|
104
|
-
error
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
compile(variables: TemplateVariables = {}): CompiledPrompt {
|
|
110
|
-
return this._compile(variables, false);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Compile with validation - throws error if required variables are missing
|
|
115
|
-
* @param variables - Template variables
|
|
116
|
-
* @returns CompiledPrompt instance with compiled content
|
|
117
|
-
*/
|
|
118
|
-
compileStrict(variables: TemplateVariables): CompiledPrompt {
|
|
119
|
-
return this._compile(variables, true);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Represents a compiled prompt that extends Prompt with reference to the original template
|
|
125
|
-
*/
|
|
126
|
-
export class CompiledPrompt extends Prompt {
|
|
127
|
-
constructor(
|
|
128
|
-
compiledData: PromptResponse,
|
|
129
|
-
public readonly original: Prompt
|
|
130
|
-
) {
|
|
131
|
-
super(compiledData);
|
|
132
|
-
}
|
|
133
|
-
}
|