OpenAI Chat & Transcription
Native OpenAI provider integration (GPT chat models + Whisper transcription). Configure baseUrl / apiKey / models via server.json — they are server-only.
| ID | chat-openai |
| Version | 1.0.0 |
| Author | Aiosa |
| Categories | AI |
| Source | plugins/chat-openai |
Keywords: chat · llm · openai · gpt · whisper · transcription · speech-to-text
Dependencies
Additional configuration keys
authMode, authContext
See include.json for details and defaults.
Documentation
Chat plugin for OpenAI (native)
Native OpenAI provider via the Vercel AI SDK's @ai-sdk/openai package:
GPT chat models plus first-class transcription (whisper-1,
gpt-4o-transcribe, …) through the SDK's transcription-model API. Use
this instead of chat-openai-compatible when you talk to OpenAI itself
and want the SDK-native features; use the compatible plugin for
everything merely speaking the OpenAI wire format.
All upstream traffic (chat and transcription) is routed through the core
SSRF guard (safeFetch) and the baseUrl is vetted before it reaches the
SDK; the API key stays server-side.
Configuration
Server-only secure config (server.json author tier, or
core.server.secure.plugins.chat-openai in your env):
"chat-openai": {
"providerDefaults": {
"baseUrl": "https://api.openai.com/v1",
"apiKey": "<% OPENAI_API_KEY %>",
"defaultModelId": "gpt-4o-mini",
"defaultTranscriptionModelId": "whisper-1"
}
}
id / label / description / authType / requiresLogin / hidden /
contexts behave exactly as in chat-anthropic / chat-openai-compatible
(managed provider, deployer flags win over untrusted input). Leave
apiKey empty to run BYOK — users supply their own key from the chat
settings dialog.
apiKey has three states:
| value | meaning |
|---|---|
"sk-…" | the operator's server-side key |
absent / "" | a key is required — model discovery does not call OpenAI until the deployer or a BYOK user supplies one (no more 401 on every boot) |
false | the endpoint is declared keyless; discovery runs with no Authorization header |
See the chat SDK's README → "No key, no discovery".
Login (opt-in, method-agnostic)
Out of the box the plugin requires no login: include.json ships
authMode: "none" / authContext: null, so the provider works with the
operator's server-side key and no auth stack configured anywhere.
To put the chat behind a login, the deployment names a context — the
plugin never names a mechanism (no OIDC/SAML knowledge, no auth module in
modules, and it never calls configureContext; see src/AUTH.md):
"plugins": {
"chat-openai": { "authMode": "jwt", "authContext": "openai" }
}
and an auth module must own that context — e.g. oidc-client-ts /
oidc-server-ts / saml-auth loaded with permaLoad and declaring
openai in its contexts block (plus a matching rpcVerifiers.openai
server-side). If nothing claims the context, the chat degrades closed: no
Login button, chat stays disabled, and the panel shows
chat.loginUnavailable naming the unclaimed context.
Both keys are read via getStaticMeta (ENV/include.json), never
getOption — a session bundle cannot downgrade them.
Speech-to-text
The adapter implements the chat SDK's optional resolveTranscriptionModel
capability natively (openai.transcription(modelId)), so the provider can
back the speech-to-text module's vercel driver:
"speech-to-text": { "driver": "vercel" }
Naming the provider is optional — the server picks a transcription-capable
one itself, using providerDefaults.defaultTranscriptionModelId as the model.
Pin it explicitly only when several providers can transcribe and you want a
specific one:
"speech-to-text": {
"driver": "vercel",
"vercel": { "providerId": "chat-openai", "model": "whisper-1" }
}
…or nominate it once on the provider side with
providerDefaults.transcriptionDefault: true, which wins the auto-selection
tie-break (it grants nothing — capability and access checks are unchanged).
Whisper hints (language, prompt) are forwarded via providerOptions
under the openai namespace. The prompt — the vocabulary bias the chat
composer assembles from its glossary and the report plugin's terms — is
dropped unless the provider allows it:
"providerDefaults": {
"defaultTranscriptionModelId": "gpt-4o-mini-transcribe",
"transcriptionDefault": true,
"transcriptionPromptMaxChars": 1000
}
transcriptionPromptMaxChars defaults to 0 (ceiling 1000) because a
self-hosted whisper-large-v3 dropped audio in proportion to prompt length
(modules/speech-to-text/README.md, prompt-length table); OpenAI's
gpt-4o-transcribe family documents prompt as the intended channel. It
is deliberately not a configSchema field — panel/RPC config writes are
intersected with the schema and outrank fixedConfig, so listing it would
let a caller raise its own cap. The client has a matching gate,
speech-to-text.promptMaxChars (also default 0); both must open. Use the
bare model ids: the pinned @ai-sdk/openai sends response_format: json
only for exactly gpt-4o-transcribe / gpt-4o-mini-transcribe, and a dated
snapshot id gets verbose_json, which those models reject once a hint is
present. env/parts/voice/openai-4o-transcribe.json is the working
composition. A dedicated transcription-only deployment
can set providerDefaults.hidden: true — the provider stays out of the
chat picker but remains resolvable for transcription (it is still listed
by the chat SDK's listTranscriptionProviders RPC, flagged hidden, and
is still an auto-selection candidate).