observa-sdk 0.0.6 → 0.0.7

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/index.js CHANGED
@@ -1,4 +1,12 @@
1
1
  // src/index.ts
2
+ var contextModule = null;
3
+ try {
4
+ const requireFn = globalThis.require;
5
+ if (typeof requireFn !== "undefined") {
6
+ contextModule = requireFn("./context.js");
7
+ }
8
+ } catch {
9
+ }
2
10
  function getNodeEnv() {
3
11
  try {
4
12
  const proc = globalThis.process;
@@ -221,6 +229,20 @@ var Observa = class {
221
229
  isProduction;
222
230
  sampleRate;
223
231
  maxResponseChars;
232
+ // Buffering and retry (now stores canonical events)
233
+ eventBuffer = [];
234
+ flushPromise = null;
235
+ flushInProgress = false;
236
+ maxBufferSize = 100;
237
+ flushIntervalMs = 5e3;
238
+ // Flush every 5 seconds
239
+ flushIntervalId = null;
240
+ // Span hierarchy tracking (for manual trace management)
241
+ currentTraceId = null;
242
+ rootSpanId = null;
243
+ spanStack = [];
244
+ // Stack for tracking parent-child relationships
245
+ traceStartTime = null;
224
246
  constructor(config) {
225
247
  this.apiKey = config.apiKey;
226
248
  let apiUrlEnv;
@@ -265,35 +287,465 @@ var Observa = class {
265
287
  `\u{1F517} [Observa] Auth: ${jwtContext ? "JWT (auto-extracted)" : "Legacy (config)"}`
266
288
  );
267
289
  }
290
+ try {
291
+ if (typeof setInterval !== "undefined") {
292
+ this.flushIntervalId = setInterval(() => {
293
+ this.flush().catch((err) => {
294
+ console.error("[Observa] Periodic flush failed:", err);
295
+ });
296
+ }, this.flushIntervalMs);
297
+ }
298
+ } catch {
299
+ }
300
+ }
301
+ /**
302
+ * Flush buffered events to the API
303
+ * Returns a promise that resolves when all events are sent
304
+ */
305
+ async flush() {
306
+ if (this.flushInProgress || this.eventBuffer.length === 0) {
307
+ return this.flushPromise || Promise.resolve();
308
+ }
309
+ this.flushInProgress = true;
310
+ this.flushPromise = this._doFlush();
311
+ try {
312
+ await this.flushPromise;
313
+ } finally {
314
+ this.flushInProgress = false;
315
+ this.flushPromise = null;
316
+ }
317
+ }
318
+ /**
319
+ * Helper: Create base event properties
320
+ */
321
+ createBaseEventProperties() {
322
+ const traceId = this.currentTraceId || crypto.randomUUID();
323
+ return {
324
+ tenant_id: this.tenantId,
325
+ project_id: this.projectId,
326
+ environment: this.environment,
327
+ trace_id: traceId
328
+ };
329
+ }
330
+ /**
331
+ * Helper: Add event to buffer with proper span hierarchy
332
+ */
333
+ addEvent(eventData) {
334
+ const baseProps = this.createBaseEventProperties();
335
+ const parentSpanId = this.spanStack.length > 0 ? this.spanStack[this.spanStack.length - 1] : null;
336
+ const event = {
337
+ ...baseProps,
338
+ span_id: eventData.span_id || crypto.randomUUID(),
339
+ parent_span_id: (eventData.parent_span_id !== void 0 ? eventData.parent_span_id : parentSpanId) ?? null,
340
+ timestamp: eventData.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
341
+ event_type: eventData.event_type,
342
+ conversation_id: eventData.conversation_id ?? null,
343
+ session_id: eventData.session_id ?? null,
344
+ user_id: eventData.user_id ?? null,
345
+ agent_name: eventData.agent_name ?? null,
346
+ version: eventData.version ?? null,
347
+ route: eventData.route ?? null,
348
+ attributes: eventData.attributes
349
+ };
350
+ this.eventBuffer.push(event);
351
+ if (this.eventBuffer.length >= this.maxBufferSize) {
352
+ this.flush().catch((err) => {
353
+ console.error("[Observa] Auto-flush failed:", err);
354
+ });
355
+ }
356
+ }
357
+ /**
358
+ * Start a new trace (manual trace management)
359
+ */
360
+ startTrace(options = {}) {
361
+ if (this.currentTraceId) {
362
+ console.warn("[Observa] Ending previous trace before starting new one");
363
+ this.endTrace().catch(console.error);
364
+ }
365
+ this.currentTraceId = crypto.randomUUID();
366
+ this.rootSpanId = crypto.randomUUID();
367
+ this.spanStack = [this.rootSpanId];
368
+ this.traceStartTime = Date.now();
369
+ this.addEvent({
370
+ event_type: "trace_start",
371
+ span_id: this.rootSpanId,
372
+ parent_span_id: null,
373
+ conversation_id: options.conversationId || null,
374
+ session_id: options.sessionId || null,
375
+ user_id: options.userId || null,
376
+ attributes: {
377
+ trace_start: {
378
+ name: options.name || null,
379
+ metadata: options.metadata || null
380
+ }
381
+ }
382
+ });
383
+ return this.currentTraceId;
384
+ }
385
+ /**
386
+ * Track a tool call
387
+ */
388
+ trackToolCall(options) {
389
+ const spanId = crypto.randomUUID();
390
+ this.addEvent({
391
+ event_type: "tool_call",
392
+ span_id: spanId,
393
+ attributes: {
394
+ tool_call: {
395
+ tool_name: options.toolName,
396
+ args: options.args || null,
397
+ result: options.result || null,
398
+ result_status: options.resultStatus,
399
+ latency_ms: options.latencyMs,
400
+ error_message: options.errorMessage || null
401
+ }
402
+ }
403
+ });
404
+ return spanId;
405
+ }
406
+ /**
407
+ * Track a retrieval operation
408
+ */
409
+ trackRetrieval(options) {
410
+ const spanId = crypto.randomUUID();
411
+ this.addEvent({
412
+ event_type: "retrieval",
413
+ span_id: spanId,
414
+ attributes: {
415
+ retrieval: {
416
+ retrieval_context_ids: options.contextIds || null,
417
+ retrieval_context_hashes: options.contextHashes || null,
418
+ k: options.k || null,
419
+ top_k: options.k || null,
420
+ similarity_scores: options.similarityScores || null,
421
+ latency_ms: options.latencyMs
422
+ }
423
+ }
424
+ });
425
+ return spanId;
426
+ }
427
+ /**
428
+ * Track an error with stack trace support
429
+ */
430
+ trackError(options) {
431
+ const spanId = crypto.randomUUID();
432
+ let stackTrace = options.stackTrace;
433
+ if (!stackTrace && options.error instanceof Error && options.error.stack) {
434
+ stackTrace = options.error.stack;
435
+ }
436
+ this.addEvent({
437
+ event_type: "error",
438
+ span_id: spanId,
439
+ attributes: {
440
+ error: {
441
+ error_type: options.errorType,
442
+ error_message: options.errorMessage,
443
+ stack_trace: stackTrace || null,
444
+ context: options.context || null
445
+ }
446
+ }
447
+ });
448
+ return spanId;
449
+ }
450
+ /**
451
+ * Track user feedback
452
+ */
453
+ trackFeedback(options) {
454
+ const spanId = options.spanId || crypto.randomUUID();
455
+ const parentSpanId = options.parentSpanId ?? (this.spanStack.length > 0 ? this.spanStack[this.spanStack.length - 1] : null);
456
+ let rating = options.rating;
457
+ if (rating !== void 0 && rating !== null) {
458
+ rating = Math.max(1, Math.min(5, rating));
459
+ }
460
+ this.addEvent({
461
+ event_type: "feedback",
462
+ span_id: spanId,
463
+ parent_span_id: parentSpanId ?? null,
464
+ conversation_id: options.conversationId ?? null,
465
+ session_id: options.sessionId ?? null,
466
+ user_id: options.userId ?? null,
467
+ agent_name: options.agentName ?? null,
468
+ version: options.version ?? null,
469
+ route: options.route ?? null,
470
+ attributes: {
471
+ feedback: {
472
+ type: options.type,
473
+ rating: rating ?? null,
474
+ comment: options.comment || null,
475
+ outcome: options.outcome || null
476
+ }
477
+ }
478
+ });
479
+ return spanId;
480
+ }
481
+ /**
482
+ * Track final output
483
+ */
484
+ trackOutput(options) {
485
+ const spanId = crypto.randomUUID();
486
+ this.addEvent({
487
+ event_type: "output",
488
+ span_id: spanId,
489
+ attributes: {
490
+ output: {
491
+ final_output: options.finalOutput || null,
492
+ output_length: options.outputLength || null
493
+ }
494
+ }
495
+ });
496
+ return spanId;
497
+ }
498
+ /**
499
+ * Execute a function within a span context (for nested operations)
500
+ * This allows tool calls to be nested under LLM calls, etc.
501
+ */
502
+ withSpan(spanId, fn) {
503
+ this.spanStack.push(spanId);
504
+ try {
505
+ return fn();
506
+ } finally {
507
+ this.spanStack.pop();
508
+ }
509
+ }
510
+ /**
511
+ * Execute an async function within a span context (for nested operations)
512
+ */
513
+ async withSpanAsync(spanId, fn) {
514
+ this.spanStack.push(spanId);
515
+ try {
516
+ return await fn();
517
+ } finally {
518
+ this.spanStack.pop();
519
+ }
520
+ }
521
+ /**
522
+ * End trace and send events (manual trace management)
523
+ */
524
+ async endTrace(options = {}) {
525
+ if (!this.currentTraceId || !this.rootSpanId) {
526
+ throw new Error("[Observa] No active trace. Call startTrace() first.");
527
+ }
528
+ const traceEvents = this.eventBuffer.filter(
529
+ (e) => e.trace_id === this.currentTraceId
530
+ );
531
+ const llmEvents = traceEvents.filter((e) => e.event_type === "llm_call");
532
+ const totalTokens = llmEvents.reduce(
533
+ (sum, e) => sum + (e.attributes.llm_call?.total_tokens || 0),
534
+ 0
535
+ );
536
+ const totalCost = llmEvents.reduce(
537
+ (sum, e) => sum + (e.attributes.llm_call?.cost || 0),
538
+ 0
539
+ );
540
+ const totalLatency = this.traceStartTime !== null ? Date.now() - this.traceStartTime : null;
541
+ this.addEvent({
542
+ event_type: "trace_end",
543
+ span_id: this.rootSpanId,
544
+ parent_span_id: null,
545
+ attributes: {
546
+ trace_end: {
547
+ total_latency_ms: totalLatency,
548
+ total_tokens: totalTokens || null,
549
+ total_cost: totalCost || null,
550
+ outcome: options.outcome || "success"
551
+ }
552
+ }
553
+ });
554
+ const traceEventsToSend = this.eventBuffer.filter(
555
+ (e) => e.trace_id === this.currentTraceId
556
+ );
557
+ if (traceEventsToSend.length > 0) {
558
+ await this._sendEventsWithRetry(traceEventsToSend);
559
+ this.eventBuffer = this.eventBuffer.filter(
560
+ (e) => e.trace_id !== this.currentTraceId
561
+ );
562
+ }
563
+ const traceId = this.currentTraceId;
564
+ this.currentTraceId = null;
565
+ this.rootSpanId = null;
566
+ this.spanStack = [];
567
+ this.traceStartTime = null;
568
+ return traceId;
569
+ }
570
+ /**
571
+ * Convert legacy TraceData to canonical events
572
+ */
573
+ traceDataToCanonicalEvents(trace) {
574
+ const events = [];
575
+ const baseEvent = {
576
+ tenant_id: trace.tenantId,
577
+ project_id: trace.projectId,
578
+ environment: trace.environment,
579
+ trace_id: trace.traceId,
580
+ conversation_id: trace.conversationId || null,
581
+ session_id: trace.sessionId || null,
582
+ user_id: trace.userId || null
583
+ };
584
+ events.push({
585
+ ...baseEvent,
586
+ span_id: trace.spanId,
587
+ parent_span_id: trace.parentSpanId || null,
588
+ timestamp: trace.timestamp,
589
+ event_type: "trace_start",
590
+ attributes: {
591
+ trace_start: {
592
+ metadata: trace.metadata || null
593
+ }
594
+ }
595
+ });
596
+ if (trace.model) {
597
+ const llmSpanId = crypto.randomUUID();
598
+ events.push({
599
+ ...baseEvent,
600
+ span_id: llmSpanId,
601
+ parent_span_id: trace.spanId,
602
+ timestamp: trace.timestamp,
603
+ event_type: "llm_call",
604
+ attributes: {
605
+ llm_call: {
606
+ model: trace.model,
607
+ input: trace.query || null,
608
+ output: trace.response || null,
609
+ input_tokens: trace.tokensPrompt || null,
610
+ output_tokens: trace.tokensCompletion || null,
611
+ total_tokens: trace.tokensTotal || null,
612
+ latency_ms: trace.latencyMs,
613
+ time_to_first_token_ms: trace.timeToFirstTokenMs || null,
614
+ streaming_duration_ms: trace.streamingDurationMs || null,
615
+ finish_reason: trace.finishReason || null,
616
+ response_id: trace.responseId || null,
617
+ system_fingerprint: trace.systemFingerprint || null,
618
+ cost: null
619
+ // Cost calculation handled by backend
620
+ }
621
+ }
622
+ });
623
+ }
624
+ events.push({
625
+ ...baseEvent,
626
+ span_id: crypto.randomUUID(),
627
+ parent_span_id: trace.spanId,
628
+ timestamp: trace.timestamp,
629
+ event_type: "output",
630
+ attributes: {
631
+ output: {
632
+ final_output: trace.response || null,
633
+ output_length: trace.responseLength || null
634
+ }
635
+ }
636
+ });
637
+ events.push({
638
+ ...baseEvent,
639
+ span_id: trace.spanId,
640
+ parent_span_id: trace.parentSpanId || null,
641
+ timestamp: trace.timestamp,
642
+ event_type: "trace_end",
643
+ attributes: {
644
+ trace_end: {
645
+ total_latency_ms: trace.latencyMs,
646
+ total_tokens: trace.tokensTotal || null,
647
+ total_cost: null,
648
+ // Cost calculation handled by backend
649
+ outcome: trace.status && trace.status >= 200 && trace.status < 300 ? "success" : "error"
650
+ }
651
+ }
652
+ });
653
+ return events;
268
654
  }
269
- async track(event, action) {
655
+ /**
656
+ * Internal flush implementation
657
+ */
658
+ async _doFlush() {
659
+ const eventsToFlush = [...this.eventBuffer];
660
+ this.eventBuffer = [];
661
+ if (eventsToFlush.length === 0) {
662
+ return;
663
+ }
664
+ const eventsByTrace = /* @__PURE__ */ new Map();
665
+ for (const event of eventsToFlush) {
666
+ if (!eventsByTrace.has(event.trace_id)) {
667
+ eventsByTrace.set(event.trace_id, []);
668
+ }
669
+ eventsByTrace.get(event.trace_id).push(event);
670
+ }
671
+ for (const [traceId, events] of eventsByTrace.entries()) {
672
+ await this._sendEventsWithRetry(events);
673
+ }
674
+ }
675
+ /**
676
+ * Send canonical events with exponential backoff retry
677
+ */
678
+ async _sendEventsWithRetry(events, maxRetries = 3) {
679
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
680
+ try {
681
+ await this.sendEvents(events);
682
+ return;
683
+ } catch (error) {
684
+ if (attempt === maxRetries) {
685
+ console.error(
686
+ `[Observa] Failed to send events after ${maxRetries + 1} attempts, re-buffering:`,
687
+ error
688
+ );
689
+ this.eventBuffer.push(...events);
690
+ if (this.eventBuffer.length > this.maxBufferSize * 2) {
691
+ const toDrop = this.eventBuffer.length - this.maxBufferSize;
692
+ this.eventBuffer.splice(0, toDrop);
693
+ }
694
+ return;
695
+ }
696
+ const delayMs = 100 * Math.pow(2, attempt);
697
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
698
+ }
699
+ }
700
+ }
701
+ /**
702
+ * Cleanup (call when shutting down)
703
+ */
704
+ async end() {
705
+ if (this.flushIntervalId) {
706
+ clearInterval(this.flushIntervalId);
707
+ this.flushIntervalId = null;
708
+ }
709
+ await this.flush();
710
+ }
711
+ async track(event, action, options) {
270
712
  if (this.sampleRate < 1 && Math.random() > this.sampleRate) {
271
713
  return action();
272
714
  }
273
715
  const startTime = Date.now();
274
716
  const traceId = crypto.randomUUID();
275
717
  const spanId = traceId;
276
- const originalResponse = await action();
718
+ const runWithContext = contextModule?.runInTraceContextAsync || ((ctx, fn) => fn());
719
+ const context = contextModule?.createSpanContext?.(
720
+ traceId,
721
+ spanId,
722
+ null
723
+ ) || { traceId, spanId, parentSpanId: null };
724
+ const originalResponse = await runWithContext(context, action);
277
725
  if (!originalResponse.body) return originalResponse;
278
726
  const responseHeaders = {};
279
727
  originalResponse.headers.forEach((value, key) => {
280
728
  responseHeaders[key] = value;
281
729
  });
282
730
  const [stream1, stream2] = originalResponse.body.tee();
283
- console.log(`[Observa] Starting captureStream for trace ${traceId}`);
284
- this.captureStream({
731
+ const capturePromise = this.captureStream({
285
732
  stream: stream2,
286
733
  event,
287
734
  traceId,
288
735
  spanId,
289
- parentSpanId: null,
736
+ parentSpanId: context.parentSpanId,
290
737
  startTime,
291
738
  status: originalResponse.status,
292
739
  statusText: originalResponse.statusText,
293
740
  headers: responseHeaders
294
- }).catch((err) => {
295
- console.error("[Observa] captureStream promise rejected:", err);
296
741
  });
742
+ if (options?.trackBlocking) {
743
+ await capturePromise;
744
+ } else {
745
+ capturePromise.catch((err) => {
746
+ console.error("[Observa] captureStream promise rejected:", err);
747
+ });
748
+ }
297
749
  return new Response(stream1, {
298
750
  headers: originalResponse.headers,
299
751
  status: originalResponse.status,
@@ -395,17 +847,40 @@ var Observa = class {
395
847
  systemFingerprint: extracted.systemFingerprint ?? null,
396
848
  ...headers !== void 0 && { headers },
397
849
  // Conversation tracking fields
398
- ...event.conversationId !== void 0 && { conversationId: event.conversationId },
850
+ ...event.conversationId !== void 0 && {
851
+ conversationId: event.conversationId
852
+ },
399
853
  ...event.sessionId !== void 0 && { sessionId: event.sessionId },
400
854
  ...event.userId !== void 0 && { userId: event.userId },
401
- ...event.messageIndex !== void 0 && { messageIndex: event.messageIndex },
402
- ...event.parentMessageId !== void 0 && { parentMessageId: event.parentMessageId }
855
+ ...event.messageIndex !== void 0 && {
856
+ messageIndex: event.messageIndex
857
+ },
858
+ ...event.parentMessageId !== void 0 && {
859
+ parentMessageId: event.parentMessageId
860
+ }
403
861
  };
404
862
  console.log(
405
- `[Observa] Trace data prepared, calling sendTrace for ${traceId}, response length: ${fullResponse.length}`
863
+ `[Observa] Trace data prepared, converting to canonical events for ${traceId}, response length: ${fullResponse.length}`
406
864
  );
407
- await this.sendTrace(traceData);
408
- console.log(`[Observa] sendTrace completed for ${traceId}`);
865
+ const canonicalEvents = this.traceDataToCanonicalEvents(traceData);
866
+ this.eventBuffer.push(...canonicalEvents);
867
+ if (this.eventBuffer.length >= this.maxBufferSize) {
868
+ this.flush().catch((err) => {
869
+ console.error("[Observa] Auto-flush failed:", err);
870
+ });
871
+ } else {
872
+ this._sendEventsWithRetry(canonicalEvents).catch((err) => {
873
+ console.error("[Observa] Failed to send events:", err);
874
+ });
875
+ }
876
+ if (!this.isProduction) {
877
+ const traceUrl = `${this.apiUrl.replace(
878
+ /\/api.*$/,
879
+ ""
880
+ )}/traces/${traceId}`;
881
+ console.log(`[Observa] \u{1F54A}\uFE0F Trace captured: ${traceUrl}`);
882
+ }
883
+ console.log(`[Observa] Trace queued for sending: ${traceId}`);
409
884
  } catch (err) {
410
885
  console.error("[Observa] Error capturing stream:", err);
411
886
  if (err instanceof Error) {
@@ -417,15 +892,57 @@ var Observa = class {
417
892
  }
418
893
  }
419
894
  }
420
- async sendTrace(trace) {
421
- if (!this.isProduction) {
422
- formatBeautifulLog(trace);
895
+ /**
896
+ * Send canonical events to Observa backend
897
+ * (internal method, use _sendEventsWithRetry for retry logic)
898
+ */
899
+ async sendEvents(events) {
900
+ if (events.length === 0) {
901
+ return;
902
+ }
903
+ const traceId = events[0]?.trace_id;
904
+ if (!this.isProduction && traceId) {
905
+ const llmEvent = events.find((e) => e.event_type === "llm_call");
906
+ const outputEvent = events.find((e) => e.event_type === "output");
907
+ const traceEndEvent = events.find((e) => e.event_type === "trace_end");
908
+ if (llmEvent && outputEvent) {
909
+ const llmAttrs = llmEvent.attributes.llm_call;
910
+ const outputAttrs = outputEvent.attributes.output;
911
+ const traceData = {
912
+ traceId: llmEvent.trace_id,
913
+ spanId: llmEvent.parent_span_id || llmEvent.span_id,
914
+ parentSpanId: llmEvent.parent_span_id || null,
915
+ timestamp: llmEvent.timestamp,
916
+ tenantId: llmEvent.tenant_id,
917
+ projectId: llmEvent.project_id,
918
+ environment: llmEvent.environment,
919
+ query: llmAttrs?.input || "",
920
+ response: outputAttrs?.final_output || "",
921
+ responseLength: outputAttrs?.output_length || 0,
922
+ ...llmAttrs?.model && { model: llmAttrs.model },
923
+ tokensPrompt: llmAttrs?.input_tokens ?? null,
924
+ tokensCompletion: llmAttrs?.output_tokens ?? null,
925
+ tokensTotal: llmAttrs?.total_tokens ?? null,
926
+ latencyMs: llmAttrs?.latency_ms || 0,
927
+ timeToFirstTokenMs: llmAttrs?.time_to_first_token_ms ?? null,
928
+ streamingDurationMs: llmAttrs?.streaming_duration_ms ?? null,
929
+ finishReason: llmAttrs?.finish_reason ?? null,
930
+ responseId: llmAttrs?.response_id ?? null,
931
+ systemFingerprint: llmAttrs?.system_fingerprint ?? null,
932
+ ...llmEvent.conversation_id && {
933
+ conversationId: llmEvent.conversation_id
934
+ },
935
+ ...llmEvent.session_id && { sessionId: llmEvent.session_id },
936
+ ...llmEvent.user_id && { userId: llmEvent.user_id }
937
+ };
938
+ formatBeautifulLog(traceData);
939
+ }
423
940
  }
424
941
  try {
425
942
  const baseUrl = this.apiUrl.replace(/\/+$/, "");
426
- const url = `${baseUrl}/api/v1/traces/ingest`;
943
+ const url = `${baseUrl}/api/v1/events/ingest`;
427
944
  console.log(
428
- `[Observa] Sending trace - URL: ${url}, TraceID: ${trace.traceId}, Tenant: ${trace.tenantId}, Project: ${trace.projectId}, APIKey: ${this.apiKey ? `Yes(${this.apiKey.length} chars)` : "No"}`
945
+ `[Observa] Sending ${events.length} canonical events - URL: ${url}, TraceID: ${traceId}, Tenant: ${events[0]?.tenant_id}, Project: ${events[0]?.project_id}, APIKey: ${this.apiKey ? `Yes(${this.apiKey.length} chars)` : "No"}`
429
946
  );
430
947
  const controller = new AbortController();
431
948
  const timeoutId = setTimeout(() => controller.abort(), 1e4);
@@ -436,7 +953,7 @@ var Observa = class {
436
953
  Authorization: `Bearer ${this.apiKey}`,
437
954
  "Content-Type": "application/json"
438
955
  },
439
- body: JSON.stringify(trace),
956
+ body: JSON.stringify(events),
440
957
  signal: controller.signal
441
958
  });
442
959
  clearTimeout(timeoutId);
@@ -455,9 +972,13 @@ var Observa = class {
455
972
  `[Observa] Backend API error: ${response.status} ${response.statusText}`,
456
973
  errorJson.error || errorText
457
974
  );
975
+ throw new Error(
976
+ `Observa API error: ${response.status} ${errorJson.error?.message || errorText}`
977
+ );
458
978
  } else {
979
+ const result = await response.json().catch(() => ({}));
459
980
  console.log(
460
- `\u2705 [Observa] Trace sent successfully - Trace ID: ${trace.traceId}`
981
+ `\u2705 [Observa] Events sent successfully - Trace ID: ${traceId}, Event count: ${result.event_count || events.length}`
461
982
  );
462
983
  }
463
984
  } catch (fetchError) {
@@ -468,7 +989,7 @@ var Observa = class {
468
989
  throw fetchError;
469
990
  }
470
991
  } catch (error) {
471
- console.error("[Observa] Failed to send trace:", error);
992
+ console.error("[Observa] Failed to send events:", error);
472
993
  if (error instanceof Error) {
473
994
  console.error("[Observa] Error message:", error.message);
474
995
  console.error("[Observa] Error name:", error.name);
@@ -481,6 +1002,7 @@ var Observa = class {
481
1002
  console.error("[Observa] Error stack:", error.stack);
482
1003
  }
483
1004
  }
1005
+ throw error;
484
1006
  }
485
1007
  }
486
1008
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "observa-sdk",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Enterprise-grade observability SDK for AI applications. Track and monitor LLM interactions with zero friction.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",