Skip to content

feat: add API server audio endpoints - #29364

Open
Codename-11 wants to merge 1 commit into
NousResearch:mainfrom
Codename-11:feat/api-server-audio
Open

Codename-11 wants to merge 1 commit into
NousResearch:mainfrom
Codename-11:feat/api-server-audio

Conversation

@Codename-11

Copy link
Copy Markdown
Contributor

Summary

  • Add a focused API Server audio surface for external clients: GET /api/audio/capabilities, POST /api/audio/transcriptions, and POST /api/audio/speech.
  • Wire audio discovery into /v1/capabilities with fallback-safe endpoint metadata.
  • Reuse existing Hermes STT/TTS primitives (tools.transcription_tools.transcribe_audio and tools.tts_tool.text_to_speech_tool) instead of introducing a parallel provider layer.
  • Preserve API Server bearer auth semantics and add thin /voice/* compatibility aliases for existing Relay-style clients.

Deferred Scope

  • Realtime voice, WebRTC, LiveKit, wake word, and provider-native realtime sessions.
  • Admin/config/profile mutation, jobs admin, broad client metadata, memory writes, and skills/category APIs.
  • Any new STT/TTS provider configuration surface or raw config read/write.

Test Plan

  • python -m py_compile gateway/platforms/api_server.py tests/gateway/test_api_server.py
  • python -m pytest tests/gateway/test_api_server.py::TestAudioEndpoints -q -o 'addopts='
  • python -m pytest tests/gateway/test_api_server.py -q -o 'addopts='
  • python -m pytest tests/gateway/test_api_server_runs.py tests/gateway/test_api_server_jobs.py -q -o 'addopts='
  • git diff --check

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery tool/tts Text-to-speech and transcription labels May 20, 2026
@alt-glitch

Copy link
Copy Markdown
Contributor

Duplicate of #8199 which adds the same /v1/audio/transcriptions and /v1/audio/speech API server endpoints. Please coordinate with that PR to avoid divergent implementations.

@Codename-11

Codename-11 commented May 20, 2026

Copy link
Copy Markdown
Contributor Author

Good catch, I missed #8199, apologies!

Looks like that already covers the /v1/audio/* side. This PR was aimed more at /api/audio/*, capability discovery, and the /voice/* aliases, but of course we shouldn't split the same plumbing across two PRs!

If #8199 is the preferred base, I can close this, or rework it into a smaller discovery/compat follow-up. 😁

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for building this on the existing STT/TTS primitives. The current-main premise remains valid: gateway/platforms/api_server.py:1546 advertises audio_api: false, and its route registration at gateway/platforms/api_server.py:4843-4883 has no audio route.

Problems

  • gateway/platforms/api_server.py:1121 breaks out after finding file, so a multipart model field sent after file is ignored.
  • gateway/platforms/api_server.py:1195 invokes TTS without output_path; the shared tool creates an output artifact when none is provided (tools/tts_tool.py:2204-2247), but the handler only reads it at :1210 and never deletes it.
  • gateway/platforms/api_server.py:1216 always returns audio/mpeg, although the TTS primitive returns an artifact path and supports non-MP3 output (tools/tts_tool.py:2440-2446).
  • The API surface lacks corresponding documentation in website/docs/user-guide/features/api-server.md.

Suggested changes

  • Coordinate/consolidate with the overlapping open #8199 before selecting a canonical public contract.
  • Parse all multipart fields, use a temporary TTS output with guaranteed cleanup, detect the artifact MIME type, and add regression coverage for each case.

Automated hermes-sweeper review.

continue
if part.name in {"file", "audio"}:
file_field = part
break

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaking here makes multipart field order observable: a normal request with file before model never forwards the requested model. Stream the file while continuing through remaining parts, and add a file-before-model regression test.


from tools.tts_tool import text_to_speech_tool

result_raw = await asyncio.to_thread(text_to_speech_tool, text=text.strip())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without output_path, text_to_speech_tool creates its normal persistent output artifact. This handler reads it but never deletes it, so each successful API request leaks a generated audio file. Use a dedicated temporary output path and clean it up after responding.

return web.json_response(_openai_error("Failed to read generated speech audio", code="tts_read_failed"), status=502)

headers = {"X-Hermes-TTS-Provider": str(result.get("provider") or "")}
return web.Response(body=audio_bytes, content_type="audio/mpeg", headers=headers)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shared TTS layer returns the actual artifact path and providers may produce WAV, OGG, or other output. Always declaring audio/mpeg can mislabel the response; detect the generated artifact MIME type or constrain and verify the requested output format.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data tool/tts Text-to-speech and transcription type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants