opencode-pollinations-plugin 6.5.0 → 6.5.1
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/dist/locales/de.json +1 -1
- package/dist/locales/en.json +1 -1
- package/dist/locales/es.json +1 -1
- package/dist/locales/fr.json +1 -1
- package/dist/locales/it.json +1 -1
- package/dist/locales/zh.json +1 -1
- package/dist/server/commands.js +2 -2
- package/dist/server/config.d.ts +2 -0
- package/dist/server/config.js +3 -2
- package/dist/server/generate-config.d.ts +7 -0
- package/dist/server/generate-config.js +22 -16
- package/dist/server/models/fetcher.js +9 -0
- package/dist/server/models/types.d.ts +8 -0
- package/dist/server/models/worker.js +32 -8
- package/dist/server/proxy.js +12 -28
- package/dist/server/quota.js +1 -1
- package/dist/tools/index.js +5 -2
- package/dist/tools/pollinations/polli_config.js +3 -0
- package/package.json +1 -1
package/dist/locales/de.json
CHANGED
|
@@ -473,7 +473,7 @@
|
|
|
473
473
|
"error": "❗ Error: {error}"
|
|
474
474
|
},
|
|
475
475
|
"gen3d": {
|
|
476
|
-
"desc": "Erzeugt ein 3D-Objekt aus einem Text-Prompt oder Referenzbild mit Pollinations AI.\n\
|
|
476
|
+
"desc": "Erzeugt ein 3D-Objekt aus einem Text-Prompt oder Referenzbild mit Pollinations AI.\n\nAktuelle 3D-Modelle, Zugriff, E/A-Fähigkeiten und Kosten werden dynamisch aus der Live Model Registry eingefügt.\n\n**⏱️ Lange Laufzeit**: Die Generierung kann 20s–150s+ dauern (LONG_BLOCKING). Nutze `timeout_seconds` zur Steuerung der lokalen Wartezeit.\n\n**♻️ Wiederherstellung**: Nach einem Timeout kann die Generierung weiterlaufen und abgerechnet werden. Ein erneuter Lauf mit dem GLEICHEN Seed liefert das Artefakt aus dem Cache (keine erneute Abrechnung).",
|
|
477
477
|
"arg_prompt": "Textbeschreibung des 3D-Objekts (optional, wenn ein Bild angegeben ist)",
|
|
478
478
|
"arg_image": "Optionale Referenzbild-URL (max. 1 Bild)",
|
|
479
479
|
"arg_model": "Zu verwendendes Modell (Standard: trellis-2).",
|
package/dist/locales/en.json
CHANGED
|
@@ -473,7 +473,7 @@
|
|
|
473
473
|
"error": "❗ Error: {error}"
|
|
474
474
|
},
|
|
475
475
|
"gen3d": {
|
|
476
|
-
"desc": "Generate a 3D object from a text prompt or reference image using Pollinations AI.\n\
|
|
476
|
+
"desc": "Generate a 3D object from a text prompt or reference image using Pollinations AI.\n\nThe current 3D models, access flags, I/O capabilities and costs are injected dynamically from the live Model Registry.\n\n**⏱️ Long-running** : generation can take 20s–150s+ (LONG_BLOCKING). Use `timeout_seconds` to control the local wait.\n\n**♻️ Recovery** : after a timeout, the generation may still be running and billed. Re-running with the SAME seed returns the artifact from cache (not rebilled).",
|
|
477
477
|
"arg_prompt": "Text description of the 3D object (optional when image is provided)",
|
|
478
478
|
"arg_image": "Optional reference image URL (1 image max)",
|
|
479
479
|
"arg_model": "Model to use (default: trellis-2).",
|
package/dist/locales/es.json
CHANGED
|
@@ -473,7 +473,7 @@
|
|
|
473
473
|
"error": "❗ Error: {error}"
|
|
474
474
|
},
|
|
475
475
|
"gen3d": {
|
|
476
|
-
"desc": "Genera un objeto 3D desde un prompt de texto o una imagen de referencia con Pollinations AI.\n\
|
|
476
|
+
"desc": "Genera un objeto 3D desde un prompt de texto o una imagen de referencia con Pollinations AI.\n\nLos modelos 3D actuales, acceso, capacidades E/S y costes se inyectan dinámicamente desde el Model Registry en vivo.\n\n**⏱️ Larga duración**: la generación puede tardar 20s–150s+ (LONG_BLOCKING). Usa `timeout_seconds` para controlar la espera local.\n\n**♻️ Recuperación**: tras un timeout, la generación puede seguir ejecutándose y facturarse. Relanzar con la MISMA seed devuelve el artefacto desde la caché (sin nueva factura).",
|
|
477
477
|
"arg_prompt": "Descripción de texto del objeto 3D (opcional si se da una imagen)",
|
|
478
478
|
"arg_image": "URL de imagen de referencia opcional (máx. 1 imagen)",
|
|
479
479
|
"arg_model": "Modelo a usar (por defecto: trellis-2).",
|
package/dist/locales/fr.json
CHANGED
|
@@ -473,7 +473,7 @@
|
|
|
473
473
|
"error": "❗ Error: {error}"
|
|
474
474
|
},
|
|
475
475
|
"gen3d": {
|
|
476
|
-
"desc": "Génère un objet 3D à partir d'un prompt texte ou d'une image de référence via Pollinations AI.\n\
|
|
476
|
+
"desc": "Génère un objet 3D à partir d'un prompt texte ou d'une image de référence via Pollinations AI.\n\nLes modèles 3D actuels, leurs accès, capacités E/S et coûts sont injectés dynamiquement depuis le Model Registry live.\n\n**⏱️ Longue durée** : la génération peut prendre 20s–150s+ (LONG_BLOCKING). Utilisez `timeout_seconds` pour contrôler l'attente locale.\n\n**♻️ Récupération** : après un timeout, la génération peut continuer et être facturée. Relancer avec le MÊME seed renvoie l'artefact depuis le cache (non refacturé).",
|
|
477
477
|
"arg_prompt": "Description texte de l'objet 3D (optionnel si une image est fournie)",
|
|
478
478
|
"arg_image": "URL d'image de référence optionnelle (1 image max)",
|
|
479
479
|
"arg_model": "Modèle à utiliser (défaut : trellis-2).",
|
package/dist/locales/it.json
CHANGED
|
@@ -473,7 +473,7 @@
|
|
|
473
473
|
"error": "❗ Error: {error}"
|
|
474
474
|
},
|
|
475
475
|
"gen3d": {
|
|
476
|
-
"desc": "Genera un oggetto 3D da un prompt di testo o un'immagine di riferimento con Pollinations AI.\n\
|
|
476
|
+
"desc": "Genera un oggetto 3D da un prompt di testo o un'immagine di riferimento con Pollinations AI.\n\nI modelli 3D correnti, accesso, capacità I/O e costi vengono inseriti dinamicamente dal Model Registry live.\n\n**⏱️ Lunga durata**: la generazione può richiedere 20s–150s+ (LONG_BLOCKING). Usa `timeout_seconds` per controllare l'attesa locale.\n\n**♻️ Recupero**: dopo un timeout la generazione può continuare ed essere fatturata. Rilanciare con lo STESSO seed restituisce l'artefatto dalla cache (non rifatturato).",
|
|
477
477
|
"arg_prompt": "Descrizione testuale dell'oggetto 3D (opzionale se si fornisce un'immagine)",
|
|
478
478
|
"arg_image": "URL dell'immagine di riferimento opzionale (max 1 immagine)",
|
|
479
479
|
"arg_model": "Modello da usare (predefinito: trellis-2).",
|
package/dist/locales/zh.json
CHANGED
|
@@ -473,7 +473,7 @@
|
|
|
473
473
|
"error": "❗ Error: {error}"
|
|
474
474
|
},
|
|
475
475
|
"gen3d": {
|
|
476
|
-
"desc": "使用 Pollinations AI 从文本提示或参考图像生成 3D 对象。\n\n
|
|
476
|
+
"desc": "使用 Pollinations AI 从文本提示或参考图像生成 3D 对象。\n\n当前 3D 模型、访问标志、输入输出能力和成本由实时 Model Registry 动态注入。\n\n**⏱️ 长时运行**:生成可能需要 20s–150s+(LONG_BLOCKING)。使用 `timeout_seconds` 控制本地等待。\n\n**♻️ 恢复**:超时后生成可能仍在运行并计费。使用相同 seed 重新运行将从缓存返回结果(不重复计费)。",
|
|
477
477
|
"arg_prompt": "3D 对象的文本描述(提供图像时可省略)",
|
|
478
478
|
"arg_image": "可选的参考图像 URL(最多 1 张)",
|
|
479
479
|
"arg_model": "使用的模型(默认:trellis-2)。",
|
package/dist/server/commands.js
CHANGED
|
@@ -164,9 +164,9 @@ async function handleModeCommand(args) {
|
|
|
164
164
|
response: t('commands.mode.current', { mode: config.mode })
|
|
165
165
|
};
|
|
166
166
|
}
|
|
167
|
-
// v6.5 Quest/Paid modes + legacy aliases (alwaysfree →
|
|
167
|
+
// v6.5 Quest/Paid modes + legacy aliases (alwaysfree → quest_only, pro → paid).
|
|
168
168
|
const LEGACY_MODE_ALIASES = {
|
|
169
|
-
'alwaysfree': '
|
|
169
|
+
'alwaysfree': 'quest_only',
|
|
170
170
|
'pro': 'paid',
|
|
171
171
|
};
|
|
172
172
|
const resolvedMode = LEGACY_MODE_ALIASES[mode] || mode;
|
package/dist/server/config.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export interface PollinationsConfigV5 {
|
|
|
25
25
|
};
|
|
26
26
|
};
|
|
27
27
|
enablePaidTools: boolean;
|
|
28
|
+
enableDeveloperTools: boolean;
|
|
28
29
|
costThreshold: number;
|
|
29
30
|
costConfirmationRequired: boolean;
|
|
30
31
|
statusBar: boolean;
|
|
@@ -65,6 +66,7 @@ export declare function saveConfig(updates: Partial<PollinationsConfigV5>): {
|
|
|
65
66
|
};
|
|
66
67
|
};
|
|
67
68
|
enablePaidTools: boolean;
|
|
69
|
+
enableDeveloperTools: boolean;
|
|
68
70
|
costThreshold: number;
|
|
69
71
|
costConfirmationRequired: boolean;
|
|
70
72
|
statusBar: boolean;
|
package/dist/server/config.js
CHANGED
|
@@ -77,6 +77,7 @@ const DEFAULT_CONFIG_V5 = {
|
|
|
77
77
|
enter: { agent: 'free/openai-fast' }
|
|
78
78
|
},
|
|
79
79
|
enablePaidTools: false,
|
|
80
|
+
enableDeveloperTools: false,
|
|
80
81
|
costThreshold: 0.15, // Default 0.15 🌻
|
|
81
82
|
costConfirmationRequired: true, // Ask confirmation when cost exceeds threshold
|
|
82
83
|
keyHasAccessToProfile: true, // Default true for legacy keys
|
|
@@ -85,13 +86,13 @@ const DEFAULT_CONFIG_V5 = {
|
|
|
85
86
|
lang: 'en', // Default language is English
|
|
86
87
|
};
|
|
87
88
|
// v6.5 migration: legacy mode/threshold names → Quest/Paid semantics.
|
|
88
|
-
// alwaysfree →
|
|
89
|
+
// alwaysfree → quest_only (QUEST_ELIGIBLE_ONLY), pro → paid (PAID_ALLOWED).
|
|
89
90
|
// Legacy tier percentage thresholds are replaced by absolute pollen floors.
|
|
90
91
|
export function migrateV65Config(raw) {
|
|
91
92
|
if (!raw || typeof raw !== 'object')
|
|
92
93
|
return raw;
|
|
93
94
|
const legacyModeMap = {
|
|
94
|
-
'alwaysfree': '
|
|
95
|
+
'alwaysfree': 'quest_only',
|
|
95
96
|
'pro': 'paid',
|
|
96
97
|
'manual': 'manual',
|
|
97
98
|
'quest': 'quest',
|
|
@@ -15,6 +15,13 @@ interface OpenCodeModel {
|
|
|
15
15
|
attachment?: boolean;
|
|
16
16
|
tool_call?: boolean;
|
|
17
17
|
reasoning?: boolean;
|
|
18
|
+
reasoning_options?: Array<{
|
|
19
|
+
type: 'effort' | 'toggle';
|
|
20
|
+
values?: string[];
|
|
21
|
+
}>;
|
|
22
|
+
interleaved?: {
|
|
23
|
+
field: 'reasoning' | 'reasoning_content' | 'reasoning_details';
|
|
24
|
+
};
|
|
18
25
|
temperature?: boolean;
|
|
19
26
|
}
|
|
20
27
|
export declare function generatePollinationsConfig(forceApiKey?: string, forceStrict?: boolean): Promise<OpenCodeModel[]>;
|
|
@@ -206,22 +206,28 @@ function mapModel(raw, prefix, namePrefix) {
|
|
|
206
206
|
modelObj.modalities.input.push('image');
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
-
// 2. REASONING
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (
|
|
220
|
-
modelObj.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
209
|
+
// 2. NATIVE REASONING — capabilities come from the live Model Registry.
|
|
210
|
+
// OpenCode keeps reasoning as structured parts; it must never be merged into content.
|
|
211
|
+
const supportsReasoning = raw.reasoning === true || raw.capabilities?.includes('reasoning');
|
|
212
|
+
modelObj.reasoning = supportsReasoning;
|
|
213
|
+
if (supportsReasoning) {
|
|
214
|
+
const advertised = Array.isArray(raw.reasoning_options) ? raw.reasoning_options : [];
|
|
215
|
+
const effectiveReasoningOptions = advertised.length > 0
|
|
216
|
+
? advertised
|
|
217
|
+
: [{ type: 'effort', values: ['low', 'high'] }];
|
|
218
|
+
modelObj.reasoning_options = effectiveReasoningOptions;
|
|
219
|
+
if (raw.interleaved?.field) {
|
|
220
|
+
modelObj.interleaved = raw.interleaved;
|
|
221
|
+
}
|
|
222
|
+
const effortValues = effectiveReasoningOptions
|
|
223
|
+
.filter(option => option.type === 'effort')
|
|
224
|
+
.flatMap(option => option.values || []);
|
|
225
|
+
for (const effort of effortValues) {
|
|
226
|
+
modelObj.variants[effort] = { reasoningEffort: effort };
|
|
227
|
+
}
|
|
228
|
+
if (effectiveReasoningOptions.some(option => option.type === 'toggle')) {
|
|
229
|
+
modelObj.variants.off = { reasoningEffort: 'none' };
|
|
230
|
+
modelObj.variants.on = {};
|
|
225
231
|
}
|
|
226
232
|
}
|
|
227
233
|
// 3. SAFETY VARIANTS — maxTokens for models with known limits
|
|
@@ -93,6 +93,9 @@ function mapRawToModel(raw, fallbackCategory, averageCost) {
|
|
|
93
93
|
voices: raw.voices,
|
|
94
94
|
tools: raw.tools,
|
|
95
95
|
reasoning: raw.reasoning,
|
|
96
|
+
reasoning_options: raw.reasoning_options,
|
|
97
|
+
interleaved: raw.interleaved,
|
|
98
|
+
capabilities: raw.capabilities,
|
|
96
99
|
is_specialized: raw.is_specialized,
|
|
97
100
|
context_window: raw.context_window || raw.context_length,
|
|
98
101
|
averageCost: averageCost !== undefined && !isNaN(averageCost) ? averageCost : undefined,
|
|
@@ -179,6 +182,12 @@ export async function fetchAllModels(apiKey) {
|
|
|
179
182
|
item.tools = v1Item.tools;
|
|
180
183
|
if (v1Item.reasoning !== undefined)
|
|
181
184
|
item.reasoning = v1Item.reasoning;
|
|
185
|
+
if (v1Item.reasoning_options !== undefined)
|
|
186
|
+
item.reasoning_options = v1Item.reasoning_options;
|
|
187
|
+
if (v1Item.interleaved !== undefined)
|
|
188
|
+
item.interleaved = v1Item.interleaved;
|
|
189
|
+
if (v1Item.capabilities !== undefined)
|
|
190
|
+
item.capabilities = v1Item.capabilities;
|
|
182
191
|
}
|
|
183
192
|
const model = mapRawToModel(item, res.fallbackCategory, avgCost);
|
|
184
193
|
const uniqueId = model.name;
|
|
@@ -33,6 +33,14 @@ export interface PollinationsModel {
|
|
|
33
33
|
voices?: string[];
|
|
34
34
|
tools?: boolean;
|
|
35
35
|
reasoning?: boolean;
|
|
36
|
+
reasoning_options?: Array<{
|
|
37
|
+
type: 'effort' | 'toggle';
|
|
38
|
+
values?: string[];
|
|
39
|
+
}>;
|
|
40
|
+
interleaved?: {
|
|
41
|
+
field: 'reasoning' | 'reasoning_content' | 'reasoning_details';
|
|
42
|
+
};
|
|
43
|
+
capabilities?: string[];
|
|
36
44
|
is_specialized?: boolean;
|
|
37
45
|
context_window?: number;
|
|
38
46
|
input_modalities: string[];
|
|
@@ -2,6 +2,7 @@ import { polliGenImageTool } from '../../tools/pollinations/gen_image.js';
|
|
|
2
2
|
import { polliGenVideoTool } from '../../tools/pollinations/gen_video.js';
|
|
3
3
|
import { polliGenAudioTool } from '../../tools/pollinations/gen_audio.js';
|
|
4
4
|
import { polliGenMusicTool } from '../../tools/pollinations/gen_music.js';
|
|
5
|
+
import { polliGen3dTool } from '../../tools/pollinations/gen_3d.js';
|
|
5
6
|
import { polliWebSearchTool } from '../../tools/pollinations/polli_web_search.js';
|
|
6
7
|
import { ModelRegistry } from './index.js';
|
|
7
8
|
import { log } from '../logger.js';
|
|
@@ -54,7 +55,7 @@ export class ToolRegistryWorker {
|
|
|
54
55
|
const videoModels = ModelRegistry.list('video');
|
|
55
56
|
if (videoModels.length > 0 && polliGenVideoTool.description) {
|
|
56
57
|
let videoTable = `\n\n**🎬 Modèles Vidéo Détectés (${videoModels.length}) :**\n`;
|
|
57
|
-
videoTable += `*(Légende: [💎 Paid] = Pollen acheté (consomme le Wallet USD), [🌿
|
|
58
|
+
videoTable += `*(Légende: [💎 Paid] = Pollen acheté (consomme le Wallet USD), [🌿 Quest] = appel éligible au Quest; en mode quest_only, fallback Free avant le plancher Quest)*\n`;
|
|
58
59
|
videoTable += `| Modèle | Source I/O | Audio | 1 pollen ≈ | Specs |\n`;
|
|
59
60
|
videoTable += `|--------|------------|-------|------------|-------|\n`;
|
|
60
61
|
for (const m of videoModels) {
|
|
@@ -62,7 +63,7 @@ export class ToolRegistryWorker {
|
|
|
62
63
|
const price = cost ? `${per1pollen(cost)} vidéos` : 'inconnu';
|
|
63
64
|
const specs = `${m.durationRange ? m.durationRange.join('-') + 's' : '?s'} / ${m.aspectRatios ? m.aspectRatios.length : '?'} ratios`;
|
|
64
65
|
const isCommunity = m.community === true || m.name.includes('/');
|
|
65
|
-
const badge = isCommunity ? '[👥]' : (m.paid_only ? '[💎 Paid]' : '[🌿
|
|
66
|
+
const badge = isCommunity ? '[👥]' : (m.paid_only ? '[💎 Paid]' : '[🌿 Quest]');
|
|
66
67
|
videoTable += `| \`${m.name}\` ${badge} | ${m.supportsI2X ? 'T2V/I2V' : 'T2V'} | ${m.output_modalities?.includes('audio') || m.name === 'grok-video' ? '✅' : '❌'} | ${price} | ${specs} |\n`;
|
|
67
68
|
}
|
|
68
69
|
if (!polliGenVideoTool.description.includes('**🎬 Modèles Vidéo Détectés')) {
|
|
@@ -76,7 +77,7 @@ export class ToolRegistryWorker {
|
|
|
76
77
|
const imageModels = ModelRegistry.list('image');
|
|
77
78
|
if (imageModels.length > 0 && polliGenImageTool.description) {
|
|
78
79
|
let imageTable = `\n\n**🎨 Modèles Image Détectés (${imageModels.length}) :**\n`;
|
|
79
|
-
imageTable += `*(Légende: [💎 Paid] = Pollen acheté (consomme le Wallet USD), [🌿
|
|
80
|
+
imageTable += `*(Légende: [💎 Paid] = Pollen acheté (consomme le Wallet USD), [🌿 Quest] = appel éligible au Quest; en mode quest_only, fallback Free avant le plancher Quest)*\n`;
|
|
80
81
|
imageTable += `| Modèle | I2I | Qualité | 1 pollen ≈ |\n`;
|
|
81
82
|
imageTable += `|--------|-----|---------|------------|\n`;
|
|
82
83
|
// Afficher max 20 modèles pour éviter de saturer le prompt LLM
|
|
@@ -84,7 +85,7 @@ export class ToolRegistryWorker {
|
|
|
84
85
|
const cost = estimateImageCost(m.name);
|
|
85
86
|
const price = cost ? `${per1pollen(cost)} images` : 'inconnu';
|
|
86
87
|
const isCommunity = m.community === true || m.name.includes('/');
|
|
87
|
-
const badge = isCommunity ? '[👥]' : (m.paid_only ? '[💎 Paid]' : '[🌿
|
|
88
|
+
const badge = isCommunity ? '[👥]' : (m.paid_only ? '[💎 Paid]' : '[🌿 Quest]');
|
|
88
89
|
imageTable += `| \`${m.name}\` ${badge} | ${m.supportsI2X ? '✅' : '❌'} | Standard | ${price} |\n`;
|
|
89
90
|
}
|
|
90
91
|
if (imageModels.length > 20) {
|
|
@@ -101,12 +102,12 @@ export class ToolRegistryWorker {
|
|
|
101
102
|
const audioModels = ModelRegistry.list('audio');
|
|
102
103
|
if (audioModels.length > 0 && polliGenAudioTool.description && polliGenMusicTool.description) {
|
|
103
104
|
let audioTable = `\n\n**🎵 Modèles Audio/Music Détectés (${audioModels.length}) :**\n`;
|
|
104
|
-
audioTable += `*(Légende: [💎 Paid] = Pollen acheté (consomme le Wallet USD), [🌿
|
|
105
|
+
audioTable += `*(Légende: [💎 Paid] = Pollen acheté (consomme le Wallet USD), [🌿 Quest] = appel éligible au Quest; en mode quest_only, fallback Free avant le plancher Quest)*\n`;
|
|
105
106
|
audioTable += `| Modèle | Durée max | Qualité |\n`;
|
|
106
107
|
audioTable += `|--------|-----------|---------|\n`;
|
|
107
108
|
for (const m of audioModels) {
|
|
108
109
|
const isCommunity = m.community === true || m.name.includes('/');
|
|
109
|
-
const badge = isCommunity ? '[👥]' : (m.paid_only ? '[💎 Paid]' : '[🌿
|
|
110
|
+
const badge = isCommunity ? '[👥]' : (m.paid_only ? '[💎 Paid]' : '[🌿 Quest]');
|
|
110
111
|
const duration = m.durationRange ? `${m.durationRange.join('-')}s` : 'Standard';
|
|
111
112
|
audioTable += `| \`${m.name}\` ${badge} | ${duration} | Standard |\n`;
|
|
112
113
|
}
|
|
@@ -123,6 +124,29 @@ export class ToolRegistryWorker {
|
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
}
|
|
127
|
+
// -- Patch 3D Tool (fully dynamic from ModelRegistry) --
|
|
128
|
+
const threeDModels = ModelRegistry.list('3d');
|
|
129
|
+
if (threeDModels.length > 0 && polliGen3dTool.description) {
|
|
130
|
+
let threeDTable = `\n\n**🧊 Live 3D models (${threeDModels.length})**\n`;
|
|
131
|
+
threeDTable += `| Model | Access | Inputs | Output | Estimated cost |\n`;
|
|
132
|
+
threeDTable += `|-------|--------|--------|--------|----------------|\n`;
|
|
133
|
+
for (const m of threeDModels) {
|
|
134
|
+
const access = m.paid_only ? 'Paid only' : 'Quest eligible';
|
|
135
|
+
const inputs = m.input_modalities.join(', ') || 'text';
|
|
136
|
+
const cost = m.averageCost
|
|
137
|
+
?? m.pricing.completionImageTokens
|
|
138
|
+
?? m.pricing.completionTextTokens;
|
|
139
|
+
const costText = typeof cost === 'number' ? `${cost.toFixed(4)} pollen` : 'live pricing';
|
|
140
|
+
threeDTable += `| \`${m.name}\` | ${access} | ${inputs} | GLB | ${costText} |\n`;
|
|
141
|
+
}
|
|
142
|
+
const sectionMarker = '**🧊 Live 3D models';
|
|
143
|
+
if (!polliGen3dTool.description.includes(sectionMarker)) {
|
|
144
|
+
polliGen3dTool.description += threeDTable;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
polliGen3dTool.description = polliGen3dTool.description.split(sectionMarker)[0] + threeDTable;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
126
150
|
// -- Patch WEB SEARCH Tool --
|
|
127
151
|
const textModels = ModelRegistry.list('text');
|
|
128
152
|
if (textModels.length > 0 && polliWebSearchTool.description) {
|
|
@@ -134,12 +158,12 @@ export class ToolRegistryWorker {
|
|
|
134
158
|
});
|
|
135
159
|
if (searchModels.length > 0) {
|
|
136
160
|
let searchTable = `\n\n**🌍 Modèles de Recherche & Grounding Détectés (${searchModels.length}) :**\n`;
|
|
137
|
-
searchTable += `*(Légende: [💎 Paid] = Pollen acheté, [🌿
|
|
161
|
+
searchTable += `*(Légende: [💎 Paid] = Pollen acheté, [🌿 Quest] = appel éligible au Quest; quest_only protège le wallet Paid. Obligatoire de choisir un modèle exact au lieu de 'deep' ou 'rapid' !)*\n`;
|
|
138
162
|
searchTable += `| Modèle | Description / Specs |\n`;
|
|
139
163
|
searchTable += `|--------|---------------------|\n`;
|
|
140
164
|
for (const m of searchModels) {
|
|
141
165
|
const isCommunity = m.community === true || m.name.includes('/');
|
|
142
|
-
const badge = isCommunity ? '[👥]' : (m.paid_only ? '[💎 Paid]' : '[🌿
|
|
166
|
+
const badge = isCommunity ? '[👥]' : (m.paid_only ? '[💎 Paid]' : '[🌿 Quest]');
|
|
143
167
|
// Clean markdown piping conflicts
|
|
144
168
|
let cleanDesc = m.description.replace(/\|/g, '-');
|
|
145
169
|
// Adding "Specialized" hint
|
package/dist/server/proxy.js
CHANGED
|
@@ -232,29 +232,12 @@ export async function fetchWithRetry(url, options, retries = MAX_RETRIES) {
|
|
|
232
232
|
return response;
|
|
233
233
|
}
|
|
234
234
|
// ============================================================================
|
|
235
|
-
// REASONING NORMALIZATION
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
// `
|
|
240
|
-
// Rule: never merge reasoning* into content; strip the backend-specific fields;
|
|
241
|
-
// preserve usage.completion_tokens_details.reasoning_tokens.
|
|
235
|
+
// NATIVE REASONING NORMALIZATION
|
|
236
|
+
// Preserve `reasoning_content`, `reasoning` and `reasoning_details` as
|
|
237
|
+
// structured sibling fields. OpenCode's OpenAI-compatible adapter consumes
|
|
238
|
+
// these natively (including interleaved replay). They must never be copied or
|
|
239
|
+
// concatenated into `content`. Only malformed tool-call parasites are removed.
|
|
242
240
|
// ============================================================================
|
|
243
|
-
const REASONING_KEYS = ['reasoning_content', 'reasoning', 'reasoning_details'];
|
|
244
|
-
function stripReasoning(obj) {
|
|
245
|
-
if (!obj || typeof obj !== 'object')
|
|
246
|
-
return obj;
|
|
247
|
-
if (Array.isArray(obj)) {
|
|
248
|
-
for (const item of obj)
|
|
249
|
-
stripReasoning(item);
|
|
250
|
-
return obj;
|
|
251
|
-
}
|
|
252
|
-
for (const key of REASONING_KEYS) {
|
|
253
|
-
if (key in obj)
|
|
254
|
-
delete obj[key];
|
|
255
|
-
}
|
|
256
|
-
return obj;
|
|
257
|
-
}
|
|
258
241
|
function normalizeToolCallShape(obj) {
|
|
259
242
|
if (!obj || typeof obj !== 'object')
|
|
260
243
|
return obj;
|
|
@@ -283,7 +266,8 @@ function normalizeChatChunk(obj) {
|
|
|
283
266
|
}
|
|
284
267
|
const delta = obj.delta;
|
|
285
268
|
if (delta && typeof delta === 'object') {
|
|
286
|
-
|
|
269
|
+
// Preserve native reasoning fields for OpenCode's OpenAI-compatible adapter.
|
|
270
|
+
// They remain separate from content and can be replayed through interleaved reasoning.
|
|
287
271
|
if (Array.isArray(delta.tool_calls)) {
|
|
288
272
|
for (const tc of delta.tool_calls)
|
|
289
273
|
normalizeToolCallShape(tc);
|
|
@@ -291,7 +275,7 @@ function normalizeChatChunk(obj) {
|
|
|
291
275
|
}
|
|
292
276
|
const message = obj.message;
|
|
293
277
|
if (message && typeof message === 'object') {
|
|
294
|
-
|
|
278
|
+
// Preserve native reasoning fields; never merge them into message.content.
|
|
295
279
|
normalizeToolCallShape(message);
|
|
296
280
|
if (Array.isArray(message.tool_calls)) {
|
|
297
281
|
for (const tc of message.tool_calls)
|
|
@@ -631,13 +615,13 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
631
615
|
isFallbackActive = true;
|
|
632
616
|
fallbackReason = t('proxy.warnings.quota_unreachable_msg');
|
|
633
617
|
}
|
|
634
|
-
else if (quota.walletBalance < (config.thresholds.wallet
|
|
635
|
-
log(`[SafetyNet] Paid Mode: Wallet (~${quota.walletBalance}) < floor (${config.thresholds.wallet
|
|
636
|
-
emitStatusToast('warning', t('proxy.warnings.wallet_limit_title', { wallet: config.thresholds.wallet
|
|
618
|
+
else if (quota.walletBalance < (config.thresholds.wallet ?? 0.5)) {
|
|
619
|
+
log(`[SafetyNet] Paid Mode: Wallet (~${quota.walletBalance}) < floor (${config.thresholds.wallet ?? 0.5}). Switching.`);
|
|
620
|
+
emitStatusToast('warning', t('proxy.warnings.wallet_limit_title', { wallet: config.thresholds.wallet ?? 0.5 }), 'Paid Mode');
|
|
637
621
|
actualModel = config.fallbacks.free.main.replace('free/', '');
|
|
638
622
|
isEnterprise = false;
|
|
639
623
|
isFallbackActive = true;
|
|
640
|
-
fallbackReason = t('proxy.warnings.wallet_limit_msg', { threshold: config.thresholds.wallet
|
|
624
|
+
fallbackReason = t('proxy.warnings.wallet_limit_msg', { threshold: config.thresholds.wallet ?? 0.5 });
|
|
641
625
|
}
|
|
642
626
|
}
|
|
643
627
|
}
|
package/dist/server/quota.js
CHANGED
|
@@ -97,7 +97,7 @@ export async function getQuotaStatus(forceRefresh = false) {
|
|
|
97
97
|
const cleanWalletBalance = Math.max(0, parseFloat(walletBalance.toFixed(4)));
|
|
98
98
|
// Alerts (v6.5): thresholds are absolute pollen floors, not percentages.
|
|
99
99
|
const questNeedsAlert = cleanQuestBalance > 0 && cleanQuestBalance < (config.thresholds.quest ?? 0.05);
|
|
100
|
-
const walletNeedsAlert = cleanWalletBalance > 0 && cleanWalletBalance < (config.thresholds.wallet
|
|
100
|
+
const walletNeedsAlert = cleanWalletBalance > 0 && cleanWalletBalance < (config.thresholds.wallet ?? 0.5);
|
|
101
101
|
logQuota(`Fetch Success. Quest: ${cleanQuestBalance}, Paid: ${cleanWalletBalance}, Total: ${totalBalance}`);
|
|
102
102
|
cachedQuota = {
|
|
103
103
|
questBalance: cleanQuestBalance,
|
package/dist/tools/index.js
CHANGED
|
@@ -52,6 +52,7 @@ function hasValidKey() {
|
|
|
52
52
|
*/
|
|
53
53
|
export function createToolRegistry() {
|
|
54
54
|
const tools = {};
|
|
55
|
+
const config = loadConfig();
|
|
55
56
|
const keyPresent = hasValidKey();
|
|
56
57
|
// === FREE UNIVERSE: Always injected (8 tools) ===
|
|
57
58
|
// Design tools (3)
|
|
@@ -88,8 +89,10 @@ export function createToolRegistry() {
|
|
|
88
89
|
tools['polli_web_search'] = polliWebSearchTool;
|
|
89
90
|
// Cost Guard Confirmation tool
|
|
90
91
|
tools['polli_gen_confirm'] = polliGenConfirmTool;
|
|
91
|
-
// Model API discovery & diagnostics
|
|
92
|
-
|
|
92
|
+
// Model API discovery & diagnostics: developer-only, hidden by default.
|
|
93
|
+
if (config.enableDeveloperTools === true) {
|
|
94
|
+
tools['polli_beta_discovery'] = polliBetaDiscoveryTool;
|
|
95
|
+
}
|
|
93
96
|
// Plugin Configuration editor (Agents)
|
|
94
97
|
tools['polli_config'] = polliConfigTool;
|
|
95
98
|
// Plugin Status / Info / Pricing helper map
|
|
@@ -34,6 +34,7 @@ Use 'action=update' to change these. NEVER confuse Chat Mode with Tools Protecti
|
|
|
34
34
|
statusBar: tool.schema.boolean().optional().describe('Enable/disable status bar visibility (true/false)'),
|
|
35
35
|
costConfirmationRequired: tool.schema.boolean().optional().describe('Safety Lock: Set to true to ask user confirmation before spending money. Set to false to spend automatically.'),
|
|
36
36
|
enablePaidTools: tool.schema.boolean().optional().describe('Allow execution of paid or premium models using Paid pollen (true/false)'),
|
|
37
|
+
enableDeveloperTools: tool.schema.boolean().optional().describe('Expose developer-only API discovery/fuzzing tools after restart (default false).'),
|
|
37
38
|
costThreshold: tool.schema.number().optional().describe('Cost threshold in 🌼 above which confirmation is required'),
|
|
38
39
|
thresholdsQuest: tool.schema.number().optional().describe('Absolute Quest pollen floor (e.g. 0.05) for quest_only fallback.'),
|
|
39
40
|
thresholdsWallet: tool.schema.number().optional().describe('Absolute Paid pollen floor (e.g. 0.5) for paid mode fallback.'),
|
|
@@ -62,6 +63,8 @@ Use 'action=update' to change these. NEVER confuse Chat Mode with Tools Protecti
|
|
|
62
63
|
updates.costConfirmationRequired = args.costConfirmationRequired;
|
|
63
64
|
if (args.enablePaidTools !== undefined)
|
|
64
65
|
updates.enablePaidTools = args.enablePaidTools;
|
|
66
|
+
if (args.enableDeveloperTools !== undefined)
|
|
67
|
+
updates.enableDeveloperTools = args.enableDeveloperTools;
|
|
65
68
|
if (args.costThreshold !== undefined)
|
|
66
69
|
updates.costThreshold = args.costThreshold;
|
|
67
70
|
if (args.lang !== undefined)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-pollinations-plugin",
|
|
3
3
|
"displayName": "Pollinations",
|
|
4
|
-
"version": "6.5.
|
|
4
|
+
"version": "6.5.1",
|
|
5
5
|
"description": "Native Pollinations.ai Provider Plugin for OpenCode — free & premium AI models, media tools, quests, cost guards",
|
|
6
6
|
"author": "fkom13",
|
|
7
7
|
"license": "MIT",
|