@sensu-ai/sdk 0.1.5 → 0.5.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/dist/client.d.ts +128 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +837 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/langchain.d.ts +60 -0
- package/dist/integrations/langchain.d.ts.map +1 -0
- package/dist/integrations/langchain.js +238 -0
- package/dist/integrations/langchain.js.map +1 -0
- package/dist/integrations/openai.d.ts +25 -0
- package/dist/integrations/openai.d.ts.map +1 -0
- package/dist/integrations/openai.js +69 -0
- package/dist/integrations/openai.js.map +1 -0
- package/dist/types.d.ts +224 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +20 -6
- package/src/client.ts +0 -771
- package/src/index.ts +0 -24
- package/src/integrations/langchain.ts +0 -175
- package/src/integrations/openai.ts +0 -109
- package/src/types.ts +0 -213
- package/tsconfig.json +0 -11
package/src/client.ts
DELETED
|
@@ -1,771 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'crypto';
|
|
2
|
-
import type { TelemetryEvent } from '@sensu-ai/shared';
|
|
3
|
-
import type {
|
|
4
|
-
SensuClientOptions,
|
|
5
|
-
StartRunOptions,
|
|
6
|
-
StartStepOptions,
|
|
7
|
-
TrackLlmOptions,
|
|
8
|
-
TrackToolOptions,
|
|
9
|
-
RawLlmCallOptions,
|
|
10
|
-
ContextBreakdown,
|
|
11
|
-
TrackRetrievalOptions,
|
|
12
|
-
RawRetrievalOptions,
|
|
13
|
-
TrackEmbeddingOptions,
|
|
14
|
-
RawEmbeddingOptions,
|
|
15
|
-
RecordFeedbackOptions,
|
|
16
|
-
RecordEvalScoreOptions,
|
|
17
|
-
HandoffOptions,
|
|
18
|
-
SpawnRunOptions,
|
|
19
|
-
TrackGuardrailOptions,
|
|
20
|
-
RawGuardrailOptions,
|
|
21
|
-
RecordPromptRenderOptions,
|
|
22
|
-
DeployPromptVersionOptions,
|
|
23
|
-
StartSessionOptions,
|
|
24
|
-
ResumeSessionOptions,
|
|
25
|
-
} from './types.js';
|
|
26
|
-
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// StepHandle — fluent API for a single step
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
export class StepHandle {
|
|
32
|
-
private readonly client: SensuClient;
|
|
33
|
-
readonly stepId: string;
|
|
34
|
-
readonly runId: string;
|
|
35
|
-
readonly sessionId: string;
|
|
36
|
-
readonly agentId: string;
|
|
37
|
-
readonly orgId: string;
|
|
38
|
-
readonly traceId: string;
|
|
39
|
-
readonly spanId: string;
|
|
40
|
-
private sequence: number;
|
|
41
|
-
private stepName?: string;
|
|
42
|
-
private ended = false;
|
|
43
|
-
|
|
44
|
-
constructor(
|
|
45
|
-
client: SensuClient,
|
|
46
|
-
opts: {
|
|
47
|
-
stepId: string;
|
|
48
|
-
runId: string;
|
|
49
|
-
sessionId: string;
|
|
50
|
-
agentId: string;
|
|
51
|
-
orgId: string;
|
|
52
|
-
traceId: string;
|
|
53
|
-
spanId: string;
|
|
54
|
-
sequence: number;
|
|
55
|
-
name?: string;
|
|
56
|
-
},
|
|
57
|
-
) {
|
|
58
|
-
this.client = client;
|
|
59
|
-
this.stepId = opts.stepId;
|
|
60
|
-
this.runId = opts.runId;
|
|
61
|
-
this.sessionId = opts.sessionId;
|
|
62
|
-
this.agentId = opts.agentId;
|
|
63
|
-
this.orgId = opts.orgId;
|
|
64
|
-
this.traceId = opts.traceId;
|
|
65
|
-
this.spanId = opts.spanId;
|
|
66
|
-
this.sequence = opts.sequence;
|
|
67
|
-
this.stepName = opts.name;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
private base() {
|
|
71
|
-
return {
|
|
72
|
-
event_id: randomUUID(),
|
|
73
|
-
timestamp: new Date().toISOString(),
|
|
74
|
-
org_id: this.orgId,
|
|
75
|
-
agent_id: this.agentId,
|
|
76
|
-
session_id: this.sessionId,
|
|
77
|
-
run_id: this.runId,
|
|
78
|
-
step_id: this.stepId,
|
|
79
|
-
trace_id: this.traceId,
|
|
80
|
-
span_id: randomUUID(),
|
|
81
|
-
parent_span_id: this.spanId,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Track an LLM call — wraps fn(), measures latency, emits event */
|
|
86
|
-
async trackLlm(opts: TrackLlmOptions): Promise<unknown> {
|
|
87
|
-
const startMs = Date.now();
|
|
88
|
-
const spanId = randomUUID();
|
|
89
|
-
|
|
90
|
-
this.client.enqueue({
|
|
91
|
-
...this.base(),
|
|
92
|
-
span_id: spanId,
|
|
93
|
-
event_type: 'llm.request.started',
|
|
94
|
-
provider: opts.provider,
|
|
95
|
-
model: opts.model,
|
|
96
|
-
max_context_tokens: opts.maxContextTokens,
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
let result: unknown;
|
|
100
|
-
let status: 'success' | 'error' = 'success';
|
|
101
|
-
let err: unknown;
|
|
102
|
-
|
|
103
|
-
try {
|
|
104
|
-
result = await opts.fn();
|
|
105
|
-
} catch (e) {
|
|
106
|
-
status = 'error';
|
|
107
|
-
err = e;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const latencyMs = Date.now() - startMs;
|
|
111
|
-
|
|
112
|
-
// Try to extract token usage from common response shapes
|
|
113
|
-
const usage = extractUsage(result, opts.model);
|
|
114
|
-
const contextBreakdown = opts.extractContextBreakdown?.(result);
|
|
115
|
-
|
|
116
|
-
this.client.enqueue({
|
|
117
|
-
...this.base(),
|
|
118
|
-
span_id: spanId,
|
|
119
|
-
event_type: 'llm.request.completed',
|
|
120
|
-
provider: opts.provider,
|
|
121
|
-
model: opts.model,
|
|
122
|
-
max_context_tokens: opts.maxContextTokens,
|
|
123
|
-
latency_ms: latencyMs,
|
|
124
|
-
status,
|
|
125
|
-
...usage,
|
|
126
|
-
...(contextBreakdown ? { context_breakdown: contextBreakdown } : {}),
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
if (err) throw err;
|
|
130
|
-
return result;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/** Emit a raw LLM call event (when you have the stats already) */
|
|
134
|
-
recordLlm(opts: RawLlmCallOptions): void {
|
|
135
|
-
const { contextBreakdown, ...rest } = opts;
|
|
136
|
-
this.client.enqueue({
|
|
137
|
-
...this.base(),
|
|
138
|
-
event_type: 'llm.request.completed',
|
|
139
|
-
...rest,
|
|
140
|
-
...(contextBreakdown ? { context_breakdown: contextBreakdown } : {}),
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/** Track a tool call — wraps fn(), measures latency */
|
|
145
|
-
async trackTool(opts: TrackToolOptions): Promise<unknown> {
|
|
146
|
-
const startMs = Date.now();
|
|
147
|
-
let result: unknown;
|
|
148
|
-
let status: 'success' | 'error' = 'success';
|
|
149
|
-
let err: unknown;
|
|
150
|
-
|
|
151
|
-
try {
|
|
152
|
-
result = await opts.fn();
|
|
153
|
-
} catch (e) {
|
|
154
|
-
status = 'error';
|
|
155
|
-
err = e;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const latencyMs = Date.now() - startMs;
|
|
159
|
-
const outputSize = estimateBytes(result);
|
|
160
|
-
|
|
161
|
-
this.client.enqueue({
|
|
162
|
-
...this.base(),
|
|
163
|
-
event_type: 'tool.call.completed',
|
|
164
|
-
tool_name: opts.toolName,
|
|
165
|
-
latency_ms: latencyMs,
|
|
166
|
-
status,
|
|
167
|
-
output_size_bytes: outputSize,
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
if (err) throw err;
|
|
171
|
-
return result;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/** Track a retrieval call — wraps fn(), measures latency, emits started + completed */
|
|
175
|
-
async trackRetrieval(opts: TrackRetrievalOptions): Promise<unknown> {
|
|
176
|
-
const startMs = Date.now();
|
|
177
|
-
const spanId = randomUUID();
|
|
178
|
-
|
|
179
|
-
this.client.enqueue({
|
|
180
|
-
...this.base(),
|
|
181
|
-
span_id: spanId,
|
|
182
|
-
event_type: 'retrieval.started',
|
|
183
|
-
vector_store_id: opts.vectorStoreId,
|
|
184
|
-
top_k: opts.topK,
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
let result: unknown;
|
|
188
|
-
let status: 'success' | 'error' = 'success';
|
|
189
|
-
let err: unknown;
|
|
190
|
-
|
|
191
|
-
try {
|
|
192
|
-
result = await opts.fn();
|
|
193
|
-
} catch (e) {
|
|
194
|
-
status = 'error';
|
|
195
|
-
err = e;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const latencyMs = Date.now() - startMs;
|
|
199
|
-
|
|
200
|
-
this.client.enqueue({
|
|
201
|
-
...this.base(),
|
|
202
|
-
span_id: spanId,
|
|
203
|
-
event_type: 'retrieval.completed',
|
|
204
|
-
vector_store_id: opts.vectorStoreId,
|
|
205
|
-
top_k: opts.topK,
|
|
206
|
-
latency_ms: latencyMs,
|
|
207
|
-
status,
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
if (err) throw err;
|
|
211
|
-
return result;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/** Emit a raw retrieval completed event (when you have the stats already) */
|
|
215
|
-
recordRetrieval(opts: RawRetrievalOptions): void {
|
|
216
|
-
this.client.enqueue({
|
|
217
|
-
...this.base(),
|
|
218
|
-
event_type: 'retrieval.completed',
|
|
219
|
-
...opts,
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/** Track an embedding call — wraps fn(), measures latency */
|
|
224
|
-
async trackEmbedding(opts: TrackEmbeddingOptions): Promise<unknown> {
|
|
225
|
-
const startMs = Date.now();
|
|
226
|
-
let result: unknown;
|
|
227
|
-
let err: unknown;
|
|
228
|
-
|
|
229
|
-
try {
|
|
230
|
-
result = await opts.fn();
|
|
231
|
-
} catch (e) {
|
|
232
|
-
err = e;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const latencyMs = Date.now() - startMs;
|
|
236
|
-
|
|
237
|
-
this.client.enqueue({
|
|
238
|
-
...this.base(),
|
|
239
|
-
event_type: 'embedding.created',
|
|
240
|
-
model: opts.model,
|
|
241
|
-
input_text_length: opts.inputTextLength,
|
|
242
|
-
batch_size: opts.batchSize,
|
|
243
|
-
latency_ms: latencyMs,
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
if (err) throw err;
|
|
247
|
-
return result;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/** Emit a raw embedding event */
|
|
251
|
-
recordEmbedding(opts: RawEmbeddingOptions): void {
|
|
252
|
-
this.client.enqueue({
|
|
253
|
-
...this.base(),
|
|
254
|
-
event_type: 'embedding.created',
|
|
255
|
-
model: opts.model,
|
|
256
|
-
input_text_length: opts.inputTextLength,
|
|
257
|
-
token_count: opts.tokenCount,
|
|
258
|
-
latency_ms: opts.latencyMs,
|
|
259
|
-
cost_usd_estimate: opts.costUsdEstimate,
|
|
260
|
-
batch_size: opts.batchSize,
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/** Track a guardrail check — wraps fn(), measures latency, handles block */
|
|
265
|
-
async trackGuardrail(opts: TrackGuardrailOptions): Promise<'pass' | 'fail' | 'modified'> {
|
|
266
|
-
const startMs = Date.now();
|
|
267
|
-
|
|
268
|
-
this.client.enqueue({
|
|
269
|
-
...this.base(),
|
|
270
|
-
event_type: 'guardrail.check.started',
|
|
271
|
-
guardrail_id: opts.guardrailId,
|
|
272
|
-
guardrail_type: opts.guardrailType,
|
|
273
|
-
input_hash: opts.inputHash,
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
let result: 'pass' | 'fail' | 'modified' = 'pass';
|
|
277
|
-
let err: unknown;
|
|
278
|
-
|
|
279
|
-
try {
|
|
280
|
-
result = await opts.fn();
|
|
281
|
-
} catch (e) {
|
|
282
|
-
err = e;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const latencyMs = Date.now() - startMs;
|
|
286
|
-
|
|
287
|
-
this.client.enqueue({
|
|
288
|
-
...this.base(),
|
|
289
|
-
event_type: 'guardrail.check.completed',
|
|
290
|
-
guardrail_id: opts.guardrailId,
|
|
291
|
-
guardrail_type: opts.guardrailType,
|
|
292
|
-
input_hash: opts.inputHash,
|
|
293
|
-
result,
|
|
294
|
-
latency_ms: latencyMs,
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
if (err) throw err;
|
|
298
|
-
return result;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/** Emit a raw guardrail result (check or block) */
|
|
302
|
-
recordGuardrail(opts: RawGuardrailOptions): void {
|
|
303
|
-
if (opts.blocked) {
|
|
304
|
-
this.client.enqueue({
|
|
305
|
-
...this.base(),
|
|
306
|
-
event_type: 'guardrail.blocked',
|
|
307
|
-
guardrail_id: opts.guardrailId,
|
|
308
|
-
guardrail_type: opts.guardrailType,
|
|
309
|
-
input_hash: opts.inputHash,
|
|
310
|
-
block_reason: opts.blockReason,
|
|
311
|
-
severity: opts.severity,
|
|
312
|
-
});
|
|
313
|
-
} else {
|
|
314
|
-
this.client.enqueue({
|
|
315
|
-
...this.base(),
|
|
316
|
-
event_type: 'guardrail.check.completed',
|
|
317
|
-
guardrail_id: opts.guardrailId,
|
|
318
|
-
guardrail_type: opts.guardrailType,
|
|
319
|
-
input_hash: opts.inputHash,
|
|
320
|
-
result: opts.result,
|
|
321
|
-
latency_ms: opts.latencyMs,
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/** Record a prompt template render event */
|
|
327
|
-
recordPromptRender(opts: RecordPromptRenderOptions): void {
|
|
328
|
-
this.client.enqueue({
|
|
329
|
-
...this.base(),
|
|
330
|
-
event_type: 'prompt.rendered',
|
|
331
|
-
template_id: opts.templateId,
|
|
332
|
-
template_version: opts.templateVersion,
|
|
333
|
-
rendered_token_count: opts.renderedTokenCount,
|
|
334
|
-
variable_count: opts.variableCount,
|
|
335
|
-
latency_ms: opts.latencyMs,
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
async end(): Promise<void> {
|
|
340
|
-
if (this.ended) return;
|
|
341
|
-
this.ended = true;
|
|
342
|
-
this.client.enqueue({
|
|
343
|
-
...this.base(),
|
|
344
|
-
event_type: 'agent.step.completed',
|
|
345
|
-
});
|
|
346
|
-
await this.client.flush();
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// ---------------------------------------------------------------------------
|
|
351
|
-
// RunHandle — fluent API for a single run
|
|
352
|
-
// ---------------------------------------------------------------------------
|
|
353
|
-
|
|
354
|
-
export class RunHandle {
|
|
355
|
-
private readonly client: SensuClient;
|
|
356
|
-
readonly runId: string;
|
|
357
|
-
readonly sessionId: string;
|
|
358
|
-
readonly agentId: string;
|
|
359
|
-
readonly orgId: string;
|
|
360
|
-
readonly traceId: string;
|
|
361
|
-
readonly spanId: string;
|
|
362
|
-
private stepCount = 0;
|
|
363
|
-
private ended = false;
|
|
364
|
-
|
|
365
|
-
constructor(
|
|
366
|
-
client: SensuClient,
|
|
367
|
-
opts: {
|
|
368
|
-
runId: string;
|
|
369
|
-
sessionId: string;
|
|
370
|
-
agentId: string;
|
|
371
|
-
orgId: string;
|
|
372
|
-
traceId: string;
|
|
373
|
-
spanId: string;
|
|
374
|
-
},
|
|
375
|
-
) {
|
|
376
|
-
this.client = client;
|
|
377
|
-
this.runId = opts.runId;
|
|
378
|
-
this.sessionId = opts.sessionId;
|
|
379
|
-
this.agentId = opts.agentId;
|
|
380
|
-
this.orgId = opts.orgId;
|
|
381
|
-
this.traceId = opts.traceId;
|
|
382
|
-
this.spanId = opts.spanId;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
private base() {
|
|
386
|
-
return {
|
|
387
|
-
event_id: randomUUID(),
|
|
388
|
-
timestamp: new Date().toISOString(),
|
|
389
|
-
org_id: this.orgId,
|
|
390
|
-
agent_id: this.agentId,
|
|
391
|
-
session_id: this.sessionId,
|
|
392
|
-
run_id: this.runId,
|
|
393
|
-
trace_id: this.traceId,
|
|
394
|
-
span_id: randomUUID(),
|
|
395
|
-
parent_span_id: this.spanId,
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
startStep(opts: StartStepOptions = {}): StepHandle {
|
|
400
|
-
const stepId = opts.stepId ?? randomUUID();
|
|
401
|
-
const sequence = this.stepCount++;
|
|
402
|
-
|
|
403
|
-
this.client.enqueue({
|
|
404
|
-
...this.base(),
|
|
405
|
-
step_id: stepId,
|
|
406
|
-
event_type: 'agent.step.started',
|
|
407
|
-
step_type: opts.stepType ?? 'llm',
|
|
408
|
-
step_name: opts.name,
|
|
409
|
-
sequence: opts.sequence ?? sequence,
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
return new StepHandle(this.client, {
|
|
413
|
-
stepId,
|
|
414
|
-
runId: this.runId,
|
|
415
|
-
sessionId: this.sessionId,
|
|
416
|
-
agentId: this.agentId,
|
|
417
|
-
orgId: this.orgId,
|
|
418
|
-
traceId: this.traceId,
|
|
419
|
-
spanId: this.spanId,
|
|
420
|
-
sequence: opts.sequence ?? sequence,
|
|
421
|
-
name: opts.name,
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/** Record user feedback for this run */
|
|
426
|
-
recordFeedback(opts: RecordFeedbackOptions): void {
|
|
427
|
-
this.client.enqueue({
|
|
428
|
-
...this.base(),
|
|
429
|
-
event_type: 'feedback.received',
|
|
430
|
-
type: opts.type,
|
|
431
|
-
score: opts.score,
|
|
432
|
-
comment: opts.comment,
|
|
433
|
-
end_user_id: opts.endUserId,
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/** Record an automated eval score for this run */
|
|
438
|
-
recordEvalScore(opts: RecordEvalScoreOptions): void {
|
|
439
|
-
this.client.enqueue({
|
|
440
|
-
...this.base(),
|
|
441
|
-
event_type: 'eval.score.recorded',
|
|
442
|
-
metric: opts.metric,
|
|
443
|
-
score: opts.score,
|
|
444
|
-
evaluator_id: opts.evaluatorId,
|
|
445
|
-
model_used_for_eval: opts.modelUsedForEval,
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
/** Emit an agent.handoff event from this run to another agent */
|
|
450
|
-
handoff(opts: HandoffOptions): void {
|
|
451
|
-
this.client.enqueue({
|
|
452
|
-
...this.base(),
|
|
453
|
-
event_type: 'agent.handoff',
|
|
454
|
-
to_agent_id: opts.toAgentId,
|
|
455
|
-
reason: opts.reason,
|
|
456
|
-
context_tokens_transferred: opts.contextTokensTransferred,
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
async end(status: 'completed' | 'failed' = 'completed'): Promise<void> {
|
|
461
|
-
if (this.ended) return;
|
|
462
|
-
this.ended = true;
|
|
463
|
-
const eventType =
|
|
464
|
-
status === 'completed' ? 'agent.run.completed' : 'agent.run.failed';
|
|
465
|
-
this.client.enqueue({ ...this.base(), event_type: eventType });
|
|
466
|
-
await this.client.flush();
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// ---------------------------------------------------------------------------
|
|
471
|
-
// SensuClient
|
|
472
|
-
// ---------------------------------------------------------------------------
|
|
473
|
-
|
|
474
|
-
export class SensuClient {
|
|
475
|
-
private readonly apiKey: string;
|
|
476
|
-
private readonly baseUrl: string;
|
|
477
|
-
private readonly agentId: string;
|
|
478
|
-
private readonly orgId: string;
|
|
479
|
-
private readonly batchSize: number;
|
|
480
|
-
private readonly flushIntervalMs: number;
|
|
481
|
-
readonly disabled: boolean;
|
|
482
|
-
|
|
483
|
-
private buffer: TelemetryEvent[] = [];
|
|
484
|
-
private flushTimer: ReturnType<typeof setInterval> | null = null;
|
|
485
|
-
|
|
486
|
-
constructor(opts: SensuClientOptions = {}) {
|
|
487
|
-
const fromEnv = opts.fromEnv ?? false;
|
|
488
|
-
|
|
489
|
-
this.apiKey =
|
|
490
|
-
opts.apiKey ??
|
|
491
|
-
(fromEnv ? (process.env.SENSU_API_KEY ?? '') : '');
|
|
492
|
-
this.baseUrl =
|
|
493
|
-
opts.baseUrl ??
|
|
494
|
-
(fromEnv
|
|
495
|
-
? (process.env.SENSU_BASE_URL ?? 'http://localhost:3001')
|
|
496
|
-
: 'http://localhost:3001');
|
|
497
|
-
this.agentId =
|
|
498
|
-
opts.agentId ??
|
|
499
|
-
(fromEnv ? (process.env.SENSU_AGENT_ID ?? 'unknown-agent') : 'unknown-agent');
|
|
500
|
-
this.orgId =
|
|
501
|
-
opts.orgId ??
|
|
502
|
-
(fromEnv ? (process.env.SENSU_ORG_ID ?? '') : '');
|
|
503
|
-
|
|
504
|
-
this.batchSize = opts.batchSize ?? 10;
|
|
505
|
-
this.flushIntervalMs = opts.flushIntervalMs ?? 2000;
|
|
506
|
-
this.disabled = opts.disabled ?? false;
|
|
507
|
-
|
|
508
|
-
if (!this.disabled) {
|
|
509
|
-
this.flushTimer = setInterval(() => {
|
|
510
|
-
void this.flush();
|
|
511
|
-
}, this.flushIntervalMs);
|
|
512
|
-
if (this.flushTimer.unref) this.flushTimer.unref();
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/** Enqueue an event for batched sending */
|
|
517
|
-
enqueue(event: TelemetryEvent): void {
|
|
518
|
-
if (this.disabled) return;
|
|
519
|
-
this.buffer.push(event);
|
|
520
|
-
if (this.buffer.length >= this.batchSize) {
|
|
521
|
-
void this.flush();
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/** Flush all buffered events to the Sensu API */
|
|
526
|
-
async flush(): Promise<void> {
|
|
527
|
-
if (this.disabled || this.buffer.length === 0) return;
|
|
528
|
-
const events = this.buffer.splice(0);
|
|
529
|
-
try {
|
|
530
|
-
const res = await fetch(`${this.baseUrl}/api/v1/events`, {
|
|
531
|
-
method: 'POST',
|
|
532
|
-
headers: {
|
|
533
|
-
'Content-Type': 'application/json',
|
|
534
|
-
'X-API-Key': this.apiKey,
|
|
535
|
-
},
|
|
536
|
-
body: JSON.stringify({ events }),
|
|
537
|
-
});
|
|
538
|
-
if (!res.ok) {
|
|
539
|
-
const body = await res.text();
|
|
540
|
-
console.error(`[sensu:sdk] flush failed ${res.status}: ${body}`);
|
|
541
|
-
}
|
|
542
|
-
} catch (err) {
|
|
543
|
-
// Re-queue on network error (best-effort)
|
|
544
|
-
console.error('[sensu:sdk] flush network error:', err);
|
|
545
|
-
this.buffer.unshift(...events);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
/** Start a new agent run */
|
|
550
|
-
startRun(opts: StartRunOptions = {}): RunHandle {
|
|
551
|
-
const runId = opts.runId ?? randomUUID();
|
|
552
|
-
const sessionId = opts.sessionId ?? randomUUID();
|
|
553
|
-
const traceId = randomUUID();
|
|
554
|
-
const spanId = randomUUID();
|
|
555
|
-
|
|
556
|
-
this.enqueue({
|
|
557
|
-
event_id: randomUUID(),
|
|
558
|
-
event_type: 'agent.run.started',
|
|
559
|
-
timestamp: new Date().toISOString(),
|
|
560
|
-
org_id: this.orgId,
|
|
561
|
-
agent_id: this.agentId,
|
|
562
|
-
session_id: sessionId,
|
|
563
|
-
run_id: runId,
|
|
564
|
-
trace_id: traceId,
|
|
565
|
-
span_id: spanId,
|
|
566
|
-
run_type: opts.runType,
|
|
567
|
-
end_user_id: opts.endUserId,
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
return new RunHandle(this, {
|
|
571
|
-
runId,
|
|
572
|
-
sessionId,
|
|
573
|
-
agentId: this.agentId,
|
|
574
|
-
orgId: this.orgId,
|
|
575
|
-
traceId,
|
|
576
|
-
spanId,
|
|
577
|
-
});
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
/**
|
|
581
|
-
* Spawn a child agent run from within a parent run.
|
|
582
|
-
* Emits `agent.spawned` on the parent and returns a RunHandle for the child.
|
|
583
|
-
*/
|
|
584
|
-
spawnRun(parentRun: RunHandle, opts: SpawnRunOptions): RunHandle {
|
|
585
|
-
const childRunId = opts.childRunId ?? randomUUID();
|
|
586
|
-
const childAgentId = opts.childAgentId;
|
|
587
|
-
const sessionId = opts.sessionId ?? parentRun.sessionId;
|
|
588
|
-
const traceId = parentRun.traceId;
|
|
589
|
-
const spanId = randomUUID();
|
|
590
|
-
|
|
591
|
-
// Emit agent.spawned on the parent run
|
|
592
|
-
this.enqueue({
|
|
593
|
-
event_id: randomUUID(),
|
|
594
|
-
event_type: 'agent.spawned',
|
|
595
|
-
timestamp: new Date().toISOString(),
|
|
596
|
-
org_id: this.orgId,
|
|
597
|
-
agent_id: parentRun.agentId,
|
|
598
|
-
session_id: sessionId,
|
|
599
|
-
run_id: parentRun.runId,
|
|
600
|
-
trace_id: traceId,
|
|
601
|
-
span_id: spanId,
|
|
602
|
-
child_run_id: childRunId,
|
|
603
|
-
child_agent_id: childAgentId,
|
|
604
|
-
spawn_reason: opts.spawnReason,
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
// Emit agent.run.started for the child run (child agent emits this itself in practice,
|
|
608
|
-
// but the SDK can also do it on behalf of known child agents)
|
|
609
|
-
this.enqueue({
|
|
610
|
-
event_id: randomUUID(),
|
|
611
|
-
event_type: 'agent.run.started',
|
|
612
|
-
timestamp: new Date().toISOString(),
|
|
613
|
-
org_id: this.orgId,
|
|
614
|
-
agent_id: childAgentId,
|
|
615
|
-
session_id: sessionId,
|
|
616
|
-
run_id: childRunId,
|
|
617
|
-
trace_id: traceId,
|
|
618
|
-
span_id: randomUUID(),
|
|
619
|
-
run_type: opts.runType,
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
return new RunHandle(this, {
|
|
623
|
-
runId: childRunId,
|
|
624
|
-
sessionId,
|
|
625
|
-
agentId: childAgentId,
|
|
626
|
-
orgId: this.orgId,
|
|
627
|
-
traceId,
|
|
628
|
-
spanId,
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
/** Explicitly start a session (sets channel and end_user_id) */
|
|
633
|
-
startSession(opts: StartSessionOptions = {}): string {
|
|
634
|
-
const sessionId = opts.sessionId ?? randomUUID();
|
|
635
|
-
const traceId = randomUUID();
|
|
636
|
-
const spanId = randomUUID();
|
|
637
|
-
|
|
638
|
-
this.enqueue({
|
|
639
|
-
event_id: randomUUID(),
|
|
640
|
-
event_type: 'session.started',
|
|
641
|
-
timestamp: new Date().toISOString(),
|
|
642
|
-
org_id: this.orgId,
|
|
643
|
-
agent_id: this.agentId,
|
|
644
|
-
session_id: sessionId,
|
|
645
|
-
run_id: sessionId, // run_id required by base schema; reuse session_id as placeholder
|
|
646
|
-
trace_id: traceId,
|
|
647
|
-
span_id: spanId,
|
|
648
|
-
channel: opts.channel,
|
|
649
|
-
end_user_id: opts.endUserId,
|
|
650
|
-
});
|
|
651
|
-
|
|
652
|
-
return sessionId;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
/** Resume a previous session */
|
|
656
|
-
resumeSession(opts: ResumeSessionOptions): string {
|
|
657
|
-
const sessionId = opts.sessionId ?? randomUUID();
|
|
658
|
-
const traceId = randomUUID();
|
|
659
|
-
const spanId = randomUUID();
|
|
660
|
-
|
|
661
|
-
this.enqueue({
|
|
662
|
-
event_id: randomUUID(),
|
|
663
|
-
event_type: 'session.resumed',
|
|
664
|
-
timestamp: new Date().toISOString(),
|
|
665
|
-
org_id: this.orgId,
|
|
666
|
-
agent_id: this.agentId,
|
|
667
|
-
session_id: sessionId,
|
|
668
|
-
run_id: sessionId,
|
|
669
|
-
trace_id: traceId,
|
|
670
|
-
span_id: spanId,
|
|
671
|
-
resumed_from_session_id: opts.resumedFromSessionId,
|
|
672
|
-
channel: opts.channel,
|
|
673
|
-
end_user_id: opts.endUserId,
|
|
674
|
-
});
|
|
675
|
-
|
|
676
|
-
return sessionId;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
/** Record a prompt version deployment (org-level event, not tied to a run) */
|
|
680
|
-
deployPromptVersion(opts: DeployPromptVersionOptions): void {
|
|
681
|
-
this.enqueue({
|
|
682
|
-
event_id: randomUUID(),
|
|
683
|
-
event_type: 'prompt.version.deployed',
|
|
684
|
-
timestamp: new Date().toISOString(),
|
|
685
|
-
org_id: this.orgId,
|
|
686
|
-
agent_id: this.agentId,
|
|
687
|
-
session_id: 'system',
|
|
688
|
-
run_id: 'system',
|
|
689
|
-
trace_id: randomUUID(),
|
|
690
|
-
span_id: randomUUID(),
|
|
691
|
-
template_id: opts.templateId,
|
|
692
|
-
new_version: opts.newVersion,
|
|
693
|
-
old_version: opts.oldVersion,
|
|
694
|
-
deployed_by: opts.deployedBy,
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
destroy(): void {
|
|
699
|
-
if (this.flushTimer) clearInterval(this.flushTimer);
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// ---------------------------------------------------------------------------
|
|
704
|
-
// Helpers
|
|
705
|
-
// ---------------------------------------------------------------------------
|
|
706
|
-
|
|
707
|
-
// Pricing per 1M tokens [input, output] in USD
|
|
708
|
-
const MODEL_PRICING: Record<string, [number, number]> = {
|
|
709
|
-
'claude-opus-4-6': [15.00, 75.00],
|
|
710
|
-
'claude-sonnet-4-6': [ 3.00, 15.00],
|
|
711
|
-
'claude-haiku-4-5-20251001': [ 0.80, 4.00],
|
|
712
|
-
'claude-3-5-sonnet-20241022': [ 3.00, 15.00],
|
|
713
|
-
'claude-3-5-haiku-20241022': [ 0.80, 4.00],
|
|
714
|
-
'claude-3-opus-20240229': [15.00, 75.00],
|
|
715
|
-
'gpt-4o': [ 5.00, 15.00],
|
|
716
|
-
'gpt-4o-mini': [ 0.15, 0.60],
|
|
717
|
-
'gpt-4-turbo': [10.00, 30.00],
|
|
718
|
-
};
|
|
719
|
-
|
|
720
|
-
function estimateCost(model: string, inputTokens: number, outputTokens: number): number {
|
|
721
|
-
const pricing = MODEL_PRICING[model];
|
|
722
|
-
if (!pricing) return 0;
|
|
723
|
-
const [inputPrice, outputPrice] = pricing;
|
|
724
|
-
return (inputTokens / 1_000_000) * inputPrice + (outputTokens / 1_000_000) * outputPrice;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
function extractUsage(result: unknown, model: string): Record<string, number | undefined> {
|
|
728
|
-
if (!result || typeof result !== 'object') return {};
|
|
729
|
-
const r = result as Record<string, unknown>;
|
|
730
|
-
|
|
731
|
-
// Anthropic shape: { usage: { input_tokens, output_tokens, cache_read_input_tokens } }
|
|
732
|
-
if (r['usage'] && typeof r['usage'] === 'object') {
|
|
733
|
-
const u = r['usage'] as Record<string, unknown>;
|
|
734
|
-
const inputTokens = num(u['input_tokens']) ?? 0;
|
|
735
|
-
const outputTokens = num(u['output_tokens']) ?? 0;
|
|
736
|
-
return {
|
|
737
|
-
input_tokens: inputTokens,
|
|
738
|
-
output_tokens: outputTokens,
|
|
739
|
-
cached_input_tokens: num(u['cache_read_input_tokens']),
|
|
740
|
-
total_tokens: inputTokens + outputTokens,
|
|
741
|
-
cost_usd_estimate: estimateCost(model, inputTokens, outputTokens),
|
|
742
|
-
};
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
// OpenAI shape: { choices: [...], usage: { prompt_tokens, completion_tokens, total_tokens } }
|
|
746
|
-
if (r['choices'] && r['usage'] && typeof r['usage'] === 'object') {
|
|
747
|
-
const u = r['usage'] as Record<string, unknown>;
|
|
748
|
-
const inputTokens = num(u['prompt_tokens']) ?? 0;
|
|
749
|
-
const outputTokens = num(u['completion_tokens']) ?? 0;
|
|
750
|
-
return {
|
|
751
|
-
input_tokens: inputTokens,
|
|
752
|
-
output_tokens: outputTokens,
|
|
753
|
-
total_tokens: num(u['total_tokens']),
|
|
754
|
-
cost_usd_estimate: estimateCost(model, inputTokens, outputTokens),
|
|
755
|
-
};
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
return {};
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
function num(v: unknown): number | undefined {
|
|
762
|
-
return typeof v === 'number' ? v : undefined;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
function estimateBytes(v: unknown): number {
|
|
766
|
-
try {
|
|
767
|
-
return JSON.stringify(v)?.length ?? 0;
|
|
768
|
-
} catch {
|
|
769
|
-
return 0;
|
|
770
|
-
}
|
|
771
|
-
}
|