O.G.A.S. Русский

OGAS SDK

A guide for those who write modules and build programs by script: what OGAS SDK holds, how to write a module in Python and in Rust, how to check it and try it without the platform, how to build a program and put a thing on a showcase. Here, too, is how the platform itself is extended: a function, a kind of node, an element of the pulpit, a window.

Introduction

Everything needed to write a part of the platform without looking into finished ones is gathered here. If the work made you look into someone else's module, that is a flaw of this document, and it is the document that gets fixed. The contract of the messages — Methods of modules, the format of the manifest — File formats, section 4, the notions — Modules. Anything new is agreed with Rules of development, 4.7.

What OGAS SDK holds. The folder sdk/ of the installation:

sdk/
  ogas-sdk            the command, with nothing to install
  python/             the code of a module in Python and the package ogas
  rust/               the package ogas-module for a module in Rust
  templates/          templates of modules
  networks/           networks of the assistant — ready modules
  examples/           examples
  tests/              checks of the SDK and test modules

A module in Python writes from ogas_runtime import …: the platform puts sdk/python on the PYTHONPATH of every module. The package ogas is for programs that talk to the platform from outside: build scripts, editors, checks. To install it as a Python package — pip install -e sdk/python, and the command is then simply ogas-sdk.

Table 1 — The command ogas-sdk

Command What it does
new <id> --template <template> --name <name> a module from a template: python-basic, python-network, python-service, rust-basic
check <folders> whether a module keeps its contract — the same checks as in make ci (table 4)
try <folder> --input <JSON> --params <JSON> one step of a module without the platform, the way the platform gives it; a native module too
pack <folder> --out <folder> --author <name> --license <SPDX> a thing for a showcase: <id>/<version>/item.json and files/ with sha256 sums; .env, .venv and what Python left behind are not taken; a module that failed the check is not packed
status whether the working platform is reachable through the door for programs
catalog what programs are built of: each kind of node and element of the pulpit with its make-up and words
run <script> run a build script of a program (section 9)

With the key --json the command answers with one JSON document: that is how another program calls it.

Table 2 — What is written in what

What What for In what Where it lies Section
module work that cannot be built of nodes: a neural network, reading a file, an outside service Python or Rust modules/<id>/ 1–4
function of the platform a new part of the platform itself with its own methods Rust functions/<id>/ 5
kind of node a new primitive of the graph Rust functions/graph/src/kinds/<id>/ 6
element of the pulpit a new way to show or set a value Rust and JavaScript functions/canvas/src/elements/<id>/ and functions/ui/window/ui/pulpit/elements/ 7
window of the workshop a new tool in a window JavaScript functions/ui/window/ui/host/<id>/ 8
program to build a program of nodes and a pulpit — by hand in the workshop or by script graph, pulpit; the script — Python, package ogas data/processes/<id>/; the script — anywhere 9, 12
network of the assistant to give the assistant of the workshop one more provider of networks Python modules/<id>/, with "role": "assistant_network" 13

1 A module in Python: the first module

1.1 The folder

The template is made by module.create; by hand, a folder is put into modules/<id>/ (Modules, section 2):

modules/shout/
  manifest.json          ports and parameters: the only source of the contract
  main.py                the code
  requirements.txt       Python dependencies, each with its version: name==1.2.3
  locales/ru.json        words for the person
  locales/en.json

1.2 The manifest

{
  "format": "ogas.module/1",
  "id": "shout",
  "version": "1.0.0",
  "name_key": "module.shout.name",
  "description_key": "module.shout.description",
  "runtime": "python",
  "entrypoint": "main.py",
  "inputs": [{ "name": "input", "required": true, "type": "text" }],
  "outputs": [{ "name": "success", "type": "text" }],
  "input_policy": "all",
  "parallelism": 1,
  "process": "step",
  "execution_semantics": "idempotent",
  "retry": { "max_attempts": 1, "delay_ms": 0, "exponential": false },
  "parameters": {
    "times": { "type": "integer", "default": 2, "min": 1, "max": 10 },
    "loud": { "type": "boolean", "default": true },
    "mark": { "type": "string", "default": "excl", "choices": ["excl", "dot"] }
  },
  "permissions_info": { "network": false, "filesystem": "module-and-run-only" },
  "timeout_ms": 10000,
  "runtime_python": ">=3.11"
}

Types of a parameter: string, integer, number, string[], color, boolean — yes or no, model — a neural network of one kind ({ "type": "model", "kind": "llm", "default": "name" }). choices is a closed set of codes. In process: step — an OS process for a step, ending after the step; long — lives on and keeps the network in memory; service — lives while the program is on and gives data by itself. resources.gpu_mb is how much video memory the module takes together with its neural network: by it the function of modules unloads those that stand idle, and the library tells the person whether the module, and a program with it, will start on their video card; a module that takes the video card names it honestly (1.9). suspend: true — the module keeps the memory of its node and saves it before it is unloaded (1.8).

1.3 The code

from ogas_runtime import Refusal, extract_text, parameters, reply, run_step

MARKS = {"excl": "!", "dot": "."}


