@reactive-agents/cost 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tyler Buell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # @reactive-agents/cost
2
+
3
+ Cost management for the [Reactive Agents](https://tylerjrbuell.github.io/reactive-agents-ts/) framework.
4
+
5
+ Automatically routes tasks to the cheapest capable model and enforces per-request and session budgets.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ bun add @reactive-agents/cost effect
11
+ ```
12
+
13
+ ## Features
14
+
15
+ - **Complexity router** — classifies tasks and selects Haiku / Sonnet / Opus accordingly
16
+ - **Budget enforcer** — blocks requests that would exceed configured limits
17
+ - **Cost tracking** — accumulates token usage and USD cost per session
18
+
19
+ ## Usage
20
+
21
+ ```typescript
22
+ import { ReactiveAgents } from "reactive-agents";
23
+
24
+ const agent = await ReactiveAgents.create()
25
+ .withName("budget-agent")
26
+ .withProvider("anthropic")
27
+ .withCostTracking({
28
+ maxSessionCost: 1.00, // $1.00 per session
29
+ maxRequestCost: 0.10, // $0.10 per request
30
+ })
31
+ .build();
32
+
33
+ const result = await agent.run("Summarize this document");
34
+ console.log(result.metadata.cost); // { usd: 0.003, tokens: 450 }
35
+ console.log(result.metadata.modelUsed); // "claude-haiku-4-5" (auto-routed)
36
+ ```
37
+
38
+ ## Routing Logic
39
+
40
+ | Task signals | → Model |
41
+ |---|---|
42
+ | Simple, short, factual | Haiku |
43
+ | Analysis, coding, multi-step | Sonnet |
44
+ | Complex reasoning, research | Opus |
45
+
46
+ ## Documentation
47
+
48
+ Full documentation at [tylerjrbuell.github.io/reactive-agents-ts](https://tylerjrbuell.github.io/reactive-agents-ts/)
@@ -0,0 +1,213 @@
1
+ import { Schema, Effect, Context, Layer } from 'effect';
2
+ import * as effect_Cause from 'effect/Cause';
3
+ import * as effect_Types from 'effect/Types';
4
+ import * as effect_Layer from 'effect/Layer';
5
+
6
+ declare const ModelTier: Schema.Literal<["haiku", "sonnet", "opus"]>;
7
+ type ModelTier = typeof ModelTier.Type;
8
+ declare const ModelCostConfigSchema: Schema.Struct<{
9
+ tier: Schema.Literal<["haiku", "sonnet", "opus"]>;
10
+ provider: Schema.Literal<["anthropic", "openai", "ollama"]>;
11
+ model: typeof Schema.String;
12
+ costPer1MInput: typeof Schema.Number;
13
+ costPer1MOutput: typeof Schema.Number;
14
+ maxContext: typeof Schema.Number;
15
+ quality: typeof Schema.Number;
16
+ speedTokensPerSec: typeof Schema.Number;
17
+ }>;
18
+ type ModelCostConfig = typeof ModelCostConfigSchema.Type;
19
+ declare const CostEntrySchema: Schema.Struct<{
20
+ id: typeof Schema.String;
21
+ timestamp: typeof Schema.DateFromSelf;
22
+ agentId: typeof Schema.String;
23
+ sessionId: typeof Schema.String;
24
+ model: typeof Schema.String;
25
+ tier: Schema.Literal<["haiku", "sonnet", "opus"]>;
26
+ inputTokens: typeof Schema.Number;
27
+ outputTokens: typeof Schema.Number;
28
+ cost: typeof Schema.Number;
29
+ cachedHit: typeof Schema.Boolean;
30
+ taskType: typeof Schema.String;
31
+ latencyMs: typeof Schema.Number;
32
+ }>;
33
+ type CostEntry = typeof CostEntrySchema.Type;
34
+ declare const BudgetLimitsSchema: Schema.Struct<{
35
+ perRequest: typeof Schema.Number;
36
+ perSession: typeof Schema.Number;
37
+ daily: typeof Schema.Number;
38
+ monthly: typeof Schema.Number;
39
+ }>;
40
+ type BudgetLimits = typeof BudgetLimitsSchema.Type;
41
+ declare const BudgetStatusSchema: Schema.Struct<{
42
+ currentSession: typeof Schema.Number;
43
+ currentDaily: typeof Schema.Number;
44
+ currentMonthly: typeof Schema.Number;
45
+ limits: Schema.Struct<{
46
+ perRequest: typeof Schema.Number;
47
+ perSession: typeof Schema.Number;
48
+ daily: typeof Schema.Number;
49
+ monthly: typeof Schema.Number;
50
+ }>;
51
+ percentUsedDaily: typeof Schema.Number;
52
+ percentUsedMonthly: typeof Schema.Number;
53
+ }>;
54
+ type BudgetStatus = typeof BudgetStatusSchema.Type;
55
+ declare const CostReportSchema: Schema.Struct<{
56
+ period: Schema.Literal<["session", "daily", "weekly", "monthly"]>;
57
+ totalCost: typeof Schema.Number;
58
+ totalRequests: typeof Schema.Number;
59
+ cacheHits: typeof Schema.Number;
60
+ cacheMisses: typeof Schema.Number;
61
+ cacheHitRate: typeof Schema.Number;
62
+ savings: typeof Schema.Number;
63
+ costByTier: Schema.Record$<typeof Schema.String, typeof Schema.Number>;
64
+ costByAgent: Schema.Record$<typeof Schema.String, typeof Schema.Number>;
65
+ avgCostPerRequest: typeof Schema.Number;
66
+ avgLatencyMs: typeof Schema.Number;
67
+ }>;
68
+ type CostReport = typeof CostReportSchema.Type;
69
+ declare const ComplexityAnalysisSchema: Schema.Struct<{
70
+ score: typeof Schema.Number;
71
+ factors: Schema.Array$<typeof Schema.String>;
72
+ recommendedTier: Schema.Literal<["haiku", "sonnet", "opus"]>;
73
+ estimatedTokens: typeof Schema.Number;
74
+ estimatedCost: typeof Schema.Number;
75
+ }>;
76
+ type ComplexityAnalysis = typeof ComplexityAnalysisSchema.Type;
77
+ declare const CacheEntrySchema: Schema.Struct<{
78
+ queryHash: typeof Schema.String;
79
+ response: typeof Schema.String;
80
+ model: typeof Schema.String;
81
+ createdAt: typeof Schema.DateFromSelf;
82
+ hitCount: typeof Schema.Number;
83
+ lastHitAt: typeof Schema.DateFromSelf;
84
+ ttlMs: typeof Schema.Number;
85
+ }>;
86
+ type CacheEntry = typeof CacheEntrySchema.Type;
87
+ declare const DEFAULT_BUDGET_LIMITS: BudgetLimits;
88
+
89
+ declare const BudgetExceededError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
90
+ readonly _tag: "BudgetExceededError";
91
+ } & Readonly<A>;
92
+ declare class BudgetExceededError extends BudgetExceededError_base<{
93
+ readonly message: string;
94
+ readonly budgetType: "perRequest" | "perSession" | "daily" | "monthly";
95
+ readonly limit: number;
96
+ readonly current: number;
97
+ readonly requested: number;
98
+ }> {
99
+ }
100
+ declare const CostTrackingError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
101
+ readonly _tag: "CostTrackingError";
102
+ } & Readonly<A>;
103
+ declare class CostTrackingError extends CostTrackingError_base<{
104
+ readonly message: string;
105
+ readonly cause?: unknown;
106
+ }> {
107
+ }
108
+ declare const CacheError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
109
+ readonly _tag: "CacheError";
110
+ } & Readonly<A>;
111
+ declare class CacheError extends CacheError_base<{
112
+ readonly message: string;
113
+ readonly cause?: unknown;
114
+ }> {
115
+ }
116
+ declare const RoutingError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
117
+ readonly _tag: "RoutingError";
118
+ } & Readonly<A>;
119
+ declare class RoutingError extends RoutingError_base<{
120
+ readonly message: string;
121
+ readonly taskComplexity?: number;
122
+ }> {
123
+ }
124
+
125
+ declare const getModelCostConfig: (tier: ModelTier) => ModelCostConfig;
126
+ declare const estimateTokens: (text: string) => number;
127
+ declare const estimateCost: (text: string, config: ModelCostConfig) => number;
128
+ declare const heuristicClassify: (task: string) => ModelTier | null;
129
+ declare const analyzeComplexity: (task: string, _context?: string) => Effect.Effect<ComplexityAnalysis, RoutingError>;
130
+ declare const routeToModel: (task: string, context?: string) => Effect.Effect<ModelCostConfig, RoutingError>;
131
+
132
+ interface SemanticCache {
133
+ readonly check: (query: string) => Effect.Effect<string | null, CacheError>;
134
+ readonly store: (query: string, response: string, model: string, ttlMs?: number) => Effect.Effect<void, CacheError>;
135
+ readonly getStats: Effect.Effect<{
136
+ entries: number;
137
+ totalHits: number;
138
+ avgHitsPerEntry: number;
139
+ }, never>;
140
+ }
141
+ declare const makeSemanticCache: Effect.Effect<{
142
+ check: (query: string) => Effect.Effect<string | null, CacheError>;
143
+ store: (query: string, response: string, model: string, ttlMs?: number) => Effect.Effect<void, CacheError>;
144
+ getStats: Effect.Effect<{
145
+ entries: number;
146
+ totalHits: number;
147
+ avgHitsPerEntry: number;
148
+ }, never, never>;
149
+ }, never, never>;
150
+
151
+ interface PromptCompressor {
152
+ readonly compress: (prompt: string, maxTokens?: number) => Effect.Effect<{
153
+ compressed: string;
154
+ savedTokens: number;
155
+ }, CostTrackingError>;
156
+ }
157
+ /**
158
+ * Tier 1: Heuristic prompt compression (no LLM calls).
159
+ * Removes redundant whitespace, blank lines, and repeated patterns.
160
+ * Tier 2 will add LLM-based summarization for large contexts.
161
+ */
162
+ declare const makePromptCompressor: Effect.Effect<{
163
+ compress: (prompt: string, _maxTokens?: number) => Effect.Effect<{
164
+ compressed: string;
165
+ savedTokens: number;
166
+ }, CostTrackingError>;
167
+ }, never, never>;
168
+
169
+ interface BudgetState {
170
+ readonly sessionSpend: Record<string, number>;
171
+ readonly dailySpend: Record<string, number>;
172
+ readonly monthlySpend: Record<string, number>;
173
+ }
174
+ interface BudgetEnforcer {
175
+ readonly check: (estimatedCost: number, agentId: string, sessionId: string) => Effect.Effect<void, BudgetExceededError>;
176
+ readonly record: (cost: number, agentId: string, sessionId: string) => Effect.Effect<void, never>;
177
+ readonly getStatus: (agentId: string) => Effect.Effect<BudgetStatus, never>;
178
+ }
179
+ declare const makeBudgetEnforcer: (limits: BudgetLimits) => Effect.Effect<{
180
+ check: (estimatedCost: number, agentId: string, sessionId: string) => Effect.Effect<void, BudgetExceededError>;
181
+ record: (cost: number, agentId: string, sessionId: string) => Effect.Effect<void, never>;
182
+ getStatus: (agentId: string) => Effect.Effect<BudgetStatus, never>;
183
+ }, never, never>;
184
+
185
+ interface CostTracker {
186
+ readonly record: (entry: Omit<CostEntry, "id" | "timestamp">) => Effect.Effect<void, CostTrackingError>;
187
+ readonly getReport: (period: "session" | "daily" | "weekly" | "monthly", agentId?: string) => Effect.Effect<CostReport, CostTrackingError>;
188
+ }
189
+ declare const makeCostTracker: Effect.Effect<{
190
+ record: (entry: Omit<CostEntry, "id" | "timestamp">) => Effect.Effect<void, CostTrackingError>;
191
+ getReport: (period: "session" | "daily" | "weekly" | "monthly", agentId?: string) => Effect.Effect<CostReport, CostTrackingError>;
192
+ }, never, never>;
193
+
194
+ declare const CostService_base: Context.TagClass<CostService, "CostService", {
195
+ readonly routeToModel: (task: string, context?: string) => Effect.Effect<ModelCostConfig, RoutingError>;
196
+ readonly checkCache: (query: string) => Effect.Effect<string | null, CacheError>;
197
+ readonly cacheResponse: (query: string, response: string, model: string, ttlMs?: number) => Effect.Effect<void, CacheError>;
198
+ readonly compressPrompt: (prompt: string, maxTokens?: number) => Effect.Effect<{
199
+ compressed: string;
200
+ savedTokens: number;
201
+ }, CostTrackingError>;
202
+ readonly checkBudget: (estimatedCost: number, agentId: string, sessionId: string) => Effect.Effect<void, BudgetExceededError>;
203
+ readonly recordCost: (entry: Omit<CostEntry, "id" | "timestamp">) => Effect.Effect<void, CostTrackingError>;
204
+ readonly getBudgetStatus: (agentId: string) => Effect.Effect<BudgetStatus, CostTrackingError>;
205
+ readonly getReport: (period: "session" | "daily" | "weekly" | "monthly", agentId?: string) => Effect.Effect<CostReport, CostTrackingError>;
206
+ }>;
207
+ declare class CostService extends CostService_base {
208
+ }
209
+ declare const CostServiceLive: (budgetLimits?: BudgetLimits) => Layer.Layer<CostService, never, never>;
210
+
211
+ declare const createCostLayer: (budgetLimits?: Partial<BudgetLimits>) => effect_Layer.Layer<CostService, never, never>;
212
+
213
+ export { type BudgetEnforcer, BudgetExceededError, type BudgetLimits, BudgetLimitsSchema, type BudgetState, type BudgetStatus, BudgetStatusSchema, type CacheEntry, CacheEntrySchema, CacheError, type ComplexityAnalysis, ComplexityAnalysisSchema, type CostEntry, CostEntrySchema, type CostReport, CostReportSchema, CostService, CostServiceLive, type CostTracker, CostTrackingError, DEFAULT_BUDGET_LIMITS, type ModelCostConfig, ModelCostConfigSchema, ModelTier, type PromptCompressor, RoutingError, type SemanticCache, analyzeComplexity, createCostLayer, estimateCost, estimateTokens, getModelCostConfig, heuristicClassify, makeBudgetEnforcer, makeCostTracker, makePromptCompressor, makeSemanticCache, routeToModel };
package/dist/index.js ADDED
@@ -0,0 +1,472 @@
1
+ // src/types.ts
2
+ import { Schema } from "effect";
3
+ var ModelTier = Schema.Literal("haiku", "sonnet", "opus");
4
+ var ModelCostConfigSchema = Schema.Struct({
5
+ tier: ModelTier,
6
+ provider: Schema.Literal("anthropic", "openai", "ollama"),
7
+ model: Schema.String,
8
+ costPer1MInput: Schema.Number,
9
+ costPer1MOutput: Schema.Number,
10
+ maxContext: Schema.Number,
11
+ quality: Schema.Number,
12
+ speedTokensPerSec: Schema.Number
13
+ });
14
+ var CostEntrySchema = Schema.Struct({
15
+ id: Schema.String,
16
+ timestamp: Schema.DateFromSelf,
17
+ agentId: Schema.String,
18
+ sessionId: Schema.String,
19
+ model: Schema.String,
20
+ tier: ModelTier,
21
+ inputTokens: Schema.Number,
22
+ outputTokens: Schema.Number,
23
+ cost: Schema.Number,
24
+ cachedHit: Schema.Boolean,
25
+ taskType: Schema.String,
26
+ latencyMs: Schema.Number
27
+ });
28
+ var BudgetLimitsSchema = Schema.Struct({
29
+ perRequest: Schema.Number,
30
+ perSession: Schema.Number,
31
+ daily: Schema.Number,
32
+ monthly: Schema.Number
33
+ });
34
+ var BudgetStatusSchema = Schema.Struct({
35
+ currentSession: Schema.Number,
36
+ currentDaily: Schema.Number,
37
+ currentMonthly: Schema.Number,
38
+ limits: BudgetLimitsSchema,
39
+ percentUsedDaily: Schema.Number,
40
+ percentUsedMonthly: Schema.Number
41
+ });
42
+ var CostReportSchema = Schema.Struct({
43
+ period: Schema.Literal("session", "daily", "weekly", "monthly"),
44
+ totalCost: Schema.Number,
45
+ totalRequests: Schema.Number,
46
+ cacheHits: Schema.Number,
47
+ cacheMisses: Schema.Number,
48
+ cacheHitRate: Schema.Number,
49
+ savings: Schema.Number,
50
+ costByTier: Schema.Record({ key: Schema.String, value: Schema.Number }),
51
+ costByAgent: Schema.Record({ key: Schema.String, value: Schema.Number }),
52
+ avgCostPerRequest: Schema.Number,
53
+ avgLatencyMs: Schema.Number
54
+ });
55
+ var ComplexityAnalysisSchema = Schema.Struct({
56
+ score: Schema.Number,
57
+ factors: Schema.Array(Schema.String),
58
+ recommendedTier: ModelTier,
59
+ estimatedTokens: Schema.Number,
60
+ estimatedCost: Schema.Number
61
+ });
62
+ var CacheEntrySchema = Schema.Struct({
63
+ queryHash: Schema.String,
64
+ response: Schema.String,
65
+ model: Schema.String,
66
+ createdAt: Schema.DateFromSelf,
67
+ hitCount: Schema.Number,
68
+ lastHitAt: Schema.DateFromSelf,
69
+ ttlMs: Schema.Number
70
+ });
71
+ var DEFAULT_BUDGET_LIMITS = {
72
+ perRequest: 1,
73
+ perSession: 5,
74
+ daily: 25,
75
+ monthly: 200
76
+ };
77
+
78
+ // src/errors.ts
79
+ import { Data } from "effect";
80
+ var BudgetExceededError = class extends Data.TaggedError("BudgetExceededError") {
81
+ };
82
+ var CostTrackingError = class extends Data.TaggedError("CostTrackingError") {
83
+ };
84
+ var CacheError = class extends Data.TaggedError("CacheError") {
85
+ };
86
+ var RoutingError = class extends Data.TaggedError("RoutingError") {
87
+ };
88
+
89
+ // src/routing/complexity-router.ts
90
+ import { Effect } from "effect";
91
+ var MODEL_CONFIGS = {
92
+ haiku: {
93
+ tier: "haiku",
94
+ provider: "anthropic",
95
+ model: "claude-3-5-haiku-20241022",
96
+ costPer1MInput: 1,
97
+ costPer1MOutput: 5,
98
+ maxContext: 2e5,
99
+ quality: 0.6,
100
+ speedTokensPerSec: 150
101
+ },
102
+ sonnet: {
103
+ tier: "sonnet",
104
+ provider: "anthropic",
105
+ model: "claude-sonnet-4-20250514",
106
+ costPer1MInput: 3,
107
+ costPer1MOutput: 15,
108
+ maxContext: 2e5,
109
+ quality: 0.85,
110
+ speedTokensPerSec: 80
111
+ },
112
+ opus: {
113
+ tier: "opus",
114
+ provider: "anthropic",
115
+ model: "claude-opus-4-20250514",
116
+ costPer1MInput: 15,
117
+ costPer1MOutput: 75,
118
+ maxContext: 1e6,
119
+ quality: 1,
120
+ speedTokensPerSec: 40
121
+ }
122
+ };
123
+ var getModelCostConfig = (tier) => MODEL_CONFIGS[tier];
124
+ var estimateTokens = (text) => Math.ceil(text.length / 4);
125
+ var estimateCost = (text, config) => estimateTokens(text) / 1e6 * config.costPer1MInput;
126
+ var heuristicClassify = (task) => {
127
+ const wordCount = task.split(/\s+/).length;
128
+ const hasCodeBlock = /```/.test(task);
129
+ const hasMultiStep = /\b(step|then|next|finally|after|before)\b/i.test(task);
130
+ const hasAnalysis = /\b(analyze|compare|evaluate|synthesize|critique)\b/i.test(task);
131
+ if (wordCount < 50 && !hasCodeBlock && !hasMultiStep && !hasAnalysis) {
132
+ return "haiku";
133
+ }
134
+ if (hasCodeBlock && hasMultiStep && hasAnalysis) {
135
+ return "opus";
136
+ }
137
+ if (hasCodeBlock || hasAnalysis) {
138
+ return "sonnet";
139
+ }
140
+ return null;
141
+ };
142
+ var analyzeComplexity = (task, _context) => Effect.try({
143
+ try: () => {
144
+ const heuristic = heuristicClassify(task);
145
+ const tier = heuristic ?? "sonnet";
146
+ const config = getModelCostConfig(tier);
147
+ const score = tier === "haiku" ? 0.2 : tier === "sonnet" ? 0.5 : 0.9;
148
+ const factors = ["heuristic-classification"];
149
+ if (/```/.test(task)) factors.push("contains-code");
150
+ if (/\b(step|then|next|finally)\b/i.test(task)) factors.push("multi-step");
151
+ if (/\b(analyze|compare|evaluate)\b/i.test(task)) factors.push("analysis-required");
152
+ return {
153
+ score,
154
+ factors,
155
+ recommendedTier: tier,
156
+ estimatedTokens: estimateTokens(task),
157
+ estimatedCost: estimateCost(task, config)
158
+ };
159
+ },
160
+ catch: (e) => new RoutingError({ message: "Complexity analysis failed", taskComplexity: void 0 })
161
+ });
162
+ var routeToModel = (task, context) => Effect.map(
163
+ analyzeComplexity(task, context),
164
+ (analysis) => getModelCostConfig(analysis.recommendedTier)
165
+ );
166
+
167
+ // src/caching/semantic-cache.ts
168
+ import { Effect as Effect2, Ref } from "effect";
169
+ var DEFAULT_TTL_MS = 36e5;
170
+ var MAX_CACHE_SIZE = 1e4;
171
+ function hashString(str) {
172
+ let hash = 0;
173
+ for (let i = 0; i < str.length; i++) {
174
+ const char = str.charCodeAt(i);
175
+ hash = (hash << 5) - hash + char;
176
+ hash |= 0;
177
+ }
178
+ return hash.toString(36);
179
+ }
180
+ var makeSemanticCache = Effect2.gen(function* () {
181
+ const cacheRef = yield* Ref.make([]);
182
+ const check = (query) => Effect2.gen(function* () {
183
+ const queryHash = hashString(query.toLowerCase().trim());
184
+ const entries = yield* Ref.get(cacheRef);
185
+ const now = Date.now();
186
+ const match = entries.find(
187
+ (e) => e.queryHash === queryHash && now - e.createdAt.getTime() < e.ttlMs
188
+ );
189
+ if (match) {
190
+ yield* Ref.update(
191
+ cacheRef,
192
+ (entries2) => entries2.map(
193
+ (e) => e.queryHash === queryHash ? { ...e, hitCount: e.hitCount + 1, lastHitAt: /* @__PURE__ */ new Date() } : e
194
+ )
195
+ );
196
+ return match.response;
197
+ }
198
+ return null;
199
+ }).pipe(
200
+ Effect2.mapError((e) => new CacheError({ message: "Cache lookup failed", cause: e }))
201
+ );
202
+ const store = (query, response, model, ttlMs = DEFAULT_TTL_MS) => Effect2.gen(function* () {
203
+ const queryHash = hashString(query.toLowerCase().trim());
204
+ const entry = {
205
+ queryHash,
206
+ response,
207
+ model,
208
+ createdAt: /* @__PURE__ */ new Date(),
209
+ hitCount: 0,
210
+ lastHitAt: /* @__PURE__ */ new Date(),
211
+ ttlMs
212
+ };
213
+ yield* Ref.update(cacheRef, (entries) => {
214
+ const now = Date.now();
215
+ const valid = entries.filter(
216
+ (e) => now - e.createdAt.getTime() < e.ttlMs
217
+ );
218
+ if (valid.length >= MAX_CACHE_SIZE) {
219
+ valid.sort((a, b) => b.lastHitAt.getTime() - a.lastHitAt.getTime());
220
+ valid.pop();
221
+ }
222
+ const existing = valid.findIndex((e) => e.queryHash === queryHash);
223
+ if (existing >= 0) {
224
+ valid[existing] = entry;
225
+ return valid;
226
+ }
227
+ return [...valid, entry];
228
+ });
229
+ }).pipe(
230
+ Effect2.mapError((e) => new CacheError({ message: "Cache store failed", cause: e }))
231
+ );
232
+ const getStats = Effect2.gen(function* () {
233
+ const entries = yield* Ref.get(cacheRef);
234
+ const totalHits = entries.reduce((sum, e) => sum + e.hitCount, 0);
235
+ return {
236
+ entries: entries.length,
237
+ totalHits,
238
+ avgHitsPerEntry: entries.length > 0 ? totalHits / entries.length : 0
239
+ };
240
+ });
241
+ return { check, store, getStats };
242
+ });
243
+
244
+ // src/compression/prompt-compressor.ts
245
+ import { Effect as Effect3 } from "effect";
246
+ var makePromptCompressor = Effect3.succeed({
247
+ compress: (prompt, _maxTokens) => Effect3.try({
248
+ try: () => {
249
+ const originalTokens = Math.ceil(prompt.length / 4);
250
+ if (originalTokens < 500) {
251
+ return { compressed: prompt, savedTokens: 0 };
252
+ }
253
+ let compressed = prompt.replace(/\n{3,}/g, "\n\n").replace(/[ \t]{2,}/g, " ").replace(/^\s+$/gm, "").replace(/\n\s*\n\s*\n/g, "\n\n");
254
+ const compressedTokens = Math.ceil(compressed.length / 4);
255
+ return {
256
+ compressed,
257
+ savedTokens: originalTokens - compressedTokens
258
+ };
259
+ },
260
+ catch: (e) => new CostTrackingError({ message: "Prompt compression failed", cause: e })
261
+ })
262
+ });
263
+
264
+ // src/budgets/budget-enforcer.ts
265
+ import { Effect as Effect4, Ref as Ref2 } from "effect";
266
+ var makeBudgetEnforcer = (limits) => Effect4.gen(function* () {
267
+ const stateRef = yield* Ref2.make({
268
+ sessionSpend: {},
269
+ dailySpend: {},
270
+ monthlySpend: {}
271
+ });
272
+ const check = (estimatedCost, agentId, sessionId) => Effect4.gen(function* () {
273
+ const state = yield* Ref2.get(stateRef);
274
+ const sessionCurrent = state.sessionSpend[sessionId] ?? 0;
275
+ const dailyCurrent = state.dailySpend[agentId] ?? 0;
276
+ const monthlyCurrent = state.monthlySpend[agentId] ?? 0;
277
+ if (estimatedCost > limits.perRequest) {
278
+ return yield* Effect4.fail(
279
+ new BudgetExceededError({
280
+ message: `Request cost $${estimatedCost.toFixed(4)} exceeds per-request limit $${limits.perRequest}`,
281
+ budgetType: "perRequest",
282
+ limit: limits.perRequest,
283
+ current: 0,
284
+ requested: estimatedCost
285
+ })
286
+ );
287
+ }
288
+ if (sessionCurrent + estimatedCost > limits.perSession) {
289
+ return yield* Effect4.fail(
290
+ new BudgetExceededError({
291
+ message: `Session spend $${(sessionCurrent + estimatedCost).toFixed(4)} exceeds limit $${limits.perSession}`,
292
+ budgetType: "perSession",
293
+ limit: limits.perSession,
294
+ current: sessionCurrent,
295
+ requested: estimatedCost
296
+ })
297
+ );
298
+ }
299
+ if (dailyCurrent + estimatedCost > limits.daily) {
300
+ return yield* Effect4.fail(
301
+ new BudgetExceededError({
302
+ message: `Daily spend $${(dailyCurrent + estimatedCost).toFixed(4)} exceeds limit $${limits.daily}`,
303
+ budgetType: "daily",
304
+ limit: limits.daily,
305
+ current: dailyCurrent,
306
+ requested: estimatedCost
307
+ })
308
+ );
309
+ }
310
+ if (monthlyCurrent + estimatedCost > limits.monthly) {
311
+ return yield* Effect4.fail(
312
+ new BudgetExceededError({
313
+ message: `Monthly spend $${(monthlyCurrent + estimatedCost).toFixed(4)} exceeds limit $${limits.monthly}`,
314
+ budgetType: "monthly",
315
+ limit: limits.monthly,
316
+ current: monthlyCurrent,
317
+ requested: estimatedCost
318
+ })
319
+ );
320
+ }
321
+ });
322
+ const record = (cost, agentId, sessionId) => Ref2.update(stateRef, (state) => ({
323
+ sessionSpend: {
324
+ ...state.sessionSpend,
325
+ [sessionId]: (state.sessionSpend[sessionId] ?? 0) + cost
326
+ },
327
+ dailySpend: {
328
+ ...state.dailySpend,
329
+ [agentId]: (state.dailySpend[agentId] ?? 0) + cost
330
+ },
331
+ monthlySpend: {
332
+ ...state.monthlySpend,
333
+ [agentId]: (state.monthlySpend[agentId] ?? 0) + cost
334
+ }
335
+ }));
336
+ const getStatus = (agentId) => Effect4.gen(function* () {
337
+ const state = yield* Ref2.get(stateRef);
338
+ const daily = state.dailySpend[agentId] ?? 0;
339
+ const monthly = state.monthlySpend[agentId] ?? 0;
340
+ return {
341
+ currentSession: 0,
342
+ currentDaily: daily,
343
+ currentMonthly: monthly,
344
+ limits,
345
+ percentUsedDaily: daily / limits.daily * 100,
346
+ percentUsedMonthly: monthly / limits.monthly * 100
347
+ };
348
+ });
349
+ return { check, record, getStatus };
350
+ });
351
+
352
+ // src/analytics/cost-tracker.ts
353
+ import { Effect as Effect5, Ref as Ref3 } from "effect";
354
+ var makeCostTracker = Effect5.gen(function* () {
355
+ const entriesRef = yield* Ref3.make([]);
356
+ const record = (entry) => Ref3.update(entriesRef, (entries) => [
357
+ ...entries,
358
+ {
359
+ ...entry,
360
+ id: crypto.randomUUID(),
361
+ timestamp: /* @__PURE__ */ new Date()
362
+ }
363
+ ]).pipe(
364
+ Effect5.mapError(
365
+ (e) => new CostTrackingError({ message: "Failed to record cost entry", cause: e })
366
+ )
367
+ );
368
+ const getReport = (period, agentId) => Effect5.gen(function* () {
369
+ const allEntries = yield* Ref3.get(entriesRef);
370
+ const now = Date.now();
371
+ const periodMs = {
372
+ session: 0,
373
+ daily: 864e5,
374
+ weekly: 6048e5,
375
+ monthly: 2592e6
376
+ };
377
+ let entries = period === "session" ? allEntries : allEntries.filter(
378
+ (e) => now - e.timestamp.getTime() < periodMs[period]
379
+ );
380
+ if (agentId) {
381
+ entries = entries.filter((e) => e.agentId === agentId);
382
+ }
383
+ const totalCost = entries.reduce((sum, e) => sum + e.cost, 0);
384
+ const cacheHits = entries.filter((e) => e.cachedHit).length;
385
+ const cacheMisses = entries.filter((e) => !e.cachedHit).length;
386
+ const costByTier = {};
387
+ for (const entry of entries) {
388
+ costByTier[entry.tier] = (costByTier[entry.tier] ?? 0) + entry.cost;
389
+ }
390
+ const costByAgent = {};
391
+ for (const entry of entries) {
392
+ costByAgent[entry.agentId] = (costByAgent[entry.agentId] ?? 0) + entry.cost;
393
+ }
394
+ return {
395
+ period,
396
+ totalCost,
397
+ totalRequests: entries.length,
398
+ cacheHits,
399
+ cacheMisses,
400
+ cacheHitRate: entries.length > 0 ? cacheHits / entries.length : 0,
401
+ savings: 0,
402
+ costByTier,
403
+ costByAgent,
404
+ avgCostPerRequest: entries.length > 0 ? totalCost / entries.length : 0,
405
+ avgLatencyMs: entries.length > 0 ? entries.reduce((sum, e) => sum + e.latencyMs, 0) / entries.length : 0
406
+ };
407
+ }).pipe(
408
+ Effect5.mapError(
409
+ (e) => new CostTrackingError({ message: "Failed to generate report", cause: e })
410
+ )
411
+ );
412
+ return { record, getReport };
413
+ });
414
+
415
+ // src/cost-service.ts
416
+ import { Effect as Effect6, Context, Layer } from "effect";
417
+ var CostService = class extends Context.Tag("CostService")() {
418
+ };
419
+ var CostServiceLive = (budgetLimits = DEFAULT_BUDGET_LIMITS) => Layer.effect(
420
+ CostService,
421
+ Effect6.gen(function* () {
422
+ const cache = yield* makeSemanticCache;
423
+ const compressor = yield* makePromptCompressor;
424
+ const budget = yield* makeBudgetEnforcer(budgetLimits);
425
+ const tracker = yield* makeCostTracker;
426
+ return {
427
+ routeToModel: (task, context) => routeToModel(task, context),
428
+ checkCache: (query) => cache.check(query),
429
+ cacheResponse: (query, response, model, ttlMs) => cache.store(query, response, model, ttlMs),
430
+ compressPrompt: (prompt, maxTokens) => compressor.compress(prompt, maxTokens),
431
+ checkBudget: (estimatedCost, agentId, sessionId) => budget.check(estimatedCost, agentId, sessionId),
432
+ recordCost: (entry) => tracker.record(entry),
433
+ getBudgetStatus: (agentId) => budget.getStatus(agentId),
434
+ getReport: (period, agentId) => tracker.getReport(period, agentId)
435
+ };
436
+ })
437
+ );
438
+
439
+ // src/runtime.ts
440
+ var createCostLayer = (budgetLimits) => CostServiceLive({
441
+ ...DEFAULT_BUDGET_LIMITS,
442
+ ...budgetLimits
443
+ });
444
+ export {
445
+ BudgetExceededError,
446
+ BudgetLimitsSchema,
447
+ BudgetStatusSchema,
448
+ CacheEntrySchema,
449
+ CacheError,
450
+ ComplexityAnalysisSchema,
451
+ CostEntrySchema,
452
+ CostReportSchema,
453
+ CostService,
454
+ CostServiceLive,
455
+ CostTrackingError,
456
+ DEFAULT_BUDGET_LIMITS,
457
+ ModelCostConfigSchema,
458
+ ModelTier,
459
+ RoutingError,
460
+ analyzeComplexity,
461
+ createCostLayer,
462
+ estimateCost,
463
+ estimateTokens,
464
+ getModelCostConfig,
465
+ heuristicClassify,
466
+ makeBudgetEnforcer,
467
+ makeCostTracker,
468
+ makePromptCompressor,
469
+ makeSemanticCache,
470
+ routeToModel
471
+ };
472
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/errors.ts","../src/routing/complexity-router.ts","../src/caching/semantic-cache.ts","../src/compression/prompt-compressor.ts","../src/budgets/budget-enforcer.ts","../src/analytics/cost-tracker.ts","../src/cost-service.ts","../src/runtime.ts"],"sourcesContent":["import { Schema } from \"effect\";\n\n// ─── Model Tier ───\n\nexport const ModelTier = Schema.Literal(\"haiku\", \"sonnet\", \"opus\");\nexport type ModelTier = typeof ModelTier.Type;\n\n// ─── Model Cost Configuration ───\n// Named ModelCostConfig (not ModelConfig) to avoid collision with llm-provider's ModelConfig\n\nexport const ModelCostConfigSchema = Schema.Struct({\n tier: ModelTier,\n provider: Schema.Literal(\"anthropic\", \"openai\", \"ollama\"),\n model: Schema.String,\n costPer1MInput: Schema.Number,\n costPer1MOutput: Schema.Number,\n maxContext: Schema.Number,\n quality: Schema.Number,\n speedTokensPerSec: Schema.Number,\n});\nexport type ModelCostConfig = typeof ModelCostConfigSchema.Type;\n\n// ─── Cost Entry ───\n\nexport const CostEntrySchema = Schema.Struct({\n id: Schema.String,\n timestamp: Schema.DateFromSelf,\n agentId: Schema.String,\n sessionId: Schema.String,\n model: Schema.String,\n tier: ModelTier,\n inputTokens: Schema.Number,\n outputTokens: Schema.Number,\n cost: Schema.Number,\n cachedHit: Schema.Boolean,\n taskType: Schema.String,\n latencyMs: Schema.Number,\n});\nexport type CostEntry = typeof CostEntrySchema.Type;\n\n// ─── Budget ───\n\nexport const BudgetLimitsSchema = Schema.Struct({\n perRequest: Schema.Number,\n perSession: Schema.Number,\n daily: Schema.Number,\n monthly: Schema.Number,\n});\nexport type BudgetLimits = typeof BudgetLimitsSchema.Type;\n\nexport const BudgetStatusSchema = Schema.Struct({\n currentSession: Schema.Number,\n currentDaily: Schema.Number,\n currentMonthly: Schema.Number,\n limits: BudgetLimitsSchema,\n percentUsedDaily: Schema.Number,\n percentUsedMonthly: Schema.Number,\n});\nexport type BudgetStatus = typeof BudgetStatusSchema.Type;\n\n// ─── Cost Report ───\n\nexport const CostReportSchema = Schema.Struct({\n period: Schema.Literal(\"session\", \"daily\", \"weekly\", \"monthly\"),\n totalCost: Schema.Number,\n totalRequests: Schema.Number,\n cacheHits: Schema.Number,\n cacheMisses: Schema.Number,\n cacheHitRate: Schema.Number,\n savings: Schema.Number,\n costByTier: Schema.Record({ key: Schema.String, value: Schema.Number }),\n costByAgent: Schema.Record({ key: Schema.String, value: Schema.Number }),\n avgCostPerRequest: Schema.Number,\n avgLatencyMs: Schema.Number,\n});\nexport type CostReport = typeof CostReportSchema.Type;\n\n// ─── Complexity Analysis ───\n\nexport const ComplexityAnalysisSchema = Schema.Struct({\n score: Schema.Number,\n factors: Schema.Array(Schema.String),\n recommendedTier: ModelTier,\n estimatedTokens: Schema.Number,\n estimatedCost: Schema.Number,\n});\nexport type ComplexityAnalysis = typeof ComplexityAnalysisSchema.Type;\n\n// ─── Cache Entry ───\n\nexport const CacheEntrySchema = Schema.Struct({\n queryHash: Schema.String,\n response: Schema.String,\n model: Schema.String,\n createdAt: Schema.DateFromSelf,\n hitCount: Schema.Number,\n lastHitAt: Schema.DateFromSelf,\n ttlMs: Schema.Number,\n});\nexport type CacheEntry = typeof CacheEntrySchema.Type;\n\n// ─── Default Budget Limits ───\n\nexport const DEFAULT_BUDGET_LIMITS: BudgetLimits = {\n perRequest: 1.0,\n perSession: 5.0,\n daily: 25.0,\n monthly: 200.0,\n};\n","import { Data } from \"effect\";\n\nexport class BudgetExceededError extends Data.TaggedError(\"BudgetExceededError\")<{\n readonly message: string;\n readonly budgetType: \"perRequest\" | \"perSession\" | \"daily\" | \"monthly\";\n readonly limit: number;\n readonly current: number;\n readonly requested: number;\n}> {}\n\nexport class CostTrackingError extends Data.TaggedError(\"CostTrackingError\")<{\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\nexport class CacheError extends Data.TaggedError(\"CacheError\")<{\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\nexport class RoutingError extends Data.TaggedError(\"RoutingError\")<{\n readonly message: string;\n readonly taskComplexity?: number;\n}> {}\n","import { Effect } from \"effect\";\nimport type { ModelTier, ModelCostConfig, ComplexityAnalysis } from \"../types.js\";\nimport { RoutingError } from \"../errors.js\";\n\n// ─── Model cost configurations ───\n\nconst MODEL_CONFIGS: Record<ModelTier, ModelCostConfig> = {\n haiku: {\n tier: \"haiku\",\n provider: \"anthropic\",\n model: \"claude-3-5-haiku-20241022\",\n costPer1MInput: 1.0,\n costPer1MOutput: 5.0,\n maxContext: 200_000,\n quality: 0.6,\n speedTokensPerSec: 150,\n },\n sonnet: {\n tier: \"sonnet\",\n provider: \"anthropic\",\n model: \"claude-sonnet-4-20250514\",\n costPer1MInput: 3.0,\n costPer1MOutput: 15.0,\n maxContext: 200_000,\n quality: 0.85,\n speedTokensPerSec: 80,\n },\n opus: {\n tier: \"opus\",\n provider: \"anthropic\",\n model: \"claude-opus-4-20250514\",\n costPer1MInput: 15.0,\n costPer1MOutput: 75.0,\n maxContext: 1_000_000,\n quality: 1.0,\n speedTokensPerSec: 40,\n },\n};\n\nexport const getModelCostConfig = (tier: ModelTier): ModelCostConfig =>\n MODEL_CONFIGS[tier];\n\nexport const estimateTokens = (text: string): number =>\n Math.ceil(text.length / 4);\n\nexport const estimateCost = (text: string, config: ModelCostConfig): number =>\n (estimateTokens(text) / 1_000_000) * config.costPer1MInput;\n\n// ─── Heuristic Classifier ───\n\nexport const heuristicClassify = (task: string): ModelTier | null => {\n const wordCount = task.split(/\\s+/).length;\n const hasCodeBlock = /```/.test(task);\n const hasMultiStep = /\\b(step|then|next|finally|after|before)\\b/i.test(task);\n const hasAnalysis = /\\b(analyze|compare|evaluate|synthesize|critique)\\b/i.test(task);\n\n // Simple tasks < 50 words, no code, no multi-step\n if (wordCount < 50 && !hasCodeBlock && !hasMultiStep && !hasAnalysis) {\n return \"haiku\";\n }\n\n // Complex tasks with code, multi-step, and analysis\n if (hasCodeBlock && hasMultiStep && hasAnalysis) {\n return \"opus\";\n }\n\n // Medium complexity: code or multi-step or analysis (but not all)\n if (hasCodeBlock || hasAnalysis) {\n return \"sonnet\";\n }\n\n // Default to sonnet for ambiguous cases\n return null;\n};\n\n// ─── Complexity Analysis ───\n\nexport const analyzeComplexity = (\n task: string,\n _context?: string,\n): Effect.Effect<ComplexityAnalysis, RoutingError> =>\n Effect.try({\n try: () => {\n const heuristic = heuristicClassify(task);\n const tier = heuristic ?? \"sonnet\";\n const config = getModelCostConfig(tier);\n\n const score =\n tier === \"haiku\" ? 0.2 :\n tier === \"sonnet\" ? 0.5 : 0.9;\n\n const factors: string[] = [\"heuristic-classification\"];\n if (/```/.test(task)) factors.push(\"contains-code\");\n if (/\\b(step|then|next|finally)\\b/i.test(task)) factors.push(\"multi-step\");\n if (/\\b(analyze|compare|evaluate)\\b/i.test(task)) factors.push(\"analysis-required\");\n\n return {\n score,\n factors,\n recommendedTier: tier,\n estimatedTokens: estimateTokens(task),\n estimatedCost: estimateCost(task, config),\n };\n },\n catch: (e) => new RoutingError({ message: \"Complexity analysis failed\", taskComplexity: undefined }),\n });\n\n// ─── Route to Model ───\n\nexport const routeToModel = (\n task: string,\n context?: string,\n): Effect.Effect<ModelCostConfig, RoutingError> =>\n Effect.map(analyzeComplexity(task, context), (analysis) =>\n getModelCostConfig(analysis.recommendedTier),\n );\n","import { Effect, Ref } from \"effect\";\nimport type { CacheEntry } from \"../types.js\";\nimport { CacheError } from \"../errors.js\";\n\nconst DEFAULT_TTL_MS = 3_600_000; // 1 hour\nconst MAX_CACHE_SIZE = 10_000;\n\nexport interface SemanticCache {\n readonly check: (query: string) => Effect.Effect<string | null, CacheError>;\n readonly store: (query: string, response: string, model: string, ttlMs?: number) => Effect.Effect<void, CacheError>;\n readonly getStats: Effect.Effect<{ entries: number; totalHits: number; avgHitsPerEntry: number }, never>;\n}\n\n/**\n * Tier 1: Hash-based semantic cache (exact match).\n * Tier 2 will upgrade to embedding-based similarity matching.\n */\nfunction hashString(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash |= 0;\n }\n return hash.toString(36);\n}\n\nexport const makeSemanticCache = Effect.gen(function* () {\n const cacheRef = yield* Ref.make<CacheEntry[]>([]);\n\n const check = (query: string): Effect.Effect<string | null, CacheError> =>\n Effect.gen(function* () {\n const queryHash = hashString(query.toLowerCase().trim());\n const entries = yield* Ref.get(cacheRef);\n const now = Date.now();\n\n // Find exact hash match within TTL\n const match = entries.find(\n (e) => e.queryHash === queryHash && now - e.createdAt.getTime() < e.ttlMs,\n );\n\n if (match) {\n // Update hit count\n yield* Ref.update(cacheRef, (entries) =>\n entries.map((e) =>\n e.queryHash === queryHash\n ? { ...e, hitCount: e.hitCount + 1, lastHitAt: new Date() }\n : e,\n ),\n );\n return match.response;\n }\n\n return null;\n }).pipe(\n Effect.mapError((e) => new CacheError({ message: \"Cache lookup failed\", cause: e })),\n );\n\n const store = (\n query: string,\n response: string,\n model: string,\n ttlMs: number = DEFAULT_TTL_MS,\n ): Effect.Effect<void, CacheError> =>\n Effect.gen(function* () {\n const queryHash = hashString(query.toLowerCase().trim());\n\n const entry: CacheEntry = {\n queryHash,\n response,\n model,\n createdAt: new Date(),\n hitCount: 0,\n lastHitAt: new Date(),\n ttlMs,\n };\n\n yield* Ref.update(cacheRef, (entries) => {\n // Evict expired entries\n const now = Date.now();\n const valid = entries.filter(\n (e) => now - e.createdAt.getTime() < e.ttlMs,\n );\n\n // Evict LRU if at capacity\n if (valid.length >= MAX_CACHE_SIZE) {\n valid.sort((a, b) => b.lastHitAt.getTime() - a.lastHitAt.getTime());\n valid.pop();\n }\n\n // Replace existing entry with same hash or add new\n const existing = valid.findIndex((e) => e.queryHash === queryHash);\n if (existing >= 0) {\n valid[existing] = entry;\n return valid;\n }\n\n return [...valid, entry];\n });\n }).pipe(\n Effect.mapError((e) => new CacheError({ message: \"Cache store failed\", cause: e })),\n );\n\n const getStats = Effect.gen(function* () {\n const entries = yield* Ref.get(cacheRef);\n const totalHits = entries.reduce((sum, e) => sum + e.hitCount, 0);\n return {\n entries: entries.length,\n totalHits,\n avgHitsPerEntry: entries.length > 0 ? totalHits / entries.length : 0,\n };\n });\n\n return { check, store, getStats } satisfies SemanticCache;\n});\n","import { Effect } from \"effect\";\nimport { CostTrackingError } from \"../errors.js\";\n\nexport interface PromptCompressor {\n readonly compress: (\n prompt: string,\n maxTokens?: number,\n ) => Effect.Effect<{ compressed: string; savedTokens: number }, CostTrackingError>;\n}\n\n/**\n * Tier 1: Heuristic prompt compression (no LLM calls).\n * Removes redundant whitespace, blank lines, and repeated patterns.\n * Tier 2 will add LLM-based summarization for large contexts.\n */\nexport const makePromptCompressor = Effect.succeed({\n compress: (\n prompt: string,\n _maxTokens?: number,\n ): Effect.Effect<{ compressed: string; savedTokens: number }, CostTrackingError> =>\n Effect.try({\n try: () => {\n const originalTokens = Math.ceil(prompt.length / 4);\n\n // Skip compression for short prompts\n if (originalTokens < 500) {\n return { compressed: prompt, savedTokens: 0 };\n }\n\n // Heuristic compression\n let compressed = prompt\n .replace(/\\n{3,}/g, \"\\n\\n\") // Collapse multiple newlines\n .replace(/[ \\t]{2,}/g, \" \") // Collapse multiple spaces\n .replace(/^\\s+$/gm, \"\") // Remove blank lines\n .replace(/\\n\\s*\\n\\s*\\n/g, \"\\n\\n\"); // Clean up remaining multi-blanks\n\n const compressedTokens = Math.ceil(compressed.length / 4);\n\n return {\n compressed,\n savedTokens: originalTokens - compressedTokens,\n };\n },\n catch: (e) => new CostTrackingError({ message: \"Prompt compression failed\", cause: e }),\n }),\n} satisfies PromptCompressor);\n","import { Effect, Ref } from \"effect\";\nimport type { BudgetLimits, BudgetStatus } from \"../types.js\";\nimport { BudgetExceededError } from \"../errors.js\";\n\nexport interface BudgetState {\n readonly sessionSpend: Record<string, number>; // sessionId -> total\n readonly dailySpend: Record<string, number>; // agentId -> today's total\n readonly monthlySpend: Record<string, number>; // agentId -> month's total\n}\n\nexport interface BudgetEnforcer {\n readonly check: (estimatedCost: number, agentId: string, sessionId: string) => Effect.Effect<void, BudgetExceededError>;\n readonly record: (cost: number, agentId: string, sessionId: string) => Effect.Effect<void, never>;\n readonly getStatus: (agentId: string) => Effect.Effect<BudgetStatus, never>;\n}\n\nexport const makeBudgetEnforcer = (limits: BudgetLimits) =>\n Effect.gen(function* () {\n const stateRef = yield* Ref.make<BudgetState>({\n sessionSpend: {},\n dailySpend: {},\n monthlySpend: {},\n });\n\n const check = (\n estimatedCost: number,\n agentId: string,\n sessionId: string,\n ): Effect.Effect<void, BudgetExceededError> =>\n Effect.gen(function* () {\n const state = yield* Ref.get(stateRef);\n\n const sessionCurrent = state.sessionSpend[sessionId] ?? 0;\n const dailyCurrent = state.dailySpend[agentId] ?? 0;\n const monthlyCurrent = state.monthlySpend[agentId] ?? 0;\n\n if (estimatedCost > limits.perRequest) {\n return yield* Effect.fail(\n new BudgetExceededError({\n message: `Request cost $${estimatedCost.toFixed(4)} exceeds per-request limit $${limits.perRequest}`,\n budgetType: \"perRequest\",\n limit: limits.perRequest,\n current: 0,\n requested: estimatedCost,\n }),\n );\n }\n\n if (sessionCurrent + estimatedCost > limits.perSession) {\n return yield* Effect.fail(\n new BudgetExceededError({\n message: `Session spend $${(sessionCurrent + estimatedCost).toFixed(4)} exceeds limit $${limits.perSession}`,\n budgetType: \"perSession\",\n limit: limits.perSession,\n current: sessionCurrent,\n requested: estimatedCost,\n }),\n );\n }\n\n if (dailyCurrent + estimatedCost > limits.daily) {\n return yield* Effect.fail(\n new BudgetExceededError({\n message: `Daily spend $${(dailyCurrent + estimatedCost).toFixed(4)} exceeds limit $${limits.daily}`,\n budgetType: \"daily\",\n limit: limits.daily,\n current: dailyCurrent,\n requested: estimatedCost,\n }),\n );\n }\n\n if (monthlyCurrent + estimatedCost > limits.monthly) {\n return yield* Effect.fail(\n new BudgetExceededError({\n message: `Monthly spend $${(monthlyCurrent + estimatedCost).toFixed(4)} exceeds limit $${limits.monthly}`,\n budgetType: \"monthly\",\n limit: limits.monthly,\n current: monthlyCurrent,\n requested: estimatedCost,\n }),\n );\n }\n });\n\n const record = (\n cost: number,\n agentId: string,\n sessionId: string,\n ): Effect.Effect<void, never> =>\n Ref.update(stateRef, (state) => ({\n sessionSpend: {\n ...state.sessionSpend,\n [sessionId]: (state.sessionSpend[sessionId] ?? 0) + cost,\n },\n dailySpend: {\n ...state.dailySpend,\n [agentId]: (state.dailySpend[agentId] ?? 0) + cost,\n },\n monthlySpend: {\n ...state.monthlySpend,\n [agentId]: (state.monthlySpend[agentId] ?? 0) + cost,\n },\n }));\n\n const getStatus = (agentId: string): Effect.Effect<BudgetStatus, never> =>\n Effect.gen(function* () {\n const state = yield* Ref.get(stateRef);\n const daily = state.dailySpend[agentId] ?? 0;\n const monthly = state.monthlySpend[agentId] ?? 0;\n\n return {\n currentSession: 0,\n currentDaily: daily,\n currentMonthly: monthly,\n limits,\n percentUsedDaily: (daily / limits.daily) * 100,\n percentUsedMonthly: (monthly / limits.monthly) * 100,\n };\n });\n\n return { check, record, getStatus } satisfies BudgetEnforcer;\n });\n","import { Effect, Ref } from \"effect\";\nimport type { CostEntry, CostReport } from \"../types.js\";\nimport { CostTrackingError } from \"../errors.js\";\n\nexport interface CostTracker {\n readonly record: (entry: Omit<CostEntry, \"id\" | \"timestamp\">) => Effect.Effect<void, CostTrackingError>;\n readonly getReport: (period: \"session\" | \"daily\" | \"weekly\" | \"monthly\", agentId?: string) => Effect.Effect<CostReport, CostTrackingError>;\n}\n\nexport const makeCostTracker = Effect.gen(function* () {\n const entriesRef = yield* Ref.make<CostEntry[]>([]);\n\n const record = (\n entry: Omit<CostEntry, \"id\" | \"timestamp\">,\n ): Effect.Effect<void, CostTrackingError> =>\n Ref.update(entriesRef, (entries) => [\n ...entries,\n {\n ...entry,\n id: crypto.randomUUID(),\n timestamp: new Date(),\n },\n ]).pipe(\n Effect.mapError(\n (e) => new CostTrackingError({ message: \"Failed to record cost entry\", cause: e }),\n ),\n );\n\n const getReport = (\n period: \"session\" | \"daily\" | \"weekly\" | \"monthly\",\n agentId?: string,\n ): Effect.Effect<CostReport, CostTrackingError> =>\n Effect.gen(function* () {\n const allEntries = yield* Ref.get(entriesRef);\n const now = Date.now();\n\n const periodMs: Record<string, number> = {\n session: 0,\n daily: 86_400_000,\n weekly: 604_800_000,\n monthly: 2_592_000_000,\n };\n\n let entries =\n period === \"session\"\n ? allEntries\n : allEntries.filter(\n (e) => now - e.timestamp.getTime() < periodMs[period]!,\n );\n\n if (agentId) {\n entries = entries.filter((e) => e.agentId === agentId);\n }\n\n const totalCost = entries.reduce((sum, e) => sum + e.cost, 0);\n const cacheHits = entries.filter((e) => e.cachedHit).length;\n const cacheMisses = entries.filter((e) => !e.cachedHit).length;\n\n const costByTier: Record<string, number> = {};\n for (const entry of entries) {\n costByTier[entry.tier] = (costByTier[entry.tier] ?? 0) + entry.cost;\n }\n\n const costByAgent: Record<string, number> = {};\n for (const entry of entries) {\n costByAgent[entry.agentId] = (costByAgent[entry.agentId] ?? 0) + entry.cost;\n }\n\n return {\n period,\n totalCost,\n totalRequests: entries.length,\n cacheHits,\n cacheMisses,\n cacheHitRate: entries.length > 0 ? cacheHits / entries.length : 0,\n savings: 0,\n costByTier,\n costByAgent,\n avgCostPerRequest: entries.length > 0 ? totalCost / entries.length : 0,\n avgLatencyMs:\n entries.length > 0\n ? entries.reduce((sum, e) => sum + e.latencyMs, 0) / entries.length\n : 0,\n };\n }).pipe(\n Effect.mapError(\n (e) => new CostTrackingError({ message: \"Failed to generate report\", cause: e }),\n ),\n );\n\n return { record, getReport } satisfies CostTracker;\n});\n","import { Effect, Context, Layer } from \"effect\";\nimport type { ModelCostConfig, CostEntry, BudgetLimits, BudgetStatus, CostReport } from \"./types.js\";\nimport { DEFAULT_BUDGET_LIMITS } from \"./types.js\";\nimport type { BudgetExceededError, CostTrackingError, CacheError, RoutingError } from \"./errors.js\";\nimport { routeToModel } from \"./routing/complexity-router.js\";\nimport { estimateTokens } from \"./routing/complexity-router.js\";\nimport { makeSemanticCache } from \"./caching/semantic-cache.js\";\nimport { makePromptCompressor } from \"./compression/prompt-compressor.js\";\nimport { makeBudgetEnforcer } from \"./budgets/budget-enforcer.js\";\nimport { makeCostTracker } from \"./analytics/cost-tracker.js\";\n\n// ─── Service Tag ───\n\nexport class CostService extends Context.Tag(\"CostService\")<\n CostService,\n {\n readonly routeToModel: (\n task: string,\n context?: string,\n ) => Effect.Effect<ModelCostConfig, RoutingError>;\n\n readonly checkCache: (\n query: string,\n ) => Effect.Effect<string | null, CacheError>;\n\n readonly cacheResponse: (\n query: string,\n response: string,\n model: string,\n ttlMs?: number,\n ) => Effect.Effect<void, CacheError>;\n\n readonly compressPrompt: (\n prompt: string,\n maxTokens?: number,\n ) => Effect.Effect<{ compressed: string; savedTokens: number }, CostTrackingError>;\n\n readonly checkBudget: (\n estimatedCost: number,\n agentId: string,\n sessionId: string,\n ) => Effect.Effect<void, BudgetExceededError>;\n\n readonly recordCost: (\n entry: Omit<CostEntry, \"id\" | \"timestamp\">,\n ) => Effect.Effect<void, CostTrackingError>;\n\n readonly getBudgetStatus: (\n agentId: string,\n ) => Effect.Effect<BudgetStatus, CostTrackingError>;\n\n readonly getReport: (\n period: \"session\" | \"daily\" | \"weekly\" | \"monthly\",\n agentId?: string,\n ) => Effect.Effect<CostReport, CostTrackingError>;\n }\n>() {}\n\n// ─── Live Implementation ───\n\nexport const CostServiceLive = (budgetLimits: BudgetLimits = DEFAULT_BUDGET_LIMITS) =>\n Layer.effect(\n CostService,\n Effect.gen(function* () {\n const cache = yield* makeSemanticCache;\n const compressor = yield* makePromptCompressor;\n const budget = yield* makeBudgetEnforcer(budgetLimits);\n const tracker = yield* makeCostTracker;\n\n return {\n routeToModel: (task, context) => routeToModel(task, context),\n\n checkCache: (query) => cache.check(query),\n\n cacheResponse: (query, response, model, ttlMs) =>\n cache.store(query, response, model, ttlMs),\n\n compressPrompt: (prompt, maxTokens) =>\n compressor.compress(prompt, maxTokens),\n\n checkBudget: (estimatedCost, agentId, sessionId) =>\n budget.check(estimatedCost, agentId, sessionId),\n\n recordCost: (entry) => tracker.record(entry),\n\n getBudgetStatus: (agentId) =>\n budget.getStatus(agentId) as Effect.Effect<BudgetStatus, CostTrackingError>,\n\n getReport: (period, agentId) => tracker.getReport(period, agentId),\n };\n }),\n );\n","import type { BudgetLimits } from \"./types.js\";\nimport { DEFAULT_BUDGET_LIMITS } from \"./types.js\";\nimport { CostServiceLive } from \"./cost-service.js\";\n\nexport const createCostLayer = (budgetLimits?: Partial<BudgetLimits>) =>\n CostServiceLive({\n ...DEFAULT_BUDGET_LIMITS,\n ...budgetLimits,\n });\n"],"mappings":"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","names":["Effect","Effect","entries","Effect","Effect","Effect","Ref","Effect","Ref","Effect","Ref","Effect","Ref","Effect","Effect"]}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@reactive-agents/cost",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsup --config ../../tsup.config.base.ts",
9
+ "typecheck": "tsc --noEmit",
10
+ "test": "bun test",
11
+ "test:watch": "bun test --watch"
12
+ },
13
+ "dependencies": {
14
+ "effect": "^3.10.0",
15
+ "@reactive-agents/core": "0.1.0",
16
+ "@reactive-agents/llm-provider": "0.1.0",
17
+ "@reactive-agents/memory": "0.1.0"
18
+ },
19
+ "devDependencies": {
20
+ "typescript": "^5.7.0",
21
+ "bun-types": "latest"
22
+ },
23
+ "license": "MIT",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/tylerjrbuell/reactive-agents-ts.git",
27
+ "directory": "packages/cost"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "exports": {
38
+ ".": {
39
+ "types": "./dist/index.d.ts",
40
+ "import": "./dist/index.js",
41
+ "default": "./dist/index.js"
42
+ }
43
+ },
44
+ "description": "Cost management for Reactive Agents — complexity routing and budget enforcement",
45
+ "homepage": "https://tylerjrbuell.github.io/reactive-agents-ts/",
46
+ "bugs": {
47
+ "url": "https://github.com/tylerjrbuell/reactive-agents-ts/issues"
48
+ }
49
+ }