Parlora.Event (Parlora v0.1.0)

Copy Markdown View Source

A live observation, independent of business completion or confirmed playback.

Match source and type before reading payload. Named payload types describe supported common fields; adapter-specific additions are open map entries, not universal guarantees. Correlation IDs belong to the envelope and may be nil when no command/request can be identified. observed_at_us is signed local monotonic time. Event IDs do not imply exactly-once delivery or global ordering.

t/2 describes a narrowed event, for example t(:request_completed, Parlora.BackendResult.t()). Runtime messages are trusted adapter observations, not a validated serialization format. Events can contain transcripts and application data; applications control retention and disclosure.

Summary

Types

adapter_closed()

@type adapter_closed() :: %{
  optional(:finalized?) => boolean(),
  optional(:usage) => map() | nil,
  optional(:uncertain_requests) => [String.t()],
  optional(:scope) => atom(),
  optional(:call_ended?) => boolean() | :unknown,
  optional(atom()) => term()
}

adapter_ready()

@type adapter_ready() :: %{
  :capabilities =>
    Parlora.Channel.Capabilities.t()
    | Parlora.Provider.Capabilities.t()
    | Parlora.Backend.Capabilities.t(),
  optional(:format) => Parlora.AudioFormat.t(),
  optional(atom()) => term()
}

audio_observation()

@type audio_observation() ::
  %{
    cause: :startup_capture_discard,
    boundary: :browser_rtp,
    packets: non_neg_integer(),
    duration_us: non_neg_integer()
  }
  | %{
      cause: :source_queue_empty | :source_resumed | :cleared | :closed,
      boundary: :browser_pcm,
      gap_id: pos_integer(),
      epoch: non_neg_integer(),
      started_at_us: integer(),
      duration_us: non_neg_integer(),
      silence_bytes: non_neg_integer(),
      inserted_silence_us: non_neg_integer(),
      peak_before: non_neg_integer(),
      peak_after: non_neg_integer() | nil,
      source_returned_at_us: integer() | nil,
      source_return_queue_bytes: non_neg_integer() | nil,
      resume_queue_bytes: non_neg_integer() | nil,
      source_unavailable_us: non_neg_integer(),
      recovery_wait_us: non_neg_integer(),
      recovery_target_us: non_neg_integer() | nil,
      recovery_release_reason:
        :deadline
        | :pressure
        | :buffering_disabled
        | :queue_ready
        | :cleared
        | :closed
        | nil
    }

Browser media measurements, not evidence of lost speech or confirmed audibility.

clarification()

@type clarification() :: %{
  delegation_id: String.t() | nil,
  task_id: String.t() | nil,
  task_revision: pos_integer() | nil
}

context_outcome()

@type context_outcome() :: %{
  optional(:error) => Parlora.Error.t(),
  optional(:outcome) => :unknown | :unconfirmed,
  optional(:acceptance) => atom(),
  optional(atom()) => term()
}

context_wait()

@type context_wait() :: %{
  target: {:delegation, String.t()} | {:task, String.t(), pos_integer()},
  recheck_after_ms: 1..60000
}

delegation()

@type delegation() :: %{
  :id => String.t(),
  :target => :client,
  :offset_ms => number() | nil,
  optional(atom()) => term()
}

delegation_associated()

@type delegation_associated() :: %{
  id: String.t(),
  task_id: String.t(),
  task_revision: pos_integer()
}

discontinuity()

@type discontinuity() :: %{
  :cause => atom(),
  optional(:dropped_bytes) => non_neg_integer(),
  optional(:dropped_frames) => non_neg_integer(),
  optional(:dropped_duration_us) => non_neg_integer(),
  optional(atom()) => term()
}

error_payload()

@type error_payload() :: %{:error => Parlora.Error.t(), optional(atom()) => term()}

playback()

@type playback() :: %{
  :stage => Parlora.Status.playback_stage(),
  :evidence => Parlora.Channel.Capabilities.evidence(),
  optional(:stream_id) => String.t(),
  optional(:sequence) => non_neg_integer(),
  optional(:epoch) => non_neg_integer(),
  optional(:first_sample) => non_neg_integer(),
  optional(:end_sample) => non_neg_integer(),
  optional(atom()) => term()
}

request_started()

@type request_started() :: %{
  :task_revision => pos_integer(),
  optional(atom()) => term()
}

result_delivery()

@type result_delivery() :: %{
  :stage => Parlora.Status.delivery(),
  optional(:task_id) => String.t(),
  optional(:task_revision) => pos_integer(),
  optional(:delegation_id) => String.t() | nil
}

