pi-free 2.0.7 → 2.0.8
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 +540 -458
- package/README.md +572 -495
- package/config.ts +58 -11
- package/constants.ts +12 -0
- package/index.ts +66 -2
- package/lib/model-detection.ts +1 -0
- package/lib/model-enhancer.ts +20 -20
- package/lib/open-browser.ts +1 -1
- package/lib/quota-monitor.ts +123 -0
- package/lib/types.ts +101 -101
- package/lib/util.ts +460 -351
- package/package.json +68 -68
- package/provider-failover/benchmark-lookup.ts +743 -702
- package/provider-failover/benchmarks-chunk-0.ts +48 -48
- package/provider-failover/benchmarks-chunk-1.ts +44 -44
- package/provider-failover/benchmarks-chunk-2.ts +39 -39
- package/provider-failover/benchmarks-chunk-3.ts +41 -41
- package/provider-failover/benchmarks-chunk-4.ts +33 -33
- package/providers/cline/cline-auth.ts +473 -473
- package/providers/cline/cline-models.ts +2 -2
- package/providers/cline/cline.ts +1 -1
- package/providers/codestral/codestral.ts +139 -0
- package/providers/crofai/crofai.ts +14 -85
- package/providers/deepinfra/deepinfra.ts +109 -0
- package/providers/kilo/kilo-auth.ts +155 -155
- package/providers/kilo/kilo.ts +1 -1
- package/providers/llm7/llm7.ts +156 -0
- package/providers/model-fetcher.ts +2 -2
- package/providers/nvidia/nvidia.ts +4 -4
- package/providers/ollama/ollama.ts +1 -1
- package/providers/opencode-session.ts +1 -1
- package/providers/qwen/qwen-models.ts +101 -101
- package/providers/qwen/qwen.ts +1 -1
- package/providers/sambanova/sambanova.ts +109 -0
- package/providers/zenmux/zenmux.ts +5 -2
- package/scripts/check-extensions.mjs +6 -4
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Qwen OAuth model definitions.
|
|
3
|
-
*
|
|
4
|
-
* @deprecated The 1,000 req/day free tier is no longer available. Auth is broken.
|
|
5
|
-
* This provider remains for backward compatibility but should not be used.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { ProviderModelConfig } from "@mariozechner/pi-coding-agent";
|
|
9
|
-
import { createLogger } from "../../lib/logger.ts";
|
|
10
|
-
|
|
11
|
-
const _logger = createLogger("qwen-models");
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* portal.qwen.ai compatibility settings.
|
|
15
|
-
*
|
|
16
|
-
* portal.qwen.ai's OpenAI-compatible API does not support several parameters
|
|
17
|
-
* that the pi framework sends by default.
|
|
18
|
-
*/
|
|
19
|
-
export const PORTAL_COMPAT: NonNullable<ProviderModelConfig["compat"]> = {
|
|
20
|
-
supportsStore: false,
|
|
21
|
-
supportsDeveloperRole: false,
|
|
22
|
-
supportsReasoningEffort: false,
|
|
23
|
-
supportsUsageInStreaming: false,
|
|
24
|
-
supportsStrictMode: false,
|
|
25
|
-
maxTokensField: "max_tokens",
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Fallback model used before OAuth completes or if model discovery fails.
|
|
30
|
-
* The real model ID is resolved dynamically via fetchQwenLiveModels() after auth.
|
|
31
|
-
*/
|
|
32
|
-
export const QWEN_FREE_MODELS: ProviderModelConfig[] = [
|
|
33
|
-
{
|
|
34
|
-
id: "coder-model",
|
|
35
|
-
name: "Qwen Coder — DEPRECATED (free tier discontinued)",
|
|
36
|
-
reasoning: false,
|
|
37
|
-
input: ["text"],
|
|
38
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
39
|
-
contextWindow: 131_072,
|
|
40
|
-
maxTokens: 16_384,
|
|
41
|
-
compat: PORTAL_COMPAT,
|
|
42
|
-
},
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Fetch Qwen models. Returns static model list for backward compatibility.
|
|
47
|
-
* @deprecated Qwen free tier is discontinued.
|
|
48
|
-
*/
|
|
49
|
-
export async function fetchQwenModels(): Promise<ProviderModelConfig[]> {
|
|
50
|
-
_logger.info("Qwen provider is deprecated, returning placeholder models");
|
|
51
|
-
return QWEN_FREE_MODELS;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Fetch live model list from the Qwen API using the OAuth access token.
|
|
56
|
-
* Returns updated models with real IDs from the server, or the original
|
|
57
|
-
* models unchanged if the request fails.
|
|
58
|
-
*/
|
|
59
|
-
export async function fetchQwenLiveModels(
|
|
60
|
-
baseUrl: string,
|
|
61
|
-
accessToken: string,
|
|
62
|
-
templateModels: ProviderModelConfig[],
|
|
63
|
-
): Promise<ProviderModelConfig[]> {
|
|
64
|
-
try {
|
|
65
|
-
const response = await fetch(`${baseUrl}/models`, {
|
|
66
|
-
headers: {
|
|
67
|
-
Authorization: `Bearer ${accessToken}`,
|
|
68
|
-
Accept: "application/json",
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
if (!response.ok) {
|
|
73
|
-
_logger.info("Qwen /v1/models fetch failed, keeping current model IDs", {
|
|
74
|
-
status: response.status,
|
|
75
|
-
});
|
|
76
|
-
return templateModels;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
interface ModelEntry {
|
|
80
|
-
id: string;
|
|
81
|
-
}
|
|
82
|
-
const data = (await response.json()) as { data?: ModelEntry[] };
|
|
83
|
-
const ids: string[] = (data.data ?? [])
|
|
84
|
-
.map((m: ModelEntry) => m.id)
|
|
85
|
-
.filter(Boolean);
|
|
86
|
-
|
|
87
|
-
_logger.info("Qwen live models discovered", { ids });
|
|
88
|
-
|
|
89
|
-
if (ids.length === 0) return templateModels;
|
|
90
|
-
|
|
91
|
-
// Prefer a coder model if available, otherwise use the first model
|
|
92
|
-
const preferred = ids.find((id) => /coder/i.test(id)) ?? ids[0];
|
|
93
|
-
|
|
94
|
-
return templateModels.map((m) => ({ ...m, id: preferred }));
|
|
95
|
-
} catch (err) {
|
|
96
|
-
_logger.info("Qwen live model fetch error, keeping current model IDs", {
|
|
97
|
-
error: String(err),
|
|
98
|
-
});
|
|
99
|
-
return templateModels;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Qwen OAuth model definitions.
|
|
3
|
+
*
|
|
4
|
+
* @deprecated The 1,000 req/day free tier is no longer available. Auth is broken.
|
|
5
|
+
* This provider remains for backward compatibility but should not be used.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ProviderModelConfig } from "@mariozechner/pi-coding-agent";
|
|
9
|
+
import { createLogger } from "../../lib/logger.ts";
|
|
10
|
+
|
|
11
|
+
const _logger = createLogger("qwen-models");
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* portal.qwen.ai compatibility settings.
|
|
15
|
+
*
|
|
16
|
+
* portal.qwen.ai's OpenAI-compatible API does not support several parameters
|
|
17
|
+
* that the pi framework sends by default.
|
|
18
|
+
*/
|
|
19
|
+
export const PORTAL_COMPAT: NonNullable<ProviderModelConfig["compat"]> = {
|
|
20
|
+
supportsStore: false,
|
|
21
|
+
supportsDeveloperRole: false,
|
|
22
|
+
supportsReasoningEffort: false,
|
|
23
|
+
supportsUsageInStreaming: false,
|
|
24
|
+
supportsStrictMode: false,
|
|
25
|
+
maxTokensField: "max_tokens",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Fallback model used before OAuth completes or if model discovery fails.
|
|
30
|
+
* The real model ID is resolved dynamically via fetchQwenLiveModels() after auth.
|
|
31
|
+
*/
|
|
32
|
+
export const QWEN_FREE_MODELS: ProviderModelConfig[] = [
|
|
33
|
+
{
|
|
34
|
+
id: "coder-model",
|
|
35
|
+
name: "Qwen Coder — DEPRECATED (free tier discontinued)",
|
|
36
|
+
reasoning: false,
|
|
37
|
+
input: ["text"],
|
|
38
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
39
|
+
contextWindow: 131_072,
|
|
40
|
+
maxTokens: 16_384,
|
|
41
|
+
compat: PORTAL_COMPAT,
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Fetch Qwen models. Returns static model list for backward compatibility.
|
|
47
|
+
* @deprecated Qwen free tier is discontinued.
|
|
48
|
+
*/
|
|
49
|
+
export async function fetchQwenModels(): Promise<ProviderModelConfig[]> {
|
|
50
|
+
_logger.info("Qwen provider is deprecated, returning placeholder models");
|
|
51
|
+
return QWEN_FREE_MODELS;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Fetch live model list from the Qwen API using the OAuth access token.
|
|
56
|
+
* Returns updated models with real IDs from the server, or the original
|
|
57
|
+
* models unchanged if the request fails.
|
|
58
|
+
*/
|
|
59
|
+
export async function fetchQwenLiveModels(
|
|
60
|
+
baseUrl: string,
|
|
61
|
+
accessToken: string,
|
|
62
|
+
templateModels: ProviderModelConfig[],
|
|
63
|
+
): Promise<ProviderModelConfig[]> {
|
|
64
|
+
try {
|
|
65
|
+
const response = await fetch(`${baseUrl}/models`, {
|
|
66
|
+
headers: {
|
|
67
|
+
Authorization: `Bearer ${accessToken}`,
|
|
68
|
+
Accept: "application/json",
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
_logger.info("Qwen /v1/models fetch failed, keeping current model IDs", {
|
|
74
|
+
status: response.status,
|
|
75
|
+
});
|
|
76
|
+
return templateModels;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface ModelEntry {
|
|
80
|
+
id: string;
|
|
81
|
+
}
|
|
82
|
+
const data = (await response.json()) as { data?: ModelEntry[] };
|
|
83
|
+
const ids: string[] = (data.data ?? [])
|
|
84
|
+
.map((m: ModelEntry) => m.id)
|
|
85
|
+
.filter(Boolean);
|
|
86
|
+
|
|
87
|
+
_logger.info("Qwen live models discovered", { ids });
|
|
88
|
+
|
|
89
|
+
if (ids.length === 0) return templateModels;
|
|
90
|
+
|
|
91
|
+
// Prefer a coder model if available, otherwise use the first model
|
|
92
|
+
const preferred = ids.find((id) => /coder/i.test(id)) ?? ids[0];
|
|
93
|
+
|
|
94
|
+
return templateModels.map((m) => ({ ...m, id: preferred }));
|
|
95
|
+
} catch (err) {
|
|
96
|
+
_logger.info("Qwen live model fetch error, keeping current model IDs", {
|
|
97
|
+
error: String(err),
|
|
98
|
+
});
|
|
99
|
+
return templateModels;
|
|
100
|
+
}
|
|
101
|
+
}
|
package/providers/qwen/qwen.ts
CHANGED
|
@@ -59,7 +59,7 @@ const DASHSCOPE_HEADERS = {
|
|
|
59
59
|
// Extension entry point
|
|
60
60
|
// =============================================================================
|
|
61
61
|
|
|
62
|
-
export default async function (pi: ExtensionAPI) {
|
|
62
|
+
export default async function qwenProvider(pi: ExtensionAPI) {
|
|
63
63
|
// DEPRECATION WARNING
|
|
64
64
|
_logger.warn(
|
|
65
65
|
"Qwen provider is deprecated. The 1,000 req/day free tier is no longer available.",
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SambaNova Provider Extension
|
|
3
|
+
*
|
|
4
|
+
* SambaNova Cloud offers fast inference on custom RDU hardware with an
|
|
5
|
+
* OpenAI-compatible API. Known for running Llama 3.3 70B faster than
|
|
6
|
+
* competitors.
|
|
7
|
+
*
|
|
8
|
+
* Free tier (no credit card, no payment method):
|
|
9
|
+
* - Production models: 20-480 RPM, 400-9600 RPD
|
|
10
|
+
* - Preview models: 10-150 RPM, 200-3000 RPD
|
|
11
|
+
* - Forever free, no token pricing
|
|
12
|
+
*
|
|
13
|
+
* Developer tier (add payment method):
|
|
14
|
+
* - Higher rate limits, same models
|
|
15
|
+
*
|
|
16
|
+
* Endpoint:
|
|
17
|
+
* Chat: https://api.sambanova.ai/v1/chat/completions
|
|
18
|
+
*
|
|
19
|
+
* Setup:
|
|
20
|
+
* 1. Sign up at https://cloud.sambanova.ai/
|
|
21
|
+
* 2. Get API key from https://cloud.sambanova.ai/apis
|
|
22
|
+
* 3. Set SAMBANOVA_API_KEY env var (or add to ~/.pi/free.json)
|
|
23
|
+
*
|
|
24
|
+
* Usage:
|
|
25
|
+
* pi install git:github.com/apmantza/pi-free
|
|
26
|
+
* # Set SAMBANOVA_API_KEY env var
|
|
27
|
+
* # Models appear in /model selector as "sambanova/Meta-Llama-3.3-70B-Instruct"
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
31
|
+
import { getSambanovaApiKey, getSambanovaShowPaid } from "../../config.ts";
|
|
32
|
+
import { BASE_URL_SAMBANOVA, PROVIDER_SAMBANOVA } from "../../constants.ts";
|
|
33
|
+
import { createLogger } from "../../lib/logger.ts";
|
|
34
|
+
import { registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
35
|
+
import { fetchOpenAICompatibleModels } from "../../lib/util.ts";
|
|
36
|
+
import { createReRegister, setupProvider } from "../../provider-helper.ts";
|
|
37
|
+
|
|
38
|
+
const _logger = createLogger("sambanova");
|
|
39
|
+
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// Extension Entry Point
|
|
42
|
+
// =============================================================================
|
|
43
|
+
|
|
44
|
+
export default async function sambanovaProvider(pi: ExtensionAPI) {
|
|
45
|
+
const apiKey = getSambanovaApiKey();
|
|
46
|
+
|
|
47
|
+
if (!apiKey) {
|
|
48
|
+
_logger.info(
|
|
49
|
+
"[sambanova] Skipping — SAMBANOVA_API_KEY not set. Sign up at https://cloud.sambanova.ai/",
|
|
50
|
+
);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Fetch models via shared OpenAI-compatible helper
|
|
55
|
+
const allModels = await fetchOpenAICompatibleModels(
|
|
56
|
+
"sambanova",
|
|
57
|
+
BASE_URL_SAMBANOVA,
|
|
58
|
+
apiKey,
|
|
59
|
+
{ maxTokens: 8_192 },
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (allModels.length === 0) {
|
|
63
|
+
_logger.warn("[sambanova] No models available");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// All SambaNova models are free-tier (no payment method required).
|
|
68
|
+
// Rate limits are lower on free tier but all models are accessible.
|
|
69
|
+
const freeModels = allModels;
|
|
70
|
+
const stored = { free: freeModels, all: allModels };
|
|
71
|
+
|
|
72
|
+
_logger.info(
|
|
73
|
+
`[sambanova] Registered ${allModels.length} models (all free tier)`,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Create re-register function
|
|
77
|
+
const reRegister = createReRegister(pi, {
|
|
78
|
+
providerId: PROVIDER_SAMBANOVA,
|
|
79
|
+
baseUrl: BASE_URL_SAMBANOVA,
|
|
80
|
+
apiKey,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Register with global toggle
|
|
84
|
+
registerWithGlobalToggle(PROVIDER_SAMBANOVA, stored, reRegister, true);
|
|
85
|
+
|
|
86
|
+
// Setup provider with toggle command
|
|
87
|
+
setupProvider(
|
|
88
|
+
pi,
|
|
89
|
+
{
|
|
90
|
+
providerId: PROVIDER_SAMBANOVA,
|
|
91
|
+
initialShowPaid: getSambanovaShowPaid(),
|
|
92
|
+
tosUrl: "https://sambanova.ai/terms",
|
|
93
|
+
reRegister: (models, _stored) => {
|
|
94
|
+
if (_stored) {
|
|
95
|
+
stored.free = _stored.free;
|
|
96
|
+
stored.all = _stored.all;
|
|
97
|
+
}
|
|
98
|
+
reRegister(models);
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
stored,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Initial registration — respect persisted toggle state
|
|
105
|
+
const showPaid = getSambanovaShowPaid();
|
|
106
|
+
const initialModels =
|
|
107
|
+
showPaid && stored.all.length > 0 ? stored.all : freeModels;
|
|
108
|
+
reRegister(initialModels);
|
|
109
|
+
}
|
|
@@ -171,6 +171,9 @@ export default async function zenmuxProvider(pi: ExtensionAPI) {
|
|
|
171
171
|
stored,
|
|
172
172
|
);
|
|
173
173
|
|
|
174
|
-
// Initial registration
|
|
175
|
-
|
|
174
|
+
// Initial registration — respect persisted toggle state
|
|
175
|
+
const showPaid = getZenmuxShowPaid();
|
|
176
|
+
const initialModels =
|
|
177
|
+
showPaid && stored.all.length > 0 ? stored.all : freeModels;
|
|
178
|
+
reRegister(initialModels);
|
|
176
179
|
}
|
|
@@ -19,7 +19,9 @@ function resolveNpm() {
|
|
|
19
19
|
for (const p of [
|
|
20
20
|
"/usr/bin/npm",
|
|
21
21
|
"/usr/local/bin/npm",
|
|
22
|
-
process.platform === "win32"
|
|
22
|
+
process.platform === "win32"
|
|
23
|
+
? String.raw`C:\Program Files\nodejs\npm.cmd`
|
|
24
|
+
: "",
|
|
23
25
|
]) {
|
|
24
26
|
if (p && existsSync(p)) return p;
|
|
25
27
|
}
|
|
@@ -77,11 +79,11 @@ const seen = new Set();
|
|
|
77
79
|
|
|
78
80
|
for (const file of files) {
|
|
79
81
|
const src = readFileSync(file, "utf8");
|
|
80
|
-
const relFile = file.slice(installDir.length + 1).
|
|
82
|
+
const relFile = file.slice(installDir.length + 1).replaceAll("\\", "/");
|
|
81
83
|
// Strip comments before matching imports
|
|
82
84
|
const stripped = src
|
|
83
|
-
.
|
|
84
|
-
.
|
|
85
|
+
.replaceAll(/\/\/[^\n]*/g, "")
|
|
86
|
+
.replaceAll(/\/\*[\s\S]*?\*\//g, "");
|
|
85
87
|
const importRe = /from\s+['"](\.[^'"]+)['"]/g;
|
|
86
88
|
let match;
|
|
87
89
|
while ((match = importRe.exec(stripped)) !== null) {
|