@visibe.ai/node 0.1.4

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 (41) hide show
  1. package/README.md +330 -0
  2. package/dist/cjs/api.js +92 -0
  3. package/dist/cjs/client.js +242 -0
  4. package/dist/cjs/index.js +216 -0
  5. package/dist/cjs/integrations/anthropic.js +277 -0
  6. package/dist/cjs/integrations/base.js +32 -0
  7. package/dist/cjs/integrations/bedrock.js +442 -0
  8. package/dist/cjs/integrations/group-context.js +10 -0
  9. package/dist/cjs/integrations/langchain.js +274 -0
  10. package/dist/cjs/integrations/langgraph.js +173 -0
  11. package/dist/cjs/integrations/openai.js +447 -0
  12. package/dist/cjs/integrations/vercel-ai.js +261 -0
  13. package/dist/cjs/types/index.js +5 -0
  14. package/dist/cjs/utils.js +122 -0
  15. package/dist/esm/api.js +87 -0
  16. package/dist/esm/client.js +238 -0
  17. package/dist/esm/index.js +209 -0
  18. package/dist/esm/integrations/anthropic.js +272 -0
  19. package/dist/esm/integrations/base.js +28 -0
  20. package/dist/esm/integrations/bedrock.js +438 -0
  21. package/dist/esm/integrations/group-context.js +7 -0
  22. package/dist/esm/integrations/langchain.js +269 -0
  23. package/dist/esm/integrations/langgraph.js +168 -0
  24. package/dist/esm/integrations/openai.js +442 -0
  25. package/dist/esm/integrations/vercel-ai.js +258 -0
  26. package/dist/esm/types/index.js +4 -0
  27. package/dist/esm/utils.js +116 -0
  28. package/dist/types/api.d.ts +27 -0
  29. package/dist/types/client.d.ts +50 -0
  30. package/dist/types/index.d.ts +7 -0
  31. package/dist/types/integrations/anthropic.d.ts +9 -0
  32. package/dist/types/integrations/base.d.ts +17 -0
  33. package/dist/types/integrations/bedrock.d.ts +11 -0
  34. package/dist/types/integrations/group-context.d.ts +12 -0
  35. package/dist/types/integrations/langchain.d.ts +40 -0
  36. package/dist/types/integrations/langgraph.d.ts +13 -0
  37. package/dist/types/integrations/openai.d.ts +11 -0
  38. package/dist/types/integrations/vercel-ai.d.ts +2 -0
  39. package/dist/types/types/index.d.ts +21 -0
  40. package/dist/types/utils.d.ts +23 -0
  41. package/package.json +80 -0