def handle(params: dict) -> dict:
    options = parameters(params)
    text = extract_text(params.get("input"))
    if not text.strip():
        raise Refusal("Nothing to shout", code="no_text")
    said = (text.upper() if options["loud"] else text) * options["times"]
    return reply("success", {"text": said + MARKS[options["mark"]]})


if __name__ == "__main__":
    run_step(handle)

stdout belongs to the protocol: a module writes its own things to stderr or with send_log. print in a module breaks the exchange.

1.4 Parameters: one default

A default lives only in the manifest: the platform puts it in before the step, and a parameter is read by its name.

So Not so Why
options["steps"] options.get("steps") or 30 or turns the person's zero into the default: «Likeness 0» becomes 60
options["negative"] options.get("negative", NEGATIVE) a field the person cleared must stay empty
options.get("seed") options.get("seed") or 0 a parameter without default is optional: it is not there until the node has set it
options["speckle"] or by_detail — allowed when 0 in the manifest means «the module decides», and the hint says so

A closed set comes as a code, not a word. The word lies in the dictionary of the module; on the pulpit the element «Choice» keeps the code in options and the word in titles (File formats, table 6). There are no tables of pulpit words in the code of a module. A pulpit switch comes as true or false into a parameter of type boolean.

A value from a variable ({ "variable": "id" }) is put in by the platform at the moment of the step and is not checked against choices. The module gets the code the pulpit put there.

1.5 What comes in

params["input"] depends on the inputs of the node (The program: graph and runs, section 6):

The data come from someone else's node and are sometimes deeper than the module expects: the answer of a question — { "question": <data>, "answer": <answer> }, a selection — a record with mask, cutout and picture, a button of the pulpit — a record by the names of the fields of carries. So a file and a value are searched for, not addressed:

picture = find_file(given, "picture", "image", media="image/")
mask = find_file(given, "mask", media="image/")
path = file_path(params, picture)
words = find_value(given, "words", "target")

find_file first looks for the named fields at any depth, then for any file. Only file_path gives the path to a file; a file from someone else's step is only read (The program: graph and runs, 24.4).

A folder or a file the person typed on the pulpit is read through person_path(said): D:\Photos becomes /mnt/d/Photos.

1.6 What a step gives

name = "frame.png"
picture.save(new_file(params, name))
made = file_ref(name)
return reply("image", {"picture": made, "width": picture.width}, [made])

A result file is written only into the work folder (new_file) and named in the third argument of reply: so it becomes a result of the run, and the reference in the output a real one. Writing into a folder of the person does not count as a result.

1.7 Refusal and words

Refusal("a text for the journal in English", code="no_face") — a refusal with its reason; the text for the person lies in module.<id>.error.no_face of both dictionaries. retryable=True — repeat by retry.max_attempts. Any other exception is a failed step.

The words a module shows in data are taken from the dictionary: say("said.remove", target=target) reads module.<id>.said.remove in the language of the interface and fills the places {target}.

Table 3 — Keys of the dictionary of a module

Key What Required
module.<id>.name, .description — the keys named by name_key and description_key of the manifest name and description the name — yes, the description — if the manifest names it
module.<id>.param.<name>.title, .help the name of a parameter and its explanation: what it sets and which way to turn it yes
module.<id>.param.<name>.choice.<code> the word for a code of a closed set yes, if there are choices
module.<id>.input.<port>.title, .output.<port>.title names of the ports yes
module.<id>.input.<port>.help, .output.<port>.help hints of the ports: what a link to the port gives yes
module.<id>.error.<code> the text of a refusal yes, for each code the module names
module.<id>.<key> a text said by say yes, for each key the module names

Each dictionary speaks its own language in the person's words: the name of a sampler is written with an explanation — «Euler a — lively, with scatter».

1.8 A long module and the memory of a node

run_long(handle, on_suspend=save) keeps the module up; the handler gets cancelled as well — a threading.Event. It is looked at between parts of the work, raising Cancelled. send_progress(share) moves the bar on the pulpit.

A module is one for all its nodes, so memory is kept by node:

memories = NodeMemories(lambda folder, was_saved: Node(folder, was_saved))

def handle(params, cancelled):
    node = memories.of(params)
    ...

def save():
    for node in memories:
        write_memory(node.folder, node.values, mark)

run_long(handle, on_suspend=save)

Rules of memory:

1.9 Neural networks

A model is chosen by a parameter of type model; chosen_model(params, "network") gives the path or None. Neural networks lie in models/<kind>/<name> — a file, a folder or a link (Modules, section 11).

A module is written for a family of models, not for a version: the family is recognised by the files of the network (model_index.json, the keys of the weights), common loaders are taken, and anything unfamiliar is refused in words: Refusal(…, code="unknown_network"). Heavy libraries are imported inside functions, so that the module comes up and answers ping quickly. At the start of the file stands os.environ.setdefault("HF_HUB_OFFLINE", "1"): a step downloads nothing.

Where to get the default neural network, a module says in the field sources of its parameter — then the library of the program shows that it is missing and downloads it at a button (Modules, section 11):

