Методы модулей
Как функция модулей говорит с модулем — сообщения шага, долгого модуля и службы — и её собственные методы: список, установка окружения, запуск шага, нейросети, сети помощника, секреты и файлы модуля.
Введение
Документ — часть договора Обмен с ядром: сообщения между функцией модулей и модулем через 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 — объект, который уходит по этому выходу. Модуль без выходов не называет ни выхода, ни того, что по нему уходит.
Артефакты — файлы, которые модуль оставляет результатом запуска:
- Модуль работает в рабочей папке прогона
processes/<process_id>/runs/<run_id>/workпапки данных и там же создаёт свои файлы. - В
artifactsон перечисляет те из них, что должны стать результатом: строкой пути ("result.txt") или объектом{ "path", "media_type" }. Путь — только внутри рабочей папки. - Функция модулей переносит файл в результаты прогона
processes/<process_id>/results/<run_id>/под его именем — папка в пути становится частью имени, занятое имя получает номер — и заменяет запись ссылкой{ artifact_handle, kind, size, media_type, name }, гдеartifact_handle— имя файла среди результатов. - Картинку, звук, видео или файл, который модуль отдаёт в
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:
module_id,context,input,params— значения параметров узла; параметры, которых узел не задал, функция модулей дополняет умолчаниями манифеста (Модули, раздел 10);- необязательное
module_version— версия изmodules.lock.json, которую функция графа передаёт при прогоне; - необязательное
environment_of— процесс, в чьём окружении Python идёт шаг; без него — процесс прогона (context.process_id). Функция графа называет его для модуля вызываемого процесса (Модули, раздел 5).
Как идёт шаг:
- если модулю не хватает видеопамяти, шаг ждёт, пока функция модулей выгрузит того, кто дольше всех не работал; место, не освободившееся за срок шага, — отказ
module_gpu_full(Модули, раздел 4); - части результата (
partial) и пакеты службы вне шага (emit) публикуются событиямиmodule.partialиmodule.emit(Обмен с ядром, раздел 7); функция графа на них подписана; - срок шага —
timeout_msиз манифеста, следит за ним функция модулей: не уложился — модуль останавливается, ответmodule_timeout. Поэтому ответ приходит всегда, и спрашивающий ждёт его без своего срока (Модули, раздел 4); - в ответе — ответ самого модуля и, если нужно наблюдающей стороне,
process_handle.
Если зависимости в requirements.txt есть, а окружения нет, приходит module_needs_environment: автоматической установки платформа не выполняет. При несовпадении версии модуля с записанной в процессе — module_version_mismatch.