pi-free 2.2.5 → 2.2.7
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 +31 -0
- package/README.md +3 -2
- package/banner.svg +1 -1
- package/config.ts +821 -779
- package/constants.ts +3 -3
- package/index.ts +29 -24
- package/lib/built-in-toggle.ts +426 -427
- 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/anyapi/anyapi.ts +270 -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 +42 -28
- package/providers/novita/novita.ts +8 -2
- package/providers/opencode-session.ts +69 -18
- package/providers/openmodel/openmodel.ts +8 -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 +12 -7
- package/providers/zenmux/zenmux.ts +8 -2
- package/providers/codestral/codestral.ts +0 -128
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
AssistantMessageEventStream,
|
|
11
11
|
Context,
|
|
12
12
|
Model,
|
|
13
|
+
ProviderHeaders,
|
|
13
14
|
SimpleStreamOptions,
|
|
14
15
|
} from "@earendil-works/pi-ai";
|
|
15
16
|
import type { ProviderConfig } from "@earendil-works/pi-coding-agent";
|
|
@@ -83,8 +84,8 @@ export type OpenCodeSessionTracker = ReturnType<
|
|
|
83
84
|
|
|
84
85
|
export function createOpenCodeHeaders(
|
|
85
86
|
tracker: OpenCodeSessionTracker,
|
|
86
|
-
existingHeaders?:
|
|
87
|
-
):
|
|
87
|
+
existingHeaders?: ProviderHeaders,
|
|
88
|
+
): ProviderHeaders {
|
|
88
89
|
return {
|
|
89
90
|
...existingHeaders,
|
|
90
91
|
...OPENCODE_STATIC_HEADERS,
|
|
@@ -115,17 +116,30 @@ type StreamSimpleFn<TApi extends Api> = (
|
|
|
115
116
|
options?: SimpleStreamOptions,
|
|
116
117
|
) => AssistantMessageEventStream;
|
|
117
118
|
|
|
118
|
-
type
|
|
119
|
-
|
|
119
|
+
type StreamSimpleModule<TApi extends Api> = {
|
|
120
|
+
streamSimple?: StreamSimpleFn<TApi>;
|
|
121
|
+
[key: string]: unknown;
|
|
120
122
|
};
|
|
121
123
|
|
|
122
|
-
type
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
type AnthropicStreamModule = StreamSimpleModule<"anthropic-messages">;
|
|
125
|
+
type OpenAICompletionsStreamModule = StreamSimpleModule<"openai-completions">;
|
|
126
|
+
|
|
127
|
+
function getStreamSimple<TApi extends Api>(
|
|
128
|
+
module: StreamSimpleModule<TApi>,
|
|
129
|
+
legacyExport: string,
|
|
130
|
+
): StreamSimpleFn<TApi> {
|
|
131
|
+
const streamSimple = module.streamSimple ?? module[legacyExport];
|
|
132
|
+
if (typeof streamSimple !== "function") {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Pi AI module does not export ${legacyExport} or streamSimple`,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return streamSimple as StreamSimpleFn<TApi>;
|
|
138
|
+
}
|
|
125
139
|
|
|
126
140
|
const piAiSubpathCache = new Map<string, Promise<unknown>>();
|
|
127
141
|
|
|
128
|
-
|
|
142
|
+
function importPiAiSubpath<T>(subpath: string): Promise<T> {
|
|
129
143
|
const specifier = `@earendil-works/pi-ai/${subpath}`;
|
|
130
144
|
const cached = piAiSubpathCache.get(specifier) as Promise<T> | undefined;
|
|
131
145
|
if (cached) return cached;
|
|
@@ -157,6 +171,9 @@ async function importPiAiRootFallback<T>(
|
|
|
157
171
|
): Promise<T | undefined> {
|
|
158
172
|
const subpath = specifier.replace("@earendil-works/pi-ai/", "");
|
|
159
173
|
const requiredExport: Record<string, string> = {
|
|
174
|
+
"api/anthropic-messages": "streamSimpleAnthropic",
|
|
175
|
+
"api/openai-completions": "streamSimpleOpenAICompletions",
|
|
176
|
+
// Keep compatibility with pre-0.80 Pi AI packages.
|
|
160
177
|
anthropic: "streamSimpleAnthropic",
|
|
161
178
|
"openai-completions": "streamSimpleOpenAICompletions",
|
|
162
179
|
};
|
|
@@ -199,6 +216,37 @@ function findPiAiPackageDir(requireBase: string): string | undefined {
|
|
|
199
216
|
return undefined;
|
|
200
217
|
}
|
|
201
218
|
|
|
219
|
+
function resolvePiAiExportTarget(
|
|
220
|
+
exportsMap: Record<string, unknown> | undefined,
|
|
221
|
+
subpath: string,
|
|
222
|
+
): string | undefined {
|
|
223
|
+
if (!exportsMap) return undefined;
|
|
224
|
+
|
|
225
|
+
const getTarget = (entry: unknown): string | undefined => {
|
|
226
|
+
if (typeof entry === "string") return entry;
|
|
227
|
+
if (!entry || typeof entry !== "object") return undefined;
|
|
228
|
+
const conditions = entry as Record<string, unknown>;
|
|
229
|
+
const target = conditions.import ?? conditions.default;
|
|
230
|
+
return typeof target === "string" ? target : undefined;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const exactTarget = getTarget(exportsMap[`./${subpath}`]);
|
|
234
|
+
if (exactTarget) return exactTarget;
|
|
235
|
+
|
|
236
|
+
for (const [pattern, entry] of Object.entries(exportsMap)) {
|
|
237
|
+
if (!pattern.endsWith("/*")) continue;
|
|
238
|
+
const prefix = pattern.slice(2, -1);
|
|
239
|
+
if (subpath.startsWith(prefix)) {
|
|
240
|
+
const target = getTarget(entry);
|
|
241
|
+
if (target) {
|
|
242
|
+
return target.replaceAll("*", subpath.slice(prefix.length));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
|
|
202
250
|
function resolvePiAiSubpathFromPackage(specifier: string): string | undefined {
|
|
203
251
|
const subpath = specifier.replace("@earendil-works/pi-ai/", "");
|
|
204
252
|
const candidates = [process.argv[1], import.meta.url].filter(
|
|
@@ -212,11 +260,8 @@ function resolvePiAiSubpathFromPackage(specifier: string): string | undefined {
|
|
|
212
260
|
const pkg = JSON.parse(
|
|
213
261
|
readFileSync(join(pkgDir, "package.json"), "utf-8"),
|
|
214
262
|
);
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
if (typeof targetPath === "string") {
|
|
218
|
-
return join(pkgDir, targetPath);
|
|
219
|
-
}
|
|
263
|
+
const targetPath = resolvePiAiExportTarget(pkg.exports, subpath);
|
|
264
|
+
if (targetPath) return join(pkgDir, targetPath);
|
|
220
265
|
} catch {
|
|
221
266
|
// Try the next resolution base.
|
|
222
267
|
}
|
|
@@ -367,8 +412,12 @@ export function createOpenCodeStreamSimple(
|
|
|
367
412
|
void (async () => {
|
|
368
413
|
try {
|
|
369
414
|
if (isAnthropicOpenCodeEndpoint(model)) {
|
|
370
|
-
const
|
|
371
|
-
await importPiAiSubpath<AnthropicStreamModule>(
|
|
415
|
+
const streamSimpleAnthropic = getStreamSimple(
|
|
416
|
+
await importPiAiSubpath<AnthropicStreamModule>(
|
|
417
|
+
"api/anthropic-messages",
|
|
418
|
+
),
|
|
419
|
+
"streamSimpleAnthropic",
|
|
420
|
+
);
|
|
372
421
|
await pipeStream(
|
|
373
422
|
stream,
|
|
374
423
|
streamSimpleAnthropic(
|
|
@@ -383,10 +432,12 @@ export function createOpenCodeStreamSimple(
|
|
|
383
432
|
return;
|
|
384
433
|
}
|
|
385
434
|
|
|
386
|
-
const
|
|
435
|
+
const streamSimpleOpenAICompletions = getStreamSimple(
|
|
387
436
|
await importPiAiSubpath<OpenAICompletionsStreamModule>(
|
|
388
|
-
"openai-completions",
|
|
389
|
-
)
|
|
437
|
+
"api/openai-completions",
|
|
438
|
+
),
|
|
439
|
+
"streamSimpleOpenAICompletions",
|
|
440
|
+
);
|
|
390
441
|
await pipeStream(
|
|
391
442
|
stream,
|
|
392
443
|
streamSimpleOpenAICompletions(
|
|
@@ -45,7 +45,11 @@ import { safeEnrichModelsWithModelsDev } from "../../lib/model-metadata.ts";
|
|
|
45
45
|
import { isLikelyReasoningModel } from "../../lib/provider-compat.ts";
|
|
46
46
|
import { isFreeModel, registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
47
47
|
import { fetchWithRetry } from "../../lib/util.ts";
|
|
48
|
-
import {
|
|
48
|
+
import {
|
|
49
|
+
createReRegister,
|
|
50
|
+
loadCachedOrFetchModels,
|
|
51
|
+
setupProvider,
|
|
52
|
+
} from "../../provider-helper.ts";
|
|
49
53
|
|
|
50
54
|
const _logger = createLogger("openmodel");
|
|
51
55
|
|
|
@@ -465,7 +469,9 @@ export default async function openmodelProvider(pi: ExtensionAPI) {
|
|
|
465
469
|
return;
|
|
466
470
|
}
|
|
467
471
|
|
|
468
|
-
const allModels = await
|
|
472
|
+
const allModels = await loadCachedOrFetchModels(PROVIDER_OPENMODEL, () =>
|
|
473
|
+
fetchOpenModelModels(apiKey),
|
|
474
|
+
);
|
|
469
475
|
|
|
470
476
|
if (allModels.length === 0) {
|
|
471
477
|
_logger.warn(
|
|
@@ -45,7 +45,11 @@ import { isFreeModel, registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
|
45
45
|
import { wrapSessionStartHandler } from "../../lib/session-start-metrics.ts";
|
|
46
46
|
import { cleanModelName, fetchWithRetry } from "../../lib/util.ts";
|
|
47
47
|
import { fetchWithTimeout } from "../../lib/util.ts";
|
|
48
|
-
import {
|
|
48
|
+
import {
|
|
49
|
+
createReRegister,
|
|
50
|
+
loadCachedOrFetchModels,
|
|
51
|
+
setupProvider,
|
|
52
|
+
} from "../../provider-helper.ts";
|
|
49
53
|
|
|
50
54
|
const _logger = createLogger("routeway");
|
|
51
55
|
|
|
@@ -303,7 +307,9 @@ export default async function routewayProvider(pi: ExtensionAPI) {
|
|
|
303
307
|
return;
|
|
304
308
|
}
|
|
305
309
|
|
|
306
|
-
const allModels = await
|
|
310
|
+
const allModels = await loadCachedOrFetchModels(PROVIDER_ROUTEWAY, () =>
|
|
311
|
+
fetchRoutewayModels(apiKey),
|
|
312
|
+
);
|
|
307
313
|
|
|
308
314
|
if (allModels.length === 0) {
|
|
309
315
|
_logger.warn("[routeway] No chat models available");
|
|
@@ -38,7 +38,11 @@ import {
|
|
|
38
38
|
fetchOpenAICompatibleModels,
|
|
39
39
|
fetchWithTimeout,
|
|
40
40
|
} from "../../lib/util.ts";
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
createReRegister,
|
|
43
|
+
loadCachedOrFetchModels,
|
|
44
|
+
setupProvider,
|
|
45
|
+
} from "../../provider-helper.ts";
|
|
42
46
|
|
|
43
47
|
const _logger = createLogger("sambanova");
|
|
44
48
|
|
|
@@ -57,11 +61,13 @@ export default async function sambanovaProvider(pi: ExtensionAPI) {
|
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
// Fetch models via shared OpenAI-compatible helper
|
|
60
|
-
const allModels = await
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
const allModels = await loadCachedOrFetchModels(PROVIDER_SAMBANOVA, () =>
|
|
65
|
+
fetchOpenAICompatibleModels(
|
|
66
|
+
"sambanova",
|
|
67
|
+
BASE_URL_SAMBANOVA,
|
|
68
|
+
apiKey,
|
|
69
|
+
{ maxTokens: 8_192 },
|
|
70
|
+
),
|
|
65
71
|
);
|
|
66
72
|
|
|
67
73
|
if (allModels.length === 0) {
|
|
@@ -50,7 +50,11 @@ import { createProviderProbe } from "../../lib/provider-probe.ts";
|
|
|
50
50
|
import { isFreeModel, registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
51
51
|
import { wrapSessionStartHandler } from "../../lib/session-start-metrics.ts";
|
|
52
52
|
import { fetchWithRetry, fetchWithTimeout } from "../../lib/util.ts";
|
|
53
|
-
import {
|
|
53
|
+
import {
|
|
54
|
+
createReRegister,
|
|
55
|
+
loadCachedOrFetchModels,
|
|
56
|
+
setupProvider,
|
|
57
|
+
} from "../../provider-helper.ts";
|
|
54
58
|
|
|
55
59
|
const _logger = createLogger("together");
|
|
56
60
|
|
|
@@ -150,7 +154,9 @@ export default async function togetherProvider(pi: ExtensionAPI) {
|
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
// Fetch models
|
|
153
|
-
const allModels = await
|
|
157
|
+
const allModels = await loadCachedOrFetchModels(PROVIDER_TOGETHER, () =>
|
|
158
|
+
fetchTogetherModels(apiKey),
|
|
159
|
+
);
|
|
154
160
|
|
|
155
161
|
if (allModels.length === 0) {
|
|
156
162
|
_logger.warn("[together] No chat models available");
|
|
@@ -26,10 +26,8 @@ import type {
|
|
|
26
26
|
SimpleStreamOptions,
|
|
27
27
|
ThinkingContent,
|
|
28
28
|
} from "@earendil-works/pi-ai";
|
|
29
|
-
import {
|
|
30
|
-
|
|
31
|
-
streamSimpleOpenAICompletions,
|
|
32
|
-
} from "@earendil-works/pi-ai";
|
|
29
|
+
import { createAssistantMessageEventStream } from "@earendil-works/pi-ai";
|
|
30
|
+
import { openAICompletionsApi } from "@earendil-works/pi-ai/compat";
|
|
33
31
|
import {
|
|
34
32
|
getTokenrouterApiKey,
|
|
35
33
|
getTokenrouterShowPaid,
|
|
@@ -49,9 +47,14 @@ import {
|
|
|
49
47
|
} from "../../lib/provider-compat.ts";
|
|
50
48
|
import { isFreeModel, registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
51
49
|
import { cleanModelName, fetchWithRetry } from "../../lib/util.ts";
|
|
52
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
enhanceWithCI,
|
|
52
|
+
loadCachedOrFetchModels,
|
|
53
|
+
setupProvider,
|
|
54
|
+
} from "../../provider-helper.ts";
|
|
53
55
|
|
|
54
56
|
const _logger = createLogger("tokenrouter");
|
|
57
|
+
const streamSimpleOpenAICompletions = openAICompletionsApi().streamSimple;
|
|
55
58
|
|
|
56
59
|
// =============================================================================
|
|
57
60
|
// Reasoning cleanup
|
|
@@ -331,7 +334,7 @@ function createTokenRouterOpenAIStream(
|
|
|
331
334
|
): AssistantMessageEventStream {
|
|
332
335
|
const forcePatch = isTokenRouterMinimaxModel(model.id);
|
|
333
336
|
return streamSimpleOpenAICompletions(
|
|
334
|
-
{ ...model, api: "openai-completions" }
|
|
337
|
+
{ ...model, api: "openai-completions" } as Model<"openai-completions">,
|
|
335
338
|
context,
|
|
336
339
|
{
|
|
337
340
|
...options,
|
|
@@ -564,7 +567,9 @@ export default async function tokenRouterProvider(pi: ExtensionAPI) {
|
|
|
564
567
|
return;
|
|
565
568
|
}
|
|
566
569
|
|
|
567
|
-
const allModels = await
|
|
570
|
+
const allModels = await loadCachedOrFetchModels(PROVIDER_TOKENROUTER, () =>
|
|
571
|
+
fetchTokenRouterModels(apiKey),
|
|
572
|
+
);
|
|
568
573
|
|
|
569
574
|
if (allModels.length === 0) {
|
|
570
575
|
_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
|
-
}
|