-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathobservability.ts
More file actions
88 lines (81 loc) · 2.72 KB
/
Copy pathobservability.ts
File metadata and controls
88 lines (81 loc) · 2.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* Observability example.
*
* Passes a `DroidObservability` bundle (logger, metrics, tracing) to
* `createSession()`, which wires it into both the spawned droid process
* transport and the client. Spawning the process records a spawn-latency
* metric and a log; every outgoing request asks the tracing sink to inject
* trace context into the request's `_meta`.
*
* The example then runs one real conversational turn through
* `session.stream()` so the sinks observe request/response traffic from actual
* work rather than only process startup.
*
* Sinks only ever receive structured, privacy-safe values — never raw
* stdout/stderr or message content.
*
* Usage:
* npx tsx examples/node/observability.ts
*/
import {
DroidMessageType,
createSession,
type DroidLogEvent,
type DroidMetricEvent,
type DroidObservability,
type DroidTraceContextCarrier,
} from '@factory/droid-sdk/node';
let logCount = 0;
const observability: DroidObservability = {
logger: {
log(event: DroidLogEvent): void {
logCount++;
console.log(
`[log:${event.level}] ${event.name} — ${event.message}`,
event.attributes ?? {}
);
},
},
metrics: {
record(event: DroidMetricEvent): void {
console.log(
`[metric] ${event.name}=${event.value}${event.unit} (${event.kind})`,
event.attributes ?? {}
);
},
},
tracing: {
inject(carrier: DroidTraceContextCarrier): void {
// Populate the carrier from your tracer's active span. The SDK copies
// traceparent/tracestate into each request's `_meta`.
carrier.traceparent =
'00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01';
carrier.tracestate = 'factory=1';
},
},
};
const session = await createSession({ observability });
// Snapshot before the turn. Spawning the process alone emits a log, so a
// total-count assertion would pass having observed only startup. Logs are also
// the only turn-scoped signal here: the SDK's sole metric is spawn latency,
// recorded before the turn begins, so metrics cannot witness turn traffic.
const logCountBeforeTurn = logCount;
let turnLogCount = 0;
try {
for await (const msg of session.stream(
'Reply with the single word: ready.'
)) {
if (msg.type === DroidMessageType.Assistant) {
console.log(`[assistant] ${msg.text}`);
}
}
// Measure before teardown: `session.close()` sends a `close_session` request,
// which is itself logged, so counting after the `finally` would always find
// at least one log regardless of what the turn did.
turnLogCount = logCount - logCountBeforeTurn;
} finally {
await session.close();
}
if (turnLogCount === 0) {
throw new Error('Observability sinks saw no logged requests for the turn.');
}