Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions packages/ai/src/provider-utils/IBackendsTransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Record<string, unknown>>;
}

/**
Expand All @@ -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<void>;
}
Expand Down Expand Up @@ -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.
*/
Expand All @@ -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.
*/
Expand All @@ -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<void>;

/**
* 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<void>;

/**
* 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<void>;
}
75 changes: 75 additions & 0 deletions packages/test/src/test/ai/IBackendsTransport.types.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* @license
* Copyright 2026 Steven Roussey <sroussey@gmail.com>
* 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<IRunningHandle> => {
return Promise.resolve({
url: "http://127.0.0.1:0",
release: (): Promise<void> => Promise.resolve(),
});
},
subscribeStatus: (
_backend: string,
_callback: (status: IBackendStatus) => void
): (() => void) => {
return (): void => undefined;
},
install: (
_backend: string,
_onProgress?: (bytes: number, total: number) => void
): Promise<void> => Promise.resolve(),
list: (): Promise<void> => Promise.resolve(),
uninstall: (_backend: string): Promise<void> => 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);
});
Original file line number Diff line number Diff line change
Expand Up @@ -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(/\/$/, "");
}
Expand Down
Loading