vibezcheck 0.2.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.
Files changed (45) hide show
  1. package/README.md +131 -197
  2. package/bin/vibezcheck.js +4 -0
  3. package/dist/ai-sdk/index.d.mts +78 -9
  4. package/dist/ai-sdk/index.d.ts +78 -9
  5. package/dist/ai-sdk/index.js +278 -64
  6. package/dist/ai-sdk/index.js.map +1 -1
  7. package/dist/ai-sdk/index.mjs +282 -64
  8. package/dist/ai-sdk/index.mjs.map +1 -1
  9. package/dist/cli/index.d.mts +1 -0
  10. package/dist/cli/index.d.ts +1 -0
  11. package/dist/cli/index.js +232 -0
  12. package/dist/cli/index.js.map +1 -0
  13. package/dist/cli/index.mjs +209 -0
  14. package/dist/cli/index.mjs.map +1 -0
  15. package/dist/{client-MJ3tl7bz.d.mts → client--iq93FQg.d.mts} +1 -1
  16. package/dist/{client-txrE0D_D.d.ts → client-kJN_Znan.d.ts} +1 -1
  17. package/dist/index.d.mts +50 -26
  18. package/dist/index.d.ts +50 -26
  19. package/dist/index.js +299 -104
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +303 -104
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/meter/index.d.mts +2 -2
  24. package/dist/meter/index.d.ts +2 -2
  25. package/dist/meter/index.js +28 -7
  26. package/dist/meter/index.js.map +1 -1
  27. package/dist/meter/index.mjs +28 -7
  28. package/dist/meter/index.mjs.map +1 -1
  29. package/dist/pricing/index.d.mts +8 -2
  30. package/dist/pricing/index.d.ts +8 -2
  31. package/dist/pricing/index.js +28 -7
  32. package/dist/pricing/index.js.map +1 -1
  33. package/dist/pricing/index.mjs +28 -7
  34. package/dist/pricing/index.mjs.map +1 -1
  35. package/dist/react/index.d.mts +95 -17
  36. package/dist/react/index.d.ts +95 -17
  37. package/dist/react/index.js +468 -7
  38. package/dist/react/index.js.map +1 -1
  39. package/dist/react/index.mjs +468 -10
  40. package/dist/react/index.mjs.map +1 -1
  41. package/dist/types-Zbzxg-Ka.d.mts +261 -0
  42. package/dist/types-Zbzxg-Ka.d.ts +261 -0
  43. package/package.json +6 -2
  44. package/dist/types-CSrSmsd1.d.mts +0 -159
  45. package/dist/types-CSrSmsd1.d.ts +0 -159
@@ -30,11 +30,24 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/ai-sdk/index.ts
31
31
  var ai_sdk_exports = {};
32
32
  __export(ai_sdk_exports, {
33
+ createVibezModel: () => createVibezModel,
34
+ createVibezSession: () => createVibezSession,
33
35
  meteredModel: () => meteredModel,
36
+ vibezcheck: () => vibezcheck,
34
37
  withBilling: () => withBilling
35
38
  });
36
39
  module.exports = __toCommonJS(ai_sdk_exports);
37
40
 
41
+ // src/types.ts
42
+ var VibezCircuitBreakerError = class extends Error {
43
+ event;
44
+ constructor(event) {
45
+ super(event.message);
46
+ this.name = "VibezCircuitBreakerError";
47
+ this.event = event;
48
+ }
49
+ };
50
+
38
51
  // src/meter/client.ts
39
52
  var import_stripe = __toESM(require("stripe"));
40
53
 
