trodo-node 1.1.0 → 2.1.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.
Files changed (68) hide show
  1. package/README.md +340 -103
  2. package/dist/cjs/TrodoClient.js +77 -19
  3. package/dist/cjs/TrodoClient.js.map +1 -1
  4. package/dist/cjs/api/ApiClient.js +21 -3
  5. package/dist/cjs/api/ApiClient.js.map +1 -1
  6. package/dist/cjs/api/endpoints.js +7 -1
  7. package/dist/cjs/api/endpoints.js.map +1 -1
  8. package/dist/cjs/index.js +104 -8
  9. package/dist/cjs/index.js.map +1 -1
  10. package/dist/cjs/otel/autoInstrument.js +253 -0
  11. package/dist/cjs/otel/autoInstrument.js.map +1 -0
  12. package/dist/cjs/otel/context.js +49 -0
  13. package/dist/cjs/otel/context.js.map +1 -0
  14. package/dist/cjs/otel/helpers.js +254 -0
  15. package/dist/cjs/otel/helpers.js.map +1 -0
  16. package/dist/cjs/otel/processor.js +129 -0
  17. package/dist/cjs/otel/processor.js.map +1 -0
  18. package/dist/cjs/otel/uuid.js +24 -0
  19. package/dist/cjs/otel/uuid.js.map +1 -0
  20. package/dist/cjs/otel/wrapAgent.js +399 -0
  21. package/dist/cjs/otel/wrapAgent.js.map +1 -0
  22. package/dist/cjs/queue/BatchFlusher.js +1 -1
  23. package/dist/cjs/queue/BatchFlusher.js.map +1 -1
  24. package/dist/esm/TrodoClient.d.ts +34 -1
  25. package/dist/esm/TrodoClient.d.ts.map +1 -1
  26. package/dist/esm/TrodoClient.js +77 -19
  27. package/dist/esm/TrodoClient.js.map +1 -1
  28. package/dist/esm/api/ApiClient.d.ts +9 -1
  29. package/dist/esm/api/ApiClient.d.ts.map +1 -1
  30. package/dist/esm/api/ApiClient.js +21 -3
  31. package/dist/esm/api/ApiClient.js.map +1 -1
  32. package/dist/esm/api/endpoints.d.ts +6 -1
  33. package/dist/esm/api/endpoints.d.ts.map +1 -1
  34. package/dist/esm/api/endpoints.js +7 -1
  35. package/dist/esm/api/endpoints.js.map +1 -1
  36. package/dist/esm/index.d.ts +84 -8
  37. package/dist/esm/index.d.ts.map +1 -1
  38. package/dist/esm/index.js +89 -7
  39. package/dist/esm/index.js.map +1 -1
  40. package/dist/esm/otel/autoInstrument.d.ts +61 -0
  41. package/dist/esm/otel/autoInstrument.d.ts.map +1 -0
  42. package/dist/esm/otel/autoInstrument.js +248 -0
  43. package/dist/esm/otel/autoInstrument.js.map +1 -0
  44. package/dist/esm/otel/context.d.ts +26 -0
  45. package/dist/esm/otel/context.d.ts.map +1 -0
  46. package/dist/esm/otel/context.js +44 -0
  47. package/dist/esm/otel/context.js.map +1 -0
  48. package/dist/esm/otel/helpers.d.ts +119 -0
  49. package/dist/esm/otel/helpers.d.ts.map +1 -0
  50. package/dist/esm/otel/helpers.js +244 -0
  51. package/dist/esm/otel/helpers.js.map +1 -0
  52. package/dist/esm/otel/processor.d.ts +94 -0
  53. package/dist/esm/otel/processor.d.ts.map +1 -0
  54. package/dist/esm/otel/processor.js +125 -0
  55. package/dist/esm/otel/processor.js.map +1 -0
  56. package/dist/esm/otel/uuid.d.ts +7 -0
  57. package/dist/esm/otel/uuid.d.ts.map +1 -0
  58. package/dist/esm/otel/uuid.js +21 -0
  59. package/dist/esm/otel/uuid.js.map +1 -0
  60. package/dist/esm/otel/wrapAgent.d.ts +100 -0
  61. package/dist/esm/otel/wrapAgent.d.ts.map +1 -0
  62. package/dist/esm/otel/wrapAgent.js +389 -0
  63. package/dist/esm/otel/wrapAgent.js.map +1 -0
  64. package/dist/esm/queue/BatchFlusher.js +1 -1
  65. package/dist/esm/queue/BatchFlusher.js.map +1 -1
  66. package/dist/esm/types/index.d.ts +26 -0
  67. package/dist/esm/types/index.d.ts.map +1 -1
  68. package/package.json +1 -1
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tool = tool;
4
+ exports.trace = trace;
5
+ exports.retrieval = retrieval;
6
+ exports.llm = llm;
7
+ exports.getTracer = getTracer;
8
+ exports.trackLlmCall = trackLlmCall;
9
+ exports.expressMiddleware = expressMiddleware;
10
+ exports.propagationHeaders = propagationHeaders;
11
+ /**
12
+ * Span helpers: `trace`, `tool`, `llm`, `retrieval` — typed function
13
+ * wrappers that auto-capture input/output/error. Plus `trackLlmCall` and
14
+ * Express middleware for cross-service run joining.
15
+ *
16
+ * These are the customer-facing surface for custom code: wrap once, get a
17
+ * span with args as input, return value as output, exception as error.
18
+ */
19
+ const context_js_1 = require("./context.js");
20
+ const wrapAgent_js_1 = require("./wrapAgent.js");
21
+ function _input(args) {
22
+ if (args.length === 0)
23
+ return undefined;
24
+ if (args.length === 1)
25
+ return args[0];
26
+ return args;
27
+ }
28
+ function _wrap(name, fn, kind, options = {}, hooks) {
29
+ return async (...args) => {
30
+ return (0, wrapAgent_js_1.withSpan)(undefined, name, async (span) => {
31
+ if (kind === 'tool')
32
+ span.setTool(name);
33
+ hooks?.onEnter?.(span);
34
+ const result = await fn(...args);
35
+ try {
36
+ hooks?.onResult?.(span, result);
37
+ }
38
+ catch {
39
+ /* never break user code */
40
+ }
41
+ span.setOutput(result);
42
+ return result;
43
+ }, { kind, input: _input(args), ...options });
44
+ };
45
+ }
46
+ function tool(a, b) {
47
+ // name-first form: tool(name, fn)
48
+ if (typeof a === 'string' && typeof b === 'function') {
49
+ return _wrap(a, b, 'tool');
50
+ }
51
+ // fn-first form: tool(fn, options?)
52
+ const fn = a;
53
+ const options = (b || {});
54
+ const toolName = options.name || fn.name || 'anonymous-tool';
55
+ const kind = (options.kind || 'tool');
56
+ return _wrap(toolName, fn, kind);
57
+ }
58
+ /**
59
+ * Wrap a function as a generic span. Mirrors the uselemma `trace()` helper.
60
+ *
61
+ * const prepare = trodo.trace('prepare', async (input) => { ... });
62
+ */
63
+ function trace(name, fn, options = {}) {
64
+ return _wrap(name, fn, 'generic', options);
65
+ }
66
+ /** Wrap a retriever / vector search as a kind='retrieval' span. */
67
+ function retrieval(name, fn, options = {}) {
68
+ return _wrap(name, fn, 'retrieval', options);
69
+ }
70
+ function _defaultExtractUsage(result) {
71
+ if (!result || typeof result !== 'object')
72
+ return {};
73
+ const r = result;
74
+ // OpenAI / OpenAI-compat: { usage: { prompt_tokens, completion_tokens } }
75
+ const usage = r.usage;
76
+ if (usage) {
77
+ if ('prompt_tokens' in usage || 'completion_tokens' in usage) {
78
+ return {
79
+ inputTokens: usage.prompt_tokens,
80
+ outputTokens: usage.completion_tokens,
81
+ };
82
+ }
83
+ // Anthropic: { usage: { input_tokens, output_tokens } }
84
+ if ('input_tokens' in usage || 'output_tokens' in usage) {
85
+ return {
86
+ inputTokens: usage.input_tokens,
87
+ outputTokens: usage.output_tokens,
88
+ };
89
+ }
90
+ }
91
+ // Gemini: { usageMetadata: { promptTokenCount, candidatesTokenCount } }
92
+ const meta = r.usageMetadata;
93
+ if (meta) {
94
+ return {
95
+ inputTokens: meta.promptTokenCount,
96
+ outputTokens: meta.candidatesTokenCount,
97
+ };
98
+ }
99
+ return {};
100
+ }
101
+ /**
102
+ * Wrap an LLM call — auto-extracts tokens from OpenAI/Anthropic/Gemini
103
+ * response shapes.
104
+ *
105
+ * const answer = trodo.llm('answer', callOpenAI, {
106
+ * model: 'gpt-4o-mini', provider: 'openai',
107
+ * });
108
+ */
109
+ function llm(name, fn, options = {}) {
110
+ const { model, provider, temperature, extractUsage, ...rest } = options;
111
+ const extractor = extractUsage || _defaultExtractUsage;
112
+ return _wrap(name, fn, 'llm', rest, {
113
+ onEnter: (s) => {
114
+ if (model || provider || temperature !== undefined) {
115
+ s.setLlm({ model, provider, temperature });
116
+ }
117
+ },
118
+ onResult: (s, result) => {
119
+ const { inputTokens, outputTokens } = extractor(result);
120
+ if (inputTokens !== undefined || outputTokens !== undefined) {
121
+ s.setLlm({
122
+ model,
123
+ provider,
124
+ inputTokens,
125
+ outputTokens,
126
+ temperature,
127
+ });
128
+ }
129
+ },
130
+ });
131
+ }
132
+ /**
133
+ * Return a raw OTel tracer — the Trodo processor is already subscribed.
134
+ * Use for advanced cases where you want to emit spans with the raw OTel
135
+ * API; they flow through to Trodo like any other span.
136
+ */
137
+ function getTracer(name = 'trodo') {
138
+ // Lazy import keeps @opentelemetry/api optional at runtime until used.
139
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
140
+ const api = require('@opentelemetry/api');
141
+ return api.trace.getTracer(name);
142
+ }
143
+ /**
144
+ * Record a one-shot LLM span for a raw-HTTP caller. Opens and immediately
145
+ * closes a span(kind='llm') populated with the model + token counts +
146
+ * prompt/completion. No-op outside an active run context.
147
+ *
148
+ * Usage:
149
+ * const resp = await fetch(url, { body: JSON.stringify(body) }).then(r => r.json());
150
+ * await trodo.trackLlmCall({
151
+ * model: 'gemini-2.5-flash',
152
+ * provider: 'google',
153
+ * inputTokens: resp.usageMetadata.promptTokenCount,
154
+ * outputTokens: resp.usageMetadata.candidatesTokenCount,
155
+ * prompt: body,
156
+ * completion: resp,
157
+ * });
158
+ */
159
+ async function trackLlmCall(params) {
160
+ if (!(0, context_js_1.getActiveContext)())
161
+ return;
162
+ const spanName = params.name || (params.model ? `llm.${params.provider}.${params.model}` : 'llm');
163
+ await (0, wrapAgent_js_1.withSpan)(undefined, spanName, async (span) => {
164
+ span.setLlm({
165
+ model: params.model,
166
+ provider: params.provider,
167
+ inputTokens: params.inputTokens,
168
+ outputTokens: params.outputTokens,
169
+ cost: params.cost,
170
+ temperature: params.temperature,
171
+ });
172
+ if (params.completion !== undefined) {
173
+ span.setOutput(params.completion);
174
+ }
175
+ }, {
176
+ kind: 'llm',
177
+ input: params.prompt,
178
+ attributes: params.metadata,
179
+ });
180
+ }
181
+ // ---------------------------------------------------------------------------
182
+ // Express / Connect middleware for automatic cross-service run joining
183
+ // ---------------------------------------------------------------------------
184
+ const TRODO_RUN_HEADER = 'x-trodo-run-id';
185
+ const TRODO_PARENT_SPAN_HEADER = 'x-trodo-parent-span-id';
186
+ const TRODO_AGENT_HEADER = 'x-trodo-agent-name';
187
+ /**
188
+ * Return an Express/Connect middleware that auto-joins inbound runs.
189
+ *
190
+ * If the inbound request has X-Trodo-Run-Id, every span created while
191
+ * handling the request nests under the caller's run. Otherwise the
192
+ * handler runs with no active context (no-op tracking).
193
+ *
194
+ * Usage:
195
+ * import express from 'express';
196
+ * import trodo from 'trodo-node';
197
+ * trodo.init({ siteId: '...' });
198
+ * const app = express();
199
+ * app.use(trodo.expressMiddleware());
200
+ */
201
+ function expressMiddleware(deps) {
202
+ const { processor, teamSiteId } = deps;
203
+ return (req, res, next) => {
204
+ const headers = req.headers || {};
205
+ const getHeader = (name) => {
206
+ const raw = headers[name] ?? headers[name.toLowerCase()];
207
+ if (!raw)
208
+ return undefined;
209
+ return Array.isArray(raw) ? raw[0] : raw;
210
+ };
211
+ const runId = getHeader(TRODO_RUN_HEADER);
212
+ if (!runId) {
213
+ next();
214
+ return;
215
+ }
216
+ const parentSpanId = getHeader(TRODO_PARENT_SPAN_HEADER) || null;
217
+ const agentHeader = getHeader(TRODO_AGENT_HEADER);
218
+ const name = agentHeader ||
219
+ `http.${(req.method || 'GET').toUpperCase()}.${req.path || req.url || ''}`;
220
+ const input = {
221
+ method: req.method,
222
+ path: req.path || req.url || req.originalUrl,
223
+ };
224
+ (0, wrapAgent_js_1.joinRun)(processor, teamSiteId, runId, parentSpanId, async (span) => {
225
+ await new Promise((resolve) => {
226
+ if (res.on) {
227
+ res.on('finish', () => {
228
+ span.setOutput({ status_code: res.statusCode });
229
+ resolve();
230
+ });
231
+ res.on('close', () => resolve());
232
+ }
233
+ next();
234
+ });
235
+ }, { name, kind: 'agent', input }).catch(() => {
236
+ /* swallow — SDK never throws into user code */
237
+ });
238
+ };
239
+ }
240
+ /**
241
+ * Return HTTP headers carrying the current run/span context. Use when
242
+ * making outbound HTTP calls to downstream services so they can joinRun
243
+ * instead of creating their own runs.
244
+ */
245
+ function propagationHeaders() {
246
+ const ctx = (0, context_js_1.getActiveContext)();
247
+ if (!ctx)
248
+ return {};
249
+ const headers = { 'X-Trodo-Run-Id': ctx.runId };
250
+ if (ctx.spanId)
251
+ headers['X-Trodo-Parent-Span-Id'] = ctx.spanId;
252
+ return headers;
253
+ }
254
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/otel/helpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TrodoSpanProcessor = void 0;
4
+ class TrodoSpanProcessor {
5
+ constructor(opts) {
6
+ this.queue = [];
7
+ this.pendingByRun = new Map();
8
+ this.joinedRuns = new Set();
9
+ this.timer = null;
10
+ this.stopped = false;
11
+ this.apiClient = opts.apiClient;
12
+ this.maxBatchSize = opts.maxBatchSize ?? 64;
13
+ this.flushIntervalMs = opts.flushIntervalMs ?? 2000;
14
+ this.maxSpansPerRun = opts.maxSpansPerRun ?? 1000;
15
+ this.startTimer();
16
+ }
17
+ startTimer() {
18
+ if (this.timer || this.stopped)
19
+ return;
20
+ this.timer = setInterval(() => {
21
+ this.flushSpans().catch(() => { });
22
+ }, this.flushIntervalMs);
23
+ // Don't block Node from exiting because of our timer.
24
+ if (this.timer && typeof this.timer.unref === 'function') {
25
+ this.timer.unref();
26
+ }
27
+ }
28
+ /** Mark a run as joined — its spans flush immediately via append_spans. */
29
+ markJoined(runId) {
30
+ this.joinedRuns.add(runId);
31
+ }
32
+ unmarkJoined(runId) {
33
+ this.joinedRuns.delete(runId);
34
+ this.pendingByRun.delete(runId);
35
+ }
36
+ /** Read (without removing) the buffered spans for a run — used for run aggregation. */
37
+ getPending(runId) {
38
+ return (this.pendingByRun.get(runId) || []).slice();
39
+ }
40
+ /** Buffer a span for async flush. Called by wrapAgent/withSpan + OTel onEnd. */
41
+ enqueueSpan(span) {
42
+ if (this.joinedRuns.has(span.run_id)) {
43
+ // Owning service is remote — forward each span immediately.
44
+ this.apiClient
45
+ .postSpansAppend(span.run_id, [span])
46
+ .catch(() => { });
47
+ return;
48
+ }
49
+ this.queue.push(span);
50
+ const runBuf = this.pendingByRun.get(span.run_id) || [];
51
+ if (runBuf.length < this.maxSpansPerRun) {
52
+ runBuf.push(span);
53
+ this.pendingByRun.set(span.run_id, runBuf);
54
+ }
55
+ if (this.queue.length >= this.maxBatchSize) {
56
+ this.flushSpans().catch(() => { });
57
+ }
58
+ }
59
+ /** Stream spans for a long-running or joined run without finalising. */
60
+ async appendSpans(runId, spans) {
61
+ if (!spans.length)
62
+ return;
63
+ try {
64
+ await this.apiClient.postSpansAppend(runId, spans);
65
+ }
66
+ catch {
67
+ /* silent */
68
+ }
69
+ }
70
+ /** Fire-and-forget run+spans ingest, used when wrapAgent finalises. */
71
+ async ingestRun(run) {
72
+ const spans = this.pendingByRun.get(run.run_id) || [];
73
+ this.pendingByRun.delete(run.run_id);
74
+ this.queue = this.queue.filter((s) => s.run_id !== run.run_id);
75
+ try {
76
+ await this.apiClient.postRunIngest({
77
+ run: run,
78
+ spans: spans,
79
+ });
80
+ }
81
+ catch {
82
+ /* silent — SDK must never throw into user code */
83
+ }
84
+ }
85
+ async flushSpans() {
86
+ if (this.queue.length === 0)
87
+ return;
88
+ const batch = this.queue.splice(0, this.maxBatchSize);
89
+ // Group pending-only spans by run_id and POST to /runs/:run_id/spans.
90
+ const byRun = new Map();
91
+ for (const span of batch) {
92
+ const arr = byRun.get(span.run_id) || [];
93
+ arr.push(span);
94
+ byRun.set(span.run_id, arr);
95
+ }
96
+ const tasks = [];
97
+ for (const [runId, spans] of byRun.entries()) {
98
+ tasks.push(this.apiClient
99
+ .postSpansAppend(runId, spans)
100
+ .catch(() => {
101
+ // Re-queue for next tick on failure.
102
+ this.queue.push(...spans);
103
+ }));
104
+ }
105
+ await Promise.all(tasks);
106
+ }
107
+ async forceFlush() {
108
+ await this.flushSpans();
109
+ }
110
+ async shutdown() {
111
+ this.stopped = true;
112
+ if (this.timer) {
113
+ clearInterval(this.timer);
114
+ this.timer = null;
115
+ }
116
+ await this.forceFlush();
117
+ }
118
+ // ---------- OpenTelemetry SpanProcessor interface (subset) ----------
119
+ // Implemented so this can be registered with OTel SDK's addSpanProcessor().
120
+ // A thin adapter is in autoInstrument.ts.
121
+ onStart() {
122
+ /* noop — Trodo flushes on span end */
123
+ }
124
+ onEnd(span) {
125
+ this.enqueueSpan(span);
126
+ }
127
+ }
128
+ exports.TrodoSpanProcessor = TrodoSpanProcessor;
129
+ //# sourceMappingURL=processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processor.js","sourceRoot":"","sources":["../../../src/otel/processor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uuidv4 = uuidv4;
4
+ /**
5
+ * RFC4122 v4 UUIDs — used for run_id, span_id, parent_span_id.
6
+ * Uses the runtime's crypto.randomUUID when available, falls back to a
7
+ * Math.random-based generator so we don't pull in a dependency for tests.
8
+ */
9
+ function uuidv4() {
10
+ try {
11
+ const c = globalThis.crypto;
12
+ if (c?.randomUUID)
13
+ return c.randomUUID();
14
+ }
15
+ catch {
16
+ /* fall through */
17
+ }
18
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (ch) => {
19
+ const r = (Math.random() * 16) | 0;
20
+ const v = ch === 'x' ? r : (r & 0x3) | 0x8;
21
+ return v.toString(16);
22
+ });
23
+ }
24
+ //# sourceMappingURL=uuid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uuid.js","sourceRoot":"","sources":["../../../src/otel/uuid.ts"],"names":[],"mappings":";;AAKA,wBAYC;AAjBD;;;;GAIG;AACH,SAAgB,MAAM;IACpB,IAAI,CAAC;QACH,MAAM,CAAC,GAAI,UAAyD,CAAC,MAAM,CAAC;QAC5E,IAAI,CAAC,EAAE,UAAU;YAAE,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;IACD,OAAO,sCAAsC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE;QACpE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,CAAC,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;QAC3C,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC"}