pi-free 2.0.7 → 2.0.9

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 (42) hide show
  1. package/CHANGELOG.md +96 -10
  2. package/README.md +572 -495
  3. package/config.ts +58 -11
  4. package/constants.ts +12 -0
  5. package/index.ts +67 -3
  6. package/lib/built-in-toggle.ts +2 -2
  7. package/lib/model-detection.ts +2 -1
  8. package/lib/model-enhancer.ts +1 -1
  9. package/lib/open-browser.ts +1 -1
  10. package/lib/provider-compat.ts +1 -1
  11. package/lib/quota-monitor.ts +123 -0
  12. package/lib/registry.ts +1 -1
  13. package/lib/types.ts +101 -101
  14. package/lib/util.ts +460 -351
  15. package/package.json +4 -4
  16. package/provider-failover/benchmark-lookup.ts +743 -702
  17. package/provider-failover/benchmarks-chunk-0.ts +48 -48
  18. package/provider-failover/benchmarks-chunk-1.ts +44 -44
  19. package/provider-failover/benchmarks-chunk-2.ts +39 -39
  20. package/provider-failover/benchmarks-chunk-3.ts +41 -41
  21. package/provider-failover/benchmarks-chunk-4.ts +33 -33
  22. package/provider-helper.ts +1 -1
  23. package/providers/cline/cline-auth.ts +473 -473
  24. package/providers/cline/cline-models.ts +2 -2
  25. package/providers/cline/cline.ts +3 -3
  26. package/providers/codestral/codestral.ts +139 -0
  27. package/providers/crofai/crofai.ts +14 -85
  28. package/providers/deepinfra/deepinfra.ts +109 -0
  29. package/providers/dynamic-built-in/index.ts +1 -1
  30. package/providers/kilo/kilo-auth.ts +155 -155
  31. package/providers/kilo/kilo.ts +3 -3
  32. package/providers/llm7/llm7.ts +156 -0
  33. package/providers/model-fetcher.ts +2 -2
  34. package/providers/nvidia/nvidia.ts +5 -5
  35. package/providers/ollama/ollama.ts +2 -2
  36. package/providers/opencode-session.ts +1 -1
  37. package/providers/qwen/qwen-auth.ts +1 -1
  38. package/providers/qwen/qwen-models.ts +1 -1
  39. package/providers/qwen/qwen.ts +3 -3
  40. package/providers/sambanova/sambanova.ts +109 -0
  41. package/providers/zenmux/zenmux.ts +6 -3
  42. package/scripts/check-extensions.mjs +6 -4
package/config.ts CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
13
13
  import { join } from "node:path";
