opsveritas-sdk 0.1.4 → 0.3.0

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/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # opsveritas-sdk
2
+
3
+ Monitor your AI agents in **three lines of code**. Tracks tokens, cost, latency, model, and silent failures (200 OK with empty output) — and routes alerts to Slack / Email / Teams via [OpsVeritas AI Agents Control Tower](https://agents.opsveritas.com).
4
+
5
+ ```bash
6
+ npm install opsveritas-sdk
7
+ ```
8
+
9
+ ## Quick start (3 lines)
10
+
11
+ ```ts
12
+ import { OpsVeritas } from 'opsveritas-sdk';
13
+
14
+ OpsVeritas.init('<your-ingest-key>'); // key from Settings → Integrations
15
+ const client = OpsVeritas.wrap(new OpenAI(), { agentName: 'Support Bot' });
16
+ // use `client` exactly as before — runs appear in your dashboard automatically
17
+ ```
18
+
19
+ Works the same for Anthropic and Gemini clients. Prefer manual control? Wrap a function:
20
+
21
+ ```ts
22
+ await OpsVeritas.trace('Nightly Report', async () => runReport());
23
+ ```
24
+
25
+ ## LangChain (one line)
26
+
27
+ Attach the handler to any LangChain chat model, LLM or chain — every call reports token / cost / latency / silent-failure automatically:
28
+
29
+ ```ts
30
+ import { ChatOpenAI } from '@langchain/openai';
31
+ import { OpsVeritas } from 'opsveritas-sdk';
32
+
33
+ OpsVeritas.init('<your-ingest-key>');
34
+ const model = new ChatOpenAI({ callbacks: [OpsVeritas.langchain('My Agent')] });
35
+ ```
36
+
37
+ No extra dependency — it's a plain LangChain callback handler. Works with any LangChain-backed provider (OpenAI, Anthropic, Gemini, Groq…). Inside `OpsVeritas.run(...)` the handler aggregates into a single execution instead of double-counting.
38
+
39
+ ## What data is sent
40
+
41
+ By design the SDK sends **metadata only, plus a short output snippet** — never your prompts/inputs:
42
+
43
+ | Sent | Detail |
44
+ |------|--------|
45
+ | ✅ Metadata | agent name, status, timestamps, duration, token counts, model, cost, tool-call count |
46
+ | ⚠️ Output snippet | first 300 chars of the response (`output_summary`) — powers silent-failure detection |
47
+ | ⚠️ Error message | the exception text, if a call fails |
48
+ | ❌ Prompts / inputs | **never sent** — only token counts |
49
+
50
+ ### Metadata-only mode (for regulated / client data)
51
+
52
+ Drop the output snippet and redact error text so **no response content ever leaves your environment** — token/cost/latency metadata still flows:
53
+
54
+ ```ts
55
+ OpsVeritas.init('<your-ingest-key>', { metadataOnly: true });
56
+ ```
57
+
58
+ Or set the environment variable:
59
+
60
+ ```bash
61
+ OPSVERITAS_METADATA_ONLY=true
62
+ ```
63
+
64
+ ## Reliability
65
+
66
+ Telemetry is **non-blocking and fire-and-forget** — it never throws into your code and never slows your agent. If the ingest endpoint is briefly unreachable, sends are **retried with backoff** and **buffered in memory** (bounded), then flushed on the next event — so a transient outage doesn't lose telemetry.
67
+
68
+ ## Configuration
69
+
70
+ ```ts
71
+ OpsVeritas.init(apiKey, {
72
+ endpoint, // optional — defaults to https://agents.opsveritas.com
73
+ metadataOnly, // optional — default false; when true, no response content is sent
74
+ });
75
+ ```
76
+
77
+ ## License
78
+
79
+ MIT
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  declare function init(apiKey: string, options?: {
2
2
  endpoint?: string;
3
+ metadataOnly?: boolean;
3
4
  }): void;
4
5
 
5
6
  declare function run<T>(agentName: string, fn: () => Promise<T>, opts?: {
@@ -19,6 +20,26 @@ interface WrapOptions {
19
20
  }
20
21
  declare function wrap<T extends object>(client: T, opts: WrapOptions): T;
21
22
 
23
+ /**
24
+ * LangChain integration — one-line callback handler.
25
+ *
26
+ * import { ChatOpenAI } from '@langchain/openai';
27
+ * import opsveritas from 'opsveritas-sdk';
28
+ *
29
+ * opsveritas.init('<key>');
30
+ * const model = new ChatOpenAI({ callbacks: [opsveritas.langchain('My Agent')] });
31
+ * // every LLM call now reports token / cost / latency / silent-failure to OpsVeritas
32
+ *
33
+ * Returns a plain CallbackHandlerMethods object — LangChain.js accepts this shape
34
+ * directly in the `callbacks` array, so there's no @langchain/core dependency.
35
+ * Reuses the same ingest, pricing and silent-failure semantics as wrap()/run().
36
+ */
37
+ interface LangChainOptions {
38
+ platform?: string;
39
+ userId?: string;
40
+ }
41
+ declare function langchain(agentName: string, opts?: LangChainOptions): Record<string, unknown>;
42
+
22
43
  interface ExecutionPayload {
23
44
  platform: string;
24
45
  agent_name: string;
@@ -42,6 +63,7 @@ declare const OpsVeritas: {
42
63
  run: typeof run;
43
64
  trace: typeof trace;
44
65
  wrap: typeof wrap;
66
+ langchain: typeof langchain;
45
67
  };
46
68
 
47
- export { type ExecutionPayload, OpsVeritas, type TraceOptions, type WrapOptions, OpsVeritas as default, init, run, trace, wrap };
69
+ export { type ExecutionPayload, type LangChainOptions, OpsVeritas, type TraceOptions, type WrapOptions, OpsVeritas as default, init, langchain, run, trace, wrap };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  declare function init(apiKey: string, options?: {
2
2
  endpoint?: string;
3
+ metadataOnly?: boolean;
3
4
  }): void;
4
5
 
5
6
  declare function run<T>(agentName: string, fn: () => Promise<T>, opts?: {
@@ -19,6 +20,26 @@ interface WrapOptions {
19
20
  }
20
21
  declare function wrap<T extends object>(client: T, opts: WrapOptions): T;
21
22
 
23
+ /**
24
+ * LangChain integration — one-line callback handler.
25
+ *
26
+ * import { ChatOpenAI } from '@langchain/openai';
27
+ * import opsveritas from 'opsveritas-sdk';
28
+ *
29
+ * opsveritas.init('<key>');
30
+ * const model = new ChatOpenAI({ callbacks: [opsveritas.langchain('My Agent')] });
31
+ * // every LLM call now reports token / cost / latency / silent-failure to OpsVeritas
32
+ *
33
+ * Returns a plain CallbackHandlerMethods object — LangChain.js accepts this shape
34
+ * directly in the `callbacks` array, so there's no @langchain/core dependency.
35
+ * Reuses the same ingest, pricing and silent-failure semantics as wrap()/run().
36
+ */
37
+ interface LangChainOptions {
38
+ platform?: string;
39
+ userId?: string;
40
+ }
41
+ declare function langchain(agentName: string, opts?: LangChainOptions): Record<string, unknown>;
42
+
22
43
  interface ExecutionPayload {
23
44
  platform: string;
24
45
  agent_name: string;
@@ -42,6 +63,7 @@ declare const OpsVeritas: {
42
63
  run: typeof run;
43
64
  trace: typeof trace;
44
65
  wrap: typeof wrap;
66
+ langchain: typeof langchain;
45
67
  };
46
68
 
47
- export { type ExecutionPayload, OpsVeritas, type TraceOptions, type WrapOptions, OpsVeritas as default, init, run, trace, wrap };
69
+ export { type ExecutionPayload, type LangChainOptions, OpsVeritas, type TraceOptions, type WrapOptions, OpsVeritas as default, init, langchain, run, trace, wrap };
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ __export(index_exports, {
23
23
  OpsVeritas: () => OpsVeritas,
24
24
  default: () => index_default,
25
25
  init: () => init,
26
+ langchain: () => langchain,
26
27
  run: () => run,
27
28
  trace: () => trace,
28
29
  wrap: () => wrap
@@ -35,7 +36,9 @@ function init(apiKey, options) {
35
36
  if (!apiKey || typeof apiKey !== "string") throw new Error("[OpsVeritas] apiKey is required");
36
37
  _config = {
37
38
  apiKey,
38
- endpoint: (options?.endpoint ?? "https://agents.opsveritas.com").replace(/\/$/, "")
39
+ endpoint: (options?.endpoint ?? "https://agents.opsveritas.com").replace(/\/$/, ""),
40
+ // Opt-in via init() or the OPSVERITAS_METADATA_ONLY=true env var. Defaults off (unchanged behavior).
41
+ metadataOnly: options?.metadataOnly ?? (typeof process !== "undefined" && process.env?.OPSVERITAS_METADATA_ONLY === "true")
39
42
  };
40
43
  }
41
44
  function getConfig() {
@@ -47,20 +50,78 @@ function getConfig() {
47
50
  var import_async_hooks = require("async_hooks");
48
51
 
49
52
  // src/http.ts
50
- async function sendExecution(payload) {
51
- const { apiKey, endpoint } = getConfig();
53
+ var RETRY_BACKOFF_MS = [500, 2e3, 5e3];
54
+ var MAX_BUFFER = 200;
55
+ var buffer = [];
56
+ var flushing = false;
57
+ function sleep(ms) {
58
+ return new Promise((resolve) => {
59
+ const t = setTimeout(resolve, ms);
60
+ if (typeof t?.unref === "function") t.unref();
61
+ });
62
+ }
63
+ function scrubForMetadataOnly(payload) {
64
+ return {
65
+ ...payload,
66
+ output_summary: void 0,
67
+ error_message: payload.error_message != null ? "[redacted \u2014 metadata-only mode]" : payload.error_message
68
+ };
69
+ }
70
+ async function postOnce(endpoint, apiKey, body) {
52
71
  try {
53
- await fetch(`${endpoint}/webhooks/agent-execution`, {
72
+ const res = await fetch(`${endpoint}/webhooks/agent-execution`, {
54
73
  method: "POST",
55
- headers: {
56
- "Content-Type": "application/json",
57
- "x-opsveritas-key": apiKey
58
- },
59
- body: JSON.stringify(payload)
74
+ headers: { "Content-Type": "application/json", "x-opsveritas-key": apiKey },
75
+ body: JSON.stringify(body)
60
76
  });
77
+ if (res.ok) return "ok";
78
+ if (res.status >= 400 && res.status < 500) return "drop";
79
+ return "retry";
61
80
  } catch {
81
+ return "retry";
82
+ }
83
+ }
84
+ async function deliver(endpoint, apiKey, body) {
85
+ for (let attempt = 0; ; attempt++) {
86
+ const outcome = await postOnce(endpoint, apiKey, body);
87
+ if (outcome === "ok" || outcome === "drop") return true;
88
+ if (attempt >= RETRY_BACKOFF_MS.length) return false;
89
+ await sleep(RETRY_BACKOFF_MS[attempt]);
62
90
  }
63
91
  }
92
+ function enqueue(body) {
93
+ buffer.push(body);
94
+ while (buffer.length > MAX_BUFFER) buffer.shift();
95
+ }
96
+ async function flush(endpoint, apiKey) {
97
+ if (flushing || buffer.length === 0) return;
98
+ flushing = true;
99
+ try {
100
+ const pending = buffer.splice(0, buffer.length);
101
+ for (let i = 0; i < pending.length; i++) {
102
+ const ok = await deliver(endpoint, apiKey, pending[i]);
103
+ if (!ok) {
104
+ for (let j = pending.length - 1; j >= i; j--) buffer.unshift(pending[j]);
105
+ break;
106
+ }
107
+ }
108
+ } finally {
109
+ flushing = false;
110
+ }
111
+ }
112
+ async function sendExecution(payload) {
113
+ let cfg;
114
+ try {
115
+ cfg = getConfig();
116
+ } catch {
117
+ return;
118
+ }
119
+ const { apiKey, endpoint, metadataOnly } = cfg;
120
+ const body = metadataOnly ? scrubForMetadataOnly(payload) : payload;
121
+ void flush(endpoint, apiKey);
122
+ const ok = await deliver(endpoint, apiKey, body);
123
+ if (!ok) enqueue(body);
124
+ }
64
125
 
65
126
  // src/context.ts
66
127
  var storage = new import_async_hooks.AsyncLocalStorage();
@@ -472,13 +533,119 @@ function wrap(client, opts) {
472
533
  return client;
473
534
  }
474
535
 
536
+ // src/langchain.ts
537
+ function extractUsage2(output) {
538
+ let model;
539
+ let inputTokens;
540
+ let outputTokens;
541
+ const lo = output?.llmOutput;
542
+ if (lo && typeof lo === "object") {
543
+ const tu = lo.tokenUsage ?? lo.token_usage ?? lo.usage;
544
+ if (tu && typeof tu === "object") {
545
+ inputTokens = tu.promptTokens ?? tu.prompt_tokens ?? tu.inputTokens ?? tu.input_tokens;
546
+ outputTokens = tu.completionTokens ?? tu.completion_tokens ?? tu.outputTokens ?? tu.output_tokens;
547
+ }
548
+ model = lo.model_name ?? lo.modelName ?? lo.model;
549
+ }
550
+ if (inputTokens == null || !model) {
551
+ try {
552
+ for (const genList of output?.generations ?? []) {
553
+ for (const gen of genList) {
554
+ const msg = gen?.message;
555
+ const um = msg?.usage_metadata ?? msg?.usageMetadata;
556
+ if (um && inputTokens == null) {
557
+ inputTokens = um.input_tokens ?? um.inputTokens;
558
+ outputTokens = um.output_tokens ?? um.outputTokens;
559
+ }
560
+ const rm = msg?.response_metadata ?? msg?.responseMetadata;
561
+ if (rm && !model) model = rm.model_name ?? rm.modelName ?? rm.model;
562
+ }
563
+ }
564
+ } catch {
565
+ }
566
+ }
567
+ return { model: model != null ? String(model) : void 0, inputTokens, outputTokens };
568
+ }
569
+ function extractText(output) {
570
+ try {
571
+ for (const genList of output?.generations ?? []) {
572
+ for (const gen of genList) {
573
+ let t = gen?.text;
574
+ if (!t) {
575
+ const c = gen?.message?.content;
576
+ if (typeof c === "string") t = c;
577
+ }
578
+ if (typeof t === "string" && t.trim()) return t.slice(0, 300);
579
+ }
580
+ }
581
+ } catch {
582
+ }
583
+ return void 0;
584
+ }
585
+ function langchain(agentName, opts = {}) {
586
+ const platform = opts.platform ?? "sdk";
587
+ const starts = /* @__PURE__ */ new Map();
588
+ const begin = (runId) => {
589
+ starts.set(runId, { t0: Date.now(), executedAt: (/* @__PURE__ */ new Date()).toISOString() });
590
+ };
591
+ const finish = (status, output, errorMessage, runId) => {
592
+ const s = starts.get(runId);
593
+ starts.delete(runId);
594
+ const duration_ms = s ? Date.now() - s.t0 : 0;
595
+ const executed_at = s?.executedAt ?? (/* @__PURE__ */ new Date()).toISOString();
596
+ const { model, inputTokens, outputTokens } = output ? extractUsage2(output) : {};
597
+ const cost_usd = model && inputTokens != null && outputTokens != null ? calcCost(model, inputTokens, outputTokens) : void 0;
598
+ const activeRun = getActiveRun();
599
+ if (activeRun) {
600
+ activeRun.calls.push({ platform, model, inputTokens, outputTokens, costUsd: cost_usd, status });
601
+ } else {
602
+ void sendExecution({
603
+ platform,
604
+ agent_name: agentName,
605
+ status,
606
+ executed_at,
607
+ duration_ms,
608
+ model,
609
+ input_tokens: inputTokens,
610
+ output_tokens: outputTokens,
611
+ cost_usd,
612
+ error_message: errorMessage ?? null,
613
+ output_summary: output ? extractText(output) : void 0,
614
+ user_id: opts.userId
615
+ });
616
+ }
617
+ };
618
+ return {
619
+ name: "opsveritas",
620
+ handleLLMStart(_llm, _prompts, runId) {
621
+ begin(runId);
622
+ },
623
+ handleChatModelStart(_llm, _messages, runId) {
624
+ begin(runId);
625
+ },
626
+ handleLLMEnd(output, runId) {
627
+ try {
628
+ finish("success", output, void 0, runId);
629
+ } catch {
630
+ }
631
+ },
632
+ handleLLMError(err, runId) {
633
+ try {
634
+ finish("failed", void 0, err instanceof Error ? err.message : String(err), runId);
635
+ } catch {
636
+ }
637
+ }
638
+ };
639
+ }
640
+
475
641
  // src/index.ts
476
- var OpsVeritas = { init, run, trace, wrap };
642
+ var OpsVeritas = { init, run, trace, wrap, langchain };
477
643
  var index_default = OpsVeritas;
478
644
  // Annotate the CommonJS export names for ESM import in node:
479
645
  0 && (module.exports = {
480
646
  OpsVeritas,
481
647
  init,
648
+ langchain,
482
649
  run,
483
650
  trace,
484
651
  wrap
package/dist/index.mjs CHANGED
@@ -4,7 +4,9 @@ function init(apiKey, options) {
4
4
  if (!apiKey || typeof apiKey !== "string") throw new Error("[OpsVeritas] apiKey is required");
5
5
  _config = {
6
6
  apiKey,
7
- endpoint: (options?.endpoint ?? "https://agents.opsveritas.com").replace(/\/$/, "")
7
+ endpoint: (options?.endpoint ?? "https://agents.opsveritas.com").replace(/\/$/, ""),
8
+ // Opt-in via init() or the OPSVERITAS_METADATA_ONLY=true env var. Defaults off (unchanged behavior).
9
+ metadataOnly: options?.metadataOnly ?? (typeof process !== "undefined" && process.env?.OPSVERITAS_METADATA_ONLY === "true")
8
10
  };
9
11
  }
10
12
  function getConfig() {
@@ -16,20 +18,78 @@ function getConfig() {
16
18
  import { AsyncLocalStorage } from "async_hooks";
17
19
 
18
20
  // src/http.ts
19
- async function sendExecution(payload) {
20
- const { apiKey, endpoint } = getConfig();
21
+ var RETRY_BACKOFF_MS = [500, 2e3, 5e3];
22
+ var MAX_BUFFER = 200;
23
+ var buffer = [];
24
+ var flushing = false;
25
+ function sleep(ms) {
26
+ return new Promise((resolve) => {
27
+ const t = setTimeout(resolve, ms);
28
+ if (typeof t?.unref === "function") t.unref();
29
+ });
30
+ }
31
+ function scrubForMetadataOnly(payload) {
32
+ return {
33
+ ...payload,
34
+ output_summary: void 0,
35
+ error_message: payload.error_message != null ? "[redacted \u2014 metadata-only mode]" : payload.error_message
36
+ };
37
+ }
38
+ async function postOnce(endpoint, apiKey, body) {
21
39
  try {
22
- await fetch(`${endpoint}/webhooks/agent-execution`, {
40
+ const res = await fetch(`${endpoint}/webhooks/agent-execution`, {
23
41
  method: "POST",
24
- headers: {
25
- "Content-Type": "application/json",
26
- "x-opsveritas-key": apiKey
27
- },
28
- body: JSON.stringify(payload)
42
+ headers: { "Content-Type": "application/json", "x-opsveritas-key": apiKey },
43
+ body: JSON.stringify(body)
29
44
  });
45
+ if (res.ok) return "ok";
46
+ if (res.status >= 400 && res.status < 500) return "drop";
47
+ return "retry";
30
48
  } catch {
49
+ return "retry";
50
+ }
51
+ }
52
+ async function deliver(endpoint, apiKey, body) {
53
+ for (let attempt = 0; ; attempt++) {
54
+ const outcome = await postOnce(endpoint, apiKey, body);
55
+ if (outcome === "ok" || outcome === "drop") return true;
56
+ if (attempt >= RETRY_BACKOFF_MS.length) return false;
57
+ await sleep(RETRY_BACKOFF_MS[attempt]);
31
58
  }
32
59
  }
60
+ function enqueue(body) {
61
+ buffer.push(body);
62
+ while (buffer.length > MAX_BUFFER) buffer.shift();
63
+ }
64
+ async function flush(endpoint, apiKey) {
65
+ if (flushing || buffer.length === 0) return;
66
+ flushing = true;
67
+ try {
68
+ const pending = buffer.splice(0, buffer.length);
69
+ for (let i = 0; i < pending.length; i++) {
70
+ const ok = await deliver(endpoint, apiKey, pending[i]);
71
+ if (!ok) {
72
+ for (let j = pending.length - 1; j >= i; j--) buffer.unshift(pending[j]);
73
+ break;
74
+ }
75
+ }
76
+ } finally {
77
+ flushing = false;
78
+ }
79
+ }
80
+ async function sendExecution(payload) {
81
+ let cfg;
82
+ try {
83
+ cfg = getConfig();
84
+ } catch {
85
+ return;
86
+ }
87
+ const { apiKey, endpoint, metadataOnly } = cfg;
88
+ const body = metadataOnly ? scrubForMetadataOnly(payload) : payload;
89
+ void flush(endpoint, apiKey);
90
+ const ok = await deliver(endpoint, apiKey, body);
91
+ if (!ok) enqueue(body);
92
+ }
33
93
 
34
94
  // src/context.ts
35
95
  var storage = new AsyncLocalStorage();
@@ -441,13 +501,119 @@ function wrap(client, opts) {
441
501
  return client;
442
502
  }
443
503
 
504
+ // src/langchain.ts
505
+ function extractUsage2(output) {
506
+ let model;
507
+ let inputTokens;
508
+ let outputTokens;
509
+ const lo = output?.llmOutput;
510
+ if (lo && typeof lo === "object") {
511
+ const tu = lo.tokenUsage ?? lo.token_usage ?? lo.usage;
512
+ if (tu && typeof tu === "object") {
513
+ inputTokens = tu.promptTokens ?? tu.prompt_tokens ?? tu.inputTokens ?? tu.input_tokens;
514
+ outputTokens = tu.completionTokens ?? tu.completion_tokens ?? tu.outputTokens ?? tu.output_tokens;
515
+ }
516
+ model = lo.model_name ?? lo.modelName ?? lo.model;
517
+ }
518
+ if (inputTokens == null || !model) {
519
+ try {
520
+ for (const genList of output?.generations ?? []) {
521
+ for (const gen of genList) {
522
+ const msg = gen?.message;
523
+ const um = msg?.usage_metadata ?? msg?.usageMetadata;
524
+ if (um && inputTokens == null) {
525
+ inputTokens = um.input_tokens ?? um.inputTokens;
526
+ outputTokens = um.output_tokens ?? um.outputTokens;
527
+ }
528
+ const rm = msg?.response_metadata ?? msg?.responseMetadata;
529
+ if (rm && !model) model = rm.model_name ?? rm.modelName ?? rm.model;
530
+ }
531
+ }
532
+ } catch {
533
+ }
534
+ }
535
+ return { model: model != null ? String(model) : void 0, inputTokens, outputTokens };
536
+ }
537
+ function extractText(output) {
538
+ try {
539
+ for (const genList of output?.generations ?? []) {
540
+ for (const gen of genList) {
541
+ let t = gen?.text;
542
+ if (!t) {
543
+ const c = gen?.message?.content;
544
+ if (typeof c === "string") t = c;
545
+ }
546
+ if (typeof t === "string" && t.trim()) return t.slice(0, 300);
547
+ }
548
+ }
549
+ } catch {
550
+ }
551
+ return void 0;
552
+ }
553
+ function langchain(agentName, opts = {}) {
554
+ const platform = opts.platform ?? "sdk";
555
+ const starts = /* @__PURE__ */ new Map();
556
+ const begin = (runId) => {
557
+ starts.set(runId, { t0: Date.now(), executedAt: (/* @__PURE__ */ new Date()).toISOString() });
558
+ };
559
+ const finish = (status, output, errorMessage, runId) => {
560
+ const s = starts.get(runId);
561
+ starts.delete(runId);
562
+ const duration_ms = s ? Date.now() - s.t0 : 0;
563
+ const executed_at = s?.executedAt ?? (/* @__PURE__ */ new Date()).toISOString();
564
+ const { model, inputTokens, outputTokens } = output ? extractUsage2(output) : {};
565
+ const cost_usd = model && inputTokens != null && outputTokens != null ? calcCost(model, inputTokens, outputTokens) : void 0;
566
+ const activeRun = getActiveRun();
567
+ if (activeRun) {
568
+ activeRun.calls.push({ platform, model, inputTokens, outputTokens, costUsd: cost_usd, status });
569
+ } else {
570
+ void sendExecution({
571
+ platform,
572
+ agent_name: agentName,
573
+ status,
574
+ executed_at,
575
+ duration_ms,
576
+ model,
577
+ input_tokens: inputTokens,
578
+ output_tokens: outputTokens,
579
+ cost_usd,
580
+ error_message: errorMessage ?? null,
581
+ output_summary: output ? extractText(output) : void 0,
582
+ user_id: opts.userId
583
+ });
584
+ }
585
+ };
586
+ return {
587
+ name: "opsveritas",
588
+ handleLLMStart(_llm, _prompts, runId) {
589
+ begin(runId);
590
+ },
591
+ handleChatModelStart(_llm, _messages, runId) {
592
+ begin(runId);
593
+ },
594
+ handleLLMEnd(output, runId) {
595
+ try {
596
+ finish("success", output, void 0, runId);
597
+ } catch {
598
+ }
599
+ },
600
+ handleLLMError(err, runId) {
601
+ try {
602
+ finish("failed", void 0, err instanceof Error ? err.message : String(err), runId);
603
+ } catch {
604
+ }
605
+ }
606
+ };
607
+ }
608
+
444
609
  // src/index.ts
445
- var OpsVeritas = { init, run, trace, wrap };
610
+ var OpsVeritas = { init, run, trace, wrap, langchain };
446
611
  var index_default = OpsVeritas;
447
612
  export {
448
613
  OpsVeritas,
449
614
  index_default as default,
450
615
  init,
616
+ langchain,
451
617
  run,
452
618
  trace,
453
619
  wrap
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opsveritas-sdk",
3
- "version": "0.1.4",
4
- "description": "Monitor your AI agents with 2 lines of code",
3
+ "version": "0.3.0",
4
+ "description": "Monitor your AI agents in 3 lines of code — tokens, cost, latency, and silent failures",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",
@@ -30,12 +30,16 @@
30
30
  },
31
31
  "peerDependencies": {
32
32
  "@anthropic-ai/sdk": ">=0.20.0",
33
+ "@langchain/core": ">=0.2.0",
33
34
  "openai": ">=4.0.0"
34
35
  },
35
36
  "peerDependenciesMeta": {
36
37
  "@anthropic-ai/sdk": {
37
38
  "optional": true
38
39
  },
40
+ "@langchain/core": {
41
+ "optional": true
42
+ },
39
43
  "openai": {
40
44
  "optional": true
41
45
  }