Kovati Docs

Concepts

Banks, lines, handles, and the Speech Library.

Documented in depthSpeechForge0.2.2Open source

The assets

Four asset types, all made the same way: Content Browser ▸ right-click ▸ Automation Forge ▸ SpeechForge.

The Content Browser create menu cascading three levels. Automation Forge is highlighted among engine categories such as Animation, Audio, Blueprint and Cinematics. The second column lists FaceForge, MeshForge, MontageForge, MotionForge, PerformanceForge, SpeechForge and SurfaceForge, with SpeechForge highlighted. The third column, headed SPEECHFORGE, offers Speaker, Speech Bank, Speech Line and Voice Profile.
Four entries, and in practice you make two of them by hand. Speech Banks and Speakers get created for you by harvesting a dialogue; Voice Profiles get created by the Cast page's Save as Profile.
AssetWhat it is
Speech Bank (SB_)Many lines, one scene
Speech LineA single line as its own asset
Speaker (SP_)Who is speaking — see casting
Voice Profile (VP_)One voice on one provider, reusable

Two asset shapes, one interface

A Speech Bank holds many lines. A single-line asset holds one. Both sit behind the same interface and are addressed by every tool through one handle type.

That means a tool never has to ask which shape it is looking at, and a project can start with loose lines and consolidate later without rewriting anything.

A line id is the naming scheme

The line id is the join. The staleness hash, the dialogue node lookup, the source-to-translation pairing and the runtime's "same line, other culture" lookup all key off it.

Generated ids are the naming scheme, not a placeholder. If you rename ids to something more readable after generating, the audio that was filed under the old id is orphaned — and a subsequent generation writes over whatever now holds the new one. Decide the id scheme before you generate at volume.

The Speech Library

Tools ▸ Speech Library, under the menu's Automation Forge heading — six pages behind one switcher:

PageWhat it is for
IngestBring lines in from somewhere else
CastSpeakers and voice profiles — see casting
WriteThe lines: text, direction, speaker, per-line voice override
ProduceThe pipeline table, the estimate, and Generate
PerformCarrying generated audio onward
LocalizeA sibling bank per language — see localisation
The Speech Library window on its Write page. A Bank picker at the top reads SB_GeneratorBriefing, beside Refresh. Below it a switcher shows six pages — Ingest, Cast, Write, Produce, Perform, Localize — with Write selected. A table lists seven lines with columns Line, Speaker, Text, Direction and Voice: GEN_Doric_Report through DLG_GeneratorBriefing_Pla..., spoken by Astronaut, Renk, Doric and Player, in the voices Roger, Callum, Will and Lily. Delete Selected and Clear All Lines sit at the foot.
One window, one bank, six pages. The switcher is the pipeline: everything left of the current page has already happened, everything right of it has not.

The bank picker at the top is the only global state. Every page below it is about the bank named there — which is why switching to a localised sibling changes what the pages offer rather than opening a second window.

The Write page's line editor

Selecting a line opens an editor under the table. It is where a line's text and direction are changed, and it is where the pipeline does its shouting.

The Write page with GEN_Doric_Rule selected and highlighted. Below the table, a panel headed 'Edit line - GEN_Doric_Rule' notes: 'Harvested from DLG_GeneratorBriefing via NarrativePro. An edit kept only here is overwritten by the next harvest - write it back to stay in step.' Two amber warnings follow: the script this bank came from has changed, DLG_GeneratorBriefing was written 2026.09.08-21.51.50 after this bank last read it; and 'A recorded performance is behind this line. Rewriting the text puts the subtitle and the performance out of step - fixing that is a re-record or a pickup session, never a regeneration.' Under them a text field reads 'Not alone, you are not. The suit rule stands.' and an empty Direction field hints 'Direction - whispers, exhausted, shouting. Never spoken, never in the subtitle.' Two buttons sit at the foot: Apply to bank and Apply to dialogue.
One line, three separate things the editor knows: where the words came from, that the source has moved since, and that a human performance is behind this particular line. The last one is in the panel before the edit, not after it.
FieldWhat it is
Text"What the player reads and what is spoken."
Direction"Direction — whispers, exhausted, shouting. Never spoken, never in the subtitle."
Apply to bank"Save onto the bank line. Changed text reads as stale in Produce — a priced report, never a silent regeneration."
Apply to dialogueThe write-back, when the bank has a source that supports one

The loud warning is the one about a recorded line. A generated line going stale is a re-read for a fraction of a cent. A recorded line going stale means the subtitle and a human performance now disagree, and the only fixes are a re-record or a pickup session.

That difference is shouted before the edit rather than reported after it. Once the text has moved, the panel escalates to "OUT OF STEP: this text no longer matches the recorded performance behind it. Regeneration will not touch a recorded line — re-record it or book a pickup."

"An edit kept only here is overwritten by the next harvest." A bank that reads from a dialogue is downstream of it. Apply to dialogue is the other half of the contract — it pushes the change back up, so the next harvest agrees with you instead of undoing you.

Sibling plugins add buttons without editing this

The pages have a reflection-based discovery seam. A sibling plugin marks a function with the right metadata and its button appears — no edit to SpeechForge, no dependency in either direction.

That is how the face and performance sets put their own actions on the Perform page — and how a dialogue framework puts Apply Voice and Faces to Dialogue there without SpeechForge knowing what a dialogue is.

There are three kinds of tag, and they land in different places:

Tag on a toolset functionBecomes
SpeechLibraryAction = "Label"A button, on the page named by SpeechLibraryPage
SpeechIngest = "Label"An ingest method, with the asset class it reads
SpeechLineSync = "Label"A write-back button in the line editor, shown only on banks whose source stamp carries that adapter's key

In the project these captures come from, every button below is contributed:

ButtonPageFrom
Harvest from DialogueIngestthe Narrative Pro adapter
Harvest Characters…Ingestthe Narrative Pro adapter
Re-harvest from DialogueIngestthe Narrative Pro adapter
Apply to dialoguethe line editorthe Narrative Pro adapter
Apply Voice to DialoguePerformthe Narrative Pro adapter
Apply Voice and Faces to DialoguePerformthe Narrative Pro adapter
Create/Update Face BankPerformFaceForgeToolset
Record Selected…PerformPerformanceForgeToolset
Plan Session…PerformPerformanceForgeToolset

The Ingest page exists exactly when at least one ingest method is found. It is not a page that can be empty — a project with no adapter installed does not have one, and the Library opens on Cast instead.

The Perform page is the seam made visible: almost every button on it arrived from a different plugin, and a project missing one simply sees fewer.

Console commands

There are no in-editor buttons on the settings page, because CallInEditor buttons do not render on a developer-settings page — a real engine limitation, not an oversight. So:

SpeechForge.CredentialStatus   # is a key available, and where is it read from
SpeechForge.TestConnection     # one cheap authenticated call; reports tier and remaining credits
SpeechForge.ClearKey           # forget the stored key

TestConnection reporting remaining credits rather than just validity is the useful part. "Your key works" and "you can afford this batch" are different facts.

Keys

Declared in the plugin's machine configuration and read from the OS credential vault — shared with the hub and the plugin's own settings page, so a key set in any of them is set for all.

See keys.

Takes

A generated result is a take. Takes are kept, and a provider that supports it can re-fetch a past take from account history for free rather than re-synthesising it.

That matters more than it sounds on a metered provider: recovering a take you already paid for should not cost again.

On this page