session_closed()

@type session_closed() :: %{
  health: Parlora.SessionHealth.t(),
  finalized?: boolean(),
  usage: map() | nil,
  jobs: %{optional(String.t()) => Parlora.Status.job_state()}
}

session_ready()

@type session_ready() :: %{
  capabilities: %{
    channel: Parlora.Channel.Capabilities.t(),
    provider: Parlora.Provider.Capabilities.t(),
    backend: Parlora.Backend.Capabilities.t()
  }
}

source()

@type source() :: :session | :channel | :provider | :backend

t()

@type t() ::
  t(:transcript_delta, transcript())
  | t(:delegation_requested, delegation())
  | t(:delegation_pending, %{delegation_id: String.t()})
  | t(:delegation_associated, delegation_associated())
  | t(:task_submitted, task_submission())
  | t(:task_queued, task_identity())
  | t(:request_started, request_started())
  | t(:request_progress, Parlora.ContextSnapshot.progress())
  | t(:request_clarification, Parlora.ContextSnapshot.backend_clarification())
  | t(
      :request_completed | :request_failed | :request_cancelled,
      Parlora.BackendResult.t()
    )
  | t(:stale_result, %{result: Parlora.BackendResult.t()})
  | t(:result_delivery, result_delivery())
  | t(:clarification_requested, clarification())
  | t(:context_wait_started, context_wait())
  | t(
      :context_acknowledged | :context_rejected | :context_submitted,
      context_outcome()
    )
  | t(:delegation_cancelled, %{id: String.t(), reason: atom()})
  | t(:output_recovery_requested, %{
      token: pos_integer(),
      cause: :output_overflow | :output_stale
    })
  | t(:output_recovered, %{:token => pos_integer(), optional(atom()) => term()})
  | t(:output_interrupted, %{token: non_neg_integer()})
  | t(:provider_activity, %{state: :in_progress | :idle})
  | t(:provider_speech, %{state: :speaking | :idle})
  | t(:response_status, %{
      status: atom(),
      cause: atom(),
      elapsed_ms: non_neg_integer(),
      evidence: :local_observation
    })
  | t(:provider_connection, map())
  | t(:progress_changed, Parlora.SessionHealth.expectation())
  | t(:playback_observed, playback())
  | t(:audio_discontinuity, discontinuity())
  | t(:channel, :audio_observed, audio_observation())
  | t(
      :failed | :policy_failed | :policy_rejected | :admission_rejected,
      error_payload()
    )
  | t(:session, :ready, session_ready())
  | t(:channel | :provider | :backend, :ready, adapter_ready())
  | t(:session, :closed, session_closed())
  | t(:channel | :provider | :backend, :closed, adapter_closed())
  | t(:adapter_down, %{
      role: :channel | :provider | :backend | :input_pipeline | :output_pipeline
    })
  | t(
      :disconnected
      | :request_uncertain
      | :update_applied
      | :update_rejected
      | :update_uncertain
      | :cancellation_requested
      | :cancellation_rejected
      | :cancellation_unconfirmed
      | :control_completed
      | :control_failed
      | :usage_updated
      | :usage_final
      | :extension,
      map()
    )

t(event_type, event_payload)

@type t(event_type, event_payload) :: t(source(), event_type, event_payload)

t(event_source, event_type, event_payload)

@type t(event_source, event_type, event_payload) :: %Parlora.Event{
  adapter_id: String.t(),
  command_id: String.t() | nil,
  generation: non_neg_integer(),
  id: String.t(),
  metadata: map(),
  observed_at_us: integer(),
  payload: event_payload,
  request_id: String.t() | nil,
  session_id: String.t(),
  source: event_source,
  type: event_type
}

task_identity()

@type task_identity() :: %{task_id: String.t(), task_revision: pos_integer()}

task_submission()

@type task_submission() :: %{
  request: Parlora.BackendRequest.t(),
  admission: :ok | {:error, Parlora.Error.t()}
}

transcript()

@type transcript() :: %{
  :speaker => Parlora.ContextSnapshot.speaker(),
  :delta => String.t(),
  :start_ms => number() | nil,
  :end_ms => number() | nil,
  optional(:segment_id) => String.t(),
  optional(atom()) => term()
}

Functions

emit(init, source, type, payload \\ %{}, fields \\ [])

@spec emit(Parlora.AdapterInit.t(), atom(), atom(), term(), keyword()) :: t()