"network": {
  "type": "model", "kind": "llm", "default": "Qwen2.5-7B-Instruct-bnb-4bit",
  "sources": [{ "source": "huggingface", "id": "unsloth/Qwen2.5-7B-Instruct-bnb-4bit" }]
}

source is the kind of source: huggingface, civitai or ogas; id — how the source names the thing; the optional files — which files to take; without it, those the source offers itself: .safetensors and what comes with them. The default name is the one under which the network will stand in the folder of networks. The form of the field is checked by the platform when it accepts the module (module.validate).

ogas_models gives what is common: TensorMemory — memory with tensors, drawing_progress — the bar and cancelling for diffusers pipelines, thumbnails — thumbnails of pictures from disk for the pulpit, hub_snapshot — weights from the Hugging Face cache, text_network, chat_prompt, layers_of and Light — for text networks. Its dependencies (torch, safetensors, pillow, transformers) the module names in its own requirements.txt.

1.10 What a module brings with it

A program — in bin/, the path to it is given by module_dir(); licences — in licenses/. What was built on your own machine must not keep the home folder: Rust is built with RUSTFLAGS="--remap-path-prefix=$HOME=/build".

1.11 The check

sdk/ogas-sdk check modules/shout
sdk/ogas-sdk try modules/shout --input '{"text": "hello"}'

The check reads the files of the module without starting it and names the place and what to do (table 4). The same for all modules of the repository is asked by make ci.

Your own tests lie in test_<id>.py beside main.py. ogas_check.execute(folder, input, parameters) carries one step from start to stop, putting in the defaults of the manifest as the platform does; for a function of the module called directly, the parameters are given through with_defaults(manifest, parameters).

Table 4 — What ogas_check.py names

Breach How to fix it
a second default of a parameter read it by name (1.4)
a parameter not in the manifest; a parameter of the manifest nobody reads declare it or remove it
chosen_model on a parameter not of type model declare the type
a loop of the wrong mode: run_step with process: long bring it in line with the manifest
suspend: true without memory on_suspend, or remove the promise
a table of pulpit words → codes of a parameter the code in the value, the word in titles
a refusal with a Russian text, or with a code that has no text in the dictionaries the text in module.<id>.error.<code>
say with a key that has no text add it to both dictionaries
a dependency without a version name==1.2.3
bin/ without licenses/; the home folder inside a program 1.10
the same function in several modules (make ci, live tasks) move it into the SDK
the code goes to the network, and permissions_info.network keeps silent about it say so in the manifest: the person reads it before installing
the module installs packages itself: pip, uv pip, ensurepip the package — in requirements.txt; the platform installs it at the person's command
a parameter named like a secret: password, api_key, bot_token and the like a secret — in .env of the module folder, read by secret(name)
an except holding only pass or continue, with no word why the reason on the except line, or a refusal
a reference to a document of the platform in code or a dictionary: ОГАС with a number, docs/, a section sign the reason in your own words
the module takes the video card — cuda in the code or a network through text_network of the SDK — and the manifest has no resources.gpu_mb or it is 0 name how much video memory the module takes with its neural network: otherwise the sharing of the video card and the library know nothing of it
a module with the SDK writes to stdout: print without file=, sys.stdout.write send_log or print(…, file=sys.stderr): stdout belongs to the exchange; a module that carries the exchange itself, without the SDK, owns its stdout
reaching inside the platform: OGAS_SOCKET, OGAS_LAUNCH_TOKEN, core/data, config/user.json, layout.json, functions.json take what is needed from the step, your own folder, the folder of the run and the memory of the node (1.5, 1.8)
a bare number in a call of a network that affects the result: guidance_scale=6.0, temperature=0.7, num_inference_steps=30 and the like a parameter of the manifest — the person turns it; an inner quantity — a constant with a name and a reason
a module, parameter, choice or port has no name or hint in one of the dictionaries; a text in the manifest the text named by name_key (and description_key, if any), module.<id>.param.<name>.title and .help, .choice.<value>, module.<id>.input.<name>.title and .help — in both dictionaries (table 3)
the dictionaries hold different texts one and the same set in ru.json and en.json
a text of the Russian dictionary in Latin letters without a single Russian letter; a word of development in it — «пакет», «прогон», «итерация», «слот» and the like write it in Russian in the person's words: «данные», «запуск», «круг»

2 Reference of the SDK for Python

It lies in sdk/python, on the PYTHONPATH of a module.

Table 5 — ogas_runtime: the loop and the exchange

Name What it does
run_step(handle) the loop of a step module: handle(params) -> reply
run_long(handle, on_init, on_suspend, on_configure) the loop of a long module and of a service: handle(params, cancelled) -> reply
send_progress(ratio, execution_id=None) the bar: a share from 0 to 1
send_log(level, message) a line of the journal: info, warn, error
send_partial(execution_id, port, data) a part of the result of a step under way
send_emit(port, data, node_id=None) data of a service outside a step
send_signal(name, data=None, key=None) a signal to its own program
Refusal(message, retryable=False, code=None) a refusal of a step
Cancelled raised when cancelled is set
write_message(message), write_result(request_id, result), write_error(request_id, message, error_code, retryable) the exchange by hand: only for a module with its own loop instead of run_step and run_long

Table 6 — ogas_runtime: reading a step

