opencode-pollinations-plugin 5.8.4-beta.15 → 5.8.4-beta.17
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/README.md +4 -2
- package/dist/server/commands.js +9 -19
- package/dist/server/generate-config.d.ts +29 -1
- package/dist/server/generate-config.js +91 -201
- package/dist/server/proxy.js +16 -24
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,9 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
<div align="center">
|
|
12
12
|
|
|
13
|
-

|
|
14
14
|

|
|
15
|
-

|
|
16
|
+
|
|
17
|
+
[📜 View Changelog](./CHANGELOG.md) | [🛣️ Roadmap](./ROADMAP.md)
|
|
16
18
|
|
|
17
19
|
</div>
|
|
18
20
|
|
package/dist/server/commands.js
CHANGED
|
@@ -303,15 +303,15 @@ async function handleConnectCommand(args) {
|
|
|
303
303
|
// 1. Universal Validation (No Syntax Check) - Functional Check
|
|
304
304
|
emitStatusToast('info', 'Vérification de la clé...', 'Pollinations Config');
|
|
305
305
|
try {
|
|
306
|
-
const models = await generatePollinationsConfig(key
|
|
307
|
-
// 2. Check if we got
|
|
308
|
-
const
|
|
309
|
-
if (
|
|
306
|
+
const models = await generatePollinationsConfig(key);
|
|
307
|
+
// 2. Check if we got real models (not just connect placeholder)
|
|
308
|
+
const realModels = models.filter(m => m.id !== 'connect');
|
|
309
|
+
if (realModels.length > 0) {
|
|
310
310
|
// SUCCESS
|
|
311
311
|
saveConfig({ apiKey: key }); // Don't force mode 'pro'. Let user decide.
|
|
312
312
|
const masked = key.substring(0, 6) + '...';
|
|
313
313
|
// Count Paid Only models found
|
|
314
|
-
const diamondCount =
|
|
314
|
+
const diamondCount = realModels.filter(m => m.name.includes('💎')).length;
|
|
315
315
|
// CHECK RESTRICTIONS: Strict Check (Usage + Profile + Balance)
|
|
316
316
|
let forcedModeMsg = "";
|
|
317
317
|
let isLimited = false;
|
|
@@ -336,10 +336,10 @@ async function handleConnectCommand(args) {
|
|
|
336
336
|
else {
|
|
337
337
|
saveConfig({ apiKey: key, keyHasAccessToProfile: true }); // Let user keep current mode or default
|
|
338
338
|
}
|
|
339
|
-
emitStatusToast('success', `Clé Valide! (${
|
|
339
|
+
emitStatusToast('success', `Clé Valide! (${realModels.length} modèles débloqués)`, 'Pollinations Config');
|
|
340
340
|
return {
|
|
341
341
|
handled: true,
|
|
342
|
-
response: `✅ **Connexion Réussie!**\n- Clé: \`${masked}\`\n- Modèles Débloqués: ${
|
|
342
|
+
response: `✅ **Connexion Réussie!**\n- Clé: \`${masked}\`\n- Modèles Débloqués: ${realModels.length} (dont ${diamondCount} 💎 Paid)${forcedModeMsg}`
|
|
343
343
|
};
|
|
344
344
|
}
|
|
345
345
|
else {
|
|
@@ -351,18 +351,8 @@ async function handleConnectCommand(args) {
|
|
|
351
351
|
// Wait, generate-config falls back to providing a list containing "[Enter] GPT-4o (Fallback)" if fetch failed.
|
|
352
352
|
// So we need to detect if it's a "REAL" fetch or a "FALLBACK" fetch.
|
|
353
353
|
// The fallback models have `variants: {}` usually, but real ones might too.
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
// Or just check if the returned models work?
|
|
357
|
-
// Simplest: If `generatePollinationsConfig` returns any model starting with `enter/` that includes "(Fallback)" in name, we assume failure?
|
|
358
|
-
// "GPT-4o (Fallback)" is the name.
|
|
359
|
-
const isFallback = models.some(m => m.name.includes('(Fallback)') && m.id.startsWith('enter/'));
|
|
360
|
-
if (isFallback) {
|
|
361
|
-
throw new Error("Clé rejetée par l'API (Accès refusé ou invalide).");
|
|
362
|
-
}
|
|
363
|
-
// If we are here, we got no enter models, or empty list?
|
|
364
|
-
// If key is valid but has no access?
|
|
365
|
-
throw new Error("Aucun modèle Enterprise détecté pour cette clé.");
|
|
354
|
+
// v6.0: No fallback prefix check needed. If models is empty (only connect), key is invalid.
|
|
355
|
+
throw new Error("Aucun modèle détecté pour cette clé. Clé invalide ou expirée.");
|
|
366
356
|
}
|
|
367
357
|
}
|
|
368
358
|
catch (e) {
|
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* generate-config.ts - v6.0 Simplified
|
|
3
|
+
*
|
|
4
|
+
* Single endpoint: gen.pollinations.ai/text/models
|
|
5
|
+
* No more Free tier, no cache ETag, no prefixes
|
|
6
|
+
*/
|
|
7
|
+
export interface PollinationsModel {
|
|
8
|
+
name: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
type?: string;
|
|
11
|
+
tools?: boolean;
|
|
12
|
+
reasoning?: boolean;
|
|
13
|
+
context?: number;
|
|
14
|
+
context_window?: number;
|
|
15
|
+
input_modalities?: string[];
|
|
16
|
+
output_modalities?: string[];
|
|
17
|
+
paid_only?: boolean;
|
|
18
|
+
vision?: boolean;
|
|
19
|
+
audio?: boolean;
|
|
20
|
+
pricing?: {
|
|
21
|
+
promptTextTokens?: number;
|
|
22
|
+
completionTextTokens?: number;
|
|
23
|
+
promptImageTokens?: number;
|
|
24
|
+
promptAudioTokens?: number;
|
|
25
|
+
completionAudioTokens?: number;
|
|
26
|
+
};
|
|
27
|
+
[key: string]: any;
|
|
28
|
+
}
|
|
1
29
|
interface OpenCodeModel {
|
|
2
30
|
id: string;
|
|
3
31
|
name: string;
|
|
@@ -12,5 +40,5 @@ interface OpenCodeModel {
|
|
|
12
40
|
};
|
|
13
41
|
tool_call?: boolean;
|
|
14
42
|
}
|
|
15
|
-
export declare function generatePollinationsConfig(forceApiKey?: string
|
|
43
|
+
export declare function generatePollinationsConfig(forceApiKey?: string): Promise<OpenCodeModel[]>;
|
|
16
44
|
export {};
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* generate-config.ts - v6.0 Simplified
|
|
3
|
+
*
|
|
4
|
+
* Single endpoint: gen.pollinations.ai/text/models
|
|
5
|
+
* No more Free tier, no cache ETag, no prefixes
|
|
6
|
+
*/
|
|
1
7
|
import * as https from 'https';
|
|
2
8
|
import * as fs from 'fs';
|
|
3
|
-
import * as os from 'os';
|
|
4
|
-
import * as path from 'path';
|
|
5
9
|
import { loadConfig } from './config.js';
|
|
6
|
-
const HOMEDIR = os.homedir();
|
|
7
|
-
const CONFIG_DIR_POLLI = path.join(HOMEDIR, '.pollinations');
|
|
8
|
-
const CACHE_FILE = path.join(CONFIG_DIR_POLLI, 'models-cache.json');
|
|
9
|
-
// --- CONSTANTS ---
|
|
10
|
-
// Seed from models-seed.ts
|
|
11
|
-
import { FREE_MODELS_SEED } from './models-seed.js';
|
|
12
10
|
// --- LOGGING ---
|
|
13
11
|
const LOG_FILE = '/tmp/opencode_pollinations_config.log';
|
|
14
12
|
function log(msg) {
|
|
@@ -16,44 +14,28 @@ function log(msg) {
|
|
|
16
14
|
const ts = new Date().toISOString();
|
|
17
15
|
if (!fs.existsSync(LOG_FILE))
|
|
18
16
|
fs.writeFileSync(LOG_FILE, '');
|
|
19
|
-
fs.appendFileSync(LOG_FILE, `[ConfigGen] ${ts} ${msg}\n`);
|
|
17
|
+
fs.appendFileSync(LOG_FILE, `[ConfigGen v6.0] ${ts} ${msg}\n`);
|
|
20
18
|
}
|
|
21
19
|
catch (e) { }
|
|
22
20
|
}
|
|
23
21
|
// --- NETWORK HELPER ---
|
|
24
|
-
function fetchHead(url) {
|
|
25
|
-
return new Promise((resolve) => {
|
|
26
|
-
// Use Node.js native https check for minimal overhead
|
|
27
|
-
const req = https.request(url, { method: 'HEAD', timeout: 5000 }, (res) => {
|
|
28
|
-
resolve(res.headers['etag'] || null);
|
|
29
|
-
});
|
|
30
|
-
req.on('error', () => resolve(null));
|
|
31
|
-
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
32
|
-
req.end();
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
22
|
function fetchJson(url, headers = {}) {
|
|
36
23
|
return new Promise((resolve, reject) => {
|
|
37
24
|
const finalHeaders = {
|
|
38
25
|
...headers,
|
|
39
|
-
'User-Agent': 'Mozilla/5.0 (compatible; OpenCode/
|
|
26
|
+
'User-Agent': 'Mozilla/5.0 (compatible; OpenCode-Pollinations/6.0; +https://opencode.ai)'
|
|
40
27
|
};
|
|
41
28
|
const req = https.get(url, { headers: finalHeaders }, (res) => {
|
|
42
|
-
const etag = res.headers['etag'];
|
|
43
29
|
let data = '';
|
|
44
30
|
res.on('data', chunk => data += chunk);
|
|
45
31
|
res.on('end', () => {
|
|
46
32
|
try {
|
|
47
33
|
const json = JSON.parse(data);
|
|
48
|
-
// HACK: Attach ETag to the object to pass it up
|
|
49
|
-
if (etag && typeof json === 'object') {
|
|
50
|
-
Object.defineProperty(json, '_etag', { value: etag, enumerable: false, writable: true });
|
|
51
|
-
}
|
|
52
34
|
resolve(json);
|
|
53
35
|
}
|
|
54
36
|
catch (e) {
|
|
55
37
|
log(`JSON Parse Error for ${url}: ${e}`);
|
|
56
|
-
resolve([]);
|
|
38
|
+
resolve([]);
|
|
57
39
|
}
|
|
58
40
|
});
|
|
59
41
|
});
|
|
@@ -67,218 +49,126 @@ function fetchJson(url, headers = {}) {
|
|
|
67
49
|
});
|
|
68
50
|
});
|
|
69
51
|
}
|
|
70
|
-
function loadCache() {
|
|
71
|
-
try {
|
|
72
|
-
if (fs.existsSync(CACHE_FILE)) {
|
|
73
|
-
const content = fs.readFileSync(CACHE_FILE, 'utf-8');
|
|
74
|
-
return JSON.parse(content);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
catch (e) {
|
|
78
|
-
log(`Error loading cache: ${e}`);
|
|
79
|
-
}
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
function saveCache(models, etag) {
|
|
83
|
-
try {
|
|
84
|
-
const data = {
|
|
85
|
-
timestamp: Date.now(),
|
|
86
|
-
etag: etag,
|
|
87
|
-
models: models
|
|
88
|
-
};
|
|
89
|
-
if (!fs.existsSync(CONFIG_DIR_POLLI))
|
|
90
|
-
fs.mkdirSync(CONFIG_DIR_POLLI, { recursive: true });
|
|
91
|
-
fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
|
|
92
|
-
}
|
|
93
|
-
catch (e) {
|
|
94
|
-
log(`Error saving cache: ${e}`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
52
|
// --- GENERATOR LOGIC ---
|
|
98
|
-
export async function generatePollinationsConfig(forceApiKey
|
|
53
|
+
export async function generatePollinationsConfig(forceApiKey) {
|
|
99
54
|
const config = loadConfig();
|
|
100
55
|
const modelsOutput = [];
|
|
101
|
-
log(`Starting Configuration (v5.8.4-Debug-Tools)...`);
|
|
102
56
|
const effectiveKey = forceApiKey || config.apiKey;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (remoteEtag && remoteEtag !== cache.etag) {
|
|
117
|
-
log(`Update Detected! (Remote: ${remoteEtag} != Local: ${cache.etag}). Forcing refresh.`);
|
|
118
|
-
shouldFetch = true;
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
log('Cache is clean (ETag match). No refresh needed.');
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
catch (e) {
|
|
125
|
-
log(`Smart Refresh check failed: ${e}. Ignoring.`);
|
|
126
|
-
}
|
|
57
|
+
log(`Starting Configuration v6.0...`);
|
|
58
|
+
log(`API Key present: ${!!effectiveKey}`);
|
|
59
|
+
// 1. ALWAYS add "connect" placeholder model
|
|
60
|
+
modelsOutput.push({
|
|
61
|
+
id: 'connect',
|
|
62
|
+
name: '🔑 Connect your Pollinations Account',
|
|
63
|
+
modalities: { input: ['text'], output: ['text'] },
|
|
64
|
+
tool_call: false
|
|
65
|
+
});
|
|
66
|
+
// 2. If no API key, return only connect placeholder
|
|
67
|
+
if (!effectiveKey || effectiveKey.length < 5 || effectiveKey === 'dummy') {
|
|
68
|
+
log('No API key configured. Returning connect placeholder only.');
|
|
69
|
+
return modelsOutput;
|
|
127
70
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
catch (e) {
|
|
144
|
-
log(`Fetch failed: ${e}.`);
|
|
145
|
-
isOffline = true;
|
|
146
|
-
// Fallback Logic
|
|
147
|
-
if (cache && cache.models.length > 0) {
|
|
148
|
-
log('Using cached models (Offline).');
|
|
149
|
-
freeModelsList = cache.models;
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
log('Using DEFAULT SEED models (Offline + No Cache).');
|
|
153
|
-
freeModelsList = FREE_MODELS_SEED;
|
|
71
|
+
// 3. Fetch models from Enterprise endpoint
|
|
72
|
+
try {
|
|
73
|
+
log('Fetching models from gen.pollinations.ai/text/models...');
|
|
74
|
+
const rawList = await fetchJson('https://gen.pollinations.ai/text/models', {
|
|
75
|
+
'Authorization': `Bearer ${effectiveKey}`
|
|
76
|
+
});
|
|
77
|
+
const modelsList = Array.isArray(rawList) ? rawList : (rawList.data || []);
|
|
78
|
+
log(`Received ${modelsList.length} models from API`);
|
|
79
|
+
modelsList.forEach((m) => {
|
|
80
|
+
// Skip models without tools support (except nomnom - special handling)
|
|
81
|
+
if (m.tools === false && m.name !== 'nomnom') {
|
|
82
|
+
log(`Skipping ${m.name} (no tools)`);
|
|
83
|
+
return;
|
|
154
84
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
log(
|
|
159
|
-
|
|
85
|
+
const mapped = mapModel(m);
|
|
86
|
+
modelsOutput.push(mapped);
|
|
87
|
+
});
|
|
88
|
+
log(`Total models registered: ${modelsOutput.length}`);
|
|
89
|
+
log(`Model IDs: ${modelsOutput.map(m => m.id).join(', ')}`);
|
|
160
90
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
//
|
|
164
|
-
// If we use cache because it's valid (Skipped fetch), we don't tag (Offline).
|
|
165
|
-
const suffix = isOffline ? ' (Offline)' : '';
|
|
166
|
-
const mapped = mapModel(m, 'free-', `[Free] `, suffix);
|
|
167
|
-
modelsOutput.push(mapped);
|
|
168
|
-
});
|
|
169
|
-
// 2. ENTERPRISE UNIVERSE
|
|
170
|
-
if (effectiveKey && effectiveKey.length > 5 && effectiveKey !== 'dummy') {
|
|
171
|
-
try {
|
|
172
|
-
const enterListRaw = await fetchJson('https://gen.pollinations.ai/text/models', {
|
|
173
|
-
'Authorization': `Bearer ${effectiveKey}`
|
|
174
|
-
});
|
|
175
|
-
const enterList = Array.isArray(enterListRaw) ? enterListRaw : (enterListRaw.data || []);
|
|
176
|
-
enterList.forEach((m) => {
|
|
177
|
-
if (m.tools === false)
|
|
178
|
-
return;
|
|
179
|
-
const mapped = mapModel(m, 'enter-', '[Enter] ');
|
|
180
|
-
modelsOutput.push(mapped);
|
|
181
|
-
});
|
|
182
|
-
log(`Total models (Free+Pro): ${modelsOutput.length}`);
|
|
183
|
-
log(`Generated IDs: ${modelsOutput.map(m => m.id).join(', ')}`);
|
|
184
|
-
}
|
|
185
|
-
catch (e) {
|
|
186
|
-
log(`Error fetching Enterprise models: ${e}`);
|
|
187
|
-
if (forceStrict)
|
|
188
|
-
throw e;
|
|
189
|
-
// STRICT: No Fallback for Enterprise. If API is down, we have 0 Enter models.
|
|
190
|
-
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
log(`Error fetching models: ${e.message}`);
|
|
93
|
+
// Return connect placeholder only on error
|
|
191
94
|
}
|
|
192
95
|
return modelsOutput;
|
|
193
96
|
}
|
|
194
97
|
// --- UTILS ---
|
|
195
98
|
function getCapabilityIcons(raw) {
|
|
196
99
|
const icons = [];
|
|
197
|
-
|
|
100
|
+
// Vision: check both input_modalities and legacy vision flag
|
|
101
|
+
if (raw.input_modalities?.includes('image') || raw.vision === true) {
|
|
198
102
|
icons.push('👁️');
|
|
199
|
-
|
|
103
|
+
}
|
|
104
|
+
// Audio input
|
|
105
|
+
if (raw.input_modalities?.includes('audio') || raw.audio === true) {
|
|
200
106
|
icons.push('🎙️');
|
|
201
|
-
|
|
107
|
+
}
|
|
108
|
+
// Audio output
|
|
109
|
+
if (raw.output_modalities?.includes('audio')) {
|
|
202
110
|
icons.push('🔊');
|
|
203
|
-
|
|
111
|
+
}
|
|
112
|
+
// Reasoning
|
|
113
|
+
if (raw.reasoning === true) {
|
|
204
114
|
icons.push('🧠');
|
|
205
|
-
|
|
115
|
+
}
|
|
116
|
+
// Search capability
|
|
117
|
+
if (raw.description?.toLowerCase().includes('search') || raw.name?.includes('search')) {
|
|
206
118
|
icons.push('🔍');
|
|
207
|
-
|
|
208
|
-
|
|
119
|
+
}
|
|
120
|
+
// Tools
|
|
121
|
+
if (raw.tools === true) {
|
|
122
|
+
icons.push('🛠️');
|
|
123
|
+
}
|
|
124
|
+
// Paid only
|
|
125
|
+
if (raw.paid_only === true) {
|
|
126
|
+
icons.push('💎');
|
|
127
|
+
}
|
|
209
128
|
return icons.length > 0 ? ` ${icons.join('')}` : '';
|
|
210
129
|
}
|
|
211
|
-
function formatName(id
|
|
212
|
-
let clean = id.replace(
|
|
130
|
+
function formatName(id) {
|
|
131
|
+
let clean = id.replace(/-/g, ' ');
|
|
213
132
|
clean = clean.replace(/\b\w/g, l => l.toUpperCase());
|
|
214
|
-
if (!censored)
|
|
215
|
-
clean += " (Uncensored)";
|
|
216
133
|
return clean;
|
|
217
134
|
}
|
|
218
|
-
function mapModel(raw
|
|
219
|
-
const rawId = raw.
|
|
220
|
-
|
|
135
|
+
function mapModel(raw) {
|
|
136
|
+
const rawId = raw.name;
|
|
137
|
+
// Build display name
|
|
221
138
|
let baseName = raw.description;
|
|
222
139
|
if (!baseName || baseName === rawId) {
|
|
223
|
-
baseName = formatName(rawId
|
|
140
|
+
baseName = formatName(rawId);
|
|
224
141
|
}
|
|
142
|
+
// Truncate after first " - "
|
|
225
143
|
if (baseName && baseName.includes(' - ')) {
|
|
226
144
|
baseName = baseName.split(' - ')[0].trim();
|
|
227
145
|
}
|
|
228
|
-
let namePrefixFinal = namePrefix;
|
|
229
|
-
if (raw.paid_only) {
|
|
230
|
-
namePrefixFinal = namePrefix.replace('[Enter]', '[💎 Paid]');
|
|
231
|
-
}
|
|
232
146
|
const capabilityIcons = getCapabilityIcons(raw);
|
|
233
|
-
const finalName = `${
|
|
147
|
+
const finalName = `${baseName}${capabilityIcons}`;
|
|
148
|
+
// Determine modalities for OpenCode
|
|
149
|
+
const inputMods = raw.input_modalities || ['text'];
|
|
150
|
+
const outputMods = raw.output_modalities || ['text'];
|
|
234
151
|
const modelObj = {
|
|
235
|
-
id:
|
|
152
|
+
id: rawId, // No prefix! Direct model ID
|
|
236
153
|
name: finalName,
|
|
237
|
-
// object: 'model',
|
|
238
|
-
// variants: {}, // POTENTIAL SCHEMA VIOLATION
|
|
239
154
|
modalities: {
|
|
240
|
-
input:
|
|
241
|
-
output:
|
|
155
|
+
input: inputMods,
|
|
156
|
+
output: outputMods
|
|
242
157
|
},
|
|
243
|
-
tool_call:
|
|
158
|
+
tool_call: raw.tools === true && rawId !== 'nomnom' // NomNom: no tools
|
|
244
159
|
};
|
|
245
|
-
//
|
|
246
|
-
/*
|
|
247
|
-
if (raw.reasoning === true || rawId.includes('thinking') || rawId.includes('reasoning')) {
|
|
248
|
-
modelObj.variants = { ...modelObj.variants, high_reasoning: { options: { reasoningEffort: "high", budgetTokens: 16000 } } };
|
|
249
|
-
}
|
|
250
|
-
if (rawId.includes('gemini') && !rawId.includes('fast')) {
|
|
251
|
-
if (!modelObj.variants.high_reasoning && (rawId === 'gemini' || rawId === 'gemini-large')) {
|
|
252
|
-
modelObj.variants.high_reasoning = { options: { reasoningEffort: "high", budgetTokens: 16000 } };
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
if (rawId.includes('claude') || rawId.includes('mistral') || rawId.includes('llama')) {
|
|
256
|
-
modelObj.variants.safe_tokens = { options: { maxTokens: 8000 } };
|
|
257
|
-
}
|
|
258
|
-
*/
|
|
160
|
+
// Model-specific limits
|
|
259
161
|
if (rawId.includes('nova')) {
|
|
260
|
-
|
|
261
|
-
modelObj.limit = { output: 8000, context: 128000 };
|
|
262
|
-
}
|
|
263
|
-
if (rawId.includes('nomnom') || rawId.includes('scrape')) {
|
|
264
|
-
modelObj.limit = { output: 2048, context: 32768 };
|
|
265
|
-
}
|
|
266
|
-
if (rawId.includes('chicky') || rawId.includes('mistral')) {
|
|
267
|
-
modelObj.limit = { output: 4096, context: 8192 };
|
|
268
|
-
modelObj.options = { ...modelObj.options, maxTokens: 4096 };
|
|
269
|
-
log(`[LimitConfig] Applied strict limit to ${fullId}: output=4096, context=8192`);
|
|
270
|
-
}
|
|
271
|
-
/*
|
|
272
|
-
if (rawId.includes('fast') || rawId.includes('flash')) {
|
|
273
|
-
if (!rawId.includes('gemini')) {
|
|
274
|
-
modelObj.variants.speed = { options: { thinking: { disabled: true } } };
|
|
275
|
-
}
|
|
162
|
+
modelObj.limit = { output: 8000, context: 128000 };
|
|
276
163
|
}
|
|
277
|
-
|
|
164
|
+
if (rawId === 'nomnom') {
|
|
165
|
+
modelObj.limit = { output: 2048, context: 32768 };
|
|
166
|
+
modelObj.tool_call = false; // NomNom is a router, no external tools
|
|
278
167
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
168
|
+
if (rawId.includes('chicky') || rawId.includes('mistral')) {
|
|
169
|
+
modelObj.limit = { output: 4096, context: 8192 };
|
|
170
|
+
modelObj.options = { maxTokens: 4096 };
|
|
282
171
|
}
|
|
172
|
+
log(`[Mapped] ${modelObj.id} → ${modelObj.name} | tools=${modelObj.tool_call} | modalities=${JSON.stringify(modelObj.modalities)}`);
|
|
283
173
|
return modelObj;
|
|
284
174
|
}
|
package/dist/server/proxy.js
CHANGED
|
@@ -216,16 +216,16 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
216
216
|
// LOAD QUOTA FOR SAFETY CHECKS
|
|
217
217
|
const { getQuotaStatus, formatQuotaForToast } = await import('./quota.js');
|
|
218
218
|
const quota = await getQuotaStatus(false);
|
|
219
|
-
// A. Resolve Base Target
|
|
220
|
-
//
|
|
219
|
+
// A. Resolve Base Target - v6.0: All models go to gen.pollinations.ai
|
|
220
|
+
// Strip any legacy prefixes for backwards compatibility
|
|
221
221
|
if (actualModel.startsWith('enter/') || actualModel.startsWith('enter-')) {
|
|
222
|
-
isEnterprise = true;
|
|
223
222
|
actualModel = actualModel.replace(/^enter[-/]/, '');
|
|
224
223
|
}
|
|
225
224
|
else if (actualModel.startsWith('free/') || actualModel.startsWith('free-')) {
|
|
226
|
-
isEnterprise = false;
|
|
227
225
|
actualModel = actualModel.replace(/^free[-/]/, '');
|
|
228
226
|
}
|
|
227
|
+
// v6.0: Everything is enterprise now (requires API key)
|
|
228
|
+
isEnterprise = true;
|
|
229
229
|
// A.1 PAID MODEL ENFORCEMENT (V5.5 Strategy)
|
|
230
230
|
// Check dynamic list saved by generate-config.ts
|
|
231
231
|
if (isEnterprise) {
|
|
@@ -337,24 +337,16 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
337
337
|
}
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
|
-
// C. Construct URL & Headers
|
|
341
|
-
if (
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
targetUrl = 'https://gen.pollinations.ai/v1/chat/completions';
|
|
349
|
-
authHeader = `Bearer ${config.apiKey}`;
|
|
350
|
-
log(`Routing to ENTERPRISE: ${actualModel}`);
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
targetUrl = 'https://text.pollinations.ai/openai/chat/completions';
|
|
354
|
-
authHeader = undefined;
|
|
355
|
-
log(`Routing to FREE: ${actualModel} ${isFallbackActive ? '(FALLBACK)' : ''}`);
|
|
356
|
-
// emitLogToast('info', `Routing to: FREE UNIVERSE (${actualModel})`, 'Pollinations Routing'); // Too noisy
|
|
340
|
+
// C. Construct URL & Headers - v6.0: Always gen.pollinations.ai
|
|
341
|
+
if (!config.apiKey) {
|
|
342
|
+
emitLogToast('error', "Missing API Key - Use /pollinations connect <key>", 'Proxy Error');
|
|
343
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
344
|
+
res.end(JSON.stringify({ error: { message: "API Key required. Use /pollinations connect <your_key> to connect." } }));
|
|
345
|
+
return;
|
|
357
346
|
}
|
|
347
|
+
targetUrl = 'https://gen.pollinations.ai/v1/chat/completions';
|
|
348
|
+
authHeader = `Bearer ${config.apiKey}`;
|
|
349
|
+
log(`Routing to gen.pollinations.ai: ${actualModel}`);
|
|
358
350
|
// NOTIFY SWITCH
|
|
359
351
|
if (isFallbackActive) {
|
|
360
352
|
emitStatusToast('warning', `⚠️ Safety Net: ${actualModel} (${fallbackReason})`, 'Pollinations Safety');
|
|
@@ -594,10 +586,10 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
594
586
|
// 2. Notify
|
|
595
587
|
emitStatusToast('warning', `⚠️ Safety Net: ${actualModel} (${fallbackReason})`, 'Pollinations Safety');
|
|
596
588
|
emitLogToast('warning', `Recovering from ${fetchRes.status} -> Switching to ${actualModel}`, 'Safety Net');
|
|
597
|
-
// 3. Re-Prepare Request
|
|
598
|
-
targetUrl = 'https://
|
|
589
|
+
// 3. Re-Prepare Request - v6.0: Stay on gen.pollinations.ai
|
|
590
|
+
targetUrl = 'https://gen.pollinations.ai/v1/chat/completions';
|
|
599
591
|
const retryHeaders = { ...headers };
|
|
600
|
-
|
|
592
|
+
// Keep Authorization for gen.pollinations.ai
|
|
601
593
|
const retryBody = { ...proxyBody, model: actualModel };
|
|
602
594
|
// 4. Retry Fetch
|
|
603
595
|
const retryRes = await fetchWithRetry(targetUrl, {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-pollinations-plugin",
|
|
3
3
|
"displayName": "Pollinations AI (V5.6)",
|
|
4
|
-
"version": "5.8.4-beta.
|
|
4
|
+
"version": "5.8.4-beta.17",
|
|
5
5
|
"description": "Native Pollinations.ai Provider Plugin for OpenCode",
|
|
6
6
|
"publisher": "pollinations",
|
|
7
7
|
"repository": {
|
|
@@ -55,4 +55,4 @@
|
|
|
55
55
|
"@types/node": "^20.0.0",
|
|
56
56
|
"typescript": "^5.0.0"
|
|
57
57
|
}
|
|
58
|
-
}
|
|
58
|
+
}
|