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
@@ -1,3 +1,20 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/types.ts
9
+ var VibezCircuitBreakerError = class extends Error {
10
+ event;
11
+ constructor(event) {
12
+ super(event.message);
13
+ this.name = "VibezCircuitBreakerError";
14
+ this.event = event;
15
+ }
16
+ };
17
+
1
18
  // src/meter/client.ts
2
19
  import Stripe from "stripe";
3
20
 
@@ -539,14 +556,26 @@ function getModelPricing(modelName) {
539
556
 
540
557
  // src/pricing/calculator.ts
541
558
  function calculateCost(params) {
542
- 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
+ }
543
572
  const inputTokens = params.inputTokens ?? 0;
544
573
  const outputTokens = params.outputTokens ?? 0;
545
574
  const reasoningTokens = params.reasoningTokens ?? 0;
546
575
  const cachedTokens = params.cachedTokens ?? 0;
547
576
  const regularInputTokens = Math.max(0, inputTokens - cachedTokens);
548
577
  const regularInputCost = regularInputTokens / 1e6 * rates.inputPer1M;
549
- const cachedRate = rates.cachedInputPer1M ?? rates.inputPer1M * 0.5;
578
+ const cachedRate = rates.cachedInputPer1M ?? rates.inputPer1M * 0.15;
550
579
  const cachedInputCost = cachedTokens / 1e6 * cachedRate;
551
580
  const inputCostUSD = regularInputCost + cachedInputCost;
552
581
  const outputCostUSD = outputTokens / 1e6 * rates.outputPer1M;
@@ -554,20 +583,27 @@ function calculateCost(params) {
554
583
  const reasoningCostUSD = reasoningTokens / 1e6 * reasoningRate;
555
584
  const standardCacheCost = cachedTokens / 1e6 * rates.inputPer1M;
556
585
  const cachedDiscountUSD = Math.max(0, standardCacheCost - cachedInputCost);
557
- const totalUSD = inputCostUSD + outputCostUSD;
586
+ let totalUSD = inputCostUSD + outputCostUSD;
558
587
  const markup = params.markupMultiplier ?? 1;
559
- 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
+ }
560
595
  return {
561
596
  inputCostUSD: Number(inputCostUSD.toFixed(8)),
562
597
  outputCostUSD: Number(outputCostUSD.toFixed(8)),
563
598
  reasoningCostUSD: reasoningTokens > 0 ? Number(reasoningCostUSD.toFixed(8)) : void 0,
564
599
  cachedDiscountUSD: cachedTokens > 0 ? Number(cachedDiscountUSD.toFixed(8)) : void 0,
565
600
  totalUSD: Number(totalUSD.toFixed(8)),
566
- retailUSD: retailUSD ? Number(retailUSD.toFixed(8)) : void 0,
601
+ retailUSD: retailUSD !== void 0 ? Number(retailUSD.toFixed(8)) : void 0,
567
602
  currency: rates.currency || "USD"
568
603
  };
569
604
  }
