langwatch 0.0.2 → 0.0.3

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/src/index.ts CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  type RAGSpan,
25
25
  type SpanInputOutput,
26
26
  } from "./types";
27
- import { convertFromVercelAIMessages } from "./utils";
27
+ import { captureError, convertFromVercelAIMessages } from "./utils";
28
28
 
29
29
  export type {
30
30
  BaseSpan,
@@ -39,7 +39,7 @@ export type {
39
39
  SpanInputOutput,
40
40
  };
41
41
 
42
- export { convertFromVercelAIMessages };
42
+ export { convertFromVercelAIMessages, captureError };
43
43
 
44
44
  export class LangWatch extends EventEmitter {
45
45
  apiKey: string | undefined;
@@ -55,9 +55,10 @@ export class LangWatch extends EventEmitter {
55
55
  super();
56
56
  const apiKey_ = apiKey ?? process.env.LANGWATCH_API_KEY;
57
57
  if (!apiKey_) {
58
- console.warn(
59
- "[LangWatch] ⚠️ LangWatch API key is not set, please set the LANGWATCH_API_KEY environment variable or pass it in the constructor. Traces will not be captured."
58
+ const error = new Error(
59
+ "LangWatch API key is not set, please set the LANGWATCH_API_KEY environment variable or pass it in the constructor. Traces will not be captured."
60
60
  );
61
+ this.emit("error", error);
61
62
  }
62
63
  this.apiKey = apiKey_;
63
64
  this.endpoint = endpoint;
@@ -99,10 +100,9 @@ export class LangWatch extends EventEmitter {
99
100
 
100
101
  if (!this.apiKey) {
101
102
  const error = new Error(
102
- "[LangWatch] ⚠️ LangWatch API key is not set, LLMs traces will not be sent, go to https://langwatch.ai to set it up"
103
+ "LangWatch API key is not set, LLMs traces will not be sent, go to https://langwatch.ai to set it up"
103
104
  );
104
105
  this.emit("error", error);
105
- console.warn(error.message);
106
106
  return;
107
107
  }
108
108
 
@@ -117,15 +117,14 @@ export class LangWatch extends EventEmitter {
117
117
 
118
118
  if (response.status === 429) {
119
119
  const error = new Error(
120
- "[LangWatch] ⚠️ Rate limit exceeded, dropping message from being sent to LangWatch. Please check your dashboard to upgrade your plan."
120
+ "Rate limit exceeded, dropping message from being sent to LangWatch. Please check your dashboard to upgrade your plan."
121
121
  );
122
122
  this.emit("error", error);
123
- console.warn(error.message);
124
123
  return;
125
124
  }
126
125
  if (!response.ok) {
127
126
  const error = new Error(
128
- `[LangWatch] ⚠️ Failed to send trace, status: ${response.status}`
127
+ `Failed to send trace, status: ${response.status}`
129
128
  );
130
129
  this.emit("error", error);
131
130
  throw error;
@@ -214,8 +213,6 @@ export class LangWatchTrace {
214
213
  if (error instanceof ZodError) {
215
214
  console.warn("[LangWatch] ⚠️ Failed to parse trace");
216
215
  console.warn(fromZodError(error).message);
217
- } else {
218
- console.warn(error);
219
216
  }
220
217
  this.client.emit("error", error);
221
218
  }
@@ -266,6 +263,14 @@ export class LangWatchSpan implements PendingBaseSpan {
266
263
  }
267
264
 
268
265
  update(params: Partial<Omit<PendingBaseSpan, "spanId" | "parentId">>) {
266
+ if (Object.isFrozen(this)) {
267
+ const error = new Error(
268
+ `Tried to update span ${this.spanId}, but the span is already finished, discarding update`
269
+ );
270
+ this.trace.client.emit("error", error);
271
+ return;
272
+ }
273
+
269
274
  if (params.type) {
270
275
  this.type = params.type;
271
276
  }
@@ -322,6 +327,8 @@ export class LangWatchSpan implements PendingBaseSpan {
322
327
  this.update(params);
323
328
  }
324
329
 
330
+ Object.freeze(this);
331
+
325
332
  try {
326
333
  const finalSpan = spanSchema.parse(
327
334
  camelToSnakeCaseNested({
@@ -332,6 +339,7 @@ export class LangWatchSpan implements PendingBaseSpan {
332
339
  ...this.timestamps,
333
340
  finishedAt: this.timestamps.finishedAt,
334
341
  },
342
+ ...(this.error && { error: captureError(this.error) }),
335
343
  }) as ServerSpan
336
344
  );
337
345
  this.trace.onEnd(finalSpan);
@@ -339,8 +347,6 @@ export class LangWatchSpan implements PendingBaseSpan {
339
347
  if (error instanceof ZodError) {
340
348
  console.warn("[LangWatch] ⚠️ Failed to parse span");
341
349
  console.warn(fromZodError(error).message);
342
- } else {
343
- console.warn(error);
344
350
  }
345
351
  this.trace.client.emit("error", error);
346
352
  }
package/src/types.ts CHANGED
@@ -41,9 +41,10 @@ export type SpanInputOutput =
41
41
  | (TypedValueChatMessages & { type: ChatMessage });
42
42
 
43
43
  export type ConvertServerSpan<T extends ServerBaseSpan> =
44
- SnakeToCamelCaseNested<Omit<T, "input" | "outputs">> & {
44
+ SnakeToCamelCaseNested<Omit<T, "input" | "outputs" | "error">> & {
45
45
  input?: SpanInputOutput | null;
46
46
  outputs: SpanInputOutput[];
47
+ error?: T["error"] | NonNullable<unknown>;
47
48
  };
48
49
 
49
50
  export type PendingSpan<T extends BaseSpan> = Omit<
package/src/utils.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { convertUint8ArrayToBase64 } from "@ai-sdk/provider-utils";
2
2
  import { type ImagePart, type CoreMessage } from "ai";
3
3
  import { type ChatMessage } from "./types";
4
+ import { type ErrorCapture } from "./server/types/tracer";
4
5
 
5
6
  const convertImageToUrl = (
6
7
  image: ImagePart["image"],
@@ -132,3 +133,47 @@ export function convertFromVercelAIMessages(
132
133
 
133
134
  return lwMessages;
134
135
  }
136
+
137
+ export const captureError = (error: unknown): ErrorCapture => {
138
+ if (
139
+ error &&
140
+ typeof error === "object" &&
141
+ "has_error" in error &&
142
+ "message" in error &&
143
+ "stacktrace" in error
144
+ ) {
145
+ return error as ErrorCapture;
146
+ } else if (error instanceof Error) {
147
+ return {
148
+ has_error: true,
149
+ message: error.message,
150
+ stacktrace: error.stack ? error.stack.split("\n") : [],
151
+ };
152
+ } else if (typeof error === "object" && error !== null) {
153
+ const err = error as { message: unknown; stack: unknown };
154
+ const message =
155
+ typeof err.message === "string"
156
+ ? err.message
157
+ : "An unknown error occurred";
158
+ const stacktrace =
159
+ typeof err.stack === "string"
160
+ ? err.stack.split("\n")
161
+ : Array.isArray(err.stack) &&
162
+ err.stack.length > 0 &&
163
+ typeof err.stack[0] === "string"
164
+ ? err.stack
165
+ : ["No stack trace available"];
166
+ return {
167
+ has_error: true,
168
+ message,
169
+ stacktrace,
170
+ };
171
+ } else {
172
+ // Handle primitives and other types that are not an error object
173
+ return {
174
+ has_error: true,
175
+ message: String(error),
176
+ stacktrace: [],
177
+ };
178
+ }
179
+ };