observa-sdk 0.0.14 → 0.0.16

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.cjs CHANGED
@@ -451,7 +451,7 @@ function extractProviderFromModel(model) {
451
451
  }
452
452
  if (typeof model === "string") {
453
453
  const parts = model.split("/");
454
- if (parts.length > 1) {
454
+ if (parts.length > 1 && parts[0]) {
455
455
  return parts[0].toLowerCase();
456
456
  }
457
457
  const modelLower = model.toLowerCase();
@@ -537,6 +537,53 @@ async function traceGenerateText(originalFn, args, options) {
537
537
  throw error;
538
538
  }
539
539
  }
540
+ function wrapReadableStream(stream, onComplete, onError) {
541
+ const [userStream, trackingStream] = stream.tee();
542
+ const decoder = new TextDecoder();
543
+ let firstTokenTime = null;
544
+ const streamStartTime = Date.now();
545
+ const chunks = [];
546
+ (async () => {
547
+ try {
548
+ const reader = trackingStream.getReader();
549
+ while (true) {
550
+ const { done, value } = await reader.read();
551
+ if (done) break;
552
+ if (firstTokenTime === null && value !== null && value !== void 0) {
553
+ firstTokenTime = Date.now();
554
+ }
555
+ let text;
556
+ if (typeof value === "string") {
557
+ text = value;
558
+ } else if (value !== null && value !== void 0) {
559
+ try {
560
+ const testValue = value;
561
+ if (testValue instanceof Uint8Array || typeof ArrayBuffer !== "undefined" && typeof ArrayBuffer.isView === "function" && ArrayBuffer.isView(testValue)) {
562
+ text = decoder.decode(testValue, { stream: true });
563
+ } else {
564
+ text = String(value);
565
+ }
566
+ } catch {
567
+ text = String(value);
568
+ }
569
+ } else {
570
+ continue;
571
+ }
572
+ chunks.push(text);
573
+ }
574
+ const fullText = chunks.join("");
575
+ onComplete({
576
+ text: fullText,
577
+ timeToFirstToken: firstTokenTime ? firstTokenTime - streamStartTime : null,
578
+ streamingDuration: firstTokenTime ? Date.now() - firstTokenTime : null,
579
+ totalLatency: Date.now() - streamStartTime
580
+ });
581
+ } catch (error) {
582
+ onError(error);
583
+ }
584
+ })();
585
+ return userStream;
586
+ }
540
587
  async function traceStreamText(originalFn, args, options) {
541
588
  const startTime = Date.now();
542
589
  const requestParams = args[0] || {};
@@ -546,43 +593,46 @@ async function traceStreamText(originalFn, args, options) {
546
593
  try {
547
594
  const result = await originalFn(...args);
548
595
  if (result.textStream) {
549
- const wrappedStream = wrapStream(
550
- result.textStream,
551
- (fullResponse) => {
552
- recordTrace3(
596
+ const originalTextStream = result.textStream;
597
+ const isReadableStream = originalTextStream && typeof originalTextStream.getReader === "function";
598
+ if (isReadableStream) {
599
+ const wrappedStream = wrapReadableStream(
600
+ originalTextStream,
601
+ (fullResponse) => {
602
+ recordTrace3(
603
+ {
604
+ model: modelIdentifier,
605
+ prompt: requestParams.prompt || requestParams.messages || null,
606
+ messages: requestParams.messages || null
607
+ },
608
+ fullResponse,
609
+ startTime,
610
+ options,
611
+ fullResponse.timeToFirstToken,
612
+ fullResponse.streamingDuration,
613
+ provider
614
+ );
615
+ },
616
+ (err) => recordError3(
553
617
  {
554
618
  model: modelIdentifier,
555
- prompt: requestParams.prompt || requestParams.messages || null,
556
- messages: requestParams.messages || null
619
+ prompt: requestParams.prompt || requestParams.messages || null
557
620
  },
558
- fullResponse,
621
+ err,
559
622
  startTime,
560
- options,
561
- fullResponse.timeToFirstToken,
562
- fullResponse.streamingDuration,
563
- provider
564
- );
565
- },
566
- (err) => recordError3(
567
- {
568
- model: modelIdentifier,
569
- prompt: requestParams.prompt || requestParams.messages || null
570
- },
571
- err,
572
- startTime,
573
- options
574
- ),
575
- "vercel-ai"
576
- );
577
- const wrappedResult = Object.create(Object.getPrototypeOf(result));
578
- Object.assign(wrappedResult, result);
579
- Object.defineProperty(wrappedResult, "textStream", {
580
- value: wrappedStream,
581
- writable: true,
582
- enumerable: true,
583
- configurable: true
584
- });
585
- return wrappedResult;
623
+ options
624
+ )
625
+ );
626
+ const wrappedResult = Object.create(Object.getPrototypeOf(result));
627
+ Object.assign(wrappedResult, result);
628
+ Object.defineProperty(wrappedResult, "textStream", {
629
+ value: wrappedStream,
630
+ writable: true,
631
+ enumerable: true,
632
+ configurable: true
633
+ });
634
+ return wrappedResult;
635
+ }
586
636
  }
587
637
  recordTrace3(
588
638
  {
package/dist/index.js CHANGED
@@ -431,7 +431,7 @@ function extractProviderFromModel(model) {
431
431
  }
432
432
  if (typeof model === "string") {
433
433
  const parts = model.split("/");
434
- if (parts.length > 1) {
434
+ if (parts.length > 1 && parts[0]) {
435
435
  return parts[0].toLowerCase();
436
436
  }
437
437
  const modelLower = model.toLowerCase();
@@ -517,6 +517,53 @@ async function traceGenerateText(originalFn, args, options) {
517
517
  throw error;
518
518
  }
519
519
  }
520
+ function wrapReadableStream(stream, onComplete, onError) {
521
+ const [userStream, trackingStream] = stream.tee();
522
+ const decoder = new TextDecoder();
523
+ let firstTokenTime = null;
524
+ const streamStartTime = Date.now();
525
+ const chunks = [];
526
+ (async () => {
527
+ try {
528
+ const reader = trackingStream.getReader();
529
+ while (true) {
530
+ const { done, value } = await reader.read();
531
+ if (done) break;
532
+ if (firstTokenTime === null && value !== null && value !== void 0) {
533
+ firstTokenTime = Date.now();
534
+ }
535
+ let text;
536
+ if (typeof value === "string") {
537
+ text = value;
538
+ } else if (value !== null && value !== void 0) {
539
+ try {
540
+ const testValue = value;
541
+ if (testValue instanceof Uint8Array || typeof ArrayBuffer !== "undefined" && typeof ArrayBuffer.isView === "function" && ArrayBuffer.isView(testValue)) {
542
+ text = decoder.decode(testValue, { stream: true });
543
+ } else {
544
+ text = String(value);
545
+ }
546
+ } catch {
547
+ text = String(value);
548
+ }
549
+ } else {
550
+ continue;
551
+ }
552
+ chunks.push(text);
553
+ }
554
+ const fullText = chunks.join("");
555
+ onComplete({
556
+ text: fullText,
557
+ timeToFirstToken: firstTokenTime ? firstTokenTime - streamStartTime : null,
558
+ streamingDuration: firstTokenTime ? Date.now() - firstTokenTime : null,
559
+ totalLatency: Date.now() - streamStartTime
560
+ });
561
+ } catch (error) {
562
+ onError(error);
563
+ }
564
+ })();
565
+ return userStream;
566
+ }
520
567
  async function traceStreamText(originalFn, args, options) {
521
568
  const startTime = Date.now();
522
569
  const requestParams = args[0] || {};
@@ -526,43 +573,46 @@ async function traceStreamText(originalFn, args, options) {
526
573
  try {
527
574
  const result = await originalFn(...args);
528
575
  if (result.textStream) {
529
- const wrappedStream = wrapStream(
530
- result.textStream,
531
- (fullResponse) => {
532
- recordTrace3(
576
+ const originalTextStream = result.textStream;
577
+ const isReadableStream = originalTextStream && typeof originalTextStream.getReader === "function";
578
+ if (isReadableStream) {
579
+ const wrappedStream = wrapReadableStream(
580
+ originalTextStream,
581
+ (fullResponse) => {
582
+ recordTrace3(
583
+ {
584
+ model: modelIdentifier,
585
+ prompt: requestParams.prompt || requestParams.messages || null,
586
+ messages: requestParams.messages || null
587
+ },
588
+ fullResponse,
589
+ startTime,
590
+ options,
591
+ fullResponse.timeToFirstToken,
592
+ fullResponse.streamingDuration,
593
+ provider
594
+ );
595
+ },
596
+ (err) => recordError3(
533
597
  {
534
598
  model: modelIdentifier,
535
- prompt: requestParams.prompt || requestParams.messages || null,
536
- messages: requestParams.messages || null
599
+ prompt: requestParams.prompt || requestParams.messages || null
537
600
  },
538
- fullResponse,
601
+ err,
539
602
  startTime,
540
- options,
541
- fullResponse.timeToFirstToken,
542
- fullResponse.streamingDuration,
543
- provider
544
- );
545
- },
546
- (err) => recordError3(
547
- {
548
- model: modelIdentifier,
549
- prompt: requestParams.prompt || requestParams.messages || null
550
- },
551
- err,
552
- startTime,
553
- options
554
- ),
555
- "vercel-ai"
556
- );
557
- const wrappedResult = Object.create(Object.getPrototypeOf(result));
558
- Object.assign(wrappedResult, result);
559
- Object.defineProperty(wrappedResult, "textStream", {
560
- value: wrappedStream,
561
- writable: true,
562
- enumerable: true,
563
- configurable: true
564
- });
565
- return wrappedResult;
603
+ options
604
+ )
605
+ );
606
+ const wrappedResult = Object.create(Object.getPrototypeOf(result));
607
+ Object.assign(wrappedResult, result);
608
+ Object.defineProperty(wrappedResult, "textStream", {
609
+ value: wrappedStream,
610
+ writable: true,
611
+ enumerable: true,
612
+ configurable: true
613
+ });
614
+ return wrappedResult;
615
+ }
566
616
  }
567
617
  recordTrace3(
568
618
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "observa-sdk",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
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",