@hiper2d/ai-agents 0.2.1 → 0.3.1

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,7 +1875,157 @@ 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
2025
+ var beforeAskHook;
2026
+ function setBeforeAskHook(hook) {
2027
+ beforeAskHook = hook;
2028
+ }
1879
2029
  var AbstractAgent = class {
1880
2030
  name;
1881
2031
  gameId;
@@ -1927,6 +2077,7 @@ var AbstractAgent = class {
1927
2077
  * must NOT override these.
1928
2078
  */
1929
2079
  async askWithZodSchema(zodSchema, messages) {
2080
+ if (beforeAskHook) await beforeAskHook(this);
1930
2081
  const startedAt = Date.now();
1931
2082
  try {
1932
2083
  const [result, thinking, usage, signature] = await this.doAskWithZodSchema(zodSchema, messages);
@@ -1937,6 +2088,7 @@ var AbstractAgent = class {
1937
2088
  }
1938
2089
  }
1939
2090
  async askText(messages) {
2091
+ if (beforeAskHook) await beforeAskHook(this);
1940
2092
  const startedAt = Date.now();
1941
2093
  try {
1942
2094
  const [content, thinking, usage, signature] = await this.doAskText(messages);
@@ -4603,6 +4755,8 @@ export {
4603
4755
  AbstractAgent,
4604
4756
  AgentFactory,
4605
4757
  BotResponseError,
4758
+ BudgetController,
4759
+ BudgetExceededError,
4606
4760
  CACHE_TIER_MARKER,
4607
4761
  ClaudeAgent,
4608
4762
  DEEPSEEK_PEAK_SCHEDULE,
@@ -4620,6 +4774,7 @@ export {
4620
4774
  GoogleVoiceAgent,
4621
4775
  Gpt5Agent,
4622
4776
  GrokAgent,
4777
+ InMemorySpendStore,
4623
4778
  KimiAgent,
4624
4779
  LLM_CONSTANTS,
4625
4780
  MESSAGE_ROLE,
@@ -4646,6 +4801,7 @@ export {
4646
4801
  VOICE_PROVIDER_API_KEY,
4647
4802
  VoiceAgentFactory,
4648
4803
  ZodSchemaConverter,
4804
+ applySpend,
4649
4805
  buildGoogleTtsPrompt,
4650
4806
  calculateAnthropicCost,
4651
4807
  calculateCost,
@@ -4664,6 +4820,7 @@ export {
4664
4820
  cleanResponse,
4665
4821
  createCatalog,
4666
4822
  createVoiceAgent,
4823
+ evaluateBudget,
4667
4824
  extractAnthropicTokenUsage,
4668
4825
  extractTokenUsageFromResponse5 as extractAnthropicTokenUsageFromResponse,
4669
4826
  extractDeepSeekTokenUsage,
@@ -4681,6 +4838,7 @@ export {
4681
4838
  extractTokenUsageFromResponse as extractOpenAITokenUsageFromResponse,
4682
4839
  extractTokenUsage,
4683
4840
  extractUsageAndCalculateCost,
4841
+ firstRefusal,
4684
4842
  generateGoogleTtsAudio,
4685
4843
  generateOpenAiTtsAudio,
4686
4844
  generateSchemaInstructions,
@@ -4689,10 +4847,12 @@ export {
4689
4847
  getModelProviderName,
4690
4848
  getModelTags,
4691
4849
  getProviderSignatureFields,
4850
+ isBudgetExceededError,
4692
4851
  isHybridThinkingModel,
4693
4852
  isInPeakWindow,
4694
4853
  isPeakBilling,
4695
4854
  isWeekendAt,
4855
+ ledgerSpend,
4696
4856
  logger,
4697
4857
  mergeThinking,
4698
4858
  modelHasTag,
@@ -4700,7 +4860,10 @@ export {
4700
4860
  needsPromptBasedSchema,
4701
4861
  parseAndValidateLlmJson,
4702
4862
  pcmToWav,
4863
+ periodEnd,
4864
+ periodKey,
4703
4865
  safeValidateResponse,
4866
+ setBeforeAskHook,
4704
4867
  setLlmLogger,
4705
4868
  stableHashHex,
4706
4869
  stripInlineThinking,