570
- function calculateUsageCost(model, usage, markupMultiplier) {
605
+ function calculateUsageCost(model, usage, optionsOrMarkup) {
606
+ const options = typeof optionsOrMarkup === "number" ? { markupMultiplier: optionsOrMarkup } : optionsOrMarkup || {};
571
607
  return calculateCost({
572
608
  model,
573
609
  inputTokens: usage.inputTokens,
@@ -575,7 +611,9 @@ function calculateUsageCost(model, usage, markupMultiplier) {
575
611
  reasoningTokens: usage.reasoningTokens,
576
612
  cachedTokens: usage.cachedTokens,
577
613
  cacheWriteTokens: usage.cacheWriteTokens,
578
- markupMultiplier
614
+ markupMultiplier: options.markupMultiplier,
615
+ minimumChargeUSD: options.minimumChargeUSD,
616
+ customRate: options.customRate
579
617
  });
580
618
  }
581
619
 
@@ -826,7 +864,22 @@ function withBilling(model, options = {}) {
826
864
  const customerId = typeof options.customer === "string" ? options.customer : options.customer?.id || options.customerId;
827
865
  const modelId = model.modelId || "unknown-model";
828
866
  const provider = model.provider?.replace(/^@ai-sdk\//, "") || "ai-sdk";
829
- 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) => {
830
883
  if (!rawUsage) return;
831
884
  const inputTokens = rawUsage.promptTokens ?? rawUsage.inputTokens ?? 0;
832
885
  const outputTokens = rawUsage.completionTokens ?? rawUsage.outputTokens ?? 0;
@@ -840,7 +893,43 @@ function withBilling(model, options = {}) {
840
893
  visibleOutputTokens: Math.max(0, outputTokens - reasoningTokens),
841
894
  cachedTokens: cachedTokens > 0 ? cachedTokens : void 0
842
895
  };
843
- const cost = calculateUsageCost(modelId, usage);
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) {
902
+ const budgetEvent = {
903
+ reason: "cost_per_call_exceeded",
904
+ limit: maxCostPerCall,
905
+ current: cost.totalUSD,
906
+ model: modelId,
907
+ customerId,
908
+ message: `[vibezcheck] Circuit Breaker tripped: Call cost ($${cost.totalUSD.toFixed(4)}) exceeded budget ceiling ($${maxCostPerCall.toFixed(4)}).`
909
+ };
910
+ if (options.onBudgetExceeded) {
911
+ options.onBudgetExceeded(budgetEvent);
912
+ }
913
+ if (options.throwOnBudgetExceeded) {
914
+ throw new VibezCircuitBreakerError(budgetEvent);
915
+ }
916
+ }
917
+ if (options.maxTokensPerCall && usage.totalTokens > options.maxTokensPerCall) {
918
+ const budgetEvent = {
919
+ reason: "max_tokens_exceeded",
920
+ limit: options.maxTokensPerCall,
921
+ current: usage.totalTokens,
922
+ model: modelId,
923
+ customerId,
924
+ message: `[vibezcheck] Circuit Breaker tripped: Token count (${usage.totalTokens}) exceeded maxTokensPerCall limit (${options.maxTokensPerCall}).`
925
+ };
926
+ if (options.onBudgetExceeded) {
927
+ options.onBudgetExceeded(budgetEvent);
928
+ }
929
+ if (options.throwOnBudgetExceeded) {
930
+ throw new VibezCircuitBreakerError(budgetEvent);
931
+ }
932
+ }
844
933
  const event = {
845
934
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
846
935
  model: modelId,
@@ -848,7 +937,11 @@ function withBilling(model, options = {}) {
848
937
  usage,
849
938
  cost,
850
939
  customerId,
851
- metadata: options.metadata
940
+ metadata: {
941
+ ...options.metadata,
942
+ ...extraMeta,
943
+ billingMode: options.billing?.mode || "postpaid"
944
+ }
852
945
  };
853
946
  meter.recordUsage({
854
947
  model: modelId,
@@ -858,24 +951,16 @@ function withBilling(model, options = {}) {
858
951
  reasoningTokens: usage.reasoningTokens,
859
952
  cachedTokens: usage.cachedTokens,
860
953
  customerId,
861
- metadata: options.metadata
954
+ metadata: event.metadata
862
955
  });
863
956
  if (options.onUsage) {
864
957
  options.onUsage(event);
865
958
  }
959
+ scheduleFlush();
866
960
  };
867
- return new Proxy(model, {
961
+ const handler = {
868
962
  get(target, prop, receiver) {
869
963
  const originalValue = Reflect.get(target, prop, receiver);
870
- if (prop === "doGenerate" && typeof originalValue === "function") {
871
- return async function(...args) {
872
- const result = await originalValue.apply(target, args);
873
- if (result && result.usage) {
874
- handleUsage(result.usage);
875
- }
876
- return result;
877
- };
878
- }
879
964
  if (prop === "doStream" && typeof originalValue === "function") {
880
965
  return async function(...args) {
881
966
  const result = await originalValue.apply(target, args);
@@ -883,62 +968,195 @@ function withBilling(model, options = {}) {
883
968
  return result;
884
969
  }
885
970
  const originalStream = result.stream;
886
- if (typeof originalStream.getReader === "function") {
887
- const reader = originalStream.getReader();
888
- const transformedStream = new ReadableStream({
889
- async start(controller) {
890
- try {
891
- while (true) {
892
- const { done, value } = await reader.read();
893
- if (done) {
894
- controller.close();
895
- break;
896
- }
897
- if (value && typeof value === "object") {
898
- if (value.type === "finish" && value.usage) {
899
- handleUsage(value.usage);
900
- }
901
- }
902
- controller.enqueue(value);
903
- }
904
- } catch (err) {
905
- controller.error(err);
906
- }
907
- }
908
- });
909
- return {
910
- ...result,
911
- stream: transformedStream
912
- };
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 {
913
980
  }
914
- if (Symbol.asyncIterator in originalStream) {
915
- const wrappedAsyncIterable = {
916
- async *[Symbol.asyncIterator]() {
917
- for await (const chunk of originalStream) {
918
- if (chunk && typeof chunk === "object") {
919
- if (chunk.type === "finish" && chunk.usage) {
920
- handleUsage(chunk.usage);
921
- }
922
- }
923
- yield chunk;
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
+ );
924
996
  }
997
+ },
998
+ { once: true }
999
+ );
1000
+ }
1001
+ const transformStream = new TransformStream({
1002
+ transform(chunk, controller) {
1003
+ controller.enqueue(chunk);
1004
+ if (chunk.type === "text-delta" && chunk.textDelta) {
1005
+ accumulatedChars += chunk.textDelta.length;
1006
+ }
1007
+ if (chunk.type === "finish" && chunk.usage) {
1008
+ streamCompleted = true;
1009
+ handleUsage(chunk.usage);
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
+ );
925
1023
  }
926
- };
927
- return {
928
- ...result,
929
- stream: wrappedAsyncIterable
930
- };
1024
+ }
1025
+ });
1026
+ return {
1027
+ ...result,
1028
+ stream: originalStream.pipeThrough(transformStream)
1029
+ };
1030
+ };
1031
+ }
1032
+ if (prop === "doGenerate" && typeof originalValue === "function") {
1033
+ return async function(...args) {
1034
+ const result = await originalValue.apply(target, args);
1035
+ if (result && result.usage) {
1036
+ handleUsage(result.usage);
931
1037
  }
932
1038
  return result;
933
1039
  };
934
1040
  }
935
1041
  return originalValue;
936
1042
  }
937
- });
1043
+ };
1044
+ return new Proxy(model, handler);
938
1045
  }
939
1046
  var meteredModel = withBilling;
1047
+
1048
+ // src/ai-sdk/declarative.ts
1049
+ function createVibezModel(modelOrId, options = {}) {
1050
+ if (typeof modelOrId === "object" && modelOrId !== null) {
1051
+ return withBilling(modelOrId, options);
1052
+ }
1053
+ if (typeof modelOrId === "string") {
1054
+ const rawId = modelOrId;
1055
+ let providerName = "openai";
1056
+ let cleanModelId = rawId;
1057
+ if (rawId.includes("/")) {
1058
+ const parts = rawId.split("/");
1059
+ providerName = parts[0].toLowerCase();
1060
+ cleanModelId = parts.slice(1).join("/");
1061
+ }
1062
+ 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;
1063
+ const baseURL = options.baseURL || process.env.AI_GATEWAY_BASE_URL || (providerName === "openai" ? "https://api.openai.com/v1" : void 0);
1064
+ let baseModelInstance = null;
1065
+ try {
1066
+ if (providerName === "openai" || providerName === "gateway" || !providerName) {
1067
+ const { createOpenAI } = __require("@ai-sdk/openai");
1068
+ const openaiProvider = createOpenAI({ apiKey, baseURL });
1069
+ baseModelInstance = openaiProvider(cleanModelId);
1070
+ } else if (providerName === "anthropic") {
1071
+ const { createAnthropic } = __require("@ai-sdk/anthropic");
1072
+ const anthropicProvider = createAnthropic({ apiKey, baseURL });
1073
+ baseModelInstance = anthropicProvider(cleanModelId);
1074
+ } else if (providerName === "google") {
1075
+ const { createGoogleGenerativeAI } = __require("@ai-sdk/google");
1076
+ const googleProvider = createGoogleGenerativeAI({ apiKey, baseURL });
1077
+ baseModelInstance = googleProvider(cleanModelId);
1078
+ }
1079
+ } catch {
1080
+ baseModelInstance = {
1081
+ specificationVersion: "v2",
1082
+ provider: providerName,
1083
+ modelId: cleanModelId
1084
+ };
1085
+ }
1086
+ if (!baseModelInstance) {
1087
+ baseModelInstance = {
1088
+ specificationVersion: "v2",
1089
+ provider: providerName,
1090
+ modelId: cleanModelId
1091
+ };
1092
+ }
1093
+ return withBilling(baseModelInstance, options);
1094
+ }
1095
+ return modelOrId;
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
+ });
940
1155
  export {
1156
+ createVibezModel,
1157
+ createVibezSession,
941
1158
  meteredModel,
1159
+ vibezcheck,
942
1160
  withBilling
943
1161
  };
944
1162
  //# sourceMappingURL=index.mjs.map