@@ -0,0 +1,442 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectProvider = exports.BedrockIntegration = void 0;
4
+ exports.patchBedrockClient = patchBedrockClient;
5
+ const node_crypto_1 = require("node:crypto");
6
+ const base_1 = require("./base");
7
+ const group_context_1 = require("./group-context");
8
+ const utils_1 = require("../utils");
9
+ Object.defineProperty(exports, "detectProvider", { enumerable: true, get: function () { return utils_1.detectProvider; } });
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ function parseInvokeAnthropic(req, res) {
12
+ return {
13
+ prompt: req.messages?.at(-1)?.content?.[0]?.text ?? '',
14
+ outputText: res.content?.[0]?.text ?? '',
15
+ inputTokens: res.usage?.input_tokens ?? 0,
16
+ outputTokens: res.usage?.output_tokens ?? 0,
17
+ };
18
+ }
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ function parseInvokeMeta(req, res) {
21
+ return {
22
+ prompt: req.prompt ?? '',
23
+ outputText: res.generation ?? '',
24
+ inputTokens: res.prompt_token_count ?? 0,
25
+ outputTokens: res.generation_token_count ?? 0,
26
+ };
27
+ }
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
+ function parseInvokeMistral(req, res) {
30
+ // Mistral via InvokeModelCommand does not return token counts.
31
+ const text = res.outputs?.[0]?.text ?? res.choices?.[0]?.message?.content ?? '';
32
+ return { prompt: req.prompt ?? '', outputText: text, inputTokens: 0, outputTokens: 0 };
33
+ }
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ function parseInvokeTitan(req, res) {
36
+ return {
37
+ prompt: req.inputText ?? '',
38
+ outputText: res.results?.[0]?.outputText ?? '',
39
+ inputTokens: res.inputTextTokenCount ?? 0,
40
+ outputTokens: res.results?.[0]?.tokenCount ?? 0,
41
+ };
42
+ }
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ function parseInvokeNova(req, res) {
45
+ return {
46
+ prompt: req.messages?.at(-1)?.content?.[0]?.text ?? '',
47
+ outputText: res.output?.message?.content?.[0]?.text ?? '',
48
+ inputTokens: res.usage?.inputTokens ?? 0,
49
+ outputTokens: res.usage?.outputTokens ?? 0,
50
+ };
51
+ }
52
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
+ function parseInvokeCohere(req, res) {
54
+ const text = res.text ?? res.generations?.[0]?.text ?? '';
55
+ const billedUnits = res.meta?.billed_units;
56
+ return {
57
+ prompt: req.prompt ?? req.message ?? '',
58
+ outputText: text,
59
+ inputTokens: billedUnits?.input_tokens ?? 0,
60
+ outputTokens: billedUnits?.output_tokens ?? 0,
61
+ };
62
+ }
63
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
+ function parseInvokeAI21(req, res) {
65
+ return {
66
+ prompt: req.messages?.at(-1)?.content ?? '',
67
+ outputText: res.choices?.[0]?.message?.content ?? '',
68
+ inputTokens: res.usage?.prompt_tokens ?? 0,
69
+ outputTokens: res.usage?.completion_tokens ?? 0,
70
+ };
71
+ }
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ function parseInvokeUnknown(_req, _res) {
74
+ return { prompt: '', outputText: '', inputTokens: 0, outputTokens: 0 };
75
+ }
76
+ function getInvokeParser(modelId) {
77
+ if (modelId.startsWith('anthropic.'))
78
+ return parseInvokeAnthropic;
79
+ if (modelId.startsWith('meta.'))
80
+ return parseInvokeMeta;
81
+ if (modelId.startsWith('mistral.'))
82
+ return parseInvokeMistral;
83
+ if (modelId.startsWith('amazon.titan'))
84
+ return parseInvokeTitan;
85
+ if (modelId.startsWith('amazon.nova'))
86
+ return parseInvokeNova;
87
+ if (modelId.startsWith('cohere.'))
88
+ return parseInvokeCohere;
89
+ if (modelId.startsWith('ai21.'))
90
+ return parseInvokeAI21;
91
+ return parseInvokeUnknown;
92
+ }
93
+ // ---------------------------------------------------------------------------
94
+ // BedrockIntegration
95
+ // ---------------------------------------------------------------------------
96
+ class BedrockIntegration extends base_1.BaseIntegration {
97
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
+ patchClient(client, agentName) {
99
+ const originalSend = client.send.bind(client);
100
+ // We need the command constructors to do instanceof checks.
101
+ // They are imported lazily so this file can be loaded without the package installed.
102
+ let ConverseCommand;
103
+ let ConverseStreamCommand;
104
+ let InvokeModelCommand;
105
+ let InvokeModelWithResponseStreamCommand;
106
+ try {
107
+ const mod = require('@aws-sdk/client-bedrock-runtime');
108
+ ConverseCommand = mod.ConverseCommand;
109
+ ConverseStreamCommand = mod.ConverseStreamCommand;
110
+ InvokeModelCommand = mod.InvokeModelCommand;
111
+ InvokeModelWithResponseStreamCommand = mod.InvokeModelWithResponseStreamCommand;
112
+ }
113
+ catch {
114
+ // Package not installed — return a no-op restore.
115
+ return () => { };
116
+ }
117
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
118
+ client.send = async (command, ...rest) => {
119
+ if (ConverseCommand && command instanceof ConverseCommand) {
120
+ return this._traceConverse(originalSend, command, rest, agentName);
121
+ }
122
+ if (InvokeModelCommand && command instanceof InvokeModelCommand) {
123
+ return this._traceInvokeModel(originalSend, command, rest, agentName);
124
+ }
125
+ if (ConverseStreamCommand && command instanceof ConverseStreamCommand) {
126
+ return this._traceConverseStream(originalSend, command, rest, agentName);
127
+ }
128
+ if (InvokeModelWithResponseStreamCommand &&
129
+ command instanceof InvokeModelWithResponseStreamCommand) {
130
+ // Streaming invoke — pass through for now; non-streaming covers most use cases.
131
+ return originalSend(command, ...rest);
132
+ }
133
+ return originalSend(command, ...rest);
134
+ };
135
+ return () => { client.send = originalSend; };
136
+ }
137
+ // ---------------------------------------------------------------------------
138
+ // ConverseCommand
139
+ // ---------------------------------------------------------------------------
140
+ async _traceConverse(
141
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
+ original,
143
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
144
+ command,
145
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
146
+ rest, agentName) {
147
+ const groupCtx = group_context_1.activeGroupTraceStorage.getStore();
148
+ const traceId = groupCtx?.traceId ?? (0, node_crypto_1.randomUUID)();
149
+ const startedAt = new Date().toISOString();
150
+ const startMs = Date.now();
151
+ const input = command.input;
152
+ if (!groupCtx) {
153
+ await this.visibe.apiClient.createTrace({
154
+ trace_id: traceId,
155
+ name: agentName,
156
+ framework: 'bedrock',
157
+ started_at: startedAt,
158
+ ...(this.visibe.sessionId ? { session_id: this.visibe.sessionId } : {}),
159
+ });
160
+ }
161
+ const spanId = this.nextSpanId();
162
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
163
+ let response;
164
+ let spanStatus = 'success';
165
+ try {
166
+ response = await original(command, ...rest);
167
+ }
168
+ catch (err) {
169
+ spanStatus = 'failed';
170
+ this.visibe.batcher.add(traceId, this.visibe.buildErrorSpan({
171
+ spanId: this.nextSpanId(),
172
+ errorType: err?.constructor?.name ?? 'Error',
173
+ errorMessage: err?.message ?? String(err),
174
+ }));
175
+ if (!groupCtx) {
176
+ this.visibe.batcher.flush();
177
+ await this.visibe.apiClient.completeTrace(traceId, {
178
+ status: 'failed', ended_at: new Date().toISOString(), duration_ms: Date.now() - startMs,
179
+ });
180
+ }
181
+ throw err;
182
+ }
183
+ const model = input.modelId ?? 'unknown';
184
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
185
+ const prompt = input.messages?.at(-1)?.content?.find((b) => b.text)?.text ?? '';
186
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
187
+ const output = response.output?.message?.content?.find((b) => b.text)?.text ?? '';
188
+ const inputTokens = response.usage?.inputTokens ?? 0;
189
+ const outputTokens = response.usage?.outputTokens ?? 0;
190
+ const cost = (0, utils_1.calculateCost)(model, inputTokens, outputTokens);
191
+ this.visibe.batcher.add(traceId, this.visibe.buildLLMSpan({
192
+ spanId,
193
+ agentName,
194
+ model,
195
+ status: spanStatus,
196
+ inputTokens,
197
+ outputTokens,
198
+ inputText: prompt,
199
+ outputText: output,
200
+ durationMs: Date.now() - startMs,
201
+ }));
202
+ // Notify the group tracker (if inside track()) about this LLM span.
203
+ groupCtx?.onLLMSpan(inputTokens, outputTokens, cost);
204
+ if (!groupCtx) {
205
+ this.visibe.batcher.flush();
206
+ const sent = await this.visibe.apiClient.completeTrace(traceId, {
207
+ status: 'completed',
208
+ ended_at: new Date().toISOString(),
209
+ duration_ms: Date.now() - startMs,
210
+ llm_call_count: 1,
211
+ prompt,
212
+ model,
213
+ total_cost: cost,
214
+ total_tokens: inputTokens + outputTokens,
215
+ total_input_tokens: inputTokens,
216
+ total_output_tokens: outputTokens,
217
+ });
218
+ _printSummary(agentName, model, inputTokens, outputTokens, cost, Date.now() - startMs, sent);
219
+ }
220
+ return response;
221
+ }
222
+ // ---------------------------------------------------------------------------
223
+ // InvokeModelCommand — raw JSON body, provider-specific parsing
224
+ // ---------------------------------------------------------------------------
225
+ async _traceInvokeModel(
226
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
227
+ original,
228
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
229
+ command,
230
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
231
+ rest, agentName) {
232
+ const groupCtx = group_context_1.activeGroupTraceStorage.getStore();
233
+ const traceId = groupCtx?.traceId ?? (0, node_crypto_1.randomUUID)();
234
+ const startedAt = new Date().toISOString();
235
+ const startMs = Date.now();
236
+ if (!groupCtx) {
237
+ await this.visibe.apiClient.createTrace({
238
+ trace_id: traceId,
239
+ name: agentName,
240
+ framework: 'bedrock',
241
+ started_at: startedAt,
242
+ ...(this.visibe.sessionId ? { session_id: this.visibe.sessionId } : {}),
243
+ });
244
+ }
245
+ const modelId = command.input?.modelId ?? 'unknown';
246
+ const spanId = this.nextSpanId();
247
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
248
+ let response;
249
+ let spanStatus = 'success';
250
+ // Decode the request body (Uint8Array or Buffer).
251
+ let reqBody = {};
252
+ try {
253
+ const bodyStr = new TextDecoder().decode(command.input?.body);
254
+ reqBody = JSON.parse(bodyStr);
255
+ }
256
+ catch { /* malformed body — continue with empty */ }
257
+ try {
258
+ response = await original(command, ...rest);
259
+ }
260
+ catch (err) {
261
+ spanStatus = 'failed';
262
+ this.visibe.batcher.add(traceId, this.visibe.buildErrorSpan({
263
+ spanId: this.nextSpanId(),
264
+ errorType: err?.constructor?.name ?? 'Error',
265
+ errorMessage: err?.message ?? String(err),
266
+ }));
267
+ if (!groupCtx) {
268
+ this.visibe.batcher.flush();
269
+ await this.visibe.apiClient.completeTrace(traceId, {
270
+ status: 'failed', ended_at: new Date().toISOString(), duration_ms: Date.now() - startMs,
271
+ });
272
+ }
273
+ throw err;
274
+ }
275
+ // The JS SDK already buffers the response body as Uint8Array — no streaming needed.
276
+ let resBody = {};
277
+ try {
278
+ const bodyStr = new TextDecoder().decode(response.body);
279
+ resBody = JSON.parse(bodyStr);
280
+ }
281
+ catch { /* malformed body — continue with empty */ }
282
+ const parse = getInvokeParser(modelId);
283
+ const { prompt, outputText, inputTokens, outputTokens } = parse(reqBody, resBody);
284
+ const cost = (0, utils_1.calculateCost)(modelId, inputTokens, outputTokens);
285
+ this.visibe.batcher.add(traceId, this.visibe.buildLLMSpan({
286
+ spanId,
287
+ agentName,
288
+ model: modelId,
289
+ status: spanStatus,
290
+ inputTokens,
291
+ outputTokens,
292
+ inputText: prompt,
293
+ outputText,
294
+ durationMs: Date.now() - startMs,
295
+ }));
296
+ // Notify the group tracker (if inside track()) about this LLM span.
297
+ groupCtx?.onLLMSpan(inputTokens, outputTokens, cost);
298
+ if (!groupCtx) {
299
+ this.visibe.batcher.flush();
300
+ const sent = await this.visibe.apiClient.completeTrace(traceId, {
301
+ status: 'completed',
302
+ ended_at: new Date().toISOString(),
303
+ duration_ms: Date.now() - startMs,
304
+ llm_call_count: 1,
305
+ prompt,
306
+ model: modelId,
307
+ total_cost: cost,
308
+ total_tokens: inputTokens + outputTokens,
309
+ total_input_tokens: inputTokens,
310
+ total_output_tokens: outputTokens,
311
+ });
312
+ _printSummary(agentName, modelId, inputTokens, outputTokens, cost, Date.now() - startMs, sent);
313
+ }
314
+ return response;
315
+ }
316
+ // ---------------------------------------------------------------------------
317
+ // ConverseStreamCommand — stream, accumulate, then finalize
318
+ // ---------------------------------------------------------------------------
319
+ async _traceConverseStream(
320
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
321
+ original,
322
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
323
+ command,
324
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
325
+ rest, agentName) {
326
+ const groupCtx = group_context_1.activeGroupTraceStorage.getStore();
327
+ const traceId = groupCtx?.traceId ?? (0, node_crypto_1.randomUUID)();
328
+ const startedAt = new Date().toISOString();
329
+ const startMs = Date.now();
330
+ const model = command.input?.modelId ?? 'unknown';
331
+ const spanId = this.nextSpanId();
332
+ if (!groupCtx) {
333
+ await this.visibe.apiClient.createTrace({
334
+ trace_id: traceId,
335
+ name: agentName,
336
+ framework: 'bedrock',
337
+ started_at: startedAt,
338
+ ...(this.visibe.sessionId ? { session_id: this.visibe.sessionId } : {}),
339
+ });
340
+ }
341
+ const response = await original(command, ...rest);
342
+ // The Converse stream emits a `metadata` event with usage totals.
343
+ let inputTokens = 0, outputTokens = 0;
344
+ let outputText = '';
345
+ const stream = response.stream;
346
+ if (stream && typeof stream[Symbol.asyncIterator] === 'function') {
347
+ const visibe = this.visibe;
348
+ const batcher = this.visibe.batcher;
349
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
350
+ const origIter = stream[Symbol.asyncIterator].bind(stream);
351
+ let finalized = false;
352
+ const finalize = async (status) => {
353
+ if (finalized)
354
+ return;
355
+ finalized = true;
356
+ const cost = (0, utils_1.calculateCost)(model, inputTokens, outputTokens);
357
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
358
+ const prompt = command.input?.messages?.at(-1)?.content?.find((b) => b.text)?.text ?? '';
359
+ batcher.add(traceId, visibe.buildLLMSpan({
360
+ spanId, agentName, model, status,
361
+ inputTokens, outputTokens,
362
+ inputText: prompt,
363
+ outputText,
364
+ durationMs: Date.now() - startMs,
365
+ }));
366
+ // Notify the group tracker (if inside track()) about this LLM span.
367
+ groupCtx?.onLLMSpan(inputTokens, outputTokens, cost);
368
+ if (!groupCtx) {
369
+ batcher.flush();
370
+ const sent = await visibe.apiClient.completeTrace(traceId, {
371
+ status: status === 'success' ? 'completed' : 'failed',
372
+ ended_at: new Date().toISOString(),
373
+ duration_ms: Date.now() - startMs,
374
+ llm_call_count: 1,
375
+ prompt,
376
+ model,
377
+ total_cost: cost,
378
+ total_tokens: inputTokens + outputTokens,
379
+ total_input_tokens: inputTokens,
380
+ total_output_tokens: outputTokens,
381
+ });
382
+ _printSummary(agentName, model, inputTokens, outputTokens, cost, Date.now() - startMs, sent);
383
+ }
384
+ };
385
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
386
+ stream[Symbol.asyncIterator] = () => {
387
+ const it = origIter();
388
+ return {
389
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
390
+ async next() {
391
+ try {
392
+ const result = await it.next();
393
+ if (!result.done) {
394
+ const chunk = result.value;
395
+ // Accumulate text deltas
396
+ const textDelta = chunk?.contentBlockDelta?.delta?.text;
397
+ if (textDelta)
398
+ outputText += textDelta;
399
+ // Metadata chunk carries usage
400
+ if (chunk?.metadata?.usage) {
401
+ inputTokens = chunk.metadata.usage.inputTokens ?? 0;
402
+ outputTokens = chunk.metadata.usage.outputTokens ?? 0;
403
+ }
404
+ }
405
+ else {
406
+ await finalize('success');
407
+ }
408
+ return result;
409
+ }
410
+ catch (err) {
411
+ await finalize('failed');
412
+ throw err;
413
+ }
414
+ },
415
+ async return(value) {
416
+ await finalize('success');
417
+ return it.return ? it.return(value) : { done: true, value };
418
+ },
419
+ };
420
+ };
421
+ }
422
+ return response;
423
+ }
424
+ }
425
+ exports.BedrockIntegration = BedrockIntegration;
426
+ // ---------------------------------------------------------------------------
427
+ // Module-level factory
428
+ // ---------------------------------------------------------------------------
429
+ function patchBedrockClient(
430
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
431
+ client, agentName, visibe) {
432
+ const integration = new BedrockIntegration(visibe);
433
+ return integration.patchClient(client, agentName);
434
+ }
435
+ // ---------------------------------------------------------------------------
436
+ // Private helpers
437
+ // ---------------------------------------------------------------------------
438
+ function _printSummary(name, model, inputTokens, outputTokens, cost, durationMs, sent) {
439
+ const tokens = (inputTokens + outputTokens).toLocaleString();
440
+ const sentStr = sent ? 'OK' : 'FAILED';
441
+ console.log(`[Visibe] Trace: ${name} | 1 LLM calls | ${tokens} tokens | $${cost.toFixed(6)} | ${(durationMs / 1000).toFixed(1)}s | 0 tool calls | status: completed | model: ${model} | sent: ${sentStr}`);
442
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.activeGroupTraceStorage = void 0;
4
+ const node_async_hooks_1 = require("node:async_hooks");
5
+ /**
6
+ * Set by Visibe.track() around the user's fn().
7
+ * All integrations read from this store to route spans into the shared group trace
8
+ * and report token/cost totals back to the track() accumulator.
9
+ */
10
+ exports.activeGroupTraceStorage = new node_async_hooks_1.AsyncLocalStorage();