Skip to content
Open
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
49 changes: 49 additions & 0 deletions packages/provider-muse/src/server/status.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import * as NodeServices from "@effect/platform-node/NodeServices";
import { expect, it } from "@effect/vitest";
import { MuseSettings } from "../settings.ts";
import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
import * as Fiber from "effect/Fiber";
import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as Schema from "effect/Schema";
import * as Sink from "effect/Sink";
import * as Stream from "effect/Stream";
import { ChildProcessSpawner } from "effect/process";
import * as TestClock from "effect/testing/TestClock";
import { vi } from "vite-plus/test";

import { writeFakeCli } from "@t3tools/provider-testing/fakeCli";
Expand Down Expand Up @@ -54,7 +61,49 @@ const fakeCli = (source = 'process.stdout.write("muse 1.0.3-R2198.1\\n");') =>
return writeFakeCli({ directory, name: "muse", source });
});

// A `muse --version` that answers only after `delay`, like Muse started inside a Windows job object.
const slowVersionSpawner = (delay: Duration.Input) =>
Layer.succeed(
ChildProcessSpawner.ChildProcessSpawner,
ChildProcessSpawner.make(() => {
const answered = Effect.sleep(delay);
return Effect.succeed(
ChildProcessSpawner.makeHandle({
pid: ChildProcessSpawner.ProcessId(900_000_010),
exitCode: Effect.as(answered, ChildProcessSpawner.ExitCode(0)),
isRunning: Effect.succeed(false),
kill: () => Effect.void,
unref: Effect.succeed(Effect.void),
stdin: Sink.drain,
stdout: Stream.unwrap(
Effect.as(answered, Stream.succeed(new TextEncoder().encode("muse 1.4.12-R6909.1\n"))),
),
stderr: Stream.empty,
all: Stream.empty,
getInputFd: () => Sink.drain,
getOutputFd: () => Stream.empty,
}),
);
}),
);

it.layer(NodeServices.layer)("Muse status", (it) => {
it.effect("waits for a `muse --version` that answers after 5 s", () =>
Effect.gen(function* () {
const fiber = yield* checkMuseProviderStatus(
settings({ enabled: true, binaryPath: "muse" }),
undefined,
undefined,
async () => makeHost(metaCatalog),
).pipe(Effect.provide(slowVersionSpawner("5 seconds")), Effect.forkChild);
yield* TestClock.adjust("5 seconds");
const snapshot = yield* Fiber.join(fiber);
expect(snapshot.message).toBeUndefined();
expect(snapshot.status).toBe("ready");
expect(snapshot.version).toBe("1.4.12-R6909.1");
}),
);

it.effect("does not expose CLI stderr or start an SDK host after a failed version check", () =>
Effect.scoped(
Effect.gen(function* () {
Expand Down
5 changes: 3 additions & 2 deletions packages/provider-muse/src/server/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { discoverMuseModels, museModelCapabilities } from "./modelCatalog.ts";
import {
buildServerProvider,
COMPACT_SLASH_COMMAND,
DEFAULT_TIMEOUT_MS,
isCommandMissingCause,
parseCliVersionResult,
providerModelsFromSettings,
Expand All @@ -31,6 +30,8 @@ const MUSE_PRESENTATION = {
} as const;

const FALLBACK_CAPABILITIES = museModelCapabilities();
// Muse can take 4 to 9 s to answer `--version` when started inside the server's Windows job object.
const VERSION_PROBE_TIMEOUT_MS = 10_000;

export const makePendingMuseProvider = Effect.fn("makePendingMuseProvider")(function* (
settings: MuseSettings,
Expand Down Expand Up @@ -83,7 +84,7 @@ export const checkMuseProviderStatus = Effect.fn("checkMuseProviderStatus")(func
...(cwd ? { cwd } : {}),
}),
);
}).pipe(Effect.timeoutOption(DEFAULT_TIMEOUT_MS), Effect.result);
}).pipe(Effect.timeoutOption(VERSION_PROBE_TIMEOUT_MS), Effect.result);

if (Result.isFailure(versionResult)) {
const missing = isCommandMissingCause(versionResult.failure);
Expand Down
Loading