pi-free 1.0.2 → 1.0.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/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "pi-free",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
- "description": "AIO Free AI models for Pi",
5
+ "description": "AIO Free AI models for Pi - Access free models from Kilo, Zen, OpenRouter, NVIDIA, Cline, Mistral, and Ollama",
6
6
  "keywords": [
7
7
  "pi-package",
8
8
  "pi-extension",
@@ -10,7 +10,6 @@
10
10
  "ai-providers",
11
11
  "openrouter",
12
12
  "nvidia-nim",
13
- "opencode",
14
13
  "kilo",
15
14
  "cline",
16
15
  "ollama",
@@ -23,11 +22,36 @@
23
22
  "bugs": {
24
23
  "url": "https://github.com/apmantza/pi-free/issues"
25
24
  },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/apmantza/pi-free.git"
28
+ },
29
+ "engines": {
30
+ "node": ">=20.0.0"
31
+ },
32
+ "files": [
33
+ "providers/**/*.ts",
34
+ "lib/**/*.ts",
35
+ "usage/**/*.ts",
36
+ "config.ts",
37
+ "constants.ts",
38
+ "provider-factory.ts",
39
+ "provider-helper.ts",
40
+ "README.md",
41
+ "LICENSE",
42
+ "CHANGELOG.md"
43
+ ],
26
44
  "scripts": {
27
45
  "test": "vitest",
28
46
  "test:ui": "vitest --ui",
29
47
  "test:run": "vitest run"
30
48
  },
49
+ "peerDependencies": {
50
+ "@mariozechner/pi-ai": "*",
51
+ "@mariozechner/pi-coding-agent": "*",
52
+ "@mariozechner/pi-tui": "*",
53
+ "@sinclair/typebox": "*"
54
+ },
31
55
  "devDependencies": {
32
56
  "vitest": "^1.0.0",
33
57
  "@vitest/ui": "^1.0.0",
@@ -44,9 +68,5 @@
44
68
  "./providers/mistral.ts",
45
69
  "./providers/ollama.ts"
46
70
  ]
47
- },
48
- "repository": {
49
- "type": "git",
50
- "url": "git+https://github.com/apmantza/pi-free.git"
51
71
  }
52
72
  }
