pi-free 2.0.8 → 2.0.10
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/CHANGELOG.md +29 -1
- package/README.md +588 -572
- package/banner.jpg +0 -0
- package/banner.png +0 -0
- package/banner.svg +12 -10
- package/config.ts +349 -337
- package/constants.ts +106 -103
- package/index.ts +242 -239
- package/lib/built-in-toggle.ts +2 -2
- package/lib/model-detection.ts +1 -1
- package/lib/model-enhancer.ts +20 -20
- package/lib/provider-compat.ts +1 -1
- package/lib/registry.ts +1 -1
- package/lib/util.ts +524 -460
- package/package.json +70 -68
- package/provider-helper.ts +1 -1
- package/providers/cline/cline-auth.ts +1 -1
- package/providers/cline/cline.ts +2 -2
- package/providers/codestral/codestral.ts +1 -1
- package/providers/crofai/crofai.ts +190 -99
- package/providers/deepinfra/deepinfra.ts +206 -109
- package/providers/dynamic-built-in/index.ts +1 -1
- package/providers/kilo/kilo-auth.ts +1 -1
- package/providers/kilo/kilo.ts +2 -2
- package/providers/llm7/llm7.ts +1 -1
- package/providers/nvidia/nvidia.ts +1 -1
- package/providers/ollama/ollama.ts +610 -295
- package/providers/ollama/thinking-levels.ts +96 -0
- package/providers/qwen/qwen-auth.ts +1 -1
- package/providers/qwen/qwen-models.ts +101 -101
- package/providers/qwen/qwen.ts +2 -2
- package/providers/sambanova/sambanova.ts +1 -1
- package/providers/together/together.ts +197 -0
- package/providers/zenmux/zenmux.ts +194 -179
package/package.json
CHANGED
|
@@ -1,68 +1,70 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pi-free",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "AI model providers for Pi with free model filtering. Shows only $0 cost models by default. Supports Kilo (free OAuth), Cline (free), NVIDIA (freemium), ZenMux, CrofAI, Ollama Cloud, and more.",
|
|
6
|
-
"keywords": [
|
|
7
|
-
"pi-package",
|
|
8
|
-
"pi-extension",
|
|
9
|
-
"free-models",
|
|
10
|
-
"paid-models",
|
|
11
|
-
"model-filter",
|
|
12
|
-
"nvidia-nim",
|
|
13
|
-
"kilo",
|
|
14
|
-
"cline",
|
|
15
|
-
"zenmux",
|
|
16
|
-
"crofai",
|
|
17
|
-
"ollama-cloud"
|
|
18
|
-
],
|
|
19
|
-
"license": "MIT",
|
|
20
|
-
"author": "Apostolos Mantzaris",
|
|
21
|
-
"homepage": "https://github.com/apmantza/pi-free#readme",
|
|
22
|
-
"bugs": {
|
|
23
|
-
"url": "https://github.com/apmantza/pi-free/issues"
|
|
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
|
-
"index.ts",
|
|
34
|
-
"providers/**/*.ts",
|
|
35
|
-
"lib/**/*.ts",
|
|
36
|
-
"provider-failover/**/*.ts",
|
|
37
|
-
"config.ts",
|
|
38
|
-
"constants.ts",
|
|
39
|
-
"provider-helper.ts",
|
|
40
|
-
"README.md",
|
|
41
|
-
"LICENSE",
|
|
42
|
-
"CHANGELOG.md",
|
|
43
|
-
"banner.svg",
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
"test
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"@
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-free",
|
|
3
|
+
"version": "2.0.10",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "AI model providers for Pi with free model filtering. Shows only $0 cost models by default. Supports Kilo (free OAuth), Cline (free), NVIDIA (freemium), ZenMux, CrofAI, Ollama Cloud, and more.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"pi-package",
|
|
8
|
+
"pi-extension",
|
|
9
|
+
"free-models",
|
|
10
|
+
"paid-models",
|
|
11
|
+
"model-filter",
|
|
12
|
+
"nvidia-nim",
|
|
13
|
+
"kilo",
|
|
14
|
+
"cline",
|
|
15
|
+
"zenmux",
|
|
16
|
+
"crofai",
|
|
17
|
+
"ollama-cloud"
|
|
18
|
+
],
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": "Apostolos Mantzaris",
|
|
21
|
+
"homepage": "https://github.com/apmantza/pi-free#readme",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/apmantza/pi-free/issues"
|
|
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
|
+
"index.ts",
|
|
34
|
+
"providers/**/*.ts",
|
|
35
|
+
"lib/**/*.ts",
|
|
36
|
+
"provider-failover/**/*.ts",
|
|
37
|
+
"config.ts",
|
|
38
|
+
"constants.ts",
|
|
39
|
+
"provider-helper.ts",
|
|
40
|
+
"README.md",
|
|
41
|
+
"LICENSE",
|
|
42
|
+
"CHANGELOG.md",
|
|
43
|
+
"banner.svg",
|
|
44
|
+
"banner.png",
|
|
45
|
+
"banner.jpg",
|
|
46
|
+
"scripts/check-extensions.mjs"
|
|
47
|
+
],
|
|
48
|
+
"scripts": {
|
|
49
|
+
"check": "node scripts/check-extensions.mjs",
|
|
50
|
+
"test": "vitest",
|
|
51
|
+
"test:ui": "vitest --ui",
|
|
52
|
+
"test:run": "vitest run"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@earendil-works/pi-ai": "*",
|
|
56
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
57
|
+
"@earendil-works/pi-tui": "*"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@vitest/ui": "^4.1.5",
|
|
61
|
+
"tsx": "^4.0.0",
|
|
62
|
+
"typescript": "^6.0.2",
|
|
63
|
+
"vitest": "^4.1.5"
|
|
64
|
+
},
|
|
65
|
+
"pi": {
|
|
66
|
+
"extensions": [
|
|
67
|
+
"./index.ts"
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
package/provider-helper.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import type {
|
|
11
11
|
ExtensionAPI,
|
|
12
12
|
ProviderModelConfig,
|
|
13
|
-
} from "@
|
|
13
|
+
} from "@earendil-works/pi-coding-agent";
|
|
14
14
|
import { saveConfig } from "./config.ts";
|
|
15
15
|
import { createLogger } from "./lib/logger.ts";
|
|
16
16
|
import { enhanceModelNameWithCodingIndex } from "./provider-failover/benchmark-lookup.ts";
|
|
@@ -14,7 +14,7 @@ import { URL as NodeURL } from "node:url";
|
|
|
14
14
|
import type {
|
|
15
15
|
OAuthCredentials,
|
|
16
16
|
OAuthLoginCallbacks,
|
|
17
|
-
} from "@
|
|
17
|
+
} from "@earendil-works/pi-ai";
|
|
18
18
|
import { BASE_URL_CLINE, CLINE_AUTH_TIMEOUT_MS } from "../../constants.ts";
|
|
19
19
|
import { createLogger } from "../../lib/logger.ts";
|
|
20
20
|
|
package/providers/cline/cline.ts
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* # Models appear immediately; run /login cline to start chatting
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import type { OAuthCredentials } from "@
|
|
18
|
-
import type { ExtensionAPI } from "@
|
|
17
|
+
import type { OAuthCredentials } from "@earendil-works/pi-ai";
|
|
18
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
19
19
|
import { getClineShowPaid } from "../../config.ts";
|
|
20
20
|
import { BASE_URL_CLINE, PROVIDER_CLINE } from "../../constants.ts";
|
|
21
21
|
import { isFreeModel, registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
@@ -1,99 +1,190 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CrofAI Provider Extension
|
|
3
|
-
*
|
|
4
|
-
* Provides access to CrofAI API - OpenAI-compatible LLM inference service
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* CrofAI Provider Extension
|
|
3
|
+
*
|
|
4
|
+
* Provides access to CrofAI API - OpenAI-compatible LLM inference service
|
|
5
|
+
* hosting DeepSeek, Qwen, and other open-source models.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: CrofAI's /v1/models returns per-model context_length, max_completion_tokens,
|
|
8
|
+
* name, custom_reasoning, and reasoning_effort. Pricing is per-MILLION tokens.
|
|
9
|
+
*
|
|
10
|
+
* Setup:
|
|
11
|
+
* 1. Get API key from https://ai.nahcrof.com
|
|
12
|
+
* 2. Set CROFAI_API_KEY env var or add to ~/.pi/free.json
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* pi install git:github.com/apmantza/pi-free
|
|
16
|
+
* # Set CROFAI_API_KEY env var
|
|
17
|
+
* # Models appear in /model selector
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type {
|
|
21
|
+
ExtensionAPI,
|
|
22
|
+
ProviderModelConfig,
|
|
23
|
+
} from "@earendil-works/pi-coding-agent";
|
|
24
|
+
import { getCrofaiApiKey, getCrofaiShowPaid } from "../../config.ts";
|
|
25
|
+
import {
|
|
26
|
+
BASE_URL_CROFAI,
|
|
27
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
28
|
+
PROVIDER_CROFAI,
|
|
29
|
+
} from "../../constants.ts";
|
|
30
|
+
import { createLogger } from "../../lib/logger.ts";
|
|
31
|
+
import {
|
|
32
|
+
getProxyModelCompat,
|
|
33
|
+
isLikelyReasoningModel,
|
|
34
|
+
} from "../../lib/provider-compat.ts";
|
|
35
|
+
import { isFreeModel, registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
36
|
+
import { fetchWithRetry } from "../../lib/util.ts";
|
|
37
|
+
import { createReRegister, setupProvider } from "../../provider-helper.ts";
|
|
38
|
+
|
|
39
|
+
const _logger = createLogger("crofai");
|
|
40
|
+
|
|
41
|
+
// =============================================================================
|
|
42
|
+
// Types
|
|
43
|
+
// =============================================================================
|
|
44
|
+
|
|
45
|
+
interface CrofaiModel {
|
|
46
|
+
id: string;
|
|
47
|
+
name?: string;
|
|
48
|
+
context_length?: number;
|
|
49
|
+
max_completion_tokens?: number;
|
|
50
|
+
custom_reasoning?: boolean;
|
|
51
|
+
reasoning_effort?: boolean;
|
|
52
|
+
pricing?: {
|
|
53
|
+
prompt?: string;
|
|
54
|
+
completion?: string;
|
|
55
|
+
cache_prompt?: string;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// Fetch
|
|
61
|
+
// =============================================================================
|
|
62
|
+
|
|
63
|
+
function parseCrofaiPrice(priceStr: string | undefined): number {
|
|
64
|
+
if (priceStr === undefined) return 0;
|
|
65
|
+
const num = Number.parseFloat(priceStr);
|
|
66
|
+
if (Number.isNaN(num)) return 0;
|
|
67
|
+
// CrofAI pricing is per-MILLION tokens. Divide to get per-token (Pi convention).
|
|
68
|
+
return num / 1_000_000;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function fetchCrofaiModels(
|
|
72
|
+
apiKey: string,
|
|
73
|
+
): Promise<ProviderModelConfig[]> {
|
|
74
|
+
const response = await fetchWithRetry(
|
|
75
|
+
`${BASE_URL_CROFAI}/models`,
|
|
76
|
+
{
|
|
77
|
+
headers: {
|
|
78
|
+
Authorization: `Bearer ${apiKey}`,
|
|
79
|
+
"Content-Type": "application/json",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
3,
|
|
83
|
+
1000,
|
|
84
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`CrofAI API error: ${response.status} ${response.statusText}`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// CrofAI returns { data: [...] }
|
|
94
|
+
const json = (await response.json()) as {
|
|
95
|
+
data?: CrofaiModel[];
|
|
96
|
+
};
|
|
97
|
+
const models = json.data ?? [];
|
|
98
|
+
|
|
99
|
+
_logger.info(`[crofai] Fetched ${models.length} models`);
|
|
100
|
+
|
|
101
|
+
return models
|
|
102
|
+
.filter((m) => m.id)
|
|
103
|
+
.map((m): ProviderModelConfig => {
|
|
104
|
+
const name = m.name || m.id;
|
|
105
|
+
const reasoning =
|
|
106
|
+
m.custom_reasoning ?? isLikelyReasoningModel({ id: m.id, name });
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
id: m.id,
|
|
110
|
+
name,
|
|
111
|
+
reasoning,
|
|
112
|
+
input: ["text"],
|
|
113
|
+
cost: {
|
|
114
|
+
input: parseCrofaiPrice(m.pricing?.prompt),
|
|
115
|
+
output: parseCrofaiPrice(m.pricing?.completion),
|
|
116
|
+
cacheRead: parseCrofaiPrice(m.pricing?.cache_prompt),
|
|
117
|
+
cacheWrite: 0,
|
|
118
|
+
},
|
|
119
|
+
contextWindow: m.context_length ?? 128_000,
|
|
120
|
+
maxTokens: m.max_completion_tokens ?? 16_384,
|
|
121
|
+
compat: getProxyModelCompat({ id: m.id, name }),
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// =============================================================================
|
|
127
|
+
// Extension Entry Point
|
|
128
|
+
// =============================================================================
|
|
129
|
+
|
|
130
|
+
export default async function crofaiProvider(pi: ExtensionAPI) {
|
|
131
|
+
const apiKey = getCrofaiApiKey();
|
|
132
|
+
|
|
133
|
+
if (!apiKey) {
|
|
134
|
+
_logger.info(
|
|
135
|
+
"[crofai] Skipping - CROFAI_API_KEY not set (env var or ~/.pi/free.json)",
|
|
136
|
+
);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Fetch models
|
|
141
|
+
const allModels = await fetchCrofaiModels(apiKey);
|
|
142
|
+
|
|
143
|
+
if (allModels.length === 0) {
|
|
144
|
+
_logger.warn("[crofai] No models available");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const freeModels = allModels.filter((m) =>
|
|
149
|
+
isFreeModel({ ...m, provider: PROVIDER_CROFAI }, allModels),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const stored = { free: freeModels, all: allModels };
|
|
153
|
+
|
|
154
|
+
_logger.info(
|
|
155
|
+
`[crofai] Registered ${allModels.length} models (${freeModels.length} free)`,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// Create re-register function
|
|
159
|
+
const reRegister = createReRegister(pi, {
|
|
160
|
+
providerId: PROVIDER_CROFAI,
|
|
161
|
+
baseUrl: BASE_URL_CROFAI,
|
|
162
|
+
apiKey,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Register with global toggle
|
|
166
|
+
registerWithGlobalToggle(PROVIDER_CROFAI, stored, reRegister, true);
|
|
167
|
+
|
|
168
|
+
// Setup provider with toggle command
|
|
169
|
+
setupProvider(
|
|
170
|
+
pi,
|
|
171
|
+
{
|
|
172
|
+
providerId: PROVIDER_CROFAI,
|
|
173
|
+
initialShowPaid: getCrofaiShowPaid(),
|
|
174
|
+
reRegister: (models, _stored) => {
|
|
175
|
+
if (_stored) {
|
|
176
|
+
stored.free = _stored.free;
|
|
177
|
+
stored.all = _stored.all;
|
|
178
|
+
}
|
|
179
|
+
reRegister(models);
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
stored,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
// Initial registration — respect persisted toggle state
|
|
186
|
+
const showPaid = getCrofaiShowPaid();
|
|
187
|
+
const initialModels =
|
|
188
|
+
showPaid && stored.all.length > 0 ? stored.all : freeModels;
|
|
189
|
+
reRegister(initialModels);
|
|
190
|
+
}
|