budget-agent 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/router.js ADDED
@@ -0,0 +1,29 @@
1
+ // ─── Default thresholds ─────────────────────────────────────────────────────
2
+ const DEFAULT_THRESHOLDS = [0.6, 0.85];
3
+ /**
4
+ * Resolves which model to use from the fallback chain based on current
5
+ * budget consumption. Returns the model and its index in the chain.
6
+ */
7
+ export function resolveModel(fallbackChain, thresholds, usage, maxCostUSD) {
8
+ if (!maxCostUSD || maxCostUSD <= 0) {
9
+ return { model: fallbackChain[0], index: 0 };
10
+ }
11
+ const pct = usage.totalCostUSD / maxCostUSD;
12
+ const t = thresholds ?? DEFAULT_THRESHOLDS;
13
+ // Find the appropriate tier
14
+ let tier = 0;
15
+ for (let i = 0; i < t.length; i++) {
16
+ if (pct >= t[i]) {
17
+ tier = i + 1;
18
+ }
19
+ }
20
+ // Clamp to chain length
21
+ const idx = Math.min(tier, fallbackChain.length - 1);
22
+ return { model: fallbackChain[idx], index: idx };
23
+ }
24
+ /**
25
+ * Checks if a downgrade occurred and returns logging info.
26
+ */
27
+ export function shouldLogDowngrade(prevIndex, currentIndex) {
28
+ return currentIndex > prevIndex;
29
+ }
@@ -0,0 +1,15 @@
1
+ import type { BudgetUsage, StepUsage } from './types.js';
2
+ export declare class UsageTracker {
3
+ private startTime;
4
+ private stepHistory;
5
+ private totalInputTokens;
6
+ private totalOutputTokens;
7
+ private totalCostUSD;
8
+ constructor();
9
+ static fromSnapshot(usage: BudgetUsage): UsageTracker;
10
+ record(step: StepUsage): void;
11
+ snapshot(): BudgetUsage;
12
+ stepCount(): number;
13
+ reset(): void;
14
+ rollback(): StepUsage | null;
15
+ }
@@ -0,0 +1,53 @@
1
+ export class UsageTracker {
2
+ startTime;
3
+ stepHistory = [];
4
+ totalInputTokens = 0;
5
+ totalOutputTokens = 0;
6
+ totalCostUSD = 0;
7
+ constructor() {
8
+ this.startTime = Date.now();
9
+ }
10
+ static fromSnapshot(usage) {
11
+ const t = new UsageTracker();
12
+ t.stepHistory = usage.stepHistory;
13
+ t.totalInputTokens = usage.totalInputTokens;
14
+ t.totalOutputTokens = usage.totalOutputTokens;
15
+ t.totalCostUSD = usage.totalCostUSD;
16
+ t.startTime = Date.now() - usage.elapsedMs;
17
+ return t;
18
+ }
19
+ record(step) {
20
+ this.stepHistory.push(step);
21
+ this.totalInputTokens += step.inputTokens;
22
+ this.totalOutputTokens += step.outputTokens;
23
+ this.totalCostUSD += step.costUSD;
24
+ }
25
+ snapshot() {
26
+ return {
27
+ steps: this.stepHistory.length,
28
+ totalInputTokens: this.totalInputTokens,
29
+ totalOutputTokens: this.totalOutputTokens,
30
+ totalCostUSD: this.totalCostUSD,
31
+ elapsedMs: Date.now() - this.startTime,
32
+ stepHistory: [...this.stepHistory],
33
+ };
34
+ }
35
+ stepCount() {
36
+ return this.stepHistory.length;
37
+ }
38
+ reset() {
39
+ this.stepHistory = [];
40
+ this.totalInputTokens = 0;
41
+ this.totalOutputTokens = 0;
42
+ this.totalCostUSD = 0;
43
+ }
44
+ rollback() {
45
+ const last = this.stepHistory.pop();
46
+ if (!last)
47
+ return null;
48
+ this.totalInputTokens -= last.inputTokens;
49
+ this.totalOutputTokens -= last.outputTokens;
50
+ this.totalCostUSD -= last.costUSD;
51
+ return last;
52
+ }
53
+ }
@@ -0,0 +1,149 @@
1
+ export interface BudgetLimits {
2
+ maxCostUSD?: number;
3
+ maxInputTokens?: number;
4
+ maxOutputTokens?: number;
5
+ maxTotalTokens?: number;
6
+ maxSteps?: number;
7
+ maxWallTimeMs?: number;
8
+ preflightCheck?: boolean;
9
+ preflightOutputTokenEstimate?: number;
10
+ }
11
+ export type ExceededStrategy = 'abort' | ((usage: BudgetUsage) => void);
12
+ export interface ExecutorResult {
13
+ model: string;
14
+ usage: {
15
+ prompt_tokens: number;
16
+ completion_tokens: number;
17
+ total_tokens: number;
18
+ };
19
+ choices: Array<{
20
+ message: {
21
+ role: string;
22
+ content: string;
23
+ };
24
+ finish_reason: string;
25
+ }>;
26
+ }
27
+ /**
28
+ * Custom API executor. Replaces the built-in OpenRouter fetch.
29
+ * Receives the StepRequest (after adaptive routing, auto-compress, etc.) and
30
+ * returns a normalized response. Use this to integrate any LLM provider.
31
+ */
32
+ export type AgentExecutor = (request: StepRequest) => Promise<ExecutorResult>;
33
+ export interface BudgetOptions {
34
+ apiKey: string;
35
+ limits: BudgetLimits;
36
+ onExceeded?: ExceededStrategy;
37
+ pricingCacheTTLMs?: number;
38
+ siteUrl?: string;
39
+ appTitle?: string;
40
+ autoCompress?: {
41
+ thresholdTokens: number;
42
+ keepLastN?: number;
43
+ };
44
+ circuitBreaker?: {
45
+ repetitionWindow?: number;
46
+ repetitionThreshold?: number;
47
+ stagnationWindow?: number;
48
+ stagnationMinLength?: number;
49
+ };
50
+ adaptiveRouting?: {
51
+ fallbackChain: string[];
52
+ thresholds?: number[];
53
+ };
54
+ checkpoint?: {
55
+ path?: string;
56
+ enabled?: boolean;
57
+ };
58
+ onEvent?: (event: import('./events.js').AgentBudgetEvent) => void;
59
+ warningThreshold?: number;
60
+ telemetry?: {
61
+ enabled: boolean;
62
+ tracer?: unknown;
63
+ };
64
+ executor?: AgentExecutor;
65
+ baseUrl?: string;
66
+ defaultHeaders?: Record<string, string>;
67
+ }
68
+ export interface ModelPricing {
69
+ promptPerToken: number;
70
+ completionPerToken: number;
71
+ }
72
+ export interface StepUsage {
73
+ stepIndex: number;
74
+ model: string;
75
+ inputTokens: number;
76
+ outputTokens: number;
77
+ costUSD: number;
78
+ durationMs: number;
79
+ outputContent?: string;
80
+ }
81
+ export interface BudgetUsage {
82
+ steps: number;
83
+ totalInputTokens: number;
84
+ totalOutputTokens: number;
85
+ totalCostUSD: number;
86
+ elapsedMs: number;
87
+ stepHistory: StepUsage[];
88
+ }
89
+ export type ExceededReason = 'cost' | 'inputTokens' | 'outputTokens' | 'totalTokens' | 'steps' | 'wallTime' | 'preflightCostEstimate' | 'circuitBreaker' | 'fallbackChainExhausted';
90
+ export interface BudgetExceededError {
91
+ reason: ExceededReason;
92
+ limit: number;
93
+ actual: number;
94
+ usage: BudgetUsage;
95
+ remainingBudget?: number;
96
+ triggerMode?: 'repetition' | 'stagnation';
97
+ windowSize?: number;
98
+ similarity?: number;
99
+ estimatedCost?: number;
100
+ }
101
+ export interface OpenRouterMessage {
102
+ role: 'system' | 'user' | 'assistant' | 'tool';
103
+ content: string | null;
104
+ tool_call_id?: string;
105
+ name?: string;
106
+ }
107
+ export interface StepRequest {
108
+ model: string;
109
+ messages: OpenRouterMessage[];
110
+ tools?: unknown[];
111
+ temperature?: number;
112
+ max_tokens?: number;
113
+ stream?: boolean;
114
+ [key: string]: unknown;
115
+ }
116
+ export interface StreamChunk {
117
+ type: 'token' | 'done' | 'error';
118
+ token?: string;
119
+ response?: OpenRouterResponse;
120
+ error?: string;
121
+ }
122
+ export type TokenCallback = (token: string) => void;
123
+ export interface OpenRouterResponse {
124
+ id: string;
125
+ model: string;
126
+ choices: Array<{
127
+ message: OpenRouterMessage;
128
+ finish_reason: string;
129
+ native_finish_reason?: string | null;
130
+ error?: {
131
+ code: number;
132
+ message: string;
133
+ metadata?: Record<string, unknown>;
134
+ };
135
+ }>;
136
+ usage: {
137
+ prompt_tokens: number;
138
+ completion_tokens: number;
139
+ total_tokens: number;
140
+ };
141
+ }
142
+ export interface CheckpointData {
143
+ checkpointVersion: string;
144
+ messages: OpenRouterMessage[];
145
+ usage: BudgetUsage;
146
+ model: string;
147
+ resumeFromStep: number;
148
+ createdAt: string;
149
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ // ─── Limits ──────────────────────────────────────────────────────────────────
2
+ export {};
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "budget-agent",
3
+ "version": "0.4.3",
4
+ "description": "Provider-agnostic budget enforcement SDK for LLM agents. Track token/cost/step usage, enforce limits, auto-compress, circuit-breaker, checkpoints, adaptive routing, and more.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "package.json"
17
+ ],
18
+ "engines": {
19
+ "node": ">=18"
20
+ },
21
+ "keywords": [
22
+ "agent",
23
+ "budget",
24
+ "token",
25
+ "cost",
26
+ "llm",
27
+ "circuit-breaker",
28
+ "checkpoint",
29
+ "rate-limit"
30
+ ],
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/duggal1/agent-budget.git"
35
+ }
36
+ }