speech-to-text
Skill15 starsChanged 58 days agoMITView on GitHub
Transcribe audio files using 0G Compute Network providers (serviceType: speech-to-text). Supports multiple audio formats and output types (JSON, text, SRT subtitles). At last verification 0G mainnet had one speech-to-text provider serving openai/whisper-large-v3. Testnet had none. Discover at runtime rather than hardcoding. Speech-to-text providers bill in one of two ways, and the usage object in the response tells you which. Pass it through to processResponse() verbatim — do not normalise or reshape it: Because both shapes are possible, never hand-build the…
- Reads credentials
- Sends data out
19 other projects use a version of this.
Adopted by
Most recently taken up by
- OpenMontageInc/OpenMontagetheir own version, first seen Nov 8, 2026
- calesthio/OpenMontagetheir own version, first seen Nov 8, 2026
- imMamdouhaboammar/motion-graphics-skillstheir own version, first seen Nov 1, 2026
- wsy1022/china-audit-toolkittheir own version, first seen Nov 1, 2026
- deepgram/skillstheir own version, first seen Oct 25, 2026
- elevenlabs/skillstheir own version, first seen Oct 19, 2026
Used with
Projects with it also use speech-engine, voice-isolator and 6 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
RobStrayerSkillspeech-engine
Add real-time voice conversations to a custom agent runtime with ElevenLabs Speech Engine. Use when building Speech Engine servers, WebSocket handlers, WebRTC browser clients, conversation token endpoints, interruption-aware streaming responses, or voice-enabled chat agents that connect developer-owned server logic to ElevenLabs speech-to-text and text-to-speech.
Also in 3 projects that use this
RobStrayerSkillvoice-isolator
Remove background noise and isolate vocals/speech from audio using ElevenLabs Voice Isolator (audio isolation) API. Use when cleaning up noisy recordings, removing music or background ambience from dialogue, isolating speech from field recordings, preparing audio for transcription, extracting vocals, or any "denoise / clean up / isolate voice" task.
Also in 3 projects that use this
perso-aiSkilldubbing
Auto-dub (voice-translate) videos into other languages, with lip-sync (Perso AI Dubbing).
Also in 4 projects that use this
awesome-genmediaSkilltext-to-speech
Convert text to natural-sounding speech using each::sense AI. Generate realistic voice audio from any text in 32 languages with control over voice, speed, emotion, and style. Powered by elevenlabs-tts for studio-quality output. Use for: narration, audiobooks, accessibility, announcements, IVR systems, e-learning, localization. Triggers: text to speech, tts, read aloud, convert to speech, speak text, voice output, ai speech, synthesize speech, read text, audio from text, narrate text
Also in 8 projects that use this
wlzhSkillvoice-changer
音频变声处理工具 - 使用 RVC AI 模型进行真实的声音转换,支持视频直接输入
Also in 3 projects that use this
awesome-genmediaSkillsound-effects
Generate custom sound effects using each::sense AI. Create realistic and stylized audio for any scenario including impacts, ambiences, foley, UI sounds, nature, mechanical effects, and transitions. Powered by stable-audio-2-5 for precise audio generation. Use for: video production, game development, podcasts, apps, UI/UX, presentations, film. Triggers: sound effect, sfx, audio effect, foley, generate sound, ambient sound, ui sound, impact sound, whoosh, explosion, nature sound, notification sound, create audio
Also in 4 projects that use this
Other versions19 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 19
- calesthio/OpenMontage★ 78ksame name
- OpenMontageInc/OpenMontage★ 78ksame name
- elevenlabs/skills★ 539same name
- NoizAI/skills★ 526same name
- tadaspetra/loop★ 296same name
- sarvamai/skills★ 72same name
- imMamdouhaboammar/motion-graphics-skills★ 50same name
- awesome-genmedia/skills★ 36same name
- deepgram/skills★ 26same name
- LinHao-city/StoryMind★ 10same name
9 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository26 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- agent-accountsSkill
- agent-reputationSkill
- deploy-agentSkill
- interact-agentSkill
- manage-agentSkill
- deploy-contractSkill
- interact-contractSkill
- scaffold-projectSkill
- account-managementSkill
- embeddingsSkill
- fine-tuningSkill
- image-editingSkill
- provider-discoverySkill
- streaming-chatSkill
- text-to-imageSkill
- video-generationSkill
- compute-plus-storageSkill
- storage-plus-chainSkill
- 0g-pc-setupSkill
- 0g-pc-switch-modelSkill
- 0g-pc-uninstallSkill
- download-fileSkill
- merkle-verificationSkill
- upload-fileSkill
The file
We split this skill into the rules it gives an agent
5 rules. 0 are common, 3 are distinctive, and 2 are very specific to this project.
0
Common
Most of the 11 edited versions we read keep these.
3
Distinctive
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 52
Create FormData with audio file and parameters
- Distinctive: line 62
Use FormData for audio upload (not JSON)
- Very specific: line 64
Call processResponse() after every transcription
- Very specific: line 65
Use correct processResponse() param order: (providerAddress, chatID, usageData)
- Distinctive: line 77
Use ethers v5 syntax
“Common” is measured: kept by at least 6 in 10 of the 11 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Speech-to-Text Transcription
Metadata
- Category: compute
- SDK:
@0gfoundation/0g-compute-ts-sdk^0.9.0,ethers6.13.1 - Activation Triggers: "transcribe", "speech-to-text", "Whisper", "audio transcription"
Purpose
Transcribe audio files using 0G Compute Network providers (serviceType: speech-to-text). Supports
multiple audio formats and output types (JSON, text, SRT subtitles).
Models
At last verification 0G mainnet had one speech-to-text provider serving openai/whisper-large-v3.
Testnet had none. Discover at runtime rather than hardcoding.
Billing: two usage shapes
Speech-to-text providers bill in one of two ways, and the usage object in the response tells you
which. Pass it through to processResponse() verbatim — do not normalise or reshape it:
| Shape | Billing |
{"type":"duration","seconds":N} | per second, against inputPrice |
{"type":"tokens","input_tokens":N,"output_tokens":N} | per token, input and output priced |
// RIGHT — forward whatever the provider returned, unchanged
await broker.inference.processResponse(
providerAddress,
chatID,
data.usage ? JSON.stringify(data.usage) : undefined,
);Because both shapes are possible, never hand-build the usage string or assume token fields exist.
Prerequisites
- Node.js >= 20
@0gfoundation/0g-compute-ts-sdkandethersinstalled- Funded and acknowledged provider with
speech-to-textservice - Audio file in supported format (mp3, wav, ogg, flac, webm)
.envwithPRIVATE_KEY,RPC_URL,PROVIDER_ADDRESS
Quick Workflow
- Initialize broker
- Get service metadata (endpoint, model)
- Create FormData with audio file and parameters
- Generate auth headers
- Make transcription request
- Extract ChatID from
ZG-Res-Keyheader ONLY - Call
processResponse(providerAddress, chatID, usageData)
Core Rules
ALWAYS
- Use FormData for audio upload (not JSON)
- Get ChatID from
ZG-Res-Keyheader (no body fallback for speech) - Call
processResponse()after every transcription - Use correct
processResponse()param order:(providerAddress, chatID, usageData) - Forward the response's
usageobject verbatim — it may be duration-billed or token-billed - Acknowledge provider before first use
NEVER
- Assume the
usageobject has token fields — duration-billed providers return{"type":"duration","seconds":N}instead - Send audio as base64 in JSON body (use FormData)
- Skip
processResponse()after transcription - Try to get ChatID from response body for speech-to-text
- Hardcode private keys
- Use ethers v5 syntax
Code Examples
Basic Transcription
import { ethers } from 'ethers';
import { createZGComputeNetworkBroker } from '@0gfoundation/0g-compute-ts-sdk';
import * as fs from 'fs';
import 'dotenv/config';
async function transcribe(audioPath: string): Promise<string> {
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const broker = await createZGComputeNetworkBroker(wallet);
const providerAddress = process.env.PROVIDER_ADDRESS!;
const { endpoint, model } = await broker.inference.getServiceMetadata(providerAddress);
const headers = await broker.inference.getRequestHeaders(providerAddress);
const formData = new FormData();
const audioBuffer = fs.readFileSync(audioPath);
const audioBlob = new Blob([audioBuffer]);
formData.append('file', audioBlob, audioPath.split('/').pop());
formData.append('model', model);
formData.append('response_format', 'json');
const response = await fetch(`${endpoint}/audio/transcriptions`, {
method: 'POST',
headers: { ...headers },
body: formData,
});
const data = await response.json();
// ChatID from header ONLY for speech-to-text
const chatID = response.headers.get('ZG-Res-Key') || response.headers.get('zg-res-key');
await broker.inference.processResponse(
providerAddress,
chatID,
data.usage ? JSON.stringify(data.usage) : undefined,
);
return data.text;
}
// Usage
const text = await transcribe('./audio/podcast.mp3');
console.log('Transcription:', text);Transcription with Format Options
type OutputFormat = 'json' | 'text' | 'srt' | 'verbose_json';
async function transcribeWithFormat(
audioPath: string,
format: OutputFormat = 'json',
language?: string,
): Promise<any> {
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const broker = await createZGComputeNetworkBroker(wallet);
const providerAddress = process.env.PROVIDER_ADDRESS!;
const { endpoint, model } = await broker.inference.getServiceMetadata(providerAddress);
const headers = await broker.inference.getRequestHeaders(providerAddress);
const formData = new FormData();
const audioBuffer = fs.readFileSync(audioPath);
formData.append('file', new Blob([audioBuffer]), audioPath.split('/').pop());
formData.append('model', model);
formData.append('response_format', format);
if (language) formData.append('language', language);
const response = await fetch(`${endpoint}/audio/transcriptions`, {
method: 'POST',
headers: { ...headers },
body: formData,
});
const chatID = response.headers.get('ZG-Res-Key') || response.headers.get('zg-res-key');
if (format === 'text' || format === 'srt') {
const text = await response.text();
if (chatID) {
await broker.inference.processResponse(providerAddress, chatID);
}
return text;
}
const data = await response.json();
await broker.inference.processResponse(
providerAddress,
chatID,
data.usage ? JSON.stringify(data.usage) : undefined,
);
return data;
}
// Usage
const srt = await transcribeWithFormat('./audio/meeting.mp3', 'srt', 'en');
fs.writeFileSync('./output/meeting.srt', srt);Error Handling
async function safeTranscribe(audioPath: string): Promise<string | null> {
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const broker = await createZGComputeNetworkBroker(wallet);
const providerAddress = process.env.PROVIDER_ADDRESS!;
try {
// Validate file exists
if (!fs.existsSync(audioPath)) {
throw new Error(`Audio file not found: ${audioPath}`);
}
// Validate file size (most providers have limits)
const stats = fs.statSync(audioPath);
const maxSize = 25 * 1024 * 1024; // 25MB typical limit
if (stats.size > maxSize) {
throw new Error(`File too large (${stats.size} bytes). Max: ${maxSize} bytes`);
}
const { endpoint, model } = await broker.inference.getServiceMetadata(providerAddress);
const headers = await broker.inference.getRequestHeaders(providerAddress);
const formData = new FormData();
const audioBuffer = fs.readFileSync(audioPath);
formData.append('file', new Blob([audioBuffer]), audioPath.split('/').pop());
formData.append('model', model);
formData.append('response_format', 'json');
const response = await fetch(`${endpoint}/audio/transcriptions`, {
method: 'POST',
headers: { ...headers },
body: formData,
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
const data = await response.json();
const chatID = response.headers.get('ZG-Res-Key') || response.headers.get('zg-res-key');
await broker.inference.processResponse(
providerAddress,
chatID,
data.usage ? JSON.stringify(data.usage) : undefined,
);
return data.text;
} catch (error) {
console.error('Transcription failed:', error);
return null;
}
}Supported Audio Formats
| Format | Extension | Notes |
| MP3 | .mp3 | Most common |
| WAV | .wav | Uncompressed |
| OGG | .ogg | Compressed |
| FLAC | .flac | Lossless |
| WebM | .webm | Web native |
Output Formats
| Format | Description |
json | { "text": "..." } |
text | Plain text string |
srt | SubRip subtitle format |
verbose_json | Includes timestamps and segments |
Cost Estimate
~0.0001 0G per minute of audio (varies by provider).
Anti-Patterns
// BAD: Sending audio as JSON
const response = await fetch(endpoint, {
body: JSON.stringify({ audio: base64Data }), // WRONG — use FormData
});
// BAD: Getting chatID from body
const chatID = data.id; // WRONG for speech — header only
// BAD: Missing processResponse
const data = await response.json();
return data.text; // processResponse() never called!
// BAD: Hardcoding private keys
const wallet = new ethers.Wallet('0xabc123...', provider); // NEVER do this
// BAD: ethers v5 syntax
const provider = new ethers.providers.JsonRpcProvider(url); // v5!Common Errors & Fixes
| Error | Cause | Fix |
Insufficient balance | Sub-account empty | Transfer more funds |
unsupported format | Wrong audio format | Use mp3, wav, ogg, flac, or webm |
file too large | Audio file too big | Split into smaller segments |
Fee verification failed | Missing chatID | Check ZG-Res-Key header |
Provider not acknowledged | First-time provider | acknowledgeProviderSigner() |
Related Skills
- Provider Discovery — find speech providers
- Account Management — fund accounts
- Compute + Storage — transcribe and store
References
- Compute Patterns
- Network Config
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

