O.G.A.S. English

Методы модулей

Как функция модулей говорит с модулем — сообщения шага, долгого модуля и службы — и её собственные методы: список, установка окружения, запуск шага, нейросети, сети помощника, секреты и файлы модуля.

Введение

Документ — часть договора Обмен с ядром: сообщения между функцией модулей и модулем через stdin и stdout и методы, которыми функция модулей отвечает другим частям. Назначение, устройство и жизненный цикл модуля — Модули. Коды отказов — Коды ошибок.

1 Договор с модулем

Сообщения, которые идут через stdin и stdout модуля. Зачем каждое из них — Модули, раздел 7.

1.1 init

{
  "jsonrpc": "2.0",
  "id": "module-1",
  "method": "init",
  "params": {
    "language": "ru"
  }
}

language — язык интерфейса или null, если функция i18n недоступна (Настройки, раздел 7). Признак resumed в init не приходит: модуль один на все свои узлы, и продолжает ли каждый узел с сохранённой памяти, говорит шаг этого узла (1.2).

Ответ: { "status": "ready", "protocol": 1, "capabilities": ["execute"] }.

1.2 execute

{
  "jsonrpc": "2.0",
  "id": "module-3",
  "method": "execute",
  "params": {
    "context": {
      "process_id": "demo",
      "run_id": "run-1",
      "attempt_id": "attempt-1",
      "node_id": "echo_one",
      "operation_id": "op-1",
      "execution_id": "exec-1",
      "iteration_id": 0,
      "trace_id": "trace-1"
    },
    "input": { "text": "пример" },
    "input_port": "input",
    "params": { "suffix": " / x" },
    "artifact_handles": ["sheet-1.svg"],
    "state_dir": "/…/data/processes/demo/state/echo_one",
    "resumed": false
  }
}

state_dir — папка памяти узла этого шага (своей или общей, которую узел назвал полем memory; имя приходит в context.memory), resumed — true, если память в ней сохранена целиком этим же модулем — тем же модулем, версией и отпечатком — при прошлой выгрузке и подтверждена ответом (1.5): узел продолжает с сохранённого места в любом прогоне и после перезапуска платформы. Модуль один на все свои узлы; память каждого узла он держит отдельно, по его папке, и читает её из папки, когда впервые обслуживает узел (Модули, раздел 4). У тестового вызова без процесса обоих полей нет. В SDK — state_dir(params) и resumed(params).

input_port — вход узла, на который пришли данные: модуль с несколькими входами узнаёт его, а не угадывает по содержимому. Поля нет, когда правило all собрало данные с нескольких входов в объект по их именам.

models — пути к нейросетям, которые выбрали параметры шага типа model: «имя параметра → полный путь» файла или папки нейросети; поля нет, если такие параметры ничего не выбрали. Выбранной нейросети нет — шаг до модуля не доходит: module_model_missing (Модули, раздел 11).

Служба получает ещё work_dir — абсолютный путь к рабочей папке прогона: она запущена в своей папке состояния и работает дольше прогона, поэтому файлы результатов кладёт туда (Модули, раздел 6). Шаговый и долгий модуль work_dir не получают: они запущены в рабочей папке прогона.

Успех: { "port": "success", "output": { "text": "..." }, "artifacts": [] }. В port модуль называет один из выходов своего манифеста, в output — объект, который уходит по этому выходу. Модуль без выходов не называет ни выхода, ни того, что по нему уходит.

Артефакты — файлы, которые модуль оставляет результатом запуска:

  1. Модуль работает в рабочей папке прогона processes/<process_id>/runs/<run_id>/work папки данных и там же создаёт свои файлы.
  2. В artifacts он перечисляет те из них, что должны стать результатом: строкой пути ("result.txt") или объектом { "path", "media_type" }. Путь — только внутри рабочей папки.
  3. Функция модулей переносит файл в результаты прогона processes/<process_id>/results/<run_id>/ под его именем — папка в пути становится частью имени, занятое имя получает номер — и заменяет запись ссылкой { artifact_handle, kind, size, media_type, name }, где artifact_handle — имя файла среди результатов.
  4. Картинку, звук, видео или файл, который модуль отдаёт в output, он называет ссылкой { "file": "out/кадр.png" } — тем же путём, что в artifacts. Перенеся файл, функция модулей ставит в file его имя среди результатов и дописывает media_type, size и name — имя файла из пути, если модуль своего не дал. Так выход несёт ссылку на файл, которую читают узел «Показать», переменная и следующий модуль (Форматы файлов, 16.2). Ссылка на путь, которого нет в artifacts, остаётся как есть.