Name What it gives
parameters(params) the parameters of the step with the defaults of the manifest
find_file(given, *names, media="") a reference to a file at any depth
find_value(given, *names) the first non-empty value under these names
extract_text(payload) the text of the input; an input without text — a refusal
file_path(params, reference) the path to the file of a reference, or None
chosen_model(params, name) the path to the neural network of a parameter, or None
person_path(said) a folder of the person as the module sees it; refusal no_folder
module_dir(), module_path(path) the folder of the module; a path relative to it
secret(name) a secret of the module — a service key, a token — from the file .env of its folder, a line NAME=value; None if it is not there
run_dir() the folder of the run

Table 7 — ogas_runtime: the answer, memory, words

Name What it does
reply(port, output=None, files=()) the answer of a step
file_ref(name, media_type=None) a reference to a file of the work folder
new_file(params, name), work_dir(params) the path of a new file, with its folders made; the work folder
state_dir(params), resumed(params) the folder of the memory of the node; whether it is whole
NodeMemories(make) memory by node: .of(params), iteration, .clear()
read_memory(folder, mark=""), write_memory(folder, values, mark="") the small memory of a node in JSON
whole_file(path) writing whole or not at all, a with block
model_mark(*paths) the mark of the weights for memory
say(key, **values) a text of the dictionary of the module in the language of the interface
parameter_value(name, value) the value of a parameter as the node takes it: type, limits and set of the manifest; otherwise ValueError — a module that sets a parameter itself takes nothing the person could not have given
titled_setting(said, settings, key) the setting named by its own name or by its label on the pulpit, in any language of the module; None if there is none
axis_values(said, current), sweep_grid(axes) the values of an axis of a sweep as the person writes them: through «;», a range «from..to/step», words through «|», «yes; no»; all combinations of the axes

Table 8 — ogas_models

Name What it does
TensorMemory(folder, was_saved) the memory of a node with values and tensors: .made_with(mark), .write(); made through NodeMemories(TensorMemory)
drawing_progress(cancelled, steps) the value of callback_on_step_end of a diffusers pipeline
thumbnails(params, paths, side=512) thumbnails of files from disk: references for viewing on the pulpit
hub_snapshot(repo) the folder of weights in the Hugging Face cache; refusal no_weights
text_network(path, precision) a text network and its tokenizer from a folder of the library, the precision of numbers bfloat16, float16, float32 or auto; refusal unknown_network
chosen_layers(said, count) layers of the network from a word: auto — the middle of the network, 14, 10-18, 12, 15, 18; refusal bad_layers
chat_prompt(tokenizer, messages) a conversation as the network reads it: a last line of the network closes its turn, otherwise the turn is open; a template without the role system gets it in the first line of the person; the thinking mode is off
layers_of(model) the decoder layers of a text network of any family; refusal unknown_network
Light(vectors, about) an addition to the outputs of layers at every token: .on(model) — a with block, .strength, .answer_only, .mask, .with_other(light, share), .only(layers), .write(path), Light.read(path, mark); an addition from other weights — refusal other_network

Table 9 — ogas_check

Name What it does
module_problems(folder) what breaks the contract, by the files of the module (table 4)
rules_problems(folder) what breaks the rules of the platform any module keeps, by its files (tables 4 and 12)
words_problems(folder) what is missing from the texts the person reads about the module, and what is wrong in them (table 4)
development_words(text), untranslated(key, text) words of development in a text for the person; a Russian text left untranslated
copies(folders) functions repeated in several modules
execute(module, payload, params, iteration, work, models) one step from start to stop
with_defaults(manifest, given) the parameters as the platform gives them
start(module, work), init(process), send(process, message), read_json(process), stop(process) a conversation by the protocol, by hand
module_environment(folder, work) the environment the platform gives
folder_of(module) the folder of a module by name or by path
main(folders) the command ogas_check.py

3 A module in Rust

A module in Rust is an executable file with "runtime": "native" and "entrypoint" — the path to it in the module folder. The SDK is the package ogas-module in sdk/rust; it depends only on serde_json and is built apart from the platform:

[dependencies]
ogas-module = { path = "../runtime/rust" }
serde_json = "1"
use ogas_module::{reply, run_step, Refusal, Reply, Step};
use serde_json::json;

fn handle(step: &Step) -> Result<Reply, Refusal> {
    let text = step.input()["text"].as_str();
    let text = text.ok_or_else(|| Refusal::new("Nothing to shout").code("no_text"))?;
    let said = text.repeat(step.integer("times")? as usize);
    Ok(reply("success", json!({ "text": said }), &[]))
}

fn main() {
    run_step(handle);
}

The manifest, dictionaries, folder, memory and rules are the same as in section 1; rules 1.4–1.10 do not depend on the language. A panic in a step is a failed step, not a fallen module. A full example — a step module and a long one with the memory of a node — is sdk/rust/examples/double.rs. The example above is the file sdk/rust/examples/shout.rs, the example of section 5 — core/protocol/examples/function.rs: cargo test builds them, and make ci compares the text here with these files word for word.

Table 10 — The SDK for Rust beside the SDK for Python

Python Rust
run_step(handle) run_step(handle)
run_long(handle, on_suspend=…) Long::new(handle).on_suspend(…).run(); also on_init, on_configure
cancelled.is_set() and Cancelled cancel.check()? or cancel.is_set(): Cancel
options["steps"] step.integer("steps")?, step.number(…), step.text(…), step.boolean(…), step.parameters()
params.get("input"), params.get("input_port") step.input(), step.input_port()
chosen_model(params, name), file_path(params, ref) step.chosen_model(name), step.file_path(&reference)
state_dir(params), resumed(params), work_dir(params) step.state_dir(), step.resumed(), step.work_dir()
new_file(params, name) step.new_file(name)?
find_file(given, "mask", media="image/") find_file(&given, &["mask"], "image/")
find_value(given, "words") find_value(&given, &["words"])
person_path(said) person_path(said)?
file_ref(name), reply(port, output, files) file_ref(name, None), reply(port, output, &[file]); the answer is read through Reply::value
Refusal("…", code="x", retryable=True) Refusal::new("…").code("x").retryable()
say("key", target=x) say("key", &[("target", x)])?
NodeMemories(make) NodeMemories::new(make): .of(step), .memories()
read_memory, write_memory, whole_file, model_mark read_memory, write_memory, whole_file(path, write), model_mark(&[path])
send_progress, send_log, send_emit, send_signal the same names
send_partial(execution_id, port, data) send_partial(step, port, data)
module_dir() module_dir()
secret(name), run_dir() secret(name), run_dir() — Option
— constants MODULE_FAILED, MODULE_CANCELLED

The check: cargo test in the package of the module; the contract by files — the same ogas_check.py (the manifest, dependencies, what is brought along). How the SDK checks itself — sdk/rust/tests/protocol.rs.

4 A module in another language

The protocol is read in Methods of modules, section 1: lines of JSON through stdin and stdout, init with { "status": "ready", "protocol": 1, "capabilities": […] }, execute, cancel, ping, suspend, configure. The model of behaviour is the sources of the SDK; a conversation with a module is checked by ogas_check.start, send and read_json.

5 A function of the platform

A function is a separate OS process and a Cargo package; the folder of a new function enters the workspace by itself. The rules — Rules of development, 2.3–2.6. The make-up of the folder — The make-up of the platform, Appendix A, A.3.

  1. The folder functions/<id>/ with Cargo.toml, src/main.rs, src/state.rs, src/codes.rs, src/constants.rs, locales/{ru,en}.json.
  2. function.json — what the function is (File formats, section 3): requires, optional, contract, restart.
  3. An entry in the register core/data/functions.json.
  4. main.rs only starts and hands out:
use ogas_protocol::{run_function_loop, RpcError, StartedFunction};
use serde_json::{json, Value};

fn handle(method: &str, _params: Value) -> Result<Value, RpcError> {
    match method {
        "function.health" => Ok(json!({ "status": "ok" })),
        _ => Err(RpcError::domain(
            ogas_protocol::codes::METHOD_NOT_FOUND,
            format!("Unknown method {method}"),
            false,
        )),
    }
}

fn main() {
    let StartedFunction { client, .. } =
        ogas_protocol::start_function(env!("CARGO_PKG_VERSION"), |spec, _| {
            spec.queries = vec!["function.health".into()];
            spec.commands = vec![];
            spec.events = vec![];
        });
    run_function_loop(
        client,
        move |method, params, _| handle(method, params),
        |_| {},
    );
}

start_function reads the environment, connects to the kernel and introduces itself; if that fails, the function ends with its reason. The line spec.commands = vec![…] is read by the check of the contract too, so the names are listed here and not gathered by code. The description gets spec and the installation folder: spec.locales, spec.surfaces, spec.capabilities are added there as well.

The handler of each request runs in its own thread. Other functions are called through client.request(method, parameters, QUERY_TIMEOUT): there are no direct ties between functions. A refusal is RpcError::domain(code, text, retryable); codes are declared in src/codes.rs, and given their text in Error codes and the dictionaries. Settings — settings.schema.json and its registration at the start of main; without the function of settings, the defaults of the schema work.

Every method and event is written into the contract, docs/contract/: make ci compares the list of the function with the documents. Code common to two functions goes into core/protocol, not copied.

6 A kind of node

A kind is justified only if it is a primitive: that is rule ЗС-10 in Kinds of nodes. The folder functions/graph/src/kinds/<id>/ holds:

A kind is entered by a line in KINDS of the file kinds/mod.rs, the only list, and by a card in Kinds of nodes.

7 An element of the pulpit

The set is closed and is extended by the platform (The workshop, 6.2).

  1. The folder functions/canvas/src/elements/<id>/: element.json — kind, family (input, show, action, decoration), bindings, parameters, size, min_size, states; locales/{ru,en}.json. A parameter has how: number, text, choice, list, image, model_kind.
  2. An entry in ELEMENTS of the file elements/mod.rs.
  3. The drawing — functions/ui/window/ui/pulpit/elements/<id>.js with draw(state, element, { name }) and a line in index.js; what it shows for a look — shows.
  4. A row in table 6 of File formats and a description of the element in the stub functions/ui/window/ui/tests/fixtures.js: otherwise the page tests draw it as unsound.

A binding to a container is declared as "objects": ["group"]: the check, the mark and the carrying over into a piece work by the description of the kind. A label and a hint (label, hint) are there on every element except a decoration.

8 A window of the workshop

A window is the folder functions/ui/window/ui/host/<id>/ with index.js (WINDOW: kind, drawing, the view, the panel, fields, tests), words locales/{ru,en}.json, a style and checks; a line in host/index.js, the only list of windows. Colours and sizes are tokens of style.css, words go through t(key), requests through rpc of lib/rpc.js, writing the state through commit(). The page is built into the window program: after a change make window is needed. The page tests run in the window itself: in the stub stubbedPlatform the page calls the platform by itself as well, so a call is looked for by its method, not taken as the last one.

9 A program by script

A script builds a program in a working platform through the door for programs. The door is closed until the person opens it in the settings of the platform («Door for programs» → «Open»); the platform writes the port and the key into gate.json of the data folder, and Platform.connect() finds it by itself — in the data folder of the installation where the SDK lies, or in the one named by the variable OGAS_DATA_DIR. A program with the key of the door can do in the platform everything the person does in the workshop, except stopping the platform and changing the door itself (Methods of the platform services, section 5).

from ogas import Platform, Program

program = Program("Hello", "Writes the name into the answer.")
program.variable("name", "text", "")
program.variable("said", "text", "")
program.event("go", source="pult")
program.node("say", "write", {"variable": "said", "mode": "replace"})
program.edge("go", "say", target_port="value")
window = program.window("window_1", "Hello", sheet=(560, 300))
box = program.group("g_main", "Hello", window)
program.element("field_name", "field", box, {"value": "var:name"}, "Name", "Whom to greet.", (24, 48, 480, 52), writes="typing")
program.element("button_go", "button", box, {"act": "event:go", "carries": {"text": "var:name"}}, "Say", "Write the name into the answer.", (24, 112, 200, 52))

with Platform.connect() as platform:
    print(program.save(platform))
sdk/ogas-sdk run build.py

Table 11 — The package ogas

Name What it does
Platform.connect(data_dir=None, port=None, key=None) enter the platform; without a port and a key they are read from gate.json
Platform(port, key, host, client) the same, when the port and the key are known
platform.call(method, params, timeout, command=True) one request; a command gets its own command_id; a refusal is raised as the exception Refused
platform.attempt(method, params) the same request, a refusal as the answer { error, message, data }
platform.programs(), platform.program_id(name) the person's programs; the id of a program by its name
platform.catalog(language) kinds of nodes with inputs, outputs, fields and words, elements of the pulpit, types of variables
platform.events the last events of the platform that came to the connection
Refused a refusal of the platform: method, code, message, data
data_folders(), read_gate(data_dir) where to look for the data folder; what gate.json says
Program(name, description) a program: node, event, module, edge, variable — the graph; window, tab, group, element — the pulpit
program.lay_out() place the nodes the script gave no place, from left to right by their distance from the events
program.save(platform) switch the program off if it is on, write the graph, check the frames of the groups, write the pulpit, check the program; the answer — { process_id, nodes, edges, elements, validate }
FramesMeet an exception of save: frames of groups run into each other or out of the sheet; crossing names them
frames_meeting(groups, shown) the same check of frames by the answer of canvas.preview

What to remember in the graph:

What to remember when building and trying:

10 The rules of the platform in a module

A module is a part of the platform and keeps its rules (Rules of development, 1.2.1); how each rule applies to a module is shown by Appendix A of the Rules of development, and the threats a module guards against — by Protection, section 3. Table 12 says what follows from them for the author of a module and who keeps it: what can be seen by the files is named by ogas_check.py (table 4), the rest is kept by the author. The check only names a breach and changes nothing in the module.

Table 12 — The rules of the platform and a module

