O.G.A.S. English

Модули

Что такое модуль и как платформа с ним обходится: папка и версии, режимы жизни и выгрузка, окружение Python, рабочая папка, память узла, параметры, нейросети.

Введение

Документ описывает модуль, его встраивание в рабочий процесс, размещение зависимостей и назначение OGAS SDK. Хозяин модулей — функция modules. Формат манифеста — Форматы файлов, раздел 4, сообщения модуля и методы функции — Методы модулей.

1 Назначение модуля

Модуль — отдельная программа, выполняющая одну задачу: получает пакет на вход и отдаёт результат на выход. Назначение модуля не ограничено. Модуль человек пишет сам или получает готовым от другого человека.

2 Папка модуля

modules/<module_id>/
  manifest.json            манифест ogas.module/1
  main.py                  точка входа
  requirements.txt         зависимости Python, если есть
  uv.lock | pylock.toml    точные версии зависимостей, если автор их положил; платформа их не читает (раздел 5)
  requirements.freeze.txt  что поставил pip, для диагностики
  locales/{ru,en}.json     название модуля, тексты его параметров и портов (раздел 10)
  test_<module_id>.py      проверки модуля без платформы, если автор их написал

Готовый модуль добавляют одним из двух способов: кладут его папку в modules/ или добавляют вместе с процессом из экспорта (process.inspect, затем process.import, Сборка, установка и обслуживание, 5.6). Список модулей перечитывается из папки при каждом запросе, перезапуск не требуется. Тексты нового модуля тоже видны сразу: функция модулей при знакомстве объявляет ядру папку словарей modules/*/locales, и ядро находит словари модулей заново при каждом вопросе (Обмен с ядром, раздел 4). Далее выполняются проверка манифеста, установка зависимостей, тестовый вызов и добавление узла в граф (С чего начать, 6). Все эти действия выполняются в мастерской без ручного копирования. Создают и удаляют модуль, решают, предлагает ли его колонка графа, в библиотеке мастерской, а окружение программы ставят в окне «Модули» этой программы (Мастерская, 5.6).

3 Версии

В одной установке у каждого module_id одна версия. Версия модуля в процессе записана в одном месте — в modules.lock.json; узел графа её не хранит. Записывает её функция модулей, когда процесс сохраняют: берёт версию и отпечаток из установленного модуля. Неустановленный модуль в modules.lock.json не записывается. Перед прогоном функция графа сверяет записанную версию с установленной и передаёт её в module.execute; при несовпадении запуск отклоняется (module_version_mismatch).

Отпечаток содержимого. Файлы модуля можно изменить без изменения версии. Чтобы такое изменение было обнаружено, процесс вместе с версией запоминает отпечаток модуля — sha256 по содержимому файлов, без окружения, кэшей, диагностики и символьных ссылок (ссылка не является частью модуля и экспортом не переносится). Перед прогоном отпечаток сверяется с установленным модулем:

Отпечаток показывает изменение файлов, но не указывает автора изменения: он не является подписью, и модуль остаётся доверенным кодом (Защита, раздел 1).

4 Режимы, выгрузка и параллельность

Время существования OS-процесса модуля объявляет поле process манифеста:

Выгрузка по ресурсам. Функция модулей учитывает запущенные модули всех включённых программ и знает, сколько видеопамяти занимает каждый: поле resources.gpu_mb манифеста. Пока сумма укладывается в настройку modules.gpu_budget_mb, модули работают одновременно; при 0 видеокартой одновременно пользуется один модуль с gpu_mb > 0. Когда для очередного модуля не хватает места, функция модулей выгружает тот, что дольше всех не работал, — долгий или службу любой включённой программы. Модуль во время шага не выгружается: шаг, которому нужна видеокарта, ожидает освобождения места не дольше своего срока (timeout_ms), а не дождавшись, завершается отказом module_gpu_full.

Память узла. Модуль — инструмент, узел — его применение: «писатель» и «судья» на одной модели — два узла и две памяти. Модуль в запуске один на все свои узлы, и веса загружаются один раз, а память у каждого узла своя — папка data/processes/<process_id>/state/<node_id>/; как узлы называют общую память и сколько она живёт — Программа: граф и запуски, раздел 13. Со стороны модуля:

Части результата. Модуль может передавать результат шага частями во время работы (уведомление partial, раздел 7): текст ответа по мере генерации, готовые элементы списка. Части передаются по рёбрам выхода и сразу отображаются на пульте через переменную (Программа: граф и запуски, разделы 7, 13). Ответ на execute остаётся окончательным результатом шага.

По умолчанию вызовы одного модуля выполняются по очереди — в том числе из разных прогонов одной программы. Одновременные вызовы возможны, только если манифест объявляет parallelism > 1: это разрешение модуля, а не обещание платформы.

Ресурсы для всех модулей человек задаёт в настройках модулей (раздел «Ресурсы для модулей»):

Срок шага задаёт автор модуля полем timeout_ms манифеста — целое число миллисекунд, не меньше 1. Этот срок единственный: платформа не сокращает его своим пределом и не устанавливает собственных сроков. Соблюдение срока контролирует функция модулей: если модуль не ответил в срок, она останавливает его (core.process.stop, затем core.process.kill) и отвечает module_timeout. Функция графа и мастерская ожидают ответа на шаг без собственного срока: ответ поступает с результатом, с module_timeout или с function_lost при отказе самой функции модулей.

5 Окружение Python

Для рабочего процесса используется одно окружение Python, общее для всех его модулей. Процесс является законченным целым: одна установка зависимостей, один набор версий. Модули при этом остаются отдельными OS-процессами.

Окружение делает uv, и одинаковые файлы лежат на диске один раз. Окружения процессов — жёсткие ссылки на файлы пакетов в общем кэше data/cache/uv/: у каждого процесса своё окружение и свой набор версий, а пакет, одинаковый в двух процессах, занимает место один раз. Кэш лежит в папке данных, чтобы быть на одном диске с окружениями: между дисками жёсткой ссылки не бывает. uv — программа, которую называет настройка modules.uv (по умолчанию uv, ищется в PATH); если её нет, установка завершается отказом module_uv_not_found. Свой Python uv не скачивает: окружение создаётся интерпретатором, который нашёлся по modules.python_candidates.

Размещение окружения и поведение при конфликте версий:

6 Рабочая папка и файлы результатов

OS-процесс модуля запускается в рабочей папке прогона в папке его процесса: data/processes/<process_id>/runs/<run_id>/work. Тестовый вызов без процесса получает свою папку data/module-tests/<module_id>/work. Служба работает дольше прогона, поэтому запускается в папке памяти процесса state/, а рабочую папку прогона получает в каждом execute полем work_dir и помещает файлы результатов туда.

7 Протокол модуля

Модуль обменивается с функцией модулей сообщениями JSON-RPC 2.0, по одному на строку, через stdin и stdout (таблица 1).

Таблица 1 — Протокол модуля

Метод Смысл
init язык интерфейса; модуль отвечает ready, версией протокола и возможностями. До ответа execute не отправляется
execute сведения о прогоне, входные данные, параметры узла (раздел 10), файлы результатов, папка памяти узла state_dir и признак resumed — память в ней сохранена целиком этим же модулем (раздел 4); ответ — выходные данные и порт
configure только службе: узлы её модуля в этом процессе с их параметрами — [{ node_id, parameters }], параметры такие, какими их получил бы шаг (раздел 10), — при каждом поднятии службы: при включении программы и снова после выгрузки, до первого шага. Модуль, не знающий метода, отвечает method_not_found и работает без него; другой отказ — служба не поднимается
cancel отмена конкретного execution_id
ping проверка работоспособности долгого модуля и службы: функция модулей спрашивает каждый поднятый раз в modules.ping_interval_ms; не ответивший за modules.ping_timeout_ms останавливается как потерянный — идущий шаг завершается module_process_lost, служба поднимается снова к следующему шагу, — и функция говорит об этом событием function.work_failed. Отвечать на ping модуль должен и посреди шага
suspend модуль с suspend: true сохраняет память каждого узла, который обслужил, в папку state_dir этого узла и отвечает, когда сохранил; после ответа функция модулей его завершает (раздел 4)

Модуль может присылать уведомления progress (доля готовности), log, partial — часть результата шага (execution_id, port, данные; раздел 4), signal — сигнал своей программе (name, необязательные key и данные): ветка, которая ждёт его на узле signal, идёт дальше (Программа: граф и запуски, раздел 22), — и, если он служба, emit — пакет на выход вне шага, начало нового прогона (port, данные и необязательный node_id: служба нескольких узлов называет, для какого из них пакет; без имени пакет уходит всем узлам модуля, у которых есть ребро с этого выхода). stdout отдан протоколу: вывод отладочного текста в него не допускается и считается повреждением протокола; диагностика идёт в stderr.

Ответ на execute и уведомления partial и emit называют один из выходов манифеста; модуль без выходов выхода не называет. Отказ несёт код: код договора модуля или причину самого модуля, текст которой лежит в словарях модуля (таблица 2). От имени платформы модуль не отказывает: названный им код платформы становится причиной модуля. Ответ не по этим правилам успехом не считается — шаг завершается module_process_lost; уведомление не по ним дальше не передаётся, и об этом сообщается. Поля ответа и отказа — Методы модулей, 1.2.

Поток stdout ограничен так же, как шина: строка длиннее 4 МиБ или больше 256 непрочитанных ответов завершают шаг ошибкой transport_overflow. Так модуль не может незаметно занять всю память платформы.

Модуль объявляет допустимость повтора своих шагов: idempotent, non_idempotent или unknown. От этого зависит допустимость повтора и продолжения прогона (Программа: граф и запуски, раздел 14). Платформа обещает, что execute с одним operation_id дойдёт не меньше одного раза, но не «ровно один раз». Защищать внешнее действие от повторного выполнения должен сам модуль.

8 Модули на других языках

Модуль на любом языке — исполняемый файл с тем же протоколом через stdin / stdout (runtime: "native"): платформа запускает его напрямую. Протокол от языка не зависит; готовые библиотеки есть для Python и для Rust (раздел 9), на другом языке протокол пишут по Методы модулей.

9 OGAS SDK

OGAS SDK — набор средств для авторов модулей, обеспечивающий совместимость модулей разных авторов. Есть SDK для Python и для Rust.

10 Параметры узла

Всё, что человек задаёт для модуля, выражается одним понятием — параметрами узла. Отдельных «настроек модуля» нет.

Таблица 2 — Тексты модуля в словарях

Текст Ключ словаря Обязателен
название параметра module.<id>.param.<имя>.title да
пояснение параметра: назначение, допустимые значения, поведение без него module.<id>.param.<имя>.help да
название значения из закрытого набора module.<id>.param.<имя>.choice.<значение> да, если есть choices
название входа module.<id>.input.<порт>.title да
название выхода module.<id>.output.<порт>.title да
подсказка входа module.<id>.input.<порт>.help да
подсказка выхода module.<id>.output.<порт>.help да
текст отказа по причине самого модуля module.<id>.error.<код> нет; без него отказ называется общим текстом module_failed

Эти подсказки показывает знак «?» в окне графа (Мастерская, раздел 5). Название порта стоит на узле строкой (Мастерская, 5.3), поэтому оно обязательно; подсказкой порта мастерская говорит, что даст связь с ним, поэтому обязательна и она. Проверка в make ci не проходит, если у параметра или порта модуля нет обязательного текста на русском или английском или в манифесте записан текст.

Панель свойств узла в мастерской строит поля по этой схеме: у каждого параметра — поле его типа, пустое поле значит «действует умолчание манифеста» (Мастерская, раздел 5). У параметра нейросети поле — выбор среди нейросетей её вида в библиотеке; выбранная нейросеть, которой уже нет, остаётся в выборе с пометкой, что её нет в папке нейросетей. Параметр, взятый из переменной, называет её в пустом поле и остаётся взятым, когда сохраняют другие параметры узла, — пока в его поле не набрали или не выбрали значение.

11 Нейросети

Нейросеть — нейросеть или другие большие данные, которыми пользуются модули разных программ: веса, словари, голоса. Она лежит в библиотеке одним экземпляром, а не копией у каждого модуля или в окружении каждой программы (Мастерская, 5.6). Какую нейросеть берёт модуль и что к ней ещё — дополнение, словарь, голос, — выбирает узел, в мастерской и на пульте.