langwatch 0.2.0 → 0.3.0-prerelease.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.
Files changed (235) hide show
  1. package/.editorconfig +16 -0
  2. package/LICENSE +7 -0
  3. package/README.md +268 -1
  4. package/copy-types.sh +19 -8
  5. package/examples/langchain/.env.example +2 -0
  6. package/examples/langchain/README.md +42 -0
  7. package/examples/langchain/package-lock.json +2930 -0
  8. package/examples/langchain/package.json +27 -0
  9. package/examples/langchain/src/cli-markdown.d.ts +137 -0
  10. package/examples/langchain/src/index.ts +109 -0
  11. package/examples/langchain/tsconfig.json +25 -0
  12. package/examples/langgraph/.env.example +2 -0
  13. package/examples/langgraph/README.md +42 -0
  14. package/examples/langgraph/package-lock.json +3031 -0
  15. package/examples/langgraph/package.json +28 -0
  16. package/examples/langgraph/src/cli-markdown.d.ts +137 -0
  17. package/examples/langgraph/src/index.ts +196 -0
  18. package/examples/langgraph/tsconfig.json +25 -0
  19. package/examples/mastra/.env.example +2 -0
  20. package/examples/mastra/README.md +57 -0
  21. package/examples/mastra/package-lock.json +5296 -0
  22. package/examples/mastra/package.json +32 -0
  23. package/examples/mastra/src/cli-markdown.d.ts +137 -0
  24. package/examples/mastra/src/index.ts +120 -0
  25. package/examples/mastra/src/mastra/agents/weather-agent.ts +30 -0
  26. package/examples/mastra/src/mastra/index.ts +21 -0
  27. package/examples/mastra/src/mastra/tools/weather-tool.ts +102 -0
  28. package/examples/mastra/tsconfig.json +25 -0
  29. package/examples/vercel-ai/.env.example +2 -0
  30. package/examples/vercel-ai/README.md +38 -0
  31. package/examples/vercel-ai/package-lock.json +2571 -0
  32. package/examples/vercel-ai/package.json +27 -0
  33. package/examples/vercel-ai/src/cli-markdown.d.ts +137 -0
  34. package/examples/vercel-ai/src/index.ts +110 -0
  35. package/examples/vercel-ai/src/instrumentation.ts +9 -0
  36. package/examples/vercel-ai/tsconfig.json +25 -0
  37. package/package.json +78 -34
  38. package/src/__tests__/client-browser.test.ts +92 -0
  39. package/src/__tests__/client-node.test.ts +76 -0
  40. package/src/__tests__/client.test.ts +71 -0
  41. package/src/__tests__/integration/client-browser.test.ts +46 -0
  42. package/src/__tests__/integration/client-node.test.ts +46 -0
  43. package/src/client-browser.ts +70 -0
  44. package/src/client-node.ts +82 -0
  45. package/src/client-shared.ts +72 -0
  46. package/src/client.ts +119 -0
  47. package/src/evaluation/__tests__/record-evaluation.test.ts +112 -0
  48. package/src/evaluation/__tests__/run-evaluation.test.ts +171 -0
  49. package/src/evaluation/index.ts +2 -0
  50. package/src/evaluation/record-evaluation.ts +101 -0
  51. package/src/evaluation/run-evaluation.ts +133 -0
  52. package/src/evaluation/tracer.ts +3 -0
  53. package/src/evaluation/types.ts +23 -0
  54. package/src/index.ts +10 -593
  55. package/src/internal/api/__tests__/errors.test.ts +98 -0
  56. package/src/internal/api/client.ts +30 -0
  57. package/src/internal/api/errors.ts +32 -0
  58. package/src/internal/generated/types/.gitkeep +0 -0
  59. package/src/observability/__tests__/integration/base.test.ts +74 -0
  60. package/src/observability/__tests__/integration/browser-setup-ordering.test.ts +60 -0
  61. package/src/observability/__tests__/integration/complex-nested-spans.test.ts +29 -0
  62. package/src/observability/__tests__/integration/error-handling.test.ts +24 -0
  63. package/src/observability/__tests__/integration/langwatch-disabled-otel.test.ts +24 -0
  64. package/src/observability/__tests__/integration/langwatch-first-then-vercel.test.ts +24 -0
  65. package/src/observability/__tests__/integration/multiple-setup-attempts.test.ts +27 -0
  66. package/src/observability/__tests__/integration/otel-ordering.test.ts +27 -0
  67. package/src/observability/__tests__/integration/vercel-configurations.test.ts +20 -0
  68. package/src/observability/__tests__/integration/vercel-first-then-langwatch.test.ts +27 -0
  69. package/src/observability/__tests__/span.test.ts +214 -0
  70. package/src/observability/__tests__/trace.test.ts +180 -0
  71. package/src/observability/exporters/index.ts +1 -0
  72. package/src/observability/exporters/langwatch-exporter.ts +53 -0
  73. package/src/observability/index.ts +4 -0
  74. package/src/observability/instrumentation/langchain/__tests__/integration/langchain-chatbot.test.ts +112 -0
  75. package/src/observability/instrumentation/langchain/__tests__/langchain.test.ts +284 -0
  76. package/src/observability/instrumentation/langchain/index.ts +624 -0
  77. package/src/observability/processors/__tests__/filterable-batch-span-exporter.test.ts +98 -0
  78. package/src/observability/processors/filterable-batch-span-processor.ts +99 -0
  79. package/src/observability/processors/index.ts +1 -0
  80. package/src/observability/semconv/attributes.ts +185 -0
  81. package/src/observability/semconv/events.ts +42 -0
  82. package/src/observability/semconv/index.ts +16 -0
  83. package/src/observability/semconv/values.ts +159 -0
  84. package/src/observability/span.ts +728 -0
  85. package/src/observability/trace.ts +301 -0
  86. package/src/prompt/__tests__/prompt.test.ts +139 -0
  87. package/src/prompt/get-prompt-version.ts +49 -0
  88. package/src/prompt/get-prompt.ts +44 -0
  89. package/src/prompt/index.ts +3 -0
  90. package/src/prompt/prompt.ts +133 -0
  91. package/src/prompt/service.ts +221 -0
  92. package/src/prompt/tracer.ts +3 -0
  93. package/src/prompt/types.ts +0 -0
  94. package/ts-to-zod.config.js +11 -0
  95. package/tsconfig.json +3 -9
  96. package/tsup.config.ts +11 -1
  97. package/vitest.config.ts +1 -0
  98. package/dist/chunk-LKD2K67J.mjs +0 -717
  99. package/dist/chunk-LKD2K67J.mjs.map +0 -1
  100. package/dist/index.d.mts +0 -1030
  101. package/dist/index.d.ts +0 -1030
  102. package/dist/index.js +0 -27310
  103. package/dist/index.js.map +0 -1
  104. package/dist/index.mjs +0 -963
  105. package/dist/index.mjs.map +0 -1
  106. package/dist/utils-Cv-rUjJ1.d.mts +0 -313
  107. package/dist/utils-Cv-rUjJ1.d.ts +0 -313
  108. package/dist/utils.d.mts +0 -2
  109. package/dist/utils.d.ts +0 -2
  110. package/dist/utils.js +0 -709
  111. package/dist/utils.js.map +0 -1
  112. package/dist/utils.mjs +0 -11
  113. package/dist/utils.mjs.map +0 -1
  114. package/example/.env.example +0 -12
  115. package/example/.eslintrc.json +0 -26
  116. package/example/LICENSE +0 -13
  117. package/example/README.md +0 -12
  118. package/example/app/(chat)/chat/[id]/page.tsx +0 -60
  119. package/example/app/(chat)/layout.tsx +0 -14
  120. package/example/app/(chat)/page.tsx +0 -27
  121. package/example/app/actions.ts +0 -156
  122. package/example/app/globals.css +0 -76
  123. package/example/app/guardrails/page.tsx +0 -26
  124. package/example/app/langchain/page.tsx +0 -27
  125. package/example/app/langchain-rag/page.tsx +0 -28
  126. package/example/app/late-update/page.tsx +0 -27
  127. package/example/app/layout.tsx +0 -64
  128. package/example/app/login/actions.ts +0 -71
  129. package/example/app/login/page.tsx +0 -18
  130. package/example/app/manual/page.tsx +0 -27
  131. package/example/app/new/page.tsx +0 -5
  132. package/example/app/opengraph-image.png +0 -0
  133. package/example/app/share/[id]/page.tsx +0 -58
  134. package/example/app/signup/actions.ts +0 -111
  135. package/example/app/signup/page.tsx +0 -18
  136. package/example/app/twitter-image.png +0 -0
  137. package/example/auth.config.ts +0 -42
  138. package/example/auth.ts +0 -45
  139. package/example/components/button-scroll-to-bottom.tsx +0 -36
  140. package/example/components/chat-history.tsx +0 -49
  141. package/example/components/chat-list.tsx +0 -52
  142. package/example/components/chat-message-actions.tsx +0 -40
  143. package/example/components/chat-message.tsx +0 -80
  144. package/example/components/chat-panel.tsx +0 -139
  145. package/example/components/chat-share-dialog.tsx +0 -95
  146. package/example/components/chat.tsx +0 -84
  147. package/example/components/clear-history.tsx +0 -75
  148. package/example/components/empty-screen.tsx +0 -38
  149. package/example/components/external-link.tsx +0 -29
  150. package/example/components/footer.tsx +0 -19
  151. package/example/components/header.tsx +0 -114
  152. package/example/components/login-button.tsx +0 -42
  153. package/example/components/login-form.tsx +0 -97
  154. package/example/components/markdown.tsx +0 -9
  155. package/example/components/prompt-form.tsx +0 -115
  156. package/example/components/providers.tsx +0 -17
  157. package/example/components/sidebar-actions.tsx +0 -125
  158. package/example/components/sidebar-desktop.tsx +0 -19
  159. package/example/components/sidebar-footer.tsx +0 -16
  160. package/example/components/sidebar-item.tsx +0 -124
  161. package/example/components/sidebar-items.tsx +0 -42
  162. package/example/components/sidebar-list.tsx +0 -38
  163. package/example/components/sidebar-mobile.tsx +0 -31
  164. package/example/components/sidebar-toggle.tsx +0 -24
  165. package/example/components/sidebar.tsx +0 -21
  166. package/example/components/signup-form.tsx +0 -95
  167. package/example/components/stocks/events-skeleton.tsx +0 -31
  168. package/example/components/stocks/events.tsx +0 -30
  169. package/example/components/stocks/index.tsx +0 -36
  170. package/example/components/stocks/message.tsx +0 -134
  171. package/example/components/stocks/spinner.tsx +0 -16
  172. package/example/components/stocks/stock-purchase.tsx +0 -146
  173. package/example/components/stocks/stock-skeleton.tsx +0 -22
  174. package/example/components/stocks/stock.tsx +0 -210
  175. package/example/components/stocks/stocks-skeleton.tsx +0 -9
  176. package/example/components/stocks/stocks.tsx +0 -67
  177. package/example/components/tailwind-indicator.tsx +0 -14
  178. package/example/components/theme-toggle.tsx +0 -31
  179. package/example/components/ui/alert-dialog.tsx +0 -141
  180. package/example/components/ui/badge.tsx +0 -36
  181. package/example/components/ui/button.tsx +0 -57
  182. package/example/components/ui/codeblock.tsx +0 -148
  183. package/example/components/ui/dialog.tsx +0 -122
  184. package/example/components/ui/dropdown-menu.tsx +0 -205
  185. package/example/components/ui/icons.tsx +0 -507
  186. package/example/components/ui/input.tsx +0 -25
  187. package/example/components/ui/label.tsx +0 -26
  188. package/example/components/ui/select.tsx +0 -164
  189. package/example/components/ui/separator.tsx +0 -31
  190. package/example/components/ui/sheet.tsx +0 -140
  191. package/example/components/ui/sonner.tsx +0 -31
  192. package/example/components/ui/switch.tsx +0 -29
  193. package/example/components/ui/textarea.tsx +0 -24
  194. package/example/components/ui/tooltip.tsx +0 -30
  195. package/example/components/user-menu.tsx +0 -53
  196. package/example/components.json +0 -17
  197. package/example/instrumentation.ts +0 -11
  198. package/example/lib/chat/guardrails.tsx +0 -181
  199. package/example/lib/chat/langchain-rag.tsx +0 -191
  200. package/example/lib/chat/langchain.tsx +0 -112
  201. package/example/lib/chat/late-update.tsx +0 -208
  202. package/example/lib/chat/manual.tsx +0 -605
  203. package/example/lib/chat/vercel-ai.tsx +0 -576
  204. package/example/lib/hooks/use-copy-to-clipboard.tsx +0 -33
  205. package/example/lib/hooks/use-enter-submit.tsx +0 -23
  206. package/example/lib/hooks/use-local-storage.ts +0 -24
  207. package/example/lib/hooks/use-scroll-anchor.tsx +0 -86
  208. package/example/lib/hooks/use-sidebar.tsx +0 -60
  209. package/example/lib/hooks/use-streamable-text.ts +0 -25
  210. package/example/lib/types.ts +0 -41
  211. package/example/lib/utils.ts +0 -89
  212. package/example/middleware.ts +0 -8
  213. package/example/next-env.d.ts +0 -5
  214. package/example/next.config.js +0 -16
  215. package/example/package-lock.json +0 -10917
  216. package/example/package.json +0 -84
  217. package/example/pnpm-lock.yaml +0 -5712
  218. package/example/postcss.config.js +0 -6
  219. package/example/prettier.config.cjs +0 -34
  220. package/example/public/apple-touch-icon.png +0 -0
  221. package/example/public/favicon-16x16.png +0 -0
  222. package/example/public/favicon.ico +0 -0
  223. package/example/public/next.svg +0 -1
  224. package/example/public/thirteen.svg +0 -1
  225. package/example/public/vercel.svg +0 -1
  226. package/example/tailwind.config.ts +0 -81
  227. package/example/tsconfig.json +0 -35
  228. package/src/LangWatchExporter.ts +0 -96
  229. package/src/evaluations.ts +0 -219
  230. package/src/index.test.ts +0 -402
  231. package/src/langchain.ts +0 -557
  232. package/src/typeUtils.ts +0 -89
  233. package/src/types.ts +0 -82
  234. package/src/utils.ts +0 -205
  235. /package/src/{server/types → internal/generated/openapi}/.gitkeep +0 -0
