import type { SentenceTokenizer } from '../tokenize/tokenizer.js';
import { type TimedString } from '../voice/io.js';
/**
 * An expressive markup tag stripped from a transcript, surfaced for the frontend.
 *
 * `type` is the markup tag name (`"emotion"`, `"expression"`, `"sound"`, ...) or the expr
 * marker type. `value` is the spoken or semantic payload (the `value="..."` attribute, the
 * expr `label`, or the tag's inner text).
 */
export interface ExpressiveTag {
    type: string;
    value: string;
}
/**
 * Non-verbal vocalizations the TTS may produce (sounds that aren't words).
 *
 * A sparse opt-out: omitted keys default to ON, and a category set to `false` is never
 * advertised to the LLM — `{ laughing: false }` removes laughter and nothing else.
 * Together the keys cover every sound the providers offer.
 */
export interface NonverbalOptions {
    /** laugh, chuckle, giggle — and laugh-speak delivery */
    laughing?: boolean;
    /** audible breath, inhale, exhale */
    breathing?: boolean;
    sighing?: boolean;
    crying?: boolean;
    /** non-lexical voiced sounds — humming a tune, sing-song or sung delivery */
    vocalizing?: boolean;
    /** tsk, tongue-click, lip-smack */
    mouthSounds?: boolean;
    /** cough, clearing the throat, yawn */
    reflexSounds?: boolean;
}
/**
 * Steers verbal delivery and non-verbal sounds in generated speech.
 *
 * Every key is a sparse override on the default (full sound vocabulary, light fillers):
 * the expressive instructions already tell the LLM to match its delivery to the register
 * of the moment, so most agents need no steering at all — set a key only to take an option
 * away regardless of context.
 */
export interface SpeechSteeringOptions {
    /**
     * Filler words such as "um" / "uh". On by default
     * ({@link DEFAULT_SPEECH_STEERING_OPTIONS}); set `false` to opt out.
     */
    disfluencies?: boolean;
    /**
     * Which non-verbal sounds the TTS may make. `true` (and omitting the key) keeps the
     * provider's full vocabulary, `false` disables every sound, and a
     * {@link NonverbalOptions} object toggles per category (omitted categories stay enabled).
     */
    nonverbalSounds?: boolean | NonverbalOptions;
    pace?: 'slow' | 'normal' | 'fast';
}
/** The default steering: full sound vocabulary, light fillers. */
export declare const DEFAULT_SPEECH_STEERING_OPTIONS: SpeechSteeringOptions;
/** What the expressive markup pipeline can do with a given voice. */
export interface MarkupInfo {
    /** {@link NonverbalOptions} field -> the labels it governs; an absent field is a no-op */
    nonverbals: Partial<Record<NonverbalField, string[]>>;
}
type NonverbalField = keyof NonverbalOptions;
/** {@link NonverbalOptions} field -> the sound/prosody labels it governs for `provider`. */
export declare function supportedNonverbals(provider: string): Partial<Record<NonverbalField, string[]>>;
/**
 * Render a {@link SpeechSteeringOptions} into delivery guidelines for `provider`.
 *
 * Only fields that change the default produce output, so an empty object adds nothing on
 * top of the base template. Disabled sounds never appear here: {@link llmInstructions}
 * filters them out of the advertised vocabulary, so the only sound guidance left is how
 * sparingly to use what remains.
 */
export declare function steeringInstructions(provider: string, steering: SpeechSteeringOptions): string;
/** The max text chunk length for a provider, or `undefined` if unlimited. */
export declare function maxInputLen(provider: string): number | undefined;
/**
 * Default sentence tokenizer for a provider's streamed TTS input.
 *
 * The provider's hard max chunk length caps every emitted token. When `expressive` is set,
 * it also raises the *minimum* to {@link EXPRESSIVE_BATCH_LEN} so a couple of consecutive
 * sentences ride one request, keeping prosody continuous across the turn; otherwise tokens
 * emit per sentence (the unchanged default). Providers with no configured limit are
 * uncapped and stay per-sentence even under expressive — Fish Audio's markers are
 * sentence-scoped, so batching would cost time-to-first-audio and buy no steering.
 */
export declare function sentenceTokenizer(provider: string, options: {
    expressive: boolean;
}): SentenceTokenizer;
/**
 * Whether `provider` has an expr instruction block, i.e. whether expressive can do
 * anything for it. Allocation-free: prefer this over testing
 * `llmInstructions(...) !== undefined`.
 */
