pi-free 2.0.2 → 2.0.4

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.
@@ -1,207 +0,0 @@
1
- /**
2
- * Provider Factory
3
- *
4
- * Extracts the common boilerplate pattern repeated across providers:
5
- * - API key check
6
- * - SHOW_PAID flag check
7
- * - Model fetching with error handling
8
- * - Provider registration
9
- * - setupProvider wiring
10
- *
11
- * Usage:
12
- * export default createProvider(pi, {
13
- * providerId: PROVIDER_NVIDIA,
14
- * baseUrl: BASE_URL_NVIDIA,
15
- * apiKeyEnvVar: "NVIDIA_API_KEY",
16
- * fetchModels: fetchNvidiaModels,
17
- * showPaidFlag: "NVIDIA_SHOW_PAID",
18
- * });
19
- */
20
-
21
- import type {
22
- ExtensionAPI,
23
- ProviderModelConfig,
24
- } from "@mariozechner/pi-coding-agent";
25
- import {
26
- getModalApiKey,
27
- getNvidiaApiKey,
28
- getNvidiaShowPaid,
29
- getOpencodeApiKey,
30
- } from "./config.ts";
31
- import { createLogger } from "./lib/logger.ts";
32
- import { logWarning } from "./lib/util.ts";
33
- import {
34
- createReRegister,
35
- enhanceWithCI,
36
- type StoredModels,
37
- setupProvider,
38
- } from "./provider-helper.ts";
39
-
40
- const _logger = createLogger("provider-factory");
41
-
42
- // =============================================================================
43
- // Types
44
- // =============================================================================
45
-
46
- export interface ProviderDefinition {
47
- /** Provider identifier (e.g., "nvidia", "modal") */
48
- providerId: string;
49
- /** Base URL for the API */
50
- baseUrl: string;
51
- /** Environment variable name for the API key */
52
- apiKeyEnvVar: string;
53
- /** Config key for the API key (e.g., "nvidia_api_key") */
54
- apiKeyConfigKey: string;
55
- /** Function to fetch models */
56
- fetchModels: () => Promise<ProviderModelConfig[]>;
57
- /** SHOW_PAID flag name (e.g., "NVIDIA_SHOW_PAID") - if set, provider requires this to be true */
58
- showPaidFlag?: string;
59
- /** ToS URL to show on first use */
60
- tosUrl?: string;
61
- /** Whether this provider has a free tier (free + paid models). Default: false */
62
- hasFreeTier?: boolean;
63
- /** Whether to skip creating a toggle command (e.g., for single-model providers). Default: false */
64
- skipToggle?: boolean;
65
- /** Additional headers to include in requests */
66
- extraHeaders?: Record<string, string>;
67
- /** Optional hook to modify request payload before sending */
68
- beforeProviderRequest?: (
69
- payload: Record<string, unknown>,
70
- ) => Record<string, unknown> | undefined;
71
- /** Optional flag to indicate paid mode (for error handling) */
72
- isPaidMode?: boolean;
73
- }
74
-
75
- // =============================================================================
76
- // Config value getters (dynamic lookup)
77
- // =============================================================================
78
-
79
- const API_KEY_GETTERS: Record<string, () => string | undefined> = {
80
- nvidia_api_key: getNvidiaApiKey,
81
- opencode_api_key: getOpencodeApiKey,
82
- modal_api_key: getModalApiKey,
83
- };
84
-
85
- const SHOW_PAID_GETTERS: Record<string, () => boolean> = {
86
- NVIDIA_SHOW_PAID: getNvidiaShowPaid,
87
- };
88
-
89
- // =============================================================================
90
- // Factory
91
- // =============================================================================
92
-
93
- /**
94
- * Create a provider with minimal boilerplate.
95
- *
96
- * Handles:
97
- * - API key check
98
- * - SHOW_PAID flag check (if applicable)
99
- * - Model fetching with error handling
100
- * - Provider registration with OpenAI-compatible API
101
- * - setupProvider wiring for commands and events
102
- *
103
- * For providers with OAuth or custom session logic, don't use this factory.
104
- */
105
- export async function createProvider(
106
- pi: ExtensionAPI,
107
- def: ProviderDefinition,
108
- ): Promise<void> {
109
- // 1. Get API key from config
110
- const getApiKey = API_KEY_GETTERS[def.apiKeyConfigKey];
111
- if (!getApiKey) {
112
- _logger.error(`Unknown API key config: ${def.apiKeyConfigKey}`);
113
- return;
114
- }
115
- const apiKey = getApiKey();
116
-
117
- // 2. Check key exists
118
- if (!apiKey) {
119
- _logger.warn(
120
- `No API key found — set ${def.apiKeyEnvVar} or add ${def.apiKeyConfigKey} to ~/.pi/free.json`,
121
- );
122
- return;
123
- }
124
-
125
- // 3. Check paid flag (if applicable)
126
- if (def.showPaidFlag) {
127
- const getShowPaid = SHOW_PAID_GETTERS[def.showPaidFlag];
128
- if (getShowPaid && !getShowPaid()) {
129
- _logger.info(
130
- `${def.providerId} disabled. Set ${def.showPaidFlag}=true to enable.`,
131
- );
132
- return;
133
- }
134
- }
135
-
136
- // 4. Fetch models
137
- let models: ProviderModelConfig[] = [];
138
- try {
139
- models = await def.fetchModels();
140
- } catch (error) {
141
- logWarning(def.providerId, "Failed to fetch models", error);
142
- return;
143
- }
144
-
145
- if (models.length === 0) {
146
- _logger.warn(`No models available for ${def.providerId}`);
147
- return;
148
- }
149
-
150
- // 5. Build storage (free/all or single set)
151
- const stored: StoredModels = def.hasFreeTier
152
- ? {
153
- free: models.filter((m) => (m.cost?.input ?? 0) === 0),
154
- all: models,
155
- }
156
- : { free: models, all: models };
157
-
158
- // 6. Register provider (pass literal key so we don't mutate process.env)
159
- pi.registerProvider(def.providerId, {
160
- baseUrl: def.baseUrl,
161
- apiKey,
162
- api: "openai-completions" as const,
163
- headers: {
164
- "User-Agent": "pi-free-providers",
165
- ...def.extraHeaders,
166
- },
167
- models: enhanceWithCI(models),
168
- });
169
-
170
- // 7. Setup boilerplate
171
- const config = {
172
- providerId: def.providerId,
173
- baseUrl: def.baseUrl,
174
- apiKey,
175
- };
176
-
177
- const reRegister = createReRegister(pi, config);
178
-
179
- setupProvider(
180
- pi,
181
- {
182
- providerId: def.providerId,
183
- tosUrl: def.tosUrl,
184
- initialShowPaid: def.isPaidMode ?? !!def.showPaidFlag,
185
- reRegister: (m: ProviderModelConfig[]) => {
186
- stored.free = m;
187
- stored.all = m;
188
- reRegister(m);
189
- },
190
- skipToggle: def.skipToggle,
191
- },
192
- stored,
193
- );
194
-
195
- // 8. Optional: before_provider_request hook
196
- if (def.beforeProviderRequest) {
197
- const hook = def.beforeProviderRequest;
198
- (pi.on as (event: string, handler: (e: unknown) => unknown) => void)(
199
- "before_provider_request",
200
- (event: unknown) => {
201
- const evt = event as { type: string; payload: unknown };
202
- const payload = evt.payload as Record<string, unknown>;
203
- return hook(payload);
204
- },
205
- );
206
- }
207
- }