@runtypelabs/flue-otel 0.2.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 +155 -135
- package/dist/index.cjs +15 -3
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.mjs +15 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
# @runtypelabs/flue-otel
|
|
2
2
|
|
|
3
|
-
OpenTelemetry instrumentation for [Flue](https://flueframework.com) agents
|
|
4
|
-
|
|
3
|
+
OpenTelemetry instrumentation for [Flue](https://flueframework.com) agents.
|
|
4
|
+
It subscribes to a Flue runtime through `instrument()` and turns every agent
|
|
5
|
+
run into a trace of GenAI semantic-convention spans carrying Runtype's
|
|
6
|
+
`runtype.*` attributes. Export those spans to [Runtype](https://runtype.com)
|
|
7
|
+
and each run appears in your dashboard as a first-class execution: model,
|
|
8
|
+
token usage, cost, loop iterations, tool calls and stop reason.
|
|
5
9
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
- Works with Flue `>=1.0.0-beta.9` and 2.x from one entry point.
|
|
11
|
+
- Depends on `@opentelemetry/api` only. It brings no SDK, provider, exporter or
|
|
12
|
+
sampler; it writes through whatever your application has registered.
|
|
13
|
+
- Emits identifiers, structure and metrics only. No prompts, completions, tool
|
|
14
|
+
arguments, tool results, error messages or stack traces reach the wire.
|
|
15
|
+
|
|
16
|
+
Full guide: [Instrumenting a Flue agent](https://docs.runtype.com/developer-guides/guides/flue-instrumentation).
|
|
17
|
+
|
|
18
|
+
## Install
|
|
10
19
|
|
|
11
20
|
```bash
|
|
12
21
|
npm install @runtypelabs/flue-otel @opentelemetry/api
|
|
13
22
|
```
|
|
14
23
|
|
|
24
|
+
Requires Node 22+ and a Flue runtime that exposes `instrument()`.
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
27
|
+
|
|
15
28
|
```ts
|
|
16
29
|
import { instrument } from '@flue/runtime'
|
|
17
30
|
import { createRuntypeFlueInstrumentation } from '@runtypelabs/flue-otel'
|
|
@@ -19,59 +32,19 @@ import { createRuntypeFlueInstrumentation } from '@runtypelabs/flue-otel'
|
|
|
19
32
|
const stopInstrumenting = instrument(createRuntypeFlueInstrumentation())
|
|
20
33
|
```
|
|
21
34
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
`instrument()` composes, and this package carries its own key, so calling both
|
|
30
|
-
is possible and silently wrong: Runtype receives two `invoke_agent` spans for
|
|
31
|
-
one run and the **token counts and cost double**. Point exactly one Flue
|
|
32
|
-
instrumentation at a given Runtype endpoint.
|
|
33
|
-
- **You will lose the transcript.** Stock `@flue/opentelemetry` exports content
|
|
34
|
-
by default, so your runs currently carry prompts and completions. This release
|
|
35
|
-
exports none (below), which also means **eval capture from these runs stops
|
|
36
|
-
working**. In exchange the runs gain loop structure, an accurate iteration
|
|
37
|
-
count, a stop reason, and priced usage. If the transcript is what you rely on,
|
|
38
|
-
stay on stock until content ships.
|
|
39
|
-
|
|
40
|
-
## It emits no content
|
|
41
|
-
|
|
42
|
-
This release puts **no prompts, completions, tool arguments, tool results, error
|
|
43
|
-
messages or stack traces on the wire.** Only identifiers, structure and metrics.
|
|
44
|
-
|
|
45
|
-
That is deliberate and it is the default we intend to keep earning: a package
|
|
46
|
-
that lands inside your process should not start shipping your users' text
|
|
47
|
-
somewhere because you installed it. Content export is a later, explicitly
|
|
48
|
-
opt-in increment. Until it exists, there is nothing to configure and nothing to
|
|
49
|
-
audit — the spans carry model ids, token counts, durations, tool _names_, and
|
|
50
|
-
correlation ids.
|
|
51
|
-
|
|
52
|
-
Two consequences worth knowing:
|
|
53
|
-
|
|
54
|
-
- Your Runtype executions will show timing, cost, iteration counts and the tool
|
|
55
|
-
call sequence, but **no transcript**. Eval capture from an external run needs
|
|
56
|
-
content and is not available yet.
|
|
57
|
-
- If you also run stock `@flue/opentelemetry`, note that it treats calling
|
|
58
|
-
`instrument()` as consent to export content by default. That is a different
|
|
59
|
-
choice, not a bug — but it is worth checking before you point it at a
|
|
60
|
-
third-party backend.
|
|
61
|
-
|
|
62
|
-
## It brings no OpenTelemetry SDK
|
|
63
|
-
|
|
64
|
-
This package depends on `@opentelemetry/api` and nothing else. It creates no
|
|
65
|
-
provider, no exporter, no sampler, no resource, and it never flushes. Your
|
|
66
|
-
application owns all of that, and this instrumentation writes through whatever
|
|
67
|
-
you have registered.
|
|
68
|
-
|
|
69
|
-
If you already run OpenTelemetry, you are done after the `instrument()` call
|
|
70
|
-
above — add the resource attributes below so Runtype knows which agent the runs
|
|
71
|
-
belong to.
|
|
35
|
+
That is the whole integration if your process already runs an OpenTelemetry
|
|
36
|
+
SDK. Spans are written to the globally registered tracer provider, so anything
|
|
37
|
+
you already export (an OTLP exporter, a vendor exporter, a collector) receives
|
|
38
|
+
them. To send them to Runtype, add an OTLP/HTTP exporter pointed at
|
|
39
|
+
`https://api.runtype.com/v1/otel` with a Runtype API key that has the
|
|
40
|
+
`TELEMETRY:WRITE` scope, and set the resource attributes described under
|
|
41
|
+
[Attribution](#attribution).
|
|
72
42
|
|
|
73
43
|
### If you have no OpenTelemetry setup yet
|
|
74
44
|
|
|
45
|
+
You need `@opentelemetry/sdk-trace-node`,
|
|
46
|
+
`@opentelemetry/exporter-trace-otlp-http` and `@opentelemetry/resources`.
|
|
47
|
+
|
|
75
48
|
```ts
|
|
76
49
|
import { NodeTracerProvider, BatchSpanProcessor } from '@opentelemetry/sdk-trace-node'
|
|
77
50
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
|
|
@@ -85,7 +58,6 @@ import {
|
|
|
85
58
|
const provider = new NodeTracerProvider({
|
|
86
59
|
resource: resourceFromAttributes({
|
|
87
60
|
'service.name': 'my-agent',
|
|
88
|
-
// Which Runtype agent these runs file under, plus adapter provenance.
|
|
89
61
|
...runtypeFlueResourceAttributes({ agentId: process.env.RUNTYPE_AGENT_ID }),
|
|
90
62
|
}),
|
|
91
63
|
spanProcessors: [
|
|
@@ -97,45 +69,61 @@ const provider = new NodeTracerProvider({
|
|
|
97
69
|
),
|
|
98
70
|
],
|
|
99
71
|
})
|
|
100
|
-
|
|
101
|
-
// the
|
|
72
|
+
|
|
73
|
+
// Registers the provider AND a context manager. Without a context manager the
|
|
74
|
+
// OTel API falls back to a no-op and every span becomes its own trace root.
|
|
102
75
|
provider.register()
|
|
103
76
|
|
|
104
77
|
instrument(createRuntypeFlueInstrumentation())
|
|
105
78
|
|
|
106
|
-
// Flush before
|
|
79
|
+
// Flush before exit. See "Shutdown" below.
|
|
107
80
|
process.on('beforeExit', () => void provider.shutdown())
|
|
108
81
|
```
|
|
109
82
|
|
|
110
|
-
|
|
111
|
-
|
|
83
|
+
## Configuration
|
|
84
|
+
|
|
85
|
+
`createRuntypeFlueInstrumentation(options?)` accepts:
|
|
86
|
+
|
|
87
|
+
| Option | Type | Purpose |
|
|
88
|
+
| -------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
89
|
+
| `agents` | `Record<string, string>` | Runtype agent id per Flue agent name, e.g. `{ triage: 'agent_01j...' }`. Stamped as `runtype.agent.id` on that agent's `invoke_agent` span. See [Attribution](#attribution). |
|
|
90
|
+
| `tracer` | `Tracer` | Where spans are written. Defaults to `trace.getTracer('@runtypelabs/flue-otel')` on the global provider. Pass one to route Runtype spans through a separate provider. |
|
|
112
91
|
|
|
113
|
-
|
|
92
|
+
The returned object implements Flue's full `FlueInstrumentation` contract:
|
|
93
|
+
`observe` (builds spans from Flue's observation stream), `interceptor` (makes
|
|
94
|
+
each span the active OTel context around the agent, model and tool work, so
|
|
95
|
+
your own HTTP and database spans nest inside the run, and joins a dispatched
|
|
96
|
+
run to its originating trace via `executionContext.traceCarrier`), and
|
|
97
|
+
`dispose` (closes anything still open as `interrupted`).
|
|
114
98
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
arrives is recorded as still in flight, permanently.
|
|
99
|
+
`instrument()` returns a disposer; call it when you want to stop instrumenting.
|
|
100
|
+
Instrumentations compose, so installing this one never replaces another
|
|
101
|
+
subscriber. Its key is exported as `RUNTYPE_FLUE_INSTRUMENTATION_KEY`.
|
|
119
102
|
|
|
120
|
-
|
|
121
|
-
returning.
|
|
103
|
+
### Other exports
|
|
122
104
|
|
|
123
|
-
|
|
124
|
-
`
|
|
125
|
-
|
|
105
|
+
- `runtypeFlueResourceAttributes({ agentId? })` builds the `runtype.*` resource
|
|
106
|
+
attributes (`schema.version`, `adapter.name`, `adapter.version`, and
|
|
107
|
+
`agent.id` when given). Spread it into your SDK `Resource`.
|
|
108
|
+
- `GEN_AI` and `RUNTYPE`: the attribute-name constants this package emits.
|
|
109
|
+
- `RUNTYPE_STOP_REASONS`, `RUNTYPE_TOOL_TYPES`, `RUNTYPE_SCHEMA_VERSION`,
|
|
110
|
+
`ADAPTER_NAME`, `ADAPTER_VERSION`, plus the `RuntypeStopReason`,
|
|
111
|
+
`RuntypeToolType`, `FlueInstrumentation`, `FlueProjectionOptions`,
|
|
112
|
+
`SpanAttributes` and `SpanIntent` types.
|
|
126
113
|
|
|
127
|
-
## Attribution
|
|
114
|
+
## Attribution
|
|
128
115
|
|
|
129
|
-
Runtype
|
|
116
|
+
Runtype files each trace under one agent, resolved in this order:
|
|
130
117
|
|
|
131
|
-
1.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
3.
|
|
118
|
+
1. The `runtype.agent.id` **resource** attribute. `runtypeFlueResourceAttributes({ agentId })` sets it. Preferred.
|
|
119
|
+
2. The `x-runtype-agent-id` **request header** on the exporter (for example via
|
|
120
|
+
`OTEL_EXPORTER_OTLP_HEADERS=x-runtype-agent-id=agent_...`).
|
|
121
|
+
3. The `runtype.agent.id` attribute on the run's `invoke_agent` **span**, which
|
|
122
|
+
the `agents` option sets.
|
|
135
123
|
|
|
136
|
-
|
|
137
|
-
runs
|
|
138
|
-
|
|
124
|
+
Use 1 or 2 when a process runs one Runtype agent. Use the `agents` option when a
|
|
125
|
+
single process runs several Runtype agents, since a shared resource cannot name
|
|
126
|
+
the right one per run:
|
|
139
127
|
|
|
140
128
|
```ts
|
|
141
129
|
createRuntypeFlueInstrumentation({
|
|
@@ -146,72 +134,104 @@ createRuntypeFlueInstrumentation({
|
|
|
146
134
|
})
|
|
147
135
|
```
|
|
148
136
|
|
|
149
|
-
A
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
> **One agent invocation per trace.** Runtype's model is that one trace is one
|
|
153
|
-
> execution, and spans inherit whatever OTel context is active. So if an
|
|
154
|
-
> enclosing span is active while two agent invocations run — an HTTP server span
|
|
155
|
-
> from auto-instrumentation is the usual way this happens — both land in the
|
|
156
|
-
> same trace, and Runtype either merges them into one execution (dropping the
|
|
157
|
-
> second run's usage and stop reason) or, when they claim different
|
|
158
|
-
> `runtype.agent.id` values through the `agents` map above with no resource
|
|
159
|
-
> attribute and no header to fall back on, rejects the trace as
|
|
160
|
-
> `ambiguous_agent_attribution` and writes nothing.
|
|
161
|
-
>
|
|
162
|
-
> If you dispatch several agent invocations inside one request or job, start
|
|
163
|
-
> each one in its own trace, or attribute at the resource level with one process
|
|
164
|
-
> per agent. Flue's own `dispatch(...)` does not propagate trace context, so a
|
|
165
|
-
> plain dispatch is already its own trace.
|
|
166
|
-
|
|
167
|
-
## Composing with other instrumentations
|
|
168
|
-
|
|
169
|
-
Flue's `instrument()` composes — an error reporter and a tracer subscribe side
|
|
170
|
-
by side — and this package carries its own key, so installing it never replaces
|
|
171
|
-
`@flue/opentelemetry`.
|
|
172
|
-
|
|
173
|
-
> **Point exactly one instrumentation at Runtype.** If this package and a stock
|
|
174
|
-
> `@flue/opentelemetry` both export to the same Runtype endpoint, Runtype sees
|
|
175
|
-
> two `invoke_agent` spans for one run and the **token counts double**. Running
|
|
176
|
-
> both is fine when they export to different backends.
|
|
137
|
+
A trace with no attribution from any source is rejected, not guessed. A
|
|
138
|
+
delegated sub-agent is not attributed separately: its work belongs to the
|
|
139
|
+
delegating agent's trace, and one trace is one execution.
|
|
177
140
|
|
|
178
|
-
|
|
141
|
+
### One agent invocation per trace
|
|
142
|
+
|
|
143
|
+
Spans inherit whatever OTel context is active. If an enclosing span is active
|
|
144
|
+
while two agent invocations run (an HTTP server span from auto-instrumentation
|
|
145
|
+
is the common case), both land in the same trace. Runtype then either merges
|
|
146
|
+
them into one execution, dropping the second run's usage and stop reason, or,
|
|
147
|
+
when they claim different ids through `agents` with no resource attribute or
|
|
148
|
+
header to settle it, rejects the trace as `ambiguous_agent_attribution`.
|
|
149
|
+
|
|
150
|
+
If you dispatch several agent invocations inside one request or job, start each
|
|
151
|
+
in its own trace, or attribute at the resource level with one process per agent.
|
|
152
|
+
Flue's own `dispatch(...)` does not propagate trace context, so a plain dispatch
|
|
153
|
+
is already its own trace.
|
|
154
|
+
|
|
155
|
+
## Shutdown
|
|
179
156
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
157
|
+
This package never flushes; the exporter is yours. A `BatchSpanProcessor`
|
|
158
|
+
exports children before their parents, so a process that exits without
|
|
159
|
+
`forceFlush()` or `shutdown()` tends to lose the closing `invoke_agent` span
|
|
160
|
+
specifically, and a run whose envelope never arrives is recorded as still in
|
|
161
|
+
flight. Wire the shutdown. In a serverless handler, `await provider.forceFlush()`
|
|
162
|
+
before returning.
|
|
186
163
|
|
|
187
|
-
|
|
188
|
-
|
|
164
|
+
The disposer returned by `instrument()` ends any still-open span as
|
|
165
|
+
`interrupted`, folding the usage and iteration count seen so far onto the
|
|
166
|
+
envelope first. It does not flush.
|
|
189
167
|
|
|
190
|
-
|
|
168
|
+
## Point one instrumentation at Runtype
|
|
169
|
+
|
|
170
|
+
Flue's `instrument()` composes and this package carries its own key, so it can
|
|
171
|
+
run alongside `@flue/opentelemetry`. Do not export both to the same Runtype
|
|
172
|
+
endpoint: Runtype would receive two `invoke_agent` spans for one run and count
|
|
173
|
+
its tokens and cost twice. Running both is fine when they export to different
|
|
174
|
+
backends.
|
|
175
|
+
|
|
176
|
+
## What it emits
|
|
191
177
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
178
|
+
| Span | When | Carries |
|
|
179
|
+
| -------------------------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
180
|
+
| `invoke_agent <agent>` | once per agent invocation | `gen_ai.agent.name`, `gen_ai.conversation.id`, the run's request/response model, summed token usage across every turn (`gen_ai.usage.*`), `runtype.stop_reason`, `runtype.tools.reported`, the highest loop iteration (`runtype.iteration`), `runtype.execution.id`, and `runtype.agent.id` when `agents` names the agent |
|
|
181
|
+
| `chat <model>` | once per model turn | `gen_ai.provider.name`, request/response model, response id, finish reason, per-turn usage, request parameters (`max_tokens`, `temperature`, reasoning level, server address), `runtype.turn.id` / `runtype.turn.index` / `runtype.iteration`, and `runtype.provider.finish_reason` / `runtype.gateway.log_id` when the provider records them (Workers AI attaches both — the gateway log id is a pointer to that exact request in your AI Gateway dashboard) |
|
|
182
|
+
| `execute_tool <tool>` | once per model-requested tool call | `gen_ai.tool.name`, `gen_ai.tool.call.id`, the loop position it belongs to, `gen_ai.tool.type` (`function`) for every tool except a sub-agent delegation, and `runtype.tool.type` when the tool's class is known |
|
|
183
|
+
| `flue.task <agent>`, `flue.compaction`, `flue.operation shell` | delegation, compaction, host shell call | correlation ids only; framework structure, not agent invocations |
|
|
184
|
+
|
|
185
|
+
Every span also carries Flue's own `flue.*` correlation attributes
|
|
186
|
+
(`flue.instance.id`, `flue.submission.id`, `flue.agent.name`,
|
|
187
|
+
`flue.session.name`, `flue.operation.id`, `flue.turn.id`, `flue.task.id`, and
|
|
188
|
+
so on), so dashboards grouped on those keys keep working.
|
|
189
|
+
|
|
190
|
+
Rules the projection follows:
|
|
191
|
+
|
|
192
|
+
- **Exactly one `invoke_agent` span per run.** A sub-agent delegation is
|
|
193
|
+
reported as an `execute_tool` span typed `subagent`; the sub-agent's own work
|
|
194
|
+
gets a `flue.task` span. Nested `prompt` operations inside a task do not open
|
|
195
|
+
a second envelope.
|
|
196
|
+
- **Usage is summed from model-turn leaves onto the envelope**, compaction
|
|
197
|
+
turns included, so the batch carrying the terminal span always carries the
|
|
198
|
+
complete total. `gen_ai.usage.input_tokens` is the total prompt-side count;
|
|
199
|
+
cache reads and writes are broken out separately.
|
|
200
|
+
- **Loop position is counted in process.** `runtype.turn.index` and
|
|
201
|
+
`runtype.iteration` are absolute ordinals; only the root agent's turns carry
|
|
202
|
+
`runtype.iteration`, so delegated work never inflates the run's iteration
|
|
203
|
+
count.
|
|
204
|
+
- **A failed span carries the error type and exception class name only.**
|
|
205
|
+
- **Framework bookkeeping is never a trace root.** `flue.compaction` and
|
|
206
|
+
`flue.operation shell` are dropped when there is no run to nest under, so a
|
|
207
|
+
host-initiated `session.compact()` or `session.shell()` cannot register as an
|
|
208
|
+
execution.
|
|
209
|
+
|
|
210
|
+
### Where it deliberately says nothing
|
|
211
|
+
|
|
212
|
+
- **`runtype.tool.type` for an ordinary tool.** Flue's `origin` field says who
|
|
213
|
+
initiated a call, not what the tool is, so it is not mapped. Only genuine
|
|
214
|
+
class facts are emitted: `subagent` for a delegation, `data_connection` for a
|
|
215
|
+
1.x `datastore` tool.
|
|
216
|
+
- **A stop reason it cannot determine.** A run that ended mid tool call reports
|
|
198
217
|
`unknown` rather than guessing between a turn cap, a tool cap and a host
|
|
199
|
-
abort.
|
|
218
|
+
abort. `submission_settled` outcomes `failed` / `aborted` map to `error`; a
|
|
219
|
+
turn's finish reason maps to `end_turn`, `length`, `content_filter` or `error`.
|
|
200
220
|
|
|
201
221
|
An absent attribute costs one column. A wrong one renders as a measurement.
|
|
202
222
|
|
|
203
|
-
##
|
|
223
|
+
## Flue compatibility
|
|
204
224
|
|
|
205
|
-
|
|
206
|
-
|
|
225
|
+
The instrumentation reads only Flue's stable observation surface: event type
|
|
226
|
+
names, envelope and correlation fields, and the normalized `turn_request` /
|
|
207
227
|
`turn` / `tool_*` / `task` / `operation` / `compaction` / `submission_settled`
|
|
208
|
-
payloads.
|
|
228
|
+
payloads. It never reads `AgentMessage`, which Flue marks unstable. The few
|
|
229
|
+
fields that differ between the 1.x and 2.x lines are detected by probing the
|
|
230
|
+
observation rather than comparing versions, which is why one build serves both.
|
|
209
231
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
observation plane was essentially frozen across Flue's major rewrite, and the
|
|
214
|
-
handful of fields that did change are probed rather than version-compared.
|
|
232
|
+
An instrumentation must never break the agent it observes: every observer and
|
|
233
|
+
disposer path swallows its own errors, and an unparseable Flue timestamp falls
|
|
234
|
+
back to the SDK clock instead of throwing.
|
|
215
235
|
|
|
216
236
|
## License
|
|
217
237
|
|
package/dist/index.cjs
CHANGED
|
@@ -76,7 +76,7 @@ function mapSettlementOutcome(outcome) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// package.json
|
|
79
|
-
var version = "0.2.
|
|
79
|
+
var version = "0.2.2";
|
|
80
80
|
|
|
81
81
|
// src/semconv.ts
|
|
82
82
|
var GEN_AI = {
|
|
@@ -118,7 +118,9 @@ var RUNTYPE = {
|
|
|
118
118
|
toolsReported: "runtype.tools.reported",
|
|
119
119
|
toolType: "runtype.tool.type",
|
|
120
120
|
turnId: "runtype.turn.id",
|
|
121
|
-
turnIndex: "runtype.turn.index"
|
|
121
|
+
turnIndex: "runtype.turn.index",
|
|
122
|
+
providerFinishReason: "runtype.provider.finish_reason",
|
|
123
|
+
gatewayLogId: "runtype.gateway.log_id"
|
|
122
124
|
};
|
|
123
125
|
var FLUE = {
|
|
124
126
|
instanceId: "flue.instance.id",
|
|
@@ -459,7 +461,17 @@ function createFlueProjection(options = {}) {
|
|
|
459
461
|
...response.responseModel ? { [GEN_AI.responseModel]: response.responseModel } : {},
|
|
460
462
|
...response.responseId ? { [GEN_AI.responseId]: response.responseId } : {},
|
|
461
463
|
...response.finishReason ? { [GEN_AI.finishReasons]: [response.finishReason] } : {},
|
|
462
|
-
...usageAttributes(response.usage)
|
|
464
|
+
...usageAttributes(response.usage),
|
|
465
|
+
// Provider-diagnostic pointers, emitted only when Flue actually provides
|
|
466
|
+
// them. Both are optional and provider-dependent (Workers AI attaches
|
|
467
|
+
// both today); an absent value costs one column, a synthesized one would
|
|
468
|
+
// render as a measurement. `gatewayLogId` is a pointer to the content
|
|
469
|
+
// without shipping the content — a customer can click through to that
|
|
470
|
+
// exact request in their own AI Gateway dashboard. `providerFinishReason`
|
|
471
|
+
// is the provider's exact finish value before normalization, which our
|
|
472
|
+
// `GEN_AI.finishReasons` above deliberately hides.
|
|
473
|
+
...response.providerFinishReason ? { [RUNTYPE.providerFinishReason]: response.providerFinishReason } : {},
|
|
474
|
+
...response.gatewayLogId ? { [RUNTYPE.gatewayLogId]: response.gatewayLogId } : {}
|
|
463
475
|
};
|
|
464
476
|
const intents = [];
|
|
465
477
|
if (Object.keys(attributes).length > 0) intents.push({ kind: "update", ref, attributes });
|
package/dist/index.d.cts
CHANGED
|
@@ -100,6 +100,13 @@ interface FlueModelResponse {
|
|
|
100
100
|
finishReason?: string;
|
|
101
101
|
/** 2.x only. The provider's raw finish value before normalization. */
|
|
102
102
|
providerFinishReason?: string;
|
|
103
|
+
/**
|
|
104
|
+
* The response's own gateway log id (e.g. Cloudflare AI Gateway's
|
|
105
|
+
* `cf-aig-log-id`), for correlating a specific turn with its entry in the
|
|
106
|
+
* gateway dashboard. Telemetry only — present only when the provider records
|
|
107
|
+
* one. The Workers AI provider attaches it today.
|
|
108
|
+
*/
|
|
109
|
+
gatewayLogId?: string;
|
|
103
110
|
error?: FlueErrorInfo;
|
|
104
111
|
}
|
|
105
112
|
/**
|
|
@@ -475,6 +482,8 @@ declare const RUNTYPE: {
|
|
|
475
482
|
readonly toolType: "runtype.tool.type";
|
|
476
483
|
readonly turnId: "runtype.turn.id";
|
|
477
484
|
readonly turnIndex: "runtype.turn.index";
|
|
485
|
+
readonly providerFinishReason: "runtype.provider.finish_reason";
|
|
486
|
+
readonly gatewayLogId: "runtype.gateway.log_id";
|
|
478
487
|
};
|
|
479
488
|
/**
|
|
480
489
|
* `runtype.tool.type` values — the closed domain that drives display and
|
package/dist/index.d.ts
CHANGED
|
@@ -100,6 +100,13 @@ interface FlueModelResponse {
|
|
|
100
100
|
finishReason?: string;
|
|
101
101
|
/** 2.x only. The provider's raw finish value before normalization. */
|
|
102
102
|
providerFinishReason?: string;
|
|
103
|
+
/**
|
|
104
|
+
* The response's own gateway log id (e.g. Cloudflare AI Gateway's
|
|
105
|
+
* `cf-aig-log-id`), for correlating a specific turn with its entry in the
|
|
106
|
+
* gateway dashboard. Telemetry only — present only when the provider records
|
|
107
|
+
* one. The Workers AI provider attaches it today.
|
|
108
|
+
*/
|
|
109
|
+
gatewayLogId?: string;
|
|
103
110
|
error?: FlueErrorInfo;
|
|
104
111
|
}
|
|
105
112
|
/**
|
|
@@ -475,6 +482,8 @@ declare const RUNTYPE: {
|
|
|
475
482
|
readonly toolType: "runtype.tool.type";
|
|
476
483
|
readonly turnId: "runtype.turn.id";
|
|
477
484
|
readonly turnIndex: "runtype.turn.index";
|
|
485
|
+
readonly providerFinishReason: "runtype.provider.finish_reason";
|
|
486
|
+
readonly gatewayLogId: "runtype.gateway.log_id";
|
|
478
487
|
};
|
|
479
488
|
/**
|
|
480
489
|
* `runtype.tool.type` values — the closed domain that drives display and
|
package/dist/index.mjs
CHANGED
|
@@ -43,7 +43,7 @@ function mapSettlementOutcome(outcome) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// package.json
|
|
46
|
-
var version = "0.2.
|
|
46
|
+
var version = "0.2.2";
|
|
47
47
|
|
|
48
48
|
// src/semconv.ts
|
|
49
49
|
var GEN_AI = {
|
|
@@ -85,7 +85,9 @@ var RUNTYPE = {
|
|
|
85
85
|
toolsReported: "runtype.tools.reported",
|
|
86
86
|
toolType: "runtype.tool.type",
|
|
87
87
|
turnId: "runtype.turn.id",
|
|
88
|
-
turnIndex: "runtype.turn.index"
|
|
88
|
+
turnIndex: "runtype.turn.index",
|
|
89
|
+
providerFinishReason: "runtype.provider.finish_reason",
|
|
90
|
+
gatewayLogId: "runtype.gateway.log_id"
|
|
89
91
|
};
|
|
90
92
|
var FLUE = {
|
|
91
93
|
instanceId: "flue.instance.id",
|
|
@@ -426,7 +428,17 @@ function createFlueProjection(options = {}) {
|
|
|
426
428
|
...response.responseModel ? { [GEN_AI.responseModel]: response.responseModel } : {},
|
|
427
429
|
...response.responseId ? { [GEN_AI.responseId]: response.responseId } : {},
|
|
428
430
|
...response.finishReason ? { [GEN_AI.finishReasons]: [response.finishReason] } : {},
|
|
429
|
-
...usageAttributes(response.usage)
|
|
431
|
+
...usageAttributes(response.usage),
|
|
432
|
+
// Provider-diagnostic pointers, emitted only when Flue actually provides
|
|
433
|
+
// them. Both are optional and provider-dependent (Workers AI attaches
|
|
434
|
+
// both today); an absent value costs one column, a synthesized one would
|
|
435
|
+
// render as a measurement. `gatewayLogId` is a pointer to the content
|
|
436
|
+
// without shipping the content — a customer can click through to that
|
|
437
|
+
// exact request in their own AI Gateway dashboard. `providerFinishReason`
|
|
438
|
+
// is the provider's exact finish value before normalization, which our
|
|
439
|
+
// `GEN_AI.finishReasons` above deliberately hides.
|
|
440
|
+
...response.providerFinishReason ? { [RUNTYPE.providerFinishReason]: response.providerFinishReason } : {},
|
|
441
|
+
...response.gatewayLogId ? { [RUNTYPE.gatewayLogId]: response.gatewayLogId } : {}
|
|
430
442
|
};
|
|
431
443
|
const intents = [];
|
|
432
444
|
if (Object.keys(attributes).length > 0) intents.push({ kind: "update", ref, attributes });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/flue-otel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "OpenTelemetry instrumentation for Flue agents that emits GenAI semconv spans plus Runtype's runtype.* extension vocabulary, so a Flue run lands in Runtype at full fidelity.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"tsup": "^8.0.2",
|
|
35
35
|
"typescript": "^6.0.3",
|
|
36
36
|
"vitest": "^4.1.0",
|
|
37
|
-
"@runtypelabs/shared": "3.
|
|
37
|
+
"@runtypelabs/shared": "3.28.0"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|