Skip to content

rutis v0.17.2

Choose a tag to compare

@github-actions github-actions released this 27 Oct 01:54
· 1236 commits to main since this release
8567832

rutis 0.17.2

中文 · 0.17.1 release notes

0.17.2 fixes problems that people using rutis run into today, all found by the developer-experience evaluation (#663). Plugin code in every language stays as it is, and so does rutis.json. The "Behaviour changes" section lists every difference you can see.

One plugin's problem stays with that plugin

  • A bad value fails only that call (#675, #706). A value that cannot cross processes (an object with methods, or a class instance, sent to a language other than Rust) or a message over 16 MiB used to end the whole runtime process, stopping every plugin in that runtime. Now it is refused before it is sent and only that call fails; the error names the method, the argument and the reason: argument 1 of py_taker.take: an object with methods cannot cross to py; pass plain data or a function instead. For a return value it says the result of ….
  • Sparse arrays cross: the holes in [1, , 3] are sent as undefined and arrive as null / None. They used to end the session.
  • A non-Error value thrown by TypeScript no longer ends the session when it passes through Go: the TypeScript caller gets the original value.
  • A runtime process that exits blames no other plugin (#682, #707). Plugins still loading in that runtime used to be reported as failed to start. Now the report says which runtime exited and how (exit status or signal), and all its rows wait for that runtime, the ones still loading included; calls in progress that fail say the same. The runtime's own error output and stack still appear in the output.
  • Providing a name that is taken fails only that row. When a plugin provides a service name the host already has, only its own row fails; plugins using the host's service keep running, in an isolated scope too. Another, innocent plugin in the same runtime used to fail to start as well.
  • When the host exits, the runtimes it started exit too, also while a plugin is in a busy loop. Node and Bun runtimes have a watchdog thread: once the connection to the host has ended, if the process is still there one second after the shutdown deadline, it ends the process. The Python runtime likewise counts from the thread that reads the connection. A busy Node runtime used to stay behind as an orphan process.
  • When a remote runtime reconnects, all its rows are resolved again with the new description (#701). Rows resolved while it was connected used to keep the old result: a plugin replaced over there (one that now uses another service, say) was still loaded by its old description. Local runtimes are unchanged.

A runtime that is not ready (for example one that never greets because a debugger is attached) still holds up the rows of other runtimes; this moves to 0.18, together with runtime status (#678).

Waiting lines say what is missing

(#676 first half, #704)

  • rutis-host's waiting lines name the missing service and why it is missing: weather: waiting for lmm (no row provides it; did you mean llm?), chat: waiting for llm (row model failed: …), waiting for llm (row model's runtime py is down), and two rows that inject each other. When disabled rows that were never resolved might provide it, the line does not claim that no row does: waiting for llm (no enabled row provides it; rutis-host check says whether a disabled row does).
  • A plugin using a service it does not declare in inject is told to add it: service tsvc is not available to this plugin, which does not declare it in inject: add 'tsvc' to its inject (the same sentence in Node, Bun, Python, Go, Swift, Kotlin and the testing tools). A service it can get is still given to it.
  • Rust embedding: when a plugin has waited 10 seconds for a service nothing provides, the error sink gets one ErrorKind::StillWaiting naming the missing service; root.report_waiting_after(None) turns it off and Some(duration) changes the delay. rutis-host does not use it (its waiting lines already say this). When a remote link is down for more than 10 seconds, the link's own plugin reports this once too, under an internal name (rutis-bridge/import#main); in 0.18 the platform's own waits show only in status (#678).
  • rutis-host check warns about rows that inject each other in a cycle and about several rows providing the same service in one scope; the exit code is unchanged (0.18 makes these errors). When the only row providing a service is disabled, the warning names that row.

rutis-host dev

(#665, #703)

  • Only the row whose file changed reloads, and it says why: weather: reloaded (src/index.ts changed); rows using it restart with it and say so too: caller: restarted (weather reloaded).
  • A change to rutis.dev.json applies while running, by the rules of run --watch-config.
  • When the new code does not import (a syntax error), the row keeps running its previous version: weather: cannot reload: …; the previous version keeps running.
  • When a runtime process has stopped, the next change to one of its rows starts it again, instead of printing a false reloaded. Every time a runtime ends, runtime …: down is printed.
  • Go and Swift projects are rebuilt only when their own sources change. After a panic in a Go plugin's apply is fixed, the old failure is no longer printed.

Host authors

(#666, #704, #705, #703)

  • When rows use py, bun or node and runtimes does not list it, it is enabled with its defaults and a note is printed once. If a runtime enabled this way cannot start, the host does not exit; only its rows fail.
  • When the Python environment does not exist, or has no rutis installed, the error gives the exact commands to create it and install it.
  • The rutis-host guide opens with a quick start (one TypeScript plugin, one Python plugin) and has a new section on how a host installs plugins.
  • The templates of rutis-host new declare provides, so a new project passes check --strict as it is.
  • rutis-host installed from npm: the launcher forwards SIGUSR1 to rutis-host (ExecReload=/bin/kill -USR1 $MAINPID works) and no longer opens Node's debugger port.

Plugins' dependency on the SDK

(#672, #705)

  • Python plugins declare only a lower bound: rutis>=0.17, no longer <0.18. A plugin and the Python runtime share the one rutis in the host's environment, and an upper bound kept the host from upgrading. While the plugin API version stays the same, Python's rutis does not remove or rename names plugins import.
  • npm and Bun plugins keep a ^ range: each plugin carries its own copy of the SDK, and several copies work side by side.

Rust embedding

(#667, #705)

  • The docs now say exactly what view.await waits for: it returns once what was queued for the plugin has been handled; a plugin whose dependencies are missing stays Pending and is not running. To wait until it runs, watch its state with view.watch().
  • New: rutis_bridge::session::call_service(&ctx, name, method, args) calls any service by name from Rust (one provided by a row in any language, a #[rutis::service] type, or a hand-written one).
  • Every example in the rust-host guide compiles and the first one runs; the loader example now includes Builtins, Chain and absolute paths.

Fixes

  • Small mistakes in templates and guides (#668): the TypeScript template builds before npm pack; the Python template's test command; the Go guide's test and rutis.dev.json are complete; rutis-host types is in the command table.
  • A reload that removes a local runtime while it is starting returns only once its process has ended (#716). A stopping link used not to wait for the step that starts the process, so the reload could return before the process started; the process then started and was ended shortly after. The same at host shutdown.

Behaviour changes

What you can see differently after upgrading; none needs a code change:

Where Before Now
Kernel FiberView::restart / update On a failed plugin whose dependencies are gone, returned the last load's error Returns success and the plugin waits for its dependencies (this restart loaded nothing, so nothing failed)
Kernel error sink Nothing reported while a plugin waits for a service nothing provides One StillWaiting after 10 seconds; report_waiting_after(None) turns it off
A bad value crossing processes The runtime process exited Only that call fails; the error starts with argument N of service.method: or the result of service.method:
Text of rutis_bridge::session::Error::Value Started with invalid binding value: No such prefix
Sparse arrays Ended the session Holes cross as undefined
use of a service not in inject (when it is not available) service x is not available Adds that it should go into inject
rutis-host waiting lines waiting for its services Name the missing service and the reason (unchanged when no reason can be told)
rutis-host check — Warns about cycles, same-name provides, and a disabled only provider; exit code unchanged
rutis-host: rows use an undeclared runtime The row failed: no plugin named "py:llmbox" The runtime is enabled with its defaults, with a note
rutis-host dev One changed file reloaded every row of that language; rutis.dev.json changes did not apply Only that row reloads, saying why; rutis.dev.json applies; SIGUSR1 is handled
Under dev, a runtime row restarting for another reason Read the file as it is now Runs the code of its last reload (run is unchanged)
The Python runtime failing to re-import a module Left the module half changed Puts back what it was before
The npm launcher receiving SIGUSR1 Node opened its debugger port Forwarded to rutis-host
Python plugin template rutis>=0.17.x,<0.18 rutis>=0.17.x
Rows still loading when their runtime process exits Failed with failed to start Wait for that runtime; fail only if the runtime's end has not reached the row within 30 seconds
Node runtime: a host service whose name a row holds Threw, failing the rows using that service Registered once that row's service is withdrawn
Node and Bun runtime processes — One more watchdog thread: once the connection has ended, a process still alive one second after the shutdown deadline is ended, with a line on stderr
The Python runtime's shutdown deadline Counted from when the main thread saw the connection end Counted from when the thread reading the connection saw it end; the give-up message adds "or a plugin keeps the runtime busy"
A remote runtime reconnecting Rows resolved while connected were not resolved again All its rows are resolved again
rutis-bridge RuntimePlugin Provided the Runtime service, then set the state to Ready Sets Ready, then provides the service (back to Starting if providing fails)

Compatibility

  • No API is removed or renamed, and the formats of rutis.json and rutis.dev.json are unchanged; nothing to migrate.
  • The protocol is unchanged. 0.17.2 connects to 0.17.1, 0.17.0 and 0.16.0 as usual. The host checks values before forwarding them, so with an older runtime under a new host a bad value also fails only that call; a sparse array sent by an older Node runtime still ends the session.
  • New public API: in rutis CtxHost::report_waiting_after, CtxHost::name_keys_with, KeyNamer, ErrorKind::StillWaiting; in rutis-bridge session::call_service, RuntimeHandle::starts, Sender::max_message (with a default), Wire::no_caller (with a default); in rutis-loader Loader::waits, Loader::waits_of, report::Wait, report::WaitReason, CheckReport's cycles and clashes, Requirement::like, Supply::OnlyDisabled, BinaryRuntimesHandle::states_started.

Packages

All of these are version 0.17.2: on crates.io rutis, rutis-macros, rutis-bridge, rutis-loader, rutis-host, rutis-sdk, rutis-dylib, rutis-dylib-meta, rutis-dylib-launcher, rutis-migrate; on npm @arcships/rutis, @arcships/rutis-runtime, @arcships/rutis-bun, @arcships/rutis-host and the five platform packages; on PyPI rutis, rutis-host; the Go module github.com/arcships/rutis/go/rutis (tag go/rutis/v0.17.2); the Swift package (v0.17.2); Kotlin io.github.arcships:rutis and io.github.arcships:rutis-ksp; the rutis-host binaries on the GitHub release page.