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.
@@ -31,7 +31,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var ai_sdk_exports = {};
32
32
  __export(ai_sdk_exports, {
33
33
  createVibezModel: () => createVibezModel,
34
+ createVibezSession: () => createVibezSession,
34
35
  meteredModel: () => meteredModel,
36
+ vibezcheck: () => vibezcheck,
35
37
  withBilling: () => withBilling
36
38
  });
37
39
  module.exports = __toCommonJS(ai_sdk_exports);
@@ -587,14 +589,26 @@ function getModelPricing(modelName) {
587
589
 
588
590
  // src/pricing/calculator.ts
589
591
  function calculateCost(params) {
590
- const rates = getModelPricing(params.model);
592
+ let rates;
593
+ if (params.customRate) {
594
+ const outRate = params.customRate.out ?? params.customRate.output ?? 0;
595
+ rates = {
596
+ inputPer1M: params.customRate.in,
597
+ outputPer1M: outRate,
598
+ reasoningPer1M: params.customRate.reasoning ?? outRate,
599
+ cachedInputPer1M: params.customRate.cached ?? params.customRate.in * 0.15,
600
+ currency: "USD"
601
+ };
602
+ } else {
603
+ rates = getModelPricing(params.model);
604
+ }
591
605
  const inputTokens = params.inputTokens ?? 0;
592
606
  const outputTokens = params.outputTokens ?? 0;
593
607
  const reasoningTokens = params.reasoningTokens ?? 0;
594
608
  const cachedTokens = params.cachedTokens ?? 0;
595
609
  const regularInputTokens = Math.max(0, inputTokens - cachedTokens);
596
610
  const regularInputCost = regularInputTokens / 1e6 * rates.inputPer1M;
597
- const cachedRate = rates.cachedInputPer1M ?? rates.inputPer1M * 0.5;
611
+ const cachedRate = rates.cachedInputPer1M ?? rates.inputPer1M * 0.15;
598
612
  const cachedInputCost = cachedTokens / 1e6 * cachedRate;
599
613
  const inputCostUSD = regularInputCost + cachedInputCost;
600
614
  const outputCostUSD = outputTokens / 1e6 * rates.outputPer1M;
@@ -602,20 +616,27 @@ function calculateCost(params) {
602
616
  const reasoningCostUSD = reasoningTokens / 1e6 * reasoningRate;
603
617
  const standardCacheCost = cachedTokens / 1e6 * rates.inputPer1M;
604
618
  const cachedDiscountUSD = Math.max(0, standardCacheCost - cachedInputCost);
605
- const totalUSD = inputCostUSD + outputCostUSD;
619
+ let totalUSD = inputCostUSD + outputCostUSD;
606
620
  const markup = params.markupMultiplier ?? 1;
607
- const retailUSD = markup !== 1 ? totalUSD * markup : void 0;
621
+ let retailUSD = void 0;
622
+ if (markup !== 1 || params.minimumChargeUSD !== void 0) {
623
+ retailUSD = totalUSD * markup;
624
+ if (params.minimumChargeUSD !== void 0 && retailUSD < params.minimumChargeUSD) {
625
+ retailUSD = params.minimumChargeUSD;
626
+ }
627
+ }
608
628
  return {
609
629
  inputCostUSD: Number(inputCostUSD.toFixed(8)),
610
630
  outputCostUSD: Number(outputCostUSD.toFixed(8)),
611
631
  reasoningCostUSD: reasoningTokens > 0 ? Number(reasoningCostUSD.toFixed(8)) : void 0,
612
632
  cachedDiscountUSD: cachedTokens > 0 ? Number(cachedDiscountUSD.toFixed(8)) : void 0,
613
633
  totalUSD: Number(totalUSD.toFixed(8)),
614
- retailUSD: retailUSD ? Number(retailUSD.toFixed(8)) : void 0,
634
+ retailUSD: retailUSD !== void 0 ? Number(retailUSD.toFixed(8)) : void 0,
615
635
  currency: rates.currency || "USD"
616
636
  };
617
637
  }
618
- function calculateUsageCost(model, usage, markupMultiplier) {
638
+ function calculateUsageCost(model, usage, optionsOrMarkup) {
639
+ const options = typeof optionsOrMarkup === "number" ? { markupMultiplier: optionsOrMarkup } : optionsOrMarkup || {};
619
640
  return calculateCost({
620
641
  model,
621
642
  inputTokens: usage.inputTokens,
@@ -623,7 +644,9 @@ function calculateUsageCost(model, usage, markupMultiplier) {
623
644
  reasoningTokens: usage.reasoningTokens,
624
645
  cachedTokens: usage.cachedTokens,
625
646
  cacheWriteTokens: usage.cacheWriteTokens,
626
- markupMultiplier
647
+ markupMultiplier: options.markupMultiplier,
648
+ minimumChargeUSD: options.minimumChargeUSD,
649
+ customRate: options.customRate
627
650
  });
628
651
  }
629
652
 
@@ -874,7 +897,22 @@ function withBilling(model, options = {}) {
874
897
  const customerId = typeof options.customer === "string" ? options.customer : options.customer?.id || options.customerId;
875
898
  const modelId = model.modelId || "unknown-model";
876
899
  const provider = model.provider?.replace(/^@ai-sdk\//, "") || "ai-sdk";
877
- const handleUsage = (rawUsage) => {
900
+ const maxCostPerCall = options.maxCostPerCallUSD !== void 0 ? options.maxCostPerCallUSD : 0.5;
901
+ const captureOnAbort = options.captureOnAbort !== false;
902
+ const scheduleFlush = () => {
903
+ try {
904
+ const flushPromise = meter.flush();
905
+ if (typeof globalThis !== "undefined") {
906
+ const g = globalThis;
907
+ if (typeof g.after === "function") {
908
+ g.after(() => flushPromise);
909
+ return;
910
+ }
911
+ }
912
+ } catch {
913
+ }
914
+ };
915
+ const handleUsage = (rawUsage, extraMeta) => {
878
916
  if (!rawUsage) return;
879
917
  const inputTokens = rawUsage.promptTokens ?? rawUsage.inputTokens ?? 0;
880
918
  const outputTokens = rawUsage.completionTokens ?? rawUsage.outputTokens ?? 0;
@@ -888,15 +926,19 @@ function withBilling(model, options = {}) {
888
926
  visibleOutputTokens: Math.max(0, outputTokens - reasoningTokens),
889
927
  cachedTokens: cachedTokens > 0 ? cachedTokens : void 0
890
928
  };
891
- const cost = calculateUsageCost(modelId, usage);
892
- if (options.maxCostPerCallUSD && cost.totalUSD > options.maxCostPerCallUSD) {
929
+ const cost = calculateUsageCost(modelId, usage, {
930
+ markupMultiplier: options.pricing?.margin,
931
+ minimumChargeUSD: options.pricing?.minimumChargeUSD,
932
+ customRate: options.rate
933
+ });
934
+ if (maxCostPerCall > 0 && maxCostPerCall !== Infinity && cost.totalUSD > maxCostPerCall) {
893
935
  const budgetEvent = {
894
936
  reason: "cost_per_call_exceeded",
895
- limit: options.maxCostPerCallUSD,
937
+ limit: maxCostPerCall,
896
938
  current: cost.totalUSD,
897
939
  model: modelId,
898
940
  customerId,
899
- message: `[vibezcheck] Circuit Breaker tripped: Call cost ($${cost.totalUSD.toFixed(4)}) exceeded maxCostPerCallUSD threshold ($${options.maxCostPerCallUSD.toFixed(4)}).`
941
+ message: `[vibezcheck] Circuit Breaker tripped: Call cost ($${cost.totalUSD.toFixed(4)}) exceeded budget ceiling ($${maxCostPerCall.toFixed(4)}).`
900
942
  };
901
943
  if (options.onBudgetExceeded) {
902
944
  options.onBudgetExceeded(budgetEvent);
@@ -928,7 +970,11 @@ function withBilling(model, options = {}) {
928
970
  usage,
929
971
  cost,
930
972
  customerId,
931
- metadata: options.metadata
973
+ metadata: {
974
+ ...options.metadata,
975
+ ...extraMeta,
976
+ billingMode: options.billing?.mode || "postpaid"
977
+ }
932
978
  };
933
979
  meter.recordUsage({
934
980
  model: modelId,
@@ -938,11 +984,12 @@ function withBilling(model, options = {}) {
938
984
  reasoningTokens: usage.reasoningTokens,
939
985
  cachedTokens: usage.cachedTokens,
940
986
  customerId,
941
- metadata: options.metadata
987
+ metadata: event.metadata
942
988
  });
943
989
  if (options.onUsage) {
944
990
  options.onUsage(event);
945
991
  }
992
+ scheduleFlush();
946
993
  };
947
994
  const handler = {
948
995
  get(target, prop, receiver) {
@@ -954,12 +1001,59 @@ function withBilling(model, options = {}) {
954
1001
  return result;
955
1002
  }
956
1003
  const originalStream = result.stream;
1004
+ let streamCompleted = false;
1005
+ let accumulatedChars = 0;
1006
+ let estimatedInputTokens = 0;
1007
+ try {
1008
+ if (args[0]?.prompt) {
1009
+ const str = typeof args[0].prompt === "string" ? args[0].prompt : JSON.stringify(args[0].prompt);
1010
+ estimatedInputTokens = Math.ceil(str.length / 4);
1011
+ }
1012
+ } catch {
1013
+ }
1014
+ const abortSignal = args[0]?.abortSignal;
1015
+ if (abortSignal && captureOnAbort) {
1016
+ abortSignal.addEventListener(
1017
+ "abort",
1018
+ () => {
1019
+ if (!streamCompleted && accumulatedChars > 0) {
1020
+ streamCompleted = true;
1021
+ const estimatedOutputTokens = Math.ceil(accumulatedChars / 3.8);
1022
+ handleUsage(
1023
+ {
1024
+ inputTokens: estimatedInputTokens,
1025
+ outputTokens: estimatedOutputTokens
1026
+ },
1027
+ { aborted: true, partial: true }
1028
+ );
1029
+ }
1030
+ },
1031
+ { once: true }
1032
+ );
1033
+ }
957
1034
  const transformStream = new TransformStream({
958
1035
  transform(chunk, controller) {
959
1036
  controller.enqueue(chunk);
1037
+ if (chunk.type === "text-delta" && chunk.textDelta) {
1038
+ accumulatedChars += chunk.textDelta.length;
1039
+ }
960
1040
  if (chunk.type === "finish" && chunk.usage) {
1041
+ streamCompleted = true;
961
1042
  handleUsage(chunk.usage);
962
1043
  }
1044
+ },
1045
+ flush() {
1046
+ if (!streamCompleted && captureOnAbort && accumulatedChars > 0) {
1047
+ streamCompleted = true;
1048
+ const estimatedOutputTokens = Math.ceil(accumulatedChars / 3.8);
1049
+ handleUsage(
1050
+ {
1051
+ inputTokens: estimatedInputTokens,
1052
+ outputTokens: estimatedOutputTokens
1053
+ },
1054
+ { aborted: true, partial: true }
1055
+ );
1056
+ }
963
1057
  }
964
1058
  });
965
1059
  return {
@@ -1033,10 +1127,70 @@ function createVibezModel(modelOrId, options = {}) {
1033
1127
  }
1034
1128
  return modelOrId;
1035
1129
  }
1130
+ function createVibezSession(sessionOptions = {}) {
1131
+ const meter = createMeter({
1132
+ apiKey: sessionOptions.stripeApiKey
1133
+ });
1134
+ const customerId = typeof sessionOptions.customer === "string" ? sessionOptions.customer : sessionOptions.customer?.id;
1135
+ return {
1136
+ meter,
1137
+ model: (modelOrId, callOptions = {}) => {
1138
+ return createVibezModel(modelOrId, {
1139
+ customer: sessionOptions.customer,
1140
+ pricing: sessionOptions.pricing,
1141
+ billing: sessionOptions.billing,
1142
+ meter,
1143
+ metadata: {
1144
+ ...sessionOptions.metadata,
1145
+ ...callOptions.metadata
1146
+ },
1147
+ ...callOptions
1148
+ });
1149
+ },
1150
+ trackTool: async (name, { costUSD, metadata }) => {
1151
+ const event = {
1152
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1153
+ model: `tool:${name}`,
1154
+ provider: "tool",
1155
+ usage: {
1156
+ inputTokens: 0,
1157
+ outputTokens: 0,
1158
+ totalTokens: 0
1159
+ },
1160
+ cost: {
1161
+ inputCostUSD: 0,
1162
+ outputCostUSD: costUSD,
1163
+ totalUSD: costUSD,
1164
+ currency: "USD"
1165
+ },
1166
+ customerId,
1167
+ metadata: {
1168
+ ...sessionOptions.metadata,
1169
+ ...metadata,
1170
+ tool: name
1171
+ }
1172
+ };
1173
+ meter.recordUsage({
1174
+ model: `tool:${name}`,
1175
+ provider: "tool",
1176
+ inputTokens: 0,
1177
+ outputTokens: 0,
1178
+ customerId,
1179
+ metadata: event.metadata
1180
+ });
1181
+ await meter.flush();
1182
+ }
1183
+ };
1184
+ }
1185
+ var vibezcheck = Object.assign(createVibezModel, {
1186
+ session: createVibezSession
1187
+ });
1036
1188
  // Annotate the CommonJS export names for ESM import in node:
1037
1189
  0 && (module.exports = {
1038
1190
  createVibezModel,
1191
+ createVibezSession,
1039
1192
  meteredModel,
1193
+ vibezcheck,
1040
1194
  withBilling
1041
1195
  });
1042
1196
  //# sourceMappingURL=index.js.map