diff --git a/packages/ai/src/provider-utils/IBackendsTransport.ts b/packages/ai/src/provider-utils/IBackendsTransport.ts index 26dd437d4..67bf0d8dd 100644 --- a/packages/ai/src/provider-utils/IBackendsTransport.ts +++ b/packages/ai/src/provider-utils/IBackendsTransport.ts @@ -23,8 +23,12 @@ export interface IEnsureRunningRequest { readonly backend: string; /** Absolute path to the model file. */ readonly modelPath: string; - /** Runtime options forwarded to the backend process. */ - readonly opts: { readonly ctx: number }; + /** + * Backend-specific runtime options forwarded to the broker as opaque JSON. + * llamacpp uses `{ ctx: number }`; sd-cpp passes an empty object `{}`; + * future backends define their own schema. + */ + readonly opts: Readonly>; } /** @@ -40,7 +44,10 @@ export interface IRunningHandle { /** * Decrements the broker's refcount for this handle. The backend may shut * down after the broker's idle timeout if refcount reaches zero. - * Fire-and-forget — no ack is awaited from the broker. + * + * The returned promise resolves once the release message has been posted + * to the port; the broker does not acknowledge. Errors posting (e.g. port + * closed) reject. */ readonly release: () => Promise; } @@ -70,7 +77,7 @@ export interface IBackendsTransport { /** * Acquire (or share) a running backend. Resolves once the backend is healthy. * - * Multiple callers requesting the same `(backend, modelPath, opts.ctx)` triple + * Multiple callers requesting the same `(backend, modelPath, opts)` triple * will share one process via the broker's refcounting. `release()` on the * returned handle decrements the refcount. */ @@ -79,9 +86,11 @@ export interface IBackendsTransport { /** * Subscribe to status updates for a backend. * - * The callback is called immediately (on the next port message) and on every - * subsequent status change. Subscriptions persist across port reconnects - * (utility crash + restart). + * The callback fires on every subsequent broker `status` event; callers + * wanting an initial snapshot must invoke `list()`. Subscriptions persist + * across port reconnects. Implementations MUST be idempotent: calling the + * returned unsubscribe twice is a no-op; subscribing the same callback + * twice is allowed and de-duplicated. * * @returns An unsubscribe function. Call it to stop receiving updates. */ @@ -95,4 +104,18 @@ export interface IBackendsTransport { * `total` may be 0 if the content-length is unknown. */ install(backend: string, onProgress?: (bytes: number, total: number) => void): Promise; + + /** + * Fire-and-forget request for the broker to broadcast a `status` + * event for every backend in its registry. Resolves once the request + * has been posted (the broker does not send a discrete reply). + */ + list(): Promise; + + /** + * Removes the backend's installed binary. In v1 the broker rejects + * this with an error; callers should handle the rejection. Future + * versions may implement teardown semantics. + */ + uninstall(backend: string): Promise; } diff --git a/packages/test/src/test/ai/IBackendsTransport.types.test.ts b/packages/test/src/test/ai/IBackendsTransport.types.test.ts new file mode 100644 index 000000000..d002ae162 --- /dev/null +++ b/packages/test/src/test/ai/IBackendsTransport.types.test.ts @@ -0,0 +1,75 @@ +/** + * @license + * Copyright 2026 Steven Roussey + * SPDX-License-Identifier: Apache-2.0 + */ + +// ──────────────────────────────────────────────────────────────────────────── +// Compile-time conformance tests for IBackendsTransport. +// +// These tests run via the test runner but their value is in `tsc` accepting +// (or rejecting) the declarations below. No runtime assertions of substance: +// if the file compiles, the contract holds. +// +// Lives under packages/test/src/test/ai/ so that scripts/test.ts picks it up +// (the runner only scans packages/test/src/test). +// ──────────────────────────────────────────────────────────────────────────── + +import type { + IBackendsTransport, + IBackendStatus, + IEnsureRunningRequest, + IRunningHandle, +} from "@workglow/ai/provider-utils"; +import { expect, test } from "vitest"; + +// `opts` is open — accepts the historic llamacpp shape … +const _checkOptsWithCtx: IEnsureRunningRequest["opts"] = { ctx: 4096 }; +// … the empty shape that sd-cpp uses today (no per-run options) … +const _checkOptsEmpty: IEnsureRunningRequest["opts"] = {}; +// … and arbitrary shapes future backends may define. +const _checkOptsArbitrary: IEnsureRunningRequest["opts"] = { foo: "bar", n: 42 }; + +// Interface exposes `list` and `uninstall` alongside the existing methods. +type _Methods = keyof IBackendsTransport; +const _hasList: _Methods = "list"; +const _hasUninstall: _Methods = "uninstall"; + +// Structural conformance using explicit parameter signatures: TypeScript +// allows assigning `() => X` to `(arg: T) => X`, so a zero-arg dummy would +// silently accept a parameter-list change. Spelling each signature out +// forces a typecheck failure on any rename, type change, or return-type +// change to a method of `IBackendsTransport`. +const _conforms: IBackendsTransport = { + ensureRunning: (_req: IEnsureRunningRequest): Promise => { + return Promise.resolve({ + url: "http://127.0.0.1:0", + release: (): Promise => Promise.resolve(), + }); + }, + subscribeStatus: ( + _backend: string, + _callback: (status: IBackendStatus) => void + ): (() => void) => { + return (): void => undefined; + }, + install: ( + _backend: string, + _onProgress?: (bytes: number, total: number) => void + ): Promise => Promise.resolve(), + list: (): Promise => Promise.resolve(), + uninstall: (_backend: string): Promise => Promise.resolve(), +}; + +// Silence `no-unused-vars` / `noUnusedLocals` on the type-only assertions. +void _checkOptsWithCtx; +void _checkOptsEmpty; +void _checkOptsArbitrary; +void _hasList; +void _hasUninstall; +void _conforms; + +// Vitest requires at least one runtime test in the file. +test("IBackendsTransport conformance compiles", () => { + expect(true).toBe(true); +}); diff --git a/providers/stable-diffusion-cpp/src/ai/StableDiffusionCppProvider.ts b/providers/stable-diffusion-cpp/src/ai/StableDiffusionCppProvider.ts index 56e3d8722..bd79f141c 100644 --- a/providers/stable-diffusion-cpp/src/ai/StableDiffusionCppProvider.ts +++ b/providers/stable-diffusion-cpp/src/ai/StableDiffusionCppProvider.ts @@ -109,7 +109,7 @@ function createStableDiffusionCppImageGenerateRunFn( handle = await options.transport.ensureRunning({ backend: "stable-diffusion-cpp", modelPath: model.model_id, - opts: { ctx: 0 }, + opts: {}, }); baseUrl = handle.url.replace(/\/$/, ""); }