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.
Files changed (35) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +1 -1
  3. package/banner.svg +1 -1
  4. package/config.ts +58 -29
  5. package/constants.ts +1 -4
  6. package/index.ts +30 -24
  7. package/lib/built-in-toggle.ts +185 -18
  8. package/lib/provider-cache.ts +22 -0
  9. package/package.json +74 -74
  10. package/provider-failover/benchmark-lookup.ts +4 -1
  11. package/provider-failover/benchmarks-chunk-0.ts +570 -570
  12. package/provider-failover/benchmarks-chunk-1.ts +676 -676
  13. package/provider-failover/benchmarks-chunk-2.ts +673 -673
  14. package/provider-failover/benchmarks-chunk-3.ts +680 -680
  15. package/provider-failover/benchmarks-chunk-4.ts +683 -683
  16. package/provider-failover/benchmarks-chunk-5.ts +816 -474
  17. package/provider-helper.ts +64 -0
  18. package/providers/bai/bai.ts +8 -2
  19. package/providers/crofai/crofai.ts +8 -2
  20. package/providers/deepinfra/deepinfra.ts +8 -2
  21. package/providers/dynamic-built-in/index.ts +36 -26
  22. package/providers/kilo/kilo.ts +41 -22
  23. package/providers/novita/novita.ts +8 -2
  24. package/providers/openmodel/openmodel.ts +8 -2
  25. package/providers/qoder/models.ts +63 -175
  26. package/providers/qoder/qoder.ts +49 -84
  27. package/providers/qoder/stream.ts +182 -274
  28. package/providers/qoder/transform.ts +5 -2
  29. package/providers/routeway/routeway.ts +8 -2
  30. package/providers/sambanova/sambanova.ts +12 -6
  31. package/providers/together/together.ts +8 -2
  32. package/providers/tokenrouter/tokenrouter.ts +8 -2
  33. package/providers/zenmux/zenmux.ts +8 -2
  34. package/providers/codestral/codestral.ts +0 -128
  35. 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 { enhanceWithCI, setupProvider } from "../../provider-helper.ts";
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 fetchTokenRouterModels(apiKey);
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 { createReRegister, setupProvider } from "../../provider-helper.ts";
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 fetchZenmuxModels(apiKey);
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
- }