@@ -576,14 +589,26 @@ function getModelPricing(modelName) {
576
589
 
577
590
  // src/pricing/calculator.ts
578
591
  function calculateCost(params) {
579
- 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
+ }
580
605
  const inputTokens = params.inputTokens ?? 0;
581
606
  const outputTokens = params.outputTokens ?? 0;
582
607
  const reasoningTokens = params.reasoningTokens ?? 0;
583
608
  const cachedTokens = params.cachedTokens ?? 0;
584
609
  const regularInputTokens = Math.max(0, inputTokens - cachedTokens);
585
610
  const regularInputCost = regularInputTokens / 1e6 * rates.inputPer1M;
586
- const cachedRate = rates.cachedInputPer1M ?? rates.inputPer1M * 0.5;
611
+ const cachedRate = rates.cachedInputPer1M ?? rates.inputPer1M * 0.15;
587
612
  const cachedInputCost = cachedTokens / 1e6 * cachedRate;
588
613
  const inputCostUSD = regularInputCost + cachedInputCost;
589
614
  const outputCostUSD = outputTokens / 1e6 * rates.outputPer1M;
@@ -591,20 +616,27 @@ function calculateCost(params) {
591
616
  const reasoningCostUSD = reasoningTokens / 1e6 * reasoningRate;
592
617
  const standardCacheCost = cachedTokens / 1e6 * rates.inputPer1M;
593
618
  const cachedDiscountUSD = Math.max(0, standardCacheCost - cachedInputCost);
594
- const totalUSD = inputCostUSD + outputCostUSD;
619
+ let totalUSD = inputCostUSD + outputCostUSD;
595
620
  const markup = params.markupMultiplier ?? 1;
596
- 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
+ }
597
628
  return {
598
629
  inputCostUSD: Number(inputCostUSD.toFixed(8)),
599
630
  outputCostUSD: Number(outputCostUSD.toFixed(8)),
600
631
  reasoningCostUSD: reasoningTokens > 0 ? Number(reasoningCostUSD.toFixed(8)) : void 0,
601
632
  cachedDiscountUSD: cachedTokens > 0 ? Number(cachedDiscountUSD.toFixed(8)) : void 0,
602
633
  totalUSD: Number(totalUSD.toFixed(8)),
603
- retailUSD: retailUSD ? Number(retailUSD.toFixed(8)) : void 0,
634
+ retailUSD: retailUSD !== void 0 ? Number(retailUSD.toFixed(8)) : void 0,
604
635
  currency: rates.currency || "USD"
605
636
  };
606
637
  }
