.NET API
ISpeechSynthesizer
Tracon.Abstractions.dllA provider that generates speech from text.
public interface ISpeechSynthesizerRemarks
Section titled “Remarks”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.
Properties
Section titled “Properties”MaxCharactersPerRequest
Section titled “ MaxCharactersPerRequest”The maximum number of characters accepted in a single ISpeechSynthesizer.SynthesizeAsync call.
int MaxCharactersPerRequest { get; }Property Value
Section titled “Property Value”Remarks
Section titled “Remarks”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.
ProviderName
Section titled “ ProviderName”The provider name. Used in cost records and health checks.
string ProviderName { get; }Property Value
Section titled “Property Value”Remarks
Section titled “Remarks”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.
SupportsTenantCredentials
Section titled “ SupportsTenantCredentials”Gets whether the synthesizer honours a tenant credential passed in SpeechRequest.Credential and to ISpeechSynthesizer.ListVoicesWithCredentialAsync.
bool SupportsTenantCredentials { get; }Property Value
Section titled “Property Value”Remarks
Section titled “Remarks”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.
Methods
Section titled “Methods”ListVoicesAsync(CancellationToken)
Section titled “ ListVoicesAsync(CancellationToken)”Lists the available voices.
ValueTask<IReadOnlyList<VoiceDescriptor>> ListVoicesAsync(CancellationToken cancellationToken = default)Parameters
Section titled “Parameters”cancellationToken CancellationToken
The cancellation token.
Returns
Section titled “Returns”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)Parameters
Section titled “Parameters”credential ModelProviderCredential?
The tenant’s credential, or null for the configured one.
cancellationToken CancellationToken
The cancellation token.
Returns
Section titled “Returns”ValueTask<IReadOnlyList<VoiceDescriptor>>
The voices, ordered by name.
Remarks
Section titled “Remarks”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.
Exceptions
Section titled “Exceptions”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)Parameters
Section titled “Parameters”request SpeechRequest
The request.
cancellationToken CancellationToken
The cancellation token.
Returns
Section titled “Returns”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)Parameters
Section titled “Parameters”request SpeechRequest
The request.
cancellationToken CancellationToken
The cancellation token.
Returns
Section titled “Returns”IAsyncEnumerable<ReadOnlyMemory<byte>>
The audio chunks.
Remarks
Section titled “Remarks”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.