Файл, записанный модулем в папку человека, результатом запуска не считается.

Входные файлы приходят модулю ссылками artifact_handles: сначала артефакты пакета, потом поле file каждой ссылки на файл в данных шага — картинки из переменной, ответа на вопрос, кадра предыдущего модуля. Те из них, чьи файлы лежат среди результатов этого прогона или в файлах процесса (files/<имя>), приходят ещё и в artifact_files: { artifact_handle, path }. Так модуль читает файл результата другого модуля и файл, который держит переменная. Такой файл только для чтения: запись в него кончается отказом системы (Программа: граф и запуски, 24.4); новый файл модуль создаёт в рабочей папке и называет в artifacts. В SDK путь к файлу ссылки даёт file_path(params, ссылка): ссылка — объект ссылки на файл или её поле file; None — файла нет.

Отказ модуль отдаёт ошибкой JSON-RPC с полями message, data.error_code и data.retryable; отказ без retryable не повторяется. Коды договора модуля — module_failed, module_cancelled и module_not_ready — функция модулей передаёт как есть. Любой другой код — причина самого модуля: шаг завершается module_failed, код модуля приходит в data.module_error_code, а если в обоих словарях модуля есть текст module.<id>.error.<код> (Модули, таблица 2), его ключ приходит в data.message_key. Код платформы модуль назвать не может: названный модулем, он тоже становится причиной модуля. В SDK причину модуля называет поле code отказа: Refusal("Лица на снимке нет", code="no_face").

Ответ не по протоколу успехом не считается: если в ответе нет ни result, ни error или есть оба, результат — не объект, выход не из манифеста, по выходу уходит не объект или у отказа нет кода или message, шаг завершается module_process_lost.

1.3 cancel

Параметры: { "execution_id": "exec-1" }. Долгий модуль должен обработать отмену, даже если шаг ещё выполняется. Шаговый модуль платформа может завершить.

1.4 ping

Ответ — { "status": "pong" }, в том числе посреди шага. Проверка работоспособности долгого модуля и службы: функция модулей спрашивает раз в modules.ping_interval_ms и останавливает не ответивший за modules.ping_timeout_ms (Модули, раздел 7).

1.5 configure

Параметры: { "nodes": [{ "node_id", "parameters", "models"? }] }. Приходит только службе — при каждом её поднятии: при включении программы и снова, когда выгруженную службу поднимает шаг, — до первого шага. nodes — узлы её модуля в процессе программы; parameters — такие, какими их получил бы шаг: настройки и переменные подставлены, умолчания манифеста дополнены; models — пути к нейросетям, которые они выбрали, как у шага (1.2). Ответ — { "status": "configured" }. Модуль, не знающий метода, отвечает method_not_found, и служба работает без него; другой отказ — служба не поднимается, и включение программы отказывает с его текстом. В SDK — run_long(…, on_configure=…): обработчик получает список узлов.

1.6 suspend

Параметры: {}. Приходит перед выгрузкой только модулю с suspend: true в манифесте (Модули, раздел 4). Модуль сохраняет память каждого узла, который обслужил, в папку state_dir этого узла и только потом отвечает { "status": "suspended" }; после ответа функция модулей ставит отметку о сохранённой памяти каждому такому узлу и завершает OS-процесс модуля. Модуль, не сумевший сохранить память, отвечает отказом с module_failed: функция модулей завершает его так же, сообщает, что память могла пропасть, и при следующем шаге узлы получают resumed: false. SDK отвечает так сам, если сохранение модуля выбросило исключение.

Уведомления модуля (у них нет id): progress с полем ratio от 0 до 1 и, у модуля с несколькими шагами сразу, execution_id; log с полями level (info, warn или error) и message; partial — часть результата шага (execution_id, port, data); signal — сигнал своей программе (name, необязательные key и data); у службы — emit, пакет на выход вне шага (port, data и необязательный node_id — один из узлов, о которых служба узнала в configure) (Модули, раздел 7). Сигнал модуля, поднятого без программы — тестовым вызовом, — дальше не передаётся, и функция модулей сообщает об этом. В port уведомление называет выход манифеста; уведомление с другим выходом или без него дальше не передаётся, а функция модулей сообщает об этом событием function.work_failed. Уведомления идут в stdout такими же строками JSON. Отладочный текст в stdout писать нельзя.

Функция модулей передаёт log и progress в журнал событиями module.log и module.progress (Обмен с ядром, раздел 7): в журнал прогона, если модуль в шаге. Уведомление с уровнем, которого журнал не знает, или с долей вне 0–1 дальше не передаётся, и об этом сообщается. В SDK их отправляют send_log и send_progress.