Rule What follows for the module Who keeps it
Rules of development, 2.1 a module is one task; main.py starts the module and hands the step to whoever's task it is; a subtask is a file of its own the author
Rules of development, 2.2 the default of a parameter — only in the manifest, words — in the dictionaries of the module; a value that affects the result is a parameter, an inner quantity is a constant with a name and a reason ogas_check.py — a second default, a text in the manifest, a bare number in a call of a network; the rest, the author
Rules of development, 2.3, 2.6; Protection, T-02, T-03 a module knows only the protocol: it takes files from the step, its own folder, the folder of the run and the memory of the node, not from others' folders and the data of the platform; it does not connect to the kernel ogas_check.py — names of the kernel and of files of the platform in the code; a path beyond the allowed folder is refused by the platform; the rest, the author
Rules of development, 2.4 a module writes only its own: result files, the memory of its node, a folder the person named; files that came in the data it only reads the platform — data files come read-only; the rest, the author
Rules of development, 2.5, 2.10 a module is a folder: manifest, code, dictionaries, its own checks; to add a module is to put the folder, to delete it is to remove the folder the platform reads the list of modules from the folder; the make-up of the folder — ogas_check.py and the author
Rules of development, 2.7 everything that affects the result is named as a parameter; the course of a long work — send_progress, what is said for the journal — send_log, a failure of a service between steps — send_log with the level error ogas_check.py — a parameter nobody reads, and a bare number; the rest, the author
Rules of development, 2.8, 2.9 a parameter nobody reads is removed; what several modules share is taken into the SDK ogas_check.py
Rules of development, 2.11 in code, comments and dictionaries there are no references to documents of the platform: the reason is told in your own words ogas_check.py
Rules of development, 3.1; Protection, T-07 packages — only from requirements.txt with versions, installed by the platform at the person's command; a module deletes the person's data only at their request or by a parameter the person sees ogas_check.py — installing and versions; deleting — the author
Rules of development, 3.2 not done — a refusal with a code and a text; a failure skipped on purpose is explained on the except line; the unfamiliar is not read as familiar; a refusal leaves nothing half-written without a mark ogas_check.py — a silent skip and the text of a refusal; the rest, the author
Rules of development, 2.7, 3.3 what the module needs from the computer is said in the manifest: video memory — resources.gpu_mb ogas_check.py — a module with the video card and without gpu_mb
Rules of development, 3.3; Protection, T-01, T-06 the network is declared in permissions_info.network; execution_semantics is honest: what sends letters, pays or writes outside is non_idempotent ogas_check.py — the network; repeating — the author
Rules of development, 3.4 the name, the description, the labels and hints of parameters, choices and ports, the texts of refusals and of say — in both dictionaries, one set; Russian is written in Russian, in the person's words ogas_check.py
Rules of development, 3.5 a file that outlives a step is written whole: into a temporary one beside it and by renaming (whole_file, write_memory); unloading saves what the work will go on from ogas_check.py — suspend without memory; the rest, the author
Rules of development, 4.5 ready means checked: ogas_check.py is clean, the own checks test_<id>.py pass, a step is carried through execute and the result looked at with your own eyes the author
Protection, T-04 stdout belongs to the exchange: a module with the SDK writes its own to stderr or send_log ogas_check.py; a line that is not a message is refused by the platform
Protection, T-05 a secret — in .env of the module folder, read by secret(name); not a parameter, not a journal entry and not result data ogas_check.py — the name of a parameter; the rest, the author
Protection, T-08 big data go as result files, not in the data of a step; a long work tells its course and hears the cancel the limits are kept by the platform; the rest, the author

Sections 4 and 5 of the Rules of development — the order of development and the documentation — are kept by whoever runs the platform; for a module, of them follows only the confirmation of readiness by a check (4.5).

11 What is checked by itself

Table 13 — Checks

Command What it checks
python3 sdk/python/ogas_check.py <folders> the contract of a module and the rules of the platform, by files (tables 4 and 12)
make ci format, clippy, the rules of scripts/check_rules.py, tests; the reference of the SDK in this guide is compared with the code: a name of the SDK, a kind of node, a source of an event or an element of the pulpit that is missing here does not pass
make build required before a live check: the functions are taken from target/debug
make window, then the page checks in the window of the sandbox (functions/ui/window/ui/tests/run.js) the page in the window itself: styles, layout, events
sdk/tests/live/run.sh <task> a whole live task

12 The program: documents, nodes and the pulpit

A reference of what a program is built of — by hand in the workshop or by script (section 9). Everything there is is named here, with its form in the document; the exact rules of each are where the link leads.

12.1 The documents of a program

A program is the folder data/processes/<process_id>/ (File formats, section 5):

A program is handed over as an export folder (File formats, 10.1): with the modules it takes and the processes it calls. A showcase hands out programs, modules, neural networks and pieces of interface as things ogas.catalog/1 (File formats, 17.1); a piece is a container of the pulpit with everything in it, the document ogas.piece/1 (File formats, 7.4).

{
  "nodes": [
    { "id": "go", "kind": "event", "x": 0, "y": 200, "parameters": { "source": "pult", "overlap": "skip" } },
    { "id": "read", "kind": "module", "module_id": "cdr_svg", "x": 280, "y": 200, "parameters": { "precision": { "variable": "precision" } } },
    { "id": "keep", "kind": "write", "x": 560, "y": 200, "parameters": { "variable": "parts", "mode": "replace", "path": "count" } }
  ],
  "edges": [
    { "id": "go.event-read.drawing", "source_node": "go", "source_port": "event", "target_node": "read", "target_port": "drawing" },
    { "id": "read.drawing-keep.value", "source_node": "read", "source_port": "drawing", "target_node": "keep", "target_port": "value" }
  ],
  "variables": [
    { "id": "precision", "type": "number", "initial": 1, "remember": true },
    { "id": "parts", "type": "number", "initial": 0, "remember": false }
  ]
}

12.3 Kinds of nodes

Each kind of node is described by a card in the catalogue — Kinds of nodes, Appendix A: what it does, the inputs and outputs with what comes to each, what the person sets, how many ports there are and where the rules are. An overview of the catalogue is there too, tables 4–13. What a kind gets when it is placed and what choices its fields have is its description functions/graph/src/kinds/<kind>/kind.json: inputs, outputs, template.parameters, fields with choices; the workshop shows the same on the node.

