@glassflow-ai/rius 0.1.0 → 0.2.2
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 +49 -0
- package/dist/index.cjs +341 -15
- package/dist/index.d.cts +53 -1
- package/dist/index.d.ts +53 -1
- package/dist/index.js +336 -11
- package/package.json +22 -2
package/dist/index.js
CHANGED
|
@@ -8,6 +8,15 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
// node_modules/tsup/assets/esm_shims.js
|
|
12
|
+
import path from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
var init_esm_shims = __esm({
|
|
15
|
+
"node_modules/tsup/assets/esm_shims.js"() {
|
|
16
|
+
"use strict";
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
11
20
|
// src/config.ts
|
|
12
21
|
function bool(name, raw, fallback) {
|
|
13
22
|
if (raw === void 0) return fallback;
|
|
@@ -25,24 +34,64 @@ function rate(raw, fallback) {
|
|
|
25
34
|
if (Number.isNaN(parsed)) return fallback;
|
|
26
35
|
return Math.min(1, Math.max(0, parsed));
|
|
27
36
|
}
|
|
37
|
+
function seconds(raw, fallback) {
|
|
38
|
+
if (raw === void 0) return fallback;
|
|
39
|
+
const parsed = Number.parseFloat(raw);
|
|
40
|
+
if (Number.isNaN(parsed)) return fallback;
|
|
41
|
+
return parsed;
|
|
42
|
+
}
|
|
43
|
+
function clamp(name, value, min, max) {
|
|
44
|
+
if (value >= min && value <= max) return value;
|
|
45
|
+
const clamped = Math.min(Math.max(value, min), max);
|
|
46
|
+
console.warn(`[rius] ${name}=${value} is outside [${min}, ${max}]; clamped to ${clamped}.`);
|
|
47
|
+
return clamped;
|
|
48
|
+
}
|
|
28
49
|
function resolveConfig(options = {}, env = process.env) {
|
|
29
50
|
const endpoint = (options.endpoint ?? env.RIUS_ENDPOINT ?? DEFAULT_ENDPOINT).replace(/\/+$/, "");
|
|
51
|
+
const serviceName = options.serviceName ?? env.RIUS_SERVICE_NAME ?? DEFAULT_SERVICE_NAME;
|
|
52
|
+
const heartbeatIntervalSeconds = clamp(
|
|
53
|
+
"heartbeatInterval",
|
|
54
|
+
options.heartbeatInterval ?? seconds(env.RIUS_HEARTBEAT_INTERVAL, DEFAULT_HEARTBEAT_INTERVAL),
|
|
55
|
+
HEARTBEAT_INTERVAL_MIN,
|
|
56
|
+
HEARTBEAT_INTERVAL_MAX
|
|
57
|
+
);
|
|
58
|
+
const partialSpansDelaySeconds = clamp(
|
|
59
|
+
"partialSpansDelay",
|
|
60
|
+
options.partialSpansDelay ?? seconds(env.RIUS_PARTIAL_SPANS_DELAY, DEFAULT_PARTIAL_SPANS_DELAY),
|
|
61
|
+
PARTIAL_SPANS_DELAY_MIN,
|
|
62
|
+
PARTIAL_SPANS_DELAY_MAX
|
|
63
|
+
);
|
|
30
64
|
return {
|
|
31
65
|
endpoint,
|
|
32
66
|
apiKey: options.apiKey ?? env.RIUS_API_KEY,
|
|
33
|
-
serviceName
|
|
67
|
+
serviceName,
|
|
34
68
|
disabled: options.disabled ?? bool("RIUS_DISABLED", env.RIUS_DISABLED, false),
|
|
35
69
|
sampleRate: options.sampleRate ?? rate(env.RIUS_SAMPLE_RATE, 1),
|
|
36
70
|
captureContent: options.captureContent ?? bool("RIUS_CAPTURE_CONTENT", env.RIUS_CAPTURE_CONTENT, true),
|
|
37
|
-
mask: options.mask
|
|
71
|
+
mask: options.mask,
|
|
72
|
+
heartbeat: options.heartbeat ?? bool("RIUS_HEARTBEAT", env.RIUS_HEARTBEAT, true),
|
|
73
|
+
heartbeatIntervalMs: heartbeatIntervalSeconds * 1e3,
|
|
74
|
+
heartbeatEndpoint: `${endpoint}/v1/heartbeat`,
|
|
75
|
+
// Empty falls through to serviceName rather than shipping a blank identity
|
|
76
|
+
// on every heartbeat payload, so `??` would be wrong here.
|
|
77
|
+
agentName: options.agentName || env.RIUS_AGENT_NAME || serviceName,
|
|
78
|
+
partialSpans: options.partialSpans ?? bool("RIUS_PARTIAL_SPANS", env.RIUS_PARTIAL_SPANS, false),
|
|
79
|
+
partialSpansDelayMs: partialSpansDelaySeconds * 1e3
|
|
38
80
|
};
|
|
39
81
|
}
|
|
40
|
-
var DEFAULT_ENDPOINT, DEFAULT_SERVICE_NAME, AFFIRMATIVE, NEGATIVE;
|
|
82
|
+
var DEFAULT_ENDPOINT, DEFAULT_SERVICE_NAME, HEARTBEAT_INTERVAL_MIN, HEARTBEAT_INTERVAL_MAX, DEFAULT_HEARTBEAT_INTERVAL, PARTIAL_SPANS_DELAY_MIN, PARTIAL_SPANS_DELAY_MAX, DEFAULT_PARTIAL_SPANS_DELAY, AFFIRMATIVE, NEGATIVE;
|
|
41
83
|
var init_config = __esm({
|
|
42
84
|
"src/config.ts"() {
|
|
43
85
|
"use strict";
|
|
86
|
+
init_esm_shims();
|
|
44
87
|
DEFAULT_ENDPOINT = "https://ingest.eu.console.rius-glassflow.com";
|
|
45
88
|
DEFAULT_SERVICE_NAME = "unknown_service";
|
|
89
|
+
HEARTBEAT_INTERVAL_MIN = 5;
|
|
90
|
+
HEARTBEAT_INTERVAL_MAX = 300;
|
|
91
|
+
DEFAULT_HEARTBEAT_INTERVAL = 15;
|
|
92
|
+
PARTIAL_SPANS_DELAY_MIN = 0;
|
|
93
|
+
PARTIAL_SPANS_DELAY_MAX = 60;
|
|
94
|
+
DEFAULT_PARTIAL_SPANS_DELAY = 0;
|
|
46
95
|
AFFIRMATIVE = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
47
96
|
NEGATIVE = /* @__PURE__ */ new Set(["0", "false", "no", "off"]);
|
|
48
97
|
}
|
|
@@ -53,6 +102,7 @@ var DelegatingSpanProcessor;
|
|
|
53
102
|
var init_delegatingProcessor = __esm({
|
|
54
103
|
"src/delegatingProcessor.ts"() {
|
|
55
104
|
"use strict";
|
|
105
|
+
init_esm_shims();
|
|
56
106
|
DelegatingSpanProcessor = class {
|
|
57
107
|
delegates = [];
|
|
58
108
|
add(processor) {
|
|
@@ -90,6 +140,7 @@ var ExportResultCode;
|
|
|
90
140
|
var init_ExportResult = __esm({
|
|
91
141
|
"node_modules/@opentelemetry/core/build/esm/ExportResult.js"() {
|
|
92
142
|
"use strict";
|
|
143
|
+
init_esm_shims();
|
|
93
144
|
(function(ExportResultCode2) {
|
|
94
145
|
ExportResultCode2[ExportResultCode2["SUCCESS"] = 0] = "SUCCESS";
|
|
95
146
|
ExportResultCode2[ExportResultCode2["FAILED"] = 1] = "FAILED";
|
|
@@ -101,6 +152,7 @@ var init_ExportResult = __esm({
|
|
|
101
152
|
var init_esm = __esm({
|
|
102
153
|
"node_modules/@opentelemetry/core/build/esm/index.js"() {
|
|
103
154
|
"use strict";
|
|
155
|
+
init_esm_shims();
|
|
104
156
|
init_ExportResult();
|
|
105
157
|
}
|
|
106
158
|
});
|
|
@@ -110,6 +162,7 @@ var ExportOutcomeExporter;
|
|
|
110
162
|
var init_exportHealth = __esm({
|
|
111
163
|
"src/exportHealth.ts"() {
|
|
112
164
|
"use strict";
|
|
165
|
+
init_esm_shims();
|
|
113
166
|
init_esm();
|
|
114
167
|
ExportOutcomeExporter = class {
|
|
115
168
|
constructor(inner, onFirstFailure = (m) => console.warn(m)) {
|
|
@@ -152,6 +205,134 @@ var init_exportHealth = __esm({
|
|
|
152
205
|
}
|
|
153
206
|
});
|
|
154
207
|
|
|
208
|
+
// src/heartbeat.ts
|
|
209
|
+
import { randomUUID } from "crypto";
|
|
210
|
+
import { createRequire } from "module";
|
|
211
|
+
function httpTransport(url, headers) {
|
|
212
|
+
return async (payload, timeoutMs) => {
|
|
213
|
+
const response = await fetch(url, {
|
|
214
|
+
method: "POST",
|
|
215
|
+
headers: { "content-type": "application/json", ...headers },
|
|
216
|
+
body: JSON.stringify(payload),
|
|
217
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
218
|
+
});
|
|
219
|
+
await response.body?.cancel().catch(() => {
|
|
220
|
+
});
|
|
221
|
+
if (!response.ok) {
|
|
222
|
+
throw new Error(`heartbeat POST ${url} returned ${response.status}`);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
var PAYLOAD_VERSION, OPEN_TRACES_CAP, DEFAULT_PING_TIMEOUT_MS, DEFAULT_FINAL_PING_TIMEOUT_MS, SDK_VERSION, OpenRootSpanTracker, HeartbeatSender;
|
|
227
|
+
var init_heartbeat = __esm({
|
|
228
|
+
"src/heartbeat.ts"() {
|
|
229
|
+
"use strict";
|
|
230
|
+
init_esm_shims();
|
|
231
|
+
PAYLOAD_VERSION = 1;
|
|
232
|
+
OPEN_TRACES_CAP = 32;
|
|
233
|
+
DEFAULT_PING_TIMEOUT_MS = 3e3;
|
|
234
|
+
DEFAULT_FINAL_PING_TIMEOUT_MS = 1e3;
|
|
235
|
+
SDK_VERSION = (() => {
|
|
236
|
+
try {
|
|
237
|
+
const pkg = createRequire(import.meta.url)("../package.json");
|
|
238
|
+
return pkg.version ?? "0.0.0";
|
|
239
|
+
} catch {
|
|
240
|
+
return "0.0.0";
|
|
241
|
+
}
|
|
242
|
+
})();
|
|
243
|
+
OpenRootSpanTracker = class {
|
|
244
|
+
// trace id (32-hex) -> count of open root spans in that trace. A trace id
|
|
245
|
+
// normally has one root, but the count guards against duplicates.
|
|
246
|
+
open = /* @__PURE__ */ new Map();
|
|
247
|
+
onStart(span, _parentContext) {
|
|
248
|
+
if (span.parentSpanContext !== void 0) return;
|
|
249
|
+
const traceId = span.spanContext().traceId;
|
|
250
|
+
this.open.set(traceId, (this.open.get(traceId) ?? 0) + 1);
|
|
251
|
+
}
|
|
252
|
+
onEnd(span) {
|
|
253
|
+
if (span.parentSpanContext !== void 0) return;
|
|
254
|
+
const traceId = span.spanContext().traceId;
|
|
255
|
+
const count = (this.open.get(traceId) ?? 0) - 1;
|
|
256
|
+
if (count <= 0) {
|
|
257
|
+
this.open.delete(traceId);
|
|
258
|
+
} else {
|
|
259
|
+
this.open.set(traceId, count);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/** Trace ids of currently-open root spans, in the order they opened. */
|
|
263
|
+
openTraceIds() {
|
|
264
|
+
return [...this.open.keys()];
|
|
265
|
+
}
|
|
266
|
+
async forceFlush() {
|
|
267
|
+
}
|
|
268
|
+
async shutdown() {
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
HeartbeatSender = class {
|
|
272
|
+
/** Identity of one process lifetime, fresh per sender. */
|
|
273
|
+
instanceId = randomUUID();
|
|
274
|
+
agentName;
|
|
275
|
+
tracker;
|
|
276
|
+
intervalMs;
|
|
277
|
+
pingTimeoutMs;
|
|
278
|
+
finalPingTimeoutMs;
|
|
279
|
+
send;
|
|
280
|
+
timer;
|
|
281
|
+
stopped = false;
|
|
282
|
+
deliveryWarned = false;
|
|
283
|
+
constructor(opts) {
|
|
284
|
+
this.agentName = opts.agentName;
|
|
285
|
+
this.tracker = opts.tracker;
|
|
286
|
+
this.intervalMs = opts.intervalMs;
|
|
287
|
+
this.pingTimeoutMs = opts.pingTimeoutMs ?? DEFAULT_PING_TIMEOUT_MS;
|
|
288
|
+
this.finalPingTimeoutMs = opts.finalPingTimeoutMs ?? DEFAULT_FINAL_PING_TIMEOUT_MS;
|
|
289
|
+
this.send = opts.transport ?? httpTransport(opts.url, opts.headers);
|
|
290
|
+
}
|
|
291
|
+
/** Starts pinging: an immediate first ping, then every `intervalMs`. */
|
|
292
|
+
start() {
|
|
293
|
+
void this.ping(this.pingTimeoutMs, false);
|
|
294
|
+
const timer = setInterval(() => void this.ping(this.pingTimeoutMs, false), this.intervalMs);
|
|
295
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
296
|
+
this.timer = timer;
|
|
297
|
+
}
|
|
298
|
+
/** Stops the interval and sends the final `stopped: true` ping. Idempotent. */
|
|
299
|
+
async stop() {
|
|
300
|
+
if (this.stopped) return;
|
|
301
|
+
this.stopped = true;
|
|
302
|
+
if (this.timer !== void 0) clearInterval(this.timer);
|
|
303
|
+
await this.ping(this.finalPingTimeoutMs, true);
|
|
304
|
+
}
|
|
305
|
+
buildPayload(stopped) {
|
|
306
|
+
const openIds = this.tracker.openTraceIds();
|
|
307
|
+
const payload = {
|
|
308
|
+
v: PAYLOAD_VERSION,
|
|
309
|
+
instance_id: this.instanceId,
|
|
310
|
+
agent_name: this.agentName,
|
|
311
|
+
// RFC3339 UTC with millisecond precision and a Z suffix, already.
|
|
312
|
+
sent_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
313
|
+
sdk_language: "typescript",
|
|
314
|
+
sdk_version: SDK_VERSION,
|
|
315
|
+
open_traces: openIds.slice(0, OPEN_TRACES_CAP),
|
|
316
|
+
open_trace_count: openIds.length
|
|
317
|
+
};
|
|
318
|
+
if (stopped) payload.stopped = true;
|
|
319
|
+
return payload;
|
|
320
|
+
}
|
|
321
|
+
async ping(timeoutMs, stopped) {
|
|
322
|
+
try {
|
|
323
|
+
await this.send(this.buildPayload(stopped), timeoutMs);
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (!this.deliveryWarned) {
|
|
326
|
+
this.deliveryWarned = true;
|
|
327
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
328
|
+
console.warn(`[rius] heartbeat delivery failed (${message}); further failures are silent.`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
155
336
|
// src/semconv.ts
|
|
156
337
|
function kindAttributes(kind) {
|
|
157
338
|
const attributes = { [OPENINFERENCE_SPAN_KIND]: kind };
|
|
@@ -159,10 +340,11 @@ function kindAttributes(kind) {
|
|
|
159
340
|
if (operation !== void 0) attributes[GEN_AI_OPERATION_NAME] = operation;
|
|
160
341
|
return attributes;
|
|
161
342
|
}
|
|
162
|
-
var TRACER_NAME, OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_MODEL, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_TOOL_NAME, GEN_AI_REQUEST_PREFIX, MCP_RESULT_TYPE, GEN_AI_FIRST_TOKEN_EVENT, SpanKind, OPERATION_BY_KIND, CONTENT_ATTRIBUTES, CONTENT_ATTRIBUTE_PREFIXES, CONTENT_ATTRIBUTE_SUFFIXES;
|
|
343
|
+
var TRACER_NAME, OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_MODEL, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_TOOL_NAME, GEN_AI_REQUEST_PREFIX, MCP_RESULT_TYPE, GEN_AI_FIRST_TOKEN_EVENT, SpanKind, OPERATION_BY_KIND, CONTENT_ATTRIBUTES, CONTENT_ATTRIBUTE_PREFIXES, CONTENT_ATTRIBUTE_SUFFIXES, GLASSFLOW_SPAN_PENDING, PENDING_IDENTITY_ATTRIBUTES, PENDING_IDENTITY_PREFIXES;
|
|
163
344
|
var init_semconv = __esm({
|
|
164
345
|
"src/semconv.ts"() {
|
|
165
346
|
"use strict";
|
|
347
|
+
init_esm_shims();
|
|
166
348
|
TRACER_NAME = "glassflow";
|
|
167
349
|
OPENINFERENCE_SPAN_KIND = "openinference.span.kind";
|
|
168
350
|
INPUT_VALUE = "input.value";
|
|
@@ -227,6 +409,14 @@ var init_semconv = __esm({
|
|
|
227
409
|
".document.content",
|
|
228
410
|
".embedding.text"
|
|
229
411
|
];
|
|
412
|
+
GLASSFLOW_SPAN_PENDING = "glassflow.span.pending";
|
|
413
|
+
PENDING_IDENTITY_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
414
|
+
OPENINFERENCE_SPAN_KIND,
|
|
415
|
+
GEN_AI_OPERATION_NAME,
|
|
416
|
+
GEN_AI_PROVIDER_NAME,
|
|
417
|
+
GEN_AI_TOOL_NAME
|
|
418
|
+
]);
|
|
419
|
+
PENDING_IDENTITY_PREFIXES = [GEN_AI_REQUEST_PREFIX];
|
|
230
420
|
}
|
|
231
421
|
});
|
|
232
422
|
|
|
@@ -255,6 +445,7 @@ function toAttributeValue(value) {
|
|
|
255
445
|
var init_serde = __esm({
|
|
256
446
|
"src/serde.ts"() {
|
|
257
447
|
"use strict";
|
|
448
|
+
init_esm_shims();
|
|
258
449
|
}
|
|
259
450
|
});
|
|
260
451
|
|
|
@@ -292,6 +483,7 @@ var Observation;
|
|
|
292
483
|
var init_spans = __esm({
|
|
293
484
|
"src/spans.ts"() {
|
|
294
485
|
"use strict";
|
|
486
|
+
init_esm_shims();
|
|
295
487
|
init_client();
|
|
296
488
|
init_semconv();
|
|
297
489
|
init_serde();
|
|
@@ -384,6 +576,7 @@ var INPUT_REQUIRED;
|
|
|
384
576
|
var init_instrumentationMcp = __esm({
|
|
385
577
|
"src/instrumentationMcp.ts"() {
|
|
386
578
|
"use strict";
|
|
579
|
+
init_esm_shims();
|
|
387
580
|
init_semconv();
|
|
388
581
|
init_spans();
|
|
389
582
|
INPUT_REQUIRED = "input_required";
|
|
@@ -391,7 +584,7 @@ var init_instrumentationMcp = __esm({
|
|
|
391
584
|
});
|
|
392
585
|
|
|
393
586
|
// src/instrumentation.ts
|
|
394
|
-
import { createRequire } from "module";
|
|
587
|
+
import { createRequire as createRequire2 } from "module";
|
|
395
588
|
import { join, sep } from "path";
|
|
396
589
|
import { registerInstrumentations } from "@opentelemetry/instrumentation";
|
|
397
590
|
function packageOf(specifier) {
|
|
@@ -437,7 +630,7 @@ function warnBroken(subject, error) {
|
|
|
437
630
|
function cachedCjsExports(pkg, toPatchable) {
|
|
438
631
|
let cache;
|
|
439
632
|
try {
|
|
440
|
-
cache =
|
|
633
|
+
cache = createRequire2(join(process.cwd(), "noop.js")).cache;
|
|
441
634
|
} catch {
|
|
442
635
|
return void 0;
|
|
443
636
|
}
|
|
@@ -506,6 +699,7 @@ var dynamicImport, REGISTRY;
|
|
|
506
699
|
var init_instrumentation = __esm({
|
|
507
700
|
"src/instrumentation.ts"() {
|
|
508
701
|
"use strict";
|
|
702
|
+
init_esm_shims();
|
|
509
703
|
dynamicImport = new Function("s", "return import(s)");
|
|
510
704
|
REGISTRY = [
|
|
511
705
|
{
|
|
@@ -600,6 +794,7 @@ var EXCEPTION_EVENT_NAME, EXCEPTION_CONTENT_KEYS, MaskingSpanExporter;
|
|
|
600
794
|
var init_masking = __esm({
|
|
601
795
|
"src/masking.ts"() {
|
|
602
796
|
"use strict";
|
|
797
|
+
init_esm_shims();
|
|
603
798
|
init_semconv();
|
|
604
799
|
init_serde();
|
|
605
800
|
EXCEPTION_EVENT_NAME = "exception";
|
|
@@ -661,6 +856,93 @@ var init_masking = __esm({
|
|
|
661
856
|
}
|
|
662
857
|
});
|
|
663
858
|
|
|
859
|
+
// src/pending.ts
|
|
860
|
+
import { SpanStatusCode as SpanStatusCode3 } from "@opentelemetry/api";
|
|
861
|
+
function identityAttributes(attributes) {
|
|
862
|
+
if (!attributes) return {};
|
|
863
|
+
const result = {};
|
|
864
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
865
|
+
if (PENDING_IDENTITY_ATTRIBUTES.has(key) || PENDING_IDENTITY_PREFIXES.some((p) => key.startsWith(p))) {
|
|
866
|
+
result[key] = value;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
return result;
|
|
870
|
+
}
|
|
871
|
+
function buildSnapshot(span) {
|
|
872
|
+
const attributes = identityAttributes(span.attributes);
|
|
873
|
+
attributes[GLASSFLOW_SPAN_PENDING] = true;
|
|
874
|
+
return {
|
|
875
|
+
name: span.name,
|
|
876
|
+
kind: span.kind,
|
|
877
|
+
spanContext: () => span.spanContext(),
|
|
878
|
+
parentSpanContext: span.parentSpanContext,
|
|
879
|
+
startTime: span.startTime,
|
|
880
|
+
endTime: span.startTime,
|
|
881
|
+
duration: [0, 0],
|
|
882
|
+
status: { code: SpanStatusCode3.UNSET },
|
|
883
|
+
attributes,
|
|
884
|
+
links: [],
|
|
885
|
+
events: [],
|
|
886
|
+
resource: span.resource,
|
|
887
|
+
instrumentationScope: span.instrumentationScope,
|
|
888
|
+
droppedAttributesCount: 0,
|
|
889
|
+
droppedEventsCount: 0,
|
|
890
|
+
droppedLinksCount: 0,
|
|
891
|
+
ended: true
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
function spanKey(context) {
|
|
895
|
+
return `${context.traceId}:${context.spanId}`;
|
|
896
|
+
}
|
|
897
|
+
var PendingSpanProcessor;
|
|
898
|
+
var init_pending = __esm({
|
|
899
|
+
"src/pending.ts"() {
|
|
900
|
+
"use strict";
|
|
901
|
+
init_esm_shims();
|
|
902
|
+
init_semconv();
|
|
903
|
+
PendingSpanProcessor = class {
|
|
904
|
+
delegate;
|
|
905
|
+
delayMs;
|
|
906
|
+
scheduled = /* @__PURE__ */ new Map();
|
|
907
|
+
constructor(delegate, opts) {
|
|
908
|
+
this.delegate = delegate;
|
|
909
|
+
this.delayMs = opts?.delayMs ?? 0;
|
|
910
|
+
}
|
|
911
|
+
onStart(span, _parentContext) {
|
|
912
|
+
if (!span.isRecording()) return;
|
|
913
|
+
const snapshot = buildSnapshot(span);
|
|
914
|
+
if (this.delayMs <= 0) {
|
|
915
|
+
this.delegate.onEnd(snapshot);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
const key = spanKey(span.spanContext());
|
|
919
|
+
const timer = setTimeout(() => {
|
|
920
|
+
this.scheduled.delete(key);
|
|
921
|
+
try {
|
|
922
|
+
this.delegate.onEnd(snapshot);
|
|
923
|
+
} catch {
|
|
924
|
+
}
|
|
925
|
+
}, this.delayMs);
|
|
926
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
927
|
+
this.scheduled.set(key, timer);
|
|
928
|
+
}
|
|
929
|
+
onEnd(span) {
|
|
930
|
+
const key = spanKey(span.spanContext());
|
|
931
|
+
const timer = this.scheduled.get(key);
|
|
932
|
+
if (timer === void 0) return;
|
|
933
|
+
clearTimeout(timer);
|
|
934
|
+
this.scheduled.delete(key);
|
|
935
|
+
}
|
|
936
|
+
async shutdown() {
|
|
937
|
+
for (const timer of this.scheduled.values()) clearTimeout(timer);
|
|
938
|
+
this.scheduled.clear();
|
|
939
|
+
}
|
|
940
|
+
async forceFlush() {
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
|
|
664
946
|
// src/client.ts
|
|
665
947
|
import { trace } from "@opentelemetry/api";
|
|
666
948
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
@@ -681,18 +963,23 @@ function init(options = {}) {
|
|
|
681
963
|
}
|
|
682
964
|
const config = resolveConfig(options);
|
|
683
965
|
const processors = new DelegatingSpanProcessor();
|
|
966
|
+
const authHeaders = config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {};
|
|
684
967
|
let health;
|
|
685
968
|
if (!config.disabled) {
|
|
686
969
|
const base = options.spanExporter ?? new OTLPTraceExporter({
|
|
687
970
|
url: `${config.endpoint}/v1/traces`,
|
|
688
|
-
headers: config.apiKey ?
|
|
971
|
+
headers: config.apiKey ? authHeaders : void 0
|
|
689
972
|
});
|
|
690
973
|
health = new ExportOutcomeExporter(base);
|
|
691
974
|
const exporter = !config.captureContent || config.mask !== void 0 ? new MaskingSpanExporter(health, {
|
|
692
975
|
captureContent: config.captureContent,
|
|
693
976
|
mask: config.mask
|
|
694
977
|
}) : health;
|
|
695
|
-
|
|
978
|
+
const batch = new BatchSpanProcessor(exporter);
|
|
979
|
+
if (config.partialSpans) {
|
|
980
|
+
processors.add(new PendingSpanProcessor(batch, { delayMs: config.partialSpansDelayMs }));
|
|
981
|
+
}
|
|
982
|
+
processors.add(batch);
|
|
696
983
|
}
|
|
697
984
|
const provider = new NodeTracerProvider({
|
|
698
985
|
resource: resourceFromAttributes({ [ATTR_SERVICE_NAME]: config.serviceName }),
|
|
@@ -706,23 +993,46 @@ function init(options = {}) {
|
|
|
706
993
|
});
|
|
707
994
|
const ready = config.disabled ? Promise.resolve([]) : enableInstrumentations(processors, provider).catch(() => []);
|
|
708
995
|
provider.register();
|
|
709
|
-
|
|
996
|
+
let heartbeat;
|
|
997
|
+
if (config.heartbeat && !config.disabled) {
|
|
998
|
+
const tracker = new OpenRootSpanTracker();
|
|
999
|
+
processors.add(tracker);
|
|
1000
|
+
const sender = new HeartbeatSender({
|
|
1001
|
+
url: config.heartbeatEndpoint,
|
|
1002
|
+
headers: authHeaders,
|
|
1003
|
+
intervalMs: config.heartbeatIntervalMs,
|
|
1004
|
+
agentName: config.agentName,
|
|
1005
|
+
tracker,
|
|
1006
|
+
transport: options.heartbeatTransport
|
|
1007
|
+
});
|
|
1008
|
+
sender.start();
|
|
1009
|
+
const beforeExitHandler = () => {
|
|
1010
|
+
void sender.stop();
|
|
1011
|
+
};
|
|
1012
|
+
process.once("beforeExit", beforeExitHandler);
|
|
1013
|
+
heartbeat = { sender, beforeExitHandler };
|
|
1014
|
+
}
|
|
1015
|
+
globalClient = createClient({ provider, processors, health, ready, heartbeat });
|
|
710
1016
|
return globalClient;
|
|
711
1017
|
}
|
|
712
1018
|
function getTracer() {
|
|
713
1019
|
return trace.getTracer(TRACER_NAME);
|
|
714
1020
|
}
|
|
715
|
-
var sinks, createClient, RiusClient, globalClient;
|
|
1021
|
+
var sinks, heartbeats, createClient, RiusClient, globalClient;
|
|
716
1022
|
var init_client = __esm({
|
|
717
1023
|
"src/client.ts"() {
|
|
718
1024
|
"use strict";
|
|
1025
|
+
init_esm_shims();
|
|
719
1026
|
init_config();
|
|
720
1027
|
init_delegatingProcessor();
|
|
721
1028
|
init_exportHealth();
|
|
1029
|
+
init_heartbeat();
|
|
722
1030
|
init_instrumentation();
|
|
723
1031
|
init_masking();
|
|
1032
|
+
init_pending();
|
|
724
1033
|
init_semconv();
|
|
725
1034
|
sinks = /* @__PURE__ */ new WeakMap();
|
|
1035
|
+
heartbeats = /* @__PURE__ */ new WeakMap();
|
|
726
1036
|
RiusClient = class _RiusClient {
|
|
727
1037
|
provider;
|
|
728
1038
|
health;
|
|
@@ -733,6 +1043,7 @@ var init_client = __esm({
|
|
|
733
1043
|
this.health = parts.health;
|
|
734
1044
|
this.ready = parts.ready;
|
|
735
1045
|
sinks.set(this, parts.processors);
|
|
1046
|
+
if (parts.heartbeat) heartbeats.set(this, parts.heartbeat);
|
|
736
1047
|
}
|
|
737
1048
|
static {
|
|
738
1049
|
createClient = (parts) => new _RiusClient(parts);
|
|
@@ -745,8 +1056,19 @@ var init_client = __esm({
|
|
|
745
1056
|
/**
|
|
746
1057
|
* Drains and tears down the provider, then releases the global registration
|
|
747
1058
|
* so a later init() can reconfigure the SDK.
|
|
1059
|
+
*
|
|
1060
|
+
* The heartbeat's final `stopped: true` ping is sent before the provider
|
|
1061
|
+
* shuts down, so the backend hears "stopped" while the trace pipeline can
|
|
1062
|
+
* still export it. Idempotent: `sender.stop()` no-ops on a second call, and
|
|
1063
|
+
* the `beforeExit` listener is removed here so repeated init/shutdown
|
|
1064
|
+
* cycles never leak listeners.
|
|
748
1065
|
*/
|
|
749
1066
|
async shutdown() {
|
|
1067
|
+
const heartbeat = heartbeats.get(this);
|
|
1068
|
+
if (heartbeat) {
|
|
1069
|
+
process.removeListener("beforeExit", heartbeat.beforeExitHandler);
|
|
1070
|
+
await heartbeat.sender.stop();
|
|
1071
|
+
}
|
|
750
1072
|
try {
|
|
751
1073
|
await this.provider.shutdown();
|
|
752
1074
|
} finally {
|
|
@@ -761,9 +1083,11 @@ var init_client = __esm({
|
|
|
761
1083
|
});
|
|
762
1084
|
|
|
763
1085
|
// src/index.ts
|
|
1086
|
+
init_esm_shims();
|
|
764
1087
|
init_client();
|
|
765
1088
|
|
|
766
1089
|
// src/generation.ts
|
|
1090
|
+
init_esm_shims();
|
|
767
1091
|
init_client();
|
|
768
1092
|
init_semconv();
|
|
769
1093
|
init_serde();
|
|
@@ -851,6 +1175,7 @@ function startAsCurrentGeneration(name, optionsOrFn, maybeFn) {
|
|
|
851
1175
|
}
|
|
852
1176
|
|
|
853
1177
|
// src/observe.ts
|
|
1178
|
+
init_esm_shims();
|
|
854
1179
|
init_spans();
|
|
855
1180
|
function observe(fn, options = {}) {
|
|
856
1181
|
const name = options.name ?? (fn.name || "anonymous");
|
|
@@ -872,7 +1197,7 @@ function observe(fn, options = {}) {
|
|
|
872
1197
|
// src/index.ts
|
|
873
1198
|
init_semconv();
|
|
874
1199
|
init_spans();
|
|
875
|
-
var VERSION = "0.
|
|
1200
|
+
var VERSION = "0.2.2";
|
|
876
1201
|
export {
|
|
877
1202
|
Generation,
|
|
878
1203
|
Observation,
|
package/package.json
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glassflow-ai/rius",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "OpenTelemetry-native tracing for AI agents and LLM applications",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"opentelemetry",
|
|
7
|
+
"otel",
|
|
8
|
+
"observability",
|
|
9
|
+
"tracing",
|
|
10
|
+
"llm",
|
|
11
|
+
"ai-agents",
|
|
12
|
+
"genai",
|
|
13
|
+
"instrumentation",
|
|
14
|
+
"rius",
|
|
15
|
+
"glassflow"
|
|
16
|
+
],
|
|
5
17
|
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/glassflow/rius-sdk-typescript.git"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://docs.glassflow.ai/rius/sdk",
|
|
23
|
+
"bugs": "https://github.com/glassflow/rius-sdk-typescript/issues",
|
|
6
24
|
"type": "module",
|
|
7
25
|
"engines": {
|
|
8
26
|
"node": ">=18"
|
|
@@ -22,7 +40,9 @@
|
|
|
22
40
|
}
|
|
23
41
|
}
|
|
24
42
|
},
|
|
25
|
-
"files": [
|
|
43
|
+
"files": [
|
|
44
|
+
"dist"
|
|
45
|
+
],
|
|
26
46
|
"scripts": {
|
|
27
47
|
"build": "tsup",
|
|
28
48
|
"test": "vitest run",
|