opencode-pollinations-plugin 6.1.0-beta.1 → 6.1.0-beta.11
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 +140 -87
- package/dist/index.js +33 -154
- package/dist/server/commands.d.ts +2 -0
- package/dist/server/commands.js +106 -60
- package/dist/server/config.d.ts +27 -23
- package/dist/server/config.js +24 -50
- package/dist/server/generate-config.d.ts +3 -30
- package/dist/server/generate-config.js +172 -100
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.js +124 -149
- package/dist/server/pollinations-api.d.ts +11 -0
- package/dist/server/pollinations-api.js +20 -0
- package/dist/server/proxy.js +187 -149
- package/dist/server/quota.d.ts +8 -0
- package/dist/server/quota.js +106 -61
- package/dist/server/toast.d.ts +3 -0
- package/dist/server/toast.js +16 -0
- package/dist/tools/design/gen_diagram.d.ts +2 -0
- package/dist/tools/design/gen_diagram.js +94 -0
- package/dist/tools/design/gen_palette.d.ts +2 -0
- package/dist/tools/design/gen_palette.js +182 -0
- package/dist/tools/design/gen_qrcode.d.ts +2 -0
- package/dist/tools/design/gen_qrcode.js +50 -0
- package/dist/tools/index.d.ts +22 -0
- package/dist/tools/index.js +81 -0
- package/dist/tools/pollinations/deepsearch.d.ts +7 -0
- package/dist/tools/pollinations/deepsearch.js +80 -0
- package/dist/tools/pollinations/gen_audio.d.ts +18 -0
- package/dist/tools/pollinations/gen_audio.js +204 -0
- package/dist/tools/pollinations/gen_image.d.ts +13 -0
- package/dist/tools/pollinations/gen_image.js +239 -0
- package/dist/tools/pollinations/gen_music.d.ts +14 -0
- package/dist/tools/pollinations/gen_music.js +139 -0
- package/dist/tools/pollinations/gen_video.d.ts +16 -0
- package/dist/tools/pollinations/gen_video.js +222 -0
- package/dist/tools/pollinations/search_crawl_scrape.d.ts +7 -0
- package/dist/tools/pollinations/search_crawl_scrape.js +85 -0
- package/dist/tools/pollinations/shared.d.ts +170 -0
- package/dist/tools/pollinations/shared.js +454 -0
- package/dist/tools/pollinations/transcribe_audio.d.ts +17 -0
- package/dist/tools/pollinations/transcribe_audio.js +235 -0
- package/dist/tools/power/extract_audio.d.ts +2 -0
- package/dist/tools/power/extract_audio.js +180 -0
- package/dist/tools/power/extract_frames.d.ts +2 -0
- package/dist/tools/power/extract_frames.js +240 -0
- package/dist/tools/power/file_to_url.d.ts +2 -0
- package/dist/tools/power/file_to_url.js +217 -0
- package/dist/tools/power/remove_background.d.ts +2 -0
- package/dist/tools/power/remove_background.js +365 -0
- package/dist/tools/power/rmbg_keys.d.ts +2 -0
- package/dist/tools/power/rmbg_keys.js +78 -0
- package/dist/tools/shared.d.ts +30 -0
- package/dist/tools/shared.js +74 -0
- package/package.json +9 -3
- package/dist/server/models-seed.d.ts +0 -18
- package/dist/server/models-seed.js +0 -55
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gen_music Tool - Pollinations Music Generation
|
|
3
|
+
*
|
|
4
|
+
* Updated: 2026-02-12 - Verified API Reference
|
|
5
|
+
*
|
|
6
|
+
* Model: elevenmusic (ElevenLabs Music)
|
|
7
|
+
* Endpoint: gen.pollinations.ai/audio/{text}
|
|
8
|
+
*
|
|
9
|
+
* Parameters:
|
|
10
|
+
* - duration: 3-300 seconds
|
|
11
|
+
* - instrumental: boolean (vocals or instrumental only)
|
|
12
|
+
*/
|
|
13
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import { getApiKey, httpsGet, ensureDir, generateFilename, getDefaultOutputDir, formatCost, formatFileSize, estimateMusicCost, isCostEstimatorEnabled, } from './shared.js';
|
|
17
|
+
// ─── Constants ─────────────────────────────────────────────────────────────
|
|
18
|
+
const MIN_DURATION = 3;
|
|
19
|
+
const MAX_DURATION = 300; // 5 minutes
|
|
20
|
+
const DEFAULT_DURATION = 10;
|
|
21
|
+
const MODEL_NAME = 'elevenmusic';
|
|
22
|
+
// ─── Tool Definition ──────────────────────────────────────────────────────
|
|
23
|
+
export const genMusicTool = tool({
|
|
24
|
+
description: `Generate music from a text description using Pollinations AI.
|
|
25
|
+
|
|
26
|
+
**🎵 Model:** elevenmusic (ElevenLabs Music)
|
|
27
|
+
|
|
28
|
+
**📝 Parameters:**
|
|
29
|
+
- \`duration\`: 3-300 seconds (default: 10s)
|
|
30
|
+
- \`instrumental\`: true = no vocals, false = vocals allowed
|
|
31
|
+
|
|
32
|
+
**💡 Example Prompts:**
|
|
33
|
+
- "upbeat jazz with saxophone solo"
|
|
34
|
+
- "ambient electronic for meditation"
|
|
35
|
+
- "epic orchestral film score with dramatic strings"
|
|
36
|
+
- "lo-fi hip hop beats with piano"
|
|
37
|
+
- "acoustic guitar ballad with soft vocals"
|
|
38
|
+
- "electronic dance music with heavy bass drop"
|
|
39
|
+
|
|
40
|
+
**💰 Cost:** ~0.005 🌻 per second
|
|
41
|
+
- 10 seconds ≈ 0.05 🌻
|
|
42
|
+
- 30 seconds ≈ 0.15 🌻
|
|
43
|
+
- 60 seconds ≈ 0.30 🌻
|
|
44
|
+
|
|
45
|
+
**⚠️ Notes:**
|
|
46
|
+
- Generation time scales with duration (~1s per second of audio)
|
|
47
|
+
- Longer tracks (60s+) may take 1-2 minutes
|
|
48
|
+
- Instrumental mode produces cleaner results for background music`,
|
|
49
|
+
args: {
|
|
50
|
+
prompt: tool.schema.string().describe('Description of the music to generate'),
|
|
51
|
+
duration: tool.schema.number().min(MIN_DURATION).max(MAX_DURATION).optional()
|
|
52
|
+
.describe(`Duration in seconds (default: ${DEFAULT_DURATION}, max: ${MAX_DURATION})`),
|
|
53
|
+
instrumental: tool.schema.boolean().optional().describe('Instrumental only - no vocals (default: false)'),
|
|
54
|
+
seed: tool.schema.number().optional().describe('Seed for reproducibility (-1 for random)'),
|
|
55
|
+
save_to: tool.schema.string().optional().describe('Custom output directory'),
|
|
56
|
+
filename: tool.schema.string().optional().describe('Custom filename (without extension)'),
|
|
57
|
+
},
|
|
58
|
+
async execute(args, context) {
|
|
59
|
+
const apiKey = getApiKey();
|
|
60
|
+
if (!apiKey) {
|
|
61
|
+
return `❌ La génération musicale nécessite une clé API Pollinations.
|
|
62
|
+
🔧 Connectez votre clé avec /pollinations connect`;
|
|
63
|
+
}
|
|
64
|
+
const duration = Math.min(args.duration || DEFAULT_DURATION, MAX_DURATION);
|
|
65
|
+
const instrumental = args.instrumental || false;
|
|
66
|
+
// Estimate cost
|
|
67
|
+
const estimatedCost = estimateMusicCost(duration);
|
|
68
|
+
// Estimate generation time
|
|
69
|
+
const genTimeSeconds = Math.ceil(duration * 1.2); // ~1.2s per second of audio
|
|
70
|
+
// Metadata
|
|
71
|
+
context.metadata({ title: `🎵 Music: ${duration}s (~${genTimeSeconds}s gen time)` });
|
|
72
|
+
try {
|
|
73
|
+
// Build URL
|
|
74
|
+
const params = new URLSearchParams({
|
|
75
|
+
model: MODEL_NAME,
|
|
76
|
+
nologo: 'true',
|
|
77
|
+
private: 'true',
|
|
78
|
+
duration: String(duration),
|
|
79
|
+
});
|
|
80
|
+
if (instrumental) {
|
|
81
|
+
params.set('instrumental', 'true');
|
|
82
|
+
}
|
|
83
|
+
// Seed for reproducibility
|
|
84
|
+
if (args.seed !== undefined) {
|
|
85
|
+
params.set('seed', String(args.seed));
|
|
86
|
+
}
|
|
87
|
+
const promptEncoded = encodeURIComponent(args.prompt);
|
|
88
|
+
const url = `https://gen.pollinations.ai/audio/${promptEncoded}?${params}`;
|
|
89
|
+
const headers = {
|
|
90
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
91
|
+
};
|
|
92
|
+
// Music generation takes time
|
|
93
|
+
const result = await httpsGet(url, headers);
|
|
94
|
+
const audioData = result.data;
|
|
95
|
+
const responseHeaders = result.headers;
|
|
96
|
+
// Save audio
|
|
97
|
+
const outputDir = args.save_to || getDefaultOutputDir('music');
|
|
98
|
+
ensureDir(outputDir);
|
|
99
|
+
const filename = args.filename || generateFilename('music', MODEL_NAME, 'mp3');
|
|
100
|
+
const filePath = path.join(outputDir, filename.endsWith('.mp3') ? filename : `${filename}.mp3`);
|
|
101
|
+
fs.writeFileSync(filePath, audioData);
|
|
102
|
+
const fileSize = fs.statSync(filePath).size;
|
|
103
|
+
// Build result
|
|
104
|
+
const lines = [
|
|
105
|
+
`🎵 Musique Générée`,
|
|
106
|
+
`━━━━━━━━━━━━━━━━━━`,
|
|
107
|
+
`Prompt: ${args.prompt}`,
|
|
108
|
+
`Durée: ~${duration}s`,
|
|
109
|
+
`Mode: ${instrumental ? 'Instrumental (sans voix)' : 'Avec voix possible'}`,
|
|
110
|
+
`Fichier: ${filePath}`,
|
|
111
|
+
`Taille: ${formatFileSize(fileSize)}`,
|
|
112
|
+
];
|
|
113
|
+
// Cost info
|
|
114
|
+
if (isCostEstimatorEnabled()) {
|
|
115
|
+
lines.push(`Coût estimé: ${formatCost(estimatedCost)}`);
|
|
116
|
+
}
|
|
117
|
+
if (responseHeaders['x-model-used']) {
|
|
118
|
+
lines.push(`Modèle utilisé: ${responseHeaders['x-model-used']}`);
|
|
119
|
+
}
|
|
120
|
+
if (responseHeaders['x-request-id']) {
|
|
121
|
+
lines.push(`Request ID: ${responseHeaders['x-request-id']}`);
|
|
122
|
+
}
|
|
123
|
+
return lines.join('\n');
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
if (err.message?.includes('402') || err.message?.includes('Payment')) {
|
|
127
|
+
return `❌ Crédits insuffisants.`;
|
|
128
|
+
}
|
|
129
|
+
if (err.message?.includes('401') || err.message?.includes('403')) {
|
|
130
|
+
return `❌ Clé API invalide ou non autorisée.`;
|
|
131
|
+
}
|
|
132
|
+
if (err.message?.includes('Timeout')) {
|
|
133
|
+
return `❌ Timeout - La génération musicale a pris trop de temps.
|
|
134
|
+
💡 Essayez une durée plus courte.`;
|
|
135
|
+
}
|
|
136
|
+
return `❌ Erreur génération musicale: ${err.message}`;
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gen_video Tool - Pollinations Video Generation
|
|
3
|
+
*
|
|
4
|
+
* Updated: 2026-02-12 - Verified API Reference
|
|
5
|
+
*
|
|
6
|
+
* Video models with different capabilities:
|
|
7
|
+
* - T2V (Text-to-Video): grok-video, ltx-2, veo, seedance, seedance-pro
|
|
8
|
+
* - I2V (Image-to-Video): wan (I2V ONLY!), veo, seedance, seedance-pro
|
|
9
|
+
* - Veo Interpolation: Uses image=url1,url2 for transitions
|
|
10
|
+
*
|
|
11
|
+
* Response headers for cost tracking:
|
|
12
|
+
* - x-usage-completion-video-seconds (grok, ltx-2, veo, wan)
|
|
13
|
+
* - x-usage-completion-video-tokens (seedance, seedance-pro)
|
|
14
|
+
*/
|
|
15
|
+
import { type ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
16
|
+
export declare const genVideoTool: ToolDefinition;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gen_video Tool - Pollinations Video Generation
|
|
3
|
+
*
|
|
4
|
+
* Updated: 2026-02-12 - Verified API Reference
|
|
5
|
+
*
|
|
6
|
+
* Video models with different capabilities:
|
|
7
|
+
* - T2V (Text-to-Video): grok-video, ltx-2, veo, seedance, seedance-pro
|
|
8
|
+
* - I2V (Image-to-Video): wan (I2V ONLY!), veo, seedance, seedance-pro
|
|
9
|
+
* - Veo Interpolation: Uses image=url1,url2 for transitions
|
|
10
|
+
*
|
|
11
|
+
* Response headers for cost tracking:
|
|
12
|
+
* - x-usage-completion-video-seconds (grok, ltx-2, veo, wan)
|
|
13
|
+
* - x-usage-completion-video-tokens (seedance, seedance-pro)
|
|
14
|
+
*/
|
|
15
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import * as path from 'path';
|
|
18
|
+
import { getApiKey, httpsGet, ensureDir, generateFilename, getDefaultOutputDir, formatCost, formatFileSize, estimateVideoCost, extractCostFromHeaders, isCostEstimatorEnabled, supportsI2V, requiresI2V, validateAspectRatio, getDurationRange, VIDEO_MODELS, } from './shared.js';
|
|
19
|
+
// ─── Constants ─────────────────────────────────────────────────────────────
|
|
20
|
+
const CHEAPEST_MODEL = 'grok-video';
|
|
21
|
+
const DEFAULT_DURATION = 3;
|
|
22
|
+
const DEFAULT_ASPECT_RATIO = '16:9';
|
|
23
|
+
// ─── Tool Definition ──────────────────────────────────────────────────────
|
|
24
|
+
export const genVideoTool = tool({
|
|
25
|
+
description: `Generate a video from a text prompt or image using Pollinations AI.
|
|
26
|
+
|
|
27
|
+
**🎬 Available Models:**
|
|
28
|
+
|
|
29
|
+
| Model | T2V | I2V | Audio | Duration | Aspect Ratios | Cost | Gen Time |
|
|
30
|
+
|-------|-----|-----|-------|----------|---------------|------|----------|
|
|
31
|
+
| grok-video | ✅ | ❌ | ✅ | 1-15s | 16:9, 9:16, 1:1, 4:3 | 0.0025/s | ~10s |
|
|
32
|
+
| ltx-2 | ✅ | ❌ | ✅ | 5-20s | 16:9 | 0.01/s | ~35s |
|
|
33
|
+
| wan | ❌ | ✅ | ✅ | 5-15s | 16:9, 9:16, 1:1, 4:3 | 0.025/s | ~30s |
|
|
34
|
+
| veo | ✅ | ✅ | ✅ | 4-8s | 16:9, 9:16, 1:1 | 0.15/s 💎 | ~45-68s |
|
|
35
|
+
| seedance | ✅ | ✅ | ❌ | 4-12s | 16:9, 9:16, 1:1 | tokens | ~30s |
|
|
36
|
+
| seedance-pro | ✅ | ✅ | ❌ | 4-12s | 16:9, 9:16, 1:1 | tokens | ~30s |
|
|
37
|
+
|
|
38
|
+
**⚠️ Important Notes:**
|
|
39
|
+
- \`wan\` = I2V **ONLY** (Text-to-Video NOT supported!)
|
|
40
|
+
- \`veo\` interpolation: Use \`reference_image=url1,url2\` for transitions
|
|
41
|
+
- \`ltx-2\` may return 520 intermittently (retry OK)
|
|
42
|
+
- \`grok-video\` includes audio generation
|
|
43
|
+
|
|
44
|
+
**💡 Tips:**
|
|
45
|
+
- Start with \`grok-video\` for testing (cheapest: 0.0025/sec)
|
|
46
|
+
- Use \`wan\` for image-to-video with native audio
|
|
47
|
+
- Use \`veo\` for highest quality (most expensive: 0.15/sec)`,
|
|
48
|
+
args: {
|
|
49
|
+
prompt: tool.schema.string().describe('Description of the video to generate'),
|
|
50
|
+
model: tool.schema.string().optional().describe(`Video model (default: ${CHEAPEST_MODEL})`),
|
|
51
|
+
duration: tool.schema.number().min(1).max(20).optional().describe('Duration in seconds (default: 3, varies by model)'),
|
|
52
|
+
aspect_ratio: tool.schema.enum(['16:9', '9:16', '1:1', '4:3']).optional().describe('Aspect ratio (default: 16:9, varies by model)'),
|
|
53
|
+
reference_image: tool.schema.string().optional().describe('URL for I2V (required for wan) or comma-separated URLs for veo interpolation'),
|
|
54
|
+
seed: tool.schema.number().optional().describe('Seed for reproducibility (-1 for random)'),
|
|
55
|
+
save_to: tool.schema.string().optional().describe('Custom output directory'),
|
|
56
|
+
filename: tool.schema.string().optional().describe('Custom filename (without extension)'),
|
|
57
|
+
},
|
|
58
|
+
async execute(args, context) {
|
|
59
|
+
const apiKey = getApiKey();
|
|
60
|
+
if (!apiKey) {
|
|
61
|
+
return `❌ La génération vidéo nécessite une clé API Pollinations.
|
|
62
|
+
🔧 Connectez votre clé avec /pollinations connect`;
|
|
63
|
+
}
|
|
64
|
+
const model = args.model || CHEAPEST_MODEL;
|
|
65
|
+
const aspectRatio = args.aspect_ratio || DEFAULT_ASPECT_RATIO;
|
|
66
|
+
// Get model config
|
|
67
|
+
const modelConfig = VIDEO_MODELS[model];
|
|
68
|
+
if (!modelConfig) {
|
|
69
|
+
return `❌ Modèle inconnu: ${model}
|
|
70
|
+
💡 Modèles disponibles: ${Object.keys(VIDEO_MODELS).join(', ')}`;
|
|
71
|
+
}
|
|
72
|
+
// Validate duration
|
|
73
|
+
const [minDuration, maxDuration] = getDurationRange(model);
|
|
74
|
+
const duration = args.duration || Math.min(DEFAULT_DURATION, maxDuration);
|
|
75
|
+
if (duration < minDuration || duration > maxDuration) {
|
|
76
|
+
return `❌ Durée invalide pour ${model}: ${duration}s
|
|
77
|
+
💡 Durée supportée: ${minDuration}-${maxDuration}s`;
|
|
78
|
+
}
|
|
79
|
+
// Validate aspect ratio
|
|
80
|
+
if (!validateAspectRatio(model, aspectRatio)) {
|
|
81
|
+
return `❌ Aspect ratio non supporté par ${model}: ${aspectRatio}
|
|
82
|
+
💡 Ratios supportés: ${modelConfig.aspectRatios.join(', ')}`;
|
|
83
|
+
}
|
|
84
|
+
// Check I2V requirements
|
|
85
|
+
const requiresReferenceImage = requiresI2V(model);
|
|
86
|
+
const supportsReferenceImage = supportsI2V(model);
|
|
87
|
+
if (requiresReferenceImage && !args.reference_image) {
|
|
88
|
+
return `❌ Le modèle "${model}" nécessite une image de départ (I2V ONLY).
|
|
89
|
+
💡 Ajoutez --reference_image <url>
|
|
90
|
+
💡 Pour du T2V, utilisez: grok-video, ltx-2, veo, seedance`;
|
|
91
|
+
}
|
|
92
|
+
if (args.reference_image && !supportsReferenceImage) {
|
|
93
|
+
return `⚠️ Le modèle "${model}" ne supporte pas l'I2V.
|
|
94
|
+
💡 Modèles I2V: ${Object.entries(VIDEO_MODELS)
|
|
95
|
+
.filter(([, info]) => info.i2v)
|
|
96
|
+
.map(([name]) => name)
|
|
97
|
+
.join(', ')}`;
|
|
98
|
+
}
|
|
99
|
+
// Estimate cost
|
|
100
|
+
const estimatedCost = estimateVideoCost(model, duration);
|
|
101
|
+
// Metadata
|
|
102
|
+
context.metadata({ title: `🎬 Video: ${model} (${duration}s)` });
|
|
103
|
+
try {
|
|
104
|
+
// Build URL
|
|
105
|
+
const params = new URLSearchParams({
|
|
106
|
+
model: model,
|
|
107
|
+
nologo: 'true',
|
|
108
|
+
private: 'true',
|
|
109
|
+
});
|
|
110
|
+
// Duration parameter
|
|
111
|
+
params.set('duration', String(duration));
|
|
112
|
+
// Aspect ratio - convert to width/height for API
|
|
113
|
+
const aspectToSize = {
|
|
114
|
+
'16:9': { w: 1920, h: 1080 },
|
|
115
|
+
'9:16': { w: 1080, h: 1920 },
|
|
116
|
+
'1:1': { w: 1024, h: 1024 },
|
|
117
|
+
'4:3': { w: 1440, h: 1080 },
|
|
118
|
+
};
|
|
119
|
+
const size = aspectToSize[aspectRatio] || aspectToSize['16:9'];
|
|
120
|
+
params.set('width', String(size.w));
|
|
121
|
+
params.set('height', String(size.h));
|
|
122
|
+
// I2V: reference image(s)
|
|
123
|
+
if (args.reference_image) {
|
|
124
|
+
// Veo interpolation: comma-separated URLs
|
|
125
|
+
// Other I2V models: single URL
|
|
126
|
+
params.set('image', args.reference_image);
|
|
127
|
+
}
|
|
128
|
+
// Seed for reproducibility
|
|
129
|
+
if (args.seed !== undefined) {
|
|
130
|
+
params.set('seed', String(args.seed));
|
|
131
|
+
}
|
|
132
|
+
const promptEncoded = encodeURIComponent(args.prompt);
|
|
133
|
+
const url = `https://gen.pollinations.ai/image/${promptEncoded}?${params}`;
|
|
134
|
+
const headers = {
|
|
135
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
136
|
+
};
|
|
137
|
+
// Video generation takes time (30-70 seconds depending on model)
|
|
138
|
+
const result = await httpsGet(url, headers);
|
|
139
|
+
const videoData = result.data;
|
|
140
|
+
const responseHeaders = result.headers;
|
|
141
|
+
// Save video
|
|
142
|
+
const outputDir = args.save_to || getDefaultOutputDir('videos');
|
|
143
|
+
ensureDir(outputDir);
|
|
144
|
+
const filename = args.filename || generateFilename('video', model, 'mp4');
|
|
145
|
+
const filePath = path.join(outputDir, filename.endsWith('.mp4') ? filename : `${filename}.mp4`);
|
|
146
|
+
fs.writeFileSync(filePath, videoData);
|
|
147
|
+
const fileSize = fs.statSync(filePath).size;
|
|
148
|
+
// Extract actual cost from headers
|
|
149
|
+
let actualCost = estimatedCost;
|
|
150
|
+
const costTracking = extractCostFromHeaders(responseHeaders);
|
|
151
|
+
if (isCostEstimatorEnabled()) {
|
|
152
|
+
if (costTracking.videoSeconds) {
|
|
153
|
+
// Calculate from actual seconds
|
|
154
|
+
const costMatch = modelConfig.cost.match(/[\d.]+/);
|
|
155
|
+
if (costMatch && modelConfig.costHeader === 'x-usage-completion-video-seconds') {
|
|
156
|
+
actualCost = costTracking.videoSeconds * parseFloat(costMatch[0]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else if (costTracking.videoTokens) {
|
|
160
|
+
// Token-based cost (seedance models)
|
|
161
|
+
actualCost = costTracking.videoTokens * 0.00001; // Approximate
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Build result
|
|
165
|
+
const lines = [
|
|
166
|
+
`🎬 Vidéo Générée`,
|
|
167
|
+
`━━━━━━━━━━━━━━━━━━`,
|
|
168
|
+
`Prompt: ${args.prompt.substring(0, 80)}${args.prompt.length > 80 ? '...' : ''}`,
|
|
169
|
+
`Modèle: ${model}${modelConfig.cost.includes('💎') ? ' 💎' : ''}`,
|
|
170
|
+
`Durée: ~${duration}s`,
|
|
171
|
+
`Aspect: ${aspectRatio}`,
|
|
172
|
+
];
|
|
173
|
+
// Add I2V info if used
|
|
174
|
+
if (args.reference_image) {
|
|
175
|
+
const isInterpolation = model === 'veo' && args.reference_image.includes(',');
|
|
176
|
+
lines.push(`I2V Mode: ${isInterpolation ? 'Interpolation (multi-image)' : 'Single image'}`);
|
|
177
|
+
lines.push(`Source: ${args.reference_image.substring(0, 50)}...`);
|
|
178
|
+
}
|
|
179
|
+
// Audio info
|
|
180
|
+
if (modelConfig.audio) {
|
|
181
|
+
lines.push(`Audio: ✅ Généré automatiquement`);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
lines.push(`Audio: ❌ Non supporté par ce modèle`);
|
|
185
|
+
}
|
|
186
|
+
lines.push(`Fichier: ${filePath}`);
|
|
187
|
+
lines.push(`Taille: ${formatFileSize(fileSize)}`);
|
|
188
|
+
lines.push(`Coût estimé: ${formatCost(actualCost)}`);
|
|
189
|
+
if (responseHeaders['x-model-used']) {
|
|
190
|
+
lines.push(`Modèle utilisé: ${responseHeaders['x-model-used']}`);
|
|
191
|
+
}
|
|
192
|
+
if (responseHeaders['x-request-id']) {
|
|
193
|
+
lines.push(`Request ID: ${responseHeaders['x-request-id']}`);
|
|
194
|
+
}
|
|
195
|
+
// Gen time estimate
|
|
196
|
+
lines.push(`⏱️ Temps de génération: ${modelConfig.genTime}`);
|
|
197
|
+
return lines.join('\n');
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
if (err.message?.includes('402') || err.message?.includes('Payment')) {
|
|
201
|
+
return `❌ Crédits insuffisants.
|
|
202
|
+
💡 Essayez \`grok-video\` (le moins cher: 0.0025/sec)`;
|
|
203
|
+
}
|
|
204
|
+
if (err.message?.includes('400')) {
|
|
205
|
+
if (requiresI2V(model) && !args.reference_image) {
|
|
206
|
+
return `❌ Le modèle "${model}" est I2V ONLY.
|
|
207
|
+
💡 Ajoutez --reference_image <url>`;
|
|
208
|
+
}
|
|
209
|
+
return `❌ Paramètres invalides: ${err.message}`;
|
|
210
|
+
}
|
|
211
|
+
if (err.message?.includes('520') && model === 'ltx-2') {
|
|
212
|
+
return `⚠️ LTX-2 a retourné une erreur 520 (intermittent).
|
|
213
|
+
💡 Réessayez dans quelques secondes.`;
|
|
214
|
+
}
|
|
215
|
+
if (err.message?.includes('Timeout')) {
|
|
216
|
+
return `❌ Timeout - La génération vidéo a pris trop de temps.
|
|
217
|
+
💡 Réessayez avec une durée plus courte.`;
|
|
218
|
+
}
|
|
219
|
+
return `❌ Erreur génération vidéo: ${err.message}`;
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* search_crawl_scrape Tool - Web Search and Content Extraction
|
|
3
|
+
*
|
|
4
|
+
* Uses perplexity-fast for quick web search with sources
|
|
5
|
+
*/
|
|
6
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
7
|
+
import { getApiKey, httpsPost, } from './shared.js';
|
|
8
|
+
// ─── Tool Definition ──────────────────────────────────────────────────────
|
|
9
|
+
export const searchCrawlScrapeTool = tool({
|
|
10
|
+
description: `Search the web and extract information quickly.
|
|
11
|
+
|
|
12
|
+
**Model:** perplexity-fast
|
|
13
|
+
|
|
14
|
+
**Features:**
|
|
15
|
+
- Real-time web search
|
|
16
|
+
- Source citations
|
|
17
|
+
- Quick summaries
|
|
18
|
+
- Current information
|
|
19
|
+
|
|
20
|
+
**Use for:**
|
|
21
|
+
- Quick fact lookups
|
|
22
|
+
- Current news/events
|
|
23
|
+
- Documentation search
|
|
24
|
+
- General web queries
|
|
25
|
+
|
|
26
|
+
**Cost:** ~0.000001 🌻 per token (very cheap)`,
|
|
27
|
+
args: {
|
|
28
|
+
query: tool.schema.string().describe('Search query'),
|
|
29
|
+
include_sources: tool.schema.boolean().optional()
|
|
30
|
+
.describe('Include source URLs in response (default: true)'),
|
|
31
|
+
recency: tool.schema.enum(['any', 'day', 'week', 'month']).optional()
|
|
32
|
+
.describe('Filter by recency (default: any)'),
|
|
33
|
+
},
|
|
34
|
+
async execute(args, context) {
|
|
35
|
+
const apiKey = getApiKey();
|
|
36
|
+
if (!apiKey) {
|
|
37
|
+
return `❌ Web Search nécessite une clé API Pollinations.
|
|
38
|
+
🔧 Connectez votre clé avec /pollinations connect`;
|
|
39
|
+
}
|
|
40
|
+
const model = 'perplexity-fast';
|
|
41
|
+
const includeSources = args.include_sources !== false;
|
|
42
|
+
// Build recency hint
|
|
43
|
+
const recencyHints = {
|
|
44
|
+
any: '',
|
|
45
|
+
day: 'Focus on information from the last 24 hours. ',
|
|
46
|
+
week: 'Focus on information from the last week. ',
|
|
47
|
+
month: 'Focus on information from the last month. ',
|
|
48
|
+
};
|
|
49
|
+
// Metadata
|
|
50
|
+
context.metadata({ title: `🔎 Search: ${args.query.substring(0, 40)}...` });
|
|
51
|
+
try {
|
|
52
|
+
const systemPrompt = `You are a web search assistant. Provide concise, accurate answers based on web search results.
|
|
53
|
+
${recencyHints[args.recency || 'any']}
|
|
54
|
+
${includeSources ? 'Always include source URLs at the end of your response.' : ''}`;
|
|
55
|
+
const { data } = await httpsPost('https://gen.pollinations.ai/v1/chat/completions', {
|
|
56
|
+
model: model,
|
|
57
|
+
messages: [
|
|
58
|
+
{ role: 'system', content: systemPrompt },
|
|
59
|
+
{ role: 'user', content: args.query },
|
|
60
|
+
],
|
|
61
|
+
max_tokens: 2000,
|
|
62
|
+
}, {
|
|
63
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
64
|
+
});
|
|
65
|
+
const jsonData = JSON.parse(data.toString());
|
|
66
|
+
const content = jsonData.choices?.[0]?.message?.content || 'No results found';
|
|
67
|
+
// Format result
|
|
68
|
+
const lines = [
|
|
69
|
+
`🔎 Web Search Results`,
|
|
70
|
+
`━━━━━━━━━━━━━━━━━━`,
|
|
71
|
+
`Query: ${args.query}`,
|
|
72
|
+
`Model: ${model}`,
|
|
73
|
+
``,
|
|
74
|
+
content,
|
|
75
|
+
];
|
|
76
|
+
return lines.join('\n');
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
if (err.message?.includes('402') || err.message?.includes('Payment')) {
|
|
80
|
+
return `❌ Crédits insuffisants.`;
|
|
81
|
+
}
|
|
82
|
+
return `❌ Erreur Web Search: ${err.message}`;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
});
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for Pollinations API tools
|
|
3
|
+
*
|
|
4
|
+
* Updated: 2026-02-12 - Verified API Reference
|
|
5
|
+
* Tests: 18/18 passed
|
|
6
|
+
*/
|
|
7
|
+
export interface PollinationsConfig {
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
mode?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ModelInfo {
|
|
12
|
+
name: string;
|
|
13
|
+
pricing: {
|
|
14
|
+
currency: string;
|
|
15
|
+
completionImageTokens?: number;
|
|
16
|
+
completionVideoSeconds?: number;
|
|
17
|
+
completionVideoTokens?: number;
|
|
18
|
+
completionAudioTokens?: number;
|
|
19
|
+
completionAudioSeconds?: number;
|
|
20
|
+
promptAudioSeconds?: number;
|
|
21
|
+
promptTextTokens?: number;
|
|
22
|
+
completionTextTokens?: number;
|
|
23
|
+
};
|
|
24
|
+
paid_only?: boolean;
|
|
25
|
+
input_modalities?: string[];
|
|
26
|
+
output_modalities?: string[];
|
|
27
|
+
description?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface GenerationResult {
|
|
30
|
+
success: boolean;
|
|
31
|
+
url?: string;
|
|
32
|
+
localPath?: string;
|
|
33
|
+
cost: number;
|
|
34
|
+
model: string;
|
|
35
|
+
error?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface CostTracking {
|
|
38
|
+
imageTokens?: number;
|
|
39
|
+
videoSeconds?: number;
|
|
40
|
+
videoTokens?: number;
|
|
41
|
+
modelUsed?: string;
|
|
42
|
+
requestId?: string;
|
|
43
|
+
}
|
|
44
|
+
export declare function getApiKey(): string | undefined;
|
|
45
|
+
export declare function hasApiKey(): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* FREE Image Models (image.pollinations.ai/models)
|
|
48
|
+
* WARNING: flux removed from free, turbo broken (shows notice)
|
|
49
|
+
*/
|
|
50
|
+
export declare const FREE_IMAGE_MODELS: {
|
|
51
|
+
sana: {
|
|
52
|
+
desc: string;
|
|
53
|
+
fileSize: string;
|
|
54
|
+
reliable: boolean;
|
|
55
|
+
};
|
|
56
|
+
zimage: {
|
|
57
|
+
desc: string;
|
|
58
|
+
fileSize: string;
|
|
59
|
+
reliable: boolean;
|
|
60
|
+
};
|
|
61
|
+
turbo: {
|
|
62
|
+
desc: string;
|
|
63
|
+
fileSize: string;
|
|
64
|
+
reliable: boolean;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Paid Image Models (gen.pollinations.ai)
|
|
69
|
+
* I2I = Image-to-Image support
|
|
70
|
+
*/
|
|
71
|
+
export declare const PAID_IMAGE_MODELS: Record<string, {
|
|
72
|
+
desc: string;
|
|
73
|
+
cost: string;
|
|
74
|
+
t2i: boolean;
|
|
75
|
+
i2i: boolean;
|
|
76
|
+
params: string[];
|
|
77
|
+
notes?: string;
|
|
78
|
+
}>;
|
|
79
|
+
/**
|
|
80
|
+
* Video Models (gen.pollinations.ai)
|
|
81
|
+
* T2V = Text-to-Video, I2V = Image-to-Video
|
|
82
|
+
*/
|
|
83
|
+
export declare const VIDEO_MODELS: Record<string, {
|
|
84
|
+
desc: string;
|
|
85
|
+
cost: string;
|
|
86
|
+
t2v: boolean;
|
|
87
|
+
i2v: boolean;
|
|
88
|
+
audio: boolean;
|
|
89
|
+
duration: [number, number];
|
|
90
|
+
aspectRatios: string[];
|
|
91
|
+
costHeader: string;
|
|
92
|
+
genTime: string;
|
|
93
|
+
}>;
|
|
94
|
+
/**
|
|
95
|
+
* Audio Models
|
|
96
|
+
* TTS = Text-to-Speech, STT = Speech-to-Text
|
|
97
|
+
*/
|
|
98
|
+
export declare const AUDIO_MODELS: Record<string, {
|
|
99
|
+
desc: string;
|
|
100
|
+
type: 'tts' | 'stt' | 'both';
|
|
101
|
+
endpoint: string;
|
|
102
|
+
params: string[];
|
|
103
|
+
voices?: string[];
|
|
104
|
+
notes?: string;
|
|
105
|
+
}>;
|
|
106
|
+
/**
|
|
107
|
+
* Music Model (separate tool)
|
|
108
|
+
*/
|
|
109
|
+
export declare const MUSIC_MODEL: {
|
|
110
|
+
elevenmusic: {
|
|
111
|
+
desc: string;
|
|
112
|
+
endpoint: string;
|
|
113
|
+
params: string[];
|
|
114
|
+
duration: number[];
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
export declare function httpsGet(url: string, headers?: Record<string, string>): Promise<{
|
|
118
|
+
data: Buffer;
|
|
119
|
+
headers: Record<string, string>;
|
|
120
|
+
}>;
|
|
121
|
+
export declare function httpsPost(url: string, body: any, headers?: Record<string, string>): Promise<{
|
|
122
|
+
data: Buffer;
|
|
123
|
+
headers: Record<string, string>;
|
|
124
|
+
}>;
|
|
125
|
+
/**
|
|
126
|
+
* Multipart POST for file uploads (STT)
|
|
127
|
+
*/
|
|
128
|
+
export declare function httpsPostMultipart(url: string, fields: Record<string, string | Buffer>, headers?: Record<string, string>): Promise<{
|
|
129
|
+
data: Buffer;
|
|
130
|
+
headers: Record<string, string>;
|
|
131
|
+
}>;
|
|
132
|
+
export declare function fetchModels(type: 'image' | 'audio' | 'text'): Promise<ModelInfo[]>;
|
|
133
|
+
export declare function getModelInfo(type: 'image' | 'audio' | 'text', name: string): Promise<ModelInfo | undefined>;
|
|
134
|
+
/**
|
|
135
|
+
* Extract cost tracking from response headers
|
|
136
|
+
*/
|
|
137
|
+
export declare function extractCostFromHeaders(headers: Record<string, string>): CostTracking;
|
|
138
|
+
/**
|
|
139
|
+
* Check if cost estimator is enabled in config
|
|
140
|
+
*/
|
|
141
|
+
export declare function isCostEstimatorEnabled(): boolean;
|
|
142
|
+
export declare function estimateImageCost(model: string): number;
|
|
143
|
+
export declare function estimateVideoCost(model: string, duration: number): number;
|
|
144
|
+
export declare function estimateTtsCost(textLength: number): number;
|
|
145
|
+
export declare function estimateMusicCost(duration: number): number;
|
|
146
|
+
export declare function ensureDir(dir: string): void;
|
|
147
|
+
export declare function generateFilename(type: string, model: string, ext: string): string;
|
|
148
|
+
export declare function getDefaultOutputDir(type: string): string;
|
|
149
|
+
export declare function formatCost(cost: number): string;
|
|
150
|
+
export declare function formatFileSize(bytes: number): string;
|
|
151
|
+
/**
|
|
152
|
+
* Check if model supports Image-to-Image
|
|
153
|
+
*/
|
|
154
|
+
export declare function supportsI2I(model: string): boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Check if video model supports Image-to-Video
|
|
157
|
+
*/
|
|
158
|
+
export declare function supportsI2V(model: string): boolean;
|
|
159
|
+
/**
|
|
160
|
+
* Check if video model requires Image-to-Video (no T2V)
|
|
161
|
+
*/
|
|
162
|
+
export declare function requiresI2V(model: string): boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Validate aspect ratio for video model
|
|
165
|
+
*/
|
|
166
|
+
export declare function validateAspectRatio(model: string, ratio: string): boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Get valid duration range for video model
|
|
169
|
+
*/
|
|
170
|
+
export declare function getDurationRange(model: string): [number, number];
|