Files
esh-pfi-infrastructure/docs/asset-engine/services.schema.json
T
vh 95f24573e4 catalog: status: down for the 6 unreachable services; StatusT extended
Per althing thread 01KRCNSF0V5NDCKB34H663MXHS — the catalog declared
14 services but 6 of them aren't running on irv-ml1 (chatterbox,
index-tts, qwen3-tts, cosyvoice, voxtral, kyutai-tts; missing from
docker ps entirely). Without action, the asset-engine UI would
declare them as available and consumers would hit unreachable
endpoints.

asset-engine consumer chose option (1) of three I sketched: extend
StatusT with `down` and treat it identically to `catalog-deferred`
in the picker (greyed, non-clickable). Lightweight, declarative, no
runtime health-check machinery, easy to revert when services
return.

Changes:

- StatusT enum (in asset_engine/catalog.py — committed there
  separately) extended from
    Literal["ready", "catalog-deferred", "experimental"]
  to
    Literal["ready", "catalog-deferred", "experimental", "down"]
- 6 services flipped to status: down.
- CATALOG-CONTRACT.md: replaced the bare-enum status row with a
  four-row sub-table that names each value's meaning AND its picker
  behavior. `down` and `catalog-deferred` get the same UI treatment
  but the tooltip text differentiates ("Catalog-deferred" vs
  "Service down — temporarily unreachable on irv-ml1") so the
  semantic distinction (design state vs fleet-ops state) is
  preserved.
- CATALOG-CONTRACT.md versioning policy table: new row codifying
  "extending an existing enum (StatusT, FieldTypeT, ResponseTypeT,
  CategoryT) with a non-conflicting value, with the consumer
  updated in the same coordinated change" → no catalog_version
  bump. Explicit rule for future enum extensions.
- JSON Schema regenerated.

catalog_version stays at 1.

Operational note (not catalog-side): the down services likely got
reaped 13+ days ago per the docker timestamps when other unrelated
work was done on irv-ml1. Bringing them back is a deploy task
outside this commit's scope. Flip status: down → ready in this file
once each one's confirmed running.
2026-05-11 16:31:53 -07:00

703 lines
16 KiB
JSON

