| extensions | ||
| .gitignore | ||
| CHANGELOG.md | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
pi-codex-transcribe
Experimental Pi extension for transcribing local audio files through ChatGPT/Codex using Pi's existing openai-codex login.
This package does not use OPENAI_API_KEY and does not call the public OpenAI API.
On Windows/macOS/Linux it uses Node fetch first, then automatically falls back to the local curl executable if ChatGPT returns a Cloudflare challenge. Install curl if it is not already on your PATH.
Install / local test
From this repository:
pi install /path/to/Pi-Extensions/PI-Codex-Transcribe
For a one-off test without installing:
pi -e /path/to/Pi-Extensions/PI-Codex-Transcribe
Authentication
The tool uses Pi's existing openai-codex provider credentials:
/login
Select ChatGPT Plus/Pro (Codex) and complete the login flow. The extension reads the token with:
ctx.modelRegistry.getApiKeyForProvider("openai-codex")
It also extracts chatgpt_account_id from the token and sends it as ChatGPT-Account-ID.
Usage that avoids context-length failures
Use the slash command for audio files. It bypasses the LLM/tool-selection step, so Pi will not try to read or attach the MP3 into model context. The full transcript is saved to ~/.pi/agent/pi-codex-transcribe/transcripts/ and only a small preview is shown in chat:
/codex-transcribe dialogo.mp3
The command also strips a leading @, so this is safe too:
/codex-transcribe @dialogo.mp3
Optional debugging flags:
/codex-transcribe dialogo.mp3 --field audio --mime audio/mpeg
The extension also intercepts simple natural-language transcription requests like:
Me puedes pasar esto a texto: @dialogo.mp3
and handles them directly before the model sees the audio path. Large transcripts and error bodies are truncated in chat to avoid filling the context window.
Tool
Also registers one LLM-callable tool:
codex_transcribe_audio
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
audioPath |
string | yes | Local audio file path. Relative paths resolve under the current workspace. |
language |
string | no | Optional language hint form field, e.g. en. |
prompt |
string | no | Optional transcription prompt/context form field. |
fieldName |
string | no | Multipart field-name override. Defaults to file; useful for testing audio or audio_file. |
mimeType |
string | no | MIME type override. Defaults to extension-based detection. |
Supported extension defaults:
| Extension | MIME type |
|---|---|
.webm |
audio/webm |
.wav |
audio/wav |
.mp3 |
audio/mpeg |
.m4a |
audio/mp4 |
.mp4 |
audio/mp4 |
| other | application/octet-stream |
How it works
- Reads the local audio file.
- Resolves the
openai-codextoken from Pi. - Extracts
chatgpt_account_idfrom the JWT. - Sends a
multipart/form-dataPOST request to:
https://chatgpt.com/backend-api/transcribe
Required headers use the Codex CLI-style fingerprint:
Authorization: Bearer <token>
ChatGPT-Account-ID: <accountId>
originator: codex_cli_rs
User-Agent: codex_cli_rs/...
OpenAI-Beta: responses=experimental
session-id: <uuid>
x-client-request-id: <uuid>
If Node fetch is challenged by Cloudflare, the extension retries with curl using the same headers and a tiny in-memory allowlist of Cloudflare infrastructure cookies only. The token is never logged or returned. Successful transcriptions are written to:
~/.pi/agent/pi-codex-transcribe/transcripts/<timestamp>-<audio-name>.txt
Only a preview is returned to Pi chat, so long audio does not fill the model context.
Experimental status and risks
This depends on a private ChatGPT/Codex backend endpoint. It may break without notice, vary across accounts/regions/clients, or be blocked by browser/session checks such as Cloudflare. It is not the official OpenAI API.
TTS is not included here and is not confirmed through Codex subscription auth.
Troubleshooting
If you previously got context_length_exceeded, update/reload this package, then start a fresh session with /new. Old sessions may already contain the huge audio/text payload.
Use /codex-transcribe dialogo.mp3 instead of asking the model to call the tool. The command runs inside the extension directly and prevents Pi from attaching the audio file to the model context.
On non-2xx responses, the tool/command error includes status code, a truncated response body preview, content type, sanitized response headers, form field name, MIME type, file name, and audio byte count.
| Status | Likely cause | What to try |
|---|---|---|
| 401 | Missing/expired Codex token | Run /login again and select ChatGPT Plus/Pro (Codex). |
| 403 | Account/session rejected or shared entitlement issue | Re-login; verify the account has Codex/ChatGPT access; check for browser/session enforcement. |
| 415 | Unsupported media type | Try .webm; override mimeType to audio/webm, audio/wav, or audio/mpeg. |
| 422 | Multipart shape rejected | Try fieldName: "audio" or fieldName: "audio_file"; check whether language/prompt fields should be omitted. |
| Cloudflare/HTML body | Node fetch blocked |
The extension should auto-fallback to curl. Check that curl --version works. Curl fallback is always enabled in this development version. |
Start with a small .webm file because browser-style recording is the most likely shape expected by the endpoint.