Route simple chat through an optional local OpenAI-compatible reception model so threads.send does not always enqueue a full sandbox-backed run. Clear work prompts still escalate to the bot's configured work model. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
155 lines
4.6 KiB
TypeScript
155 lines
4.6 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import {
|
|
heuristicReceptionRoute,
|
|
isReceptionConfigured,
|
|
parseReceptionResponse,
|
|
receptionEnabled,
|
|
resolveReceptionChecker,
|
|
runReceptionPlan,
|
|
shouldSkipReception,
|
|
} from "./reception.js";
|
|
|
|
describe("reception env", () => {
|
|
it("defaults off", () => {
|
|
expect(receptionEnabled({})).toBe(false);
|
|
expect(isReceptionConfigured({})).toBe(false);
|
|
expect(receptionEnabled({ RAKAZO_RECEPTION_ENABLED: "1" })).toBe(true);
|
|
});
|
|
|
|
it("requires model and url when enabled", () => {
|
|
expect(
|
|
resolveReceptionChecker({
|
|
RAKAZO_RECEPTION_ENABLED: "1",
|
|
RAKAZO_RECEPTION_MODEL: "qwen2.5-coder-3b",
|
|
RAKAZO_RECEPTION_MODELS_URL: "http://127.0.0.1:1239/v1",
|
|
}),
|
|
).toEqual({
|
|
model: "qwen2.5-coder-3b",
|
|
baseUrl: "http://127.0.0.1:1239/v1",
|
|
});
|
|
expect(
|
|
resolveReceptionChecker({
|
|
RAKAZO_RECEPTION_ENABLED: "1",
|
|
RAKAZO_RECEPTION_MODEL: "qwen2.5-coder-3b",
|
|
}),
|
|
).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("shouldSkipReception", () => {
|
|
it("skips attachments and empty text", () => {
|
|
expect(shouldSkipReception({ hasAttachments: true, text: "Hey" })).toBe(true);
|
|
expect(shouldSkipReception({ hasAttachments: false, text: " " })).toBe(true);
|
|
expect(shouldSkipReception({ hasAttachments: false, text: "Hey" })).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("heuristicReceptionRoute", () => {
|
|
it("routes obvious greetings to chat", () => {
|
|
expect(heuristicReceptionRoute("Hey")).toBe("chat");
|
|
expect(heuristicReceptionRoute("Hallo!")).toBe("chat");
|
|
});
|
|
|
|
it("routes clear work orders to work", () => {
|
|
expect(
|
|
heuristicReceptionRoute("Split executor.ts into modules and open a PR"),
|
|
).toBe("work");
|
|
expect(heuristicReceptionRoute("Refactor the login flow and add tests")).toBe("work");
|
|
});
|
|
|
|
it("returns null for ambiguous messages", () => {
|
|
expect(heuristicReceptionRoute("Can you help with something?")).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("parseReceptionResponse", () => {
|
|
it("parses chat, clarify, and work routes", () => {
|
|
expect(parseReceptionResponse('{"route":"chat","reply":"Hello!"}')).toEqual({
|
|
route: "chat",
|
|
reply: "Hello!",
|
|
});
|
|
expect(parseReceptionResponse('{"route":"clarify","reply":"What should I change?"}')).toEqual({
|
|
route: "clarify",
|
|
reply: "What should I change?",
|
|
});
|
|
expect(parseReceptionResponse('{"route":"work"}')).toEqual({ route: "work" });
|
|
});
|
|
|
|
it("returns null for invalid payloads", () => {
|
|
expect(parseReceptionResponse("not json")).toBeNull();
|
|
expect(parseReceptionResponse('{"route":"chat"}')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("runReceptionPlan", () => {
|
|
it("escalates clear work without calling the runtime", async () => {
|
|
const runtime = { run: vi.fn() };
|
|
const plan = await runReceptionPlan(
|
|
{
|
|
runtime: runtime as never,
|
|
checker: { model: "fast", baseUrl: "http://127.0.0.1:1239/v1" },
|
|
},
|
|
{
|
|
botId: "bot-1",
|
|
botName: "Coder",
|
|
spaceId: "space-1",
|
|
userId: "user-1",
|
|
threadId: "thread-1",
|
|
text: "Split executor.ts into modules and open a PR",
|
|
hasAttachments: false,
|
|
history: [],
|
|
},
|
|
);
|
|
expect(plan).toEqual({ route: "work" });
|
|
expect(runtime.run).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("returns null when disabled inputs skip reception", async () => {
|
|
const runtime = { run: vi.fn() };
|
|
await expect(
|
|
runReceptionPlan(
|
|
{
|
|
runtime: runtime as never,
|
|
checker: { model: "fast", baseUrl: "http://127.0.0.1:1239/v1" },
|
|
},
|
|
{
|
|
botId: "bot-1",
|
|
botName: "Coder",
|
|
spaceId: "space-1",
|
|
userId: "user-1",
|
|
threadId: "thread-1",
|
|
text: "Hey",
|
|
hasAttachments: true,
|
|
history: [],
|
|
},
|
|
),
|
|
).resolves.toBeNull();
|
|
expect(runtime.run).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("uses runtime for ambiguous messages", async () => {
|
|
async function* run() {
|
|
yield { type: "done", text: '{"route":"chat","reply":"Sure — hi!"}' } as const;
|
|
}
|
|
const runtime = { run: vi.fn(() => run()) };
|
|
const plan = await runReceptionPlan(
|
|
{
|
|
runtime: runtime as never,
|
|
checker: { model: "fast", baseUrl: "http://127.0.0.1:1239/v1" },
|
|
},
|
|
{
|
|
botId: "bot-1",
|
|
botName: "Coder",
|
|
spaceId: "space-1",
|
|
userId: "user-1",
|
|
threadId: "thread-1",
|
|
text: "Can you help with something?",
|
|
hasAttachments: false,
|
|
history: [],
|
|
},
|
|
);
|
|
expect(plan).toEqual({ route: "chat", reply: "Sure — hi!" });
|
|
expect(runtime.run).toHaveBeenCalledOnce();
|
|
});
|
|
});
|