Skip to the content.

Inference Runtime Guide

core/inference is the unified, instance-owned runtime for model inference. All workloads are one of Generate, Embed, Transcription, or Realtime.

Exact addressing

Every call takes a concrete ModelRef:

model := inference.ModelRef{
    ID: inference.ModelID{
        Provider: "deepseek",
        Name:     "deepseek-v4-flash",
    },
}

The runtime never picks or replaces a model. Optional routing is provided by core/inference/route. Route selection consults the providers' declared model capabilities: targets whose declared output kinds cannot serve the request intent are skipped, while models with undeclared capabilities are treated as undeclared (not unsupported) — preflight remains the final arbiter for those.

Deployment config

Providers and the assembly are separate resources:

resources:
  provider:
    kind: inference.Provider
    impl: deepseek
    settings:
      id: deepseek
      profiles:
        - secrets:
            api_key: ${env:DEEPSEEK_API_KEY}
  infer:
    kind: inference.Assembly
    impl: unified
    deps:
      provider: provider

Provider implementations are registered by the application from provider driver modules:

reg.MustRegister(deepseek.NewFactory())
reg.MustRegister(inference.Factory{})

Routing

Optional target selection is an inference.Router resource. It consumes one inference.Assembly as its target dep and reads the route policy from its own settings:

resources:
  router:
    kind: inference.Router
    impl: unified
    deps:
      target: infer
    settings:
      generate:
        - tier: fast
          targets:
            - model: {id: {provider: deepseek, name: deepseek-v4-flash}}
              score: {quality: 0.8, speed: 0.9}
      retry:
        generate:
          max_attempts: 2
          max_total_attempts: 5
          backoff:
            kind: exponential   # fixed | exponential (default)
            initial: 100ms
            max: 2s
            multiplier: 2
            jitter: full        # none | equal | full (default)
          retryable: [rate_limit, timeout, unavailable]
          fallback_on_retry_exhausted: true
      circuit_breaker:
        failure_threshold: 5      # consecutive transient failures; default 5
        recovery_window: 30s      # open-circuit window; default 30s
        half_open_max_probes: 1   # concurrent probes while half-open; default 1

The policy has three operation areas — generate, embed, and transcription — each a list of tier pools. A pool is an allowlist of exact model targets plus optional normalized score signals (quality / economy / speed / reliability, all in [0, 1]). Scores guide selection only; they never claim a request is executable.

Streaming

GenerateStream returns a stream of deltas plus the final result. Streaming is provider-neutral; each driver adapts its native protocol.

Media streams

Live media input is a first-class transport, not a new DTO: a stream is a sequence of ordinary message.Parts. The media layer owns the generic pull contract (media.Stream[T] / media.Pipe[T]); message.Stream is that contract instantiated over Part, and message.NewPartPipe builds a bounded pipe whose Send blocks when the buffer is full — that is the backpressure contract. Interrupt aborts a stream (barge-in, error), while Close ends it normally; after Interrupt, Read returns context.Canceled even if buffered parts remain.

An AudioSource or VideoSource can carry a live stream via message.NewAudioStream / message.NewVideoStream (source kind stream). Stream sources are valid only while a message is in flight:

Transcription

Speech recognition is a first-class workload with two execution shapes:

Both shapes address the same ModelRef and share the Transcription route pools. Drivers that only serve one shape leave the other opener nil and the assembly reports UnsupportedOperation for it.

Sessions may emit multiple Final events for continuous recognition. A provider session can expose the optional TranscriptionSessionFinisher capability: callers that have no more audio call FinishInput, then drain Next to io.EOF and read Result. TranscribeStream performs that end-of-input handshake automatically after the source stream ends; the script bridge exposes it as the session handle's finish().

Live input rides the part-stream transport: FeedTranscription pumps a message.Stream[Part] into an open session (audio parts become chunks with monotonic sequence; EOF ends feeding; a stream failure interrupts the session), and TranscribeStream is the one-shot open + feed + drain + result form. Unary Transcribe rejects stream sources — whole-file recognition takes complete audio, live audio goes through a session.

See graph.md for the inference node.