607
- function calculateUsageCost(model, usage, markupMultiplier) {
638
+ function calculateUsageCost(model, usage, optionsOrMarkup) {
639
+ const options = typeof optionsOrMarkup === "number" ? { markupMultiplier: optionsOrMarkup } : optionsOrMarkup || {};
608
640
  return calculateCost({
609
641
  model,
610
642
  inputTokens: usage.inputTokens,
@@ -612,7 +644,9 @@ function calculateUsageCost(model, usage, markupMultiplier) {
612
644
  reasoningTokens: usage.reasoningTokens,
613
645
  cachedTokens: usage.cachedTokens,
614
646
  cacheWriteTokens: usage.cacheWriteTokens,
615
- markupMultiplier
647
+ markupMultiplier: options.markupMultiplier,
648
+ minimumChargeUSD: options.minimumChargeUSD,
649
+ customRate: options.customRate
616
650
  });
617
651
  }
618
652
 
@@ -863,7 +897,22 @@ function withBilling(model, options = {}) {
863
897
  const customerId = typeof options.customer === "string" ? options.customer : options.customer?.id || options.customerId;
864
898
  const modelId = model.modelId || "unknown-model";
865
899
  const provider = model.provider?.replace(/^@ai-sdk\//, "") || "ai-sdk";
866
- 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) => {
867
916
  if (!rawUsage) return;
868
917
  const inputTokens = rawUsage.promptTokens ?? rawUsage.inputTokens ?? 0;
869
918
  const outputTokens = rawUsage.completionTokens ?? rawUsage.outputTokens ?? 0;
@@ -877,7 +926,43 @@ function withBilling(model, options = {}) {
877
926
  visibleOutputTokens: Math.max(0, outputTokens - reasoningTokens),
878
927
  cachedTokens: cachedTokens > 0 ? cachedTokens : void 0
879
928
  };
880
- const cost = calculateUsageCost(modelId, usage);
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) {
935
+ const budgetEvent = {
936
+ reason: "cost_per_call_exceeded",
937
+ limit: maxCostPerCall,
938
+ current: cost.totalUSD,
939
+ model: modelId,
940
+ customerId,
941
+ message: `[vibezcheck] Circuit Breaker tripped: Call cost ($${cost.totalUSD.toFixed(4)}) exceeded budget ceiling ($${maxCostPerCall.toFixed(4)}).`
942
+ };
943
+ if (options.onBudgetExceeded) {
944
+ options.onBudgetExceeded(budgetEvent);
945
+ }
946
+ if (options.throwOnBudgetExceeded) {
947
+ throw new VibezCircuitBreakerError(budgetEvent);
948
+ }
949
+ }
950
+ if (options.maxTokensPerCall && usage.totalTokens > options.maxTokensPerCall) {
951
+ const budgetEvent = {
952
+ reason: "max_tokens_exceeded",
953
+ limit: options.maxTokensPerCall,
954
+ current: usage.totalTokens,
955
+ model: modelId,
956
+ customerId,
957
+ message: `[vibezcheck] Circuit Breaker tripped: Token count (${usage.totalTokens}) exceeded maxTokensPerCall limit (${options.maxTokensPerCall}).`
958
+ };
959
+ if (options.onBudgetExceeded) {
960
+ options.onBudgetExceeded(budgetEvent);
961
+ }
962
+ if (options.throwOnBudgetExceeded) {
963
+ throw new VibezCircuitBreakerError(budgetEvent);
964
+ }
965
+ }
881
966
  const event = {
882
967
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
883
968
  model: modelId,
@@ -885,7 +970,11 @@ function withBilling(model, options = {}) {
885
970
  usage,
886
971
  cost,
887
972
  customerId,
888
- metadata: options.metadata
973
+ metadata: {
974
+ ...options.metadata,
975
+ ...extraMeta,
976
+ billingMode: options.billing?.mode || "postpaid"
977
+ }
889
978
  };
890
979
  meter.recordUsage({
891
980
  model: modelId,
@@ -895,24 +984,16 @@ function withBilling(model, options = {}) {
895
984
  reasoningTokens: usage.reasoningTokens,
896
985
  cachedTokens: usage.cachedTokens,
897
986
  customerId,
898
- metadata: options.metadata
987
+ metadata: event.metadata
899
988
  });
900
989
  if (options.onUsage) {
901
990
  options.onUsage(event);
902
991
  }
992
+ scheduleFlush();
903
993
  };
904
- return new Proxy(model, {
994
+ const handler = {
905
995
  get(target, prop, receiver) {
906
996
  const originalValue = Reflect.get(target, prop, receiver);
907
- if (prop === "doGenerate" && typeof originalValue === "function") {
908
- return async function(...args) {
909
- const result = await originalValue.apply(target, args);
910
- if (result && result.usage) {
911
- handleUsage(result.usage);
912
- }
913
- return result;
914
- };
915
- }
916
997
  if (prop === "doStream" && typeof originalValue === "function") {
917
998
  return async function(...args) {
918
999
  const result = await originalValue.apply(target, args);
@@ -920,63 +1001,196 @@ function withBilling(model, options = {}) {
920
1001
  return result;
921
1002
  }
922
1003
  const originalStream = result.stream;
923
- if (typeof originalStream.getReader === "function") {
924
- const reader = originalStream.getReader();
925
- const transformedStream = new ReadableStream({
926
- async start(controller) {
927
- try {
928
- while (true) {
929
- const { done, value } = await reader.read();
930
- if (done) {
931
- controller.close();
932
- break;
933
- }
934
- if (value && typeof value === "object") {
935
- if (value.type === "finish" && value.usage) {
936
- handleUsage(value.usage);
937
- }
938
- }
939
- controller.enqueue(value);
940
- }
941
- } catch (err) {
942
- controller.error(err);
943
- }
944
- }
945
- });
946
- return {
947
- ...result,
948
- stream: transformedStream
949
- };
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 {
950
1013
  }
951
- if (Symbol.asyncIterator in originalStream) {
952
- const wrappedAsyncIterable = {
953
- async *[Symbol.asyncIterator]() {
954
- for await (const chunk of originalStream) {
955
- if (chunk && typeof chunk === "object") {
956
- if (chunk.type === "finish" && chunk.usage) {
957
- handleUsage(chunk.usage);
958
- }
959
- }
960
- yield chunk;
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
+ );
961
1029
  }
1030
+ },
1031
+ { once: true }
1032
+ );
1033
+ }
1034
+ const transformStream = new TransformStream({
1035
+ transform(chunk, controller) {
1036
+ controller.enqueue(chunk);
1037
+ if (chunk.type === "text-delta" && chunk.textDelta) {
1038
+ accumulatedChars += chunk.textDelta.length;
962
1039
  }
963
- };
964
- return {
965
- ...result,
966
- stream: wrappedAsyncIterable
967
- };
1040
+ if (chunk.type === "finish" && chunk.usage) {
1041
+ streamCompleted = true;
1042
+ handleUsage(chunk.usage);
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
+ }
1057
+ }
1058
+ });
1059
+ return {
1060
+ ...result,
1061
+ stream: originalStream.pipeThrough(transformStream)
1062
+ };
1063
+ };
1064
+ }
1065
+ if (prop === "doGenerate" && typeof originalValue === "function") {
1066
+ return async function(...args) {
1067
+ const result = await originalValue.apply(target, args);
1068
+ if (result && result.usage) {
1069
+ handleUsage(result.usage);
968
1070
  }
969
1071
  return result;
970
1072
  };
971
1073
  }
972
1074
  return originalValue;
973
1075
  }
974
- });
1076
+ };
1077
+ return new Proxy(model, handler);
975
1078
  }
976
1079
  var meteredModel = withBilling;
1080
+
1081
+ // src/ai-sdk/declarative.ts
1082
+ function createVibezModel(modelOrId, options = {}) {
1083
+ if (typeof modelOrId === "object" && modelOrId !== null) {
1084
+ return withBilling(modelOrId, options);
1085
+ }
1086
+ if (typeof modelOrId === "string") {
1087
+ const rawId = modelOrId;
1088
+ let providerName = "openai";
1089
+ let cleanModelId = rawId;
1090
+ if (rawId.includes("/")) {
1091
+ const parts = rawId.split("/");
1092
+ providerName = parts[0].toLowerCase();
1093
+ cleanModelId = parts.slice(1).join("/");
1094
+ }
1095
+ const apiKey = options.apiKey || process.env.AI_GATEWAY_API_KEY || process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY || process.env.GOOGLE_GENERATIVE_AI_API_KEY;
1096
+ const baseURL = options.baseURL || process.env.AI_GATEWAY_BASE_URL || (providerName === "openai" ? "https://api.openai.com/v1" : void 0);
1097
+ let baseModelInstance = null;
1098
+ try {
1099
+ if (providerName === "openai" || providerName === "gateway" || !providerName) {
1100
+ const { createOpenAI } = require("@ai-sdk/openai");
1101
+ const openaiProvider = createOpenAI({ apiKey, baseURL });
1102
+ baseModelInstance = openaiProvider(cleanModelId);
1103
+ } else if (providerName === "anthropic") {
1104
+ const { createAnthropic } = require("@ai-sdk/anthropic");
1105
+ const anthropicProvider = createAnthropic({ apiKey, baseURL });
1106
+ baseModelInstance = anthropicProvider(cleanModelId);
1107
+ } else if (providerName === "google") {
1108
+ const { createGoogleGenerativeAI } = require("@ai-sdk/google");
1109
+ const googleProvider = createGoogleGenerativeAI({ apiKey, baseURL });
1110
+ baseModelInstance = googleProvider(cleanModelId);
1111
+ }
1112
+ } catch {
1113
+ baseModelInstance = {
1114
+ specificationVersion: "v2",
1115
+ provider: providerName,
1116
+ modelId: cleanModelId
1117
+ };
1118
+ }
1119
+ if (!baseModelInstance) {
1120
+ baseModelInstance = {
1121
+ specificationVersion: "v2",
1122
+ provider: providerName,
1123
+ modelId: cleanModelId
1124
+ };
1125
+ }
1126
+ return withBilling(baseModelInstance, options);
1127
+ }
1128
+ return modelOrId;
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
+ });
977
1188
  // Annotate the CommonJS export names for ESM import in node:
978
1189
  0 && (module.exports = {
1190
+ createVibezModel,
1191
+ createVibezSession,
979
1192
  meteredModel,
1193
+ vibezcheck,
980
1194
  withBilling
981
1195
  });
982
1196
  //# sourceMappingURL=index.js.map