@glassflow-ai/rius 0.6.1 → 0.8.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 +24 -2
- package/dist/index.cjs +138 -63
- package/dist/index.d.cts +32 -9
- package/dist/index.d.ts +32 -9
- package/dist/index.js +107 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -118,8 +118,30 @@ automatically and recording any thrown exception. The options argument is
|
|
|
118
118
|
optional in both, so `startAsCurrentSpan("step", async (span) => { ... })`
|
|
119
119
|
works when you have nothing to configure.
|
|
120
120
|
|
|
121
|
+
`kind` sets the span's taxonomy (`openinference.span.kind`, our `SpanKind`
|
|
122
|
+
enum: CHAIN by default, or TOOL, RETRIEVER, EMBEDDING, AGENT, LLM). A TOOL
|
|
123
|
+
span also carries `gen_ai.tool.name`, set to the span name. Each kind also
|
|
124
|
+
decides the OpenTelemetry `SpanKind` field the conventions expect: LLM,
|
|
125
|
+
EMBEDDING and RETRIEVER spans are CLIENT, everything else INTERNAL. Pass
|
|
126
|
+
`otelKind` to override it, for example CLIENT for a call to a hosted agent.
|
|
127
|
+
Note the name collision: `SpanKind` exported by this package is the taxonomy
|
|
128
|
+
enum; the value for `otelKind` is `SpanKind` from `@opentelemetry/api`, so
|
|
129
|
+
import that one under an alias.
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { SpanKind as OtelSpanKind } from "@opentelemetry/api";
|
|
133
|
+
import { SpanKind, startAsCurrentSpan } from "@glassflow-ai/rius";
|
|
134
|
+
|
|
135
|
+
await startAsCurrentSpan(
|
|
136
|
+
"delegate-to-planner",
|
|
137
|
+
{ kind: SpanKind.AGENT, otelKind: OtelSpanKind.CLIENT, input: task },
|
|
138
|
+
async (span) => { ... },
|
|
139
|
+
);
|
|
140
|
+
```
|
|
141
|
+
|
|
121
142
|
On the manual path, `recordException()` does what the scoped form does for
|
|
122
|
-
you: it records the error
|
|
143
|
+
you: it records the error, sets ERROR status and sets `error.type` to the
|
|
144
|
+
error's name.
|
|
123
145
|
|
|
124
146
|
```ts
|
|
125
147
|
const span = startSpan("fetch-documents");
|
|
@@ -182,7 +204,7 @@ Install only the ones you use:
|
|
|
182
204
|
- **`anthropic`** wraps the Anthropic SDK, via `@arizeai/openinference-instrumentation-anthropic`, turning Messages calls into LLM spans with model, messages and token counts.
|
|
183
205
|
- **`langchain`** traces LangChain.js chains, models, tools and retrievers, via `@arizeai/openinference-instrumentation-langchain`. It patches the callback manager in `@langchain/core`, which every LangChain.js application already has, and the SDK resolves that module for you so nothing has to be passed in at `init()`.
|
|
184
206
|
- **`vercel-ai`** attaches to spans the Vercel AI SDK's own OpenTelemetry integration produces, via `@arizeai/openinference-vercel`, adding OpenInference attributes to them. This package requires Node 22 or newer.
|
|
185
|
-
- **`mcp`** patches `@modelcontextprotocol/sdk`'s `Client.callTool` so every MCP tool call becomes a TOOL span
|
|
207
|
+
- **`mcp`** patches `@modelcontextprotocol/sdk`'s `Client.callTool` so every MCP tool call becomes a TOOL span named `execute_tool <tool>`, carrying the tool name, arguments, result, latency, and error status. The span follows the OpenTelemetry MCP conventions: OTel `SpanKind` CLIENT, `mcp.method.name` set to `tools/call`, `mcp.protocol.version` set to the negotiated version when the transport exposes it (Streamable HTTP does; stdio and SSE do not), and `error.type` set to `tool_error` when the server returns an `isError` result.
|
|
186
208
|
|
|
187
209
|
There is no selection option: `init()` always attempts every bundled
|
|
188
210
|
integration, so which ones actually attach is determined entirely by
|
package/dist/index.cjs
CHANGED
|
@@ -367,17 +367,22 @@ var init_heartbeat = __esm({
|
|
|
367
367
|
});
|
|
368
368
|
|
|
369
369
|
// src/semconv.ts
|
|
370
|
-
function
|
|
370
|
+
function otelSpanKind(kind) {
|
|
371
|
+
return OTEL_KIND_BY_KIND[kind];
|
|
372
|
+
}
|
|
373
|
+
function kindAttributes(kind, name) {
|
|
371
374
|
const attributes = { [OPENINFERENCE_SPAN_KIND]: kind };
|
|
372
375
|
const operation = OPERATION_BY_KIND[kind];
|
|
373
376
|
if (operation !== void 0) attributes[GEN_AI_OPERATION_NAME] = operation;
|
|
377
|
+
if (kind === "TOOL" /* TOOL */ && name !== void 0) attributes[GEN_AI_TOOL_NAME] = name;
|
|
374
378
|
return attributes;
|
|
375
379
|
}
|
|
376
|
-
var TRACER_NAME, SERVICE_INSTANCE_ID, OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, SESSION_ID, USER_ID, WORKSPACE_ROUTE, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_REQUEST_REASONING_LEVEL, GEN_AI_RESPONSE_MODEL, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS, GEN_AI_USAGE_REASONING_OUTPUT_TOKENS, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_TOOL_NAME, GEN_AI_TOOL_DEFINITIONS, GEN_AI_REQUEST_PREFIX, MCP_RESULT_TYPE, GEN_AI_FIRST_TOKEN_EVENT, SpanKind, OPERATION_BY_KIND, CONTENT_ATTRIBUTES, LLM_INVOCATION_PARAMETERS, INVOCATION_PARAMETERS_CONTENT_MEMBERS, CONTENT_ATTRIBUTE_PREFIXES, CONTENT_ATTRIBUTE_SUFFIXES, GLASSFLOW_SPAN_PENDING, PENDING_IDENTITY_ATTRIBUTES, PENDING_IDENTITY_PREFIXES;
|
|
380
|
+
var import_api, TRACER_NAME, SERVICE_INSTANCE_ID, OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, SESSION_ID, USER_ID, WORKSPACE_ROUTE, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_REQUEST_REASONING_LEVEL, GEN_AI_RESPONSE_MODEL, GEN_AI_REQUEST_STREAM, GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS, GEN_AI_USAGE_REASONING_OUTPUT_TOKENS, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_TOOL_NAME, GEN_AI_TOOL_DEFINITIONS, GEN_AI_REQUEST_PREFIX, MCP_METHOD_NAME, MCP_METHOD_TOOLS_CALL, MCP_PROTOCOL_VERSION, ERROR_TYPE, ERROR_TYPE_TOOL_ERROR, MCP_RESULT_TYPE, GEN_AI_FIRST_TOKEN_EVENT, SpanKind, OPERATION_BY_KIND, OTEL_KIND_BY_KIND, CONTENT_ATTRIBUTES, LLM_INVOCATION_PARAMETERS, INVOCATION_PARAMETERS_CONTENT_MEMBERS, CONTENT_ATTRIBUTE_PREFIXES, CONTENT_ATTRIBUTE_SUFFIXES, GLASSFLOW_SPAN_PENDING, PENDING_IDENTITY_ATTRIBUTES, PENDING_IDENTITY_PREFIXES;
|
|
377
381
|
var init_semconv = __esm({
|
|
378
382
|
"src/semconv.ts"() {
|
|
379
383
|
"use strict";
|
|
380
384
|
init_cjs_shims();
|
|
385
|
+
import_api = require("@opentelemetry/api");
|
|
381
386
|
TRACER_NAME = "glassflow";
|
|
382
387
|
SERVICE_INSTANCE_ID = "service.instance.id";
|
|
383
388
|
OPENINFERENCE_SPAN_KIND = "openinference.span.kind";
|
|
@@ -391,6 +396,8 @@ var init_semconv = __esm({
|
|
|
391
396
|
GEN_AI_REQUEST_MODEL = "gen_ai.request.model";
|
|
392
397
|
GEN_AI_REQUEST_REASONING_LEVEL = "gen_ai.request.reasoning.level";
|
|
393
398
|
GEN_AI_RESPONSE_MODEL = "gen_ai.response.model";
|
|
399
|
+
GEN_AI_REQUEST_STREAM = "gen_ai.request.stream";
|
|
400
|
+
GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK = "gen_ai.response.time_to_first_chunk";
|
|
394
401
|
GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens";
|
|
395
402
|
GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens";
|
|
396
403
|
GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens";
|
|
@@ -402,6 +409,11 @@ var init_semconv = __esm({
|
|
|
402
409
|
GEN_AI_TOOL_NAME = "gen_ai.tool.name";
|
|
403
410
|
GEN_AI_TOOL_DEFINITIONS = "gen_ai.tool.definitions";
|
|
404
411
|
GEN_AI_REQUEST_PREFIX = "gen_ai.request.";
|
|
412
|
+
MCP_METHOD_NAME = "mcp.method.name";
|
|
413
|
+
MCP_METHOD_TOOLS_CALL = "tools/call";
|
|
414
|
+
MCP_PROTOCOL_VERSION = "mcp.protocol.version";
|
|
415
|
+
ERROR_TYPE = "error.type";
|
|
416
|
+
ERROR_TYPE_TOOL_ERROR = "tool_error";
|
|
405
417
|
MCP_RESULT_TYPE = "mcp.result_type";
|
|
406
418
|
GEN_AI_FIRST_TOKEN_EVENT = "gen_ai.first_token";
|
|
407
419
|
SpanKind = /* @__PURE__ */ ((SpanKind2) => {
|
|
@@ -419,6 +431,14 @@ var init_semconv = __esm({
|
|
|
419
431
|
["EMBEDDING" /* EMBEDDING */]: "embeddings",
|
|
420
432
|
["AGENT" /* AGENT */]: "invoke_agent"
|
|
421
433
|
};
|
|
434
|
+
OTEL_KIND_BY_KIND = {
|
|
435
|
+
["LLM" /* LLM */]: import_api.SpanKind.CLIENT,
|
|
436
|
+
["EMBEDDING" /* EMBEDDING */]: import_api.SpanKind.CLIENT,
|
|
437
|
+
["RETRIEVER" /* RETRIEVER */]: import_api.SpanKind.CLIENT,
|
|
438
|
+
["TOOL" /* TOOL */]: import_api.SpanKind.INTERNAL,
|
|
439
|
+
["AGENT" /* AGENT */]: import_api.SpanKind.INTERNAL,
|
|
440
|
+
["CHAIN" /* CHAIN */]: import_api.SpanKind.INTERNAL
|
|
441
|
+
};
|
|
422
442
|
CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
423
443
|
INPUT_VALUE,
|
|
424
444
|
OUTPUT_VALUE,
|
|
@@ -503,6 +523,11 @@ var init_semconv = __esm({
|
|
|
503
523
|
GEN_AI_OPERATION_NAME,
|
|
504
524
|
GEN_AI_PROVIDER_NAME,
|
|
505
525
|
GEN_AI_TOOL_NAME,
|
|
526
|
+
// Protocol identity, not content: a still-running MCP call must be
|
|
527
|
+
// distinguishable from a local tool in the live view — the one place
|
|
528
|
+
// setting the marker at creation pays off.
|
|
529
|
+
MCP_METHOD_NAME,
|
|
530
|
+
MCP_PROTOCOL_VERSION,
|
|
506
531
|
// Identity, not content: a pending span must be groupable into its
|
|
507
532
|
// session while still running, that is the live view's whole point.
|
|
508
533
|
SESSION_ID,
|
|
@@ -531,6 +556,9 @@ function renderable(raw, value) {
|
|
|
531
556
|
if (raw instanceof ArrayBuffer) return `<ArrayBuffer ${raw.byteLength} bytes>`;
|
|
532
557
|
return value;
|
|
533
558
|
}
|
|
559
|
+
function asRecord(value) {
|
|
560
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
561
|
+
}
|
|
534
562
|
function toAttributeValue(value) {
|
|
535
563
|
if (typeof value === "string") return truncate(value);
|
|
536
564
|
if (typeof value === "number" || typeof value === "boolean") return value;
|
|
@@ -555,6 +583,9 @@ function toAttributeValue(value) {
|
|
|
555
583
|
return "[unserializable]";
|
|
556
584
|
}
|
|
557
585
|
}
|
|
586
|
+
function errorType(error) {
|
|
587
|
+
return error instanceof Error ? error.name : typeof error;
|
|
588
|
+
}
|
|
558
589
|
var MAX_ATTR_CHARS, TRUNCATION_MARKER;
|
|
559
590
|
var init_serde = __esm({
|
|
560
591
|
"src/serde.ts"() {
|
|
@@ -567,16 +598,16 @@ var init_serde = __esm({
|
|
|
567
598
|
|
|
568
599
|
// src/user.ts
|
|
569
600
|
function withUser(userId, fn) {
|
|
570
|
-
return
|
|
601
|
+
return import_api2.context.with(import_api2.context.active().setValue(USER_KEY, userId), () => fn(userId));
|
|
571
602
|
}
|
|
572
|
-
var
|
|
603
|
+
var import_api2, USER_KEY, UserSpanProcessor;
|
|
573
604
|
var init_user = __esm({
|
|
574
605
|
"src/user.ts"() {
|
|
575
606
|
"use strict";
|
|
576
607
|
init_cjs_shims();
|
|
577
|
-
|
|
608
|
+
import_api2 = require("@opentelemetry/api");
|
|
578
609
|
init_semconv();
|
|
579
|
-
USER_KEY = (0,
|
|
610
|
+
USER_KEY = (0, import_api2.createContextKey)("rius-user-id");
|
|
580
611
|
UserSpanProcessor = class {
|
|
581
612
|
onStart(span, parentContext) {
|
|
582
613
|
const value = parentContext.getValue(USER_KEY);
|
|
@@ -597,27 +628,34 @@ function configure(observation, options) {
|
|
|
597
628
|
if (options.input !== void 0) observation.setInput(options.input);
|
|
598
629
|
return observation;
|
|
599
630
|
}
|
|
600
|
-
function creationAttributes(options) {
|
|
601
|
-
const attributes = {
|
|
631
|
+
function creationAttributes(name, options) {
|
|
632
|
+
const attributes = {
|
|
633
|
+
...kindAttributes(options.kind ?? "CHAIN" /* CHAIN */, name),
|
|
634
|
+
...options.attributes
|
|
635
|
+
};
|
|
602
636
|
if (options.userId !== void 0) attributes[USER_ID] = options.userId;
|
|
603
637
|
return attributes;
|
|
604
638
|
}
|
|
605
639
|
function startSpan(name, options = {}) {
|
|
606
|
-
const span = getTracer().startSpan(name, {
|
|
640
|
+
const span = getTracer().startSpan(name, {
|
|
641
|
+
kind: options.otelKind ?? otelSpanKind(options.kind ?? "CHAIN" /* CHAIN */),
|
|
642
|
+
attributes: creationAttributes(name, options)
|
|
643
|
+
});
|
|
607
644
|
return configure(new Observation(span), options);
|
|
608
645
|
}
|
|
609
646
|
function startAsCurrentSpan(name, optionsOrFn, maybeFn) {
|
|
610
647
|
const [options, fn] = typeof optionsOrFn === "function" ? [{}, optionsOrFn] : [optionsOrFn, maybeFn];
|
|
611
648
|
return runActive(
|
|
612
649
|
name,
|
|
613
|
-
creationAttributes(options),
|
|
650
|
+
creationAttributes(name, options),
|
|
614
651
|
options.userId,
|
|
615
652
|
(span) => configure(new Observation(span), options),
|
|
616
|
-
fn
|
|
653
|
+
fn,
|
|
654
|
+
options.otelKind ?? otelSpanKind(options.kind ?? "CHAIN" /* CHAIN */)
|
|
617
655
|
);
|
|
618
656
|
}
|
|
619
|
-
function runActive(name, attributes, userId, makeHandle, fn) {
|
|
620
|
-
const run = () => getTracer().startActiveSpan(name, { attributes }, async (span) => {
|
|
657
|
+
function runActive(name, attributes, userId, makeHandle, fn, otelKind) {
|
|
658
|
+
const run = () => getTracer().startActiveSpan(name, { kind: otelKind, attributes }, async (span) => {
|
|
621
659
|
const handle = makeHandle(span);
|
|
622
660
|
try {
|
|
623
661
|
return await fn(handle);
|
|
@@ -630,12 +668,12 @@ function runActive(name, attributes, userId, makeHandle, fn) {
|
|
|
630
668
|
});
|
|
631
669
|
return userId !== void 0 ? withUser(userId, run) : run();
|
|
632
670
|
}
|
|
633
|
-
var
|
|
671
|
+
var import_api3, Observation;
|
|
634
672
|
var init_spans = __esm({
|
|
635
673
|
"src/spans.ts"() {
|
|
636
674
|
"use strict";
|
|
637
675
|
init_cjs_shims();
|
|
638
|
-
|
|
676
|
+
import_api3 = require("@opentelemetry/api");
|
|
639
677
|
init_client();
|
|
640
678
|
init_semconv();
|
|
641
679
|
init_serde();
|
|
@@ -679,9 +717,15 @@ var init_spans = __esm({
|
|
|
679
717
|
return this;
|
|
680
718
|
}
|
|
681
719
|
/**
|
|
682
|
-
* Record an error on the span
|
|
683
|
-
* `startAsCurrent*` helpers do on a thrown error, exposed
|
|
684
|
-
* `start*` path does not have to reach through `.span` to
|
|
720
|
+
* Record an error on the span, set ERROR status and `error.type`. This is
|
|
721
|
+
* exactly what the `startAsCurrent*` helpers do on a thrown error, exposed
|
|
722
|
+
* so the manual `start*` path does not have to reach through `.span` to
|
|
723
|
+
* match it.
|
|
724
|
+
*
|
|
725
|
+
* `error.type` is Conditionally Required by the GenAI conventions on every
|
|
726
|
+
* span that ends in an error, and every helper's throw path funnels through
|
|
727
|
+
* here, so this is the one place that sets it. The error's name only, never
|
|
728
|
+
* the message: it must stay low-cardinality and free of echoed content.
|
|
685
729
|
*
|
|
686
730
|
* Accepts `unknown` because that is what a `catch` binding is; a non-Error
|
|
687
731
|
* throwable is wrapped so `recordException` still gets a real Error.
|
|
@@ -689,7 +733,8 @@ var init_spans = __esm({
|
|
|
689
733
|
recordException(error) {
|
|
690
734
|
const wrapped = error instanceof Error ? error : new Error(String(error));
|
|
691
735
|
this.span.recordException(wrapped);
|
|
692
|
-
this.span.setStatus({ code:
|
|
736
|
+
this.span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message: wrapped.message });
|
|
737
|
+
this.span.setAttribute(ERROR_TYPE, errorType(error));
|
|
693
738
|
return this;
|
|
694
739
|
}
|
|
695
740
|
end() {
|
|
@@ -711,27 +756,25 @@ __export(instrumentationMcp_exports, {
|
|
|
711
756
|
instrumentMcpClient: () => instrumentMcpClient
|
|
712
757
|
});
|
|
713
758
|
function serializeResult(result) {
|
|
714
|
-
|
|
715
|
-
|
|
759
|
+
const record = asRecord(result);
|
|
760
|
+
if (record === void 0) return toAttributeValue(result);
|
|
716
761
|
const structured = record.structured_content ?? record.structuredContent;
|
|
717
762
|
if (structured !== void 0 && structured !== null) return toAttributeValue(structured);
|
|
718
763
|
const content = record.content;
|
|
719
764
|
if (Array.isArray(content)) {
|
|
720
|
-
const texts = content.map(
|
|
721
|
-
(block) => typeof block === "object" && block !== null ? block.text : void 0
|
|
722
|
-
).filter((text) => typeof text === "string");
|
|
765
|
+
const texts = content.map((block) => asRecord(block)?.text).filter((text) => typeof text === "string");
|
|
723
766
|
if (texts.length === 1) return truncate(texts[0]);
|
|
724
767
|
if (texts.length > 1) return toAttributeValue(texts);
|
|
725
768
|
}
|
|
726
769
|
return toAttributeValue(result);
|
|
727
770
|
}
|
|
728
771
|
function resultIsError(result) {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
return Boolean(record.is_error ?? record.isError);
|
|
772
|
+
const record = asRecord(result);
|
|
773
|
+
return Boolean(record?.is_error ?? record?.isError);
|
|
732
774
|
}
|
|
733
775
|
function recordResult(observation, result) {
|
|
734
|
-
const
|
|
776
|
+
const record = asRecord(result);
|
|
777
|
+
const resultType = record?.result_type ?? record?.resultType;
|
|
735
778
|
if (resultType === INPUT_REQUIRED) {
|
|
736
779
|
observation.setAttribute(MCP_RESULT_TYPE, INPUT_REQUIRED);
|
|
737
780
|
return;
|
|
@@ -739,11 +782,24 @@ function recordResult(observation, result) {
|
|
|
739
782
|
observation.setAttribute(OUTPUT_VALUE, serializeResult(result));
|
|
740
783
|
if (resultIsError(result)) {
|
|
741
784
|
observation.span.setStatus({
|
|
742
|
-
code:
|
|
785
|
+
code: import_api4.SpanStatusCode.ERROR,
|
|
743
786
|
message: "tool returned an error result"
|
|
744
787
|
});
|
|
788
|
+
observation.setAttribute(ERROR_TYPE, ERROR_TYPE_TOOL_ERROR);
|
|
745
789
|
}
|
|
746
790
|
}
|
|
791
|
+
function negotiatedProtocolVersion(client) {
|
|
792
|
+
const version = asRecord(asRecord(client)?.transport)?.protocolVersion;
|
|
793
|
+
return typeof version === "string" ? version : void 0;
|
|
794
|
+
}
|
|
795
|
+
function callAttributes(toolName, protocolVersion) {
|
|
796
|
+
const attributes = {
|
|
797
|
+
[GEN_AI_TOOL_NAME]: toolName,
|
|
798
|
+
[MCP_METHOD_NAME]: MCP_METHOD_TOOLS_CALL
|
|
799
|
+
};
|
|
800
|
+
if (protocolVersion !== void 0) attributes[MCP_PROTOCOL_VERSION] = protocolVersion;
|
|
801
|
+
return attributes;
|
|
802
|
+
}
|
|
747
803
|
function instrumentMcpClient(ClientClass) {
|
|
748
804
|
const current = ClientClass.prototype.callTool;
|
|
749
805
|
const alreadyWrapped = current.riusOriginal;
|
|
@@ -756,12 +812,14 @@ function instrumentMcpClient(ClientClass) {
|
|
|
756
812
|
const instrumented = function instrumentedCallTool(params, ...rest) {
|
|
757
813
|
return startAsCurrentSpan(
|
|
758
814
|
`execute_tool ${params.name}`,
|
|
759
|
-
// Tool name at CREATION: pending snapshots are built at start, so an
|
|
760
|
-
// attribute set inside the callback never reaches them.
|
|
761
815
|
{
|
|
762
816
|
kind: "TOOL" /* TOOL */,
|
|
817
|
+
// The OTel SpanKind FIELD (orthogonal to our taxonomy attribute above):
|
|
818
|
+
// a remote tool call is CLIENT under both the MCP and the GenAI
|
|
819
|
+
// execute-tool conventions.
|
|
820
|
+
otelKind: import_api4.SpanKind.CLIENT,
|
|
763
821
|
input: params.arguments,
|
|
764
|
-
attributes:
|
|
822
|
+
attributes: callAttributes(params.name, negotiatedProtocolVersion(this))
|
|
765
823
|
},
|
|
766
824
|
async (observation) => {
|
|
767
825
|
const result = await original.call(this, params, ...rest);
|
|
@@ -776,12 +834,12 @@ function instrumentMcpClient(ClientClass) {
|
|
|
776
834
|
ClientClass.prototype.callTool = original;
|
|
777
835
|
};
|
|
778
836
|
}
|
|
779
|
-
var
|
|
837
|
+
var import_api4, INPUT_REQUIRED;
|
|
780
838
|
var init_instrumentationMcp = __esm({
|
|
781
839
|
"src/instrumentationMcp.ts"() {
|
|
782
840
|
"use strict";
|
|
783
841
|
init_cjs_shims();
|
|
784
|
-
|
|
842
|
+
import_api4 = require("@opentelemetry/api");
|
|
785
843
|
init_semconv();
|
|
786
844
|
init_serde();
|
|
787
845
|
init_spans();
|
|
@@ -1055,12 +1113,12 @@ function redactInvocationParameters(value) {
|
|
|
1055
1113
|
for (const member of INVOCATION_PARAMETERS_CONTENT_MEMBERS) delete bag[member];
|
|
1056
1114
|
return JSON.stringify(bag);
|
|
1057
1115
|
}
|
|
1058
|
-
var
|
|
1116
|
+
var import_api5, METADATA_PREFIX, EXCEPTION_EVENT_NAME, EXCEPTION_CONTENT_KEYS, STATUS_DESCRIPTION_KEY, MaskingSpanExporter;
|
|
1059
1117
|
var init_masking = __esm({
|
|
1060
1118
|
"src/masking.ts"() {
|
|
1061
1119
|
"use strict";
|
|
1062
1120
|
init_cjs_shims();
|
|
1063
|
-
|
|
1121
|
+
import_api5 = require("@opentelemetry/api");
|
|
1064
1122
|
init_semconv();
|
|
1065
1123
|
init_serde();
|
|
1066
1124
|
METADATA_PREFIX = "metadata.";
|
|
@@ -1104,7 +1162,7 @@ var init_masking = __esm({
|
|
|
1104
1162
|
*/
|
|
1105
1163
|
sanitizeStatus(span) {
|
|
1106
1164
|
const status = span.status;
|
|
1107
|
-
if (status?.code !==
|
|
1165
|
+
if (status?.code !== import_api5.SpanStatusCode.ERROR || !status.message) return;
|
|
1108
1166
|
if (!this.opts.captureContent) {
|
|
1109
1167
|
status.message = void 0;
|
|
1110
1168
|
return;
|
|
@@ -1174,7 +1232,7 @@ function buildSnapshot(span) {
|
|
|
1174
1232
|
startTime: span.startTime,
|
|
1175
1233
|
endTime: span.startTime,
|
|
1176
1234
|
duration: [0, 0],
|
|
1177
|
-
status: { code:
|
|
1235
|
+
status: { code: import_api6.SpanStatusCode.UNSET },
|
|
1178
1236
|
attributes,
|
|
1179
1237
|
links: [],
|
|
1180
1238
|
events: [],
|
|
@@ -1189,12 +1247,12 @@ function buildSnapshot(span) {
|
|
|
1189
1247
|
function spanKey(context2) {
|
|
1190
1248
|
return `${context2.traceId}:${context2.spanId}`;
|
|
1191
1249
|
}
|
|
1192
|
-
var
|
|
1250
|
+
var import_api6, PendingSpanProcessor;
|
|
1193
1251
|
var init_pending = __esm({
|
|
1194
1252
|
"src/pending.ts"() {
|
|
1195
1253
|
"use strict";
|
|
1196
1254
|
init_cjs_shims();
|
|
1197
|
-
|
|
1255
|
+
import_api6 = require("@opentelemetry/api");
|
|
1198
1256
|
init_semconv();
|
|
1199
1257
|
PendingSpanProcessor = class {
|
|
1200
1258
|
delegate;
|
|
@@ -1242,17 +1300,17 @@ var init_pending = __esm({
|
|
|
1242
1300
|
// src/session.ts
|
|
1243
1301
|
function withSession(sessionIdOrFn, maybeFn) {
|
|
1244
1302
|
const [sessionId, fn] = typeof sessionIdOrFn === "function" ? [(0, import_node_crypto.randomUUID)(), sessionIdOrFn] : [sessionIdOrFn, maybeFn];
|
|
1245
|
-
return
|
|
1303
|
+
return import_api7.context.with(import_api7.context.active().setValue(SESSION_KEY, sessionId), () => fn(sessionId));
|
|
1246
1304
|
}
|
|
1247
|
-
var import_node_crypto,
|
|
1305
|
+
var import_node_crypto, import_api7, SESSION_KEY, SessionSpanProcessor;
|
|
1248
1306
|
var init_session = __esm({
|
|
1249
1307
|
"src/session.ts"() {
|
|
1250
1308
|
"use strict";
|
|
1251
1309
|
init_cjs_shims();
|
|
1252
1310
|
import_node_crypto = require("crypto");
|
|
1253
|
-
|
|
1311
|
+
import_api7 = require("@opentelemetry/api");
|
|
1254
1312
|
init_semconv();
|
|
1255
|
-
SESSION_KEY = (0,
|
|
1313
|
+
SESSION_KEY = (0, import_api7.createContextKey)("rius-session-id");
|
|
1256
1314
|
SessionSpanProcessor = class {
|
|
1257
1315
|
constructor(defaultSessionId) {
|
|
1258
1316
|
this.defaultSessionId = defaultSessionId;
|
|
@@ -1260,7 +1318,7 @@ var init_session = __esm({
|
|
|
1260
1318
|
defaultSessionId;
|
|
1261
1319
|
onStart(span, parentContext) {
|
|
1262
1320
|
const value = parentContext.getValue(SESSION_KEY);
|
|
1263
|
-
const parent =
|
|
1321
|
+
const parent = import_api7.trace.getSpan(parentContext);
|
|
1264
1322
|
const inherited = parent?.attributes?.[SESSION_ID];
|
|
1265
1323
|
const sessionId = typeof value === "string" ? value : typeof inherited === "string" ? inherited : this.defaultSessionId;
|
|
1266
1324
|
if (sessionId !== void 0) span.setAttribute(SESSION_ID, sessionId);
|
|
@@ -1278,7 +1336,7 @@ var init_session = __esm({
|
|
|
1278
1336
|
// src/workspace.ts
|
|
1279
1337
|
function withWorkspace(alias, fn) {
|
|
1280
1338
|
if (!alias) throw new Error("workspace alias must be a non-empty string");
|
|
1281
|
-
return
|
|
1339
|
+
return import_api8.context.with(import_api8.context.active().setValue(WORKSPACE_KEY, alias), () => fn(alias));
|
|
1282
1340
|
}
|
|
1283
1341
|
function setGlobalRouting(routing) {
|
|
1284
1342
|
globalRouting = routing;
|
|
@@ -1291,20 +1349,20 @@ function registerWorkspace(alias, apiKey) {
|
|
|
1291
1349
|
}
|
|
1292
1350
|
globalRouting.register(alias, apiKey);
|
|
1293
1351
|
}
|
|
1294
|
-
var
|
|
1352
|
+
var import_api8, WORKSPACE_KEY, WorkspaceSpanProcessor, RoutingSpanExporter, globalRouting;
|
|
1295
1353
|
var init_workspace = __esm({
|
|
1296
1354
|
"src/workspace.ts"() {
|
|
1297
1355
|
"use strict";
|
|
1298
1356
|
init_cjs_shims();
|
|
1299
|
-
|
|
1357
|
+
import_api8 = require("@opentelemetry/api");
|
|
1300
1358
|
init_esm();
|
|
1301
1359
|
init_semconv();
|
|
1302
|
-
WORKSPACE_KEY = (0,
|
|
1360
|
+
WORKSPACE_KEY = (0, import_api8.createContextKey)("rius-workspace-alias");
|
|
1303
1361
|
WorkspaceSpanProcessor = class {
|
|
1304
1362
|
/** Straddles already warned about, as "parentAlias->alias"; one warning each, not one per span. */
|
|
1305
1363
|
warnedStraddles = /* @__PURE__ */ new Set();
|
|
1306
1364
|
onStart(span, parentContext) {
|
|
1307
|
-
const parent =
|
|
1365
|
+
const parent = import_api8.trace.getSpan(parentContext);
|
|
1308
1366
|
const parentAlias = parent?.attributes?.[WORKSPACE_ROUTE];
|
|
1309
1367
|
const scoped = parentContext.getValue(WORKSPACE_KEY);
|
|
1310
1368
|
const alias = typeof scoped === "string" ? scoped : parentAlias;
|
|
@@ -1493,15 +1551,15 @@ function init(options = {}) {
|
|
|
1493
1551
|
return globalClient;
|
|
1494
1552
|
}
|
|
1495
1553
|
function getTracer() {
|
|
1496
|
-
return
|
|
1554
|
+
return import_api9.trace.getTracer(TRACER_NAME, SDK_VERSION);
|
|
1497
1555
|
}
|
|
1498
|
-
var import_node_crypto2,
|
|
1556
|
+
var import_node_crypto2, import_api9, import_exporter_trace_otlp_proto, import_resources, import_sdk_trace_base, import_sdk_trace_node, import_semantic_conventions, sinks, heartbeats, createClient, RiusClient, globalClient;
|
|
1499
1557
|
var init_client = __esm({
|
|
1500
1558
|
"src/client.ts"() {
|
|
1501
1559
|
"use strict";
|
|
1502
1560
|
init_cjs_shims();
|
|
1503
1561
|
import_node_crypto2 = require("crypto");
|
|
1504
|
-
|
|
1562
|
+
import_api9 = require("@opentelemetry/api");
|
|
1505
1563
|
import_exporter_trace_otlp_proto = require("@opentelemetry/exporter-trace-otlp-proto");
|
|
1506
1564
|
import_resources = require("@opentelemetry/resources");
|
|
1507
1565
|
import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
|
|
@@ -1572,9 +1630,9 @@ var init_client = __esm({
|
|
|
1572
1630
|
if (globalClient === this) {
|
|
1573
1631
|
globalClient = void 0;
|
|
1574
1632
|
setGlobalRouting(void 0);
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1633
|
+
import_api9.trace.disable();
|
|
1634
|
+
import_api9.context.disable();
|
|
1635
|
+
import_api9.propagation.disable();
|
|
1578
1636
|
}
|
|
1579
1637
|
}
|
|
1580
1638
|
}
|
|
@@ -1684,6 +1742,12 @@ var Generation = class extends Observation {
|
|
|
1684
1742
|
}
|
|
1685
1743
|
provider;
|
|
1686
1744
|
firstTokenRecorded = false;
|
|
1745
|
+
/**
|
|
1746
|
+
* Monotonic clock at construction, which is span creation for both
|
|
1747
|
+
* `startGeneration` and the scoped form. The API `Span` exposes no start
|
|
1748
|
+
* time, so this is what `recordFirstToken` measures the first chunk against.
|
|
1749
|
+
*/
|
|
1750
|
+
startedAt = performance.now();
|
|
1687
1751
|
/**
|
|
1688
1752
|
* Record the request messages (`gen_ai.input.messages`), normalised to the
|
|
1689
1753
|
* GenAI `{role, parts}` shape like the Python SDK does: bare strings, OpenAI
|
|
@@ -1758,17 +1822,24 @@ var Generation = class extends Observation {
|
|
|
1758
1822
|
return this;
|
|
1759
1823
|
}
|
|
1760
1824
|
/**
|
|
1761
|
-
*
|
|
1762
|
-
*
|
|
1763
|
-
*
|
|
1764
|
-
*
|
|
1825
|
+
* Mark the arrival of the first streamed token. Records the
|
|
1826
|
+
* `gen_ai.first_token` event (the timestamp the backend derives TTFT from)
|
|
1827
|
+
* and, per the GenAI conventions, the derived
|
|
1828
|
+
* `gen_ai.response.time_to_first_chunk` (seconds since span creation) plus
|
|
1829
|
+
* `gen_ai.request.stream = true`: a first chunk arriving is what tells the
|
|
1830
|
+
* SDK the request streamed. Idempotent: only the first call records, so a
|
|
1831
|
+
* streaming loop can call this unconditionally on every chunk without
|
|
1832
|
+
* inflating the span. A no-op after the span has ended.
|
|
1765
1833
|
*/
|
|
1766
1834
|
recordFirstToken() {
|
|
1767
1835
|
if (this.firstTokenRecorded || !this.span.isRecording()) {
|
|
1768
1836
|
return this;
|
|
1769
1837
|
}
|
|
1838
|
+
const elapsedSeconds = Math.max(performance.now() - this.startedAt, 0) / 1e3;
|
|
1770
1839
|
this.span.addEvent(GEN_AI_FIRST_TOKEN_EVENT);
|
|
1771
1840
|
this.firstTokenRecorded = true;
|
|
1841
|
+
this.span.setAttribute(GEN_AI_REQUEST_STREAM, true);
|
|
1842
|
+
this.span.setAttribute(GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK, elapsedSeconds);
|
|
1772
1843
|
return this;
|
|
1773
1844
|
}
|
|
1774
1845
|
};
|
|
@@ -1792,7 +1863,10 @@ function configure2(generation, options) {
|
|
|
1792
1863
|
return generation;
|
|
1793
1864
|
}
|
|
1794
1865
|
function startGeneration(name, options = {}) {
|
|
1795
|
-
const span = getTracer().startSpan(name, {
|
|
1866
|
+
const span = getTracer().startSpan(name, {
|
|
1867
|
+
kind: otelSpanKind("LLM" /* LLM */),
|
|
1868
|
+
attributes: attributesFor(options)
|
|
1869
|
+
});
|
|
1796
1870
|
return configure2(new Generation(span, options.provider), options);
|
|
1797
1871
|
}
|
|
1798
1872
|
function startAsCurrentGeneration(name, optionsOrFn, maybeFn) {
|
|
@@ -1802,7 +1876,8 @@ function startAsCurrentGeneration(name, optionsOrFn, maybeFn) {
|
|
|
1802
1876
|
attributesFor(options),
|
|
1803
1877
|
options.userId,
|
|
1804
1878
|
(span) => configure2(new Generation(span, options.provider), options),
|
|
1805
|
-
fn
|
|
1879
|
+
fn,
|
|
1880
|
+
otelSpanKind("LLM" /* LLM */)
|
|
1806
1881
|
);
|
|
1807
1882
|
}
|
|
1808
1883
|
|
|
@@ -1835,7 +1910,7 @@ init_session();
|
|
|
1835
1910
|
init_user();
|
|
1836
1911
|
init_spans();
|
|
1837
1912
|
init_workspace();
|
|
1838
|
-
var VERSION = "0.
|
|
1913
|
+
var VERSION = "0.8.0";
|
|
1839
1914
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1840
1915
|
0 && (module.exports = {
|
|
1841
1916
|
Generation,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Tracer, Span } from '@opentelemetry/api';
|
|
1
|
+
import { Tracer, Span, SpanKind as SpanKind$1 } from '@opentelemetry/api';
|
|
2
2
|
import { SpanExporter } from '@opentelemetry/sdk-trace-base';
|
|
3
3
|
|
|
4
4
|
/** Redacts content attribute values at export. Receives the key when it accepts one. */
|
|
@@ -171,6 +171,13 @@ declare enum SpanKind {
|
|
|
171
171
|
/** Options for {@link startSpan} and {@link startAsCurrentSpan}. */
|
|
172
172
|
interface SpanOptions {
|
|
173
173
|
kind?: SpanKind;
|
|
174
|
+
/**
|
|
175
|
+
* The OpenTelemetry `SpanKind` FIELD (INTERNAL, CLIENT, …), orthogonal to
|
|
176
|
+
* `kind` above, which is our taxonomy attribute. Conventions set it per
|
|
177
|
+
* operation, so by default it is derived from `kind` (see `otelSpanKind`);
|
|
178
|
+
* set this to override, as the MCP wrapper does for a remote tool call.
|
|
179
|
+
*/
|
|
180
|
+
otelKind?: SpanKind$1;
|
|
174
181
|
input?: unknown;
|
|
175
182
|
/**
|
|
176
183
|
* End-user identity (`user.id`). Sugar for `withUser`: set on this span at
|
|
@@ -203,9 +210,15 @@ declare class Observation {
|
|
|
203
210
|
*/
|
|
204
211
|
setAttribute(key: string, value: unknown): this;
|
|
205
212
|
/**
|
|
206
|
-
* Record an error on the span
|
|
207
|
-
* `startAsCurrent*` helpers do on a thrown error, exposed
|
|
208
|
-
* `start*` path does not have to reach through `.span` to
|
|
213
|
+
* Record an error on the span, set ERROR status and `error.type`. This is
|
|
214
|
+
* exactly what the `startAsCurrent*` helpers do on a thrown error, exposed
|
|
215
|
+
* so the manual `start*` path does not have to reach through `.span` to
|
|
216
|
+
* match it.
|
|
217
|
+
*
|
|
218
|
+
* `error.type` is Conditionally Required by the GenAI conventions on every
|
|
219
|
+
* span that ends in an error, and every helper's throw path funnels through
|
|
220
|
+
* here, so this is the one place that sets it. The error's name only, never
|
|
221
|
+
* the message: it must stay low-cardinality and free of echoed content.
|
|
209
222
|
*
|
|
210
223
|
* Accepts `unknown` because that is what a `catch` binding is; a non-Error
|
|
211
224
|
* throwable is wrapped so `recordException` still gets a real Error.
|
|
@@ -275,6 +288,12 @@ interface GenerationOptions {
|
|
|
275
288
|
declare class Generation extends Observation {
|
|
276
289
|
private readonly provider?;
|
|
277
290
|
private firstTokenRecorded;
|
|
291
|
+
/**
|
|
292
|
+
* Monotonic clock at construction, which is span creation for both
|
|
293
|
+
* `startGeneration` and the scoped form. The API `Span` exposes no start
|
|
294
|
+
* time, so this is what `recordFirstToken` measures the first chunk against.
|
|
295
|
+
*/
|
|
296
|
+
private readonly startedAt;
|
|
278
297
|
/**
|
|
279
298
|
* The provider passed at creation; drives the Anthropic input-token summing
|
|
280
299
|
* in {@link setUsage}. A bare `new Generation(span)` has none and never sums.
|
|
@@ -334,10 +353,14 @@ declare class Generation extends Observation {
|
|
|
334
353
|
*/
|
|
335
354
|
setFinishReasons(reasons: string | string[]): this;
|
|
336
355
|
/**
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
356
|
+
* Mark the arrival of the first streamed token. Records the
|
|
357
|
+
* `gen_ai.first_token` event (the timestamp the backend derives TTFT from)
|
|
358
|
+
* and, per the GenAI conventions, the derived
|
|
359
|
+
* `gen_ai.response.time_to_first_chunk` (seconds since span creation) plus
|
|
360
|
+
* `gen_ai.request.stream = true`: a first chunk arriving is what tells the
|
|
361
|
+
* SDK the request streamed. Idempotent: only the first call records, so a
|
|
362
|
+
* streaming loop can call this unconditionally on every chunk without
|
|
363
|
+
* inflating the span. A no-op after the span has ended.
|
|
341
364
|
*/
|
|
342
365
|
recordFirstToken(): this;
|
|
343
366
|
}
|
|
@@ -441,6 +464,6 @@ declare function withSession<T>(sessionId: string, fn: (sessionId: string) => T)
|
|
|
441
464
|
*/
|
|
442
465
|
declare function withUser<T>(userId: string, fn: (userId: string) => T): T;
|
|
443
466
|
|
|
444
|
-
declare const VERSION = "0.
|
|
467
|
+
declare const VERSION = "0.8.0";
|
|
445
468
|
|
|
446
469
|
export { Generation, type GenerationBody, type GenerationOptions, type InitOptions, type Mask, Observation, type ObserveOptions, RiusClient, type RiusOptions, type SpanBody, SpanKind, type SpanOptions, VERSION, type WorkspaceExporterFactory, getTracer, init, observe, registerWorkspace, startAsCurrentGeneration, startAsCurrentSpan, startGeneration, startSpan, withSession, withUser, withWorkspace };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Tracer, Span } from '@opentelemetry/api';
|
|
1
|
+
import { Tracer, Span, SpanKind as SpanKind$1 } from '@opentelemetry/api';
|
|
2
2
|
import { SpanExporter } from '@opentelemetry/sdk-trace-base';
|
|
3
3
|
|
|
4
4
|
/** Redacts content attribute values at export. Receives the key when it accepts one. */
|
|
@@ -171,6 +171,13 @@ declare enum SpanKind {
|
|
|
171
171
|
/** Options for {@link startSpan} and {@link startAsCurrentSpan}. */
|
|
172
172
|
interface SpanOptions {
|
|
173
173
|
kind?: SpanKind;
|
|
174
|
+
/**
|
|
175
|
+
* The OpenTelemetry `SpanKind` FIELD (INTERNAL, CLIENT, …), orthogonal to
|
|
176
|
+
* `kind` above, which is our taxonomy attribute. Conventions set it per
|
|
177
|
+
* operation, so by default it is derived from `kind` (see `otelSpanKind`);
|
|
178
|
+
* set this to override, as the MCP wrapper does for a remote tool call.
|
|
179
|
+
*/
|
|
180
|
+
otelKind?: SpanKind$1;
|
|
174
181
|
input?: unknown;
|
|
175
182
|
/**
|
|
176
183
|
* End-user identity (`user.id`). Sugar for `withUser`: set on this span at
|
|
@@ -203,9 +210,15 @@ declare class Observation {
|
|
|
203
210
|
*/
|
|
204
211
|
setAttribute(key: string, value: unknown): this;
|
|
205
212
|
/**
|
|
206
|
-
* Record an error on the span
|
|
207
|
-
* `startAsCurrent*` helpers do on a thrown error, exposed
|
|
208
|
-
* `start*` path does not have to reach through `.span` to
|
|
213
|
+
* Record an error on the span, set ERROR status and `error.type`. This is
|
|
214
|
+
* exactly what the `startAsCurrent*` helpers do on a thrown error, exposed
|
|
215
|
+
* so the manual `start*` path does not have to reach through `.span` to
|
|
216
|
+
* match it.
|
|
217
|
+
*
|
|
218
|
+
* `error.type` is Conditionally Required by the GenAI conventions on every
|
|
219
|
+
* span that ends in an error, and every helper's throw path funnels through
|
|
220
|
+
* here, so this is the one place that sets it. The error's name only, never
|
|
221
|
+
* the message: it must stay low-cardinality and free of echoed content.
|
|
209
222
|
*
|
|
210
223
|
* Accepts `unknown` because that is what a `catch` binding is; a non-Error
|
|
211
224
|
* throwable is wrapped so `recordException` still gets a real Error.
|
|
@@ -275,6 +288,12 @@ interface GenerationOptions {
|
|
|
275
288
|
declare class Generation extends Observation {
|
|
276
289
|
private readonly provider?;
|
|
277
290
|
private firstTokenRecorded;
|
|
291
|
+
/**
|
|
292
|
+
* Monotonic clock at construction, which is span creation for both
|
|
293
|
+
* `startGeneration` and the scoped form. The API `Span` exposes no start
|
|
294
|
+
* time, so this is what `recordFirstToken` measures the first chunk against.
|
|
295
|
+
*/
|
|
296
|
+
private readonly startedAt;
|
|
278
297
|
/**
|
|
279
298
|
* The provider passed at creation; drives the Anthropic input-token summing
|
|
280
299
|
* in {@link setUsage}. A bare `new Generation(span)` has none and never sums.
|
|
@@ -334,10 +353,14 @@ declare class Generation extends Observation {
|
|
|
334
353
|
*/
|
|
335
354
|
setFinishReasons(reasons: string | string[]): this;
|
|
336
355
|
/**
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
356
|
+
* Mark the arrival of the first streamed token. Records the
|
|
357
|
+
* `gen_ai.first_token` event (the timestamp the backend derives TTFT from)
|
|
358
|
+
* and, per the GenAI conventions, the derived
|
|
359
|
+
* `gen_ai.response.time_to_first_chunk` (seconds since span creation) plus
|
|
360
|
+
* `gen_ai.request.stream = true`: a first chunk arriving is what tells the
|
|
361
|
+
* SDK the request streamed. Idempotent: only the first call records, so a
|
|
362
|
+
* streaming loop can call this unconditionally on every chunk without
|
|
363
|
+
* inflating the span. A no-op after the span has ended.
|
|
341
364
|
*/
|
|
342
365
|
recordFirstToken(): this;
|
|
343
366
|
}
|
|
@@ -441,6 +464,6 @@ declare function withSession<T>(sessionId: string, fn: (sessionId: string) => T)
|
|
|
441
464
|
*/
|
|
442
465
|
declare function withUser<T>(userId: string, fn: (userId: string) => T): T;
|
|
443
466
|
|
|
444
|
-
declare const VERSION = "0.
|
|
467
|
+
declare const VERSION = "0.8.0";
|
|
445
468
|
|
|
446
469
|
export { Generation, type GenerationBody, type GenerationOptions, type InitOptions, type Mask, Observation, type ObserveOptions, RiusClient, type RiusOptions, type SpanBody, SpanKind, type SpanOptions, VERSION, type WorkspaceExporterFactory, getTracer, init, observe, registerWorkspace, startAsCurrentGeneration, startAsCurrentSpan, startGeneration, startSpan, withSession, withUser, withWorkspace };
|
package/dist/index.js
CHANGED
|
@@ -354,13 +354,18 @@ var init_heartbeat = __esm({
|
|
|
354
354
|
});
|
|
355
355
|
|
|
356
356
|
// src/semconv.ts
|
|
357
|
-
|
|
357
|
+
import { SpanKind as OtelSpanKind } from "@opentelemetry/api";
|
|
358
|
+
function otelSpanKind(kind) {
|
|
359
|
+
return OTEL_KIND_BY_KIND[kind];
|
|
360
|
+
}
|
|
361
|
+
function kindAttributes(kind, name) {
|
|
358
362
|
const attributes = { [OPENINFERENCE_SPAN_KIND]: kind };
|
|
359
363
|
const operation = OPERATION_BY_KIND[kind];
|
|
360
364
|
if (operation !== void 0) attributes[GEN_AI_OPERATION_NAME] = operation;
|
|
365
|
+
if (kind === "TOOL" /* TOOL */ && name !== void 0) attributes[GEN_AI_TOOL_NAME] = name;
|
|
361
366
|
return attributes;
|
|
362
367
|
}
|
|
363
|
-
var TRACER_NAME, SERVICE_INSTANCE_ID, OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, SESSION_ID, USER_ID, WORKSPACE_ROUTE, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_REQUEST_REASONING_LEVEL, GEN_AI_RESPONSE_MODEL, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS, GEN_AI_USAGE_REASONING_OUTPUT_TOKENS, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_TOOL_NAME, GEN_AI_TOOL_DEFINITIONS, GEN_AI_REQUEST_PREFIX, MCP_RESULT_TYPE, GEN_AI_FIRST_TOKEN_EVENT, SpanKind, OPERATION_BY_KIND, CONTENT_ATTRIBUTES, LLM_INVOCATION_PARAMETERS, INVOCATION_PARAMETERS_CONTENT_MEMBERS, CONTENT_ATTRIBUTE_PREFIXES, CONTENT_ATTRIBUTE_SUFFIXES, GLASSFLOW_SPAN_PENDING, PENDING_IDENTITY_ATTRIBUTES, PENDING_IDENTITY_PREFIXES;
|
|
368
|
+
var TRACER_NAME, SERVICE_INSTANCE_ID, OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, SESSION_ID, USER_ID, WORKSPACE_ROUTE, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_REQUEST_REASONING_LEVEL, GEN_AI_RESPONSE_MODEL, GEN_AI_REQUEST_STREAM, GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS, GEN_AI_USAGE_REASONING_OUTPUT_TOKENS, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_TOOL_NAME, GEN_AI_TOOL_DEFINITIONS, GEN_AI_REQUEST_PREFIX, MCP_METHOD_NAME, MCP_METHOD_TOOLS_CALL, MCP_PROTOCOL_VERSION, ERROR_TYPE, ERROR_TYPE_TOOL_ERROR, MCP_RESULT_TYPE, GEN_AI_FIRST_TOKEN_EVENT, SpanKind, OPERATION_BY_KIND, OTEL_KIND_BY_KIND, CONTENT_ATTRIBUTES, LLM_INVOCATION_PARAMETERS, INVOCATION_PARAMETERS_CONTENT_MEMBERS, CONTENT_ATTRIBUTE_PREFIXES, CONTENT_ATTRIBUTE_SUFFIXES, GLASSFLOW_SPAN_PENDING, PENDING_IDENTITY_ATTRIBUTES, PENDING_IDENTITY_PREFIXES;
|
|
364
369
|
var init_semconv = __esm({
|
|
365
370
|
"src/semconv.ts"() {
|
|
366
371
|
"use strict";
|
|
@@ -378,6 +383,8 @@ var init_semconv = __esm({
|
|
|
378
383
|
GEN_AI_REQUEST_MODEL = "gen_ai.request.model";
|
|
379
384
|
GEN_AI_REQUEST_REASONING_LEVEL = "gen_ai.request.reasoning.level";
|
|
380
385
|
GEN_AI_RESPONSE_MODEL = "gen_ai.response.model";
|
|
386
|
+
GEN_AI_REQUEST_STREAM = "gen_ai.request.stream";
|
|
387
|
+
GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK = "gen_ai.response.time_to_first_chunk";
|
|
381
388
|
GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens";
|
|
382
389
|
GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens";
|
|
383
390
|
GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens";
|
|
@@ -389,6 +396,11 @@ var init_semconv = __esm({
|
|
|
389
396
|
GEN_AI_TOOL_NAME = "gen_ai.tool.name";
|
|
390
397
|
GEN_AI_TOOL_DEFINITIONS = "gen_ai.tool.definitions";
|
|
391
398
|
GEN_AI_REQUEST_PREFIX = "gen_ai.request.";
|
|
399
|
+
MCP_METHOD_NAME = "mcp.method.name";
|
|
400
|
+
MCP_METHOD_TOOLS_CALL = "tools/call";
|
|
401
|
+
MCP_PROTOCOL_VERSION = "mcp.protocol.version";
|
|
402
|
+
ERROR_TYPE = "error.type";
|
|
403
|
+
ERROR_TYPE_TOOL_ERROR = "tool_error";
|
|
392
404
|
MCP_RESULT_TYPE = "mcp.result_type";
|
|
393
405
|
GEN_AI_FIRST_TOKEN_EVENT = "gen_ai.first_token";
|
|
394
406
|
SpanKind = /* @__PURE__ */ ((SpanKind2) => {
|
|
@@ -406,6 +418,14 @@ var init_semconv = __esm({
|
|
|
406
418
|
["EMBEDDING" /* EMBEDDING */]: "embeddings",
|
|
407
419
|
["AGENT" /* AGENT */]: "invoke_agent"
|
|
408
420
|
};
|
|
421
|
+
OTEL_KIND_BY_KIND = {
|
|
422
|
+
["LLM" /* LLM */]: OtelSpanKind.CLIENT,
|
|
423
|
+
["EMBEDDING" /* EMBEDDING */]: OtelSpanKind.CLIENT,
|
|
424
|
+
["RETRIEVER" /* RETRIEVER */]: OtelSpanKind.CLIENT,
|
|
425
|
+
["TOOL" /* TOOL */]: OtelSpanKind.INTERNAL,
|
|
426
|
+
["AGENT" /* AGENT */]: OtelSpanKind.INTERNAL,
|
|
427
|
+
["CHAIN" /* CHAIN */]: OtelSpanKind.INTERNAL
|
|
428
|
+
};
|
|
409
429
|
CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
410
430
|
INPUT_VALUE,
|
|
411
431
|
OUTPUT_VALUE,
|
|
@@ -490,6 +510,11 @@ var init_semconv = __esm({
|
|
|
490
510
|
GEN_AI_OPERATION_NAME,
|
|
491
511
|
GEN_AI_PROVIDER_NAME,
|
|
492
512
|
GEN_AI_TOOL_NAME,
|
|
513
|
+
// Protocol identity, not content: a still-running MCP call must be
|
|
514
|
+
// distinguishable from a local tool in the live view — the one place
|
|
515
|
+
// setting the marker at creation pays off.
|
|
516
|
+
MCP_METHOD_NAME,
|
|
517
|
+
MCP_PROTOCOL_VERSION,
|
|
493
518
|
// Identity, not content: a pending span must be groupable into its
|
|
494
519
|
// session while still running, that is the live view's whole point.
|
|
495
520
|
SESSION_ID,
|
|
@@ -518,6 +543,9 @@ function renderable(raw, value) {
|
|
|
518
543
|
if (raw instanceof ArrayBuffer) return `<ArrayBuffer ${raw.byteLength} bytes>`;
|
|
519
544
|
return value;
|
|
520
545
|
}
|
|
546
|
+
function asRecord(value) {
|
|
547
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
548
|
+
}
|
|
521
549
|
function toAttributeValue(value) {
|
|
522
550
|
if (typeof value === "string") return truncate(value);
|
|
523
551
|
if (typeof value === "number" || typeof value === "boolean") return value;
|
|
@@ -542,6 +570,9 @@ function toAttributeValue(value) {
|
|
|
542
570
|
return "[unserializable]";
|
|
543
571
|
}
|
|
544
572
|
}
|
|
573
|
+
function errorType(error) {
|
|
574
|
+
return error instanceof Error ? error.name : typeof error;
|
|
575
|
+
}
|
|
545
576
|
var MAX_ATTR_CHARS, TRUNCATION_MARKER;
|
|
546
577
|
var init_serde = __esm({
|
|
547
578
|
"src/serde.ts"() {
|
|
@@ -585,27 +616,34 @@ function configure(observation, options) {
|
|
|
585
616
|
if (options.input !== void 0) observation.setInput(options.input);
|
|
586
617
|
return observation;
|
|
587
618
|
}
|
|
588
|
-
function creationAttributes(options) {
|
|
589
|
-
const attributes = {
|
|
619
|
+
function creationAttributes(name, options) {
|
|
620
|
+
const attributes = {
|
|
621
|
+
...kindAttributes(options.kind ?? "CHAIN" /* CHAIN */, name),
|
|
622
|
+
...options.attributes
|
|
623
|
+
};
|
|
590
624
|
if (options.userId !== void 0) attributes[USER_ID] = options.userId;
|
|
591
625
|
return attributes;
|
|
592
626
|
}
|
|
593
627
|
function startSpan(name, options = {}) {
|
|
594
|
-
const span = getTracer().startSpan(name, {
|
|
628
|
+
const span = getTracer().startSpan(name, {
|
|
629
|
+
kind: options.otelKind ?? otelSpanKind(options.kind ?? "CHAIN" /* CHAIN */),
|
|
630
|
+
attributes: creationAttributes(name, options)
|
|
631
|
+
});
|
|
595
632
|
return configure(new Observation(span), options);
|
|
596
633
|
}
|
|
597
634
|
function startAsCurrentSpan(name, optionsOrFn, maybeFn) {
|
|
598
635
|
const [options, fn] = typeof optionsOrFn === "function" ? [{}, optionsOrFn] : [optionsOrFn, maybeFn];
|
|
599
636
|
return runActive(
|
|
600
637
|
name,
|
|
601
|
-
creationAttributes(options),
|
|
638
|
+
creationAttributes(name, options),
|
|
602
639
|
options.userId,
|
|
603
640
|
(span) => configure(new Observation(span), options),
|
|
604
|
-
fn
|
|
641
|
+
fn,
|
|
642
|
+
options.otelKind ?? otelSpanKind(options.kind ?? "CHAIN" /* CHAIN */)
|
|
605
643
|
);
|
|
606
644
|
}
|
|
607
|
-
function runActive(name, attributes, userId, makeHandle, fn) {
|
|
608
|
-
const run = () => getTracer().startActiveSpan(name, { attributes }, async (span) => {
|
|
645
|
+
function runActive(name, attributes, userId, makeHandle, fn, otelKind) {
|
|
646
|
+
const run = () => getTracer().startActiveSpan(name, { kind: otelKind, attributes }, async (span) => {
|
|
609
647
|
const handle = makeHandle(span);
|
|
610
648
|
try {
|
|
611
649
|
return await fn(handle);
|
|
@@ -666,9 +704,15 @@ var init_spans = __esm({
|
|
|
666
704
|
return this;
|
|
667
705
|
}
|
|
668
706
|
/**
|
|
669
|
-
* Record an error on the span
|
|
670
|
-
* `startAsCurrent*` helpers do on a thrown error, exposed
|
|
671
|
-
* `start*` path does not have to reach through `.span` to
|
|
707
|
+
* Record an error on the span, set ERROR status and `error.type`. This is
|
|
708
|
+
* exactly what the `startAsCurrent*` helpers do on a thrown error, exposed
|
|
709
|
+
* so the manual `start*` path does not have to reach through `.span` to
|
|
710
|
+
* match it.
|
|
711
|
+
*
|
|
712
|
+
* `error.type` is Conditionally Required by the GenAI conventions on every
|
|
713
|
+
* span that ends in an error, and every helper's throw path funnels through
|
|
714
|
+
* here, so this is the one place that sets it. The error's name only, never
|
|
715
|
+
* the message: it must stay low-cardinality and free of echoed content.
|
|
672
716
|
*
|
|
673
717
|
* Accepts `unknown` because that is what a `catch` binding is; a non-Error
|
|
674
718
|
* throwable is wrapped so `recordException` still gets a real Error.
|
|
@@ -677,6 +721,7 @@ var init_spans = __esm({
|
|
|
677
721
|
const wrapped = error instanceof Error ? error : new Error(String(error));
|
|
678
722
|
this.span.recordException(wrapped);
|
|
679
723
|
this.span.setStatus({ code: SpanStatusCode.ERROR, message: wrapped.message });
|
|
724
|
+
this.span.setAttribute(ERROR_TYPE, errorType(error));
|
|
680
725
|
return this;
|
|
681
726
|
}
|
|
682
727
|
end() {
|
|
@@ -697,29 +742,27 @@ var instrumentationMcp_exports = {};
|
|
|
697
742
|
__export(instrumentationMcp_exports, {
|
|
698
743
|
instrumentMcpClient: () => instrumentMcpClient
|
|
699
744
|
});
|
|
700
|
-
import { SpanStatusCode as SpanStatusCode2 } from "@opentelemetry/api";
|
|
745
|
+
import { SpanKind as OtelSpanKind2, SpanStatusCode as SpanStatusCode2 } from "@opentelemetry/api";
|
|
701
746
|
function serializeResult(result) {
|
|
702
|
-
|
|
703
|
-
|
|
747
|
+
const record = asRecord(result);
|
|
748
|
+
if (record === void 0) return toAttributeValue(result);
|
|
704
749
|
const structured = record.structured_content ?? record.structuredContent;
|
|
705
750
|
if (structured !== void 0 && structured !== null) return toAttributeValue(structured);
|
|
706
751
|
const content = record.content;
|
|
707
752
|
if (Array.isArray(content)) {
|
|
708
|
-
const texts = content.map(
|
|
709
|
-
(block) => typeof block === "object" && block !== null ? block.text : void 0
|
|
710
|
-
).filter((text) => typeof text === "string");
|
|
753
|
+
const texts = content.map((block) => asRecord(block)?.text).filter((text) => typeof text === "string");
|
|
711
754
|
if (texts.length === 1) return truncate(texts[0]);
|
|
712
755
|
if (texts.length > 1) return toAttributeValue(texts);
|
|
713
756
|
}
|
|
714
757
|
return toAttributeValue(result);
|
|
715
758
|
}
|
|
716
759
|
function resultIsError(result) {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
return Boolean(record.is_error ?? record.isError);
|
|
760
|
+
const record = asRecord(result);
|
|
761
|
+
return Boolean(record?.is_error ?? record?.isError);
|
|
720
762
|
}
|
|
721
763
|
function recordResult(observation, result) {
|
|
722
|
-
const
|
|
764
|
+
const record = asRecord(result);
|
|
765
|
+
const resultType = record?.result_type ?? record?.resultType;
|
|
723
766
|
if (resultType === INPUT_REQUIRED) {
|
|
724
767
|
observation.setAttribute(MCP_RESULT_TYPE, INPUT_REQUIRED);
|
|
725
768
|
return;
|
|
@@ -730,8 +773,21 @@ function recordResult(observation, result) {
|
|
|
730
773
|
code: SpanStatusCode2.ERROR,
|
|
731
774
|
message: "tool returned an error result"
|
|
732
775
|
});
|
|
776
|
+
observation.setAttribute(ERROR_TYPE, ERROR_TYPE_TOOL_ERROR);
|
|
733
777
|
}
|
|
734
778
|
}
|
|
779
|
+
function negotiatedProtocolVersion(client) {
|
|
780
|
+
const version = asRecord(asRecord(client)?.transport)?.protocolVersion;
|
|
781
|
+
return typeof version === "string" ? version : void 0;
|
|
782
|
+
}
|
|
783
|
+
function callAttributes(toolName, protocolVersion) {
|
|
784
|
+
const attributes = {
|
|
785
|
+
[GEN_AI_TOOL_NAME]: toolName,
|
|
786
|
+
[MCP_METHOD_NAME]: MCP_METHOD_TOOLS_CALL
|
|
787
|
+
};
|
|
788
|
+
if (protocolVersion !== void 0) attributes[MCP_PROTOCOL_VERSION] = protocolVersion;
|
|
789
|
+
return attributes;
|
|
790
|
+
}
|
|
735
791
|
function instrumentMcpClient(ClientClass) {
|
|
736
792
|
const current = ClientClass.prototype.callTool;
|
|
737
793
|
const alreadyWrapped = current.riusOriginal;
|
|
@@ -744,12 +800,14 @@ function instrumentMcpClient(ClientClass) {
|
|
|
744
800
|
const instrumented = function instrumentedCallTool(params, ...rest) {
|
|
745
801
|
return startAsCurrentSpan(
|
|
746
802
|
`execute_tool ${params.name}`,
|
|
747
|
-
// Tool name at CREATION: pending snapshots are built at start, so an
|
|
748
|
-
// attribute set inside the callback never reaches them.
|
|
749
803
|
{
|
|
750
804
|
kind: "TOOL" /* TOOL */,
|
|
805
|
+
// The OTel SpanKind FIELD (orthogonal to our taxonomy attribute above):
|
|
806
|
+
// a remote tool call is CLIENT under both the MCP and the GenAI
|
|
807
|
+
// execute-tool conventions.
|
|
808
|
+
otelKind: OtelSpanKind2.CLIENT,
|
|
751
809
|
input: params.arguments,
|
|
752
|
-
attributes:
|
|
810
|
+
attributes: callAttributes(params.name, negotiatedProtocolVersion(this))
|
|
753
811
|
},
|
|
754
812
|
async (observation) => {
|
|
755
813
|
const result = await original.call(this, params, ...rest);
|
|
@@ -1655,6 +1713,12 @@ var Generation = class extends Observation {
|
|
|
1655
1713
|
}
|
|
1656
1714
|
provider;
|
|
1657
1715
|
firstTokenRecorded = false;
|
|
1716
|
+
/**
|
|
1717
|
+
* Monotonic clock at construction, which is span creation for both
|
|
1718
|
+
* `startGeneration` and the scoped form. The API `Span` exposes no start
|
|
1719
|
+
* time, so this is what `recordFirstToken` measures the first chunk against.
|
|
1720
|
+
*/
|
|
1721
|
+
startedAt = performance.now();
|
|
1658
1722
|
/**
|
|
1659
1723
|
* Record the request messages (`gen_ai.input.messages`), normalised to the
|
|
1660
1724
|
* GenAI `{role, parts}` shape like the Python SDK does: bare strings, OpenAI
|
|
@@ -1729,17 +1793,24 @@ var Generation = class extends Observation {
|
|
|
1729
1793
|
return this;
|
|
1730
1794
|
}
|
|
1731
1795
|
/**
|
|
1732
|
-
*
|
|
1733
|
-
*
|
|
1734
|
-
*
|
|
1735
|
-
*
|
|
1796
|
+
* Mark the arrival of the first streamed token. Records the
|
|
1797
|
+
* `gen_ai.first_token` event (the timestamp the backend derives TTFT from)
|
|
1798
|
+
* and, per the GenAI conventions, the derived
|
|
1799
|
+
* `gen_ai.response.time_to_first_chunk` (seconds since span creation) plus
|
|
1800
|
+
* `gen_ai.request.stream = true`: a first chunk arriving is what tells the
|
|
1801
|
+
* SDK the request streamed. Idempotent: only the first call records, so a
|
|
1802
|
+
* streaming loop can call this unconditionally on every chunk without
|
|
1803
|
+
* inflating the span. A no-op after the span has ended.
|
|
1736
1804
|
*/
|
|
1737
1805
|
recordFirstToken() {
|
|
1738
1806
|
if (this.firstTokenRecorded || !this.span.isRecording()) {
|
|
1739
1807
|
return this;
|
|
1740
1808
|
}
|
|
1809
|
+
const elapsedSeconds = Math.max(performance.now() - this.startedAt, 0) / 1e3;
|
|
1741
1810
|
this.span.addEvent(GEN_AI_FIRST_TOKEN_EVENT);
|
|
1742
1811
|
this.firstTokenRecorded = true;
|
|
1812
|
+
this.span.setAttribute(GEN_AI_REQUEST_STREAM, true);
|
|
1813
|
+
this.span.setAttribute(GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK, elapsedSeconds);
|
|
1743
1814
|
return this;
|
|
1744
1815
|
}
|
|
1745
1816
|
};
|
|
@@ -1763,7 +1834,10 @@ function configure2(generation, options) {
|
|
|
1763
1834
|
return generation;
|
|
1764
1835
|
}
|
|
1765
1836
|
function startGeneration(name, options = {}) {
|
|
1766
|
-
const span = getTracer().startSpan(name, {
|
|
1837
|
+
const span = getTracer().startSpan(name, {
|
|
1838
|
+
kind: otelSpanKind("LLM" /* LLM */),
|
|
1839
|
+
attributes: attributesFor(options)
|
|
1840
|
+
});
|
|
1767
1841
|
return configure2(new Generation(span, options.provider), options);
|
|
1768
1842
|
}
|
|
1769
1843
|
function startAsCurrentGeneration(name, optionsOrFn, maybeFn) {
|
|
@@ -1773,7 +1847,8 @@ function startAsCurrentGeneration(name, optionsOrFn, maybeFn) {
|
|
|
1773
1847
|
attributesFor(options),
|
|
1774
1848
|
options.userId,
|
|
1775
1849
|
(span) => configure2(new Generation(span, options.provider), options),
|
|
1776
|
-
fn
|
|
1850
|
+
fn,
|
|
1851
|
+
otelSpanKind("LLM" /* LLM */)
|
|
1777
1852
|
);
|
|
1778
1853
|
}
|
|
1779
1854
|
|
|
@@ -1806,7 +1881,7 @@ init_session();
|
|
|
1806
1881
|
init_user();
|
|
1807
1882
|
init_spans();
|
|
1808
1883
|
init_workspace();
|
|
1809
|
-
var VERSION = "0.
|
|
1884
|
+
var VERSION = "0.8.0";
|
|
1810
1885
|
export {
|
|
1811
1886
|
Generation,
|
|
1812
1887
|
Observation,
|