Five kinds are enough for a first program: event — the work begins with it (for a button of the pulpit source: pult), module — the work of a module, write — put data into a variable the pulpit shows, variable — take the value of a variable as a cold input, show — show data on the node and on the pulpit.

12.4 The pulpit: containers and elements

{
  "groups": [
    { "id": "window_1", "name": "Site", "holds": "tabs" },
    { "id": "tab_waiting", "name": "Waiting for a look", "parent": "window_1", "screen": { "sheet": [1240, 900] } },
    { "id": "g_list", "name": "List", "parent": "tab_waiting" }
  ],
  "elements": [
    { "id": "button_refresh", "kind": "button", "group": "g_list", "label": "Refresh", "hint": "Ask the server what is waiting.",
      "bindings": { "act": "event:refresh" }, "screen": { "place": [24, 48], "size": [240, 52] } }
  ]
}

The elements with their bindings and parameters — table 6 of File formats, their states for looks — table 7 there; what a kind of element takes is its description functions/canvas/src/elements/<kind>/element.json. A binding names an object of the graph: var:<id> — a variable, event:<node_id> — an event of the pulpit, cancel:, retry: — stop and retry it, status: — how the run goes, progress: — the course of a step of a module, out:<node_id>/<output> — the last thing an output gave, show: — what «Show» showed, ask: — an open question, signal: — a signal a node waits for, program — whether the program is on; what each object means and what the pulpit does with it — The program: graph and runs, table 5.

12.5 A parameter of a module on the pulpit

A parameter of a module on the pulpit is a variable the node takes into the parameter by the record { "variable": "<id>" }, and an input element bound to it. The element is chosen by the type of the parameter in the manifest; the limits, the choices and the first value come from there too, the label and the hint from the dictionary of the module (module.<id>.param.<name>.title and .help): the person sees on the pulpit the same words as on the node.

Type of the parameter Variable Element Its parameters
integer, number number «Number» min, max from the manifest, step
boolean boolean «Switch» —
string with choices text «Choice» options — the codes of the manifest, titles — the words .choice.<code> of the dictionary
string text «Field» lines, if the text is long
string[] list «Rows» —
model text «Choice» models — the kind of neural networks of the parameter
color text «Field» —

A long set of settings is written by the script itself — from the manifests of the modules: so a field does not drift from its parameter when the module changes. A button with the binding resets returns the fields of its container to their first values.

12.6 The requests a program is built and tried with

A script and a check call the platform with the same requests as the workshop; each is described in the contract of its function:

13 A network of the assistant

The assistant of the workshop thinks with the network the person chose. A network is a module with "role": "assistant_network": the input request, the output reply. Ready networks come with OGAS SDK (sdk/networks): by the OpenAI protocol — spoken by OpenAI, DeepSeek, OpenRouter, Mistral, Groq, Qwen, xAI and the local servers Ollama, LM Studio, llama.cpp, vLLM — Anthropic, Gemini, GigaChat, YandexGPT and a local network from the folder of networks. The panel of the assistant puts the chosen one into the folder of modules. Another provider is added by a module of your own with the same contract.

The request that comes to the module:

{ "system": "…",
  "messages": [ { "role": "user", "text": "…" },
                { "role": "assistant", "text": "", "calls": [ { "id": "c1", "name": "programs_list", "arguments": {} } ] },
                { "role": "tool", "call_id": "c1", "name": "programs_list", "text": "…" } ],
  "tools": [ { "name": "draft_create", "description": "…", "schema": { "type": "object" } } ],
  "limit_tokens": 4000 }

The answer of the module — reply("reply", { text, calls, usage: { input_tokens, output_tokens }, model }). What all networks share lies in ogas_assistant; the module only has to put the request into the language of its service and the answer back. The module reads the key of the service by secret("NAME") from its .env, and names its secrets in the field secrets of the manifest: by it the panel of the assistant asks the person for the key and writes it into .env.

Table 14 — ogas_assistant

Name What it does
request_of(params) the request of the assistant the step carries; without it — refusal bad_answer
answer(text, calls, input_tokens, output_tokens, model) the answer of the network, ready to be given by the step
arguments_of(raw) the arguments of a call as an object — from an object or from its JSON text
new_call_id() the id of a call when the service gave none
post(url, body, headers, form=None, verify=True) a POST to the service and its JSON answer; no answer — refusal unreachable, which the platform repeats; an answer with an error — service_refused with the words of the service

The refusal codes of a network are no_key, no_model, unreachable, service_refused, bad_answer; their texts lie in the dictionaries of the module, as with any refusal (1.7).

14 Examples

sdk/examples/ holds modules and programs that run. word_count is a module with a parameter and a refusal, and a program for it built by script through the door:

sdk/ogas-sdk try sdk/examples/word_count/modules/word_count --input '{"text": "one two two"}'
cp -r sdk/examples/word_count/modules/word_count modules/
sdk/ogas-sdk run sdk/examples/word_count/build.py

Programs of several modules lie in sdk/tests/live/: a backup copy of a folder (backup), reading invoices (invoices) and others; networks of the assistant — in sdk/networks/.