Секреты в параметрах, на входе, на выходе и в журнале платформа не распознаёт и не закрывает звёздочками.

2 Методы функции модулей

Методы:

Таблица 1 — Методы функции модулей

Метод Тип Назначение
module.execute команда при первом вызове init, затем execute
module.cancel команда отмена execution_id; ответ { status: "cancelling" } приходит, только когда просьба об отмене дошла до модуля, иначе — отказ с причиной
module.release команда run_id: выгрузить долгие модули этого прогона после него — завершить их OS-процессы, у модуля с suspend: true — после suspend. Модули других прогонов, даже того же модуля, и модуль посреди шага не трогаются; службы выгружает module.service.stop. Без run_id — invalid_request. Ответ — { released }
module.service.start команда process_id, module_ids и необязательно environments — { module_id: process_id }, в чьём окружении поднять службу вызываемого процесса: поднять службы включаемой программы (Модули, разделы 4, 5). Ответ — { started }
module.service.stop команда process_id: выгрузить службы выключаемой программы, с suspend для тех, кто умеет. Ответ — { stopped }
module.install команда process_id: собрать требования модулей процесса — тех, что названы в его modules.lock.json (нет замка — нет такого процесса: document_not_found), создать data/processes/<id>/.venv и поставить их. Конфликт версий — module_environment_conflict. Вызов с module_id отклоняется: окружение принадлежит процессу (Модули, раздел 5)
module.validate запрос проверить манифест: { ok, id, version, execution_semantics } или module_invalid с data.errors — по строке на каждое нарушенное поле
module.list запрос список установленных модулей: id (имя папки модуля), ok, version, digest, bytes и files — сколько весят и сколько их файлов, тех же, что входят в отпечаток, name_key и description_key — ключи названия и описания в словаре модуля (Форматы файлов, раздел 4), inputs, outputs, parameters, input_policy, retry, execution_semantics (нет в манифесте — unknown), parallelism (нет в манифесте — 1), process — время существования OS-процесса модуля, как его называет манифест (Модули, раздел 4): по нему окно узнаёт узел службы; gpu_mb — сколько видеопамяти нужно модулю, если манифест называет (resources.gpu_mb). Модуль, манифест которого нарушает формат, приходит с ok: false, error_code: module_invalid и errors — по строке на каждое нарушенное поле, как у module.validate; поля, которые удалось прочитать, приходят как есть. Модуль, манифест которого не читается или не является объектом JSON, приходит только с id, ok: false, error_code и errors
module.space запрос { parts: [{ key, title_key, bytes, clear_method? }] } — части места и сколько каждая занимает на диске; title_key — название части в словаре хозяина, clear_method — команда, которая убирает восстановимое в этой части (её зовут с { part: key }): окружения процессов, кэш пакетов (пакет, который держит окружение, считается в кэше один раз), установленные модули, нейросети
module.space.clear команда part: "cache": кэш пакетов теряет то, чего не держит ни одно окружение (uv cache prune); окружения не ломаются, недостающее следующая установка возьмёт снова
module.create команда шаблон модуля: манифест, main.py, локали, requirements.txt
module.delete команда module_id: удалить модуль из папки модулей вместе с файлами; { module_id, deleted: true }. Модуль, который называет замок какой-нибудь программы, не удаляется — module_in_use, и names называет программы; поднятый сейчас — тоже module_in_use; не установленный — module_not_found. Модуль, лежащий ссылкой на папку в другом месте, теряет ссылку, а не то, на что она указывает
module.model.list запрос нейросети (Модули, раздел 11): { dir, dir_windows, found, kinds, models } — папка нейросетей так, как её видит WSL, и, если wslpath может сказать, как её видит Windows (иначе null); found — есть ли она; kinds — виды { kind, modules } с модулями, которые их берут: виды установленных модулей, подпапки которых функция заводит при чтении, и подпапки, которые есть; у нейросети id (<вид>/<имя>), kind, name — имя файла или папки, size — байты файла или файлов папки без ссылок внутри, folder — папка ли она, linked — лежит ли ссылкой, changed — когда менялась, modules — установленные модули, которые берут её по умолчанию
module.model.delete команда kind, name: удалить файл нейросети или её папку вместе с файлами, а ссылку — без того, на что она указывает; { kind, name, deleted: true }. Нейросеть, которую установленный модуль берёт по умолчанию, — model_in_use с data.names — модулями; нейросети нет — model_not_found
module.model.take команда from, kind, name: поставить файл или папку из downloads/ папки данных в папку нейросетей как <kind>/<name> — переносом, а между дисками копией с удалением; { kind, name, size }. Путь не из downloads/ — path_outside_root; нейросеть с таким именем уже есть — model_exists: существующая не заменяется
module.model.needs запрос process_id: какие нейросети берут по умолчанию модули программы — те, что названы в её modules.lock.json: { needs: [{ module_id, parameter, kind, name, present, sources }] }, sources — откуда взять её, как назвал манифест (Форматы файлов, раздел 4)
module.hardware запрос что есть у этого компьютера: { gpus: [{ name, memory_mb, free_mb }], memory_mb, free_memory_mb, cores, models_free_mb } — видеокарты по nvidia-smi (его нет — пустой список), память и ядра, как их видит WSL, свободное место на диске папки нейросетей. Ничего не пишет
module.networks запрос сети помощника, которые приходят готовыми с OGAS SDK (sdk/networks): { networks: [{ id, version, name_key, description_key, process, parameters, installed }] }, installed — лежит ли сеть в папке модулей
module.network.add команда module_id: положить готовую сеть помощника в папку модулей; без её .env. Уже лежит — module_exists, нет такой готовой — module_not_found. Ответ — { module_id, added: true }
module.secret.set команда module_id, name, value: строка NAME=value в .env папки модуля, файл с правами 0600; пустое value убирает строку. Имя — заглавные буквы, цифры и _, значение — одна строка, иначе secret_invalid. Ответ — { module_id, name, set }, значения в нём нет
module.secret.names запрос module_id: { module_id, names } — какие секреты модуля вписаны; значения платформа не читает и не отдаёт
module.file.write команда module_id, path, text: записать текстовый файл модуля целиком; путь — простой, внутри папки модуля, без частей с точкой в начале (не .env, не .venv), не длиннее 200 знаков, текст — не больше 512 КиБ, иначе module_file_refused. Ответ — { module_id, path, bytes }
module.file.read запрос module_id, path: текст файла модуля по тем же правилам пути; { module_id, path, text }
module.test команда тестовый execute без прогона: module_id, input, params
module.lock.sync команда process_id, module_ids: записать modules.lock.json процесса. Версию и отпечаток каждого модуля функция берёт из установленного модуля; не установленный модуль не записывается (Модули, раздел 3)
module.lock.get запрос прочитать записанные версии модулей процесса
module.state.list запрос process_id — { process_id, states }: у каждой сохранённой памяти — memory (идентификатор узла или имя общей памяти), module_id модуля, который её сохранил, bytes и files папки, saved — отметка о сохранённой памяти (version, digest, saved_at) или null, up — обслуживает ли её поднятый сейчас модуль (Форматы файлов, 5.3)
module.state.clear команда process_id, memory или список memories — убрать папки памяти и отметки: её узлы начнут заново; { process_id, cleared }. Память, которую обслуживает поднятый модуль, — module_state_in_use, и тогда не очищается ничего. Функция графа зовёт метод сама для памяти, которую после сохранения процесса не называет ни один узел
module.state.export команда process_id, target — положить в папку пакета target/<имя>/ сохранённую память процесса вместе с отметками; память без отметки и память, которую обслуживает поднятый модуль, не кладутся; { states, left_out } — имена памяти
module.state.import команда process_id, source, memories — сделать память из пакета памятью с теми же именами в процессе вместе с отметками; { process_id, states }
module.lock.import команда process_id, lock — документ ogas.modules-lock/1 из экспорта: записать версии и отпечатки как они есть, а не по установленным модулям, чтобы расхождение с установленным дало module_version_mismatch при включении программы; поколение начинается заново. Документ другого формата или запись модуля без id и version — document_invalid, ничего не записывается
module.export команда module_ids, target (папка от корня данных): скопировать файлы модулей в target/<module_id>/; ответ — modules с id, версией, отпечатком, числом файлов, байтами и links_left_out
module.import команда modules и source (папка экспорта): скопировать модули, которых в установке нет; установленный не перезаписывается, а попадает в skipped. Ссылка в пакете — module_invalid, отпечаток копии не сошёлся — module_changed. Без source модули приходят с файлами внутри запроса
module.inspect запрос source, module_ids: что экспорт принесёт — { id, version, runtime, names, files, bytes, downloads, digest, links, installed }, где names — название и описание из словарей модуля, downloads — строки requirements.txt, installed — absent | same | different

Параметры module.execute:

Как идёт шаг:

Если зависимости в requirements.txt есть, а окружения нет, приходит module_needs_environment: автоматической установки платформа не выполняет. При несовпадении версии модуля с записанной в процессе — module_version_mismatch.