@hiper2d/ai-agents 0.2.1 → 0.3.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.
package/dist/index.mjs CHANGED
@@ -1875,6 +1875,152 @@ function createVoiceAgent(provider, apiKey) {
1875
1875
  return VoiceAgentFactory.createAgent(provider, apiKey);
1876
1876
  }
1877
1877
 
1878
+ // src/budget/index.ts
1879
+ function round6(n) {
1880
+ return parseFloat((Number(n) || 0).toFixed(6));
1881
+ }
1882
+ function periodKey(timestamp, window) {
1883
+ const d = new Date(timestamp);
1884
+ const y = d.getUTCFullYear();
1885
+ const m = String(d.getUTCMonth() + 1).padStart(2, "0");
1886
+ if (window === "month") {
1887
+ return `${y}-${m}`;
1888
+ }
1889
+ const day = String(d.getUTCDate()).padStart(2, "0");
1890
+ return `${y}-${m}-${day}`;
1891
+ }
1892
+ function periodEnd(timestamp, window) {
1893
+ const d = new Date(timestamp);
1894
+ if (window === "month") {
1895
+ return Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 1);
1896
+ }
1897
+ return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() + 1);
1898
+ }
1899
+ function normalizeLedger(ledger, period) {
1900
+ if (!ledger || ledger.period !== period) {
1901
+ return { period, totalUSD: 0, buckets: {} };
1902
+ }
1903
+ const buckets = {};
1904
+ for (const [k, v] of Object.entries(ledger.buckets ?? {})) {
1905
+ buckets[k] = round6(v);
1906
+ }
1907
+ return { period, totalUSD: round6(ledger.totalUSD ?? 0), buckets };
1908
+ }
1909
+ function applySpend(ledger, input) {
1910
+ const timestamp = input.timestamp ?? Date.now();
1911
+ const period = periodKey(timestamp, input.window);
1912
+ const current2 = normalizeLedger(ledger, period);
1913
+ const amount = round6(input.amountUSD);
1914
+ if (!(amount > 0)) {
1915
+ return current2;
1916
+ }
1917
+ const buckets = { ...current2.buckets };
1918
+ if (input.bucket) {
1919
+ buckets[input.bucket] = round6((buckets[input.bucket] ?? 0) + amount);
1920
+ }
1921
+ return { period, totalUSD: round6(current2.totalUSD + amount), buckets };
1922
+ }
1923
+ function ledgerSpend(ledger, window, timestamp = Date.now(), bucket) {
1924
+ const current2 = normalizeLedger(ledger, periodKey(timestamp, window));
1925
+ return bucket ? current2.buckets[bucket] ?? 0 : current2.totalUSD;
1926
+ }
1927
+ function evaluateBudget(spentUSD, limit, timestamp = Date.now()) {
1928
+ const spent = round6(spentUSD);
1929
+ const unlimited = !Number.isFinite(limit.limitUSD);
1930
+ const remaining = unlimited ? Number.POSITIVE_INFINITY : Math.max(0, round6(limit.limitUSD - spent));
1931
+ return {
1932
+ allowed: unlimited || spent < limit.limitUSD,
1933
+ window: limit.window,
1934
+ ...limit.bucket ? { bucket: limit.bucket } : {},
1935
+ limitUSD: limit.limitUSD,
1936
+ spentUSD: spent,
1937
+ remainingUSD: remaining,
1938
+ resetsAt: periodEnd(timestamp, limit.window)
1939
+ };
1940
+ }
1941
+ function firstRefusal(verdicts) {
1942
+ return verdicts.find((v) => !v.allowed);
1943
+ }
1944
+ var BudgetExceededError = class _BudgetExceededError extends Error {
1945
+ verdict;
1946
+ subject;
1947
+ constructor(verdict, subject, message) {
1948
+ super(message ?? _BudgetExceededError.describe(verdict));
1949
+ this.name = "BudgetExceededError";
1950
+ this.verdict = verdict;
1951
+ this.subject = subject;
1952
+ }
1953
+ static describe(v) {
1954
+ const when = v.window === "day" ? "daily" : "monthly";
1955
+ return `${when} budget of $${v.limitUSD} exhausted ($${v.spentUSD} spent); resets at ${new Date(v.resetsAt).toISOString()}`;
1956
+ }
1957
+ };
1958
+ function isBudgetExceededError(err) {
1959
+ return err instanceof BudgetExceededError || typeof err === "object" && err !== null && err.name === "BudgetExceededError" && !!err.verdict;
1960
+ }
1961
+ var InMemorySpendStore = class {
1962
+ ledgers = /* @__PURE__ */ new Map();
1963
+ async read(subject) {
1964
+ return { ...this.ledgers.get(subject) ?? {} };
1965
+ }
1966
+ async update(subject, fn) {
1967
+ const next = fn({ ...this.ledgers.get(subject) ?? {} });
1968
+ this.ledgers.set(subject, next);
1969
+ return { ...next };
1970
+ }
1971
+ };
1972
+ var BudgetController = class {
1973
+ constructor(store, options) {
1974
+ this.store = store;
1975
+ this.limits = options.limits;
1976
+ this.bucket = options.bucket;
1977
+ this.clock = options.clock ?? (() => Date.now());
1978
+ }
1979
+ store;
1980
+ limits;
1981
+ bucket;
1982
+ clock;
1983
+ verdicts(ledgers, now) {
1984
+ return this.limits.map((limit) => {
1985
+ const spent = ledgerSpend(ledgers[limit.window], limit.window, now, limit.bucket ?? this.bucket);
1986
+ return evaluateBudget(spent, limit, now);
1987
+ });
1988
+ }
1989
+ async check(subject) {
1990
+ return this.verdicts(await this.store.read(subject), this.clock());
1991
+ }
1992
+ async assertWithinBudget(subject) {
1993
+ const refused = firstRefusal(await this.check(subject));
1994
+ if (refused) {
1995
+ throw new BudgetExceededError(refused, subject);
1996
+ }
1997
+ }
1998
+ /**
1999
+ * Record `amountUSD` against every limited window. Re-checks the limits on the
2000
+ * ledgers as they are at write time and throws `BudgetExceededError` (writing
2001
+ * nothing) if any window is already exhausted.
2002
+ */
2003
+ async record(subject, amountUSD) {
2004
+ const now = this.clock();
2005
+ return this.store.update(subject, (current2) => {
2006
+ const refused = firstRefusal(this.verdicts(current2, now));
2007
+ if (refused) {
2008
+ throw new BudgetExceededError(refused, subject);
2009
+ }
2010
+ const next = { ...current2 };
2011
+ for (const limit of this.limits) {
2012
+ next[limit.window] = applySpend(current2[limit.window], {
2013
+ window: limit.window,
2014
+ amountUSD,
2015
+ bucket: limit.bucket ?? this.bucket,
2016
+ timestamp: now
2017
+ });
2018
+ }
2019
+ return next;
2020
+ });
2021
+ }
2022
+ };
2023
+
1878
2024
  // src/agents/abstract-agent.ts
