@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/README.md +31 -0
- package/dist/index.d.mts +150 -1
- package/dist/index.d.ts +150 -1
- package/dist/index.js +174 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +163 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -35,6 +35,8 @@ __export(index_exports, {
|
|
|
35
35
|
AbstractAgent: () => AbstractAgent,
|
|
36
36
|
AgentFactory: () => AgentFactory,
|
|
37
37
|
BotResponseError: () => BotResponseError,
|
|
38
|
+
BudgetController: () => BudgetController,
|
|
39
|
+
BudgetExceededError: () => BudgetExceededError,
|
|
38
40
|
CACHE_TIER_MARKER: () => CACHE_TIER_MARKER,
|
|
39
41
|
ClaudeAgent: () => ClaudeAgent,
|
|
40
42
|
DEEPSEEK_PEAK_SCHEDULE: () => DEEPSEEK_PEAK_SCHEDULE,
|
|
@@ -52,6 +54,7 @@ __export(index_exports, {
|
|
|
52
54
|
GoogleVoiceAgent: () => GoogleVoiceAgent,
|
|
53
55
|
Gpt5Agent: () => Gpt5Agent,
|
|
54
56
|
GrokAgent: () => GrokAgent,
|
|
57
|
+
InMemorySpendStore: () => InMemorySpendStore,
|
|
55
58
|
KimiAgent: () => KimiAgent,
|
|
56
59
|
LLM_CONSTANTS: () => LLM_CONSTANTS,
|
|
57
60
|
MESSAGE_ROLE: () => MESSAGE_ROLE,
|
|
@@ -78,6 +81,7 @@ __export(index_exports, {
|
|
|
78
81
|
VOICE_PROVIDER_API_KEY: () => VOICE_PROVIDER_API_KEY,
|
|
79
82
|
VoiceAgentFactory: () => VoiceAgentFactory,
|
|
80
83
|
ZodSchemaConverter: () => ZodSchemaConverter,
|
|
84
|
+
applySpend: () => applySpend,
|
|
81
85
|
buildGoogleTtsPrompt: () => buildGoogleTtsPrompt,
|
|
82
86
|
calculateAnthropicCost: () => calculateAnthropicCost,
|
|
83
87
|
calculateCost: () => calculateCost,
|
|
@@ -96,6 +100,7 @@ __export(index_exports, {
|
|
|
96
100
|
cleanResponse: () => cleanResponse,
|
|
97
101
|
createCatalog: () => createCatalog,
|
|
98
102
|
createVoiceAgent: () => createVoiceAgent,
|
|
103
|
+
evaluateBudget: () => evaluateBudget,
|
|
99
104
|
extractAnthropicTokenUsage: () => extractAnthropicTokenUsage,
|
|
100
105
|
extractAnthropicTokenUsageFromResponse: () => extractTokenUsageFromResponse5,
|
|
101
106
|
extractDeepSeekTokenUsage: () => extractDeepSeekTokenUsage,
|
|
@@ -113,6 +118,7 @@ __export(index_exports, {
|
|
|
113
118
|
extractOpenAITokenUsageFromResponse: () => extractTokenUsageFromResponse,
|
|
114
119
|
extractTokenUsage: () => extractTokenUsage,
|
|
115
120
|
extractUsageAndCalculateCost: () => extractUsageAndCalculateCost,
|
|
121
|
+
firstRefusal: () => firstRefusal,
|
|
116
122
|
generateGoogleTtsAudio: () => generateGoogleTtsAudio,
|
|
117
123
|
generateOpenAiTtsAudio: () => generateOpenAiTtsAudio,
|
|
118
124
|
generateSchemaInstructions: () => generateSchemaInstructions,
|
|
@@ -121,10 +127,12 @@ __export(index_exports, {
|
|
|
121
127
|
getModelProviderName: () => getModelProviderName,
|
|
122
128
|
getModelTags: () => getModelTags,
|
|
123
129
|
getProviderSignatureFields: () => getProviderSignatureFields,
|
|
130
|
+
isBudgetExceededError: () => isBudgetExceededError,
|
|
124
131
|
isHybridThinkingModel: () => isHybridThinkingModel,
|
|
125
132
|
isInPeakWindow: () => isInPeakWindow,
|
|
126
133
|
isPeakBilling: () => isPeakBilling,
|
|
127
134
|
isWeekendAt: () => isWeekendAt,
|
|
135
|
+
ledgerSpend: () => ledgerSpend,
|
|
128
136
|
logger: () => logger,
|
|
129
137
|
mergeThinking: () => mergeThinking,
|
|
130
138
|
modelHasTag: () => modelHasTag,
|
|
@@ -132,7 +140,10 @@ __export(index_exports, {
|
|
|
132
140
|
needsPromptBasedSchema: () => needsPromptBasedSchema,
|
|
133
141
|
parseAndValidateLlmJson: () => parseAndValidateLlmJson,
|
|
134
142
|
pcmToWav: () => pcmToWav,
|
|
143
|
+
periodEnd: () => periodEnd,
|
|
144
|
+
periodKey: () => periodKey,
|
|
135
145
|
safeValidateResponse: () => safeValidateResponse,
|
|
146
|
+
setBeforeAskHook: () => setBeforeAskHook,
|
|
136
147
|
setLlmLogger: () => setLlmLogger,
|
|
137
148
|
stableHashHex: () => stableHashHex,
|
|
138
149
|
stripInlineThinking: () => stripInlineThinking,
|
|
@@ -2026,7 +2037,157 @@ function createVoiceAgent(provider, apiKey) {
|
|
|
2026
2037
|
return VoiceAgentFactory.createAgent(provider, apiKey);
|
|
2027
2038
|
}
|
|
2028
2039
|
|
|
2040
|
+
// src/budget/index.ts
|
|
2041
|
+
function round6(n) {
|
|
2042
|
+
return parseFloat((Number(n) || 0).toFixed(6));
|
|
2043
|
+
}
|
|
2044
|
+
function periodKey(timestamp, window) {
|
|
2045
|
+
const d = new Date(timestamp);
|
|
2046
|
+
const y = d.getUTCFullYear();
|
|
2047
|
+
const m = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
2048
|
+
if (window === "month") {
|
|
2049
|
+
return `${y}-${m}`;
|
|
2050
|
+
}
|
|
2051
|
+
const day = String(d.getUTCDate()).padStart(2, "0");
|
|
2052
|
+
return `${y}-${m}-${day}`;
|
|
2053
|
+
}
|
|
2054
|
+
function periodEnd(timestamp, window) {
|
|
2055
|
+
const d = new Date(timestamp);
|
|
2056
|
+
if (window === "month") {
|
|
2057
|
+
return Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 1);
|
|
2058
|
+
}
|
|
2059
|
+
return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() + 1);
|
|
2060
|
+
}
|
|
2061
|
+
function normalizeLedger(ledger, period) {
|
|
2062
|
+
if (!ledger || ledger.period !== period) {
|
|
2063
|
+
return { period, totalUSD: 0, buckets: {} };
|
|
2064
|
+
}
|
|
2065
|
+
const buckets = {};
|
|
2066
|
+
for (const [k, v] of Object.entries(ledger.buckets ?? {})) {
|
|
2067
|
+
buckets[k] = round6(v);
|
|
2068
|
+
}
|
|
2069
|
+
return { period, totalUSD: round6(ledger.totalUSD ?? 0), buckets };
|
|
2070
|
+
}
|
|
2071
|
+
function applySpend(ledger, input) {
|
|
2072
|
+
const timestamp = input.timestamp ?? Date.now();
|
|
2073
|
+
const period = periodKey(timestamp, input.window);
|
|
2074
|
+
const current2 = normalizeLedger(ledger, period);
|
|
2075
|
+
const amount = round6(input.amountUSD);
|
|
2076
|
+
if (!(amount > 0)) {
|
|
2077
|
+
return current2;
|
|
2078
|
+
}
|
|
2079
|
+
const buckets = { ...current2.buckets };
|
|
2080
|
+
if (input.bucket) {
|
|
2081
|
+
buckets[input.bucket] = round6((buckets[input.bucket] ?? 0) + amount);
|
|
2082
|
+
}
|
|
2083
|
+
return { period, totalUSD: round6(current2.totalUSD + amount), buckets };
|
|
2084
|
+
}
|
|
2085
|
+
function ledgerSpend(ledger, window, timestamp = Date.now(), bucket) {
|
|
2086
|
+
const current2 = normalizeLedger(ledger, periodKey(timestamp, window));
|
|
2087
|
+
return bucket ? current2.buckets[bucket] ?? 0 : current2.totalUSD;
|
|
2088
|
+
}
|
|
2089
|
+
function evaluateBudget(spentUSD, limit, timestamp = Date.now()) {
|
|
2090
|
+
const spent = round6(spentUSD);
|
|
2091
|
+
const unlimited = !Number.isFinite(limit.limitUSD);
|
|
2092
|
+
const remaining = unlimited ? Number.POSITIVE_INFINITY : Math.max(0, round6(limit.limitUSD - spent));
|
|
2093
|
+
return {
|
|
2094
|
+
allowed: unlimited || spent < limit.limitUSD,
|
|
2095
|
+
window: limit.window,
|
|
2096
|
+
...limit.bucket ? { bucket: limit.bucket } : {},
|
|
2097
|
+
limitUSD: limit.limitUSD,
|
|
2098
|
+
spentUSD: spent,
|
|
2099
|
+
remainingUSD: remaining,
|
|
2100
|
+
resetsAt: periodEnd(timestamp, limit.window)
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
function firstRefusal(verdicts) {
|
|
2104
|
+
return verdicts.find((v) => !v.allowed);
|
|
2105
|
+
}
|
|
2106
|
+
var BudgetExceededError = class _BudgetExceededError extends Error {
|
|
2107
|
+
verdict;
|
|
2108
|
+
subject;
|
|
2109
|
+
constructor(verdict, subject, message) {
|
|
2110
|
+
super(message ?? _BudgetExceededError.describe(verdict));
|
|
2111
|
+
this.name = "BudgetExceededError";
|
|
2112
|
+
this.verdict = verdict;
|
|
2113
|
+
this.subject = subject;
|
|
2114
|
+
}
|
|
2115
|
+
static describe(v) {
|
|
2116
|
+
const when = v.window === "day" ? "daily" : "monthly";
|
|
2117
|
+
return `${when} budget of $${v.limitUSD} exhausted ($${v.spentUSD} spent); resets at ${new Date(v.resetsAt).toISOString()}`;
|
|
2118
|
+
}
|
|
2119
|
+
};
|
|
2120
|
+
function isBudgetExceededError(err) {
|
|
2121
|
+
return err instanceof BudgetExceededError || typeof err === "object" && err !== null && err.name === "BudgetExceededError" && !!err.verdict;
|
|
2122
|
+
}
|
|
2123
|
+
var InMemorySpendStore = class {
|
|
2124
|
+
ledgers = /* @__PURE__ */ new Map();
|
|
2125
|
+
async read(subject) {
|
|
2126
|
+
return { ...this.ledgers.get(subject) ?? {} };
|
|
2127
|
+
}
|
|
2128
|
+
async update(subject, fn) {
|
|
2129
|
+
const next = fn({ ...this.ledgers.get(subject) ?? {} });
|
|
2130
|
+
this.ledgers.set(subject, next);
|
|
2131
|
+
return { ...next };
|
|
2132
|
+
}
|
|
2133
|
+
};
|
|
2134
|
+
var BudgetController = class {
|
|
2135
|
+
constructor(store, options) {
|
|
2136
|
+
this.store = store;
|
|
2137
|
+
this.limits = options.limits;
|
|
2138
|
+
this.bucket = options.bucket;
|
|
2139
|
+
this.clock = options.clock ?? (() => Date.now());
|
|
2140
|
+
}
|
|
2141
|
+
store;
|
|
2142
|
+
limits;
|
|
2143
|
+
bucket;
|
|
2144
|
+
clock;
|
|
2145
|
+
verdicts(ledgers, now) {
|
|
2146
|
+
return this.limits.map((limit) => {
|
|
2147
|
+
const spent = ledgerSpend(ledgers[limit.window], limit.window, now, limit.bucket ?? this.bucket);
|
|
2148
|
+
return evaluateBudget(spent, limit, now);
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
2151
|
+
async check(subject) {
|
|
2152
|
+
return this.verdicts(await this.store.read(subject), this.clock());
|
|
2153
|
+
}
|
|
2154
|
+
async assertWithinBudget(subject) {
|
|
2155
|
+
const refused = firstRefusal(await this.check(subject));
|
|
2156
|
+
if (refused) {
|
|
2157
|
+
throw new BudgetExceededError(refused, subject);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
/**
|
|
2161
|
+
* Record `amountUSD` against every limited window. Re-checks the limits on the
|
|
2162
|
+
* ledgers as they are at write time and throws `BudgetExceededError` (writing
|
|
2163
|
+
* nothing) if any window is already exhausted.
|
|
2164
|
+
*/
|
|
2165
|
+
async record(subject, amountUSD) {
|
|
2166
|
+
const now = this.clock();
|
|
2167
|
+
return this.store.update(subject, (current2) => {
|
|
2168
|
+
const refused = firstRefusal(this.verdicts(current2, now));
|
|
2169
|
+
if (refused) {
|
|
2170
|
+
throw new BudgetExceededError(refused, subject);
|
|
2171
|
+
}
|
|
2172
|
+
const next = { ...current2 };
|
|
2173
|
+
for (const limit of this.limits) {
|
|
2174
|
+
next[limit.window] = applySpend(current2[limit.window], {
|
|
2175
|
+
window: limit.window,
|
|
2176
|
+
amountUSD,
|
|
2177
|
+
bucket: limit.bucket ?? this.bucket,
|
|
2178
|
+
timestamp: now
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
2181
|
+
return next;
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
};
|
|
2185
|
+
|
|
2029
2186
|
// src/agents/abstract-agent.ts
|
|
2187
|
+
var beforeAskHook;
|
|
2188
|
+
function setBeforeAskHook(hook) {
|
|
2189
|
+
beforeAskHook = hook;
|
|
2190
|
+
}
|
|
2030
2191
|
var AbstractAgent = class {
|
|
2031
2192
|
name;
|
|
2032
2193
|
gameId;
|
|
@@ -2078,6 +2239,7 @@ var AbstractAgent = class {
|
|
|
2078
2239
|
* must NOT override these.
|
|
2079
2240
|
*/
|
|
2080
2241
|
async askWithZodSchema(zodSchema, messages) {
|
|
2242
|
+
if (beforeAskHook) await beforeAskHook(this);
|
|
2081
2243
|
const startedAt = Date.now();
|
|
2082
2244
|
try {
|
|
2083
2245
|
const [result, thinking, usage, signature] = await this.doAskWithZodSchema(zodSchema, messages);
|
|
@@ -2088,6 +2250,7 @@ var AbstractAgent = class {
|
|
|
2088
2250
|
}
|
|
2089
2251
|
}
|
|
2090
2252
|
async askText(messages) {
|
|
2253
|
+
if (beforeAskHook) await beforeAskHook(this);
|
|
2091
2254
|
const startedAt = Date.now();
|
|
2092
2255
|
try {
|
|
2093
2256
|
const [content, thinking, usage, signature] = await this.doAskText(messages);
|
|
@@ -4755,6 +4918,8 @@ var AgentFactory = class {
|
|
|
4755
4918
|
AbstractAgent,
|
|
4756
4919
|
AgentFactory,
|
|
4757
4920
|
BotResponseError,
|
|
4921
|
+
BudgetController,
|
|
4922
|
+
BudgetExceededError,
|
|
4758
4923
|
CACHE_TIER_MARKER,
|
|
4759
4924
|
ClaudeAgent,
|
|
4760
4925
|
DEEPSEEK_PEAK_SCHEDULE,
|
|
@@ -4772,6 +4937,7 @@ var AgentFactory = class {
|
|
|
4772
4937
|
GoogleVoiceAgent,
|
|
4773
4938
|
Gpt5Agent,
|
|
4774
4939
|
GrokAgent,
|
|
4940
|
+
InMemorySpendStore,
|
|
4775
4941
|
KimiAgent,
|
|
4776
4942
|
LLM_CONSTANTS,
|
|
4777
4943
|
MESSAGE_ROLE,
|
|
@@ -4798,6 +4964,7 @@ var AgentFactory = class {
|
|
|
4798
4964
|
VOICE_PROVIDER_API_KEY,
|
|
4799
4965
|
VoiceAgentFactory,
|
|
4800
4966
|
ZodSchemaConverter,
|
|
4967
|
+
applySpend,
|
|
4801
4968
|
buildGoogleTtsPrompt,
|
|
4802
4969
|
calculateAnthropicCost,
|
|
4803
4970
|
calculateCost,
|
|
@@ -4816,6 +4983,7 @@ var AgentFactory = class {
|
|
|
4816
4983
|
cleanResponse,
|
|
4817
4984
|
createCatalog,
|
|
4818
4985
|
createVoiceAgent,
|
|
4986
|
+
evaluateBudget,
|
|
4819
4987
|
extractAnthropicTokenUsage,
|
|
4820
4988
|
extractAnthropicTokenUsageFromResponse,
|
|
4821
4989
|
extractDeepSeekTokenUsage,
|
|
@@ -4833,6 +5001,7 @@ var AgentFactory = class {
|
|
|
4833
5001
|
extractOpenAITokenUsageFromResponse,
|
|
4834
5002
|
extractTokenUsage,
|
|
4835
5003
|
extractUsageAndCalculateCost,
|
|
5004
|
+
firstRefusal,
|
|
4836
5005
|
generateGoogleTtsAudio,
|
|
4837
5006
|
generateOpenAiTtsAudio,
|
|
4838
5007
|
generateSchemaInstructions,
|
|
@@ -4841,10 +5010,12 @@ var AgentFactory = class {
|
|
|
4841
5010
|
getModelProviderName,
|
|
4842
5011
|
getModelTags,
|
|
4843
5012
|
getProviderSignatureFields,
|
|
5013
|
+
isBudgetExceededError,
|
|
4844
5014
|
isHybridThinkingModel,
|
|
4845
5015
|
isInPeakWindow,
|
|
4846
5016
|
isPeakBilling,
|
|
4847
5017
|
isWeekendAt,
|
|
5018
|
+
ledgerSpend,
|
|
4848
5019
|
logger,
|
|
4849
5020
|
mergeThinking,
|
|
4850
5021
|
modelHasTag,
|
|
@@ -4852,7 +5023,10 @@ var AgentFactory = class {
|
|
|
4852
5023
|
needsPromptBasedSchema,
|
|
4853
5024
|
parseAndValidateLlmJson,
|
|
4854
5025
|
pcmToWav,
|
|
5026
|
+
periodEnd,
|
|
5027
|
+
periodKey,
|
|
4855
5028
|
safeValidateResponse,
|
|
5029
|
+
setBeforeAskHook,
|
|
4856
5030
|
setLlmLogger,
|
|
4857
5031
|
stableHashHex,
|
|
4858
5032
|
stripInlineThinking,
|