@@ -1,275 +0,0 @@
1
- /**
2
- * Error classification for provider failover
3
- * Detects 429 rate limits, capacity errors, and other provider-specific errors
4
- */
5
-
6
- import { createLogger } from "../lib/logger.ts";
7
- import { getFreeTierUsage, getLimitWarning } from "../usage/limits.ts";
8
-
9
- const _logger = createLogger("failover");
10
-
11
- export type ErrorType =
12
- | "rate_limit" // 429, quota exceeded
13
- | "capacity" // No capacity, overloaded
14
- | "auth" // Invalid key, unauthorized
15
- | "network" // Timeout, connection error
16
- | "unknown"; // Unclassified
17
-
18
- export interface ClassifiedError {
19
- type: ErrorType;
20
- provider?: string;
21
- model?: string;
22
- statusCode?: number;
23
- message: string;
24
- retryable: boolean;
25
- retryAfterMs?: number; // Server-suggested retry delay
26
- }
27
-
28
- // Pattern matching for various provider error messages
29
- const RATE_LIMIT_PATTERNS = [
30
- /429/i,
31
- /rate.?limit/i,
32
- /too.?many.?requests/i,
33
- /quota.*exceeded/i,
34
- /insufficient.*quota/i,
35
- /billing.*quota/i,
36
- /limit.*exceeded/i,
37
- /throttled/i,
38
- /ratelimit/i,
39
- ];
40
-
41
- const CAPACITY_PATTERNS = [
42
- /no.*capacity/i,
43
- /overloaded/i,
44
- /engine.*overloaded/i,
45
- /temporarily.*unavailable/i,
46
- /service.*unavailable/i,
47
- /503/i,
48
- /529/i, // Cloudflare origin is overloaded
49
- /busy/i,
50
- ];
51
-
52
- const AUTH_PATTERNS = [
53
- /401/i,
54
- /403/i,
55
- /unauthorized/i,
56
- /invalid.*key/i,
57
- /invalid.*token/i,
58
- /authentication/i,
59
- /api.*key.*invalid/i,
60
- /key.*not.*valid/i,
61
- /invalid.*api.*key/i,
62
- /invalid.*auth/i,
63
- ];
64
-
65
- const NETWORK_PATTERNS = [
66
- /timeout/i,
67
- /etimedout/i,
68
- /enetunreach/i,
69
- /econnreset/i,
70
- /connection.*refused/i,
71
- /fetch.*failed/i,
72
- /network.*error/i,
73
- /abort/i,
74
- /signal/i,
75
- ];
76
-
77
- /**
78
- * Extract HTTP status code from error object or message
79
- */
80
- function extractStatusCode(error: unknown): number | undefined {
81
- // Check for statusCode property
82
- if (
83
- typeof error === "object" &&
84
- error !== null &&
85
- "statusCode" in error &&
86
- typeof error.statusCode === "number"
87
- ) {
88
- return error.statusCode;
89
- }
90
-
91
- // Check for status property
92
- if (
93
- typeof error === "object" &&
94
- error !== null &&
95
- "status" in error &&
96
- typeof error.status === "number"
97
- ) {
98
- return error.status;
99
- }
100
-
101
- // Extract from message
102
- const message = String(error);
103
- const match = message.match(/\b(\d{3})\b/);
104
- if (match) {
105
- const code = Number.parseInt(match[1], 10);
106
- if (code >= 400 && code < 600) return code;
107
- }
108
-
109
- return undefined;
110
- }
111
-
112
- /**
113
- * Extract retry-after hint from error
114
- */
115
- function extractRetryAfter(error: unknown): number | undefined {
116
- const message = String(error);
117
-
118
- // Look for "retry after X seconds/minutes"
119
- const secondsMatch = message.match(/retry.?after\s+(\d+)\s*s/i);
120
- if (secondsMatch) {
121
- return Number.parseInt(secondsMatch[1], 10) * 1000;
122
- }
123
-
124
- const minutesMatch = message.match(/retry.?after\s+(\d+)\s*m/i);
125
- if (minutesMatch) {
126
- return Number.parseInt(minutesMatch[1], 10) * 60 * 1000;
127
- }
128
-
129
- // Check for retry_after property
130
- if (
131
- typeof error === "object" &&
132
- error !== null &&
133
- "retry_after" in error &&
134
- typeof error.retry_after === "number"
135
- ) {
136
- return error.retry_after * 1000;
137
- }
138
-
139
- return undefined;
140
- }
141
-
142
- /**
143
- * Classify an error to determine if it's a 429/capacity issue
144
- */
145
- export function classifyError(error: unknown): ClassifiedError {
146
- const message = String(error);
147
- const statusCode = extractStatusCode(error);
148
- const retryAfterMs = extractRetryAfter(error);
149
-
150
- // Check status code first
151
- if (statusCode === 429) {
152
- return {
153
- type: "rate_limit",
154
- statusCode,
155
- message,
156
- retryable: true,
157
- retryAfterMs: retryAfterMs ?? 60000, // Default 1 min
158
- };
159
- }
160
-
161
- if (statusCode === 503 || statusCode === 529) {
162
- return {
163
- type: "capacity",
164
- statusCode,
165
- message,
166
- retryable: true,
167
- retryAfterMs: retryAfterMs ?? 30000, // Default 30 sec
168
- };
169
- }
170
-
171
- if (statusCode === 401 || statusCode === 403) {
172
- return {
173
- type: "auth",
174
- statusCode,
175
- message,
176
- retryable: false,
177
- };
178
- }
179
-
180
- // Check patterns in message
181
- if (RATE_LIMIT_PATTERNS.some((p) => p.test(message))) {
182
- return {
183
- type: "rate_limit",
184
- statusCode,
185
- message,
186
- retryable: true,
187
- retryAfterMs: retryAfterMs ?? 60000,
188
- };
189
- }
190
-
191
- if (CAPACITY_PATTERNS.some((p) => p.test(message))) {
192
- return {
193
- type: "capacity",
194
- statusCode,
195
- message,
196
- retryable: true,
197
- retryAfterMs: retryAfterMs ?? 30000,
198
- };
199
- }
200
-
201
- if (AUTH_PATTERNS.some((p) => p.test(message))) {
202
- return {
203
- type: "auth",
204
- statusCode,
205
- message,
206
- retryable: false,
207
- };
208
- }
209
-
210
- if (NETWORK_PATTERNS.some((p) => p.test(message))) {
211
- return {
212
- type: "network",
213
- statusCode,
214
- message,
215
- retryable: true,
216
- retryAfterMs: 5000, // Short retry for network
217
- };
218
- }
219
-
220
- // Unknown error - assume retryable but with caution
221
- return {
222
- type: "unknown",
223
- statusCode,
224
- message,
225
- retryable: statusCode ? statusCode >= 500 : true,
226
- retryAfterMs: 10000,
227
- };
228
- }
229
-
230
- /**
231
- * Check if error is specifically a rate limit (429)
232
- */
233
- export function isRateLimit(error: unknown): boolean {
234
- return classifyError(error).type === "rate_limit";
235
- }
236
-
237
- /**
238
- * Check if error is capacity-related (provider overloaded)
239
- */
240
- export function isCapacityError(error: unknown): boolean {
241
- return classifyError(error).type === "capacity";
242
- }
243
-
244
- /**
245
- * Log error classification for debugging
246
- */
247
- export function logErrorClassification(
248
- _error: unknown,
249
- classified: ClassifiedError,
250
- ): void {
251
- _logger.info(`Error classified: ${classified.type}`, {
252
- statusCode: classified.statusCode,
253
- retryable: classified.retryable,
254
- retryAfterMs: classified.retryAfterMs,
255
- message: classified.message.slice(0, 100),
256
- });
257
- }
258
-
259
- /**
260
- * Log free tier usage when rate limit occurs
261
- * Helps users understand their quota consumption
262
- */
263
- export function logFreeTierUsage(provider: string): void {
264
- const usage = getFreeTierUsage(provider);
265
- const warning = getLimitWarning(provider);
266
-
267
- if (warning) {
268
- _logger.warn(`Free tier warning: ${warning}`, { provider });
269
- } else {
270
- _logger.info(`${provider} usage`, {
271
- requestsToday: usage.requestsToday,
272
- limit: usage.limit.description,
273
- });
274
- }
275
- }