Skip to content

feat(tts): OpenAI engine talks to any OpenAI-compatible server - #58

Merged
shreyaskarnik merged 1 commit into
shreyaskarnik:mainfrom
lordrip:feat/openai-base-url
Oct 27, 2026
Merged

shreyaskarnik merged 1 commit into
shreyaskarnik:mainfrom
lordrip:feat/openai-base-url

Conversation

@lordrip

@lordrip lordrip commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

engines.openai() gains two options so it can narrate through any server that implements OpenAI's POST /v1/audio/speech (self-hosted TTS such as Kokoro-FastAPI or a CosyVoice server), without a custom engine:

  • baseUrl: the server's base URL, including /v1. Named like engines.mlxAudio()'s option.
  • timeoutMs: the SDK request timeout, since local servers on CPU can take minutes per clip.
tts: { engine: engines.openai({ baseUrl: 'http://localhost:8881/v1', model: 'my-model', timeoutMs: 600_000 }) }

Key safety

With baseUrl set, the engine never reads OPENAI_API_KEY, so an OpenAI key in the environment is not sent to another server. An explicit apiKey is passed through; otherwise the placeholder unused is sent, because the SDK refuses an empty key. Without baseUrl, behaviour is unchanged (a key is still required for OpenAI).

baseUrl is added to describe(), so the video's metadata names the server. It is not part of the clip cache key, as with the other engine options.

Known limit: ~300 s with openai >= 5

timeoutMs alone does not let a request wait longer than about 5 minutes on openai 5+: those versions use Node's built-in fetch, whose headersTimeout (300 s) applies first. Measured against a stub server that answers after 320 s: v4.104 ok, v6.49 fails at 304 s. This PR documents it (README and the option's doc comment) rather than working around it; #59 describes the options for a fix.

Tests

tests/tts/openai.test.ts (new, 7 tests, SDK mocked at the constructor): default key from the environment; key still required for OpenAI; baseUrl + timeoutMs reach the client; OPENAI_API_KEY never sent to another server; explicit apiKey passed through; voice and PCM request; metadata. tsc --noEmit clean.

The full suite has 11 failures on this machine (Windows) in export, pipeline, repo-hygiene, e2e record, hf block-filter and shader-render; they fail identically on a clean main (c563ef2), so they are unrelated to this change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UV59h7xwn1rScigEo4KA88

engines.openai() gains `baseUrl` and `timeoutMs`, so self-hosted TTS servers that implement
/v1/audio/speech work without a custom engine. With `baseUrl` set, OPENAI_API_KEY is never read,
so an OpenAI key is not sent to another server; an explicit `apiKey` is passed through, otherwise
a placeholder, since the SDK refuses an empty key. `baseUrl` is recorded in the engine metadata.

With openai >= 5, Node's built-in fetch still ends a request after ~300 s without response
headers, whatever `timeoutMs` says; documented, not worked around.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UV59h7xwn1rScigEo4KA88
@shreyaskarnik
shreyaskarnik merged commit b0f1fa7 into shreyaskarnik:main Oct 27, 2026
15 of 18 checks passed
shreyaskarnik added a commit that referenced this pull request Oct 27, 2026
Document engines.openai() baseUrl/timeoutMs (#58) in the argo-guide skill.

Claude-Session: https://claude.ai/code/session_01A5noi8MogUjD2mdirpzXmY
@lordrip
lordrip deleted the feat/openai-base-url branch October 27, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants