Documentation

Providers

The four interfaces, what is implemented, and the budget gate.

Four provider interfaces, one rule: no vendor name appears in an agent. An agent asks for text, voice, image, video or music, and configuration decides who serves it.

The two questions

Every provider has two independent answers, and conflating them is how a project comes to believe it is licensed when it is merely configured.

Question Field Values
Can it serve a request right now? status AVAILABLE, CONFIGURED, DEGRADED, UNAVAILABLE
May its output be monetized? commercial_use LICENSE_VERIFIED, UNVERIFIED, PROHIBITED, NOT_APPLICABLE

A provider can be AVAILABLE and UNVERIFIED simultaneously. That combination may be used for development and may not enter a monetized publication. PROHIBITED may not run at all.

A third field, implemented, separates "declared in configuration" from "an adapter exists". Kokoro is declared and unimplemented; it reports as such rather than being silently omitted or quietly simulated.

Current standing

Kind Provider Implemented Enabled Status Commercial use Cost class
text manual_text yes yes AVAILABLE NOT_APPLICABLE FREE_LOCAL
text deepseek yes no UNAVAILABLE UNVERIFIED PAID
voice manual_voice yes yes AVAILABLE NOT_APPLICABLE FREE_LOCAL
voice kokoro no no UNAVAILABLE UNVERIFIED FREE_LOCAL
image manual_image yes yes AVAILABLE NOT_APPLICABLE FREE_LOCAL
image comfyui no no UNAVAILABLE UNVERIFIED FREE_LOCAL
video manual_video yes yes AVAILABLE NOT_APPLICABLE FREE_LOCAL
video ffmpeg no no UNAVAILABLE LICENSE_VERIFIED FREE_LOCAL
music manual_music yes yes AVAILABLE NOT_APPLICABLE FREE_LOCAL
music youtube_audio_library no no UNAVAILABLE UNVERIFIED FREE_EXTERNAL

No model has been downloaded and no paid adapter is active. ffmpeg is not installed on the current machine, so even the assembly path is unavailable until it is.

Configuration

config/providers.toml at the repository root. Path overridable with GIA_PROVIDER_CONFIG.

[text.deepseek]
enabled = false          # enabling is not authorizing; the budget gate still refuses PAID calls
model = "deepseek-flash"
input_per_mtok = 0.28
output_per_mtok = 0.42
license_status = "UNVERIFIED"
cost_class = "PAID"

Pricing lives in configuration so an outdated number in source cannot become a silent billing surprise. Provider credentials come from the environment only — GIA_DEEPSEEK_API_KEY — and are never written to configuration, logs, or the dashboard.

The budget gate

app/budget.py. One question, one answer, before any call leaves the process.

  • FREE_LOCAL / FREE_EXTERNAL → allowed, no invoice possible.
  • PAID with a zero authorized ceiling → REQUIRES_AUTHORIZATION.
  • PAID with per-request approval on → REQUIRES_AUTHORIZATION, reported differently from a zero ceiling, because "raise the ceiling" and "approve this call" are different decisions.
  • PAID exceeding the remaining ceiling → BLOCKED.
  • UNKNOWN → BLOCKED. An undeclared cost is treated as a possible charge.

There is also a rule at the boundary of the text adapters: the coding agent's access to a model is not application access. DeepSeekTextProvider requires its own key for this process, refuses to run when disabled, and never scrapes a consumer interface or replays a session.

Manual import is a first-class path

With no authorized API access, manual import is how a production actually moves. It is therefore as traceable as an automated path:

  • Text: <manual_drop_dir>/<task_id>.json ({"text": "...", "author": "..."}) or .txt.
  • Voice: <segment_id>.wav / .mp3 per segment, so one paragraph can be re-recorded alone.
  • Image: <character_id>.png, with the prompt recorded for the operator.
  • Video: a finished render, imported with provenance.
  • Music: music.json catalogue; a track without title, creator, source and license is refused, because an unlicensed track cannot enter a production.

An absent artifact raises ManualInputRequired, classified HUMAN_APPROVAL — not a technical failure. The job resumes from that point; nothing upstream is regenerated.

Failure classification

Failures are not interchangeable, and merging them hides what actually went wrong:

Class Meaning Response
TECHNICAL transport, timeout, 5xx, rate limit retry with backoff
LICENSING rights unknown or refused stop; do not substitute a provider
THEOLOGICAL the audit gate refused the content revise, never publish
QUALITY produced, but below standard re-render, do not downgrade
BUDGET would incur unauthorized spend stop; ask the owner
HUMAN_APPROVAL a person must supply or decide something wait; resume from here

An insufficient-balance response is a BUDGET failure, not a technical one, and topping up is never automatic.

Adding an adapter

  1. Implement the interface in app/providers/.
  2. Declare a ProviderSpec with honest status, commercial_use, and cost_class.
  3. Register it in _build_adapter.
  4. Add it to config/providers.toml, disabled.
  5. Prove the refusals in a test before proving the happy path.

Installing a model, downloading a checkpoint, or verifying a commercial licence are separate steps from writing an adapter, and each needs its own approval.

Source

Rendered from docs/PROVIDERS.md at build time. Return to the documentation index.