@@ -0,0 +1,728 @@
1
+ import {
2
+ Attributes,
3
+ AttributeValue,
4
+ Span,
5
+ SpanContext,
6
+ SpanStatus,
7
+ Link,
8
+ Exception
9
+ } from "@opentelemetry/api";
10
+ import semconv from "@opentelemetry/semantic-conventions/incubating";
11
+ import * as intSemconv from "./semconv";
12
+ import {
13
+ RecordedEvaluationDetails,
14
+ recordEvaluation,
15
+ } from "../evaluation/record-evaluation";
16
+ import { EvaluationResultModel } from "../evaluation/types";
17
+ import { Prompt } from "../prompt/prompt";
18
+
19
+ /**
20
+ * Supported types of spans for LangWatch observability. These types categorize the nature of the span for downstream analysis and visualization.
21
+ *
22
+ * @example
23
+ * import { spanTypes, SpanType } from './span';
24
+ * const myType: SpanType = 'llm';
25
+ */
26
+ export const spanTypes = [
27
+ "span",
28
+ "llm",
29
+ "chain",
30
+ "tool",
31
+ "agent",
32
+ "guardrail",
33
+ "evaluation",
34
+ "rag",
35
+ "prompt",
36
+ "workflow",
37
+ "component",
38
+ "module",
39
+ "server",
40
+ "client",
41
+ "producer",
42
+ "consumer",
43
+ "task",
44
+ "unknown",
45
+ ] as const;
46
+
47
+ export type SpanType = (typeof spanTypes)[number];
48
+
49
+ /**
50
+ * Context for a RAG (Retrieval-Augmented Generation) span.
51
+ *
52
+ * This structure is used to record which document and chunk were retrieved and used as context for a generation.
53
+ *
54
+ * @property document_id - Unique identifier for the source document.
55
+ * @property chunk_id - Unique identifier for the chunk within the document.
56
+ * @property content - The actual content of the chunk provided to the model.
57
+ *
58
+ * @example
59
+ * const ragContext: LangWatchSpanRAGContext = {
60
+ * document_id: 'doc-123',
61
+ * chunk_id: 'chunk-456',
62
+ * content: 'Relevant passage from the document.'
63
+ * };
64
+ */
65
+ export interface LangWatchSpanRAGContext {
66
+ document_id: string;
67
+ chunk_id: string;
68
+ content: string;
69
+ }
70
+
71
+ /**
72
+ * Metrics for a LangWatch span.
73
+ *
74
+ * @property promptTokens - The number of prompt tokens used.
75
+ * @property completionTokens - The number of completion tokens used.
76
+ * @property cost - The cost of the span.
77
+ */
78
+ export interface LangWatchSpanMetrics {
79
+ /** The number of prompt tokens used */
80
+ promptTokens?: number;
81
+ /** The number of completion tokens used */
82
+ completionTokens?: number;
83
+ /** The cost of the span */
84
+ cost?: number;
85
+ }
86
+
87
+ /**
88
+ * Body for a system message event in a GenAI span.
89
+ *
90
+ * Used to log system/instruction messages sent to the model.
91
+ *
92
+ * @property content - The message content.
93
+ * @property role - The role of the message, typically 'system' or 'instruction'.
94
+ *
95
+ * @example
96
+ * span.addGenAISystemMessageEvent({ content: 'You are a helpful assistant.' });
97
+ */
98
+ export interface LangWatchSpanGenAISystemMessageEventBody {
99
+ /** Content of the system message */
100
+ content?: string;
101
+ /** Role of the message (system or instruction) */
102
+ role?: "system" | "instruction";
103
+ }
104
+
105
+ /**
106
+ * Body for a user message event in a GenAI span.
107
+ *
108
+ * Used to log user/customer messages sent to the model.
109
+ *
110
+ * @property content - The message content.
111
+ * @property role - The role of the message, typically 'user' or 'customer'.
112
+ *
113
+ * @example
114
+ * span.addGenAIUserMessageEvent({ content: 'What is the weather today?' });
115
+ */
116
+ export interface LangWatchSpanGenAIUserMessageEventBody {
117
+ /** Content of the user message */
118
+ content?: string;
119
+ /** Role of the message (user or customer) */
120
+ role?: "user" | "customer";
121
+ }
122
+
123
+ /**
124
+ * Body for an assistant message event in a GenAI span.
125
+ *
126
+ * Used to log assistant/bot responses, including tool calls.
127
+ *
128
+ * @property content - The message content.
129
+ * @property role - The role of the message, typically 'assistant' or 'bot'.
130
+ * @property tool_calls - Array of tool call objects, if the assistant invoked tools/functions.
131
+ *
132
+ * @example
133
+ * span.addGenAIAssistantMessageEvent({ content: 'The weather is sunny.', role: 'assistant' });
134
+ */
135
+ export interface LangWatchSpanGenAIAssistantMessageEventBody {
136
+ /** Content of the assistant message */
137
+ content?: string;
138
+ /** Role of the message (assistant or bot) */
139
+ role?: "assistant" | "bot";
140
+ /** Tool calls made by the assistant */
141
+ tool_calls?: {
142
+ function: {
143
+ /** Name of the function called */
144
+ name: string;
145
+ /** Arguments passed to the function */
146
+ arguments?: string;
147
+ };
148
+ /** Tool call identifier */
149
+ id: string;
150
+ /** Type of tool call */
151
+ type: "function";
152
+ }[];
153
+ }
154
+
155
+ /**
156
+ * Body for a tool message event in a GenAI span.
157
+ *
158
+ * Used to log messages from tools/functions invoked by the assistant.
159
+ *
160
+ * @property content - The message content.
161
+ * @property id - Unique identifier for the tool call.
162
+ * @property role - The role, typically 'tool' or 'function'.
163
+ *
164
+ * @example
165
+ * span.addGenAIToolMessageEvent({ content: 'Result from tool', id: 'tool-1', role: 'tool' });
166
+ */
167
+ export interface LangWatchSpanGenAIToolMessageEventBody {
168
+ /** Content of the tool message */
169
+ content?: string;
170
+ /** Tool call identifier */
171
+ id: string;
172
+ /** Role of the message (tool or function) */
173
+ role?: "tool" | "function";
174
+ }
175
+
176
+ /**
177
+ * Body for a choice event in a GenAI span.
178
+ *
179
+ * Used to log the model's output choices, including finish reason and message content.
180
+ *
181
+ * @property finish_reason - Why the generation finished (e.g., 'stop', 'length').
182
+ * @property index - Index of the choice (for multi-choice outputs).
183
+ * @property message - The message content and tool calls for this choice.
184
+ *
185
+ * @example
186
+ * span.addGenAIChoiceEvent({ finish_reason: 'stop', index: 0, message: { content: 'Hello!' } });
187
+ */
188
+ export interface LangWatchSpanGenAIChoiceEventBody {
189
+ /** Reason the generation finished */
190
+ finish_reason: intSemconv.VAL_GEN_AI_FINISH_REASONS | (string & {});
191
+ /** Index of the choice */
192
+ index: number;
193
+ /** Message content for the choice */
194
+ message?: {
195
+ /** Content of the message */
196
+ content?: string;
197
+ /** Role of the message (assistant or bot) */
198
+ role?: "assistant" | "bot";
199
+ /** Tool calls made by the assistant */
200
+ tool_calls?: {
201
+ function: {
202
+ /** Name of the function called */
203
+ name: string;
204
+ /** Arguments passed to the function */
205
+ arguments?: string;
206
+ };
207
+ /** Tool call identifier */
208
+ id: string;
209
+ /** Type of tool call */
210
+ type: "function";
211
+ }[];
212
+ };
213
+ }
214
+
215
+ /**
216
+ * Extension of OpenTelemetry's Span with LangWatch-specific helpers for LLM, RAG, and GenAI tracing.
217
+ *
218
+ * This interface provides ergonomic methods for recording structured LLM/GenAI data, such as inputs, outputs, RAG contexts, and message events.
219
+ *
220
+ * All methods return `this` for chaining.
221
+ *
222
+ * @example
223
+ * const span = createLangWatchSpan(otelSpan);
224
+ * span
225
+ * .setType('llm')
226
+ * .setInput({ prompt: 'Hello' })
227
+ * .setOutput('Hi!')
228
+ * .addGenAIUserMessageEvent({ content: 'Hello' })
229
+ * .addGenAIAssistantMessageEvent({ content: 'Hi!' });
230
+ */
231
+ export interface LangWatchSpan extends Span {
232
+ /**
233
+ * Record the evaluation result for the span.
234
+ *
235
+ * @param details - The evaluation details
236
+ * @param attributes - Additional attributes to add to the evaluation span.
237
+ * @returns this
238
+ */
239
+ recordEvaluation(
240
+ details: RecordedEvaluationDetails,
241
+ attributes?: Attributes,
242
+ ): this;
243
+
244
+ /**
245
+ * Add a GenAI system message event to the span.
246
+ *
247
+ * This logs a system/instruction message sent to the model.
248
+ *
249
+ * @param body - The event body (content and role)
250
+ * @param system - The GenAI system (optional, e.g., 'openai', 'anthropic')
251
+ * @param attributes - Additional OpenTelemetry attributes (optional)
252
+ * @returns this
253
+ */
254
+ addGenAISystemMessageEvent(
255
+ body: LangWatchSpanGenAISystemMessageEventBody,
256
+ system?: intSemconv.VAL_GEN_AI_SYSTEMS | (string & {}),
257
+ attributes?: Record<string, AttributeValue>,
258
+ ): this;
259
+ /**
260
+ * Add a GenAI user message event to the span.
261
+ *
262
+ * This logs a user/customer message sent to the model.
263
+ *
264
+ * @param body - The event body (content and role)
265
+ * @param system - The GenAI system (optional)
266
+ * @param attributes - Additional OpenTelemetry attributes (optional)
267
+ * @returns this
268
+ */
269
+ addGenAIUserMessageEvent(
270
+ body: LangWatchSpanGenAIUserMessageEventBody,
271
+ system?: intSemconv.VAL_GEN_AI_SYSTEMS | (string & {}),
272
+ attributes?: Record<string, AttributeValue>,
273
+ ): this;
274
+ /**
275
+ * Add a GenAI assistant message event to the span.
276
+ *
277
+ * This logs an assistant/bot response, including tool calls if present.
278
+ *
279
+ * @param body - The event body (content, role, tool_calls)
280
+ * @param system - The GenAI system (optional)
281
+ * @param attributes - Additional OpenTelemetry attributes (optional)
282
+ * @returns this
283
+ */
284
+ addGenAIAssistantMessageEvent(
285
+ body: LangWatchSpanGenAIAssistantMessageEventBody,
286
+ system?: intSemconv.VAL_GEN_AI_SYSTEMS | (string & {}),
287
+ attributes?: Record<string, AttributeValue>,
288
+ ): this;
289
+ /**
290
+ * Add a GenAI tool message event to the span.
291
+ *
292
+ * This logs a message from a tool/function invoked by the assistant.
293
+ *
294
+ * @param body - The event body (content, id, role)
295
+ * @param system - The GenAI system (optional)
296
+ * @param attributes - Additional OpenTelemetry attributes (optional)
297
+ * @returns this
298
+ */
299
+ addGenAIToolMessageEvent(
300
+ body: LangWatchSpanGenAIToolMessageEventBody,
301
+ system?: intSemconv.VAL_GEN_AI_SYSTEMS | (string & {}),
302
+ attributes?: Record<string, AttributeValue>,
303
+ ): this;
304
+ /**
305
+ * Add a GenAI choice event to the span.
306
+ *
307
+ * This logs a model output choice, including finish reason and message content.
308
+ *
309
+ * @param body - The event body (finish_reason, index, message)
310
+ * @param system - The GenAI system (optional)
311
+ * @param attributes - Additional OpenTelemetry attributes (optional)
312
+ * @returns this
313
+ */
314
+ addGenAIChoiceEvent(
315
+ body: LangWatchSpanGenAIChoiceEventBody,
316
+ system?: intSemconv.VAL_GEN_AI_SYSTEMS | (string & {}),
317
+ attributes?: Record<string, AttributeValue>,
318
+ ): this;
319
+
320
+ /**
321
+ * Set the type of the span (e.g., 'llm', 'rag', 'tool', etc).
322
+ *
323
+ * This is used for downstream filtering and analytics.
324
+ *
325
+ * @param type - The span type (see SpanType)
326
+ * @returns this
327
+ */
328
+ setType(type: SpanType): this;
329
+
330
+ /**
331
+ * Set the request model name for the span.
332
+ *
333
+ * This is typically the model name sent in the API request (e.g., 'gpt-4', 'claude-3').
334
+ *
335
+ * @param model - The request model name
336
+ * @returns this
337
+ */
338
+ setRequestModel(model: string): this;
339
+ /**
340
+ * Set the response model name for the span.
341
+ *
342
+ * This is the model name returned in the API response, if different from the request.
343
+ *
344
+ * @param model - The response model name
345
+ * @returns this
346
+ */
347
+ setResponseModel(model: string): this;
348
+
349
+ /**
350
+ * Set multiple RAG contexts for the span.
351
+ *
352
+ * Use this to record all retrieved documents/chunks used as context for a generation.
353
+ *
354
+ * @param ragContexts - Array of RAG context objects
355
+ * @returns this
356
+ */
357
+ setRAGContexts(ragContexts: LangWatchSpanRAGContext[]): this;
358
+ /**
359
+ * Set a single RAG context for the span.
360
+ *
361
+ * Use this if only one context was retrieved.
362
+ *
363
+ * @param ragContext - The RAG context object
364
+ * @returns this
365
+ */
366
+ setRAGContext(ragContext: LangWatchSpanRAGContext): this;
367
+
368
+ /**
369
+ * Set the metrics for the span.
370
+ *
371
+ * @param metrics - The metrics object
372
+ * @returns this
373
+ */
374
+ setMetrics(metrics: LangWatchSpanMetrics): this;
375
+
376
+ /**
377
+ * Set the selected prompt for the span. This will attach this prompt to the trace. If
378
+ * this is set on multiple spans, the last one will be used.
379
+ *
380
+ * @param prompt - The prompt object
381
+ * @returns this
382
+ */
383
+ setSelectedPrompt(prompt: Prompt): this;
384
+
385
+ /**
386
+ * Record the input to the span as a JSON-serializable value.
387
+ *
388
+ * The input is stringified and stored as a span attribute for later analysis.
389
+ *
390
+ * @param input - The input value (any type, will be JSON.stringified)
391
+ * @returns this
392
+ */
393
+ setInput(input: unknown): this;
394
+ /**
395
+ * Record the input to the span as a plain string.
396
+ *
397
+ * Use this for raw text prompts or queries.
398
+ *
399
+ * @param input - The input string
400
+ * @returns this
401
+ */
402
+ setInputString(input: string): this;
403
+ /**
404
+ * Record the output from the span as a JSON-serializable value.
405
+ *
406
+ * The output is stringified and stored as a span attribute for later analysis.
407
+ *
408
+ * @param output - The output value (any type, will be JSON.stringified)
409
+ * @returns this
410
+ */
411
+ setOutput(output: unknown): this;
412
+ /**
413
+ * Record the output from the span as a plain string.
414
+ *
415
+ * Use this for raw text completions or responses.
416
+ *
417
+ * @param output - The output string
418
+ * @returns this
419
+ */
420
+ setOutputString(output: string): this;
421
+
422
+ /**
423
+ * Set the evaluation output for the span.
424
+ *
425
+ * @param guardrail - Whether the evaluation is a guardrail
426
+ * @param output - The evaluation result
427
+ * @returns this
428
+ */
429
+ setOutputEvaluation(guardrail: boolean, output: EvaluationResultModel): this;
430
+ }
431
+
432
+ /**
433
+ * LangWatchSpan class that wraps an OpenTelemetry Span with LangWatch-specific helpers.
434
+ *
435
+ * This class provides a clean, type-safe wrapper around OpenTelemetry spans with
436
+ * additional methods for LLM, RAG, and GenAI tracing. All methods support fluent API chaining.
437
+ *
438
+ * @example
439
+ * import { createLangWatchSpan } from './span';
440
+ * const otelSpan = tracer.startSpan('llm-call');
441
+ * const span = createLangWatchSpan(otelSpan);
442
+ * span.setType('llm').setInput('Prompt').setOutput('Completion');
443
+ */
444
+ class LangWatchSpanImpl implements LangWatchSpan {
445
+ constructor(private span: Span) {}
446
+
447
+ // OpenTelemetry Span methods with fluent API support
448
+ setAttribute(key: string, value: AttributeValue): this {
449
+ this.span.setAttribute(key, value);
450
+ return this;
451
+ }
452
+
453
+ setAttributes(attributes: Attributes): this {
454
+ this.span.setAttributes(attributes);
455
+ return this;
456
+ }
457
+
458
+ addEvent(name: string, attributes?: Attributes): this {
459
+ this.span.addEvent(name, attributes);
460
+ return this;
461
+ }
462
+
463
+ recordException(exception: Exception): this {
464
+ this.span.recordException(exception);
465
+ return this;
466
+ }
467
+
468
+ setStatus(status: SpanStatus): this {
469
+ this.span.setStatus(status);
470
+ return this;
471
+ }
472
+
473
+ updateName(name: string): this {
474
+ this.span.updateName(name);
475
+ return this;
476
+ }
477
+
478
+ // Pass through other Span methods without chaining
479
+ end(endTime?: number): void {
480
+ this.span.end(endTime);
481
+ }
482
+
483
+ isRecording(): boolean {
484
+ return this.span.isRecording();
485
+ }
486
+
487
+ spanContext(): SpanContext {
488
+ return this.span.spanContext();
489
+ }
490
+
491
+ addLink(link: Link): this {
492
+ this.span.addLink(link);
493
+ return this;
494
+ }
495
+
496
+ addLinks(links: Link[]): this {
497
+ this.span.addLinks(links);
498
+ return this;
499
+ }
500
+
501
+ // LangWatch-specific methods
502
+ recordEvaluation(
503
+ details: RecordedEvaluationDetails,
504
+ attributes?: Attributes,
505
+ ): this {
506
+ recordEvaluation(details, attributes);
507
+ return this;
508
+ }
509
+
510
+ addGenAISystemMessageEvent(
511
+ body: LangWatchSpanGenAISystemMessageEventBody,
512
+ system?: intSemconv.VAL_GEN_AI_SYSTEMS | (string & {}),
513
+ attributes?: Record<string, AttributeValue>,
514
+ ): this {
515
+ if (body.role === void 0) {
516
+ body.role = "system";
517
+ }
518
+
519
+ this.span.addEvent(intSemconv.LOG_EVNT_GEN_AI_SYSTEM_MESSAGE, {
520
+ ...attributes,
521
+ [semconv.ATTR_GEN_AI_SYSTEM]: system,
522
+ [intSemconv.ATTR_LANGWATCH_GEN_AI_LOG_EVENT_BODY]: JSON.stringify(body),
523
+ [intSemconv.ATTR_LANGWATCH_GEN_AI_LOG_EVENT_IMPOSTER]: true,
524
+ });
525
+ return this;
526
+ }
527
+
528
+ addGenAIUserMessageEvent(
529
+ body: LangWatchSpanGenAIUserMessageEventBody,
530
+ system?: intSemconv.VAL_GEN_AI_SYSTEMS | (string & {}),
531
+ attributes?: Record<string, AttributeValue>,
532
+ ): this {
533
+ if (body.role === void 0) {
534
+ body.role = "user";
535
+ }
536
+
537
+ this.span.addEvent(intSemconv.LOG_EVNT_GEN_AI_USER_MESSAGE, {
538
+ ...attributes,
539
+ [semconv.ATTR_GEN_AI_SYSTEM]: system,
540
+ [intSemconv.ATTR_LANGWATCH_GEN_AI_LOG_EVENT_BODY]: JSON.stringify(body),
541
+ [intSemconv.ATTR_LANGWATCH_GEN_AI_LOG_EVENT_IMPOSTER]: true,
542
+ });
543
+ return this;
544
+ }
545
+
546
+ addGenAIAssistantMessageEvent(
547
+ body: LangWatchSpanGenAIAssistantMessageEventBody,
548
+ system?: intSemconv.VAL_GEN_AI_SYSTEMS | (string & {}),
549
+ attributes?: Record<string, AttributeValue>,
550
+ ): this {
551
+ if (body.role === void 0) {
552
+ body.role = "assistant";
553
+ }
554
+
555
+ this.span.addEvent(intSemconv.LOG_EVNT_GEN_AI_ASSISTANT_MESSAGE, {
556
+ ...attributes,
557
+ [semconv.ATTR_GEN_AI_SYSTEM]: system,
558
+ [intSemconv.ATTR_LANGWATCH_GEN_AI_LOG_EVENT_BODY]: JSON.stringify(body),
559
+ [intSemconv.ATTR_LANGWATCH_GEN_AI_LOG_EVENT_IMPOSTER]: true,
560
+ });
561
+ return this;
562
+ }
563
+
564
+ addGenAIToolMessageEvent(
565
+ body: LangWatchSpanGenAIToolMessageEventBody,
566
+ system?: intSemconv.VAL_GEN_AI_SYSTEMS | (string & {}),
567
+ attributes?: Record<string, AttributeValue>,
568
+ ): this {
569
+ if (body.role === void 0) {
570
+ body.role = "tool";
571
+ }
572
+
573
+ this.span.addEvent(intSemconv.LOG_EVNT_GEN_AI_TOOL_MESSAGE, {
574
+ ...attributes,
575
+ [semconv.ATTR_GEN_AI_SYSTEM]: system,
576
+ [intSemconv.ATTR_LANGWATCH_GEN_AI_LOG_EVENT_BODY]: JSON.stringify(body),
577
+ [intSemconv.ATTR_LANGWATCH_GEN_AI_LOG_EVENT_IMPOSTER]: true,
578
+ });
579
+ return this;
580
+ }
581
+
582
+ addGenAIChoiceEvent(
583
+ body: LangWatchSpanGenAIChoiceEventBody,
584
+ system?: intSemconv.VAL_GEN_AI_SYSTEMS | (string & {}),
585
+ attributes?: Record<string, AttributeValue>,
586
+ ): this {
587
+ if (body.message && body.message.role === void 0) {
588
+ body.message.role = "assistant";
589
+ }
590
+
591
+ this.span.addEvent(intSemconv.LOG_EVNT_GEN_AI_CHOICE, {
592
+ ...attributes,
593
+ [semconv.ATTR_GEN_AI_SYSTEM]: system,
594
+ [intSemconv.ATTR_LANGWATCH_GEN_AI_LOG_EVENT_BODY]: JSON.stringify(body),
595
+ [intSemconv.ATTR_LANGWATCH_GEN_AI_LOG_EVENT_IMPOSTER]: true,
596
+ });
597
+ return this;
598
+ }
599
+
600
+ setType(type: SpanType): this {
601
+ this.span.setAttribute(intSemconv.ATTR_LANGWATCH_SPAN_TYPE, type);
602
+ return this;
603
+ }
604
+
605
+ setRequestModel(model: string): this {
606
+ this.span.setAttribute(semconv.ATTR_GEN_AI_REQUEST_MODEL, model);
607
+ return this;
608
+ }
609
+
610
+ setResponseModel(model: string): this {
611
+ this.span.setAttribute(semconv.ATTR_GEN_AI_RESPONSE_MODEL, model);
612
+ return this;
613
+ }
614
+
615
+ setRAGContexts(ragContexts: LangWatchSpanRAGContext[]): this {
616
+ this.span.setAttribute(
617
+ intSemconv.ATTR_LANGWATCH_RAG_CONTEXTS,
618
+ JSON.stringify({
619
+ type: "json",
620
+ value: ragContexts,
621
+ }),
622
+ );
623
+ return this;
624
+ }
625
+
626
+ setRAGContext(ragContext: LangWatchSpanRAGContext): this {
627
+ this.span.setAttribute(
628
+ intSemconv.ATTR_LANGWATCH_RAG_CONTEXTS,
629
+ JSON.stringify({
630
+ type: "json",
631
+ value: [ragContext],
632
+ }),
633
+ );
634
+ return this;
635
+ }
636
+
637
+ setMetrics(metrics: LangWatchSpanMetrics): this {
638
+ this.span.setAttribute(
639
+ intSemconv.ATTR_LANGWATCH_METRICS,
640
+ JSON.stringify({
641
+ type: "json",
642
+ value: metrics,
643
+ }),
644
+ );
645
+ return this;
646
+ }
647
+
648
+ setSelectedPrompt(prompt: Prompt): this {
649
+ this.span.setAttributes({
650
+ [intSemconv.ATTR_LANGWATCH_PROMPT_SELECTED_ID]: prompt.id,
651
+ [intSemconv.ATTR_LANGWATCH_PROMPT_ID]: prompt.id,
652
+ [intSemconv.ATTR_LANGWATCH_PROMPT_VERSION_ID]: prompt.versionId,
653
+ [intSemconv.ATTR_LANGWATCH_PROMPT_VERSION_NUMBER]: prompt.version,
654
+ });
655
+ return this;
656
+ }
657
+
658
+ setInput(input: unknown): this {
659
+ this.span.setAttribute(
660
+ intSemconv.ATTR_LANGWATCH_INPUT,
661
+ JSON.stringify({
662
+ type: "json",
663
+ value: input,
664
+ }),
665
+ );
666
+ return this;
667
+ }
668
+
669
+ setInputString(input: string): this {
670
+ this.span.setAttribute(
671
+ intSemconv.ATTR_LANGWATCH_INPUT,
672
+ JSON.stringify({
673
+ type: "text",
674
+ value: input,
675
+ }),
676
+ );
677
+ return this;
678
+ }
679
+
680
+ setOutput(output: unknown): this {
681
+ this.span.setAttribute(
682
+ intSemconv.ATTR_LANGWATCH_OUTPUT,
683
+ JSON.stringify({
684
+ type: "json",
685
+ value: output,
686
+ }),
687
+ );
688
+ return this;
689
+ }
690
+
691
+ setOutputString(output: string): this {
692
+ this.span.setAttribute(
693
+ intSemconv.ATTR_LANGWATCH_OUTPUT,
694
+ JSON.stringify({
695
+ type: "text",
696
+ value: output,
697
+ }),
698
+ );
699
+ return this;
700
+ }
701
+
702
+ setOutputEvaluation(guardrail: boolean, output: EvaluationResultModel): this {
703
+ this.span.setAttribute(
704
+ intSemconv.ATTR_LANGWATCH_OUTPUT,
705
+ JSON.stringify({
706
+ type: guardrail ? "guardrail_result" : "evaluation_result",
707
+ value: output,
708
+ }),
709
+ );
710
+ return this;
711
+ }
712
+ }
713
+
714
+ /**
715
+ * Creates a LangWatchSpan wrapper around an OpenTelemetry Span.
716
+ *
717
+ * @param span - The OpenTelemetry Span to wrap
718
+ * @returns A LangWatchSpan with additional methods for LLM/GenAI observability
719
+ *
720
+ * @example
721
+ * import { createLangWatchSpan } from './span';
722
+ * const otelSpan = tracer.startSpan('llm-call');
723
+ * const span = createLangWatchSpan(otelSpan);
724
+ * span.setType('llm').setInput('Prompt').setOutput('Completion');
725
+ */
726
+ export function createLangWatchSpan(span: Span): LangWatchSpan {
727
+ return new LangWatchSpanImpl(span);
728
+ }