1879
2025
  var AbstractAgent = class {
1880
2026
  name;
@@ -4603,6 +4749,8 @@ export {
4603
4749
  AbstractAgent,
4604
4750
  AgentFactory,
4605
4751
  BotResponseError,
4752
+ BudgetController,
4753
+ BudgetExceededError,
4606
4754
  CACHE_TIER_MARKER,
4607
4755
  ClaudeAgent,
4608
4756
  DEEPSEEK_PEAK_SCHEDULE,
@@ -4620,6 +4768,7 @@ export {
4620
4768
  GoogleVoiceAgent,
4621
4769
  Gpt5Agent,
4622
4770
  GrokAgent,
4771
+ InMemorySpendStore,
4623
4772
  KimiAgent,
4624
4773
  LLM_CONSTANTS,
4625
4774
  MESSAGE_ROLE,
@@ -4646,6 +4795,7 @@ export {
4646
4795
  VOICE_PROVIDER_API_KEY,
4647
4796
  VoiceAgentFactory,
4648
4797
  ZodSchemaConverter,
4798
+ applySpend,
4649
4799
  buildGoogleTtsPrompt,
4650
4800
  calculateAnthropicCost,
4651
4801
  calculateCost,
@@ -4664,6 +4814,7 @@ export {
4664
4814
  cleanResponse,
4665
4815
  createCatalog,
4666
4816
  createVoiceAgent,
4817
+ evaluateBudget,
4667
4818
  extractAnthropicTokenUsage,
4668
4819
  extractTokenUsageFromResponse5 as extractAnthropicTokenUsageFromResponse,
4669
4820
  extractDeepSeekTokenUsage,
@@ -4681,6 +4832,7 @@ export {
4681
4832
  extractTokenUsageFromResponse as extractOpenAITokenUsageFromResponse,
4682
4833
  extractTokenUsage,
4683
4834
  extractUsageAndCalculateCost,
4835
+ firstRefusal,
4684
4836
  generateGoogleTtsAudio,
4685
4837
  generateOpenAiTtsAudio,
4686
4838
  generateSchemaInstructions,
@@ -4689,10 +4841,12 @@ export {
4689
4841
  getModelProviderName,
4690
4842
  getModelTags,
4691
4843
  getProviderSignatureFields,
4844
+ isBudgetExceededError,
4692
4845
  isHybridThinkingModel,
4693
4846
  isInPeakWindow,
4694
4847
  isPeakBilling,
4695
4848
  isWeekendAt,
4849
+ ledgerSpend,
4696
4850
  logger,
4697
4851
  mergeThinking,
4698
4852
  modelHasTag,
@@ -4700,6 +4854,8 @@ export {
4700
4854
  needsPromptBasedSchema,
4701
4855
  parseAndValidateLlmJson,
4702
4856
  pcmToWav,
4857
+ periodEnd,
4858
+ periodKey,
4703
4859
  safeValidateResponse,
4704
4860
  setLlmLogger,
4705
4861
  stableHashHex,