@backendkit-labs/agent-otel 0.0.0-stage → 0.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.
- package/README.md +92 -3
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/mapping.d.ts +20 -0
- package/dist/mapping.d.ts.map +1 -0
- package/dist/mapping.js +107 -0
- package/dist/mapping.js.map +1 -0
- package/dist/otelApiExporter.d.ts +26 -0
- package/dist/otelApiExporter.d.ts.map +1 -0
- package/dist/otelApiExporter.js +87 -0
- package/dist/otelApiExporter.js.map +1 -0
- package/dist/otlpHttpExporter.d.ts +43 -0
- package/dist/otlpHttpExporter.d.ts.map +1 -0
- package/dist/otlpHttpExporter.js +108 -0
- package/dist/otlpHttpExporter.js.map +1 -0
- package/package.json +53 -6
package/README.md
CHANGED
|
@@ -1,3 +1,92 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# @backendkit-labs/agent-otel
|
|
2
|
+
|
|
3
|
+
OpenTelemetry export for the traces of [`@backendkit-labs/agent-core`](../core). Pick the exporter that fits
|
|
4
|
+
how you run telemetry:
|
|
5
|
+
|
|
6
|
+
| You have | Use |
|
|
7
|
+
|---|---|
|
|
8
|
+
| A collector or backend that speaks OTLP/HTTP (Jaeger, Tempo, Langfuse, Datadog, the OpenTelemetry Collector…) and **no** OpenTelemetry SDK | `otlpHttpSpanExporter` |
|
|
9
|
+
| An OpenTelemetry SDK already registered in the process (auto-instrumentation, your own processors/exporters) | `otelApiSpanExporter` |
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { createBaseEngine } from '@backendkit-labs/agent-core';
|
|
13
|
+
import { otlpHttpSpanExporter } from '@backendkit-labs/agent-otel';
|
|
14
|
+
|
|
15
|
+
const exporter = otlpHttpSpanExporter({ endpoint: 'http://localhost:4318', serviceName: 'support-agent' });
|
|
16
|
+
|
|
17
|
+
const engine = createBaseEngine({
|
|
18
|
+
// ...
|
|
19
|
+
observability: { tracing: { exporter } },
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// on shutdown
|
|
23
|
+
await exporter.shutdown();
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { otelApiSpanExporter } from '@backendkit-labs/agent-otel'; // needs @opentelemetry/api
|
|
28
|
+
observability: { tracing: { exporter: otelApiSpanExporter() } }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## What you get
|
|
32
|
+
|
|
33
|
+
One trace per `engine.run()`:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
agent.run
|
|
37
|
+
└─ invoke_agent Main
|
|
38
|
+
├─ chat deepseek-chat (tokens, model, provider, cost, cache tokens)
|
|
39
|
+
├─ execute_tool read_file (success, duration — parallel tools overlap in time)
|
|
40
|
+
└─ chat deepseek-chat
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Attributes follow the OpenTelemetry **GenAI semantic conventions**: `gen_ai.operation.name`
|
|
44
|
+
(`invoke_agent` / `chat` / `execute_tool`), `gen_ai.request.model`, `gen_ai.provider.name`,
|
|
45
|
+
`gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens`, `gen_ai.usage.cache_read.input_tokens`,
|
|
46
|
+
`gen_ai.usage.cache_creation.input_tokens`, `gen_ai.agent.id/name`, `gen_ai.tool.name`. Anything specific to
|
|
47
|
+
agent-core (cost in USD, the original trace id, guardrail actions, why an extra LLM call was made) lives under
|
|
48
|
+
`bk.*`, e.g. `bk.cost_usd`, `bk.llm.purpose` (`context_summary`, `structured_output`), `bk.guardrail.<name>.<action>`.
|
|
49
|
+
|
|
50
|
+
**Conversation content is never exported.** Spans carry names, durations, token counts and ids only: no
|
|
51
|
+
prompts, answers, tool arguments or tool results.
|
|
52
|
+
|
|
53
|
+
Requires `@backendkit-labs/agent-core` with `llm_call` / `tool_call` spans (0.28+). With 0.27 you only get
|
|
54
|
+
`agent.run` and `invoke_agent`.
|
|
55
|
+
|
|
56
|
+
## `otlpHttpSpanExporter(options)`
|
|
57
|
+
|
|
58
|
+
Sends OTLP/JSON to `<endpoint>/v1/traces`. No SDK, no dependencies.
|
|
59
|
+
|
|
60
|
+
| Option | Default | |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `endpoint` | `OTEL_EXPORTER_OTLP_ENDPOINT` or `http://localhost:4318` | `/v1/traces` is appended unless already there |
|
|
63
|
+
| `headers` | – | e.g. `{ Authorization: 'Basic …' }` |
|
|
64
|
+
| `serviceName` | `agent` | `service.name` resource attribute |
|
|
65
|
+
| `resourceAttributes` | – | `deployment.environment`, `service.version`… |
|
|
66
|
+
| `batchSize` | 64 | spans buffered before sending |
|
|
67
|
+
| `flushIntervalMs` | 2000 | a partial batch is sent after this long (timer is `unref`'d) |
|
|
68
|
+
| `timeoutMs` | 10000 | per request |
|
|
69
|
+
| `onError` | – | a failed send is reported here and **that batch is dropped**; it never reaches the agent |
|
|
70
|
+
| `fetch` | global `fetch` | for tests or custom transports |
|
|
71
|
+
|
|
72
|
+
Returns a function with `flush()` and `shutdown()`. Sends are serialized, never overlapping.
|
|
73
|
+
Ids are derived deterministically from agent-core's ids, so parent/child links are preserved.
|
|
74
|
+
|
|
75
|
+
## `otelApiSpanExporter(options)`
|
|
76
|
+
|
|
77
|
+
Re-creates the spans through `@opentelemetry/api` (`options.tracer`, default
|
|
78
|
+
`trace.getTracer('@backendkit-labs/agent-core')`), so they reach whatever your SDK exports to.
|
|
79
|
+
|
|
80
|
+
The OpenTelemetry API cannot choose a span's id, and a child must be created from its parent's real span.
|
|
81
|
+
agent-core exports spans as they end (children first), so this adapter holds a trace's spans until the root
|
|
82
|
+
`agent.run` ends and then creates the tree parents-first with the original start/end times. Consequences:
|
|
83
|
+
|
|
84
|
+
- Spans of a run appear in your backend when the run finishes, not while it runs.
|
|
85
|
+
- A run that never ends (crash) leaves its spans buffered: call `exporter.flush()` on shutdown (spans whose
|
|
86
|
+
parent never arrived become roots). `maxBufferedSpans` (default 5000) bounds memory.
|
|
87
|
+
- `@opentelemetry/api` is an optional peer dependency; without it the exporter throws a clear error at creation.
|
|
88
|
+
|
|
89
|
+
## Verified
|
|
90
|
+
|
|
91
|
+
Unit tests plus a run of a real engine through both exporters. The API adapter was also checked against a real
|
|
92
|
+
`@opentelemetry/sdk-trace-base` provider: one trace, correct hierarchy, parallel tool spans overlapping in time.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { otlpHttpSpanExporter, toOtlpSpan } from './otlpHttpExporter';
|
|
2
|
+
export type { OtlpHttpExporterOptions, OtlpHttpSpanExporter } from './otlpHttpExporter';
|
|
3
|
+
export { otelApiSpanExporter } from './otelApiExporter';
|
|
4
|
+
export type { OtelApiExporterOptions, OtelApiSpanExporter } from './otelApiExporter';
|
|
5
|
+
export { mapSpan, toTraceId, toSpanId } from './mapping';
|
|
6
|
+
export type { MappedSpan, OtelAttributes } from './mapping';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACtE,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACxF,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACzD,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toSpanId = exports.toTraceId = exports.mapSpan = exports.otelApiSpanExporter = exports.toOtlpSpan = exports.otlpHttpSpanExporter = void 0;
|
|
4
|
+
var otlpHttpExporter_1 = require("./otlpHttpExporter");
|
|
5
|
+
Object.defineProperty(exports, "otlpHttpSpanExporter", { enumerable: true, get: function () { return otlpHttpExporter_1.otlpHttpSpanExporter; } });
|
|
6
|
+
Object.defineProperty(exports, "toOtlpSpan", { enumerable: true, get: function () { return otlpHttpExporter_1.toOtlpSpan; } });
|
|
7
|
+
var otelApiExporter_1 = require("./otelApiExporter");
|
|
8
|
+
Object.defineProperty(exports, "otelApiSpanExporter", { enumerable: true, get: function () { return otelApiExporter_1.otelApiSpanExporter; } });
|
|
9
|
+
var mapping_1 = require("./mapping");
|
|
10
|
+
Object.defineProperty(exports, "mapSpan", { enumerable: true, get: function () { return mapping_1.mapSpan; } });
|
|
11
|
+
Object.defineProperty(exports, "toTraceId", { enumerable: true, get: function () { return mapping_1.toTraceId; } });
|
|
12
|
+
Object.defineProperty(exports, "toSpanId", { enumerable: true, get: function () { return mapping_1.toSpanId; } });
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uDAAsE;AAA7D,wHAAA,oBAAoB,OAAA;AAAE,8GAAA,UAAU,OAAA;AAEzC,qDAAwD;AAA/C,sHAAA,mBAAmB,OAAA;AAE5B,qCAAyD;AAAhD,kGAAA,OAAO,OAAA;AAAE,oGAAA,SAAS,OAAA;AAAE,mGAAA,QAAQ,OAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Span } from '@backendkit-labs/agent-core';
|
|
2
|
+
export type OtelAttributes = Record<string, string | number | boolean>;
|
|
3
|
+
export interface MappedSpan {
|
|
4
|
+
name: string;
|
|
5
|
+
kind: 'internal' | 'client';
|
|
6
|
+
attributes: OtelAttributes;
|
|
7
|
+
/** Set when the span ended in error. */
|
|
8
|
+
errorMessage?: string;
|
|
9
|
+
isError: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Deterministic OTel-shaped ids (32 / 16 hex chars) from whatever id format the tracer used. */
|
|
12
|
+
export declare function toTraceId(id: string): string;
|
|
13
|
+
export declare function toSpanId(id: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Translates an agent-core span into OpenTelemetry names and attributes following the GenAI semantic
|
|
16
|
+
* conventions (`invoke_agent`, `chat`, `execute_tool`). Prompts, answers and tool arguments are never
|
|
17
|
+
* part of a span, so nothing here can export conversation content.
|
|
18
|
+
*/
|
|
19
|
+
export declare function mapSpan(span: Span): MappedSpan;
|
|
20
|
+
//# sourceMappingURL=mapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../src/mapping.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAC;AAExD,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAEvE,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC5B,UAAU,EAAE,cAAc,CAAC;IAC3B,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;CACpB;AAED,iGAAiG;AACjG,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE3C;AAiBD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,UAAU,CAgE9C"}
|
package/dist/mapping.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toTraceId = toTraceId;
|
|
4
|
+
exports.toSpanId = toSpanId;
|
|
5
|
+
exports.mapSpan = mapSpan;
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
/** Deterministic OTel-shaped ids (32 / 16 hex chars) from whatever id format the tracer used. */
|
|
8
|
+
function toTraceId(id) {
|
|
9
|
+
return (0, crypto_1.createHash)('sha256').update(`trace:${id}`).digest('hex').slice(0, 32);
|
|
10
|
+
}
|
|
11
|
+
function toSpanId(id) {
|
|
12
|
+
return (0, crypto_1.createHash)('sha256').update(`span:${id}`).digest('hex').slice(0, 16);
|
|
13
|
+
}
|
|
14
|
+
/** `cost_usd` and similar keys keep their meaning under a vendor prefix. */
|
|
15
|
+
const VENDOR = 'bk.';
|
|
16
|
+
function str(v) {
|
|
17
|
+
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
18
|
+
}
|
|
19
|
+
function rest(tags, consumed) {
|
|
20
|
+
const out = {};
|
|
21
|
+
for (const [k, v] of Object.entries(tags)) {
|
|
22
|
+
if (!consumed.includes(k))
|
|
23
|
+
out[`${VENDOR}${k}`] = v;
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Translates an agent-core span into OpenTelemetry names and attributes following the GenAI semantic
|
|
29
|
+
* conventions (`invoke_agent`, `chat`, `execute_tool`). Prompts, answers and tool arguments are never
|
|
30
|
+
* part of a span, so nothing here can export conversation content.
|
|
31
|
+
*/
|
|
32
|
+
function mapSpan(span) {
|
|
33
|
+
const t = span.tags;
|
|
34
|
+
const isError = span.status === 'error';
|
|
35
|
+
switch (span.name) {
|
|
36
|
+
case 'run': {
|
|
37
|
+
const message = str(t.error);
|
|
38
|
+
return {
|
|
39
|
+
name: 'agent.run', kind: 'internal', isError, ...(message ? { errorMessage: message } : {}),
|
|
40
|
+
attributes: { 'bk.trace_id': span.traceId, ...rest(t, ['error']) },
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
case 'agent_block': {
|
|
44
|
+
const id = str(t.agent_id);
|
|
45
|
+
const name = str(t.agent_name);
|
|
46
|
+
return {
|
|
47
|
+
name: `invoke_agent ${name ?? id ?? 'agent'}`, kind: 'internal', isError,
|
|
48
|
+
attributes: {
|
|
49
|
+
'gen_ai.operation.name': 'invoke_agent',
|
|
50
|
+
...(id ? { 'gen_ai.agent.id': id } : {}),
|
|
51
|
+
...(name ? { 'gen_ai.agent.name': name } : {}),
|
|
52
|
+
...rest(t, ['agent_id', 'agent_name']),
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
case 'llm_call': {
|
|
57
|
+
const model = str(t.model);
|
|
58
|
+
const provider = str(t.provider);
|
|
59
|
+
const agent = str(t.agent_id);
|
|
60
|
+
const attributes = { 'gen_ai.operation.name': 'chat' };
|
|
61
|
+
if (model)
|
|
62
|
+
attributes['gen_ai.request.model'] = model;
|
|
63
|
+
if (provider) {
|
|
64
|
+
attributes['gen_ai.provider.name'] = provider;
|
|
65
|
+
attributes['gen_ai.system'] = provider;
|
|
66
|
+
}
|
|
67
|
+
if (agent)
|
|
68
|
+
attributes['gen_ai.agent.id'] = agent;
|
|
69
|
+
if (typeof t.input_tokens === 'number')
|
|
70
|
+
attributes['gen_ai.usage.input_tokens'] = t.input_tokens;
|
|
71
|
+
if (typeof t.output_tokens === 'number')
|
|
72
|
+
attributes['gen_ai.usage.output_tokens'] = t.output_tokens;
|
|
73
|
+
if (typeof t.cache_read_tokens === 'number')
|
|
74
|
+
attributes['gen_ai.usage.cache_read.input_tokens'] = t.cache_read_tokens;
|
|
75
|
+
if (typeof t.cache_write_tokens === 'number')
|
|
76
|
+
attributes['gen_ai.usage.cache_creation.input_tokens'] = t.cache_write_tokens;
|
|
77
|
+
if (typeof t.cost_usd === 'number')
|
|
78
|
+
attributes['bk.cost_usd'] = t.cost_usd;
|
|
79
|
+
if (str(t.purpose))
|
|
80
|
+
attributes['bk.llm.purpose'] = String(t.purpose);
|
|
81
|
+
Object.assign(attributes, rest(t, [
|
|
82
|
+
'agent_id', 'provider', 'model', 'input_tokens', 'output_tokens',
|
|
83
|
+
'cache_read_tokens', 'cache_write_tokens', 'cost_usd', 'purpose',
|
|
84
|
+
]));
|
|
85
|
+
return {
|
|
86
|
+
name: `chat ${model ?? 'unknown'}`, kind: 'client', isError, attributes,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
case 'tool_call': {
|
|
90
|
+
const tool = str(t['tool.name']);
|
|
91
|
+
const agent = str(t.agent_id);
|
|
92
|
+
return {
|
|
93
|
+
name: `execute_tool ${tool ?? 'unknown'}`, kind: 'internal', isError,
|
|
94
|
+
attributes: {
|
|
95
|
+
'gen_ai.operation.name': 'execute_tool',
|
|
96
|
+
'gen_ai.tool.type': 'function',
|
|
97
|
+
...(tool ? { 'gen_ai.tool.name': tool } : {}),
|
|
98
|
+
...(agent ? { 'gen_ai.agent.id': agent } : {}),
|
|
99
|
+
...rest(t, ['tool.name', 'agent_id']),
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
default:
|
|
104
|
+
return { name: span.name, kind: 'internal', isError, attributes: rest(t, []) };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=mapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping.js","sourceRoot":"","sources":["../src/mapping.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SpanExporter } from '@backendkit-labs/agent-core';
|
|
2
|
+
import type { Tracer as OtelTracer } from '@opentelemetry/api';
|
|
3
|
+
export interface OtelApiExporterOptions {
|
|
4
|
+
/** Tracer to create spans with. Default: `trace.getTracer('@backendkit-labs/agent-core')`. */
|
|
5
|
+
tracer?: OtelTracer;
|
|
6
|
+
/**
|
|
7
|
+
* Spans held while waiting for their root to end. Past this, everything pending is flushed with
|
|
8
|
+
* orphans promoted to roots. Default 5000.
|
|
9
|
+
*/
|
|
10
|
+
maxBufferedSpans?: number;
|
|
11
|
+
}
|
|
12
|
+
export type OtelApiSpanExporter = SpanExporter & {
|
|
13
|
+
/** Emits spans still waiting for a root (e.g. a run that never finished). */
|
|
14
|
+
flush(): void;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Re-creates agent-core spans through the OpenTelemetry API, so they flow to whatever SDK, processor and
|
|
18
|
+
* exporter the host application already configured.
|
|
19
|
+
*
|
|
20
|
+
* The OpenTelemetry API cannot choose a span's id, so a child must be created from its parent's real
|
|
21
|
+
* OTel span. agent-core exports spans when they end (children first), therefore this adapter holds a
|
|
22
|
+
* trace's spans until its root `run` span ends and then creates the tree parents-first, keeping the
|
|
23
|
+
* original start and end times. Use `otlpHttpSpanExporter` when you do not run the SDK.
|
|
24
|
+
*/
|
|
25
|
+
export declare function otelApiSpanExporter(options?: OtelApiExporterOptions): OtelApiSpanExporter;
|
|
26
|
+
//# sourceMappingURL=otelApiExporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otelApiExporter.d.ts","sourceRoot":"","sources":["../src/otelApiExporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,YAAY,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAA6B,MAAM,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAG1F,MAAM,WAAW,sBAAsB;IACnC,8FAA8F;IAC9F,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG;IAC7C,6EAA6E;IAC7E,KAAK,IAAI,IAAI,CAAC;CACjB,CAAC;AAgBF;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,sBAA2B,GAAG,mBAAmB,CA4D7F"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.otelApiSpanExporter = otelApiSpanExporter;
|
|
4
|
+
const mapping_1 = require("./mapping");
|
|
5
|
+
function loadApi() {
|
|
6
|
+
try {
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
8
|
+
return require('@opentelemetry/api');
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
throw new Error('otelApiSpanExporter needs "@opentelemetry/api" (npm i @opentelemetry/api) and an OpenTelemetry SDK ' +
|
|
12
|
+
'registered in the process. Use otlpHttpSpanExporter() to export without the SDK.');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Re-creates agent-core spans through the OpenTelemetry API, so they flow to whatever SDK, processor and
|
|
17
|
+
* exporter the host application already configured.
|
|
18
|
+
*
|
|
19
|
+
* The OpenTelemetry API cannot choose a span's id, so a child must be created from its parent's real
|
|
20
|
+
* OTel span. agent-core exports spans when they end (children first), therefore this adapter holds a
|
|
21
|
+
* trace's spans until its root `run` span ends and then creates the tree parents-first, keeping the
|
|
22
|
+
* original start and end times. Use `otlpHttpSpanExporter` when you do not run the SDK.
|
|
23
|
+
*/
|
|
24
|
+
function otelApiSpanExporter(options = {}) {
|
|
25
|
+
const api = loadApi();
|
|
26
|
+
const tracer = options.tracer ?? api.trace.getTracer('@backendkit-labs/agent-core');
|
|
27
|
+
const maxBuffered = options.maxBufferedSpans ?? 5000;
|
|
28
|
+
const pending = new Map(); // by core traceId
|
|
29
|
+
let buffered = 0;
|
|
30
|
+
const create = (span, parent) => {
|
|
31
|
+
const mapped = (0, mapping_1.mapSpan)(span);
|
|
32
|
+
const ctx = parent ? api.trace.setSpan(api.ROOT_CONTEXT, parent) : api.ROOT_CONTEXT;
|
|
33
|
+
const otel = tracer.startSpan(mapped.name, {
|
|
34
|
+
kind: mapped.kind === 'client' ? api.SpanKind.CLIENT : api.SpanKind.INTERNAL,
|
|
35
|
+
startTime: span.startedAt,
|
|
36
|
+
attributes: mapped.attributes,
|
|
37
|
+
}, ctx);
|
|
38
|
+
if (mapped.isError) {
|
|
39
|
+
otel.setStatus({ code: api.SpanStatusCode.ERROR, ...(mapped.errorMessage ? { message: mapped.errorMessage } : {}) });
|
|
40
|
+
}
|
|
41
|
+
otel.end(span.endedAt);
|
|
42
|
+
return otel;
|
|
43
|
+
};
|
|
44
|
+
// Creates `roots` and all their descendants, parents before children, and drops them from the buffer.
|
|
45
|
+
const emit = (traceId, rootIds) => {
|
|
46
|
+
const list = pending.get(traceId) ?? [];
|
|
47
|
+
const children = new Map();
|
|
48
|
+
for (const s of list) {
|
|
49
|
+
if (s.parentSpanId)
|
|
50
|
+
children.set(s.parentSpanId, [...(children.get(s.parentSpanId) ?? []), s]);
|
|
51
|
+
}
|
|
52
|
+
const used = new Set();
|
|
53
|
+
const walk = (span, parent) => {
|
|
54
|
+
used.add(span);
|
|
55
|
+
const otel = create(span, parent);
|
|
56
|
+
for (const child of (children.get(span.spanId) ?? []).sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime())) {
|
|
57
|
+
walk(child, otel);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
for (const root of list.filter(s => rootIds.has(s.spanId)))
|
|
61
|
+
walk(root, undefined);
|
|
62
|
+
const left = list.filter(s => !used.has(s));
|
|
63
|
+
buffered -= used.size;
|
|
64
|
+
if (left.length > 0)
|
|
65
|
+
pending.set(traceId, left);
|
|
66
|
+
else
|
|
67
|
+
pending.delete(traceId);
|
|
68
|
+
};
|
|
69
|
+
const flush = () => {
|
|
70
|
+
for (const [traceId, list] of [...pending]) {
|
|
71
|
+
const ids = new Set(list.map(s => s.spanId));
|
|
72
|
+
// Whatever has no pending parent starts a tree: real roots and spans whose parent never arrived.
|
|
73
|
+
emit(traceId, new Set(list.filter(s => !s.parentSpanId || !ids.has(s.parentSpanId)).map(s => s.spanId)));
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const exporter = ((span) => {
|
|
77
|
+
pending.set(span.traceId, [...(pending.get(span.traceId) ?? []), span]);
|
|
78
|
+
buffered++;
|
|
79
|
+
if (!span.parentSpanId)
|
|
80
|
+
emit(span.traceId, new Set([span.spanId]));
|
|
81
|
+
else if (buffered > maxBuffered)
|
|
82
|
+
flush();
|
|
83
|
+
});
|
|
84
|
+
exporter.flush = flush;
|
|
85
|
+
return exporter;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=otelApiExporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otelApiExporter.js","sourceRoot":"","sources":["../src/otelApiExporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Span, SpanExporter } from '@backendkit-labs/agent-core';
|
|
2
|
+
import type { OtelAttributes } from './mapping';
|
|
3
|
+
export interface OtlpHttpExporterOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Base URL of the collector or backend. Default: `OTEL_EXPORTER_OTLP_ENDPOINT` or
|
|
6
|
+
* `http://localhost:4318`. `/v1/traces` is appended unless the URL already ends with it.
|
|
7
|
+
*/
|
|
8
|
+
endpoint?: string;
|
|
9
|
+
/** Extra HTTP headers, e.g. `{ Authorization: 'Basic …' }` for Langfuse or a hosted backend. */
|
|
10
|
+
headers?: Record<string, string>;
|
|
11
|
+
/** Value of the `service.name` resource attribute. Default `agent`. */
|
|
12
|
+
serviceName?: string;
|
|
13
|
+
/** Additional resource attributes (deployment.environment, service.version, …). */
|
|
14
|
+
resourceAttributes?: OtelAttributes;
|
|
15
|
+
/** Spans buffered before a send is triggered. Default 64. */
|
|
16
|
+
batchSize?: number;
|
|
17
|
+
/** A partial batch is sent after this long. Default 2000 ms. */
|
|
18
|
+
flushIntervalMs?: number;
|
|
19
|
+
/** Request timeout. Default 10000 ms. */
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
/** Called when a send fails (the spans of that batch are dropped). Never thrown. */
|
|
22
|
+
onError?: (error: Error) => void;
|
|
23
|
+
/** Override for tests or custom transports. Default: global `fetch`. */
|
|
24
|
+
fetch?: typeof fetch;
|
|
25
|
+
}
|
|
26
|
+
export type OtlpHttpSpanExporter = SpanExporter & {
|
|
27
|
+
/** Sends everything buffered and resolves when the collector answered. */
|
|
28
|
+
flush(): Promise<void>;
|
|
29
|
+
/** Flushes and stops the timer. Call it on process shutdown. */
|
|
30
|
+
shutdown(): Promise<void>;
|
|
31
|
+
};
|
|
32
|
+
/** OTLP/JSON form of one span. */
|
|
33
|
+
export declare function toOtlpSpan(span: Span): Record<string, unknown>;
|
|
34
|
+
/**
|
|
35
|
+
* Sends agent-core spans to any OTLP/HTTP collector or backend (Jaeger, Tempo, Langfuse, Datadog, the
|
|
36
|
+
* OpenTelemetry Collector…) without the OpenTelemetry SDK. Spans are batched; a failing send never reaches
|
|
37
|
+
* the agent: it is reported through `onError` and that batch is dropped.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* observability: { tracing: { exporter: otlpHttpSpanExporter({ endpoint: 'http://localhost:4318', serviceName: 'support-agent' }) } }
|
|
41
|
+
*/
|
|
42
|
+
export declare function otlpHttpSpanExporter(options?: OtlpHttpExporterOptions): OtlpHttpSpanExporter;
|
|
43
|
+
//# sourceMappingURL=otlpHttpExporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otlpHttpExporter.d.ts","sourceRoot":"","sources":["../src/otlpHttpExporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,MAAM,WAAW,uBAAuB;IACpC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gGAAgG;IAChG,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,cAAc,CAAC;IACpC,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG;IAC9C,0EAA0E;IAC1E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,gEAAgE;IAChE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B,CAAC;AAmBF,kCAAkC;AAClC,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAa9D;AAOD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,uBAA4B,GAAG,oBAAoB,CAwDhG"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toOtlpSpan = toOtlpSpan;
|
|
4
|
+
exports.otlpHttpSpanExporter = otlpHttpSpanExporter;
|
|
5
|
+
const mapping_1 = require("./mapping");
|
|
6
|
+
const KIND = { internal: 1, client: 3 };
|
|
7
|
+
const SCOPE = { name: '@backendkit-labs/agent-otel', version: '0.1.0' };
|
|
8
|
+
function attrValue(v) {
|
|
9
|
+
if (typeof v === 'string')
|
|
10
|
+
return { stringValue: v };
|
|
11
|
+
if (typeof v === 'boolean')
|
|
12
|
+
return { boolValue: v };
|
|
13
|
+
return Number.isInteger(v) ? { intValue: String(v) } : { doubleValue: v };
|
|
14
|
+
}
|
|
15
|
+
function attributes(attrs) {
|
|
16
|
+
return Object.entries(attrs).map(([key, value]) => ({ key, value: attrValue(value) }));
|
|
17
|
+
}
|
|
18
|
+
function nanos(d) {
|
|
19
|
+
return (BigInt(d.getTime()) * 1000000n).toString();
|
|
20
|
+
}
|
|
21
|
+
/** OTLP/JSON form of one span. */
|
|
22
|
+
function toOtlpSpan(span) {
|
|
23
|
+
const m = (0, mapping_1.mapSpan)(span);
|
|
24
|
+
return {
|
|
25
|
+
traceId: (0, mapping_1.toTraceId)(span.traceId),
|
|
26
|
+
spanId: (0, mapping_1.toSpanId)(span.spanId),
|
|
27
|
+
...(span.parentSpanId ? { parentSpanId: (0, mapping_1.toSpanId)(span.parentSpanId) } : {}),
|
|
28
|
+
name: m.name,
|
|
29
|
+
kind: KIND[m.kind],
|
|
30
|
+
startTimeUnixNano: nanos(span.startedAt),
|
|
31
|
+
endTimeUnixNano: nanos(span.endedAt),
|
|
32
|
+
attributes: attributes(m.attributes),
|
|
33
|
+
status: m.isError ? { code: 2, ...(m.errorMessage ? { message: m.errorMessage } : {}) } : { code: 1 },
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function resolveUrl(endpoint) {
|
|
37
|
+
const base = endpoint.replace(/\/+$/, '');
|
|
38
|
+
return base.endsWith('/v1/traces') ? base : `${base}/v1/traces`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Sends agent-core spans to any OTLP/HTTP collector or backend (Jaeger, Tempo, Langfuse, Datadog, the
|
|
42
|
+
* OpenTelemetry Collector…) without the OpenTelemetry SDK. Spans are batched; a failing send never reaches
|
|
43
|
+
* the agent: it is reported through `onError` and that batch is dropped.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* observability: { tracing: { exporter: otlpHttpSpanExporter({ endpoint: 'http://localhost:4318', serviceName: 'support-agent' }) } }
|
|
47
|
+
*/
|
|
48
|
+
function otlpHttpSpanExporter(options = {}) {
|
|
49
|
+
const url = resolveUrl(options.endpoint ?? process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? 'http://localhost:4318');
|
|
50
|
+
const batchSize = Math.max(1, options.batchSize ?? 64);
|
|
51
|
+
const intervalMs = options.flushIntervalMs ?? 2000;
|
|
52
|
+
const timeoutMs = options.timeoutMs ?? 10_000;
|
|
53
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
54
|
+
const resourceAttrs = { 'service.name': options.serviceName ?? 'agent', ...(options.resourceAttributes ?? {}) };
|
|
55
|
+
let queue = [];
|
|
56
|
+
let timer;
|
|
57
|
+
// Sends run one after another so a slow collector never gets overlapping requests.
|
|
58
|
+
let chain = Promise.resolve();
|
|
59
|
+
const send = async (spans) => {
|
|
60
|
+
try {
|
|
61
|
+
const res = await doFetch(url, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: { 'content-type': 'application/json', ...(options.headers ?? {}) },
|
|
64
|
+
body: JSON.stringify({
|
|
65
|
+
resourceSpans: [{
|
|
66
|
+
resource: { attributes: attributes(resourceAttrs) },
|
|
67
|
+
scopeSpans: [{ scope: SCOPE, spans }],
|
|
68
|
+
}],
|
|
69
|
+
}),
|
|
70
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
71
|
+
});
|
|
72
|
+
if (!res.ok)
|
|
73
|
+
throw new Error(`OTLP export failed: HTTP ${res.status}`);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
options.onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const flush = () => {
|
|
80
|
+
if (queue.length > 0) {
|
|
81
|
+
const batch = queue;
|
|
82
|
+
queue = [];
|
|
83
|
+
chain = chain.then(() => send(batch));
|
|
84
|
+
}
|
|
85
|
+
return chain;
|
|
86
|
+
};
|
|
87
|
+
const exporter = ((span) => {
|
|
88
|
+
queue.push(toOtlpSpan(span));
|
|
89
|
+
if (queue.length >= batchSize) {
|
|
90
|
+
void flush();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (!timer && intervalMs > 0) {
|
|
94
|
+
timer = setInterval(() => { void flush(); }, intervalMs);
|
|
95
|
+
timer.unref?.();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
exporter.flush = flush;
|
|
99
|
+
exporter.shutdown = async () => {
|
|
100
|
+
if (timer) {
|
|
101
|
+
clearInterval(timer);
|
|
102
|
+
timer = undefined;
|
|
103
|
+
}
|
|
104
|
+
await flush();
|
|
105
|
+
};
|
|
106
|
+
return exporter;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=otlpHttpExporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otlpHttpExporter.js","sourceRoot":"","sources":["../src/otlpHttpExporter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@backendkit-labs/agent-otel",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@backendkit-labs/agent-otel",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenTelemetry export for @backendkit-labs/agent-core traces: OTLP/HTTP exporter (no SDK needed) and an adapter for the OpenTelemetry API, with GenAI semantic conventions",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist/",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"dev": "tsc --watch",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"test": "jest"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@backendkit-labs/agent-core": ">=0.27.0 <0.29.0",
|
|
26
|
+
"@opentelemetry/api": ">=1.4.0 <2"
|
|
27
|
+
},
|
|
28
|
+
"peerDependenciesMeta": {
|
|
29
|
+
"@opentelemetry/api": {
|
|
30
|
+
"optional": true
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@opentelemetry/api": "^1.9.0",
|
|
35
|
+
"@types/jest": "^29.0.0",
|
|
36
|
+
"@types/node": "^20.0.0",
|
|
37
|
+
"jest": "^29.0.0",
|
|
38
|
+
"ts-jest": "^29.0.0",
|
|
39
|
+
"typescript": "^5.4.0"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20.0.0"
|
|
43
|
+
},
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/BackendKit-labs/backendkit-agent-framework.git",
|
|
47
|
+
"directory": "packages/otel"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"license": "MIT"
|
|
53
|
+
}
|