aigie-sdk 0.0.1

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.
@@ -0,0 +1,646 @@
1
+ import { AsyncLocalStorage } from 'async_hooks';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __esm = (fn, res) => function __init() {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+
13
+ // src/utils/uuid.ts
14
+ function uuidv7() {
15
+ let timestamp = Date.now();
16
+ if (timestamp === lastTimestamp) {
17
+ sequence++;
18
+ if (sequence > 4095) {
19
+ timestamp = Date.now();
20
+ sequence = 0;
21
+ }
22
+ } else {
23
+ sequence = 0;
24
+ lastTimestamp = timestamp;
25
+ }
26
+ const timestampHex = timestamp.toString(16).padStart(12, "0");
27
+ const seqHex = (7 << 12 | sequence & 4095).toString(16).padStart(4, "0");
28
+ const randomBits = new Uint8Array(8);
29
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
30
+ crypto.getRandomValues(randomBits);
31
+ } else {
32
+ for (let i = 0; i < 8; i++) {
33
+ randomBits[i] = Math.floor(Math.random() * 256);
34
+ }
35
+ }
36
+ randomBits[0] = randomBits[0] & 63 | 128;
37
+ const randomHex = Array.from(randomBits).map((b) => b.toString(16).padStart(2, "0")).join("");
38
+ return [
39
+ timestampHex.slice(0, 8),
40
+ timestampHex.slice(8, 12),
41
+ seqHex,
42
+ randomHex.slice(0, 4),
43
+ randomHex.slice(4, 16)
44
+ ].join("-");
45
+ }
46
+ var lastTimestamp, sequence;
47
+ var init_uuid = __esm({
48
+ "src/utils/uuid.ts"() {
49
+ lastTimestamp = 0;
50
+ sequence = 0;
51
+ }
52
+ });
53
+
54
+ // src/client.ts
55
+ var client_exports = {};
56
+ __export(client_exports, {
57
+ AigieClient: () => AigieClient,
58
+ KyteClient: () => AigieClient,
59
+ getAigie: () => getAigie,
60
+ getKyte: () => getKyte,
61
+ initAigie: () => initAigie,
62
+ initKyte: () => initKyte
63
+ });
64
+ function initKyte(config) {
65
+ globalClient = new AigieClient(config);
66
+ return globalClient;
67
+ }
68
+ function getKyte() {
69
+ if (!globalClient) {
70
+ throw new Error("Kyte SDK not initialized. Call initKyte() first.");
71
+ }
72
+ return globalClient;
73
+ }
74
+ var contextStorage, globalClient, initAigie, getAigie, SpanBuilderImpl, ActiveSpanImpl, AigieClient;
75
+ var init_client = __esm({
76
+ "src/client.ts"() {
77
+ init_uuid();
78
+ contextStorage = new AsyncLocalStorage();
79
+ globalClient = null;
80
+ initAigie = initKyte;
81
+ getAigie = getKyte;
82
+ SpanBuilderImpl = class {
83
+ client;
84
+ options = {};
85
+ constructor(client, name) {
86
+ this.client = client;
87
+ this.options.name = name;
88
+ }
89
+ setName(name) {
90
+ this.options.name = name;
91
+ return this;
92
+ }
93
+ setType(type) {
94
+ this.options.type = type;
95
+ return this;
96
+ }
97
+ setInput(input) {
98
+ this.options.input = input;
99
+ return this;
100
+ }
101
+ setMetadata(metadata) {
102
+ this.options.metadata = metadata;
103
+ return this;
104
+ }
105
+ addMetadata(key, value) {
106
+ this.options.metadata = { ...this.options.metadata, [key]: value };
107
+ return this;
108
+ }
109
+ setTags(tags) {
110
+ this.options.tags = tags;
111
+ return this;
112
+ }
113
+ addTag(tag) {
114
+ this.options.tags = [...this.options.tags || [], tag];
115
+ return this;
116
+ }
117
+ setUserId(userId) {
118
+ this.options.userId = userId;
119
+ return this;
120
+ }
121
+ setSessionId(sessionId) {
122
+ this.options.sessionId = sessionId;
123
+ return this;
124
+ }
125
+ setModelName(modelName) {
126
+ this.options.modelName = modelName;
127
+ return this;
128
+ }
129
+ start() {
130
+ return this.client.startSpan(this.options);
131
+ }
132
+ };
133
+ ActiveSpanImpl = class {
134
+ id;
135
+ traceId;
136
+ client;
137
+ span;
138
+ scores = [];
139
+ ended = false;
140
+ constructor(client, span) {
141
+ this.client = client;
142
+ this.span = span;
143
+ this.id = span.id;
144
+ this.traceId = span.traceId;
145
+ }
146
+ update(data) {
147
+ if (this.ended) {
148
+ console.warn("Cannot update ended span");
149
+ return this;
150
+ }
151
+ if (data.output) this.span.output = data.output;
152
+ if (data.tokens) this.span.tokens = data.tokens;
153
+ if (data.cost) this.span.cost = data.cost;
154
+ if (data.metadata) this.span.metadata = { ...this.span.metadata, ...data.metadata };
155
+ if (data.tags) this.span.tags = [...this.span.tags, ...data.tags];
156
+ if (data.modelName) this.span.modelName = data.modelName;
157
+ return this;
158
+ }
159
+ setOutput(output) {
160
+ return this.update({ output });
161
+ }
162
+ setTokens(tokens) {
163
+ return this.update({ tokens });
164
+ }
165
+ setCost(cost) {
166
+ return this.update({ cost });
167
+ }
168
+ addScore(score) {
169
+ this.scores.push(score);
170
+ return this;
171
+ }
172
+ setError(error) {
173
+ if (this.ended) {
174
+ console.warn("Cannot set error on ended span");
175
+ return this;
176
+ }
177
+ this.span.status = "failed";
178
+ this.span.errorMessage = typeof error === "string" ? error : error.message;
179
+ return this;
180
+ }
181
+ async end() {
182
+ if (this.ended) {
183
+ console.warn("Span already ended");
184
+ return;
185
+ }
186
+ this.ended = true;
187
+ this.span.endTime = (/* @__PURE__ */ new Date()).toISOString();
188
+ this.span.durationNs = (new Date(this.span.endTime).getTime() - new Date(this.span.startTime).getTime()) * 1e6;
189
+ if (this.span.status === "pending") {
190
+ this.span.status = "success";
191
+ }
192
+ await this.client.sendSpanUpdate(this.span);
193
+ for (const score of this.scores) {
194
+ await this.client.addScore(this.traceId, score, this.id);
195
+ }
196
+ }
197
+ };
198
+ AigieClient = class {
199
+ config;
200
+ eventQueue = [];
201
+ batchTimer = null;
202
+ enabled;
203
+ constructor(config) {
204
+ const rawUrl = config.apiUrl || process.env.KYTTE_URL || process.env.AIGIE_URL || process.env.AIGIE_API_URL || "";
205
+ const apiUrl = rawUrl.replace(/\/v1?\/?$/, "");
206
+ const apiKey = config.apiKey || process.env.KYTTE_TOKEN || process.env.AIGIE_TOKEN || process.env.AIGIE_API_KEY || "";
207
+ this.config = {
208
+ batchEnabled: true,
209
+ batchSize: 10,
210
+ batchTimeout: 5e3,
211
+ maxQueueSize: 1e3,
212
+ maxRetries: 1,
213
+ retryDelay: 2e3,
214
+ enabled: true,
215
+ ...config,
216
+ apiUrl,
217
+ apiKey
218
+ };
219
+ this.enabled = this.config.enabled ?? true;
220
+ }
221
+ // ============================================================================
222
+ // Enable/Disable
223
+ // ============================================================================
224
+ isEnabled() {
225
+ return this.enabled;
226
+ }
227
+ disable() {
228
+ this.enabled = false;
229
+ }
230
+ enable() {
231
+ this.enabled = true;
232
+ }
233
+ // ============================================================================
234
+ // Context Management
235
+ // ============================================================================
236
+ getCurrentContext() {
237
+ return contextStorage.getStore();
238
+ }
239
+ // ============================================================================
240
+ // Fluent Builder API
241
+ // ============================================================================
242
+ spanBuilder(name) {
243
+ return new SpanBuilderImpl(this, name);
244
+ }
245
+ startSpan(options) {
246
+ const context = this.getCurrentContext();
247
+ const spanId = uuidv7();
248
+ const traceId = context?.traceId || uuidv7();
249
+ const span = {
250
+ id: spanId,
251
+ traceId,
252
+ parentSpanId: context?.spanId || options.parentSpanId,
253
+ name: options.name || "unnamed-span",
254
+ type: options.type || "custom",
255
+ input: options.input || {},
256
+ status: "pending",
257
+ tags: [...this.config.defaultTags || [], ...options.tags || []],
258
+ metadata: { ...this.config.defaultMetadata, ...options.metadata },
259
+ modelName: options.modelName,
260
+ startTime: (/* @__PURE__ */ new Date()).toISOString(),
261
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
262
+ };
263
+ this.queueEvent(this.spanToEvent(span));
264
+ return new ActiveSpanImpl(this, span);
265
+ }
266
+ // ============================================================================
267
+ // Trace API
268
+ // ============================================================================
269
+ async trace(name, fn, options = {}) {
270
+ if (!this.enabled) {
271
+ return fn();
272
+ }
273
+ const traceId = uuidv7();
274
+ const trace = {
275
+ id: traceId,
276
+ name,
277
+ type: options.type || "chain",
278
+ input: options.input || {},
279
+ status: "pending",
280
+ tags: [...this.config.defaultTags || [], ...options.tags || []],
281
+ metadata: { ...this.config.defaultMetadata, ...options.metadata },
282
+ projectName: this.config.projectName,
283
+ userId: options.userId || this.config.defaultUserId,
284
+ sessionId: options.sessionId || this.config.defaultSessionId,
285
+ startTime: (/* @__PURE__ */ new Date()).toISOString(),
286
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
287
+ };
288
+ const context = { traceId };
289
+ this.queueEvent(this.traceToEvent(trace));
290
+ try {
291
+ const result = await contextStorage.run(context, async () => {
292
+ return await fn();
293
+ });
294
+ trace.status = "success";
295
+ trace.output = { result: typeof result === "object" ? result : { value: result } };
296
+ trace.endTime = (/* @__PURE__ */ new Date()).toISOString();
297
+ trace.durationNs = (new Date(trace.endTime).getTime() - new Date(trace.startTime).getTime()) * 1e6;
298
+ await this.sendTraceUpdate(trace);
299
+ return result;
300
+ } catch (error) {
301
+ trace.status = "failed";
302
+ trace.errorMessage = error instanceof Error ? error.message : String(error);
303
+ trace.endTime = (/* @__PURE__ */ new Date()).toISOString();
304
+ trace.durationNs = (new Date(trace.endTime).getTime() - new Date(trace.startTime).getTime()) * 1e6;
305
+ await this.sendTraceUpdate(trace);
306
+ throw error;
307
+ }
308
+ }
309
+ // ============================================================================
310
+ // Span API
311
+ // ============================================================================
312
+ async span(name, fn, options = {}) {
313
+ if (!this.enabled) {
314
+ return fn();
315
+ }
316
+ const context = this.getCurrentContext();
317
+ if (!context) {
318
+ return this.trace(name, fn, options);
319
+ }
320
+ const spanId = uuidv7();
321
+ const span = {
322
+ id: spanId,
323
+ traceId: context.traceId,
324
+ parentSpanId: context.spanId || options.parentSpanId,
325
+ name,
326
+ type: options.type || "custom",
327
+ input: options.input || {},
328
+ status: "pending",
329
+ tags: [...this.config.defaultTags || [], ...options.tags || []],
330
+ metadata: { ...this.config.defaultMetadata, ...options.metadata },
331
+ modelName: options.modelName,
332
+ startTime: (/* @__PURE__ */ new Date()).toISOString(),
333
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
334
+ };
335
+ const newContext = {
336
+ traceId: context.traceId,
337
+ spanId,
338
+ parentSpanId: context.spanId
339
+ };
340
+ this.queueEvent(this.spanToEvent(span));
341
+ try {
342
+ const result = await contextStorage.run(newContext, async () => {
343
+ return await fn();
344
+ });
345
+ span.status = "success";
346
+ span.output = { result: typeof result === "object" ? result : { value: result } };
347
+ span.endTime = (/* @__PURE__ */ new Date()).toISOString();
348
+ span.durationNs = (new Date(span.endTime).getTime() - new Date(span.startTime).getTime()) * 1e6;
349
+ await this.sendSpanUpdate(span);
350
+ return result;
351
+ } catch (error) {
352
+ span.status = "failed";
353
+ span.errorMessage = error instanceof Error ? error.message : String(error);
354
+ span.endTime = (/* @__PURE__ */ new Date()).toISOString();
355
+ span.durationNs = (new Date(span.endTime).getTime() - new Date(span.startTime).getTime()) * 1e6;
356
+ await this.sendSpanUpdate(span);
357
+ throw error;
358
+ }
359
+ }
360
+ // ============================================================================
361
+ // Score API
362
+ // ============================================================================
363
+ async addScore(traceId, score, spanId) {
364
+ if (!this.enabled) return;
365
+ try {
366
+ await this.sendToApi("/v1/scores", {
367
+ traceId,
368
+ spanId,
369
+ ...score
370
+ });
371
+ } catch (error) {
372
+ if (this.config.debug) {
373
+ console.error("Failed to add score:", error);
374
+ }
375
+ }
376
+ }
377
+ // ============================================================================
378
+ // Send APIs
379
+ // ============================================================================
380
+ async sendTraceUpdate(trace) {
381
+ if (!this.enabled) return;
382
+ const event = this.traceUpdateEvent(trace);
383
+ if (this.config.batchEnabled) {
384
+ this.queueEvent(event);
385
+ } else {
386
+ await this.sendToApi("/v1/ingestion", { batch: [event] });
387
+ }
388
+ }
389
+ async sendSpanUpdate(span) {
390
+ if (!this.enabled) return;
391
+ const event = this.spanUpdateEvent(span);
392
+ if (this.config.batchEnabled) {
393
+ this.queueEvent(event);
394
+ } else {
395
+ await this.sendToApi("/v1/ingestion", { batch: [event] });
396
+ }
397
+ }
398
+ // ============================================================================
399
+ // Batching
400
+ // ============================================================================
401
+ queueEvent(event) {
402
+ const maxSize = this.config.maxQueueSize || 1e3;
403
+ if (this.eventQueue.length >= maxSize) {
404
+ this.eventQueue.splice(0, this.eventQueue.length - maxSize + 1);
405
+ if (this.config.debug) {
406
+ console.warn(`Event queue exceeded max size (${maxSize}), dropping oldest events`);
407
+ }
408
+ }
409
+ this.eventQueue.push(event);
410
+ if (this.eventQueue.length >= (this.config.batchSize || 10)) {
411
+ this.flushBatch();
412
+ } else if (!this.batchTimer) {
413
+ this.batchTimer = setTimeout(() => {
414
+ this.flushBatch();
415
+ }, this.config.batchTimeout || 5e3);
416
+ }
417
+ }
418
+ mapSpanType(type) {
419
+ const typeMap = {
420
+ llm: "generation",
421
+ custom: "span",
422
+ chain: "span",
423
+ tool: "tool",
424
+ retrieval: "retrieval",
425
+ retriever: "retrieval",
426
+ agent: "agent",
427
+ embedding: "generation",
428
+ prompt: "span",
429
+ guardrail: "span",
430
+ evaluation: "span",
431
+ remediation: "span"
432
+ };
433
+ return typeMap[type] || "span";
434
+ }
435
+ traceToEvent(trace) {
436
+ return {
437
+ id: uuidv7(),
438
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
439
+ type: "trace-create",
440
+ body: {
441
+ id: trace.id,
442
+ name: trace.name,
443
+ input: trace.input,
444
+ output: trace.output,
445
+ status: trace.status,
446
+ status_message: trace.errorMessage,
447
+ metadata: trace.metadata,
448
+ tags: trace.tags,
449
+ user_id: trace.userId,
450
+ session_id: trace.sessionId,
451
+ environment: this.config.environment,
452
+ start_time: trace.startTime,
453
+ end_time: trace.endTime,
454
+ duration_ns: trace.durationNs
455
+ }
456
+ };
457
+ }
458
+ buildTokenUsage(tokens) {
459
+ if (!tokens) return void 0;
460
+ return {
461
+ prompt_tokens: tokens.prompt,
462
+ completion_tokens: tokens.completion,
463
+ total_tokens: tokens.total,
464
+ cached_tokens: tokens.cached
465
+ };
466
+ }
467
+ spanToEvent(span) {
468
+ return {
469
+ id: uuidv7(),
470
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
471
+ type: "span-create",
472
+ body: {
473
+ id: span.id,
474
+ trace_id: span.traceId,
475
+ parent_id: span.parentSpanId,
476
+ name: span.name,
477
+ type: this.mapSpanType(span.type),
478
+ start_time: span.startTime,
479
+ input: span.input,
480
+ model: span.modelName,
481
+ metadata: span.metadata,
482
+ tags: span.tags,
483
+ status: span.status
484
+ }
485
+ };
486
+ }
487
+ traceUpdateEvent(trace) {
488
+ return {
489
+ id: uuidv7(),
490
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
491
+ type: "trace-update",
492
+ body: {
493
+ id: trace.id,
494
+ output: trace.output,
495
+ status: trace.status,
496
+ status_message: trace.errorMessage,
497
+ end_time: trace.endTime,
498
+ duration_ns: trace.durationNs
499
+ }
500
+ };
501
+ }
502
+ spanUpdateEvent(span) {
503
+ return {
504
+ id: uuidv7(),
505
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
506
+ type: "span-update",
507
+ body: {
508
+ id: span.id,
509
+ trace_id: span.traceId,
510
+ output: span.output,
511
+ status: span.status,
512
+ status_message: span.errorMessage,
513
+ end_time: span.endTime,
514
+ duration_ns: span.durationNs,
515
+ token_usage: this.buildTokenUsage(span.tokens),
516
+ cost: span.cost
517
+ }
518
+ };
519
+ }
520
+ async flushBatch() {
521
+ if (this.batchTimer) {
522
+ clearTimeout(this.batchTimer);
523
+ this.batchTimer = null;
524
+ }
525
+ if (this.eventQueue.length === 0) return;
526
+ const events = [...this.eventQueue];
527
+ this.eventQueue = [];
528
+ const typePriority = {
529
+ "trace-create": 0,
530
+ "span-create": 1,
531
+ "span-update": 2,
532
+ "trace-update": 3
533
+ };
534
+ events.sort((a, b) => (typePriority[a.type] ?? 99) - (typePriority[b.type] ?? 99));
535
+ const maxRetries = this.config.maxRetries ?? 1;
536
+ const retryDelay = this.config.retryDelay ?? 2e3;
537
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
538
+ try {
539
+ await this.sendToApi("/v1/ingestion", { batch: events });
540
+ return;
541
+ } catch (error) {
542
+ if (attempt < maxRetries) {
543
+ const delay = retryDelay * 2 ** attempt;
544
+ await new Promise((resolve) => setTimeout(resolve, delay));
545
+ } else {
546
+ if (this.config.onError) {
547
+ this.config.onError(error instanceof Error ? error : new Error(String(error)));
548
+ }
549
+ if (this.config.debug) {
550
+ console.error("Failed to send batch after retries:", error);
551
+ }
552
+ }
553
+ }
554
+ }
555
+ }
556
+ // ============================================================================
557
+ // API Communication
558
+ // ============================================================================
559
+ async sendToApi(endpoint, data) {
560
+ const response = await fetch(`${this.config.apiUrl}${endpoint}`, {
561
+ method: "POST",
562
+ headers: {
563
+ "Content-Type": "application/json",
564
+ "X-API-Key": this.config.apiKey,
565
+ "X-Project-Name": this.config.projectName || ""
566
+ },
567
+ body: JSON.stringify(data)
568
+ });
569
+ if (!response.ok) {
570
+ throw new Error(`API error: ${response.status} ${response.statusText}`);
571
+ }
572
+ return response;
573
+ }
574
+ // ============================================================================
575
+ // Shutdown
576
+ // ============================================================================
577
+ /**
578
+ * Force flush all pending spans and signals immediately.
579
+ * Unlike shutdown(), this does not disable the client afterwards.
580
+ */
581
+ async forceFlush() {
582
+ await this.flushBatch();
583
+ }
584
+ async shutdown() {
585
+ await this.flushBatch();
586
+ this.disable();
587
+ }
588
+ };
589
+ }
590
+ });
591
+
592
+ // src/testing/jest.ts
593
+ function wrapJest(testFn) {
594
+ return (name, fn, timeout) => {
595
+ testFn(
596
+ name,
597
+ async () => {
598
+ let client;
599
+ try {
600
+ const { getKyte: getKyte2 } = await Promise.resolve().then(() => (init_client(), client_exports));
601
+ client = getKyte2();
602
+ } catch {
603
+ return fn();
604
+ }
605
+ return client.trace(
606
+ `test: ${name}`,
607
+ async () => {
608
+ return fn();
609
+ },
610
+ { type: "custom", metadata: { testFramework: "jest" } }
611
+ );
612
+ },
613
+ timeout
614
+ );
615
+ };
616
+ }
617
+
618
+ // src/testing/vitest.ts
619
+ function wrapVitest(testFn) {
620
+ return (name, fn, timeout) => {
621
+ testFn(
622
+ name,
623
+ async () => {
624
+ let client;
625
+ try {
626
+ const { getKyte: getKyte2 } = await Promise.resolve().then(() => (init_client(), client_exports));
627
+ client = getKyte2();
628
+ } catch {
629
+ return fn();
630
+ }
631
+ return client.trace(
632
+ `test: ${name}`,
633
+ async () => {
634
+ return fn();
635
+ },
636
+ { type: "custom", metadata: { testFramework: "vitest" } }
637
+ );
638
+ },
639
+ timeout
640
+ );
641
+ };
642
+ }
643
+
644
+ export { wrapJest, wrapVitest };
645
+ //# sourceMappingURL=index.mjs.map
646
+ //# sourceMappingURL=index.mjs.map