@gammatech/aijsx 0.5.0-beta.1 → 0.5.0-dev.2024-03-12

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
@@ -30,16 +30,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  var src_exports = {};
31
31
  __export(src_exports, {
32
32
  AIFragment: () => AIFragment,
33
+ AISpanProcessor: () => AISpanProcessor,
33
34
  AnthropicChatCompletion: () => AnthropicChatCompletion,
34
35
  AnthropicClient: () => import_sdk2.default,
35
36
  AnthropicClientContext: () => AnthropicClientContext,
36
37
  AssistantMessage: () => AssistantMessage,
37
38
  BoundLogger: () => BoundLogger,
38
- ChainParseVariablesError: () => ChainParseVariablesError,
39
39
  ChatCompletionError: () => ChatCompletionError,
40
+ ClaudeImageBlock: () => ClaudeImageBlock,
40
41
  CombinedLogger: () => CombinedLogger,
41
42
  ConsoleLogger: () => ConsoleLogger,
42
43
  ContentTypeImage: () => ContentTypeImage,
44
+ EnrichingSpanProcessor: () => EnrichingSpanProcessor,
43
45
  LogImplementation: () => LogImplementation,
44
46
  NoopLogImplementation: () => NoopLogImplementation,
45
47
  OpenAIChatCompletion: () => OpenAIChatCompletion,
@@ -48,10 +50,9 @@ __export(src_exports, {
48
50
  OpenAIVisionChatCompletion: () => OpenAIVisionChatCompletion,
49
51
  ParseVariablesError: () => ParseVariablesError,
50
52
  PromptInvalidOutputError: () => PromptInvalidOutputError,
51
- PromptParseVariablesError: () => PromptParseVariablesError,
52
53
  SystemMessage: () => SystemMessage,
54
+ Trace: () => Trace,
53
55
  UserMessage: () => UserMessage,
54
- aijsxTracing: () => aijsxTracing,
55
56
  attachedContextSymbol: () => attachedContextSymbol,
56
57
  computeUsage: () => computeUsage,
57
58
  countAnthropicTokens: () => import_tokenizer4.countTokens,
@@ -61,12 +62,12 @@ __export(src_exports, {
61
62
  createPrompt: () => createPrompt,
62
63
  createRenderContext: () => createRenderContext,
63
64
  createStreamChain: () => createStreamChain,
64
- defaultMaxTokens: () => defaultMaxTokens,
65
65
  evaluatePrompt: () => evaluatePrompt,
66
66
  tokenCountForOpenAIMessage: () => tokenCountForOpenAIMessage,
67
67
  tokenCountForOpenAIVisionMessage: () => tokenCountForOpenAIVisionMessage,
68
68
  tokenLimitForChatModel: () => tokenLimitForChatModel,
69
- tokenizer: () => tokenizer
69
+ tokenizer: () => tokenizer,
70
+ tracing: () => tracing
70
71
  });
71
72
  module.exports = __toCommonJS(src_exports);
72
73
 
@@ -301,29 +302,30 @@ function coalesceParallelStreams(streams) {
301
302
  return iter;
302
303
  }
303
304
 
304
- // src/logging/log.ts
305
+ // src/log.ts
305
306
  var LogImplementation = class {
306
307
  loggedExceptions = /* @__PURE__ */ new WeakMap();
307
- chatCompletionRequest(provider, payload) {
308
- }
309
- chatCompletionResponse(provider, payload) {
310
- }
311
308
  /**
312
309
  * Logs exceptions thrown during an element's render.
313
310
  */
314
- logException(exception) {
311
+ logException(ctx, exception) {
315
312
  if (typeof exception === "object" && exception !== null) {
316
313
  if (this.loggedExceptions.has(exception)) {
317
314
  return;
318
315
  }
319
316
  this.loggedExceptions.set(exception, true);
320
317
  }
321
- this.log("error", `AIJSX Exception: ${exception}`);
318
+ this.log(ctx, "error", `Rendering failed with exception: ${exception}`);
319
+ }
320
+ chatCompletionRequest(_ctx, _provider, _payload) {
321
+ }
322
+ chatCompletionResponse(_ctx, _provider, _payload) {
322
323
  }
323
324
  };
324
325
  var BoundLogger = class {
325
- constructor(impl) {
326
+ constructor(impl, ctx) {
326
327
  this.impl = impl;
328
+ this.ctx = ctx;
327
329
  }
328
330
  formatMessage = (...msgs) => msgs.map((m) => {
329
331
  if (typeof m === "string") {
@@ -340,24 +342,24 @@ var BoundLogger = class {
340
342
  return JSON.stringify(m);
341
343
  }
342
344
  }).join(" ");
343
- error = (...msgs) => this.impl.log("error", this.formatMessage(...msgs));
344
- warn = (...msgs) => this.impl.log("warn", this.formatMessage(...msgs));
345
- info = (...msgs) => this.impl.log("info", this.formatMessage(...msgs));
346
- debug = (...msgs) => this.impl.log("debug", this.formatMessage(...msgs));
347
- logException = (exception) => this.impl.logException(exception);
345
+ error = (...msgs) => this.impl.log(this.ctx, "error", this.formatMessage(...msgs));
346
+ warn = (...msgs) => this.impl.log(this.ctx, "warn", this.formatMessage(...msgs));
347
+ info = (...msgs) => this.impl.log(this.ctx, "info", this.formatMessage(...msgs));
348
+ debug = (...msgs) => this.impl.log(this.ctx, "debug", this.formatMessage(...msgs));
349
+ logException = (exception) => this.impl.logException(this.ctx, exception);
348
350
  chatCompletionRequest = (provider, payload) => {
349
- return this.impl.chatCompletionRequest(provider, payload);
351
+ return this.impl.chatCompletionRequest(this.ctx, provider, payload);
350
352
  };
351
353
  chatCompletionResponse = (provider, payload) => {
352
- return this.impl.chatCompletionResponse(provider, payload);
354
+ return this.impl.chatCompletionResponse(this.ctx, provider, payload);
353
355
  };
354
356
  };
355
357
  var NoopLogImplementation = class extends LogImplementation {
356
- log(level, message) {
358
+ log(_ctx, _level, _message) {
357
359
  }
358
360
  };
359
361
  var ConsoleLogger = class extends LogImplementation {
360
- log(level, message) {
362
+ log(ctx, level, message) {
361
363
  console.log(`[${level}] ${message}`);
362
364
  }
363
365
  };
@@ -369,9 +371,6 @@ var CombinedLogger = class extends LogImplementation {
369
371
  log(...args) {
370
372
  this.loggers.forEach((l) => l.log(...args));
371
373
  }
372
- logException(...args) {
373
- this.loggers.forEach((l) => l.logException(...args));
374
- }
375
374
  chatCompletionRequest(...args) {
376
375
  this.loggers.forEach((l) => l.chatCompletionRequest(...args));
377
376
  }
@@ -381,10 +380,6 @@ var CombinedLogger = class extends LogImplementation {
381
380
  };
382
381
 
383
382
  // src/prompt/errors.ts
384
- var PromptParseVariablesError = class extends Error {
385
- };
386
- var ChainParseVariablesError = class extends Error {
387
- };
388
383
  var ParseVariablesError = class extends Error {
389
384
  };
390
385
  var PromptInvalidOutputError = class extends Error {
@@ -393,121 +388,25 @@ var PromptInvalidOutputError = class extends Error {
393
388
  // src/prompt/parseVariables.ts
394
389
  function parseVariables(schema, variables) {
395
390
  try {
396
- return { success: true, result: schema.parse(variables) };
391
+ return { parsedVariables: schema.parse(variables), error: null };
397
392
  } catch (err) {
398
393
  let error = err;
399
394
  if (err && typeof err === "object" && "flatten" in err) {
400
395
  const errorMessage = JSON.stringify(err.flatten());
401
396
  error = new ParseVariablesError(errorMessage);
402
397
  }
403
- return { success: false, error };
398
+ return { parsedVariables: null, error };
404
399
  }
405
400
  }
406
401
 
407
- // src/render.ts
408
- function renderLiteral(renderable) {
409
- if (typeof renderable === "string") {
410
- return renderable;
411
- }
412
- if (typeof renderable === "number") {
413
- return renderable.toString();
414
- }
415
- if (typeof renderable === "undefined" || typeof renderable === "boolean" || renderable === null) {
416
- return "";
417
- }
418
- return "";
419
- }
420
- function createRenderContext({
421
- parentSpanId = null,
422
- logger = new NoopLogImplementation(),
423
- traceId = null,
424
- contextValues = {}
425
- } = {}) {
426
- let spanContext = {};
427
- if (traceId || parentSpanId) {
428
- spanContext = {
429
- traceId: traceId || void 0,
430
- // set to invalid span id to make sure the span
431
- // isn't mistakenly tagged for having a parent
432
- spanId: parentSpanId || ""
433
- };
434
- }
435
- return new StreamRenderContext(logger, contextValues, spanContext);
436
- }
437
-
438
- // src/RenderResult.ts
439
- var accumResults = async (result) => {
440
- let accum = "";
441
- const iterator = result[Symbol.asyncIterator]();
442
- for await (const value of iterator) {
443
- accum += value;
444
- }
445
- return accum;
446
- };
447
- var GeneratorRenderResult = class {
448
- // this promise like thing
449
- constructor(generator) {
450
- this.generator = generator;
451
- }
452
- accumPromise = void 0;
453
- async next(...args) {
454
- return this.generator.next(...args);
455
- }
456
- async return(value) {
457
- return this.generator.return(value);
458
- }
459
- throw(e) {
460
- return this.generator.throw(e);
461
- }
462
- [Symbol.asyncIterator]() {
463
- return this;
464
- }
465
- then(onFulfilled, onRejected) {
466
- if (!this.accumPromise) {
467
- this.accumPromise = accumResults(this);
468
- }
469
- return this.accumPromise.then(onFulfilled, onRejected);
470
- }
471
- };
472
- var AccumulatedRenderResult = class extends GeneratorRenderResult {
473
- constructor(generator, cbs) {
474
- super(generator);
475
- this.generator = generator;
476
- this.cbs = cbs;
477
- }
478
- accumulated = "";
479
- async next(...args) {
480
- let ret;
481
- try {
482
- ret = await this.generator.next(...args);
483
- } catch (e) {
484
- this.cbs.onError(e);
485
- throw e;
486
- }
487
- const { done, value } = ret;
488
- if (done) {
489
- this.cbs.onDone(this.accumulated);
490
- }
491
- this.accumulated += value;
492
- if (done) {
493
- return { value: void 0, done: true };
494
- }
495
- return { value, done: false };
496
- }
497
- async throw(e) {
498
- this.cbs.onError(e);
499
- return this.generator.throw(e);
500
- }
501
- };
502
-
503
- // src/tracing/types/TracingContext.ts
402
+ // src/tracing/TracingContext.ts
504
403
  function createContextKey(description) {
505
404
  return {
506
405
  key: Symbol.for(description)
507
406
  };
508
407
  }
509
408
  var BaseTracingContext = class _BaseTracingContext {
510
- _currentContext;
409
+ current;
511
410
  /**
512
411
  * Construct a new context which inherits values from an optional parent context.
513
412
  *
@@ -515,16 +414,16 @@ var BaseTracingContext = class _BaseTracingContext {
515
414
  */
516
415
  constructor(parentContext) {
517
416
  const self = this;
518
- self._currentContext = parentContext ? new Map(parentContext) : /* @__PURE__ */ new Map();
519
- self.getValue = ({ key }) => self._currentContext.get(key);
417
+ self.current = parentContext ? new Map(parentContext) : /* @__PURE__ */ new Map();
418
+ self.getValue = ({ key }) => self.current.get(key);
520
419
  self.setValue = ({ key }, value) => {
521
- const context = new _BaseTracingContext(self._currentContext);
522
- context._currentContext.set(key, value);
420
+ const context = new _BaseTracingContext(self.current);
421
+ context.current.set(key, value);
523
422
  return context;
524
423
  };
525
424
  self.deleteValue = ({ key }) => {
526
- const context = new _BaseTracingContext(self._currentContext);
527
- context._currentContext.delete(key);
425
+ const context = new _BaseTracingContext(self.current);
426
+ context.current.delete(key);
528
427
  return context;
529
428
  };
530
429
  }
@@ -550,8 +449,6 @@ var BaseTracingContext = class _BaseTracingContext {
550
449
  */
551
450
  deleteValue;
552
451
  };
553
-
554
- // src/tracing/TracingContextManager.ts
555
452
  var ROOT_CONTEXT = new BaseTracingContext();
556
453
  var NoopTracingContextManager = class {
557
454
  active() {
@@ -611,26 +508,25 @@ var ContextAPI = class _ContextAPI {
611
508
  with(context, fn, ...args) {
612
509
  return this.getContextManager().with(context, fn, ...args);
613
510
  }
511
+ enable() {
512
+ this.manager.enable();
513
+ }
514
+ disable() {
515
+ this.manager.disable();
516
+ }
614
517
  /**
615
518
  * Bind a context to a target function or event emitter
616
519
  *
617
520
  * @param context context to bind to the event emitter or function. Defaults to the currently active context
618
521
  * @param target function or event emitter to bind
619
522
  */
620
- bind(context, target) {
621
- return this.getContextManager().bind(context, target);
523
+ bind(target, context) {
524
+ const ctx = context || this.active();
525
+ return this.getContextManager().bind(ctx, target);
622
526
  }
623
527
  getContextManager() {
624
528
  return this.manager || NOOP_CONTEXT_MANAGER;
625
529
  }
626
- /** Disable and remove the global context manager */
627
- disable() {
628
- this.getContextManager().disable();
629
- this.manager = NOOP_CONTEXT_MANAGER;
630
- }
631
- reset() {
632
- this.manager = NOOP_CONTEXT_MANAGER;
633
- }
634
530
  };
635
531
  var contextApi = ContextAPI.getInstance();
636
532
 
@@ -640,13 +536,16 @@ var HrTime = class _HrTime {
640
536
  this.time = time;
641
537
  this.time = time;
642
538
  }
539
+ static from(time) {
540
+ return new _HrTime(time);
541
+ }
643
542
  static now() {
644
543
  const timeOrigin = performance.timeOrigin;
645
544
  const now = performance.now();
646
545
  const totalNanoseconds = timeOrigin * 1e6 + now * 1e6;
647
546
  const seconds = Math.floor(totalNanoseconds / 1e9);
648
547
  const nanoseconds = totalNanoseconds % 1e9;
649
- return new _HrTime([seconds, Math.floor(nanoseconds)]);
548
+ return [seconds, Math.floor(nanoseconds)];
650
549
  }
651
550
  toISOString() {
652
551
  const milliseconds = this.time[0] * 1e3 + this.time[1] / 1e6;
@@ -661,61 +560,39 @@ var HrTime = class _HrTime {
661
560
  };
662
561
 
663
562
  // src/tracing/AISpan.ts
664
- var SPAN_KEY = createContextKey("SPAN KEY");
563
+ var SPAN_KEY = createContextKey("AIJSX SPAN");
665
564
  var AISpan = class {
666
- constructor(spanProcessorProvider, context, spanName, spanContext, parentSpanId, startTime, attributes) {
667
- this.spanProcessorProvider = spanProcessorProvider;
668
- this.name = spanName;
669
- this._spanContext = spanContext;
670
- this.parentSpanId = parentSpanId;
671
- this.startHrTime = startTime || HrTime.now();
672
- this.startTime = this.startHrTime.toISOString();
673
- if (attributes != null) {
674
- this.setAttributes(attributes);
675
- }
676
- const processors = spanProcessorProvider.getSpanProcessors();
677
- processors.forEach((processor) => {
678
- processor.onStart(this, context);
679
- });
680
- }
681
- _spanContext;
565
+ name;
566
+ status = "unset";
567
+ spanContext;
682
568
  parentSpanId;
683
569
  attributes = {};
684
- data = {};
685
570
  events = [];
686
- //timing
687
571
  startTime;
688
- startHrTime;
689
572
  endTime = null;
690
- endHrTime = null;
573
+ startTimestamp;
574
+ endTimestamp = null;
575
+ ended = false;
691
576
  duration = -1;
692
- name;
693
- _ended = false;
694
- status = {
695
- code: 0 /* UNSET */
696
- };
697
- spanContext() {
698
- return this._spanContext;
699
- }
700
- setAttribute(key, value) {
701
- if (value == null || this._isSpanEnded())
702
- return this;
703
- this.attributes[key] = value;
704
- return this;
577
+ processor;
578
+ constructor(opts) {
579
+ this.name = opts.name;
580
+ this.spanContext = opts.spanContext;
581
+ this.parentSpanId = opts.parentSpanId;
582
+ this.startTimestamp = opts.startTime || HrTime.now();
583
+ this.startTime = new HrTime(this.startTimestamp).toISOString();
584
+ this.processor = opts.processor;
585
+ if (opts.attributes != null) {
586
+ this.setAttributes(opts.attributes);
587
+ }
588
+ this.processor?.onStart?.(this);
705
589
  }
706
590
  setAttributes(attributes) {
707
591
  for (const [k, v] of Object.entries(attributes)) {
708
- this.setAttribute(k, v);
592
+ this.attributes[k] = v;
709
593
  }
710
594
  return this;
711
595
  }
712
- setData(key, value) {
713
- this.data[key] = value;
714
- return this;
715
- }
716
- getData(key) {
717
- return this.data[key];
718
- }
719
596
  /**
720
597
  *
721
598
  * @param name Span Name
@@ -724,46 +601,40 @@ var AISpan = class {
724
601
  * @param [timeStamp] Specified time stamp for the event
725
602
  */
726
603
  addEvent(name, attributes, startTime = HrTime.now()) {
727
- if (this._isSpanEnded())
604
+ if (this.ended)
728
605
  return this;
729
- this.events.push({
730
- timestamp: startTime.toISOString(),
731
- time: startTime.time,
606
+ const event = {
732
607
  name,
733
- attributes
734
- });
735
- return this;
736
- }
737
- setStatus(status) {
738
- if (this._isSpanEnded())
739
- return this;
740
- this.status = status;
741
- return this;
742
- }
743
- updateName(name) {
744
- if (this._isSpanEnded())
745
- return this;
746
- this.name = name;
608
+ attributes,
609
+ timestamp: new HrTime(startTime).toISOString(),
610
+ time: startTime
611
+ };
612
+ this.events.push(event);
613
+ if (name === "exception") {
614
+ this.processor?.onSpanException?.(event, this);
615
+ } else {
616
+ this.processor?.onSpanEvent?.(event, this);
617
+ }
747
618
  return this;
748
619
  }
749
- end(endHrTime = HrTime.now()) {
750
- if (this._isSpanEnded()) {
620
+ end(endTime = HrTime.now()) {
621
+ if (this.ended) {
751
622
  return;
752
623
  }
753
- this._ended = true;
754
- this.endHrTime = endHrTime;
624
+ if (this.status === "unset") {
625
+ this.status = "ok";
626
+ }
627
+ this.ended = true;
628
+ const endHrTime = new HrTime(endTime);
629
+ const startHrTime = new HrTime(this.startTimestamp);
630
+ this.endTimestamp = endHrTime.time;
755
631
  this.endTime = endHrTime.toISOString();
756
- this.duration = endHrTime.minus(this.startHrTime);
757
- const processors = this.spanProcessorProvider.getSpanProcessors();
758
- processors.forEach((processor) => {
759
- processor.onEnd(this);
760
- });
761
- }
762
- isRecording() {
763
- return this._ended === false;
632
+ this.duration = endHrTime.minus(startHrTime);
633
+ this.processor?.onEnd?.(this);
764
634
  }
765
635
  recordException(exception, time = HrTime.now()) {
766
636
  let attributes = {};
637
+ this.status = "error";
767
638
  if (typeof exception === "string") {
768
639
  attributes = {
769
640
  name: "Error",
@@ -778,18 +649,83 @@ var AISpan = class {
778
649
  }
779
650
  return this.addEvent("exception", attributes, time);
780
651
  }
781
- get ended() {
782
- return this._ended;
652
+ };
653
+ var NoopSpan = class extends AISpan {
654
+ constructor() {
655
+ super({
656
+ name: "noop-span",
657
+ spanContext: {
658
+ traceId: "",
659
+ spanId: ""
660
+ },
661
+ parentSpanId: null
662
+ });
783
663
  }
784
- _isSpanEnded() {
785
- return this._ended;
664
+ };
665
+
666
+ // src/tracing/utils/BoundAsyncGenerator.ts
667
+ var BoundAsyncGenerator = class {
668
+ generator;
669
+ onDone;
670
+ onError;
671
+ constructor(generator, cbs) {
672
+ this.generator = contextApi.bind(generator);
673
+ if (cbs.onDone) {
674
+ this.onDone = cbs.onDone;
675
+ }
676
+ if (cbs.onError) {
677
+ this.onError = cbs.onError;
678
+ }
679
+ }
680
+ async next(...args) {
681
+ try {
682
+ const result = await this.generator.next(...args);
683
+ if (result.done) {
684
+ this.onDone?.();
685
+ }
686
+ return result;
687
+ } catch (e) {
688
+ this.onError?.(e);
689
+ throw e;
690
+ }
691
+ }
692
+ async return(value) {
693
+ return this.generator.return(value);
694
+ }
695
+ throw(e) {
696
+ return this.generator.throw(e);
697
+ }
698
+ [Symbol.asyncIterator]() {
699
+ return this;
700
+ }
701
+ };
702
+ var BoundRenderResult = class extends BoundAsyncGenerator {
703
+ constructor(renderResult, cbs) {
704
+ super(renderResult, cbs);
705
+ this.renderResult = renderResult;
706
+ }
707
+ then(onFulfilled, onRejected) {
708
+ return this.renderResult.then(
709
+ (val) => {
710
+ this.onDone?.();
711
+ if (!onFulfilled) {
712
+ return val;
713
+ }
714
+ return onFulfilled?.(val);
715
+ },
716
+ (err) => {
717
+ this.onError?.(err);
718
+ if (!onRejected) {
719
+ throw err;
720
+ }
721
+ return onRejected?.(err);
722
+ }
723
+ );
786
724
  }
787
725
  };
788
726
 
789
727
  // src/tracing/utils/id.ts
790
728
  var import_nanoid = require("nanoid");
791
- var INVALID_SPANID = "0000000000000000";
792
- var INVALID_TRACEID = "00000000000000000000000000000000";
793
729
  var hexAlphabet = "0123456789abcdef";
794
730
  var traceIdSize = 32;
795
731
  var spanIdSize = 16;
@@ -797,250 +733,182 @@ var idGenerator = {
797
733
  traceId: (0, import_nanoid.customAlphabet)(hexAlphabet, traceIdSize),
798
734
  spanId: (0, import_nanoid.customAlphabet)(hexAlphabet, spanIdSize)
799
735
  };
800
- function isValidTraceId(traceId) {
801
- return traceId !== INVALID_TRACEID;
802
- }
803
- function isValidSpanId(spanId) {
804
- return spanId !== INVALID_SPANID;
805
- }
806
- function isSpanContextValid(spanContext) {
807
- return isValidTraceId(spanContext.traceId) && isValidSpanId(spanContext.spanId);
808
- }
809
-
810
- // src/tracing/NonRecordingSpan.ts
811
- var INVALID_SPAN_CONTEXT = {
812
- traceId: INVALID_TRACEID,
813
- spanId: INVALID_SPANID
814
- };
815
- var NonRecordingSpan = class {
816
- constructor(_spanContext = INVALID_SPAN_CONTEXT) {
817
- this._spanContext = _spanContext;
818
- }
819
- name = "non-recording-span";
820
- attributes = {};
821
- data = {};
822
- events = [];
823
- startTime = (/* @__PURE__ */ new Date()).toISOString();
824
- endTime = null;
825
- duration = -1;
826
- parentSpanId = null;
827
- setData(key, value) {
828
- this.data[key] = value;
829
- return this;
830
- }
831
- getData(key) {
832
- return this.data[key];
833
- }
834
- // Returns a SpanContext.
835
- spanContext() {
836
- return this._spanContext;
837
- }
838
- // By default does nothing
839
- setAttribute(_key, _value) {
840
- return this;
841
- }
842
- // By default does nothing
843
- setAttributes(_attributes) {
844
- return this;
845
- }
846
- // By default does nothing
847
- addEvent(_name, _attributes, _startTime) {
848
- return this;
849
- }
850
- // By default does nothing
851
- setStatus(_status) {
852
- return this;
853
- }
854
- // By default does nothing
855
- updateName(_name) {
856
- return this;
857
- }
858
- // By default does nothing
859
- end(_endTime) {
860
- }
861
- // isRecording always returns false for NonRecordingSpan.
862
- isRecording() {
863
- return false;
864
- }
865
- // By default does nothing
866
- recordException(exception, time) {
867
- return this;
868
- }
869
- };
870
736
 
871
737
  // src/tracing/AITracer.ts
872
738
  var AITracer = class {
739
+ traceId;
740
+ processor;
873
741
  /**
874
742
  * Constructs a new Tracer instance.
875
743
  */
876
- constructor(spanProcessorProvider) {
877
- this.spanProcessorProvider = spanProcessorProvider;
744
+ constructor({
745
+ traceId,
746
+ processor
747
+ } = {}) {
748
+ this.traceId = traceId || idGenerator.traceId();
749
+ this.processor = processor;
878
750
  }
879
751
  /**
880
752
  * Starts a new Span or returns the default NoopSpan based on the sampling
881
753
  * decision.
882
754
  */
883
- startSpan(name, options = {}, context = contextApi.active()) {
884
- if (options.root) {
885
- context = context.deleteValue(SPAN_KEY);
886
- }
887
- const parentSpan = context.getValue(SPAN_KEY);
888
- const parentSpanContext = parentSpan?.spanContext();
755
+ createSpan(name, attributes = {}) {
756
+ const traceId = this.traceId;
889
757
  const spanId = idGenerator.spanId();
890
- let traceId;
891
- let parentSpanId = null;
892
- if (!parentSpanContext) {
893
- traceId = idGenerator.traceId();
894
- } else {
895
- traceId = isValidTraceId(parentSpanContext.traceId) ? parentSpanContext.traceId : idGenerator.traceId();
896
- parentSpanId = isValidSpanId(parentSpanContext.spanId) ? parentSpanContext.spanId : null;
897
- }
898
- const spanContext = { traceId, spanId };
899
- const span = new AISpan(
900
- this.spanProcessorProvider,
901
- context,
758
+ const parentSpan = contextApi.active().getValue(SPAN_KEY);
759
+ const parentSpanId = parentSpan ? parentSpan.spanContext.spanId : null;
760
+ const span = new AISpan({
902
761
  name,
903
- spanContext,
762
+ spanContext: { traceId, spanId },
904
763
  parentSpanId,
905
- void 0,
906
- // auto compute start time
907
- options.attributes
908
- );
764
+ processor: this.processor,
765
+ attributes
766
+ });
909
767
  return span;
910
768
  }
911
- startActiveSpan(name, arg2, arg3, arg4) {
912
- let opts;
913
- let ctx;
914
- let fn;
915
- if (arguments.length < 2) {
916
- return;
917
- } else if (arguments.length === 2) {
918
- fn = arg2;
919
- } else if (arguments.length === 3) {
920
- opts = arg2;
921
- fn = arg3;
922
- } else {
923
- opts = arg2;
924
- ctx = arg3;
925
- fn = arg4;
926
- }
927
- const activeContext = ctx || contextApi.active();
928
- const span = this.startSpan(name, opts, activeContext);
929
- const newContext = activeContext.setValue(SPAN_KEY, span);
769
+ startSpan(name, attributes, fn) {
770
+ const span = this.createSpan(name, attributes);
771
+ const newContext = contextApi.active().setValue(SPAN_KEY, span);
930
772
  return contextApi.with(newContext, fn, span);
931
773
  }
774
+ trace(name, attributes, fn) {
775
+ return this.startSpan(name, attributes, (span) => {
776
+ const onError = (e) => {
777
+ span.recordException(e);
778
+ span.end();
779
+ };
780
+ const onDone = () => {
781
+ span.end();
782
+ };
783
+ try {
784
+ const result = fn(span);
785
+ if (isRenderResult(result)) {
786
+ return new BoundRenderResult(result, {
787
+ onDone,
788
+ onError
789
+ });
790
+ }
791
+ if (isGenerator(result)) {
792
+ return new BoundAsyncGenerator(result, {
793
+ onDone,
794
+ onError
795
+ });
796
+ }
797
+ if (isPromise(result)) {
798
+ return result.then(
799
+ (val) => {
800
+ onDone();
801
+ return val;
802
+ },
803
+ (e) => {
804
+ onError(e);
805
+ throw e;
806
+ }
807
+ );
808
+ }
809
+ onDone();
810
+ return result;
811
+ } catch (e) {
812
+ onError(e);
813
+ throw e;
814
+ }
815
+ throw new Error("Did not return result");
816
+ });
817
+ }
932
818
  getActiveSpan() {
933
819
  const res = contextApi.active().getValue(SPAN_KEY);
934
- return res;
820
+ return res || null;
935
821
  }
936
- setSpanContext(spanContext) {
937
- const ctx = contextApi.active().setValue(SPAN_KEY, new NonRecordingSpan(spanContext));
938
- return ctx;
822
+ bind(target) {
823
+ return contextApi.bind(target);
939
824
  }
940
825
  };
941
-
942
- // src/tracing/NoopTracer.ts
826
+ function isRenderResult(val) {
827
+ return isGenerator(val) && isPromise(val);
828
+ }
829
+ function isGenerator(val) {
830
+ return Boolean(
831
+ val && typeof val === "object" && "next" in val && "throw" in val && "return" in val && Symbol.asyncIterator in val
832
+ );
833
+ }
834
+ var isPromise = (val) => {
835
+ return Boolean(val && typeof val === "object" && "then" in val);
836
+ };
943
837
  var NoopTracer = class {
944
- // startSpan starts a noop span.
945
- startSpan(name, options, context = contextApi.active()) {
946
- const root = Boolean(options?.root);
947
- if (root) {
948
- return new NonRecordingSpan();
949
- }
950
- const parentFromContext = context && context.getValue(SPAN_KEY);
951
- if (isSpanContext(parentFromContext) && isSpanContextValid(parentFromContext)) {
952
- return new NonRecordingSpan(parentFromContext);
953
- } else {
954
- return new NonRecordingSpan();
955
- }
956
- }
957
- startActiveSpan(name, arg2, arg3, arg4) {
958
- let opts;
959
- let ctx;
960
- let fn;
961
- if (arguments.length < 2) {
962
- return;
963
- } else if (arguments.length === 2) {
964
- fn = arg2;
965
- } else if (arguments.length === 3) {
966
- opts = arg2;
967
- fn = arg3;
968
- } else {
969
- opts = arg2;
970
- ctx = arg3;
971
- fn = arg4;
972
- }
973
- const parentContext = ctx ?? contextApi.active();
974
- const span = this.startSpan(name, opts, parentContext);
975
- const contextWithSpanSet = parentContext.setValue(SPAN_KEY, span);
976
- return contextApi.with(contextWithSpanSet, fn, span);
977
- }
978
838
  getActiveSpan() {
979
- return contextApi.active().getValue(SPAN_KEY);
839
+ return null;
980
840
  }
981
- setSpanContext(spanContext) {
982
- return contextApi.active().setValue(SPAN_KEY, new NonRecordingSpan(spanContext));
841
+ trace(_name, _attributes, fn) {
842
+ const span = new NoopSpan();
843
+ return fn(span);
983
844
  }
984
845
  };
985
- function isSpanContext(spanContext) {
986
- return typeof spanContext === "object" && typeof spanContext["spanId"] === "string" && typeof spanContext["traceId"] === "string" && typeof spanContext["traceFlags"] === "number";
987
- }
988
846
 
989
- // src/tracing/SpanProcessorProvider.ts
990
- var SpanProcessorProvider = class {
991
- processors = [];
992
- setSpanProcessors(processors) {
993
- this.processors = processors;
847
+ // src/tracing/AsyncLocalStorageContextManager.ts
848
+ var import_node_async_hooks = require("async_hooks");
849
+ var isAsyncGenerator = (obj) => obj != null && typeof obj === "object" && "next" in obj && "return" in obj && "throw" in obj && Symbol.asyncIterator in obj;
850
+ var AsyncLocalStorageContextManager = class {
851
+ asyncLocalStorage;
852
+ constructor() {
853
+ this.asyncLocalStorage = new import_node_async_hooks.AsyncLocalStorage();
994
854
  }
995
- getSpanProcessors() {
996
- return this.processors;
855
+ active() {
856
+ return this.asyncLocalStorage.getStore() ?? ROOT_CONTEXT;
997
857
  }
998
- };
999
-
1000
- // src/tracing/api/traceApi.ts
1001
- var TraceAPI = class _TraceAPI {
1002
- static _instance;
1003
- spanProcessorProvider = new SpanProcessorProvider();
1004
- tracer = new AITracer(this.spanProcessorProvider);
1005
- /** Empty private constructor prevents end users from constructing a new instance of the API */
1006
- constructor() {
858
+ with(context, fn, ...args) {
859
+ return this.asyncLocalStorage.run(context, () => {
860
+ const result = fn(...args);
861
+ return result;
862
+ });
1007
863
  }
1008
- /** Get the singleton instance of the Trace API */
1009
- static getInstance() {
1010
- if (!this._instance) {
1011
- this._instance = new _TraceAPI();
1012
- }
1013
- return this._instance;
864
+ enable() {
865
+ return this;
1014
866
  }
1015
- setTracer(tracer) {
1016
- this.tracer = tracer;
867
+ disable() {
868
+ this.asyncLocalStorage.disable();
869
+ return this;
1017
870
  }
1018
871
  /**
1019
- * Returns a tracer from the global tracer provider.
872
+ * Binds a the certain context or the active one to the target function and then returns the target
873
+ * @param context A context (span) to be bind to target
874
+ * @param target a function or event emitter. When target or one of its callbacks is called,
875
+ * the provided context will be used as the active context for the duration of the call.
1020
876
  */
1021
- getTracer() {
1022
- return this.tracer;
1023
- }
1024
- /** Remove the global tracer provider */
1025
- disable() {
1026
- this.tracer = new NoopTracer();
877
+ bind(context, target) {
878
+ if (typeof target === "function") {
879
+ return this.bindFunction(context, target);
880
+ }
881
+ if (isAsyncGenerator(target)) {
882
+ return this.bindAsyncGenerator(target);
883
+ }
884
+ return target;
1027
885
  }
1028
- setSpanProcessors(spanProcessors) {
1029
- this.spanProcessorProvider.setSpanProcessors(spanProcessors);
886
+ bindFunction(context, target) {
887
+ const manager = this;
888
+ const contextWrapper = function(...args) {
889
+ return manager.with(context, () => target.apply(this, args));
890
+ };
891
+ Object.defineProperty(contextWrapper, "length", {
892
+ enumerable: false,
893
+ configurable: true,
894
+ writable: false,
895
+ value: target.length
896
+ });
897
+ return contextWrapper;
1030
898
  }
1031
- reset() {
1032
- this.spanProcessorProvider = new SpanProcessorProvider();
1033
- this.tracer = new AITracer(this.spanProcessorProvider);
899
+ bindAsyncGenerator(generator) {
900
+ generator.next = this.bindFunction(this.active(), generator.next);
901
+ return generator;
1034
902
  }
1035
903
  };
1036
- var traceApi = TraceAPI.getInstance();
1037
904
 
1038
905
  // src/tracing/api/index.ts
1039
906
  var AITraceAPI = class _AITraceAPI {
1040
907
  static _instance;
1041
- initialized = false;
908
+ enabled = false;
1042
909
  /** Empty private constructor prevents end users from constructing a new instance of the API */
1043
910
  constructor() {
911
+ this.enable();
1044
912
  }
1045
913
  /** Get the singleton instance of the Trace API */
1046
914
  static getInstance() {
@@ -1049,40 +917,102 @@ var AITraceAPI = class _AITraceAPI {
1049
917
  }
1050
918
  return this._instance;
1051
919
  }
1052
- init(options) {
1053
- if (this.initialized) {
1054
- throw new Error("Tracing can only be initialized once");
1055
- }
1056
- const processors = options.spanProcessors || [];
1057
- traceApi.setSpanProcessors(processors);
1058
- if (options.contextManager) {
1059
- contextApi.setContextManager(options.contextManager);
1060
- }
1061
- this.initialized = true;
1062
- return this.getTracer();
1063
- }
1064
- getTracer() {
1065
- return traceApi.getTracer();
920
+ createTracer(opts = {}) {
921
+ return this.enabled ? new AITracer(opts) : new NoopTracer();
1066
922
  }
1067
- getActiveContext() {
1068
- return contextApi.active();
1069
- }
1070
- reset() {
1071
- this.initialized = false;
1072
- traceApi.reset();
1073
- contextApi.reset();
923
+ enable() {
924
+ this.enabled = true;
925
+ contextApi.setContextManager(new AsyncLocalStorageContextManager());
926
+ contextApi.disable();
1074
927
  }
1075
928
  disable() {
1076
- traceApi.disable();
929
+ this.enabled = false;
930
+ contextApi.setContextManager(NOOP_CONTEXT_MANAGER);
1077
931
  contextApi.disable();
1078
932
  }
1079
- bind(target) {
1080
- const activeContext = contextApi.active();
1081
- return contextApi.bind(activeContext, target);
1082
- }
1083
933
  };
1084
934
  var tracing = AITraceAPI.getInstance();
1085
935
 
936
+ // src/render.ts
937
+ function renderLiteral(renderable) {
938
+ if (typeof renderable === "string") {
939
+ return renderable;
940
+ }
941
+ if (typeof renderable === "number") {
942
+ return renderable.toString();
943
+ }
944
+ if (typeof renderable === "undefined" || typeof renderable === "boolean" || renderable === null) {
945
+ return "";
946
+ }
947
+ return "";
948
+ }
949
+ function createRenderContext({
950
+ logger = new NoopLogImplementation(),
951
+ traceId,
952
+ processor,
953
+ contextValues = {}
954
+ } = {}) {
955
+ const tracer = tracing.createTracer({
956
+ traceId,
957
+ processor
958
+ });
959
+ return new StreamRenderContext(tracer, logger, contextValues);
960
+ }
961
+
962
+ // src/RenderResult.ts
963
+ var accumResults = async (result) => {
964
+ let accum = "";
965
+ const iterator = result[Symbol.asyncIterator]();
966
+ for await (const value of iterator) {
967
+ accum += value;
968
+ }
969
+ return accum;
970
+ };
971
+ var GeneratorRenderResult = class {
972
+ // this promise like thing
973
+ constructor(generator) {
974
+ this.generator = generator;
975
+ }
976
+ accumPromise = void 0;
977
+ async next(...args) {
978
+ return this.generator.next(...args);
979
+ }
980
+ async return(value) {
981
+ return this.generator.return(value);
982
+ }
983
+ throw(e) {
984
+ return this.generator.throw(e);
985
+ }
986
+ [Symbol.asyncIterator]() {
987
+ return this;
988
+ }
989
+ then(onFulfilled, onRejected) {
990
+ if (!this.accumPromise) {
991
+ this.accumPromise = accumResults(this);
992
+ }
993
+ return this.accumPromise.then(onFulfilled, onRejected);
994
+ }
995
+ };
996
+ var AccumulatedRenderResult = class extends GeneratorRenderResult {
997
+ constructor(generator, onDone) {
998
+ super(generator);
999
+ this.generator = generator;
1000
+ this.onDone = onDone;
1001
+ }
1002
+ accumulated = "";
1003
+ async next(...args) {
1004
+ const { done, value } = await this.generator.next(...args);
1005
+ if (done) {
1006
+ this.onDone(this.accumulated);
1007
+ }
1008
+ this.accumulated += value;
1009
+ if (done) {
1010
+ return { value: void 0, done: true };
1011
+ }
1012
+ return { done: false, value };
1013
+ }
1014
+ };
1015
+
1086
1016
  // src/types.ts
1087
1017
  var attachedContextSymbol = Symbol("AI.attachedContext");
1088
1018
 
@@ -1168,6 +1098,9 @@ function parseXml(input) {
1168
1098
  const attributeName = Object.keys(nodeObject)[1];
1169
1099
  const childObjects = nodeObject[nodeName];
1170
1100
  const attributes = Object.entries(nodeObject[attributeName] || {}).reduce((acc, [key, value]) => {
1101
+ if (!value) {
1102
+ return acc;
1103
+ }
1171
1104
  try {
1172
1105
  acc[key] = JSON.parse(value);
1173
1106
  } catch (e) {
@@ -1185,6 +1118,9 @@ function parseXml(input) {
1185
1118
  return constructNode(null, parsed[0]);
1186
1119
  }
1187
1120
  function escape(html) {
1121
+ if (!html) {
1122
+ return "";
1123
+ }
1188
1124
  return html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1189
1125
  }
1190
1126
 
@@ -1197,52 +1133,48 @@ function jsx(type, config, maybeKey) {
1197
1133
 
1198
1134
  // src/context.tsx
1199
1135
  var StreamRenderContext = class _StreamRenderContext {
1200
- constructor(logImplementation, contextValues, rootTraceContext) {
1201
- this.logImplementation = logImplementation;
1136
+ constructor(tracer, logImpl, contextValues) {
1137
+ this.tracer = tracer;
1138
+ this.logImpl = logImpl;
1202
1139
  this.contextValues = contextValues;
1203
- this.rootTraceContext = rootTraceContext;
1204
1140
  const self = this;
1205
- this.logger = new BoundLogger(logImplementation);
1141
+ this.logger = new BoundLogger(logImpl, this);
1206
1142
  this.render = this.render.bind(this);
1207
- this.runChain = this.runChain.bind(this);
1208
- this.renderElement = (...args) => {
1209
- const generator = renderElementInternal(...args);
1210
- return tracing.bind(generator);
1211
- };
1212
- async function* renderElementInternal(element, opts = {}) {
1143
+ this.renderElement = async function* (renderable, opts = {}) {
1213
1144
  const span = self.tracer.getActiveSpan();
1214
1145
  const preserveTags = opts.preserveTags ?? false;
1215
1146
  const renderedProps = opts.renderedProps || {};
1216
1147
  const renderEscaped = (val) => {
1217
1148
  return preserveTags ? escape(val) : val;
1218
1149
  };
1219
- if (isLiteral(element)) {
1220
- yield renderEscaped(renderLiteral(element));
1150
+ if (isLiteral(renderable)) {
1151
+ yield renderEscaped(renderLiteral(renderable));
1221
1152
  return;
1222
1153
  }
1223
- if (isAIElement(element)) {
1224
- const newCtx = self.enterElement(attachedContextValues(element) || {});
1225
- const isFragment = element.tag.name === "AIFragment";
1154
+ if (isAIElement(renderable)) {
1155
+ const newCtx = self.enterElement(renderable);
1156
+ const logger = newCtx.logger;
1157
+ const isFragment = renderable.tag.name === "AIFragment";
1226
1158
  try {
1227
1159
  if (preserveTags && !isFragment) {
1228
- yield renderOpenTag(element, renderedProps);
1160
+ yield renderOpenTag(renderable, renderedProps);
1229
1161
  }
1230
- yield* newCtx.renderElement(element.render(newCtx), opts);
1162
+ yield* newCtx.renderElement(renderable.render(newCtx), opts);
1231
1163
  if (preserveTags && !isFragment) {
1232
- yield renderCloseTag(element);
1164
+ yield renderCloseTag(renderable);
1233
1165
  }
1234
1166
  return;
1235
1167
  } catch (ex) {
1236
- span?.recordException(ex);
1168
+ logger.logException(ex);
1237
1169
  throw ex;
1238
1170
  }
1239
1171
  }
1240
- if (Array.isArray(element)) {
1241
- if (element.every((r) => isLiteral(r))) {
1242
- yield element.map((r) => renderEscaped(renderLiteral(r))).join("");
1172
+ if (Array.isArray(renderable)) {
1173
+ if (renderable.every((r) => isLiteral(r))) {
1174
+ yield renderable.map((r) => renderEscaped(renderLiteral(r))).join("");
1243
1175
  return;
1244
1176
  }
1245
- const streams = element.filter((a) => !!a).map((r) => self.renderElement(r, opts));
1177
+ const streams = renderable.filter((a) => !!a).map((r) => self.renderElement(r, opts));
1246
1178
  const result = coalesceParallelStreams(streams);
1247
1179
  while (true) {
1248
1180
  const { value, done } = await result.next();
@@ -1252,201 +1184,140 @@ var StreamRenderContext = class _StreamRenderContext {
1252
1184
  yield value;
1253
1185
  }
1254
1186
  }
1255
- if (Symbol.asyncIterator in element) {
1256
- return yield* element[Symbol.asyncIterator]();
1187
+ if (Symbol.asyncIterator in renderable) {
1188
+ return yield* renderable[Symbol.asyncIterator]();
1257
1189
  }
1258
- if (!("then" in element)) {
1190
+ if (!("then" in renderable)) {
1259
1191
  throw new Error(
1260
- `Unexpected renderable type: ${JSON.stringify(element)}`
1192
+ `Unexpected renderable type: ${JSON.stringify(renderable)}`
1261
1193
  );
1262
1194
  }
1263
- const next = await element.then(
1195
+ const next = await renderable.then(
1264
1196
  (r) => r
1265
1197
  );
1266
1198
  return yield* self.renderElement(next, opts);
1267
- }
1199
+ };
1268
1200
  }
1269
1201
  renderElement;
1270
1202
  logger;
1271
- tracer = tracing.getTracer();
1272
1203
  // Implementation of the overloaded function
1273
1204
  render(arg1, arg2, arg3) {
1274
1205
  switch (true) {
1275
1206
  case (isPromptParsed(arg1) && !!arg2): {
1276
- return this.trace("ai.prompt", () => {
1277
- return this.renderPromptParsed(arg1, arg2, arg3 || {});
1278
- });
1207
+ return this.tracer.trace(
1208
+ "ai.prompt",
1209
+ {
1210
+ prompt: arg1,
1211
+ promptKey: arg1.key,
1212
+ variables: arg2
1213
+ },
1214
+ () => this.renderPromptParsed(arg1, arg2, arg3 || {})
1215
+ );
1279
1216
  }
1280
1217
  case (isPrompt(arg1) && !!arg2): {
1281
- return this.trace("ai.prompt", () => {
1282
- return this.renderPrompt(arg1, arg2);
1283
- });
1218
+ return this.tracer.trace(
1219
+ "ai.prompt",
1220
+ {
1221
+ prompt: arg1,
1222
+ promptKey: arg1.key,
1223
+ variables: arg2
1224
+ },
1225
+ () => this.renderPrompt(arg1, arg2)
1226
+ );
1284
1227
  }
1285
1228
  case (isFunctionChain(arg1) && !!arg2): {
1286
- return this.trace("ai.chain", () => {
1287
- return this.runChain(arg1, arg2);
1288
- });
1229
+ return this.tracer.trace(
1230
+ "ai.chain",
1231
+ { chainKey: arg1.key, chainType: arg1.chainType, variables: arg2 },
1232
+ () => this.runChain(arg1, arg2)
1233
+ );
1289
1234
  }
1290
1235
  case (isStreamChain(arg1) && !!arg2): {
1291
- return this.trace("ai.chain", () => {
1292
- return this.runChain(arg1, arg2);
1293
- });
1236
+ return this.tracer.trace(
1237
+ "ai.chain",
1238
+ { chainKey: arg1.key, chainType: arg1.chainType, variables: arg2 },
1239
+ () => this.runChain(arg1, arg2)
1240
+ );
1294
1241
  }
1295
1242
  default: {
1296
- return this.trace("ai.component", () => {
1297
- return new GeneratorRenderResult(
1298
- this.renderElement(
1299
- arg1,
1300
- arg2 || { preserveTags: false }
1301
- )
1302
- );
1303
- });
1243
+ const currentSpan = this.tracer.getActiveSpan();
1244
+ const stream = this.renderElement(
1245
+ arg1,
1246
+ arg2 || { preserveTags: false }
1247
+ );
1248
+ if (currentSpan) {
1249
+ return new GeneratorRenderResult(stream);
1250
+ }
1251
+ return this.tracer.trace(
1252
+ "ai.component",
1253
+ {},
1254
+ (span) => new AccumulatedRenderResult(stream, (output) => {
1255
+ span.setAttributes({
1256
+ output
1257
+ });
1258
+ })
1259
+ );
1304
1260
  }
1305
1261
  }
1306
1262
  }
1307
- /**
1308
- * @internal
1309
- *
1310
- * Helper method to create a new span and set it as the active span
1311
- * if there is a rootTraceContext attached, start a different context
1312
- * with that spanId / traceId as the SpanContext
1313
- *
1314
- * This can safely be used other places becuase it depends on the
1315
- * current active span, and only uses the rootTraceContext if there
1316
- * is no active span
1317
- */
1318
- // TODO(jordan) better typing of this
1319
- trace(spanName, fn) {
1320
- const currentSpan = this.tracer.getActiveSpan();
1321
- const ctx = currentSpan ? tracing.getActiveContext() : this.tracer.setSpanContext({
1322
- traceId: this.rootTraceContext?.traceId || idGenerator.traceId(),
1323
- spanId: this.rootTraceContext?.spanId || INVALID_SPANID
1324
- });
1325
- return this.tracer.startActiveSpan(spanName, {}, ctx, fn);
1326
- }
1327
1263
  renderPrompt(prompt, variables) {
1328
- const span = this.tracer.getActiveSpan();
1329
- span?.setData("prompt", prompt);
1330
- span?.setAttributes({
1331
- promptKey: prompt.key,
1332
- variables
1333
- });
1334
1264
  const { schema, component: Component } = prompt;
1335
- const parsedVariables = parseVariables(schema, variables);
1336
- if (!parsedVariables.success) {
1337
- const { error } = parsedVariables;
1338
- span?.recordException(error).end();
1265
+ const { error, parsedVariables } = parseVariables(schema, variables);
1266
+ if (error) {
1339
1267
  throw error;
1340
1268
  }
1341
- const generator = this.renderElement(
1342
- /* @__PURE__ */ jsx(Component, { ...parsedVariables.result }),
1343
- {}
1344
- );
1345
- const result = new AccumulatedRenderResult(generator, {
1346
- onDone: tracing.bind((output) => {
1347
- span?.setAttributes({
1348
- output
1349
- }).end();
1350
- return output;
1351
- }),
1352
- onError: tracing.bind((error) => {
1353
- span?.recordException(error).end();
1354
- throw error;
1355
- })
1269
+ const generator = this.renderElement(/* @__PURE__ */ jsx(Component, { ...parsedVariables }), {});
1270
+ return new AccumulatedRenderResult(generator, (output) => {
1271
+ this.tracer.getActiveSpan()?.setAttributes({
1272
+ output
1273
+ });
1356
1274
  });
1357
- return result;
1358
1275
  }
1359
1276
  async renderPromptParsed(prompt, variables, options) {
1360
1277
  const span = this.tracer.getActiveSpan();
1361
- span?.setData("prompt", prompt);
1362
- span?.setAttributes({
1363
- promptKey: prompt.key,
1364
- variables,
1365
- renderPromptOptions: options
1366
- });
1367
1278
  const { validateOutput } = options;
1368
1279
  const { schema, component: Component } = prompt;
1369
- const parsedVariables = parseVariables(schema, variables);
1370
- if (!parsedVariables.success) {
1371
- const { error } = parsedVariables;
1372
- span?.recordException(error).end();
1280
+ const { error, parsedVariables } = parseVariables(schema, variables);
1281
+ if (error) {
1373
1282
  throw error;
1374
1283
  }
1375
- try {
1376
- const stream = this.renderElement(
1377
- /* @__PURE__ */ jsx(Component, { ...parsedVariables.result })
1378
- );
1379
- const output = await accumResults(stream);
1380
- const parsedOutput = prompt.parse(output);
1381
- if (validateOutput) {
1382
- try {
1383
- prompt.outputSchema.parse(parsedOutput);
1384
- } catch (err) {
1385
- if (err && typeof err === "object" && "flatten" in err) {
1386
- const errors = JSON.stringify(err.flatten());
1387
- throw new PromptInvalidOutputError("Invalid output: " + errors);
1388
- }
1389
- throw err;
1390
- }
1284
+ const stream = this.renderElement(/* @__PURE__ */ jsx(Component, { ...parsedVariables }));
1285
+ const output = await accumResults(stream);
1286
+ const parsedOutput = prompt.parse(output);
1287
+ span?.setAttributes({
1288
+ output,
1289
+ parsedOutput
1290
+ });
1291
+ if (validateOutput) {
1292
+ try {
1293
+ prompt.outputSchema.parse(parsedOutput);
1294
+ } catch (e) {
1295
+ const err = e;
1296
+ const errors = JSON.stringify(err.flatten());
1297
+ throw new PromptInvalidOutputError("Invalid output: " + errors);
1391
1298
  }
1392
- span?.setAttributes({
1393
- output: parsedOutput
1394
- }).end();
1395
- return parsedOutput;
1396
- } catch (error) {
1397
- span?.recordException(error).end();
1398
- throw error;
1399
1299
  }
1300
+ return parsedOutput;
1400
1301
  }
1401
1302
  runChain(chain, variables) {
1402
- const span = this.tracer.getActiveSpan();
1403
- span?.setData("chain", chain);
1404
- span?.setAttributes({
1405
- chainKey: chain.key,
1406
- variables
1407
- });
1408
- const { schema } = chain;
1409
- const parsedVariables = parseVariables(schema, variables);
1410
- if (!parsedVariables.success) {
1411
- const { error } = parsedVariables;
1412
- span?.recordException(error).end();
1303
+ const { error, parsedVariables } = parseVariables(chain.schema, variables);
1304
+ if (error) {
1413
1305
  throw error;
1414
1306
  }
1415
1307
  if (chain.chainType === "function") {
1416
- const onChainComplete = (output) => {
1417
- span?.setAttributes({
1418
- output
1419
- }).end();
1420
- };
1421
- const onChainError = (error) => {
1422
- span?.recordException(error).end();
1308
+ const span = this.tracer.getActiveSpan();
1309
+ const onOutput = (output) => {
1310
+ span?.setAttributes({ output });
1311
+ return output;
1423
1312
  };
1424
- let chainResult;
1425
- try {
1426
- chainResult = chain.run(parsedVariables.result, this);
1427
- } catch (e) {
1428
- onChainError(e);
1429
- throw e;
1430
- }
1431
- if (chainResult && typeof chainResult === "object" && "then" in chainResult) {
1432
- chainResult.then(onChainComplete, onChainError);
1433
- return chainResult;
1313
+ const result = chain.run(parsedVariables, this);
1314
+ if (result && typeof result === "object" && "then" in result) {
1315
+ return result.then(onOutput);
1434
1316
  }
1435
- onChainComplete(chainResult);
1436
- return chainResult;
1317
+ return onOutput(result);
1437
1318
  }
1438
1319
  if (chain.chainType === "stream") {
1439
- try {
1440
- const stream = chain.run(
1441
- parsedVariables.result,
1442
- this
1443
- );
1444
- span?.end();
1445
- return tracing.bind(stream);
1446
- } catch (error) {
1447
- span?.recordException(error).end();
1448
- throw error;
1449
- }
1320
+ return chain.run(parsedVariables, this);
1450
1321
  }
1451
1322
  throw new Error("Invalid chain type");
1452
1323
  }
@@ -1454,8 +1325,9 @@ var StreamRenderContext = class _StreamRenderContext {
1454
1325
  return this.contextValues[context.key] ?? context.defaultValue;
1455
1326
  };
1456
1327
  // @internal
1457
- enterElement(newCtx) {
1458
- return new _StreamRenderContext(this.logImplementation, {
1328
+ enterElement(element) {
1329
+ const newCtx = attachedContextValues(element);
1330
+ return new _StreamRenderContext(this.tracer, this.logImpl, {
1459
1331
  ...this.contextValues,
1460
1332
  ...newCtx
1461
1333
  });
@@ -1506,7 +1378,7 @@ function withContextValues(renderable, additionalContext) {
1506
1378
  );
1507
1379
  }
1508
1380
  function attachedContextValues(element) {
1509
- return element[attachedContextSymbol];
1381
+ return element[attachedContextSymbol] || {};
1510
1382
  }
1511
1383
  function isPrompt(value) {
1512
1384
  return Boolean(value && typeof value === "object" && "component" in value);
@@ -1542,70 +1414,157 @@ function renderCloseTag(element) {
1542
1414
  return `</${element.tag.name}>`;
1543
1415
  }
1544
1416
 
1545
- // src/tracing/AsyncLocalStorageContextManager.ts
1546
- var import_node_async_hooks = require("async_hooks");
1547
- var isAsyncGenerator = (obj) => obj != null && typeof obj === "object" && "next" in obj && "return" in obj && "throw" in obj && Symbol.asyncIterator in obj;
1548
- var AsyncLocalStorageContextManager = class {
1549
- asyncLocalStorage;
1550
- constructor() {
1551
- this.asyncLocalStorage = new import_node_async_hooks.AsyncLocalStorage();
1417
+ // src/prompt/evaluatePrompt.ts
1418
+ async function evaluatePrompt(prompt, variables, result) {
1419
+ const { error, parsedVariables } = parseVariables(prompt.schema, variables);
1420
+ if (error) {
1421
+ throw error;
1552
1422
  }
1553
- active() {
1554
- return this.asyncLocalStorage.getStore() ?? ROOT_CONTEXT;
1423
+ return (await Promise.all(
1424
+ prompt.evaluators.map(
1425
+ (evaluator) => evaluator(parsedVariables, result)
1426
+ )
1427
+ )).flat();
1428
+ }
1429
+
1430
+ // src/tracing/AISpanProcessor.ts
1431
+ var EnrichingSpanProcessor = class {
1432
+ constructor(exporter) {
1433
+ this.exporter = exporter;
1434
+ this.onAllDonePromise = new Promise((resolve) => {
1435
+ this.onAllDonePromiseResolve = resolve;
1436
+ });
1555
1437
  }
1556
- with(context, fn, ...args) {
1557
- return this.asyncLocalStorage.run(context, () => {
1558
- const result = fn(...args);
1559
- return result;
1438
+ finalSpans = [];
1439
+ onAllDonePromise = null;
1440
+ onAllDonePromiseResolve = null;
1441
+ async onStart(span) {
1442
+ this.finalSpans.push({
1443
+ status: "running",
1444
+ span
1560
1445
  });
1561
1446
  }
1562
- enable() {
1563
- return this;
1447
+ async onEnd(span) {
1448
+ const entry = this.finalSpans.find(
1449
+ (a) => a.span.spanContext.spanId === span.spanContext.spanId
1450
+ );
1451
+ if (!entry) {
1452
+ throw new Error("onEnd called without onStart");
1453
+ }
1454
+ const enrichedSpan = await this.enrichSpan(span);
1455
+ entry.status = "processing";
1456
+ entry.span = enrichedSpan;
1457
+ entry.status = "done";
1458
+ if (this.finalSpans.every((a) => a.status === "done")) {
1459
+ this.onAllDonePromiseResolve?.();
1460
+ }
1461
+ if (span.parentSpanId == null) {
1462
+ await this.onAllDonePromise;
1463
+ await this.onTraceComplete(enrichedSpan);
1464
+ }
1465
+ this.exporter.export([enrichedSpan]);
1564
1466
  }
1565
- disable() {
1566
- this.asyncLocalStorage.disable();
1567
- return this;
1467
+ shutdown() {
1468
+ return this.exporter.shutdown?.();
1469
+ }
1470
+ };
1471
+ var AISpanProcessor = class extends EnrichingSpanProcessor {
1472
+ totalCost = 0;
1473
+ totalUsage = {
1474
+ prompt: 0,
1475
+ completion: 0,
1476
+ total: 0
1477
+ };
1478
+ async onTraceComplete(rootSpan) {
1479
+ rootSpan.attributes.totalCost = this.totalCost;
1480
+ rootSpan.attributes.totalUsage = this.totalUsage;
1481
+ }
1482
+ async enrichSpan(span) {
1483
+ const result1 = await this.evaluatePrompt(span);
1484
+ const result2 = await this.updateCost(result1);
1485
+ const result3 = await this.updateUsage(result2);
1486
+ return result3;
1487
+ }
1488
+ async updateCost(span) {
1489
+ if (!isChatCompletionSpan(span)) {
1490
+ return span;
1491
+ }
1492
+ const { cost } = span.attributes;
1493
+ if (typeof cost === "number") {
1494
+ this.totalCost += cost;
1495
+ }
1496
+ return span;
1568
1497
  }
1569
- /**
1570
- * Binds a the certain context or the active one to the target function and then returns the target
1571
- * @param context A context (span) to be bind to target
1572
- * @param target a function or event emitter. When target or one of its callbacks is called,
1573
- * the provided context will be used as the active context for the duration of the call.
1574
- */
1575
- bind(context, target) {
1576
- if (typeof target === "function") {
1577
- return this.bindFunction(context, target);
1498
+ async updateUsage(span) {
1499
+ if (!isChatCompletionSpan(span)) {
1500
+ return span;
1578
1501
  }
1579
- if (isAsyncGenerator(target)) {
1580
- return this.bindAsyncGenerator(target);
1502
+ const tokensUsed = span.attributes.tokensUsed;
1503
+ if (!tokensUsed || tokensUsed.total == null || tokensUsed.completion == null || tokensUsed.prompt == null) {
1504
+ return span;
1581
1505
  }
1582
- return target;
1506
+ this.totalUsage.prompt += tokensUsed.prompt;
1507
+ this.totalUsage.completion += tokensUsed.completion;
1508
+ this.totalUsage.total += tokensUsed.total;
1509
+ return span;
1583
1510
  }
1584
- bindFunction(context, target) {
1585
- const manager = this;
1586
- const contextWrapper = function(...args) {
1587
- return manager.with(context, () => target.apply(this, args));
1511
+ async evaluatePrompt(span) {
1512
+ const { prompt, variables, output } = span.attributes;
1513
+ if (!isPrompt2(prompt)) {
1514
+ return span;
1515
+ }
1516
+ delete span.attributes["prompt"];
1517
+ let evaluators = [];
1518
+ let evaluatorResults = {};
1519
+ let extraAttributes = {};
1520
+ try {
1521
+ evaluators = await evaluatePrompt(prompt, variables, output);
1522
+ evaluatorResults = evaluators.reduce(
1523
+ (accum, result) => {
1524
+ accum[result.key] = result;
1525
+ return accum;
1526
+ },
1527
+ {}
1528
+ );
1529
+ extraAttributes = {
1530
+ evaluators,
1531
+ evaluatorResults
1532
+ };
1533
+ } catch (e) {
1534
+ extraAttributes = {
1535
+ evaluatorError: e.message,
1536
+ evaluators,
1537
+ evaluatorResults
1538
+ };
1539
+ }
1540
+ const toSend = {
1541
+ ...span,
1542
+ attributes: {
1543
+ ...span.attributes,
1544
+ ...extraAttributes
1545
+ }
1588
1546
  };
1589
- Object.defineProperty(contextWrapper, "length", {
1590
- enumerable: false,
1591
- configurable: true,
1592
- writable: false,
1593
- value: target.length
1594
- });
1595
- return contextWrapper;
1596
- }
1597
- bindAsyncGenerator(generator) {
1598
- generator.next = this.bindFunction(this.active(), generator.next);
1599
- return generator;
1547
+ return toSend;
1600
1548
  }
1601
1549
  };
1550
+ var isChatCompletionSpan = (value) => {
1551
+ return Boolean(
1552
+ value && typeof value == "object" && "name" in value && value.name === "ai.chatCompletion"
1553
+ );
1554
+ };
1555
+ var isPrompt2 = (value) => {
1556
+ return Boolean(
1557
+ value && typeof value == "object" && "schema" in value && "evaluators" in value
1558
+ );
1559
+ };
1602
1560
 
1603
- // src/tracing/index.ts
1604
- var aijsxTracing;
1605
- ((aijsxTracing2) => {
1606
- aijsxTracing2.api = tracing;
1607
- aijsxTracing2.AsyncLocalStorageContextManager = AsyncLocalStorageContextManager;
1608
- })(aijsxTracing || (aijsxTracing = {}));
1561
+ // src/tracing/Trace.tsx
1562
+ var Trace = ({ name, attributes, children }, { tracer, render }) => {
1563
+ const result = tracer.trace(name, attributes || {}, () => {
1564
+ return render(children);
1565
+ });
1566
+ return result;
1567
+ };
1609
1568
 
1610
1569
  // src/prompt/createPrompt.tsx
1611
1570
  var import_zod = require("zod");
@@ -1668,19 +1627,6 @@ function createStreamChain(chain) {
1668
1627
  };
1669
1628
  }
1670
1629
 
1671
- // src/prompt/evaluatePrompt.ts
1672
- async function evaluatePrompt(prompt, variables, result) {
1673
- const parsed = parseVariables(prompt.schema, variables);
1674
- if (!parsed.success) {
1675
- throw parsed.error;
1676
- }
1677
- return (await Promise.all(
1678
- prompt.evaluators.map(
1679
- (evaluator) => evaluator(parsed.result, result)
1680
- )
1681
- )).flat();
1682
- }
1683
-
1684
1630
  // src/lib/openai/OpenAI.tsx
1685
1631
  var import_openai = require("openai");
1686
1632
 
@@ -1777,13 +1723,6 @@ var renderChatMessageContent = (content) => {
1777
1723
  };
1778
1724
 
1779
1725
  // src/utils.ts
1780
- var import_nanoid2 = require("nanoid");
1781
- var uuid = {
1782
- renderId: () => (0, import_nanoid2.nanoid)(),
1783
- spanId: () => (0, import_nanoid2.nanoid)(),
1784
- traceId: () => (0, import_nanoid2.nanoid)(),
1785
- requestId: () => (0, import_nanoid2.nanoid)()
1786
- };
1787
1726
  function getEnvVar(name, shouldThrow = true) {
1788
1727
  let env = globalThis.process?.env ?? void 0;
1789
1728
  if (env === void 0) {
@@ -1806,15 +1745,19 @@ var OpenAIClientContext = createContext(() => {
1806
1745
  return defaultClient;
1807
1746
  }
1808
1747
  const apiKey = getEnvVar("OPENAI_API_KEY", true);
1809
- defaultClient = new import_openai.OpenAI({ apiKey });
1748
+ const client = new import_openai.OpenAI({ apiKey });
1749
+ defaultClient = {
1750
+ client,
1751
+ provider: "openai"
1752
+ };
1810
1753
  return defaultClient;
1811
1754
  });
1812
1755
  function buildOpenAIMessages(childrenXml) {
1813
1756
  const messages = [];
1814
- const chatMessageTags2 = ["UserMessage", "AssistantMessage", "SystemMessage"];
1815
- const parsed = parseXml(childrenXml).collapse(chatMessageTags2);
1757
+ const chatMessageTags = ["UserMessage", "AssistantMessage", "SystemMessage"];
1758
+ const parsed = parseXml(childrenXml).collapse(chatMessageTags);
1816
1759
  const topLevelValid = parsed.childNodes.every(
1817
- (node) => chatMessageTags2.includes(node.nodeName)
1760
+ (node) => chatMessageTags.includes(node.nodeName)
1818
1761
  );
1819
1762
  if (!topLevelValid) {
1820
1763
  throw new Error("Invalid top level chat message tags");
@@ -1839,105 +1782,111 @@ function buildOpenAIMessages(childrenXml) {
1839
1782
  }
1840
1783
  return messages;
1841
1784
  }
1842
- async function* OpenAIChatCompletion(props, { render, logger, getContext }) {
1843
- const span = tracing.getTracer().startSpan("ai.chatCompletion");
1844
- const requestId = uuid.requestId();
1845
- const perfStart = performance.now();
1846
- const startTime = (/* @__PURE__ */ new Date()).toISOString();
1847
- span.setAttributes({
1848
- requestId,
1849
- model: props.model,
1850
- provider: props.provider,
1851
- providerRegion: props.providerRegion
1852
- });
1853
- const client = getContext(OpenAIClientContext)();
1854
- if (!client) {
1855
- throw new Error("[OpenAI] must supply OpenAI model via context");
1856
- }
1857
- const openAIMessages = buildOpenAIMessages(
1858
- await render(props.children, {
1859
- preserveTags: true
1860
- })
1861
- );
1862
- const renderedMessages = openAIMessages.map((message) => {
1863
- return {
1864
- role: message.role,
1865
- content: renderChatMessageContent(message.content),
1866
- tokens: tokenCountForOpenAIMessage(message)
1867
- };
1868
- });
1869
- const chatCompletionRequest = {
1870
- model: props.model,
1871
- max_tokens: props.maxTokens,
1872
- temperature: props.temperature,
1873
- messages: openAIMessages,
1874
- response_format: props.responseFormat ? {
1875
- type: props.responseFormat
1876
- } : void 0,
1877
- stream: true
1878
- };
1879
- const logRequestData = {
1880
- requestId,
1881
- startTime,
1882
- model: props.model,
1883
- provider: props.provider,
1884
- providerRegion: props.providerRegion,
1885
- inputMessages: renderedMessages,
1886
- request: chatCompletionRequest
1887
- };
1888
- logger.chatCompletionRequest("openai", logRequestData);
1889
- span.setAttributes({
1890
- inputMessages: renderedMessages,
1891
- request: chatCompletionRequest
1892
- });
1893
- let chatResponse;
1894
- try {
1895
- chatResponse = await client.chat.completions.create(chatCompletionRequest);
1896
- } catch (ex) {
1897
- const error = ex instanceof import_openai.OpenAI.APIError ? new ChatCompletionError(
1898
- `OpenAIClient.APIError: ${ex.message}`,
1899
- span.attributes
1900
- ) : new ChatCompletionError(ex.message, span.attributes);
1901
- span.recordException(error).end();
1902
- throw error;
1903
- }
1904
- let finishReason = void 0;
1905
- let content = "";
1906
- for await (const message of chatResponse) {
1907
- if (!message.choices || !message.choices[0]) {
1908
- continue;
1909
- }
1910
- const delta = message.choices[0].delta;
1911
- if (message.choices[0].finish_reason) {
1912
- finishReason = message.choices[0].finish_reason;
1913
- }
1914
- if (delta.content) {
1915
- content += delta.content;
1916
- yield delta.content;
1785
+ function OpenAIChatCompletion(props, { logger, render, tracer, getContext }) {
1786
+ const startTime = performance.now();
1787
+ return tracer.trace(
1788
+ "ai.chatCompletion",
1789
+ {},
1790
+ async function* OpenAIChatCompletionInner(span) {
1791
+ const { client, provider, providerRegion, costFn } = getContext(OpenAIClientContext)();
1792
+ if (!client) {
1793
+ throw new Error("[OpenAI] must supply OpenAI model via context");
1794
+ }
1795
+ const openAIMessages = buildOpenAIMessages(
1796
+ await render(props.children, {
1797
+ preserveTags: true
1798
+ })
1799
+ );
1800
+ const renderedMessages = openAIMessages.map((message) => {
1801
+ return {
1802
+ role: message.role,
1803
+ content: renderChatMessageContent(message.content),
1804
+ tokens: tokenCountForOpenAIMessage(message)
1805
+ };
1806
+ });
1807
+ const chatCompletionRequest = {
1808
+ model: props.model,
1809
+ max_tokens: props.maxTokens,
1810
+ temperature: props.temperature,
1811
+ messages: openAIMessages,
1812
+ response_format: props.responseFormat ? {
1813
+ type: props.responseFormat
1814
+ } : void 0,
1815
+ stream: true
1816
+ };
1817
+ const logRequestData = {
1818
+ startTime,
1819
+ model: props.model,
1820
+ provider,
1821
+ providerRegion,
1822
+ inputMessages: renderedMessages,
1823
+ request: chatCompletionRequest
1824
+ };
1825
+ logger.chatCompletionRequest("openai", logRequestData);
1826
+ span.setAttributes({
1827
+ model: props.model,
1828
+ provider,
1829
+ providerRegion,
1830
+ requestType: "openai",
1831
+ chatCompletionRequest
1832
+ });
1833
+ let chatResponse;
1834
+ try {
1835
+ chatResponse = await client.chat.completions.create(
1836
+ chatCompletionRequest
1837
+ );
1838
+ } catch (ex) {
1839
+ if (ex instanceof import_openai.OpenAI.APIError) {
1840
+ throw new ChatCompletionError(
1841
+ `OpenAIClient.APIError: ${ex.message}`,
1842
+ logRequestData
1843
+ );
1844
+ } else if (ex instanceof Error) {
1845
+ throw new ChatCompletionError(ex.message, logRequestData);
1846
+ }
1847
+ throw ex;
1848
+ }
1849
+ let finishReason = void 0;
1850
+ let content = "";
1851
+ for await (const message of chatResponse) {
1852
+ if (!message.choices || !message.choices[0]) {
1853
+ continue;
1854
+ }
1855
+ const delta = message.choices[0].delta;
1856
+ if (message.choices[0].finish_reason) {
1857
+ finishReason = message.choices[0].finish_reason;
1858
+ }
1859
+ if (delta.content) {
1860
+ content += delta.content;
1861
+ yield delta.content;
1862
+ }
1863
+ }
1864
+ const outputMessage = {
1865
+ role: "assistant",
1866
+ content,
1867
+ tokens: tokenCountForOpenAIMessage({
1868
+ role: "assistant",
1869
+ content
1870
+ })
1871
+ };
1872
+ const tokensUsed = computeUsage([...renderedMessages, outputMessage]);
1873
+ const cost = costFn?.(tokensUsed) ?? void 0;
1874
+ const responseData = {
1875
+ ...logRequestData,
1876
+ finishReason,
1877
+ latency: performance.now() - startTime,
1878
+ outputMessage,
1879
+ tokensUsed
1880
+ };
1881
+ logger.chatCompletionResponse("openai", responseData);
1882
+ span.setAttributes({
1883
+ tokensUsed,
1884
+ output: content,
1885
+ finishReason,
1886
+ cost
1887
+ });
1917
1888
  }
1918
- }
1919
- const outputMessage = {
1920
- role: "assistant",
1921
- content,
1922
- tokens: tokenCountForOpenAIMessage({
1923
- role: "assistant",
1924
- content
1925
- })
1926
- };
1927
- const responseData = {
1928
- ...logRequestData,
1929
- endTime: (/* @__PURE__ */ new Date()).toISOString(),
1930
- finishReason,
1931
- latency: performance.now() - perfStart,
1932
- outputMessage,
1933
- tokensUsed: computeUsage([...renderedMessages, outputMessage])
1934
- };
1935
- logger.chatCompletionResponse("openai", responseData);
1936
- span.setAttributes({
1937
- outputMessage,
1938
- finishReason,
1939
- tokensUsed: computeUsage([...renderedMessages, outputMessage])
1940
- }).end();
1889
+ );
1941
1890
  }
1942
1891
 
1943
1892
  // src/lib/openai/OpenAIVision.tsx
@@ -1948,15 +1897,15 @@ var ContentTypeImage = (_props) => {
1948
1897
  };
1949
1898
  function buildOpenAIVisionChatMessages(childrenXml) {
1950
1899
  const messages = [];
1951
- const chatMessageTags2 = [
1900
+ const chatMessageTags = [
1952
1901
  "UserMessage",
1953
1902
  "AssistantMessage",
1954
1903
  "SystemMessage",
1955
1904
  "ContentTypeImage"
1956
1905
  ];
1957
- const parsed = parseXml(childrenXml).collapse(chatMessageTags2);
1906
+ const parsed = parseXml(childrenXml).collapse(chatMessageTags);
1958
1907
  const topLevelValid = parsed.childNodes.every(
1959
- (node) => chatMessageTags2.includes(node.nodeName)
1908
+ (node) => chatMessageTags.includes(node.nodeName)
1960
1909
  );
1961
1910
  if (!topLevelValid) {
1962
1911
  throw new Error("Invalid top level chat message tags");
@@ -2003,148 +1952,154 @@ function buildOpenAIVisionChatMessages(childrenXml) {
2003
1952
  }
2004
1953
  return { messages, dimensions };
2005
1954
  }
2006
- async function* OpenAIVisionChatCompletion(props, { render, getContext, logger }) {
2007
- const span = tracing.getTracer().startSpan("ai.chatCompletion");
2008
- const requestId = uuid.requestId();
2009
- const perfStart = performance.now();
2010
- const startTime = (/* @__PURE__ */ new Date()).toISOString();
1955
+ function OpenAIVisionChatCompletion(props, { logger, render, tracer, getContext }) {
1956
+ const startTime = performance.now();
2011
1957
  const model = props.model || DEFAULT_MODEL;
2012
- span.setAttributes({
2013
- requestId,
2014
- model,
2015
- provider: props.provider,
2016
- providerRegion: props.providerRegion
2017
- });
2018
- const client = getContext(OpenAIClientContext)();
2019
- if (!client) {
2020
- throw new Error("[OpenAI] must supply OpenAI model via context");
2021
- }
2022
- const { messages: openAIMessages, dimensions } = buildOpenAIVisionChatMessages(
2023
- await render(props.children, {
2024
- preserveTags: true,
2025
- renderedProps: {
2026
- ContentTypeImage: {
2027
- url: true,
2028
- dimensions: true,
2029
- detail: true
2030
- }
1958
+ return tracer.trace(
1959
+ "ai.chatCompletion",
1960
+ {},
1961
+ async function* OpenAIVIsionChatCompletionInner(span) {
1962
+ const { client, provider, providerRegion, costFn } = getContext(OpenAIClientContext)();
1963
+ if (!client) {
1964
+ throw new Error("[OpenAI] must supply OpenAI model via context");
2031
1965
  }
2032
- })
2033
- );
2034
- const renderedMessages = openAIMessages.map((message) => {
2035
- if (message.role === "user") {
2036
- if (typeof message.content === "string") {
1966
+ const { messages: openAIMessages, dimensions } = buildOpenAIVisionChatMessages(
1967
+ await render(props.children, {
1968
+ preserveTags: true,
1969
+ renderedProps: {
1970
+ ContentTypeImage: {
1971
+ url: true,
1972
+ dimensions: true,
1973
+ detail: true
1974
+ }
1975
+ }
1976
+ })
1977
+ );
1978
+ const renderedMessages = openAIMessages.map((message) => {
1979
+ if (message.role === "user") {
1980
+ if (typeof message.content === "string") {
1981
+ return {
1982
+ role: message.role,
1983
+ content: message.content,
1984
+ tokens: tokenCountForOpenAIMessage(message)
1985
+ };
1986
+ }
1987
+ const BASE_COST = 85;
1988
+ const tokens = message.content.reduce((acc, part) => {
1989
+ if (part.type === "text") {
1990
+ return acc + tokenCountForOpenAIMessage({
1991
+ role: message.role,
1992
+ content: part.text
1993
+ });
1994
+ }
1995
+ const detail = part.image_url.detail || "auto";
1996
+ if (detail === "low") {
1997
+ return acc + BASE_COST;
1998
+ } else if (detail === "high") {
1999
+ const dim = dimensions.get(part);
2000
+ if (!dim) {
2001
+ return acc + (170 * 4 + BASE_COST);
2002
+ }
2003
+ const area = dim.width * dim.height;
2004
+ const num512Images = area / (512 * 512);
2005
+ const highCost = num512Images * 170;
2006
+ return acc + highCost + BASE_COST;
2007
+ } else {
2008
+ return acc + (170 * 4 + BASE_COST);
2009
+ }
2010
+ }, 0);
2011
+ return {
2012
+ role: message.role,
2013
+ content: renderChatMessageContent(message.content),
2014
+ tokens
2015
+ };
2016
+ }
2037
2017
  return {
2038
2018
  role: message.role,
2039
- content: message.content,
2019
+ content: renderChatMessageContent(message.content),
2040
2020
  tokens: tokenCountForOpenAIMessage(message)
2041
2021
  };
2022
+ });
2023
+ const chatCompletionRequest = {
2024
+ model,
2025
+ max_tokens: props.maxTokens,
2026
+ temperature: props.temperature,
2027
+ messages: openAIMessages,
2028
+ stream: true
2029
+ };
2030
+ const logRequestData = {
2031
+ startTime,
2032
+ model,
2033
+ provider,
2034
+ providerRegion,
2035
+ inputMessages: renderedMessages,
2036
+ request: chatCompletionRequest
2037
+ };
2038
+ logger.chatCompletionRequest("openai", logRequestData);
2039
+ span.setAttributes({
2040
+ model: props.model,
2041
+ provider,
2042
+ providerRegion,
2043
+ requestType: "openai",
2044
+ chatCompletionRequest
2045
+ });
2046
+ let chatResponse;
2047
+ try {
2048
+ chatResponse = await client.chat.completions.create(
2049
+ chatCompletionRequest
2050
+ );
2051
+ } catch (ex) {
2052
+ if (ex instanceof import_openai2.OpenAI.APIError) {
2053
+ throw new ChatCompletionError(
2054
+ `OpenAIClient.APIError: ${ex.message}`,
2055
+ logRequestData
2056
+ );
2057
+ } else if (ex instanceof Error) {
2058
+ throw new ChatCompletionError(ex.message, logRequestData);
2059
+ }
2060
+ throw ex;
2042
2061
  }
2043
- const BASE_COST = 85;
2044
- const tokens = message.content.reduce((acc, part) => {
2045
- if (part.type === "text") {
2046
- return acc + tokenCountForOpenAIMessage({
2047
- role: message.role,
2048
- content: part.text
2049
- });
2062
+ let finishReason = void 0;
2063
+ let content = "";
2064
+ for await (const message of chatResponse) {
2065
+ if (!message.choices || !message.choices[0]) {
2066
+ continue;
2050
2067
  }
2051
- const detail = part.image_url.detail || "auto";
2052
- if (detail === "low") {
2053
- return acc + BASE_COST;
2054
- } else if (detail === "high") {
2055
- const dim = dimensions.get(part);
2056
- if (!dim) {
2057
- return acc + (170 * 4 + BASE_COST);
2058
- }
2059
- const area = dim.width * dim.height;
2060
- const num512Images = area / (512 * 512);
2061
- const highCost = num512Images * 170;
2062
- return acc + highCost + BASE_COST;
2063
- } else {
2064
- return acc + (170 * 4 + BASE_COST);
2068
+ const delta = message.choices[0].delta;
2069
+ if (message.choices[0].finish_reason) {
2070
+ finishReason = message.choices[0].finish_reason;
2065
2071
  }
2066
- }, 0);
2067
- return {
2068
- role: message.role,
2069
- content: renderChatMessageContent(message.content),
2070
- tokens
2072
+ if (delta.content) {
2073
+ content += delta.content;
2074
+ yield delta.content;
2075
+ }
2076
+ }
2077
+ const outputMessage = {
2078
+ role: "assistant",
2079
+ content,
2080
+ tokens: tokenCountForOpenAIMessage({
2081
+ role: "assistant",
2082
+ content
2083
+ })
2071
2084
  };
2085
+ const tokensUsed = computeUsage([...renderedMessages, outputMessage]);
2086
+ const cost = costFn?.(tokensUsed) ?? void 0;
2087
+ const responseData = {
2088
+ ...logRequestData,
2089
+ finishReason,
2090
+ latency: performance.now() - startTime,
2091
+ outputMessage,
2092
+ tokensUsed
2093
+ };
2094
+ logger.chatCompletionResponse("openai", responseData);
2095
+ span.setAttributes({
2096
+ tokensUsed,
2097
+ output: content,
2098
+ cost,
2099
+ finishReason
2100
+ });
2072
2101
  }
2073
- return {
2074
- role: message.role,
2075
- content: renderChatMessageContent(message.content),
2076
- tokens: tokenCountForOpenAIMessage(message)
2077
- };
2078
- });
2079
- const chatCompletionRequest = {
2080
- model,
2081
- max_tokens: props.maxTokens,
2082
- temperature: props.temperature,
2083
- messages: openAIMessages,
2084
- stream: true
2085
- };
2086
- const logRequestData = {
2087
- requestId,
2088
- startTime,
2089
- model,
2090
- provider: props.provider,
2091
- providerRegion: props.providerRegion,
2092
- inputMessages: renderedMessages,
2093
- request: chatCompletionRequest
2094
- };
2095
- logger.chatCompletionRequest("openai", logRequestData);
2096
- span.setAttributes({
2097
- inputMessages: renderedMessages,
2098
- request: chatCompletionRequest
2099
- });
2100
- let chatResponse;
2101
- try {
2102
- chatResponse = await client.chat.completions.create(chatCompletionRequest);
2103
- } catch (ex) {
2104
- const error = ex instanceof import_openai2.OpenAI.APIError ? new ChatCompletionError(
2105
- `OpenAIClient.APIError: ${ex.message}`,
2106
- span.attributes
2107
- ) : new ChatCompletionError(ex.message, span.attributes);
2108
- span.recordException(error).end();
2109
- throw error;
2110
- }
2111
- let finishReason = void 0;
2112
- let content = "";
2113
- for await (const message of chatResponse) {
2114
- if (!message.choices || !message.choices[0]) {
2115
- continue;
2116
- }
2117
- const delta = message.choices[0].delta;
2118
- if (message.choices[0].finish_reason) {
2119
- finishReason = message.choices[0].finish_reason;
2120
- }
2121
- if (delta.content) {
2122
- content += delta.content;
2123
- yield delta.content;
2124
- }
2125
- }
2126
- const outputMessage = {
2127
- role: "assistant",
2128
- content,
2129
- tokens: tokenCountForOpenAIMessage({
2130
- role: "assistant",
2131
- content
2132
- })
2133
- };
2134
- const responseData = {
2135
- ...logRequestData,
2136
- endTime: (/* @__PURE__ */ new Date()).toISOString(),
2137
- finishReason,
2138
- latency: performance.now() - perfStart,
2139
- outputMessage,
2140
- tokensUsed: computeUsage([...renderedMessages, outputMessage])
2141
- };
2142
- logger.chatCompletionResponse("openai", responseData);
2143
- span.setAttributes({
2144
- outputMessage,
2145
- finishReason,
2146
- tokensUsed: computeUsage([...renderedMessages, outputMessage])
2147
- }).end();
2102
+ );
2148
2103
  }
2149
2104
 
2150
2105
  // src/lib/openai/index.ts
@@ -2154,21 +2109,29 @@ var import_openai3 = require("openai");
2154
2109
  var import_sdk = __toESM(require("@anthropic-ai/sdk"));
2155
2110
  var import_tokenizer3 = require("@anthropic-ai/tokenizer");
2156
2111
  var defaultClient2 = null;
2157
- var AnthropicClientContext = createContext(
2158
- () => {
2159
- if (defaultClient2) {
2160
- return defaultClient2;
2161
- }
2162
- defaultClient2 = new import_sdk.default({
2163
- apiKey: getEnvVar("ANTHROPIC_API_KEY", false)
2164
- });
2112
+ var AnthropicClientContext = createContext(() => {
2113
+ if (defaultClient2) {
2165
2114
  return defaultClient2;
2166
2115
  }
2167
- );
2116
+ const client = new import_sdk.default({
2117
+ apiKey: getEnvVar("ANTHROPIC_API_KEY", false)
2118
+ });
2119
+ defaultClient2 = {
2120
+ client,
2121
+ provider: "anthropic"
2122
+ };
2123
+ return defaultClient2;
2124
+ });
2168
2125
  var defaultMaxTokens = 4096;
2169
- var chatMessageTags = ["UserMessage", "AssistantMessage", "SystemMessage"];
2170
- function buildChatMessages(childrenXml) {
2126
+ function buildAnthropicMessages(childrenXml) {
2127
+ let system = "";
2171
2128
  const messages = [];
2129
+ const chatMessageTags = [
2130
+ "UserMessage",
2131
+ "AssistantMessage",
2132
+ "SystemMessage",
2133
+ "ClaudeImageBlockParam"
2134
+ ];
2172
2135
  const parsed = parseXml(childrenXml).collapse(chatMessageTags);
2173
2136
  const topLevelValid = parsed.childNodes.every(
2174
2137
  (node) => chatMessageTags.includes(node.nodeName)
@@ -2178,126 +2141,240 @@ function buildChatMessages(childrenXml) {
2178
2141
  }
2179
2142
  for (const node of parsed.childNodes) {
2180
2143
  if (node.nodeName === "UserMessage") {
2181
- const content = `${import_sdk.default.HUMAN_PROMPT} ${node.textContent}`;
2144
+ if (node.childNodes?.length === 1 && node.childNodes[0].nodeName === "#text") {
2145
+ messages.push({
2146
+ content: node.childNodes[0].value,
2147
+ role: "user"
2148
+ });
2149
+ continue;
2150
+ }
2151
+ const parts = node.childNodes.map((n) => {
2152
+ if (n.nodeName === "#text") {
2153
+ return {
2154
+ type: "text",
2155
+ text: n.value
2156
+ };
2157
+ } else if (n.nodeName === "ClaudeImageBlockParam") {
2158
+ const imagePart = {
2159
+ type: "image",
2160
+ source: {
2161
+ type: "base64",
2162
+ media_type: n.attributes.mediaType,
2163
+ data: n.attributes.data
2164
+ }
2165
+ };
2166
+ return imagePart;
2167
+ }
2168
+ throw new Error(
2169
+ "Invalid ChatCompletionContentPart, expecting text or ContentTypeImage"
2170
+ );
2171
+ });
2182
2172
  messages.push({
2183
- role: "user",
2184
- content,
2185
- tokens: (0, import_tokenizer3.countTokens)(content)
2173
+ content: parts,
2174
+ role: "user"
2186
2175
  });
2187
2176
  } else if (node.nodeName === "AssistantMessage") {
2188
- const content = `${import_sdk.default.AI_PROMPT} ${node.textContent}`;
2189
2177
  messages.push({
2190
- role: "assistant",
2191
- content,
2192
- tokens: (0, import_tokenizer3.countTokens)(content)
2178
+ content: node.textContent,
2179
+ role: "assistant"
2193
2180
  });
2194
2181
  } else if (node.nodeName === "SystemMessage") {
2195
- const userContent = `${import_sdk.default.HUMAN_PROMPT} For subsequent replies you will adhere to the following instructions: ${node.textContent}`;
2196
- messages.push({
2197
- role: "user",
2198
- content: userContent,
2199
- tokens: (0, import_tokenizer3.countTokens)(userContent)
2182
+ system = node.textContent;
2183
+ }
2184
+ }
2185
+ return { messages, system };
2186
+ }
2187
+ function AnthropicChatCompletion(props, { render, logger, tracer, getContext }) {
2188
+ const startTime = performance.now();
2189
+ return tracer.trace(
2190
+ "ai.chatCompletion",
2191
+ {},
2192
+ async function* AnthropicChatCompletionInner(span) {
2193
+ const { client, provider, providerRegion, costFn } = getContext(
2194
+ AnthropicClientContext
2195
+ )();
2196
+ if (!client) {
2197
+ throw new Error(
2198
+ "[AnthropicChatCompletion] must supply AnthropicClient via context"
2199
+ );
2200
+ }
2201
+ const { system, messages } = buildAnthropicMessages(
2202
+ await render(props.children, {
2203
+ preserveTags: true,
2204
+ renderedProps: {
2205
+ ClaudeImageBlockParam: {
2206
+ data: true,
2207
+ mediaType: true
2208
+ }
2209
+ }
2210
+ })
2211
+ );
2212
+ const inputMessages = getRenderedMessages(system, messages);
2213
+ const anthropicCompletionRequest = {
2214
+ system,
2215
+ messages,
2216
+ max_tokens: props.maxTokens ?? defaultMaxTokens,
2217
+ temperature: props.temperature,
2218
+ model: props.model
2219
+ };
2220
+ const logRequestData = {
2221
+ startTime,
2222
+ model: props.model,
2223
+ provider,
2224
+ providerRegion,
2225
+ inputMessages,
2226
+ request: anthropicCompletionRequest
2227
+ };
2228
+ logger.chatCompletionRequest("anthropic", logRequestData);
2229
+ span.setAttributes({
2230
+ model: props.model,
2231
+ provider,
2232
+ providerRegion,
2233
+ requestType: "anthropic",
2234
+ chatCompletionRequest: anthropicCompletionRequest
2200
2235
  });
2201
- const assistantContent = `${import_sdk.default.AI_PROMPT} Okay, I will do that.`;
2202
- messages.push({
2236
+ let response;
2237
+ try {
2238
+ response = client.messages.stream(anthropicCompletionRequest);
2239
+ } catch (err) {
2240
+ if (err instanceof import_sdk.default.APIError) {
2241
+ throw new ChatCompletionError(
2242
+ `AnthropicClient.APIError: ${err.message}`,
2243
+ logRequestData
2244
+ );
2245
+ } else if (err instanceof Error) {
2246
+ throw new ChatCompletionError(err.message, logRequestData);
2247
+ }
2248
+ throw err;
2249
+ }
2250
+ let content = "";
2251
+ let outputUsage = 0;
2252
+ let inputUsage = 0;
2253
+ let finishReason = null;
2254
+ for await (const event of response) {
2255
+ if (event.type === "message_start") {
2256
+ inputUsage = event.message.usage?.input_tokens || 0;
2257
+ }
2258
+ if (event.type === "content_block_delta") {
2259
+ const chunk = event.delta.text;
2260
+ content += chunk;
2261
+ yield chunk;
2262
+ }
2263
+ if (event.type === "message_delta") {
2264
+ finishReason = event.delta.stop_reason;
2265
+ outputUsage = event.usage?.output_tokens;
2266
+ }
2267
+ }
2268
+ const outputMessage = {
2203
2269
  role: "assistant",
2204
- content: assistantContent,
2205
- tokens: (0, import_tokenizer3.countTokens)(assistantContent)
2270
+ content,
2271
+ tokens: outputUsage
2272
+ };
2273
+ const userMessage = inputMessages.find(
2274
+ (m) => m.role === "user" && m.tokens === 0
2275
+ );
2276
+ const restMessages = inputMessages.filter(
2277
+ (m) => m.role !== "assistant" && m.tokens > 0
2278
+ );
2279
+ const restMessagesTokens = restMessages.reduce(
2280
+ (acc, m) => acc + m.tokens,
2281
+ 0
2282
+ );
2283
+ if (userMessage && inputUsage) {
2284
+ userMessage.tokens = inputUsage - restMessagesTokens;
2285
+ }
2286
+ const tokensUsed = computeUsage([...inputMessages, outputMessage]);
2287
+ const cost = costFn?.(tokensUsed) ?? void 0;
2288
+ const responseData = {
2289
+ ...logRequestData,
2290
+ finishReason,
2291
+ latency: performance.now() - startTime,
2292
+ // NOTE(jordan) we dont REALLY need inputMessages, they are this kind of intermediary that we use
2293
+ // to store the token values for the messages, but we could easily just compute the tokensUsed and call
2294
+ // it a day
2295
+ inputMessages,
2296
+ outputMessage,
2297
+ tokensUsed
2298
+ };
2299
+ logger.chatCompletionResponse("anthropic", responseData);
2300
+ span.setAttributes({
2301
+ tokensUsed,
2302
+ output: content,
2303
+ cost,
2304
+ finishReason
2206
2305
  });
2207
2306
  }
2208
- }
2209
- return messages;
2210
- }
2211
- async function* AnthropicChatCompletion(props, { render, getContext, logger }) {
2212
- const span = tracing.getTracer().startSpan("ai.chatCompletion");
2213
- const requestId = uuid.requestId();
2214
- const perfStart = performance.now();
2215
- const startTime = (/* @__PURE__ */ new Date()).toISOString();
2216
- span.setAttributes({
2217
- requestId,
2218
- model: props.model,
2219
- provider: props.provider,
2220
- providerRegion: props.providerRegion
2221
- });
2222
- const client = getContext(AnthropicClientContext)();
2223
- if (!client) {
2224
- throw new Error(
2225
- "[AnthropicChatCompletion] must supply AnthropicClient via context"
2226
- );
2227
- }
2228
- const inputMessages = buildChatMessages(
2229
- await render(props.children, {
2230
- preserveTags: true
2231
- })
2232
2307
  );
2233
- const prompt = [
2234
- ...inputMessages.map((message) => message.content),
2235
- import_sdk.default.AI_PROMPT
2236
- ].join("");
2237
- const anthropicCompletionRequest = {
2238
- prompt,
2239
- max_tokens_to_sample: props.maxTokens ?? defaultMaxTokens,
2240
- temperature: props.temperature,
2241
- model: props.model,
2242
- stream: true
2243
- };
2244
- const logRequestData = {
2245
- requestId,
2246
- startTime,
2247
- model: props.model,
2248
- provider: props.provider,
2249
- providerRegion: props.providerRegion,
2250
- inputMessages,
2251
- request: anthropicCompletionRequest
2308
+ }
2309
+ function getRenderedMessages(system, messages) {
2310
+ const systemMessage = {
2311
+ role: "system",
2312
+ content: system,
2313
+ tokens: (0, import_tokenizer3.countTokens)(system)
2252
2314
  };
2253
- logger.chatCompletionRequest("anthropic", logRequestData);
2254
- span.setAttributes({
2255
- inputMessages,
2256
- request: anthropicCompletionRequest
2315
+ const renderedMessages = messages.map((message) => {
2316
+ if (message.role === "user") {
2317
+ return {
2318
+ role: message.role,
2319
+ content: renderChatMessageContent2(message.content),
2320
+ // we keep user message tokens 0 and just let anthropic tell us
2321
+ tokens: 0
2322
+ };
2323
+ }
2324
+ return {
2325
+ role: message.role,
2326
+ content: renderChatMessageContent2(message.content),
2327
+ tokens: (0, import_tokenizer3.countTokens)(message.content)
2328
+ };
2257
2329
  });
2258
- let response;
2259
- try {
2260
- response = await client.completions.create(anthropicCompletionRequest);
2261
- } catch (ex) {
2262
- const error = ex instanceof import_sdk.default.APIError ? new ChatCompletionError(
2263
- `AnthropicClient.APIError: ${ex.message}`,
2264
- span.attributes
2265
- ) : new ChatCompletionError(ex.message, span.attributes);
2266
- span.recordException(error).end();
2267
- throw error;
2330
+ return [systemMessage, ...renderedMessages];
2331
+ }
2332
+ var renderChatMessageContent2 = (content) => {
2333
+ if (content == null) {
2334
+ return "";
2268
2335
  }
2269
- let content = "";
2270
- let isFirstResponse = true;
2271
- for await (const completion of response) {
2272
- let text = completion.completion;
2273
- if (isFirstResponse && text.length > 0) {
2274
- isFirstResponse = false;
2275
- if (text.startsWith(" ")) {
2276
- text = text.slice(1);
2277
- }
2278
- }
2279
- content += text;
2280
- yield text;
2336
+ if (typeof content === "string") {
2337
+ return content;
2281
2338
  }
2282
- const outputMessage = {
2283
- role: "assistant",
2284
- content,
2285
- tokens: (0, import_tokenizer3.countTokens)(content)
2286
- };
2287
- const responseData = {
2288
- ...logRequestData,
2289
- endTime: (/* @__PURE__ */ new Date()).toISOString(),
2290
- finishReason: "stop",
2291
- latency: performance.now() - perfStart,
2292
- outputMessage,
2293
- tokensUsed: computeUsage([...inputMessages, outputMessage])
2339
+ return content.map((part) => {
2340
+ if (typeof part === "string") {
2341
+ return part;
2342
+ } else if ("text" in part) {
2343
+ return part.text;
2344
+ } else if ("source" in part && typeof part.source === "object") {
2345
+ return `<ImageBlockParam data="base64..." />`;
2346
+ }
2347
+ throw new Error("Invalid ImageBlockParam type");
2348
+ }).join("\n\n");
2349
+ };
2350
+
2351
+ // src/lib/anthropic/ClaudeImageBlock.tsx
2352
+ var ClaudeImageBlockParam = (_props) => {
2353
+ return null;
2354
+ };
2355
+ async function fetchImageAndConvertToBase64(url) {
2356
+ const response = await fetch(url);
2357
+ const contentType = response.headers.get("content-type");
2358
+ const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
2359
+ if (!contentType || !allowedTypes.includes(contentType)) {
2360
+ throw new Error(`Unsupported media type: ${contentType}`);
2361
+ }
2362
+ const blob = await response.blob();
2363
+ const buffer = Buffer.from(await blob.arrayBuffer());
2364
+ const base64 = buffer.toString("base64");
2365
+ return {
2366
+ base64,
2367
+ mediaType: contentType
2294
2368
  };
2295
- logger.chatCompletionResponse("anthropic", responseData);
2296
- span.setAttributes({
2297
- outputMessage,
2298
- tokensUsed: computeUsage([...inputMessages, outputMessage])
2299
- }).end();
2300
2369
  }
2370
+ var ClaudeImageBlock = async (props) => {
2371
+ if ("data" in props) {
2372
+ return /* @__PURE__ */ jsx(ClaudeImageBlockParam, { ...props });
2373
+ }
2374
+ const { url } = props;
2375
+ const { base64, mediaType } = await fetchImageAndConvertToBase64(url);
2376
+ return /* @__PURE__ */ jsx(ClaudeImageBlockParam, { mediaType, data: base64 });
2377
+ };
2301
2378
 
2302
2379
  // src/lib/anthropic/index.ts
2303
2380
  var import_sdk2 = __toESM(require("@anthropic-ai/sdk"));
@@ -2305,16 +2382,18 @@ var import_tokenizer4 = require("@anthropic-ai/tokenizer");
2305
2382
  // Annotate the CommonJS export names for ESM import in node:
2306
2383
  0 && (module.exports = {
2307
2384
  AIFragment,
2385
+ AISpanProcessor,
2308
2386
  AnthropicChatCompletion,
2309
2387
  AnthropicClient,
2310
2388
  AnthropicClientContext,
2311
2389
  AssistantMessage,
2312
2390
  BoundLogger,
2313
- ChainParseVariablesError,
2314
2391
  ChatCompletionError,
2392
+ ClaudeImageBlock,
2315
2393
  CombinedLogger,
2316
2394
  ConsoleLogger,
2317
2395
  ContentTypeImage,
2396
+ EnrichingSpanProcessor,
2318
2397
  LogImplementation,
2319
2398
  NoopLogImplementation,
2320
2399
  OpenAIChatCompletion,
@@ -2323,10 +2402,9 @@ var import_tokenizer4 = require("@anthropic-ai/tokenizer");
2323
2402
  OpenAIVisionChatCompletion,
2324
2403
  ParseVariablesError,
2325
2404
  PromptInvalidOutputError,
2326
- PromptParseVariablesError,
2327
2405
  SystemMessage,
2406
+ Trace,
2328
2407
  UserMessage,
2329
- aijsxTracing,
2330
2408
  attachedContextSymbol,
2331
2409
  computeUsage,
2332
2410
  countAnthropicTokens,
@@ -2336,10 +2414,10 @@ var import_tokenizer4 = require("@anthropic-ai/tokenizer");
2336
2414
  createPrompt,
2337
2415
  createRenderContext,
2338
2416
  createStreamChain,
2339
- defaultMaxTokens,
2340
2417
  evaluatePrompt,
2341
2418
  tokenCountForOpenAIMessage,
2342
2419
  tokenCountForOpenAIVisionMessage,
2343
2420
  tokenLimitForChatModel,
2344
- tokenizer
2421
+ tokenizer,
2422
+ tracing
2345
2423
  });