export declare function hasMarkupDialect(provider: string): boolean;
/**
 * LLM instruction text for a TTS provider, or `undefined` when it has no markup dialect.
 *
 * Each markup-capable provider gets its own expr instruction block — shared marker syntax,
 * but only the types and label vocabularies that provider actually supports;
 * {@link convertMarkup} lowers the markers to native syntax. Expr is the only dialect the
 * LLM is ever taught. When `steering` disables a non-verbal sound, its labels (and any
 * example demonstrating them) are omitted from the block entirely rather than advertised
 * and then revoked.
 */
export declare function llmInstructions(provider: string, steering?: SpeechSteeringOptions): string | undefined;
/**
 * Strip the union of every provider's expressive XML markup (provider-agnostic).
 *
 * The transcript sinks strip downstream, where the originating TTS/provider is no longer
 * in scope, so they remove every provider's XML tags at once: expr markers (all the LLM is
 * ever taught) plus every native tag name, so a hallucinated native tag is stripped rather
 * than leaked.
 *
 * Square-bracket spans are *not* stripped: the LLM only writes expr, so brackets in its
 * output are prose (a `[text](url)` link) that a strip would mangle. Provider-native
 * brackets never arrive here — {@link dropBracketCues} removes them at their source.
 */
export declare function splitAllMarkup(text: string): [string, ExpressiveTag[]];
/** {@link splitAllMarkup} returning only the clean text (tags discarded). */
export declare function stripAllMarkup(text: string): string;
/**
 * Strip only the `<expr/>` dialect, leaving all other markup untouched.
 *
 * Unlike {@link stripAllMarkup}, provider-native tags survive (both leave square-bracket
 * spans alone).
 */
export declare function stripExprMarkup(text: string): string;
/**
 * Build the `lk.expression` transcription attribute from stripped markup tags.
 *
 * Surfaces a segment's leading delivery/emotion (`expression` for Inworld/xAI, `emotion`
 * for Cartesia) as `{"expression": ..., "mood": ...}`: the provider's own words, plus the
 * mood they normalize to, so a client can drive UI off a fixed enum without
 * reimplementing the matching. Returns `undefined` when no such tag was present.
 */
export declare function expressionAttribute(tags: ExpressiveTag[]): Record<string, string> | undefined;
/**
 * Stateful, provider-agnostic markup stripper for one transcript segment.
 *
 * Fed text chunk-by-chunk, it returns the user-visible text and accumulates the stripped
 * tags. A tag-shaped trailing fragment (a partial `<...` arriving split across chunks) is
 * held back until it closes, so a tag straddling a chunk boundary is never emitted
 * half-stripped. Shared by the transcript sinks (room output + transcript synchronizer) so
 * stripping and expression extraction stay identical across them.
 */
export declare class TranscriptMarkupStripper {
    #private;
    /** Feed a chunk; return the clean text ready to emit (may be empty). */
    push(text: string): string;
    /** Drain any buffered text at segment end; return the remaining clean text. */
    flush(): string;
    /** The markup tags stripped so far, in document order. */
    get tags(): ExpressiveTag[];
    /** The `lk.expression` attribute for the tags stripped so far, if any. */
    expressionAttribute(): Record<string, string> | undefined;
}
/**
 * Remove bracket cues from TTS-aligned tokens, keeping the survivors' timings.
 *
 * `useTtsAlignedTranscript` makes the provider's alignment of the text it was sent the
 * transcript, and that text is post-{@link convertMarkup}, so it carries native
 * `[laugh]`/`[speak calmly]` cues as words the agent never spoke. Every bracket span goes:
 * the provider reads them all as cues, so none is ever audio, and markdown links are
 * already gone (`filterMarkdown` runs on TTS input by default).
 *
 * Alignment arrives in messages finer-grained than a cue — often one word at a time — so
 * `held` carries the tail of an unclosed span across calls; pass the same array every time
 * and call once more with `final: true` at end of stream to release it.
 */
export declare function dropBracketCues(tokens: TimedString[], held: TimedString[], options?: {
    final?: boolean;
}): TimedString[];
/**
 * Fix common LLM markup mistakes for a provider.
 *
 * Closes opening tags that should be self-closing (e.g. the LLM writes
 * `<expression value="happy">` instead of `<expression value="happy"/>` — or
 * `<expr type="sound" label="laugh">` instead of `<expr type="sound" label="laugh"/>`).
 */
export declare function normalizeMarkup(provider: string, text: string): string;
/** Convert framework-standard markup to a provider's native syntax. */
export declare function convertMarkup(provider: string, text: string): string;
export {};
//# sourceMappingURL=provider_format.d.ts.map