pi-free 2.2.4 → 2.2.6
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 +24 -0
- package/README.md +1 -1
- package/banner.svg +1 -1
- package/config.ts +58 -29
- package/constants.ts +1 -4
- package/index.ts +30 -24
- package/lib/built-in-toggle.ts +185 -18
- package/lib/provider-cache.ts +22 -0
- package/package.json +74 -74
- package/provider-failover/benchmark-lookup.ts +4 -1
- package/provider-failover/benchmarks-chunk-0.ts +570 -570
- package/provider-failover/benchmarks-chunk-1.ts +676 -676
- package/provider-failover/benchmarks-chunk-2.ts +673 -673
- package/provider-failover/benchmarks-chunk-3.ts +680 -680
- package/provider-failover/benchmarks-chunk-4.ts +683 -683
- package/provider-failover/benchmarks-chunk-5.ts +816 -474
- package/provider-helper.ts +64 -0
- package/providers/bai/bai.ts +8 -2
- package/providers/crofai/crofai.ts +8 -2
- package/providers/deepinfra/deepinfra.ts +8 -2
- package/providers/dynamic-built-in/index.ts +36 -26
- package/providers/kilo/kilo.ts +41 -22
- package/providers/novita/novita.ts +8 -2
- package/providers/openmodel/openmodel.ts +8 -2
- package/providers/qoder/models.ts +63 -175
- package/providers/qoder/qoder.ts +49 -84
- package/providers/qoder/stream.ts +182 -274
- package/providers/qoder/transform.ts +5 -2
- package/providers/routeway/routeway.ts +8 -2
- package/providers/sambanova/sambanova.ts +12 -6
- package/providers/together/together.ts +8 -2
- package/providers/tokenrouter/tokenrouter.ts +8 -2
- package/providers/zenmux/zenmux.ts +8 -2
- package/providers/codestral/codestral.ts +0 -128
- package/providers/qoder/encoding.ts +0 -48
|
@@ -49,7 +49,11 @@ import {
|
|
|
49
49
|
} from "../../lib/provider-compat.ts";
|
|
50
50
|
import { isFreeModel, registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
51
51
|
import { cleanModelName, fetchWithRetry } from "../../lib/util.ts";
|
|
52
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
enhanceWithCI,
|
|
54
|
+
loadCachedOrFetchModels,
|
|
55
|
+
setupProvider,
|
|
56
|
+
} from "../../provider-helper.ts";
|
|
53
57
|
|
|
54
58
|
const _logger = createLogger("tokenrouter");
|
|
55
59
|
|
|
@@ -564,7 +568,9 @@ export default async function tokenRouterProvider(pi: ExtensionAPI) {
|
|
|
564
568
|
return;
|
|
565
569
|
}
|
|
566
570
|
|
|
567
|
-
const allModels = await
|
|
571
|
+
const allModels = await loadCachedOrFetchModels(PROVIDER_TOKENROUTER, () =>
|
|
572
|
+
fetchTokenRouterModels(apiKey),
|
|
573
|
+
);
|
|
568
574
|
|
|
569
575
|
if (allModels.length === 0) {
|
|
570
576
|
_logger.warn("[tokenrouter] No text chat models available");
|
|
@@ -31,7 +31,11 @@ import { safeEnrichModelsWithModelsDev } from "../../lib/model-metadata.ts";
|
|
|
31
31
|
import { getProxyModelCompat } from "../../lib/provider-compat.ts";
|
|
32
32
|
import { isFreeModel, registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
33
33
|
import { fetchWithRetry } from "../../lib/util.ts";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
createReRegister,
|
|
36
|
+
loadCachedOrFetchModels,
|
|
37
|
+
setupProvider,
|
|
38
|
+
} from "../../provider-helper.ts";
|
|
35
39
|
|
|
36
40
|
const _logger = createLogger("zenmux");
|
|
37
41
|
|
|
@@ -146,7 +150,9 @@ export default async function zenmuxProvider(pi: ExtensionAPI) {
|
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
// Fetch models
|
|
149
|
-
const allModels = await
|
|
153
|
+
const allModels = await loadCachedOrFetchModels(PROVIDER_ZENMUX, () =>
|
|
154
|
+
fetchZenmuxModels(apiKey),
|
|
155
|
+
);
|
|
150
156
|
|
|
151
157
|
if (allModels.length === 0) {
|
|
152
158
|
_logger.warn("[zenmux] No models available");
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Codestral Provider Extension
|
|
3
|
-
*
|
|
4
|
-
* Codestral is Mistral AI's code-focused model. This provider registers it
|
|
5
|
-
* through the Codestral-specific endpoint (codestral.mistral.ai) using
|
|
6
|
-
* the Mistral SDK (api: "mistral-conversations") — separate from the built-in
|
|
7
|
-
* "mistral" provider which uses api.mistral.ai.
|
|
8
|
-
*
|
|
9
|
-
* NOTE: Do NOT use api: "openai-completions" here. Codestral's API is
|
|
10
|
-
* Mistral-format (camelCase fields, maxTokens, no stream_options/store).
|
|
11
|
-
* The OpenAI completions adapter sends OpenAI-specific fields that Mistral
|
|
12
|
-
* rejects with HTTP 422 "Extra inputs are not permitted".
|
|
13
|
-
*
|
|
14
|
-
* Free tier (Experiment plan):
|
|
15
|
-
* - 2 req/min, 500K tokens/min, 1B tokens/month
|
|
16
|
-
* - No credit card — phone verification only
|
|
17
|
-
* - Sign up at https://console.mistral.ai/codestral
|
|
18
|
-
*
|
|
19
|
-
* Endpoints:
|
|
20
|
-
* Chat: https://codestral.mistral.ai/v1/chat/completions
|
|
21
|
-
* FIM: https://codestral.mistral.ai/v1/fim/completions (not used by pi)
|
|
22
|
-
*
|
|
23
|
-
* Setup:
|
|
24
|
-
* 1. Get API key from https://console.mistral.ai/codestral
|
|
25
|
-
* 2. Set CODESTRAL_API_KEY env var (or MISTRAL_API_KEY as fallback)
|
|
26
|
-
*
|
|
27
|
-
* Usage:
|
|
28
|
-
* pi install git:github.com/apmantza/pi-free
|
|
29
|
-
* # Set CODESTRAL_API_KEY env var
|
|
30
|
-
* # Models appear in /model selector as "codestral/codestral-latest"
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
import type {
|
|
34
|
-
ExtensionAPI,
|
|
35
|
-
ProviderModelConfig,
|
|
36
|
-
} from "@earendil-works/pi-coding-agent";
|
|
37
|
-
import {
|
|
38
|
-
getCodestralApiKey,
|
|
39
|
-
getCodestralShowPaid,
|
|
40
|
-
getMistralApiKey,
|
|
41
|
-
} from "../../config.ts";
|
|
42
|
-
import { BASE_URL_CODESTRAL, PROVIDER_CODESTRAL } from "../../constants.ts";
|
|
43
|
-
import { createLogger } from "../../lib/logger.ts";
|
|
44
|
-
import { registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
45
|
-
import { enhanceWithCI, setupProvider } from "../../provider-helper.ts";
|
|
46
|
-
|
|
47
|
-
const _logger = createLogger("codestral");
|
|
48
|
-
|
|
49
|
-
// =============================================================================
|
|
50
|
-
// Model Definition
|
|
51
|
-
// =============================================================================
|
|
52
|
-
|
|
53
|
-
const CODESTRAL_MODEL: ProviderModelConfig = {
|
|
54
|
-
id: "codestral-latest",
|
|
55
|
-
name: "Codestral",
|
|
56
|
-
reasoning: false,
|
|
57
|
-
input: ["text"],
|
|
58
|
-
cost: {
|
|
59
|
-
input: 0.3,
|
|
60
|
-
output: 0.9,
|
|
61
|
-
cacheRead: 0,
|
|
62
|
-
cacheWrite: 0,
|
|
63
|
-
},
|
|
64
|
-
contextWindow: 256_000,
|
|
65
|
-
maxTokens: 4_096,
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// =============================================================================
|
|
69
|
-
// Extension Entry Point
|
|
70
|
-
// =============================================================================
|
|
71
|
-
|
|
72
|
-
export default async function codestralProvider(pi: ExtensionAPI) {
|
|
73
|
-
// Try CODESTRAL_API_KEY first, fall back to MISTRAL_API_KEY
|
|
74
|
-
const apiKey = getCodestralApiKey() || getMistralApiKey();
|
|
75
|
-
|
|
76
|
-
if (!apiKey) {
|
|
77
|
-
_logger.info(
|
|
78
|
-
"[codestral] Skipping — neither CODESTRAL_API_KEY nor MISTRAL_API_KEY set",
|
|
79
|
-
);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const keySource = getCodestralApiKey()
|
|
84
|
-
? "CODESTRAL_API_KEY"
|
|
85
|
-
: "MISTRAL_API_KEY";
|
|
86
|
-
_logger.info(`[codestral] Using key from ${keySource}`);
|
|
87
|
-
|
|
88
|
-
const allModels = [CODESTRAL_MODEL];
|
|
89
|
-
const freeModels = allModels; // All $0.30/$0.90 — still accessible via Experiment free tier
|
|
90
|
-
const stored = { free: freeModels, all: allModels };
|
|
91
|
-
|
|
92
|
-
// Re-register function — uses mistral-conversations API (Mistral SDK)
|
|
93
|
-
// NOT openai-completions: Codestral uses the same API format as Mistral
|
|
94
|
-
// and rejects OpenAI-specific fields (stream_options, store, max_completion_tokens) with 422.
|
|
95
|
-
const reRegister = (models: typeof freeModels) => {
|
|
96
|
-
pi.registerProvider(PROVIDER_CODESTRAL, {
|
|
97
|
-
baseUrl: BASE_URL_CODESTRAL,
|
|
98
|
-
apiKey,
|
|
99
|
-
api: "mistral-conversations" as const,
|
|
100
|
-
models: enhanceWithCI(models, PROVIDER_CODESTRAL),
|
|
101
|
-
});
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
// Register with global toggle
|
|
105
|
-
registerWithGlobalToggle(PROVIDER_CODESTRAL, stored, reRegister, true);
|
|
106
|
-
|
|
107
|
-
// Setup provider (toggle command, status bar, error handling)
|
|
108
|
-
setupProvider(
|
|
109
|
-
pi,
|
|
110
|
-
{
|
|
111
|
-
providerId: PROVIDER_CODESTRAL,
|
|
112
|
-
initialShowPaid: getCodestralShowPaid(),
|
|
113
|
-
skipToggle: true, // Only one model, no toggle needed
|
|
114
|
-
reRegister: (models) => {
|
|
115
|
-
stored.free = models;
|
|
116
|
-
stored.all = models;
|
|
117
|
-
reRegister(models);
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
stored,
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
// Initial registration — uses mistral-conversations API (Mistral SDK)
|
|
124
|
-
reRegister(freeModels);
|
|
125
|
-
|
|
126
|
-
_logger.info(`[codestral] Registered codestral-latest via ${keySource}`);
|
|
127
|
-
|
|
128
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Qoder WAF bypass body encoding.
|
|
3
|
-
*
|
|
4
|
-
* Qoder's API uses a custom base64 variant with a scrambled alphabet and
|
|
5
|
-
* rearranged segments to evade WAF detection. This is the same encoding
|
|
6
|
-
* used by the official qodercli binary.
|
|
7
|
-
*
|
|
8
|
-
* The algorithm:
|
|
9
|
-
* 1. Take the standard base64 of the input
|
|
10
|
-
* 2. Rearrange segments: last N/3 chars + middle N-2N/3 chars + first N/3 chars
|
|
11
|
-
* 3. Substitute each character through a custom alphabet
|
|
12
|
-
* 4. Replace '=' padding with '$'
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const QODER_CUSTOM_ALPHABET =
|
|
16
|
-
"_doRTgHZBKcGVjlvpC,@aFSx#DPuNJme&i*MzLOEn)sUrthbf%Y^w.(kIQyXqWA!";
|
|
17
|
-
const QODER_STD_ALPHABET =
|
|
18
|
-
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Encode a body string or buffer using Qoder's custom WAF-bypass encoding.
|
|
22
|
-
*
|
|
23
|
-
* @param plaintext - String or Buffer to encode
|
|
24
|
-
* @returns Encoded string ready for transmission
|
|
25
|
-
*/
|
|
26
|
-
export function qoderEncodeBody(plaintext: string | Buffer): string {
|
|
27
|
-
const std = Buffer.isBuffer(plaintext)
|
|
28
|
-
? plaintext.toString("base64")
|
|
29
|
-
: Buffer.from(plaintext).toString("base64");
|
|
30
|
-
const n = std.length;
|
|
31
|
-
const a = Math.floor(n / 3);
|
|
32
|
-
const rearranged = std.slice(n - a) + std.slice(a, n - a) + std.slice(0, a);
|
|
33
|
-
let out = "";
|
|
34
|
-
for (let i = 0; i < n; i++) {
|
|
35
|
-
const c = rearranged[i];
|
|
36
|
-
if (c === "=") {
|
|
37
|
-
out += "$";
|
|
38
|
-
} else {
|
|
39
|
-
const idx = QODER_STD_ALPHABET.indexOf(c);
|
|
40
|
-
if (idx >= 0) {
|
|
41
|
-
out += QODER_CUSTOM_ALPHABET[idx];
|
|
42
|
-
} else {
|
|
43
|
-
out += c;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return out;
|
|
48
|
-
}
|