14
- import {
14
+ export {
15
15
  PROVIDER_CLINE,
16
16
  PROVIDER_KILO,
17
17
  PROVIDER_MODAL,
@@ -27,7 +27,11 @@ interface PiFreeConfig {
27
27
  ollama_api_key?: string;
28
28
  zenmux_api_key?: string;
29
29
  crofai_api_key?: string;
30
+ codestral_api_key?: string;
30
31
  mistral_api_key?: string;
32
+ llm7_api_key?: string;
33
+ deepinfra_api_key?: string;
34
+ sambanova_api_key?: string;
31
35
  groq_api_key?: string;
32
36
  cerebras_api_key?: string;
33
37
  xai_api_key?: string;
@@ -40,6 +44,10 @@ interface PiFreeConfig {
40
44
  cline_show_paid?: boolean;
41
45
  zenmux_show_paid?: boolean;
42
46
  crofai_show_paid?: boolean;
47
+ codestral_show_paid?: boolean;
48
+ llm7_show_paid?: boolean;
49
+ deepinfra_show_paid?: boolean;
50
+ sambanova_show_paid?: boolean;
43
51
  openrouter_show_paid?: boolean;
44
52
  opencode_show_paid?: boolean;
45
53
  }
@@ -49,7 +57,11 @@ const CONFIG_TEMPLATE: PiFreeConfig = {
49
57
  ollama_api_key: "",
50
58
  zenmux_api_key: "",
51
59
  crofai_api_key: "",
60
+ codestral_api_key: "",
52
61
  mistral_api_key: "",
62
+ llm7_api_key: "",
63
+ deepinfra_api_key: "",
64
+ sambanova_api_key: "",
53
65
  groq_api_key: "",
54
66
  cerebras_api_key: "",
55
67
  xai_api_key: "",
@@ -63,6 +75,10 @@ const CONFIG_TEMPLATE: PiFreeConfig = {
63
75
  cline_show_paid: false,
64
76
  zenmux_show_paid: false,
65
77
  crofai_show_paid: false,
78
+ codestral_show_paid: false,
79
+ llm7_show_paid: false,
80
+ deepinfra_show_paid: false,
81
+ sambanova_show_paid: false,
66
82
  openrouter_show_paid: false,
67
83
  opencode_show_paid: false,
68
84
  };
@@ -147,6 +163,31 @@ export function getCrofaiShowPaid(): boolean {
147
163
  return resolveBool("CROFAI_SHOW_PAID", loadConfigFile().crofai_show_paid);
148
164
  }
149
165
 
166
+ export function getCodestralShowPaid(): boolean {
167
+ return resolveBool(
168
+ "CODESTRAL_SHOW_PAID",
169
+ loadConfigFile().codestral_show_paid,
170
+ );
171
+ }
172
+
173
+ export function getLlm7ShowPaid(): boolean {
174
+ return resolveBool("LLM7_SHOW_PAID", loadConfigFile().llm7_show_paid);
175
+ }
176
+
177
+ export function getDeepinfraShowPaid(): boolean {
178
+ return resolveBool(
179
+ "DEEPINFRA_SHOW_PAID",
180
+ loadConfigFile().deepinfra_show_paid,
181
+ );
182
+ }
183
+
184
+ export function getSambanovaShowPaid(): boolean {
185
+ return resolveBool(
186
+ "SAMBANOVA_SHOW_PAID",
187
+ loadConfigFile().sambanova_show_paid,
188
+ );
189
+ }
190
+
150
191
  export function getOllamaShowPaid(): boolean {
151
192
  return resolveBool("OLLAMA_SHOW_PAID", loadConfigFile().ollama_show_paid);
152
193
  }
@@ -190,6 +231,22 @@ export function getCrofaiApiKey(): string | undefined {
190
231
  return resolve("CROFAI_API_KEY", loadConfigFile().crofai_api_key);
191
232
  }
192
233
 
234
+ export function getCodestralApiKey(): string | undefined {
235
+ return resolve("CODESTRAL_API_KEY", loadConfigFile().codestral_api_key);
236
+ }
237
+
238
+ export function getLlm7ApiKey(): string | undefined {
239
+ return resolve("LLM7_API_KEY", loadConfigFile().llm7_api_key);
240
+ }
241
+
242
+ export function getDeepinfraApiKey(): string | undefined {
243
+ return resolve("DEEPINFRA_TOKEN", loadConfigFile().deepinfra_api_key);
244
+ }
245
+
246
+ export function getSambanovaApiKey(): string | undefined {
247
+ return resolve("SAMBANOVA_API_KEY", loadConfigFile().sambanova_api_key);
248
+ }
249
+
193
250
  export function getOllamaApiKey(): string | undefined {
194
251
  return resolve("OLLAMA_API_KEY", loadConfigFile().ollama_api_key);
195
252
  }
@@ -278,13 +335,3 @@ export function getConfig(): PiFreeConfig {
278
335
  }
279
336
 
280
337
  // =============================================================================
281
- // Re-export provider names for consistency
282
- // =============================================================================
283
-
284
- export {
285
- PROVIDER_CLINE,
286
- PROVIDER_KILO,
287
- PROVIDER_MODAL,
288
- PROVIDER_NVIDIA,
289
- PROVIDER_QWEN,
290
- };
package/constants.ts CHANGED
@@ -17,6 +17,10 @@ export const PROVIDER_QWEN = "qwen";
17
17
  export const PROVIDER_MODAL = "modal";
18
18
  export const PROVIDER_ZENMUX = "zenmux";
19
19
  export const PROVIDER_CROFAI = "crofai";
20
+ export const PROVIDER_CODESTRAL = "codestral";
21
+ export const PROVIDER_LLM7 = "llm7";
22
+ export const PROVIDER_DEEPINFRA = "deepinfra";
23
+ export const PROVIDER_SAMBANOVA = "sambanova";
20
24
 
21
25
  export const ALL_UNIQUE_PROVIDERS = [
22
26
  PROVIDER_KILO,
@@ -28,6 +32,10 @@ export const ALL_UNIQUE_PROVIDERS = [
28
32
  PROVIDER_OLLAMA,
29
33
  PROVIDER_ZENMUX,
30
34
  PROVIDER_CROFAI,
35
+ PROVIDER_CODESTRAL,
36
+ PROVIDER_LLM7,
37
+ PROVIDER_DEEPINFRA,
38
+ PROVIDER_SAMBANOVA,
31
39
  ] as const;
32
40
 
33
41
  // =============================================================================
@@ -44,6 +52,10 @@ export const BASE_URL_QWEN =
44
52
  "https://dashscope.aliyuncs.com/compatible-mode/v1";
45
53
  export const BASE_URL_ZENMUX = "https://zenmux.ai/api/v1";
46
54
  export const BASE_URL_CROFAI = "https://crof.ai/v1";
55
+ export const BASE_URL_CODESTRAL = "https://codestral.mistral.ai/v1";
56
+ export const BASE_URL_LLM7 = "https://api.llm7.io/v1";
57
+ export const BASE_URL_DEEPINFRA = "https://api.deepinfra.com/v1/openai";
58
+ export const BASE_URL_SAMBANOVA = "https://api.sambanova.ai/v1";
47
59
 
48
60
  /** Cline fetches free models from OpenRouter */
49
61
  export const BASE_URL_OPENROUTER = "https://openrouter.ai/api/v1";
package/index.ts CHANGED
@@ -10,11 +10,19 @@
10
10
  * - NVIDIA: NVIDIA NIM hosting (free tier available)
11
11
  * - Ollama Cloud: Ollama's cloud-hosted models with usage-based free tier
12
12
  * - ZenMux: Unified AI API gateway with 200+ models
13
+ * - Codestral: Mistral's code-focused model via codestral.mistral.ai (free tier)
14
+ * - DeepInfra: AI inference cloud ($5 trial credit)
15
+ * - SambaNova: Fast inference on RDU hardware (free tier, no credit card)
16
+ * - LLM7: AI gateway (free default/fast selectors)
13
17
  */
14
18
 
15
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
19
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
16
20
  import { setupBuiltInProviderToggles } from "./lib/built-in-toggle.ts";
17
21
  import { createLogger } from "./lib/logger.ts";
22
+ import {
23
+ processQuotaResponse,
24
+ formatQuotaStatus,
25
+ } from "./lib/quota-monitor.ts";
18
26
  import {
19
27
  applyGlobalFilter,
20
28
  getGlobalFreeOnly,
@@ -24,8 +32,12 @@ import {
24
32
  } from "./lib/registry.ts";
25
33
  // Import unique provider extensions (only providers NOT built into pi)
26
34
  import cline from "./providers/cline/cline.ts";
35
+ import codestral from "./providers/codestral/codestral.ts";
27
36
  import crofai from "./providers/crofai/crofai.ts";
28
37
  import kilo from "./providers/kilo/kilo.ts";
38
+ import llm7 from "./providers/llm7/llm7.ts";
39
+ import deepinfra from "./providers/deepinfra/deepinfra.ts";
40
+ import sambanova from "./providers/sambanova/sambanova.ts";
29
41
  import nvidia from "./providers/nvidia/nvidia.ts";
30
42
  import ollama from "./providers/ollama/ollama.ts";
31
43
  import zenmux from "./providers/zenmux/zenmux.ts";
@@ -87,7 +99,13 @@ function setupGlobalCommands(pi: ExtensionAPI) {
87
99
  "cerebras",
88
100
  ]);
89
101
  // Freemium providers - all models share a free tier quota
90
- const freemiumProviders = new Set(["nvidia"]);
102
+ const freemiumProviders = new Set([
103
+ "nvidia",
104
+ "sambanova",
105
+ "ollama-cloud",
106
+ ]);
107
+ // Trial credit providers - one-time credits, otherwise paid
108
+ const trialCreditProviders = new Set(["deepinfra"]);
91
109
 
92
110
  for (const [id, entry] of registry) {
93
111
  const free = entry.stored.free.length;
@@ -98,6 +116,9 @@ function setupGlobalCommands(pi: ExtensionAPI) {
98
116
  if (freemiumProviders.has(id)) {
99
117
  // Freemium: all models share a free tier (e.g., 1,000 reqs/month)
100
118
  lines.push(`${indicator} ${id}: ${all} models (freemium)`);
119
+ } else if (trialCreditProviders.has(id)) {
120
+ // Trial credit: one-time credits, otherwise paid
121
+ lines.push(`${indicator} ${id}: ${all} models ($5 trial credit)`);
101
122
  } else if (noPricingApi.has(id)) {
102
123
  // Provider doesn't expose pricing - can't determine free vs paid
103
124
  lines.push(
@@ -123,17 +144,56 @@ function setupGlobalCommands(pi: ExtensionAPI) {
123
144
  });
124
145
  }
125
146
 
147
+ // =============================================================================
148
+ // Quota Monitoring
149
+ // =============================================================================
150
+
151
+ function setupQuotaMonitoring(pi: ExtensionAPI) {
152
+ // Capture rate-limit headers from every provider response
153
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
+ (pi as any).on(
155
+ "after_provider_response",
156
+ (event: { status: number; headers: Record<string, string> }, ctx: any) => {
157
+ const providerId = ctx.model?.provider;
158
+ if (!providerId) return;
159
+
160
+ processQuotaResponse(providerId, event.headers);
161
+
162
+ // Update status bar with quota for the active provider
163
+ const status = formatQuotaStatus(providerId);
164
+ if (status) {
165
+ ctx.ui.setStatus("quota", status);
166
+ }
167
+ },
168
+ );
169
+
170
+ // Clear quota status when switching away from a provider
171
+ pi.on("model_select", (_event, ctx) => {
172
+ const providerId = ctx.model?.provider;
173
+ if (!providerId) {
174
+ ctx.ui.setStatus("quota", undefined);
175
+ return;
176
+ }
177
+ // Show cached quota on provider switch (if still fresh)
178
+ const status = formatQuotaStatus(providerId);
179
+ ctx.ui.setStatus("quota", status);
180
+ });
181
+ }
182
+
126
183
  // =============================================================================
127
184
  // Main Entry Point
128
185
  // =============================================================================
129
186
 
130
- export default async function (pi: ExtensionAPI) {
187
+ export default async function piFreeEntry(pi: ExtensionAPI) {
131
188
  const globalFreeOnly = getGlobalFreeOnly();
132
189
  _logger.info(`[pi-free] Initializing (global free-only: ${globalFreeOnly})`);
133
190
 
134
191
  // Setup global commands first
135
192
  setupGlobalCommands(pi);
136
193
 
194
+ // Setup quota monitoring (passive, no extra API calls)
195
+ setupQuotaMonitoring(pi);
196
+
137
197
  // Load all unique providers
138
198
  // Each provider will register itself with the global toggle system
139
199
  await Promise.allSettled([
@@ -143,6 +203,10 @@ export default async function (pi: ExtensionAPI) {
143
203
  cline(pi),
144
204
  zenmux(pi),
145
205
  crofai(pi),
206
+ codestral(pi),
207
+ llm7(pi),
208
+ deepinfra(pi),
209
+ sambanova(pi),
146
210
  ]);
147
211
 
148
212
  // Setup dynamic built-in providers (Mistral, Groq, Cerebras, xAI, Hugging Face)
@@ -11,11 +11,11 @@
11
11
  * Usage: /toggle-opencode
12
12
  */
13
13
 
14
- import type { Api, Model } from "@mariozechner/pi-ai";
14
+ import type { Api, Model } from "@earendil-works/pi-ai";
15
15
  import type {
16
16
  ExtensionAPI,
17
17
  ProviderModelConfig,
18
- } from "@mariozechner/pi-coding-agent";
18
+ } from "@earendil-works/pi-coding-agent";
19
19
  import { getOpencodeShowPaid, getOpenrouterShowPaid } from "../config.ts";
20
20
  import { createLogger } from "./logger.ts";
21
21
  import { isFreeModel, registerWithGlobalToggle } from "./registry.ts";
@@ -4,7 +4,7 @@
4
4
  * Used for failover when providers hit rate limits.
5
5
  */
6
6
 
7
- import type { Model } from "@mariozechner/pi-ai";
7
+ import type { Model } from "@earendil-works/pi-ai";
8
8
  import type { ProviderModelConfig } from "./types.ts";
9
9
 
10
10
  export interface ModelInfo {
@@ -206,6 +206,7 @@ export function normalizeModelName(name: string): string {
206
206
  }
207
207
 
208
208
  // CI score suffix — regex with disjoint char classes (linear)
209
+ // Anchored with $, matches at most once → .replace() is correct (S4144 N/A)
209
210
  normalized = normalized.replace(/\(ci:\s*[\d.]+\)$/, "").trimEnd();
210
211
  normalized = normalized.replace(/\[ci:\s*[\d.]+\]$/, "").trimEnd();
211
212
 
@@ -3,7 +3,7 @@
3
3
  * Adds Coding Index scores to model names for display in /model
4
4
  */
5
5
 
6
- import type { ProviderModelConfig } from "@mariozechner/pi-coding-agent";
6
+ import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
7
7
  import { enhanceModelNameWithCodingIndex } from "../provider-failover/benchmark-lookup.ts";
8
8
 
9
9
  /**
@@ -50,7 +50,7 @@ export function openBrowser(url: string): void {
50
50
  "-NoProfile",
51
51
  "-NonInteractive",
52
52
  "-Command",
53
- `Start-Process "${url.replace(/[\\"]/g, "\\$&")}"`,
53
+ `Start-Process "${url.replaceAll(/[\\"]/g, "\\$&")}"`,
54
54
  ],
55
55
  { detached: true, shell: false, windowsHide: true },
56
56
  ).unref();
@@ -1,4 +1,4 @@
1
- import type { ProviderModelConfig } from "@mariozechner/pi-coding-agent";
1
+ import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
2
2
 
3
3
  export interface ProviderModelIdentity {
4
4
  id: string;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Quota Monitoring for pi-free providers.
3
+ *
4
+ * Subscribes to pi's `after_provider_response` event to extract rate-limit
5
+ * headers from provider responses and track remaining quota per provider.
6
+ *
7
+ * Inspired by free-coding-models' extractQuotaPercent and provider-quota-fetchers.
8
+ *
9
+ * Supported header formats (tried in order):
10
+ * 1. x-ratelimit-remaining-requests / x-ratelimit-limit-requests (SambaNova)
11
+ * 2. x-ratelimit-remaining / x-ratelimit-limit (Mistral, others)
12
+ * 3. ratelimit-remaining-requests / ratelimit-limit-requests
13
+ * 4. ratelimit-remaining / ratelimit-limit
14
+ * 5. x-ratelimit-remaining-requests-day / x-ratelimit-limit-requests-day (SambaNova daily)
15
+ */
16
+
17
+ const _quotaState = new Map<string, QuotaSnapshot>();
18
+
19
+ /** Snapshot of quota state for a single provider. */
20
+ export interface QuotaSnapshot {
21
+ /** Requests remaining in the current window. */
22
+ remaining: number;
23
+ /** Total requests allowed in the current window. */
24
+ limit: number;
25
+ /** Remaining as percentage 0–100. */
26
+ percent: number;
27
+ /** Timestamp (Date.now()) when this snapshot was captured. */
28
+ lastUpdated: number;
29
+ /** Which header variant was matched (for debugging). */
30
+ source: string;
31
+ }
32
+
33
+ // Header key pairs to try, in priority order.
34
+ // Each pair is [remaining, limit].
35
+ const HEADER_PAIRS: [string, string][] = [
36
+ // Per-minute (most common)
37
+ ["x-ratelimit-remaining-requests", "x-ratelimit-limit-requests"],
38
+ ["x-ratelimit-remaining", "x-ratelimit-limit"],
39
+ ["ratelimit-remaining-requests", "ratelimit-limit-requests"],
40
+ ["ratelimit-remaining", "ratelimit-limit"],
41
+ // Per-day
42
+ ["x-ratelimit-remaining-requests-day", "x-ratelimit-limit-requests-day"],
43
+ ["x-ratelimit-remaining-day", "x-ratelimit-limit-day"],
44
+ ];
45
+
46
+ /**
47
+ * Attempt to extract quota from response headers.
48
+ * Returns { remaining, limit, source } or null if no quota headers found.
49
+ */
50
+ export function extractQuota(
51
+ headers: Record<string, string>,
52
+ ): { remaining: number; limit: number; source: string } | null {
53
+ // Normalize keys to lowercase for case-insensitive matching.
54
+ // Some proxies/servers vary header casing.
55
+ const normalized: Record<string, string> = {};
56
+ for (const [key, value] of Object.entries(headers)) {
57
+ normalized[key.toLowerCase()] = value;
58
+ }
59
+
60
+ for (const [remainingKey, limitKey] of HEADER_PAIRS) {
61
+ const remaining = Number.parseFloat(normalized[remainingKey]);
62
+ const limit = Number.parseFloat(normalized[limitKey]);
63
+ if (Number.isFinite(remaining) && Number.isFinite(limit) && limit > 0) {
64
+ return { remaining, limit, source: remainingKey };
65
+ }
66
+ }
67
+
68
+ return null;
69
+ }
70
+
71
+ /**
72
+ * Process an after_provider_response event, updating quota state.
73
+ * Call from the event handler in index.ts.
74
+ */
75
+ export function processQuotaResponse(
76
+ providerId: string,
77
+ headers: Record<string, string>,
78
+ ): void {
79
+ const extracted = extractQuota(headers);
80
+ if (!extracted) return;
81
+
82
+ const percent = Math.round((extracted.remaining / extracted.limit) * 100);
83
+
84
+ _quotaState.set(providerId, {
85
+ remaining: extracted.remaining,
86
+ limit: extracted.limit,
87
+ percent: Math.max(0, Math.min(100, percent)),
88
+ lastUpdated: Date.now(),
89
+ source: extracted.source,
90
+ });
91
+ }
92
+
93
+ /**
94
+ * Get the latest quota snapshot for a provider, or null if unknown.
95
+ */
96
+ export function getQuota(providerId: string): QuotaSnapshot | null {
97
+ return _quotaState.get(providerId) ?? null;
98
+ }
99
+
100
+ /**
101
+ * Get all tracked quotas.
102
+ */
103
+ export function getAllQuotas(): ReadonlyMap<string, QuotaSnapshot> {
104
+ return _quotaState;
105
+ }
106
+
107
+ /**
108
+ * Build a human-readable status bar line for a provider's quota.
109
+ * Returns undefined if no quota data is available.
110
+ */
111
+ export function formatQuotaStatus(providerId: string): string | undefined {
112
+ const q = _quotaState.get(providerId);
113
+ if (!q) return undefined;
114
+
115
+ // Stale after 5 minutes
116
+ if (Date.now() - q.lastUpdated > 5 * 60 * 1000) return undefined;
117
+
118
+ if (q.percent <= 10)
119
+ return `⚠️ ${providerId}: ${q.remaining}/${q.limit} (${q.percent}%)`;
120
+ if (q.percent <= 25)
121
+ return `⚡ ${providerId}: ${q.remaining}/${q.limit} (${q.percent}%)`;
122
+ return `${providerId}: ${q.remaining}/${q.limit} (${q.percent}%)`;
123
+ }
package/lib/registry.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  import type {
9
9
  ExtensionAPI,
10
10
  ProviderModelConfig,
11
- } from "@mariozechner/pi-coding-agent";
11
+ } from "@earendil-works/pi-coding-agent";
12
12
  import { getFreeOnly, saveConfig } from "../config.ts";
13
13
  import { createLogger } from "./logger.ts";
14
14
 
package/lib/types.ts CHANGED
@@ -1,101 +1,101 @@
1
- /**
2
- * Shared types for pi-free-providers.
3
- * Interfaces duplicated across providers consolidated here.
4
- */
5
-
6
- // =============================================================================
7
- // Provider model configuration (matches Pi's ProviderModelConfig)
8
- // =============================================================================
9
-
10
- export interface CostConfig {
11
- input: number;
12
- output: number;
13
- cacheRead: number;
14
- cacheWrite: number;
15
- }
16
-
17
- export interface ProviderModelConfig {
18
- id: string;
19
- name: string;
20
- reasoning: boolean;
21
- input: ("text" | "image")[];
22
- cost: CostConfig;
23
- contextWindow: number;
24
- maxTokens: number;
25
- }
26
-
27
- // =============================================================================
28
- // models.dev schema types
29
- // =============================================================================
30
-
31
- export interface ModelsDevCost {
32
- input: number;
33
- output: number;
34
- cache_read?: number;
35
- cache_write?: number;
36
- }
37
-
38
- export interface ModelsDevLimit {
39
- context: number;
40
- output: number;
41
- }
42
-
43
- export interface ModelsDevModalities {
44
- input?: string[];
45
- output?: string[];
46
- }
47
-
48
- export interface ModelsDevModel {
49
- id: string;
50
- name: string;
51
- reasoning: boolean;
52
- cost?: ModelsDevCost;
53
- limit: ModelsDevLimit;
54
- modalities?: ModelsDevModalities;
55
- }
56
-
57
- export interface ModelsDevProvider {
58
- id: string;
59
- api: string;
60
- models: Record<string, ModelsDevModel>;
61
- }
62
-
63
- // =============================================================================
64
- // OpenRouter API response types
65
- // =============================================================================
66
-
67
- export interface OpenRouterPricing {
68
- prompt?: string | null;
69
- completion?: string | null;
70
- input_cache_write?: string | null;
71
- input_cache_read?: string | null;
72
- }
73
-
74
- export interface OpenRouterArchitecture {
75
- input_modalities?: string[] | null;
76
- output_modalities?: string[] | null;
77
- }
78
-
79
- export interface OpenRouterTopProvider {
80
- max_completion_tokens?: number | null;
81
- }
82
-
83
- export interface OpenRouterModel {
84
- id: string;
85
- name: string;
86
- context_length: number;
87
- max_completion_tokens?: number | null;
88
- pricing?: OpenRouterPricing;
89
- architecture?: OpenRouterArchitecture;
90
- top_provider?: OpenRouterTopProvider;
91
- supported_parameters?: string[];
92
- }
93
-
94
- // =============================================================================
95
- // Zen gateway types
96
- // =============================================================================
97
-
98
- export interface ZenGatewayModel {
99
- id: string;
100
- object?: string;
101
- }
1
+ /**
2
+ * Shared types for pi-free-providers.
3
+ * Interfaces duplicated across providers consolidated here.
4
+ */
5
+
6
+ // =============================================================================
7
+ // Provider model configuration (matches Pi's ProviderModelConfig)
8
+ // =============================================================================
9
+
10
+ export interface CostConfig {
11
+ input: number;
12
+ output: number;
13
+ cacheRead: number;
14
+ cacheWrite: number;
15
+ }
16
+
17
+ export interface ProviderModelConfig {
18
+ id: string;
19
+ name: string;
20
+ reasoning: boolean;
21
+ input: ("text" | "image")[];
22
+ cost: CostConfig;
23
+ contextWindow: number;
24
+ maxTokens: number;
25
+ }
26
+
27
+ // =============================================================================
28
+ // models.dev schema types
29
+ // =============================================================================
30
+
31
+ export interface ModelsDevCost {
32
+ input: number;
33
+ output: number;
34
+ cache_read?: number;
35
+ cache_write?: number;
36
+ }
37
+
38
+ export interface ModelsDevLimit {
39
+ context: number;
40
+ output: number;
41
+ }
42
+
43
+ export interface ModelsDevModalities {
44
+ input?: string[];
45
+ output?: string[];
46
+ }
47
+
48
+ export interface ModelsDevModel {
49
+ id: string;
50
+ name: string;
51
+ reasoning: boolean;
52
+ cost?: ModelsDevCost;
53
+ limit: ModelsDevLimit;
54
+ modalities?: ModelsDevModalities;
55
+ }
56
+
57
+ export interface ModelsDevProvider {
58
+ id: string;
59
+ api: string;
60
+ models: Record<string, ModelsDevModel>;
61
+ }
62
+
63
+ // =============================================================================
64
+ // OpenRouter API response types
65
+ // =============================================================================
66
+
67
+ export interface OpenRouterPricing {
68
+ prompt?: string | null;
69
+ completion?: string | null;
70
+ input_cache_write?: string | null;
71
+ input_cache_read?: string | null;
72
+ }
73
+
74
+ export interface OpenRouterArchitecture {
75
+ input_modalities?: string[] | null;
76
+ output_modalities?: string[] | null;
77
+ }
78
+
79
+ export interface OpenRouterTopProvider {
80
+ max_completion_tokens?: number | null;
81
+ }
82
+
83
+ export interface OpenRouterModel {
84
+ id: string;
85
+ name: string;
86
+ context_length: number;
87
+ max_completion_tokens?: number | null;
88
+ pricing?: OpenRouterPricing;
89
+ architecture?: OpenRouterArchitecture;
90
+ top_provider?: OpenRouterTopProvider;
91
+ supported_parameters?: string[];
92
+ }
93
+
94
+ // =============================================================================
95
+ // Zen gateway types
96
+ // =============================================================================
97
+
98
+ export interface ZenGatewayModel {
99
+ id: string;
100
+ object?: string;
101
+ }