Перейти к содержанию

Спецификация стадии

Спецификация стадии — YAML в её docstring: description, arguments, outputs и визуальные подсказки для редактора.

description: "Increment numeric value by delta"
icon: "+"          # глиф, ссылка на SVG, data-URI или <svg>-разметка
icon_mono: false    # перекрасить SVG в цвет узла (монохромные наборы)
color: "#ff8800"    # акцент карточки (по умолчанию — цвет категории)

icon принимает четыре формы:

Значение Что рисуется
"+", "👋" глиф или эмодзи
"/icons/globe.svg", "https://…/x.svg" SVG по ссылке
"data:image/svg+xml;utf8,…" data-URI
"<svg …>…</svg>" разметка из docstring

icon_mono: true рисует SVG маской в цвет узла — для монохромных наборов (lucide, feather, tabler), использующих currentColor. Без icon редактор рисует монограмму из имени стадии (IncrementStage → IS), без color — детерминированный цвет категории.

В get_specs() попадает ещё четыре вещи, и они задаются атрибутами класса, а не ключами докстринга: category, timeout, allowed_events и allowed_inputs (EventSpec / InputSpec с payload_schema).

В докстринге Атрибутом класса
description, icon, icon_mono, color category
arguments, outputs, reserve timeout
allowed_events, allowed_inputs

category: или timeout:, написанные в докстринге, разбираются и отбрасываются, и ничто об этом не предупреждает. get_specs() отдаёт значение атрибута, поэтому спека и дедлайн, который держит прогон, друг другу не противоречат — заметить нечего. Стадия, которая просит timeout: 90 только в докстринге, работает с дефолтом BaseStage, тридцатью секундами, и редактору сообщают те же тридцать.

У прозы есть языки, у идентификаторов нет

В спеке всё — либо идентификатор, либо проза. Имя, тип, категория, счётчик, цвет — идентификаторы, одинаковые на любом языке, и не переводятся никогда. description самой стадии и description каждого аргумента, выхода, события и входа — проза, а у прозы язык есть.

Поэтому любое из них может быть таблицей {локаль: текст} вместо строки:

description:
  en: "Increment numeric value by delta"
  ru: "Увеличивает число на delta"
arguments:
  delta:
    type: int
    description:
      en: "How much to add"
      ru: "Насколько увеличить"

А get_specs() отдаёт все языки, какие есть, а не выбирает один:

>>> IncrementStage.get_specs()["description"]
{'en': 'Increment numeric value by delta', 'ru': 'Увеличивает число на delta'}

Именно эта форма нужна клиенту. Редактор забирает спеки один раз, а его читатель выбирает язык потом — и выбирает заново всякий раз, когда передумает, — так что спеку, уже сведённую к одному языку, приходилось бы забирать снова на каждое такое «передумал». Спека несёт выбор; выбирает тот, кто её рисует.

Проза на одном языке остаётся обычной строкой: таблица из одной записи была бы выбором, не несущим информации:

>>> LoadTicketStage.get_specs()["description"]
'Takes a prepared ticket out of data/tickets.json'

Явно названная локаль прозу сворачивает — для того, кто действительно отвечает одному читателю, а не обслуживает клиента:

IncrementStage.get_specs(locale="ru")["description"]   # 'Увеличивает число на delta'

Таблица — это весь механизм для горстки стадий. Для сотни каталог gettext менее многословен, и встроенные стадии живут именно так — и о том и о другом в Локализации.

Что стадия просит зарезервировать

reserve объявляет, сколько прогон стадии может израсходовать, в тех счётчиках, которые считает хост. Значения — числа или CEL по args, то есть по аргументам в том виде, в каком их получит стадия:

reserve:
  llm_calls: 1
  tokens: "args.max_tokens + size(args.text) / 3"

Это объявлено, а не вычислено, потому что читается до запуска стадии: хост отказывает графу, который не может оплатить, не исполняя его, а редактор показывает цифру на карточке. Сколько потрачено на самом деле, сообщает сама стадия через self.charge(...) — и это уже код, потому что правда известна только в конце.

Вот во что спецификация выше превращается на холсте: иконка, описание, аргументы, которые узел читает, и выходы, которые он пишет.

Карточка стадии, нарисованная по спецификации