veryfront 0.1.379 → 0.1.381
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/esm/deno.js +3 -3
- package/esm/src/agent/hosted-chat-runtime-contract.d.ts +86 -0
- package/esm/src/agent/hosted-chat-runtime-contract.d.ts.map +1 -0
- package/esm/src/agent/hosted-chat-runtime-contract.js +1 -0
- package/esm/src/agent/hosted-runtime-request-config.d.ts +27 -0
- package/esm/src/agent/hosted-runtime-request-config.d.ts.map +1 -0
- package/esm/src/agent/hosted-runtime-request-config.js +54 -0
- package/esm/src/agent/index.d.ts +2 -0
- package/esm/src/agent/index.d.ts.map +1 -1
- package/esm/src/agent/index.js +1 -0
- package/esm/src/agent/middleware/cache/cache.d.ts +27 -0
- package/esm/src/agent/middleware/cache/cache.d.ts.map +1 -0
- package/esm/src/agent/middleware/cache/cache.js +241 -0
- package/esm/src/agent/middleware/cache/index.d.ts +7 -0
- package/esm/src/agent/middleware/cache/index.d.ts.map +1 -0
- package/esm/src/agent/middleware/cache/index.js +6 -0
- package/esm/src/agent/middleware/cost-tracking/index.d.ts +7 -0
- package/esm/src/agent/middleware/cost-tracking/index.d.ts.map +1 -0
- package/esm/src/agent/middleware/cost-tracking/index.js +6 -0
- package/esm/src/agent/middleware/cost-tracking/tracker.d.ts +65 -0
- package/esm/src/agent/middleware/cost-tracking/tracker.d.ts.map +1 -0
- package/esm/src/agent/middleware/cost-tracking/tracker.js +223 -0
- package/esm/src/agent/middleware/index.d.ts +11 -0
- package/esm/src/agent/middleware/index.d.ts.map +1 -0
- package/esm/src/agent/middleware/index.js +10 -0
- package/esm/src/agent/middleware/rate-limit/index.d.ts +7 -0
- package/esm/src/agent/middleware/rate-limit/index.d.ts.map +1 -0
- package/esm/src/agent/middleware/rate-limit/index.js +6 -0
- package/esm/src/agent/middleware/rate-limit/limiter.d.ts +20 -0
- package/esm/src/agent/middleware/rate-limit/limiter.d.ts.map +1 -0
- package/esm/src/agent/middleware/rate-limit/limiter.js +136 -0
- package/esm/src/agent/middleware/security/index.d.ts +7 -0
- package/esm/src/agent/middleware/security/index.d.ts.map +1 -0
- package/esm/src/agent/middleware/security/index.js +6 -0
- package/esm/src/prompt/factory.js +1 -1
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/deno.js +3 -3
- package/src/deps/esm.sh/@types/react-dom@19.2.3/client.d.ts +1 -1
- package/src/deps/esm.sh/@types/{react@19.2.14 → react@19.2.3}/global.d.ts +0 -1
- package/src/deps/esm.sh/@types/{react@19.2.14 → react@19.2.3}/index.d.ts +24 -93
- package/src/deps/esm.sh/react-dom@19.2.4/client.d.ts +1 -1
- package/src/src/agent/hosted-chat-runtime-contract.ts +101 -0
- package/src/src/agent/hosted-runtime-request-config.ts +112 -0
- package/src/src/agent/index.ts +22 -0
- package/src/src/agent/middleware/cache/cache.ts +318 -0
- package/src/src/agent/middleware/cache/index.ts +7 -0
- package/src/src/agent/middleware/cost-tracking/index.ts +13 -0
- package/src/src/agent/middleware/cost-tracking/tracker.ts +351 -0
- package/src/src/agent/middleware/index.ts +33 -0
- package/src/src/agent/middleware/rate-limit/index.ts +12 -0
- package/src/src/agent/middleware/rate-limit/limiter.ts +197 -0
- package/src/src/agent/middleware/security/index.ts +14 -0
- package/src/src/prompt/factory.ts +1 -1
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { AgentMiddleware, AgentResponse } from "../../types.js";
|
|
2
|
+
export interface CostConfig {
|
|
3
|
+
/** Provider pricing (cost per 1M tokens) */
|
|
4
|
+
pricing: {
|
|
5
|
+
[provider: string]: {
|
|
6
|
+
input: number;
|
|
7
|
+
output: number;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
limits?: {
|
|
11
|
+
daily?: number;
|
|
12
|
+
monthly?: number;
|
|
13
|
+
/** Per-user daily cost limit */
|
|
14
|
+
userDaily?: number;
|
|
15
|
+
};
|
|
16
|
+
/** Maximum number of per-user cost entries to retain (default: 10_000) */
|
|
17
|
+
maxTrackedUsers?: number;
|
|
18
|
+
onLimitExceeded?: (usage: UsageSummary) => void;
|
|
19
|
+
}
|
|
20
|
+
export interface UsageRecord {
|
|
21
|
+
timestamp: number;
|
|
22
|
+
agentId: string;
|
|
23
|
+
model: string;
|
|
24
|
+
provider: string;
|
|
25
|
+
tokens: {
|
|
26
|
+
prompt: number;
|
|
27
|
+
completion: number;
|
|
28
|
+
total: number;
|
|
29
|
+
};
|
|
30
|
+
cost: number;
|
|
31
|
+
userId?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface UsageSummary {
|
|
34
|
+
requests: number;
|
|
35
|
+
tokens: {
|
|
36
|
+
prompt: number;
|
|
37
|
+
completion: number;
|
|
38
|
+
total: number;
|
|
39
|
+
};
|
|
40
|
+
cost: number;
|
|
41
|
+
byProvider: Record<string, {
|
|
42
|
+
requests: number;
|
|
43
|
+
tokens: number;
|
|
44
|
+
cost: number;
|
|
45
|
+
}>;
|
|
46
|
+
period: {
|
|
47
|
+
start: number;
|
|
48
|
+
end: number;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export declare function createCostTracker(config: CostConfig): {
|
|
52
|
+
track: (agentId: string, model: string, response: AgentResponse, userId?: string) => UsageRecord;
|
|
53
|
+
isOverBudget: (userId?: string) => string | null;
|
|
54
|
+
getSummary: (startTime?: number, endTime?: number) => UsageSummary;
|
|
55
|
+
getDailySummary: () => UsageSummary;
|
|
56
|
+
getMonthlySummary: () => UsageSummary;
|
|
57
|
+
getAllRecords: () => UsageRecord[];
|
|
58
|
+
getTrackedUserCount: () => number;
|
|
59
|
+
clear: () => void;
|
|
60
|
+
destroy: () => void;
|
|
61
|
+
};
|
|
62
|
+
export declare function costTrackingMiddleware(config: CostConfig): AgentMiddleware & {
|
|
63
|
+
destroy(): void;
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../../../../src/src/agent/middleware/cost-tracking/tracker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAQrE,MAAM,WAAW,UAAU;IACzB,4CAA4C;IAC5C,OAAO,EAAE;QACP,CAAC,QAAQ,EAAE,MAAM,GAAG;YAClB,KAAK,EAAE,MAAM,CAAC;YACd,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC;KACH,CAAC;IACF,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gCAAgC;QAChC,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE;QACN,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE;QACN,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAChB,MAAM,EACN;QACE,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,CACF,CAAC;IACF,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAwOD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG;IACrD,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,WAAW,CAAC;IACjG,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACjD,UAAU,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,YAAY,CAAC;IACnE,eAAe,EAAE,MAAM,YAAY,CAAC;IACpC,iBAAiB,EAAE,MAAM,YAAY,CAAC;IACtC,aAAa,EAAE,MAAM,WAAW,EAAE,CAAC;IACnC,mBAAmB,EAAE,MAAM,MAAM,CAAC;IAClC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAcA;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,UAAU,GACjB,eAAe,GAAG;IAAE,OAAO,IAAI,IAAI,CAAA;CAAE,CA4BvC"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import * as dntShim from "../../../../_dnt.shims.js";
|
|
2
|
+
import { agentLogger } from "../../../utils/logger/logger.js";
|
|
3
|
+
import { COST_LIMIT_EXCEEDED } from "../../../errors/index.js";
|
|
4
|
+
const ONE_DAY_MS = 24 * 60 * 60 * 1_000;
|
|
5
|
+
const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1_000;
|
|
6
|
+
const RESET_CHECK_INTERVAL_MS = 60_000;
|
|
7
|
+
function getProvider(model) {
|
|
8
|
+
return model.split("/")[0] || "unknown";
|
|
9
|
+
}
|
|
10
|
+
class CostTracker {
|
|
11
|
+
config;
|
|
12
|
+
records = [];
|
|
13
|
+
dailyTotal = 0;
|
|
14
|
+
monthlyTotal = 0;
|
|
15
|
+
userDailyTotals = new Map();
|
|
16
|
+
maxTrackedUsers;
|
|
17
|
+
lastDayReset = Date.now();
|
|
18
|
+
lastMonthReset = Date.now();
|
|
19
|
+
resetInterval = null;
|
|
20
|
+
constructor(config) {
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.maxTrackedUsers = config.maxTrackedUsers ?? 10_000;
|
|
23
|
+
this.startPeriodicReset();
|
|
24
|
+
}
|
|
25
|
+
isOverBudget(userId) {
|
|
26
|
+
const dailyLimit = this.config.limits?.daily;
|
|
27
|
+
if (dailyLimit && this.dailyTotal >= dailyLimit) {
|
|
28
|
+
return "Daily cost limit exceeded";
|
|
29
|
+
}
|
|
30
|
+
const monthlyLimit = this.config.limits?.monthly;
|
|
31
|
+
if (monthlyLimit && this.monthlyTotal >= monthlyLimit) {
|
|
32
|
+
return "Monthly cost limit exceeded";
|
|
33
|
+
}
|
|
34
|
+
const userDailyLimit = this.config.limits?.userDaily;
|
|
35
|
+
if (userDailyLimit && userId) {
|
|
36
|
+
const userTotal = this.userDailyTotals.get(userId) ?? 0;
|
|
37
|
+
if (userTotal >= userDailyLimit) {
|
|
38
|
+
return "Per-user daily cost limit exceeded";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
track(agentId, model, response, userId) {
|
|
44
|
+
if (!response.usage) {
|
|
45
|
+
agentLogger.warn("No usage data in response, cannot track costs");
|
|
46
|
+
return this.createEmptyRecord(agentId, model);
|
|
47
|
+
}
|
|
48
|
+
const provider = getProvider(model);
|
|
49
|
+
const cost = this.calculateCost(provider, response.usage.promptTokens, response.usage.completionTokens);
|
|
50
|
+
const record = {
|
|
51
|
+
timestamp: Date.now(),
|
|
52
|
+
agentId,
|
|
53
|
+
model,
|
|
54
|
+
provider,
|
|
55
|
+
tokens: {
|
|
56
|
+
prompt: response.usage.promptTokens,
|
|
57
|
+
completion: response.usage.completionTokens,
|
|
58
|
+
total: response.usage.totalTokens,
|
|
59
|
+
},
|
|
60
|
+
cost,
|
|
61
|
+
userId,
|
|
62
|
+
};
|
|
63
|
+
this.records.push(record);
|
|
64
|
+
this.dailyTotal += cost;
|
|
65
|
+
this.monthlyTotal += cost;
|
|
66
|
+
if (userId && this.maxTrackedUsers > 0) {
|
|
67
|
+
// Keep tracking the current user even after the cap is reached by
|
|
68
|
+
// evicting the oldest tracked user first. This preserves per-user
|
|
69
|
+
// enforcement for active users without unbounded memory growth.
|
|
70
|
+
if (!this.userDailyTotals.has(userId) &&
|
|
71
|
+
this.userDailyTotals.size >= this.maxTrackedUsers) {
|
|
72
|
+
const oldestTrackedUser = this.userDailyTotals.keys().next().value;
|
|
73
|
+
if (oldestTrackedUser !== undefined) {
|
|
74
|
+
this.userDailyTotals.delete(oldestTrackedUser);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
this.userDailyTotals.set(userId, (this.userDailyTotals.get(userId) ?? 0) + cost);
|
|
78
|
+
}
|
|
79
|
+
this.checkLimits(userId);
|
|
80
|
+
return record;
|
|
81
|
+
}
|
|
82
|
+
calculateCost(provider, inputTokens, outputTokens) {
|
|
83
|
+
const pricing = this.config.pricing[provider];
|
|
84
|
+
if (!pricing) {
|
|
85
|
+
agentLogger.warn(`No pricing configured for provider: ${provider}`);
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
return (inputTokens / 1_000_000) * pricing.input + (outputTokens / 1_000_000) * pricing.output;
|
|
89
|
+
}
|
|
90
|
+
getSummary(startTime, endTime) {
|
|
91
|
+
const start = startTime ?? 0;
|
|
92
|
+
const end = endTime ?? Date.now();
|
|
93
|
+
const summary = {
|
|
94
|
+
requests: 0,
|
|
95
|
+
tokens: { prompt: 0, completion: 0, total: 0 },
|
|
96
|
+
cost: 0,
|
|
97
|
+
byProvider: {},
|
|
98
|
+
period: { start, end },
|
|
99
|
+
};
|
|
100
|
+
for (const record of this.records) {
|
|
101
|
+
if (record.timestamp < start || record.timestamp > end)
|
|
102
|
+
continue;
|
|
103
|
+
summary.requests++;
|
|
104
|
+
summary.tokens.prompt += record.tokens.prompt;
|
|
105
|
+
summary.tokens.completion += record.tokens.completion;
|
|
106
|
+
summary.tokens.total += record.tokens.total;
|
|
107
|
+
summary.cost += record.cost;
|
|
108
|
+
const providerStats = (summary.byProvider[record.provider] ??= {
|
|
109
|
+
requests: 0,
|
|
110
|
+
tokens: 0,
|
|
111
|
+
cost: 0,
|
|
112
|
+
});
|
|
113
|
+
providerStats.requests++;
|
|
114
|
+
providerStats.tokens += record.tokens.total;
|
|
115
|
+
providerStats.cost += record.cost;
|
|
116
|
+
}
|
|
117
|
+
return summary;
|
|
118
|
+
}
|
|
119
|
+
getDailySummary() {
|
|
120
|
+
const now = Date.now();
|
|
121
|
+
return this.getSummary(now - ONE_DAY_MS, now);
|
|
122
|
+
}
|
|
123
|
+
getMonthlySummary() {
|
|
124
|
+
const now = Date.now();
|
|
125
|
+
return this.getSummary(now - THIRTY_DAYS_MS, now);
|
|
126
|
+
}
|
|
127
|
+
checkLimits(userId) {
|
|
128
|
+
const dailyLimit = this.config.limits?.daily;
|
|
129
|
+
if (dailyLimit && this.dailyTotal >= dailyLimit) {
|
|
130
|
+
this.config.onLimitExceeded?.(this.getDailySummary());
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const monthlyLimit = this.config.limits?.monthly;
|
|
134
|
+
if (monthlyLimit && this.monthlyTotal >= monthlyLimit) {
|
|
135
|
+
this.config.onLimitExceeded?.(this.getMonthlySummary());
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const userDailyLimit = this.config.limits?.userDaily;
|
|
139
|
+
if (userDailyLimit && userId) {
|
|
140
|
+
const userTotal = this.userDailyTotals.get(userId) ?? 0;
|
|
141
|
+
if (userTotal >= userDailyLimit) {
|
|
142
|
+
this.config.onLimitExceeded?.(this.getDailySummary());
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
startPeriodicReset() {
|
|
147
|
+
this.resetInterval = dntShim.setInterval(() => {
|
|
148
|
+
const now = Date.now();
|
|
149
|
+
if (now - this.lastDayReset >= ONE_DAY_MS) {
|
|
150
|
+
this.dailyTotal = 0;
|
|
151
|
+
this.userDailyTotals.clear();
|
|
152
|
+
this.lastDayReset = now;
|
|
153
|
+
}
|
|
154
|
+
if (now - this.lastMonthReset >= THIRTY_DAYS_MS) {
|
|
155
|
+
this.monthlyTotal = 0;
|
|
156
|
+
this.lastMonthReset = now;
|
|
157
|
+
}
|
|
158
|
+
}, RESET_CHECK_INTERVAL_MS);
|
|
159
|
+
}
|
|
160
|
+
destroy() {
|
|
161
|
+
if (this.resetInterval) {
|
|
162
|
+
clearInterval(this.resetInterval);
|
|
163
|
+
this.resetInterval = null;
|
|
164
|
+
}
|
|
165
|
+
this.clear();
|
|
166
|
+
}
|
|
167
|
+
createEmptyRecord(agentId, model) {
|
|
168
|
+
return {
|
|
169
|
+
timestamp: Date.now(),
|
|
170
|
+
agentId,
|
|
171
|
+
model,
|
|
172
|
+
provider: getProvider(model),
|
|
173
|
+
tokens: { prompt: 0, completion: 0, total: 0 },
|
|
174
|
+
cost: 0,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
getAllRecords() {
|
|
178
|
+
return [...this.records];
|
|
179
|
+
}
|
|
180
|
+
getTrackedUserCount() {
|
|
181
|
+
return this.userDailyTotals.size;
|
|
182
|
+
}
|
|
183
|
+
clear() {
|
|
184
|
+
this.records = [];
|
|
185
|
+
this.dailyTotal = 0;
|
|
186
|
+
this.monthlyTotal = 0;
|
|
187
|
+
this.userDailyTotals.clear();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
export function createCostTracker(config) {
|
|
191
|
+
const tracker = new CostTracker(config);
|
|
192
|
+
return {
|
|
193
|
+
track: tracker.track.bind(tracker),
|
|
194
|
+
isOverBudget: tracker.isOverBudget.bind(tracker),
|
|
195
|
+
getSummary: tracker.getSummary.bind(tracker),
|
|
196
|
+
getDailySummary: tracker.getDailySummary.bind(tracker),
|
|
197
|
+
getMonthlySummary: tracker.getMonthlySummary.bind(tracker),
|
|
198
|
+
getAllRecords: tracker.getAllRecords.bind(tracker),
|
|
199
|
+
getTrackedUserCount: tracker.getTrackedUserCount.bind(tracker),
|
|
200
|
+
clear: tracker.clear.bind(tracker),
|
|
201
|
+
destroy: tracker.destroy.bind(tracker),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
export function costTrackingMiddleware(config) {
|
|
205
|
+
const tracker = createCostTracker(config);
|
|
206
|
+
const middleware = (async (context, next) => {
|
|
207
|
+
const userId = context.data?.userId;
|
|
208
|
+
const budgetError = tracker.isOverBudget(userId);
|
|
209
|
+
if (budgetError) {
|
|
210
|
+
throw COST_LIMIT_EXCEEDED.create({
|
|
211
|
+
detail: budgetError,
|
|
212
|
+
context: { userId },
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
const result = await next();
|
|
216
|
+
tracker.track(context.agentId, context.model || "unknown", result, userId);
|
|
217
|
+
return result;
|
|
218
|
+
});
|
|
219
|
+
middleware.destroy = () => {
|
|
220
|
+
tracker.destroy();
|
|
221
|
+
};
|
|
222
|
+
return middleware;
|
|
223
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Middleware
|
|
3
|
+
*
|
|
4
|
+
* @module agent/middleware
|
|
5
|
+
*/
|
|
6
|
+
export { createMiddlewareChain, MiddlewareChain } from "./chain.js";
|
|
7
|
+
export { createRateLimiter, type RateLimitConfig, rateLimitMiddleware, type RateLimitResult, } from "./rate-limit/index.js";
|
|
8
|
+
export { type CacheConfig, type CacheEntry, cacheMiddleware, createCache } from "./cache/index.js";
|
|
9
|
+
export { type CostConfig, costTrackingMiddleware, createCostTracker, type UsageRecord, type UsageSummary, } from "./cost-tracking/index.js";
|
|
10
|
+
export { COMMON_BLOCKED_PATTERNS, InputValidator, OutputFilter, type SecurityConfig, securityMiddleware, type SecurityViolation, } from "./security/index.js";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/middleware/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEpE,OAAO,EACL,iBAAiB,EACjB,KAAK,eAAe,EACpB,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEnG,OAAO,EACL,KAAK,UAAU,EACf,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,YAAY,EACZ,KAAK,cAAc,EACnB,kBAAkB,EAClB,KAAK,iBAAiB,GACvB,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Middleware
|
|
3
|
+
*
|
|
4
|
+
* @module agent/middleware
|
|
5
|
+
*/
|
|
6
|
+
export { createMiddlewareChain, MiddlewareChain } from "./chain.js";
|
|
7
|
+
export { createRateLimiter, rateLimitMiddleware, } from "./rate-limit/index.js";
|
|
8
|
+
export { cacheMiddleware, createCache } from "./cache/index.js";
|
|
9
|
+
export { costTrackingMiddleware, createCostTracker, } from "./cost-tracking/index.js";
|
|
10
|
+
export { COMMON_BLOCKED_PATTERNS, InputValidator, OutputFilter, securityMiddleware, } from "./security/index.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/agent/middleware/rate-limit/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,iBAAiB,EACjB,KAAK,eAAe,EACpB,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface RateLimitConfig {
|
|
2
|
+
strategy: "fixed-window" | "sliding-window" | "token-bucket";
|
|
3
|
+
maxRequests: number;
|
|
4
|
+
windowMs: number;
|
|
5
|
+
identify?: (context: Record<string, unknown>) => string;
|
|
6
|
+
errorMessage?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface RateLimitResult {
|
|
9
|
+
allowed: boolean;
|
|
10
|
+
remaining: number;
|
|
11
|
+
resetAt: number;
|
|
12
|
+
retryAfter?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function createRateLimiter(config: RateLimitConfig): {
|
|
15
|
+
check: (context?: Record<string, unknown>) => RateLimitResult;
|
|
16
|
+
reset: (context?: Record<string, unknown>) => void;
|
|
17
|
+
clear: () => void;
|
|
18
|
+
};
|
|
19
|
+
export declare function rateLimitMiddleware(config: RateLimitConfig): <T>(context: Record<string, unknown>, next: () => Promise<T>) => Promise<T>;
|
|
20
|
+
//# sourceMappingURL=limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"limiter.d.ts","sourceRoot":"","sources":["../../../../../src/src/agent/middleware/rate-limit/limiter.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,cAAc,GAAG,gBAAgB,GAAG,cAAc,CAAC;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAoHD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG;IAC1D,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,eAAe,CAAC;IAC9D,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACnD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAkBA;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,eAAe,GACtB,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAsC7E"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { createError, toError } from "../../../errors/veryfront-error.js";
|
|
2
|
+
import { setActiveSpanAttributes, withSpan } from "../../../observability/tracing/otlp-setup.js";
|
|
3
|
+
class FixedWindowLimiter {
|
|
4
|
+
config;
|
|
5
|
+
requests = new Map();
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
}
|
|
9
|
+
check(identifier) {
|
|
10
|
+
const now = Date.now();
|
|
11
|
+
const entry = this.requests.get(identifier);
|
|
12
|
+
if (!entry || now >= entry.resetAt) {
|
|
13
|
+
const resetAt = now + this.config.windowMs;
|
|
14
|
+
this.requests.set(identifier, { count: 1, resetAt });
|
|
15
|
+
return {
|
|
16
|
+
allowed: true,
|
|
17
|
+
remaining: this.config.maxRequests - 1,
|
|
18
|
+
resetAt,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (entry.count >= this.config.maxRequests) {
|
|
22
|
+
return {
|
|
23
|
+
allowed: false,
|
|
24
|
+
remaining: 0,
|
|
25
|
+
resetAt: entry.resetAt,
|
|
26
|
+
retryAfter: Math.ceil((entry.resetAt - now) / 1000),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
entry.count++;
|
|
30
|
+
return {
|
|
31
|
+
allowed: true,
|
|
32
|
+
remaining: this.config.maxRequests - entry.count,
|
|
33
|
+
resetAt: entry.resetAt,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
reset(identifier) {
|
|
37
|
+
this.requests.delete(identifier);
|
|
38
|
+
}
|
|
39
|
+
clear() {
|
|
40
|
+
this.requests.clear();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
class TokenBucketLimiter {
|
|
44
|
+
config;
|
|
45
|
+
buckets = new Map();
|
|
46
|
+
refillRate;
|
|
47
|
+
constructor(config) {
|
|
48
|
+
this.config = config;
|
|
49
|
+
this.refillRate = config.maxRequests / config.windowMs;
|
|
50
|
+
}
|
|
51
|
+
check(identifier) {
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
const bucket = this.buckets.get(identifier);
|
|
54
|
+
if (!bucket) {
|
|
55
|
+
const tokens = this.config.maxRequests - 1;
|
|
56
|
+
this.buckets.set(identifier, { tokens, lastRefill: now });
|
|
57
|
+
return {
|
|
58
|
+
allowed: true,
|
|
59
|
+
remaining: tokens,
|
|
60
|
+
resetAt: now + this.config.windowMs,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const timePassed = now - bucket.lastRefill;
|
|
64
|
+
bucket.tokens = Math.min(this.config.maxRequests, bucket.tokens + timePassed * this.refillRate);
|
|
65
|
+
bucket.lastRefill = now;
|
|
66
|
+
if (bucket.tokens < 1) {
|
|
67
|
+
const timeUntilToken = (1 - bucket.tokens) / this.refillRate;
|
|
68
|
+
return {
|
|
69
|
+
allowed: false,
|
|
70
|
+
remaining: 0,
|
|
71
|
+
resetAt: now + this.config.windowMs,
|
|
72
|
+
retryAfter: Math.ceil(timeUntilToken / 1000),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
bucket.tokens--;
|
|
76
|
+
return {
|
|
77
|
+
allowed: true,
|
|
78
|
+
remaining: Math.floor(bucket.tokens),
|
|
79
|
+
resetAt: now + this.config.windowMs,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
reset(identifier) {
|
|
83
|
+
this.buckets.delete(identifier);
|
|
84
|
+
}
|
|
85
|
+
clear() {
|
|
86
|
+
this.buckets.clear();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function createLimiterByStrategy(config) {
|
|
90
|
+
if (config.strategy === "fixed-window")
|
|
91
|
+
return new FixedWindowLimiter(config);
|
|
92
|
+
return new TokenBucketLimiter(config);
|
|
93
|
+
}
|
|
94
|
+
export function createRateLimiter(config) {
|
|
95
|
+
const limiter = createLimiterByStrategy(config);
|
|
96
|
+
function getIdentifier(context) {
|
|
97
|
+
return config.identify?.(context ?? {}) ?? "default";
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
check(context) {
|
|
101
|
+
return limiter.check(getIdentifier(context));
|
|
102
|
+
},
|
|
103
|
+
reset(context) {
|
|
104
|
+
limiter.reset(getIdentifier(context));
|
|
105
|
+
},
|
|
106
|
+
clear() {
|
|
107
|
+
limiter.clear();
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export function rateLimitMiddleware(config) {
|
|
112
|
+
const limiter = createRateLimiter(config);
|
|
113
|
+
return function middleware(context, next) {
|
|
114
|
+
return withSpan("agent.middleware.rateLimit", () => {
|
|
115
|
+
const result = limiter.check(context);
|
|
116
|
+
setActiveSpanAttributes({
|
|
117
|
+
"rateLimit.allowed": result.allowed,
|
|
118
|
+
"rateLimit.remaining": result.remaining,
|
|
119
|
+
"rateLimit.strategy": config.strategy,
|
|
120
|
+
});
|
|
121
|
+
if (result.allowed)
|
|
122
|
+
return next();
|
|
123
|
+
setActiveSpanAttributes({
|
|
124
|
+
"rateLimit.retryAfter": result.retryAfter ?? 0,
|
|
125
|
+
});
|
|
126
|
+
throw toError(createError({
|
|
127
|
+
type: "agent",
|
|
128
|
+
message: config.errorMessage ??
|
|
129
|
+
`Rate limit exceeded. Try again in ${result.retryAfter} seconds.`,
|
|
130
|
+
}));
|
|
131
|
+
}, {
|
|
132
|
+
"rateLimit.strategy": config.strategy,
|
|
133
|
+
"rateLimit.maxRequests": config.maxRequests,
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware - Security
|
|
3
|
+
*
|
|
4
|
+
* @module agent/middleware/security
|
|
5
|
+
*/
|
|
6
|
+
export { COMMON_BLOCKED_PATTERNS, InputValidator, OutputFilter, type SecurityConfig, securityMiddleware, type SecurityViolation, } from "./validator.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/agent/middleware/security/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,YAAY,EACZ,KAAK,cAAc,EACnB,kBAAkB,EAClB,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createError, toError } from "../errors/veryfront-error.js";
|
|
2
|
-
import { COMMON_BLOCKED_PATTERNS } from "../agent/middleware/
|
|
2
|
+
import { COMMON_BLOCKED_PATTERNS } from "../agent/middleware/index.js";
|
|
3
3
|
const BLOCKED_PROMPT_PATTERNS = COMMON_BLOCKED_PATTERNS.promptInjection;
|
|
4
4
|
export function prompt(config) {
|
|
5
5
|
const { content, description, generate, suggestion } = config;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.381";
|
|
2
2
|
//# sourceMappingURL=version-constant.d.ts.map
|
package/package.json
CHANGED
package/src/deno.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.381",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"nodeModulesDir": "auto",
|
|
6
6
|
"workspace": [
|
|
@@ -343,8 +343,8 @@ export default {
|
|
|
343
343
|
"test:coverage": "rm -rf coverage && VF_DISABLE_LRU_INTERVAL=1 SSR_TRANSFORM_PER_PROJECT_LIMIT=0 REVALIDATION_PER_PROJECT_LIMIT=0 NODE_ENV=production LOG_FORMAT=text deno test --no-check --parallel --fail-fast --allow-all --coverage=coverage '--ignore=tests/integration/compiled-binary-e2e.test.ts' --unstable-worker-options --unstable-net || exit 1",
|
|
344
344
|
"test:coverage:unit": "rm -rf coverage && VF_DISABLE_LRU_INTERVAL=1 SSR_TRANSFORM_PER_PROJECT_LIMIT=0 REVALIDATION_PER_PROJECT_LIMIT=0 NODE_ENV=production LOG_FORMAT=text deno test --no-check --parallel --fail-fast --allow-all --coverage=coverage '--ignore=tests,src/workflow/__tests__' --unstable-worker-options --unstable-net $(find src cli -name '*.test.ts' ! -name '*.integration.test.ts') || exit 1",
|
|
345
345
|
"test:coverage:integration": "rm -rf coverage && VF_DISABLE_LRU_INTERVAL=1 SSR_TRANSFORM_PER_PROJECT_LIMIT=0 REVALIDATION_PER_PROJECT_LIMIT=0 NODE_ENV=production LOG_FORMAT=text deno test --no-check --parallel --fail-fast --allow-all --coverage=coverage '--ignore=tests/e2e,tests/integration/compiled-binary-e2e.test.ts' tests --unstable-worker-options --unstable-net || exit 1",
|
|
346
|
-
"coverage:report": "deno coverage coverage --include=src/ --exclude=tests --exclude=src/**/*_test.ts --exclude=src/**/*_test.tsx --exclude=src/**/*.test.ts --exclude=src/**/*.test.tsx --lcov > coverage/lcov.info && deno run --allow-read scripts/lint/check-coverage.ts 80",
|
|
347
|
-
"coverage:html": "deno coverage coverage --include=src/ --exclude=tests --exclude=src/**/*_test.ts --exclude=src/**/*_test.tsx --exclude=src/**/*.test.ts --exclude=src/**/*.test.tsx --html",
|
|
346
|
+
"coverage:report": "deno coverage coverage --include=src/ --exclude=tests '--exclude=src/**/*_test.ts' '--exclude=src/**/*_test.tsx' '--exclude=src/**/*.test.ts' '--exclude=src/**/*.test.tsx' --lcov > coverage/lcov.info && deno run --allow-read scripts/lint/check-coverage.ts 80",
|
|
347
|
+
"coverage:html": "deno coverage coverage --include=src/ --exclude=tests '--exclude=src/**/*_test.ts' '--exclude=src/**/*_test.tsx' '--exclude=src/**/*.test.ts' '--exclude=src/**/*.test.tsx' --html",
|
|
348
348
|
"bench": "VF_DISABLE_LRU_INTERVAL=1 NODE_ENV=production LOG_FORMAT=text deno bench --no-check --allow-all --unstable-worker-options --unstable-net $(find src -name '*.bench.ts')",
|
|
349
349
|
"clean": "rm -rf .cache/",
|
|
350
350
|
"lint": "DENO_NO_PACKAGE_JSON=1 deno lint src/ cli/",
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
// See https://github.com/facebook/react/blob/main/packages/react-dom/client.js to see how the exports are declared,
|
|
6
6
|
|
|
7
|
-
import React = require("https://esm.sh/@types/react@19.2.
|
|
7
|
+
import React = require("https://esm.sh/@types/react@19.2.3/index.d.ts");
|
|
8
8
|
|
|
9
9
|
export {};
|
|
10
10
|
|
|
@@ -18,7 +18,6 @@ interface KeyboardEvent extends Event {}
|
|
|
18
18
|
interface MouseEvent extends Event {}
|
|
19
19
|
interface TouchEvent extends Event {}
|
|
20
20
|
interface PointerEvent extends Event {}
|
|
21
|
-
interface SubmitEvent extends Event {}
|
|
22
21
|
interface ToggleEvent extends Event {}
|
|
23
22
|
interface TransitionEvent extends Event {}
|
|
24
23
|
interface UIEvent extends Event {}
|