harn-stdlib 0.10.53

Embedded Harn standard library source catalog
Documentation
import { MediaAsset } from "std/media/asset"

/** Provider-neutral contracts for asynchronous model work. */
pub type ModelTask = "image.generate" \
  | "image.edit" \
  | "audio.generate" \
  | "video.generate" \
  | "embedding" \
  | "custom"

pub type ModelJobState = "queued" | "running" | "succeeded" | "failed" | "canceled"

pub type ModelOutputSpec = {
  mime_type: string,
  width?: int,
  height?: int,
  duration_ms?: int,
  count?: int,
}

pub type ModelJobRequest = {
  id: string,
  task: ModelTask,
  prompt: string,
  output: ModelOutputSpec,
  model?: string,
  inputs?: list<MediaAsset>,
  seed?: int,
  params?: dict,
  metadata?: dict,
}

pub type ModelJobOutput = {
  name: string,
  mime_type: string,
  bytes?: bytes,
  path?: string,
  url?: string,
  width?: int,
  height?: int,
  duration_ms?: int,
  metadata?: dict,
}

pub type ModelJobErrorKind = "invalid_request" \
  | "backend" \
  | "invalid_state" \
  | "invalid_transition" \
  | "timeout" \
  | "canceled" \
  | "malformed_output" \
  | "asset_mismatch" \
  | "replay_mismatch"

pub type ModelJobError = {
  kind: ModelJobErrorKind,
  message: string,
  retryable: bool,
  backend?: string,
  job_id?: string,
  detail?: unknown,
}

pub type ModelJobObservation = {
  job_id: string,
  state: ModelJobState,
  backend_state?: string,
  progress?: float,
  outputs?: list<ModelJobOutput>,
  error?: ModelJobError,
  metadata?: dict,
}

pub type ModelJob = {
  schema: "harn.model_job.v1",
  id: string,
  request_id: string,
  request_digest: string,
  backend: string,
  state: ModelJobState,
  backend_state?: string,
  progress?: float,
  outputs?: list<ModelJobOutput>,
  error?: ModelJobError,
  submitted_at_ms: int,
  updated_at_ms: int,
  metadata?: dict,
}

pub type ModelJobEventKind = "submitted" | "state_changed" | "progress" | "output" | "failed"

pub type ModelJobEvent = {
  schema: "harn.model_job_event.v1",
  kind: ModelJobEventKind,
  job_id: string,
  request_id: string,
  backend: string,
  state: ModelJobState,
  at_ms: int,
  previous_state?: ModelJobState,
  progress?: float,
  output_count?: int,
  error?: ModelJobError,
}

pub type ModelJobReceipt = {
  schema: "harn.model_job_receipt.v1",
  request_digest: string,
  backend: string,
  job: ModelJob,
  assets: list<MediaAsset>,
  events: list<ModelJobEvent>,
}

/** Progress record for interactive views and long-running work. */
pub type ModelJobRun = {
  schema: "harn.model_job_run.v1",
  job: ModelJob,
  events: list<ModelJobEvent>,
  started_at_ms: int,
  attempts: int,
}

pub type ModelBackend = {
  id: string,
  submit: fn(Harness, ModelJobRequest) -> Result<ModelJobObservation, ModelJobError>,
  inspect: fn(Harness, ModelJob) -> Result<ModelJobObservation, ModelJobError>,
  cancel: fn(Harness, ModelJob) -> Result<ModelJobObservation, ModelJobError>,
}

/**
 * Build a stable error record for backends and callers.
 *
 * @effects: []
 * @errors: []
 */
pub fn model_job_error(kind: ModelJobErrorKind, message: string, options = {}) -> ModelJobError {
  const opts = options ?? {}
  let error: ModelJobError = {kind: kind, message: message, retryable: opts?.retryable ?? false}
  if opts?.backend != nil {
    error.backend = to_string(opts.backend)
  }
  if opts?.job_id != nil {
    error.job_id = to_string(opts.job_id)
  }
  if opts?.detail != nil {
    error.detail = opts.detail
  }
  return error
}

/**
 * Map an untrusted provider status into Harn's closed state vocabulary.
 *
 * @effects: []
 * @errors: []
 */
pub fn model_job_state_result(
  raw_state: unknown,
  backend = nil,
  job_id = nil,
) -> Result<ModelJobState, ModelJobError> {
  const state = lowercase(trim(to_string(raw_state ?? "")))
  if state == "queued" || state == "pending" || state == "created" || state == "submitted" {
    return Ok("queued")
  }
  if state == "running" || state == "processing" || state == "in_progress" {
    return Ok("running")
  }
  if state == "succeeded" || state == "success" || state == "completed" || state == "complete" {
    return Ok("succeeded")
  }
  if state == "failed" || state == "error" || state == "errored" {
    return Ok("failed")
  }
  if state == "canceled" || state == "cancelled" {
    return Ok("canceled")
  }
  return Err(
    model_job_error(
      "invalid_state",
      "unknown model job state: " + to_string(raw_state),
      {backend: backend, job_id: job_id, detail: raw_state},
    ),
  )
}

/**
 * Terminal states never transition to a different state.
 *
 * @effects: []
 * @errors: []
 */
pub fn model_job_transition_result(
  previous: ModelJobState,
  next: ModelJobState,
  backend = nil,
  job_id = nil,
) -> Result<ModelJobState, ModelJobError> {
  if previous == next {
    return Ok(next)
  }
  const terminal = previous == "succeeded" || previous == "failed" || previous == "canceled"
  if terminal {
    return Err(
      model_job_error(
        "invalid_transition",
        "model job cannot transition from "
          + to_string(previous)
          + " to "
          + to_string(next),
        {backend: backend, job_id: job_id},
      ),
    )
  }
  if previous == "queued"
    && (next == "running" || next == "succeeded" || next == "failed"
    || next
    == "canceled") {
    return Ok(next)
  }
  if previous == "running" && (next == "succeeded" || next == "failed" || next == "canceled") {
    return Ok(next)
  }
  return Err(
    model_job_error(
      "invalid_transition",
      "model job cannot transition from " + to_string(previous) + " to " + to_string(next),
      {backend: backend, job_id: job_id},
    ),
  )
}

/**
 * Stable identity used by replay to reject the wrong request.
 *
 * @effects: []
 * @errors: []
 */
pub fn model_job_request_digest(request: ModelJobRequest) -> string {
  let inputs = []
  for input in request.inputs ?? [] {
    inputs = inputs + [{id: input.id, mime_type: input.mime_type, sha256: input.sha256}]
  }
  return sha256(
    json_stringify(
      {
        task: request.task,
        prompt: request.prompt,
        output: request.output,
        model: request.model,
        inputs: inputs,
        seed: request.seed,
        params: request.params,
      },
    ),
  )
}

/**
 * Validate the provider-neutral request before any backend is called.
 *
 * @effects: []
 * @errors: []
 */
pub fn model_job_request_result(
  request: ModelJobRequest,
) -> Result<ModelJobRequest, ModelJobError> {
  if trim(request.id) == "" {
    return Err(model_job_error("invalid_request", "model job request id is required"))
  }
  if trim(request.prompt) == "" && request.task != "embedding" {
    return Err(model_job_error("invalid_request", "model job prompt is required"))
  }
  if trim(request.output.mime_type) == "" {
    return Err(model_job_error("invalid_request", "model job output MIME type is required"))
  }
  if (request.output.count ?? 1) < 1 {
    return Err(model_job_error("invalid_request", "model job output count must be positive"))
  }
  return Ok(request)
}