{
"$defs": {
"CatalogAuditEntry": {
"additionalProperties": false,
"properties": {
"service": {
"title": "Service",
"type": "string"
},
"seedable": {
"title": "Seedable",
"type": "boolean"
},
"model_deterministic": {
"title": "Model Deterministic",
"type": "boolean"
},
"image_tag_mutable": {
"title": "Image Tag Mutable",
"type": "boolean"
},
"notes": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Notes"
}
},
"required": [
"service",
"seedable",
"model_deterministic",
"image_tag_mutable"
],
"title": "CatalogAuditEntry",
"type": "object"
},
"CatalogField": {
"additionalProperties": false,
"description": "One request parameter, rendered as one form control.",
"properties": {
"name": {
"title": "Name",
"type": "string"
},
"type": {
"enum": [
"text",
"textarea",
"number",
"slider",
"select",
"bool",
"file",
"json"
],
"title": "Type",
"type": "string"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Label"
},
"required": {
"default": true,
"title": "Required",
"type": "boolean"
},
"optional": {
"default": false,
"title": "Optional",
"type": "boolean"
},
"default": {
"default": null,
"title": "Default"
},
"min": {
"anyOf": [
{
"type": "integer"
},
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Min"
},
"max": {
"anyOf": [
{
"type": "integer"
},
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Max"
},
"step": {
"anyOf": [
{
"type": "integer"
},
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Step"
},
"options": {
"anyOf": [
{
"items": {
"anyOf": [
{
"type": "string"
},
{
"$ref": "#/$defs/FieldOption"
}
]
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"title": "Options"
},
"source_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Source Url"
},
"source_jsonpath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Source Jsonpath"
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Description"
},
"max_length": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Max Length"
},
"accepted_types": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"title": "Accepted Types"
},
"section": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Section"
}
},
"required": [
"name",
"type"
],
"title": "CatalogField",
"type": "object"
},
"CatalogLatency": {
"additionalProperties": false,
"properties": {
"cold_start_s": {
"anyOf": [
{
"type": "integer"
},
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Cold Start S"
},
"warm_per_unit": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Warm Per Unit"
}
},
"title": "CatalogLatency",
"type": "object"
},
"CatalogModel": {
"additionalProperties": false,
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"revision": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Revision"
},
"image": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Image"
}
},
"required": [
"id"
],
"title": "CatalogModel",
"type": "object"
},
"CatalogReproducibility": {
"additionalProperties": false,
"properties": {
"seedable": {
"title": "Seedable",
"type": "boolean"
},
"deterministic": {
"title": "Deterministic",
"type": "boolean"
},
"notes": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Notes"
}
},
"required": [
"seedable",
"deterministic"
],
"title": "CatalogReproducibility",
"type": "object"
},
"CatalogResponse": {
"additionalProperties": false,
"description": "How to interpret the inference response.\n\nThe `type` is the *renderer dispatch* \u2014 what kind of asset the user\nultimately sees (audio player, text block, etc). The other fields\ndescribe how to *extract* that asset from the wire shape:\n\n - Raw-bytes wire (e.g. /v1/audio/speech returns raw audio):\n type: audio\n mime: audio/wav (or mime_from_field for dynamic)\n\n - JSON-envelope wire with base64-encoded asset (e.g. captioned\n speech returns {audio: <b64>, audio_format: ..., timestamps: ...}):\n type: audio\n audio_field: audio # JSON key with the base64 bytes\n audio_format_field: audio_format # JSON key with the MIME\n timestamps_field: timestamps # optional structured aside\n\n - JSON-envelope wire with text payload (e.g. ASR transcript):\n type: text\n output_field: text # JSON key with the text body\n timestamps_field: segments # optional, for ASR-with-timestamps\n\nRules:\n - When audio_field is set, the renderer expects a JSON envelope and\n will base64-decode that key. The wire MIME is application/json\n regardless of `mime`; `mime_from_field` is incompatible.\n - timestamps_field is independent of type \u2014 any service that\n emits structured timestamps alongside its primary output can\n declare it; the renderer extends accordingly.",
"properties": {
"type": {
"enum": [
"audio",
"image",
"video",
"text",
"json",
"file"
],
"title": "Type",
"type": "string"
},
"mime": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Mime"
},
"mime_from_field": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Mime From Field"
},
"output_field": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Output Field"
},
"audio_field": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Audio Field"
},
"audio_format_field": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Audio Format Field"
},
"timestamps_field": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Timestamps Field"
}
},
"required": [
"type"
],
"title": "CatalogResponse",
"type": "object"
},
"CatalogSectionGroup": {
"additionalProperties": false,
"description": "A named group of fields, for progressive disclosure in form rendering.\n\nServices with many fields (ace-step has 27) declare section_groups and\ntag each field with a `section: <id>`. Consumers render fields grouped\nunder tabs / accordions / etc. Fields without a section go into an\nimplicit 'main' group rendered first.",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"label": {
"title": "Label",
"type": "string"
},
"hint": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Hint"
}
},
"required": [
"id",
"label"
],
"title": "CatalogSectionGroup",
"type": "object"
},
"CatalogService": {
"additionalProperties": false,
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Description"
},
"category": {
"enum": [
"tts",
"asr",
"sfx",
"music",
"image",
"multimodal"
],
"title": "Category",
"type": "string"
},
"version": {
"title": "Version",
"type": "integer"
},
"status": {
"default": "ready",
"enum": [
"ready",
"catalog-deferred",
"experimental",
"down"
],
"title": "Status",
"type": "string"
},
"host": {
"title": "Host",
"type": "string"
},
"endpoint": {
"title": "Endpoint",
"type": "string"
},
"method": {
"default": "POST",
"title": "Method",
"type": "string"
},
"content_type": {
"default": "application/json",
"title": "Content Type",
"type": "string"
},
"model": {
"$ref": "#/$defs/CatalogModel"
},
"fields": {
"items": {
"$ref": "#/$defs/CatalogField"
},
"title": "Fields",
"type": "array"
},
"section_groups": {
"items": {
"$ref": "#/$defs/CatalogSectionGroup"
},
"title": "Section Groups",
"type": "array"
},
"response": {
"$ref": "#/$defs/CatalogResponse"
},
"reproducibility": {
"$ref": "#/$defs/CatalogReproducibility"
},
"estimated_latency": {
"anyOf": [
{
"$ref": "#/$defs/CatalogLatency"
},
{
"type": "null"
}
],
"default": null
},
"license": {
"title": "License",
"type": "string"
},
"license_warning": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "License Warning"
},
"notes": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Notes"
}
},
"required": [
"id",
"name",
"category",
"version",
"host",
"endpoint",
"model",
"response",
"reproducibility",
"license"
],
"title": "CatalogService",
"type": "object"
},
"FieldOption": {
"additionalProperties": false,
"description": "A select option. May be just the value (when label = value) or {value, label}.",
"properties": {
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
},
{
"type": "number"
},
{
"type": "boolean"
}
],
"title": "Value"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Label"
}
},
"required": [
"value"
],
"title": "FieldOption",
"type": "object"
}
},
"additionalProperties": false,
"description": "Schema for the canonical inference-services catalog. The Python Pydantic model in asset_engine/catalog.py is the source of truth; this JSON Schema is a derived view for non-Python consumers.",
"properties": {
"catalog_version": {
"default": 1,
"title": "Catalog Version",
"type": "integer"
},
"generated_at": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Generated At"
},
"services": {
"items": {
"$ref": "#/$defs/CatalogService"
},
"title": "Services",
"type": "array"
},
"reproducibility_audit": {
"items": {
"$ref": "#/$defs/CatalogAuditEntry"
},
"title": "Reproducibility Audit",
"type": "array"
}
},
"required": [
"services"
],
"title": "Asset Engine \u2014 services.yaml catalog",
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://eshpfi.local/schemas/services.schema.json"
}