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