vibezcheck 0.3.0 → 0.4.0

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.
@@ -556,14 +556,26 @@ function getModelPricing(modelName) {
556
556
 
557
557
  // src/pricing/calculator.ts
558
558
  function calculateCost(params) {
559
- const rates = getModelPricing(params.model);
559
+ let rates;
560
+ if (params.customRate) {
561
+ const outRate = params.customRate.out ?? params.customRate.output ?? 0;
562
+ rates = {
563
+ inputPer1M: params.customRate.in,
564
+ outputPer1M: outRate,
565
+ reasoningPer1M: params.customRate.reasoning ?? outRate,
566
+ cachedInputPer1M: params.customRate.cached ?? params.customRate.in * 0.15,
567
+ currency: "USD"
568
+ };
569
+ } else {
570
+ rates = getModelPricing(params.model);
571
+ }
560
572
  const inputTokens = params.inputTokens ?? 0;
561
573
  const outputTokens = params.outputTokens ?? 0;
562
574
  const reasoningTokens = params.reasoningTokens ?? 0;
563
575
  const cachedTokens = params.cachedTokens ?? 0;
564
576
  const regularInputTokens = Math.max(0, inputTokens - cachedTokens);
565
577
  const regularInputCost = regularInputTokens / 1e6 * rates.inputPer1M;
566
- const cachedRate = rates.cachedInputPer1M ?? rates.inputPer1M * 0.5;
578
+ const cachedRate = rates.cachedInputPer1M ?? rates.inputPer1M * 0.15;
567
579
  const cachedInputCost = cachedTokens / 1e6 * cachedRate;
568
580
  const inputCostUSD = regularInputCost + cachedInputCost;
569
581
  const outputCostUSD = outputTokens / 1e6 * rates.outputPer1M;
@@ -571,20 +583,27 @@ function calculateCost(params) {
571
583
  const reasoningCostUSD = reasoningTokens / 1e6 * reasoningRate;
572
584
  const standardCacheCost = cachedTokens / 1e6 * rates.inputPer1M;
573
585
  const cachedDiscountUSD = Math.max(0, standardCacheCost - cachedInputCost);
574
- const totalUSD = inputCostUSD + outputCostUSD;
586
+ let totalUSD = inputCostUSD + outputCostUSD;
575
587
  const markup = params.markupMultiplier ?? 1;
576
- const retailUSD = markup !== 1 ? totalUSD * markup : void 0;
588
+ let retailUSD = void 0;
589
+ if (markup !== 1 || params.minimumChargeUSD !== void 0) {
590
+ retailUSD = totalUSD * markup;
591
+ if (params.minimumChargeUSD !== void 0 && retailUSD < params.minimumChargeUSD) {
592
+ retailUSD = params.minimumChargeUSD;
593
+ }
594
+ }
577
595
  return {
578
596
  inputCostUSD: Number(inputCostUSD.toFixed(8)),
579
597
  outputCostUSD: Number(outputCostUSD.toFixed(8)),
580
598
  reasoningCostUSD: reasoningTokens > 0 ? Number(reasoningCostUSD.toFixed(8)) : void 0,
581
599
  cachedDiscountUSD: cachedTokens > 0 ? Number(cachedDiscountUSD.toFixed(8)) : void 0,
582
600
  totalUSD: Number(totalUSD.toFixed(8)),
583
- retailUSD: retailUSD ? Number(retailUSD.toFixed(8)) : void 0,
601
+ retailUSD: retailUSD !== void 0 ? Number(retailUSD.toFixed(8)) : void 0,
584
602
  currency: rates.currency || "USD"
585
603
  };
586
604
  }
587
- function calculateUsageCost(model, usage, markupMultiplier) {
605
+ function calculateUsageCost(model, usage, optionsOrMarkup) {
606
+ const options = typeof optionsOrMarkup === "number" ? { markupMultiplier: optionsOrMarkup } : optionsOrMarkup || {};
588
607
  return calculateCost({
589
608
  model,
590
609
  inputTokens: usage.inputTokens,
@@ -592,7 +611,9 @@ function calculateUsageCost(model, usage, markupMultiplier) {
592
611
  reasoningTokens: usage.reasoningTokens,
593
612
  cachedTokens: usage.cachedTokens,
594
613
  cacheWriteTokens: usage.cacheWriteTokens,
595
- markupMultiplier
614
+ markupMultiplier: options.markupMultiplier,
615
+ minimumChargeUSD: options.minimumChargeUSD,
616
+ customRate: options.customRate
596
617
  });
597
618
  }
598
619
 
@@ -843,7 +864,22 @@ function withBilling(model, options = {}) {
843
864
  const customerId = typeof options.customer === "string" ? options.customer : options.customer?.id || options.customerId;
844
865
  const modelId = model.modelId || "unknown-model";
845
866
  const provider = model.provider?.replace(/^@ai-sdk\//, "") || "ai-sdk";
846
- const handleUsage = (rawUsage) => {
867
+ const maxCostPerCall = options.maxCostPerCallUSD !== void 0 ? options.maxCostPerCallUSD : 0.5;
868
+ const captureOnAbort = options.captureOnAbort !== false;
869
+ const scheduleFlush = () => {
870
+ try {
871
+ const flushPromise = meter.flush();
872
+ if (typeof globalThis !== "undefined") {
873
+ const g = globalThis;
874
+ if (typeof g.after === "function") {
875
+ g.after(() => flushPromise);
876
+ return;
877
+ }
878
+ }
879
+ } catch {
880
+ }
881
+ };
882
+ const handleUsage = (rawUsage, extraMeta) => {
847
883
  if (!rawUsage) return;
848
884
  const inputTokens = rawUsage.promptTokens ?? rawUsage.inputTokens ?? 0;
849
885
  const outputTokens = rawUsage.completionTokens ?? rawUsage.outputTokens ?? 0;
@@ -857,15 +893,19 @@ function withBilling(model, options = {}) {
857
893
  visibleOutputTokens: Math.max(0, outputTokens - reasoningTokens),
858
894
  cachedTokens: cachedTokens > 0 ? cachedTokens : void 0
859
895
  };
860
- const cost = calculateUsageCost(modelId, usage);
861
- if (options.maxCostPerCallUSD && cost.totalUSD > options.maxCostPerCallUSD) {
896
+ const cost = calculateUsageCost(modelId, usage, {
897
+ markupMultiplier: options.pricing?.margin,
898
+ minimumChargeUSD: options.pricing?.minimumChargeUSD,
899
+ customRate: options.rate
900
+ });
901
+ if (maxCostPerCall > 0 && maxCostPerCall !== Infinity && cost.totalUSD > maxCostPerCall) {
862
902
  const budgetEvent = {
863
903
  reason: "cost_per_call_exceeded",
864
- limit: options.maxCostPerCallUSD,
904
+ limit: maxCostPerCall,
865
905
  current: cost.totalUSD,
866
906
  model: modelId,
867
907
  customerId,
868
- message: `[vibezcheck] Circuit Breaker tripped: Call cost ($${cost.totalUSD.toFixed(4)}) exceeded maxCostPerCallUSD threshold ($${options.maxCostPerCallUSD.toFixed(4)}).`
908
+ message: `[vibezcheck] Circuit Breaker tripped: Call cost ($${cost.totalUSD.toFixed(4)}) exceeded budget ceiling ($${maxCostPerCall.toFixed(4)}).`
869
909
  };
870
910
  if (options.onBudgetExceeded) {
871
911
  options.onBudgetExceeded(budgetEvent);
@@ -897,7 +937,11 @@ function withBilling(model, options = {}) {
897
937
  usage,
898
938
  cost,
899
939
  customerId,
900
- metadata: options.metadata
940
+ metadata: {
941
+ ...options.metadata,
942
+ ...extraMeta,
943
+ billingMode: options.billing?.mode || "postpaid"
944
+ }
901
945
  };
902
946
  meter.recordUsage({
903
947
  model: modelId,
@@ -907,11 +951,12 @@ function withBilling(model, options = {}) {
907
951
  reasoningTokens: usage.reasoningTokens,
908
952
  cachedTokens: usage.cachedTokens,
909
953
  customerId,
910
- metadata: options.metadata
954
+ metadata: event.metadata
911
955
  });
912
956
  if (options.onUsage) {
913
957
  options.onUsage(event);
914
958
  }
959
+ scheduleFlush();
915
960
  };
916
961
  const handler = {
917
962
  get(target, prop, receiver) {
@@ -923,12 +968,59 @@ function withBilling(model, options = {}) {
923
968
  return result;
924
969
  }
925
970
  const originalStream = result.stream;
971
+ let streamCompleted = false;
972
+ let accumulatedChars = 0;
973
+ let estimatedInputTokens = 0;
974
+ try {
975
+ if (args[0]?.prompt) {
976
+ const str = typeof args[0].prompt === "string" ? args[0].prompt : JSON.stringify(args[0].prompt);
977
+ estimatedInputTokens = Math.ceil(str.length / 4);
978
+ }
979
+ } catch {
980
+ }
981
+ const abortSignal = args[0]?.abortSignal;
982
+ if (abortSignal && captureOnAbort) {
983
+ abortSignal.addEventListener(
984
+ "abort",
985
+ () => {
986
+ if (!streamCompleted && accumulatedChars > 0) {
987
+ streamCompleted = true;
988
+ const estimatedOutputTokens = Math.ceil(accumulatedChars / 3.8);
989
+ handleUsage(
990
+ {
991
+ inputTokens: estimatedInputTokens,
992
+ outputTokens: estimatedOutputTokens
993
+ },
994
+ { aborted: true, partial: true }
995
+ );
996
+ }
997
+ },
998
+ { once: true }
999
+ );
1000
+ }
926
1001
  const transformStream = new TransformStream({
927
1002
  transform(chunk, controller) {
928
1003
  controller.enqueue(chunk);
1004
+ if (chunk.type === "text-delta" && chunk.textDelta) {
1005
+ accumulatedChars += chunk.textDelta.length;
1006
+ }
929
1007
  if (chunk.type === "finish" && chunk.usage) {
1008
+ streamCompleted = true;
930
1009
  handleUsage(chunk.usage);
931
1010
  }
1011
+ },
1012
+ flush() {
1013
+ if (!streamCompleted && captureOnAbort && accumulatedChars > 0) {
1014
+ streamCompleted = true;
1015
+ const estimatedOutputTokens = Math.ceil(accumulatedChars / 3.8);
1016
+ handleUsage(
1017
+ {
1018
+ inputTokens: estimatedInputTokens,
1019
+ outputTokens: estimatedOutputTokens
1020
+ },
1021
+ { aborted: true, partial: true }
1022
+ );
1023
+ }
932
1024
  }
933
1025
  });
934
1026
  return {
@@ -1002,9 +1094,69 @@ function createVibezModel(modelOrId, options = {}) {
1002
1094
  }
1003
1095
  return modelOrId;
1004
1096
  }
1097
+ function createVibezSession(sessionOptions = {}) {
1098
+ const meter = createMeter({
1099
+ apiKey: sessionOptions.stripeApiKey
1100
+ });
1101
+ const customerId = typeof sessionOptions.customer === "string" ? sessionOptions.customer : sessionOptions.customer?.id;
1102
+ return {
1103
+ meter,
1104
+ model: (modelOrId, callOptions = {}) => {
1105
+ return createVibezModel(modelOrId, {
1106
+ customer: sessionOptions.customer,
1107
+ pricing: sessionOptions.pricing,
1108
+ billing: sessionOptions.billing,
1109
+ meter,
1110
+ metadata: {
1111
+ ...sessionOptions.metadata,
1112
+ ...callOptions.metadata
1113
+ },
1114
+ ...callOptions
1115
+ });
1116
+ },
1117
+ trackTool: async (name, { costUSD, metadata }) => {
1118
+ const event = {
1119
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1120
+ model: `tool:${name}`,
1121
+ provider: "tool",
1122
+ usage: {
1123
+ inputTokens: 0,
1124
+ outputTokens: 0,
1125
+ totalTokens: 0
1126
+ },
1127
+ cost: {
1128
+ inputCostUSD: 0,
1129
+ outputCostUSD: costUSD,
1130
+ totalUSD: costUSD,
1131
+ currency: "USD"
1132
+ },
1133
+ customerId,
1134
+ metadata: {
1135
+ ...sessionOptions.metadata,
1136
+ ...metadata,
1137
+ tool: name
1138
+ }
1139
+ };
1140
+ meter.recordUsage({
1141
+ model: `tool:${name}`,
1142
+ provider: "tool",
1143
+ inputTokens: 0,
1144
+ outputTokens: 0,
1145
+ customerId,
1146
+ metadata: event.metadata
1147
+ });
1148
+ await meter.flush();
1149
+ }
1150
+ };
1151
+ }
1152
+ var vibezcheck = Object.assign(createVibezModel, {
1153
+ session: createVibezSession
1154
+ });
1005
1155
  export {
1006
1156
  createVibezModel,
1157
+ createVibezSession,
1007
1158
  meteredModel,
1159
+ vibezcheck,
1008
1160
  withBilling
1009
1161
  };
1010
1162
  //# sourceMappingURL=index.mjs.map