caplyr 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/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # Caplyr
2
+
3
+ **AI Cost Control Plane** — Stop runaway API bills automatically.
4
+
5
+ Caplyr sits between your app and AI providers, controlling how requests execute based on cost constraints. Budget guardrails, auto-downgrade, and kill switch — in 2 lines of code.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install caplyr
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import Anthropic from "@anthropic-ai/sdk";
17
+ import { protect } from "caplyr";
18
+
19
+ // Wrap your client — everything else stays the same
20
+ const client = protect(new Anthropic(), {
21
+ apiKey: "caplyr_...", // Get yours at https://app.caplyr.com
22
+ budget: 500, // Monthly cap in dollars
23
+ fallback: "claude-haiku-3-5-20241022", // Auto-downgrade target
24
+ });
25
+
26
+ // Use exactly as before — Caplyr is invisible
27
+ const response = await client.messages.create({
28
+ model: "claude-sonnet-4-20250514",
29
+ max_tokens: 1024,
30
+ messages: [{ role: "user", content: "Hello" }],
31
+ });
32
+ ```
33
+
34
+ Works with OpenAI too:
35
+
36
+ ```typescript
37
+ import OpenAI from "openai";
38
+ import { protect } from "caplyr";
39
+
40
+ const client = protect(new OpenAI(), {
41
+ apiKey: "caplyr_...",
42
+ budget: 500,
43
+ fallback: "gpt-4o-mini",
44
+ });
45
+
46
+ const response = await client.chat.completions.create({
47
+ model: "gpt-4o",
48
+ messages: [{ role: "user", content: "Hello" }],
49
+ });
50
+ ```
51
+
52
+ ## What It Does
53
+
54
+ | Feature | Description |
55
+ |---------|-------------|
56
+ | **Budget Guardrails** | Daily and monthly caps enforced at the SDK level. Hit the limit → requests are blocked or downgraded. |
57
+ | **Auto Downgrade** | When budget threshold is reached, automatically route to a cheaper model. Your app keeps working. |
58
+ | **Kill Switch** | One-click emergency stop. Halts all AI API calls instantly. |
59
+
60
+ ## Modes
61
+
62
+ ```typescript
63
+ // Alert-only (default): observe and project, don't enforce
64
+ protect(client, { apiKey: "...", mode: "alert_only" });
65
+
66
+ // Cost protect: enforce budget caps and auto-downgrade
67
+ protect(client, { apiKey: "...", mode: "cost_protect", budget: 500 });
68
+ ```
69
+
70
+ ## Configuration
71
+
72
+ | Option | Type | Default | Description |
73
+ |--------|------|---------|-------------|
74
+ | `apiKey` | `string` | required | Your Caplyr project API key |
75
+ | `budget` | `number` | — | Monthly budget cap in dollars |
76
+ | `dailyBudget` | `number` | — | Daily budget cap in dollars |
77
+ | `fallback` | `string` | auto | Fallback model for auto-downgrade |
78
+ | `mode` | `string` | `"alert_only"` | `"alert_only"` \| `"cost_protect"` \| `"kill_switch"` |
79
+ | `downgradeThreshold` | `number` | `0.8` | Budget % at which downgrade activates |
80
+ | `endpoint_tag` | `string` | — | Custom tag for cost attribution |
81
+
82
+ ## Handling Blocked Requests
83
+
84
+ When a request is blocked, Caplyr throws a structured error:
85
+
86
+ ```typescript
87
+ try {
88
+ const response = await client.messages.create({ ... });
89
+ } catch (err) {
90
+ if (err.caplyr) {
91
+ // Caplyr enforcement event
92
+ console.log(err.caplyr.code); // "BUDGET_EXCEEDED" | "KILL_SWITCH_ACTIVE"
93
+ console.log(err.caplyr.retry_after); // ISO timestamp for next reset
94
+ console.log(err.caplyr.budget_used); // Current spend
95
+ }
96
+ }
97
+ ```
98
+
99
+ ## Shutdown
100
+
101
+ ```typescript
102
+ import { shutdown } from "caplyr";
103
+
104
+ // Flush pending logs on app exit
105
+ process.on("SIGTERM", () => shutdown());
106
+ ```
107
+
108
+ ## Links
109
+
110
+ - **Dashboard**: [app.caplyr.com](https://app.caplyr.com)
111
+ - **Docs**: [docs.caplyr.com](https://docs.caplyr.com)
112
+ - **Website**: [caplyr.com](https://caplyr.com)
113
+
114
+ ## License
115
+
116
+ MIT
@@ -0,0 +1,168 @@
1
+ /** Protection status of the SDK connection */
2
+ type ProtectionStatus = "ACTIVE" | "DEGRADED" | "OFF";
3
+ /** Supported AI providers */
4
+ type Provider = "anthropic" | "openai";
5
+ /** Operating mode for the SDK */
6
+ type CaplyrMode = "cost_protect" | "alert_only" | "kill_switch";
7
+ /** Configuration for the protect() function */
8
+ interface CaplyrConfig {
9
+ /** Your Caplyr project API key */
10
+ apiKey: string;
11
+ /** Caplyr backend URL (defaults to https://api.caplyr.com) */
12
+ endpoint?: string;
13
+ /** Operating mode (defaults to "alert_only" for trust-building) */
14
+ mode?: CaplyrMode;
15
+ /** Monthly budget cap in dollars */
16
+ budget?: number;
17
+ /** Daily budget cap in dollars */
18
+ dailyBudget?: number;
19
+ /** Fallback model when budget is approached/exceeded */
20
+ fallback?: string;
21
+ /** Budget threshold (0-1) at which auto-downgrade activates (default: 0.8) */
22
+ downgradeThreshold?: number;
23
+ /** Custom endpoint tag for attribution */
24
+ endpoint_tag?: string;
25
+ /** Batch size before flushing logs (default: 10) */
26
+ batchSize?: number;
27
+ /** Max interval in ms between log flushes (default: 30000) */
28
+ flushInterval?: number;
29
+ /** Heartbeat interval in ms (default: 60000) */
30
+ heartbeatInterval?: number;
31
+ /** Called when protection status changes */
32
+ onStatusChange?: (status: ProtectionStatus) => void;
33
+ /** Called when an enforcement event occurs */
34
+ onEnforcement?: (event: EnforcementEvent) => void;
35
+ /** Called when an error occurs in the SDK (non-blocking) */
36
+ onError?: (error: Error) => void;
37
+ }
38
+ /** Metadata captured for every AI API call */
39
+ interface RequestLog {
40
+ /** Unique request ID */
41
+ id: string;
42
+ /** Timestamp of the request */
43
+ timestamp: number;
44
+ /** Provider: "anthropic" | "openai" */
45
+ provider: Provider;
46
+ /** Model used (e.g., "claude-sonnet-4-20250514") */
47
+ model: string;
48
+ /** Input tokens consumed */
49
+ input_tokens: number;
50
+ /** Output tokens consumed */
51
+ output_tokens: number;
52
+ /** Calculated cost in dollars */
53
+ cost: number;
54
+ /** Request latency in milliseconds */
55
+ latency_ms: number;
56
+ /** Endpoint tag for attribution */
57
+ endpoint_tag?: string;
58
+ /** Whether this request was auto-downgraded */
59
+ downgraded: boolean;
60
+ /** Original model if downgraded */
61
+ original_model?: string;
62
+ /** Whether this request was blocked */
63
+ blocked: boolean;
64
+ /** Reason for block/downgrade if applicable */
65
+ enforcement_reason?: string;
66
+ }
67
+ /** Enforcement event emitted when guardrails activate */
68
+ interface EnforcementEvent {
69
+ type: "downgrade" | "block" | "kill_switch";
70
+ timestamp: number;
71
+ reason: string;
72
+ original_model?: string;
73
+ fallback_model?: string;
74
+ budget_used: number;
75
+ budget_limit: number;
76
+ estimated_savings: number;
77
+ }
78
+ /** Budget status returned from the backend */
79
+ interface BudgetStatus {
80
+ daily_used: number;
81
+ daily_limit: number | null;
82
+ monthly_used: number;
83
+ monthly_limit: number | null;
84
+ status: ProtectionStatus;
85
+ kill_switch_active: boolean;
86
+ }
87
+ /** Structured error returned when a request is blocked */
88
+ interface CaplyrBlockedError {
89
+ code: "BUDGET_EXCEEDED" | "KILL_SWITCH_ACTIVE" | "RATE_LIMITED";
90
+ message: string;
91
+ budget_used: number;
92
+ budget_limit: number;
93
+ retry_after?: string;
94
+ dashboard_url: string;
95
+ }
96
+ /** Internal state of the SDK */
97
+ interface CaplyrState {
98
+ status: ProtectionStatus;
99
+ mode: CaplyrMode;
100
+ budget_daily_used: number;
101
+ budget_monthly_used: number;
102
+ kill_switch_active: boolean;
103
+ last_heartbeat: number;
104
+ request_count: number;
105
+ total_cost: number;
106
+ total_savings: number;
107
+ }
108
+
109
+ /**
110
+ * Wrap an AI provider client with Caplyr cost control.
111
+ *
112
+ * Returns a proxy that is type-compatible with the original client.
113
+ * All existing code works unchanged — Caplyr is invisible to callers.
114
+ *
115
+ * @param client - An Anthropic or OpenAI client instance
116
+ * @param config - Caplyr configuration
117
+ * @returns A proxied client with cost control applied
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * import Anthropic from "@anthropic-ai/sdk"
122
+ * import { protect } from "caplyr"
123
+ *
124
+ * const client = protect(new Anthropic(), {
125
+ * apiKey: "caplyr_...",
126
+ * budget: 500,
127
+ * })
128
+ * ```
129
+ */
130
+ declare function protect<T>(client: T, config: CaplyrConfig): T;
131
+ /**
132
+ * Get the current protection status for a given API key.
133
+ */
134
+ declare function getStatus(apiKey: string): ProtectionStatus;
135
+ /**
136
+ * Get the full SDK state for a given API key.
137
+ */
138
+ declare function getState(apiKey: string): CaplyrState | null;
139
+ /**
140
+ * Flush all pending logs and stop background tasks.
141
+ * Call this during application shutdown.
142
+ */
143
+ declare function shutdown(apiKey?: string): Promise<void>;
144
+
145
+ interface ModelPricing {
146
+ input: number;
147
+ output: number;
148
+ }
149
+ /**
150
+ * Calculate the cost of a single API request.
151
+ * Returns cost in dollars.
152
+ */
153
+ declare function calculateCost(model: string, inputTokens: number, outputTokens: number): number;
154
+ /**
155
+ * Register a custom model with pricing at runtime.
156
+ * Useful for new models not yet in the built-in table.
157
+ */
158
+ declare function registerModel(model: string, pricing: ModelPricing, fallback?: string): void;
159
+ /**
160
+ * Check if a model is known to the pricing table.
161
+ */
162
+ declare function isKnownModel(model: string): boolean;
163
+ /**
164
+ * Get pricing for a model. Returns null if unknown.
165
+ */
166
+ declare function getModelPricing(model: string): ModelPricing | null;
167
+
168
+ export { type BudgetStatus, type CaplyrBlockedError, type CaplyrConfig, type CaplyrMode, type CaplyrState, type EnforcementEvent, type ProtectionStatus, type Provider, type RequestLog, calculateCost, getModelPricing, getState, getStatus, isKnownModel, protect, registerModel, shutdown };
@@ -0,0 +1,168 @@
1
+ /** Protection status of the SDK connection */
2
+ type ProtectionStatus = "ACTIVE" | "DEGRADED" | "OFF";
3
+ /** Supported AI providers */
4
+ type Provider = "anthropic" | "openai";
5
+ /** Operating mode for the SDK */
6
+ type CaplyrMode = "cost_protect" | "alert_only" | "kill_switch";
7
+ /** Configuration for the protect() function */
8
+ interface CaplyrConfig {
9
+ /** Your Caplyr project API key */
10
+ apiKey: string;
11
+ /** Caplyr backend URL (defaults to https://api.caplyr.com) */
12
+ endpoint?: string;
13
+ /** Operating mode (defaults to "alert_only" for trust-building) */
14
+ mode?: CaplyrMode;
15
+ /** Monthly budget cap in dollars */
16
+ budget?: number;
17
+ /** Daily budget cap in dollars */
18
+ dailyBudget?: number;
19
+ /** Fallback model when budget is approached/exceeded */
20
+ fallback?: string;
21
+ /** Budget threshold (0-1) at which auto-downgrade activates (default: 0.8) */
22
+ downgradeThreshold?: number;
23
+ /** Custom endpoint tag for attribution */
24
+ endpoint_tag?: string;
25
+ /** Batch size before flushing logs (default: 10) */
26
+ batchSize?: number;
27
+ /** Max interval in ms between log flushes (default: 30000) */
28
+ flushInterval?: number;
29
+ /** Heartbeat interval in ms (default: 60000) */
30
+ heartbeatInterval?: number;
31
+ /** Called when protection status changes */
32
+ onStatusChange?: (status: ProtectionStatus) => void;
33
+ /** Called when an enforcement event occurs */
34
+ onEnforcement?: (event: EnforcementEvent) => void;
35
+ /** Called when an error occurs in the SDK (non-blocking) */
36
+ onError?: (error: Error) => void;
37
+ }
38
+ /** Metadata captured for every AI API call */
39
+ interface RequestLog {
40
+ /** Unique request ID */
41
+ id: string;
42
+ /** Timestamp of the request */
43
+ timestamp: number;
44
+ /** Provider: "anthropic" | "openai" */
45
+ provider: Provider;
46
+ /** Model used (e.g., "claude-sonnet-4-20250514") */
47
+ model: string;
48
+ /** Input tokens consumed */
49
+ input_tokens: number;
50
+ /** Output tokens consumed */
51
+ output_tokens: number;
52
+ /** Calculated cost in dollars */
53
+ cost: number;
54
+ /** Request latency in milliseconds */
55
+ latency_ms: number;
56
+ /** Endpoint tag for attribution */
57
+ endpoint_tag?: string;
58
+ /** Whether this request was auto-downgraded */
59
+ downgraded: boolean;
60
+ /** Original model if downgraded */
61
+ original_model?: string;
62
+ /** Whether this request was blocked */
63
+ blocked: boolean;
64
+ /** Reason for block/downgrade if applicable */
65
+ enforcement_reason?: string;
66
+ }
67
+ /** Enforcement event emitted when guardrails activate */
68
+ interface EnforcementEvent {
69
+ type: "downgrade" | "block" | "kill_switch";
70
+ timestamp: number;
71
+ reason: string;
72
+ original_model?: string;
73
+ fallback_model?: string;
74
+ budget_used: number;
75
+ budget_limit: number;
76
+ estimated_savings: number;
77
+ }
78
+ /** Budget status returned from the backend */
79
+ interface BudgetStatus {
80
+ daily_used: number;
81
+ daily_limit: number | null;
82
+ monthly_used: number;
83
+ monthly_limit: number | null;
84
+ status: ProtectionStatus;
85
+ kill_switch_active: boolean;
86
+ }
87
+ /** Structured error returned when a request is blocked */
88
+ interface CaplyrBlockedError {
89
+ code: "BUDGET_EXCEEDED" | "KILL_SWITCH_ACTIVE" | "RATE_LIMITED";
90
+ message: string;
91
+ budget_used: number;
92
+ budget_limit: number;
93
+ retry_after?: string;
94
+ dashboard_url: string;
95
+ }
96
+ /** Internal state of the SDK */
97
+ interface CaplyrState {
98
+ status: ProtectionStatus;
99
+ mode: CaplyrMode;
100
+ budget_daily_used: number;
101
+ budget_monthly_used: number;
102
+ kill_switch_active: boolean;
103
+ last_heartbeat: number;
104
+ request_count: number;
105
+ total_cost: number;
106
+ total_savings: number;
107
+ }
108
+
109
+ /**
110
+ * Wrap an AI provider client with Caplyr cost control.
111
+ *
112
+ * Returns a proxy that is type-compatible with the original client.
113
+ * All existing code works unchanged — Caplyr is invisible to callers.
114
+ *
115
+ * @param client - An Anthropic or OpenAI client instance
116
+ * @param config - Caplyr configuration
117
+ * @returns A proxied client with cost control applied
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * import Anthropic from "@anthropic-ai/sdk"
122
+ * import { protect } from "caplyr"
123
+ *
124
+ * const client = protect(new Anthropic(), {
125
+ * apiKey: "caplyr_...",
126
+ * budget: 500,
127
+ * })
128
+ * ```
129
+ */
130
+ declare function protect<T>(client: T, config: CaplyrConfig): T;
131
+ /**
132
+ * Get the current protection status for a given API key.
133
+ */
134
+ declare function getStatus(apiKey: string): ProtectionStatus;
135
+ /**
136
+ * Get the full SDK state for a given API key.
137
+ */
138
+ declare function getState(apiKey: string): CaplyrState | null;
139
+ /**
140
+ * Flush all pending logs and stop background tasks.
141
+ * Call this during application shutdown.
142
+ */
143
+ declare function shutdown(apiKey?: string): Promise<void>;
144
+
145
+ interface ModelPricing {
146
+ input: number;
147
+ output: number;
148
+ }
149
+ /**
150
+ * Calculate the cost of a single API request.
151
+ * Returns cost in dollars.
152
+ */
153
+ declare function calculateCost(model: string, inputTokens: number, outputTokens: number): number;
154
+ /**
155
+ * Register a custom model with pricing at runtime.
156
+ * Useful for new models not yet in the built-in table.
157
+ */
158
+ declare function registerModel(model: string, pricing: ModelPricing, fallback?: string): void;
159
+ /**
160
+ * Check if a model is known to the pricing table.
161
+ */
162
+ declare function isKnownModel(model: string): boolean;
163
+ /**
164
+ * Get pricing for a model. Returns null if unknown.
165
+ */
166
+ declare function getModelPricing(model: string): ModelPricing | null;
167
+
168
+ export { type BudgetStatus, type CaplyrBlockedError, type CaplyrConfig, type CaplyrMode, type CaplyrState, type EnforcementEvent, type ProtectionStatus, type Provider, type RequestLog, calculateCost, getModelPricing, getState, getStatus, isKnownModel, protect, registerModel, shutdown };