Skip to content
Tracon

ISpeechSynthesizer

Namespace Tracon · Assembly Tracon.Abstractions.dll

A provider that generates speech from text.

public interface ISpeechSynthesizer

ElevenLabs is an implementation, not a dependency. A consumer wanting another provider registers their own implementation; tools know only this contract.

The contract lives HERE, not in Tracon.Voice: the HTTP layer (Tracon.AspNetCore) sees these types while serving the voice endpoints, but it CANNOT reference Tracon.Voice (the package direction rule). The same pattern was applied to the MCP abstractions —.

DI lifetime — singleton. The unnamed default is registered as a singleton with TryAdd; a consumer’s own registration wins. More synthesizers are registered by name with AddSpeechSynthesizer.

Tenant behavior — TENANT-INDEPENDENT implementation, tenant-gated calls. An instance is configured once, with its own credentials; it never reads the tenant and no member takes a tenant parameter. Every Tracon call that carries a tenant’s text or audio resolves the instance through ISpeechProviderResolver, which applies the tenant’s egress policy to ISpeechSynthesizer.ProviderName and, when the tenant has its own credential for that provider, passes it in SpeechRequest.Credential — only to an implementation whose ISpeechSynthesizer.SupportsTenantCredentials is true. The platform health check calls the default instance directly, with its configured credential and no tenant data.

The maximum number of characters accepted in a single ISpeechSynthesizer.SynthesizeAsync call.

int MaxCharactersPerRequest { get; }

int

The tool call (speak) AND the direct HTTP operator endpoint (POST /api/voice/speak) read the SAME value — the limit is applied consistently across both paths.

The provider name. Used in cost records and health checks.

string ProviderName { get; }

string

A tenant’s egress policy and provider bindings name this value. Two registrations that report the same name are one provider to the tenant’s checks.

Gets whether the synthesizer honours a tenant credential passed in SpeechRequest.Credential and to ISpeechSynthesizer.ListVoicesWithCredentialAsync.

bool SupportsTenantCredentials { get; }

bool

The default is false. Tracon then refuses a call for a tenant that has its own binding for ISpeechSynthesizer.ProviderName rather than make it on the configured credential, which would bill the wrong account. An implementation that returns true uses the credential for the call it came with and never falls back to its configured one.

Lists the available voices.

ValueTask<IReadOnlyList<VoiceDescriptor>> ListVoicesAsync(CancellationToken cancellationToken = default)

cancellationToken CancellationToken

The cancellation token.

ValueTask<IReadOnlyList<VoiceDescriptor>>

The voices, ordered by name.

ListVoicesWithCredentialAsync(ModelProviderCredential?, CancellationToken)

Section titled “ ListVoicesWithCredentialAsync(ModelProviderCredential?, CancellationToken)”

Lists the available voices with a tenant’s own credential.

ValueTask<IReadOnlyList<VoiceDescriptor>> ListVoicesWithCredentialAsync(ModelProviderCredential? credential, CancellationToken cancellationToken = default)

credential ModelProviderCredential?

The tenant’s credential, or null for the configured one.

cancellationToken CancellationToken

The cancellation token.

ValueTask<IReadOnlyList<VoiceDescriptor>>

The voices, ordered by name.

The default implementation calls ISpeechSynthesizer.ListVoicesAsync when no credential is given and throws when one is: an implementation that cannot use the tenant’s credential must not list with its own instead. An implementation whose ISpeechSynthesizer.SupportsTenantCredentials is true overrides it.

NotSupportedException

credential is set and the implementation does not override this method.

SynthesizeAsync(SpeechRequest, CancellationToken)

Section titled “ SynthesizeAsync(SpeechRequest, CancellationToken)”

Synthesizes speech from a text.

ValueTask<SpeechAudio> SynthesizeAsync(SpeechRequest request, CancellationToken cancellationToken = default)

request SpeechRequest

The request.

cancellationToken CancellationToken

The cancellation token.

ValueTask<SpeechAudio>

The generated audio.

SynthesizeStreamingAsync(SpeechRequest, CancellationToken)

Section titled “ SynthesizeStreamingAsync(SpeechRequest, CancellationToken)”

Synthesizes speech from a text and streams the audio piece by piece.

IAsyncEnumerable<ReadOnlyMemory<byte>> SynthesizeStreamingAsync(SpeechRequest request, CancellationToken cancellationToken = default)

request SpeechRequest

The request.

cancellationToken CancellationToken

The cancellation token.

IAsyncEnumerable<ReadOnlyMemory<byte>>

The audio chunks.

The streaming path is for low latency, and the voice layer uses it. The signature is defined now: adding a member to an interface later is a breaking change for consumers implementing that interface.

Streamed audio is not stored as an attachment. The billed quantity is unknown until the stream ends, and the chunks are not a valid file on their own; the speak tool uses the non-streaming path for this reason.