openclaw / src /llm /stream.complete-host.test.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
25d91a4 verified
Raw History Blame Contribute Delete
5.97 kB
import { createApiRegistry, createLlmRuntime, getAiTransportHost } from "@openclaw/ai";
import type {
AssistantMessage,
AssistantMessageEventStreamContract,
Context,
Model,
SimpleStreamOptions,
} from "@openclaw/llm-core";
import { describe, expect, it, vi } from "vitest";
import { createZeroUsageFixture } from "../agents/test-helpers/usage-fixtures.js";
import { attachModelProviderRuntimePluginHandle } from "../plugins/provider-hook-runtime.js";
import { bindModelLlmRuntime } from "./model-runtime-binding.js";
import { completeSimple } from "./stream.js";
import { createAssistantMessageEventStream } from "./utils/event-stream.js";
function createCompletionRuntime(
onDispatch?: (model: Model, context: Context, options?: SimpleStreamOptions) => void,
) {
const registry = createApiRegistry();
const runtime = createLlmRuntime(registry);
const model = {
api: "test-runtime-host-api",
provider: "test-runtime-host",
id: "test-runtime-host-model",
name: "Test Runtime Host Model",
baseUrl: "https://example.test",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 1024,
maxTokens: 512,
} satisfies Model;
const message = {
role: "assistant",
content: [{ type: "text", text: "configured" }],
api: model.api,
provider: model.provider,
model: model.id,
usage: createZeroUsageFixture(),
stopReason: "stop",
timestamp: Date.now(),
} satisfies AssistantMessage;
const providerStream = vi.fn(
(
runtimeModel: Model,
context: Context,
options?: SimpleStreamOptions,
): AssistantMessageEventStreamContract => {
onDispatch?.(runtimeModel, context, options);
const output = createAssistantMessageEventStream();
output.push({ type: "done", reason: "stop", message });
output.end();
return output;
},
);
registry.registerApiProvider({
api: model.api,
stream: providerStream,
streamSimple: providerStream,
});
return { model: bindModelLlmRuntime(model, runtime), runtime, message, providerStream };
}
describe("LLM completion transport host", () => {
it("installs runtime transport ports before direct preparation and bare completion", async () => {
const inertWrapper = getAiTransportHost().plugin.wrapSimpleCompletionStream;
const { model, runtime, message, providerStream } = createCompletionRuntime(
(_runtimeModel, context) => {
expect(getAiTransportHost().plugin.wrapSimpleCompletionStream).not.toBe(inertWrapper);
expect(context.messages).toEqual([]);
},
);
const directModel = bindModelLlmRuntime(
attachModelProviderRuntimePluginHandle(model, {
provider: model.provider,
plugin: {
id: model.provider,
label: "Completion fixture",
auth: [],
wrapSimpleCompletionStreamFn: ({ streamFn }) =>
streamFn &&
((target, context, options) =>
streamFn(target, context, {
...options,
headers: { ...options?.headers, "x-runtime-host": "prepared" },
})),
},
}),
runtime,
);
const { completeWithPreparedSimpleCompletionModel } =
await import("../agents/simple-completion-execution.js");
await expect(
completeWithPreparedSimpleCompletionModel({
model: directModel,
auth: { apiKey: "fixture-key", source: "test", mode: "api-key" },
context: { messages: [] },
}),
).resolves.toEqual(message);
await expect(completeSimple(model, { messages: [] })).resolves.toEqual(message);
expect(providerStream.mock.calls.map((call) => call[2]?.headers)).toEqual([
{ "x-runtime-host": "prepared" },
undefined,
]);
});
it.each(["current", "retired", "aborted"] as const)(
"checks %s host-prepared authority after deferred transport initialization",
async (authority) => {
const { runHostPreparedIsolatedCompletion } =
await import("../agents/host-prepared-isolated-completion.js");
const { model, message, providerStream } = createCompletionRuntime(
(_runtimeModel, context, options) => {
expect(context.messages).toEqual([
{ role: "user", content: "Title this chat.", timestamp: expect.any(Number) },
]);
expect(options).toMatchObject({ apiKey: "synthetic-completion-key" });
expect(options).not.toHaveProperty("assertCurrent");
},
);
const controller = new AbortController();
const authorityError = new Error("Completion owner retired.");
let current = true;
const completion = runHostPreparedIsolatedCompletion({
provider: model.provider,
modelId: model.id,
authorization: {
owner: "host",
model,
auth: { mode: "api-key", source: "test", apiKey: "synthetic-completion-key" },
},
config: {},
agentId: "main",
agentDir: "/test/agent",
workspaceDir: "/test/workspace",
systemPrompt: "Return a brief title.",
prompt: "Title this chat.",
timeoutMs: 10_000,
abortSignal: controller.signal,
assertCurrent: () => {
if (!current) {
throw authorityError;
}
},
});
// Even a warm transport host yields before it invokes the provider.
current = authority !== "retired";
if (authority === "aborted") {
controller.abort(authorityError);
}
if (authority === "current") {
await expect(completion).resolves.toEqual({ assistant: message });
expect(providerStream).toHaveBeenCalledOnce();
} else {
await expect(completion).rejects.toBe(authorityError);
expect(providerStream).not.toHaveBeenCalled();
}
expect(controller.signal.aborted).toBe(authority === "aborted");
},
);
});