122 lines
4.2 KiB
TypeScript
122 lines
4.2 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
||
import {
|
||
runAssistantLivingChatAttemptRuntime,
|
||
type RunAssistantLivingChatAttemptRuntimeInput
|
||
} from "../src/services/assistantLivingChatAttemptRuntimeAdapter";
|
||
|
||
type TestResponse = { ok: boolean; llm: string };
|
||
|
||
function buildInput(
|
||
overrides: Partial<RunAssistantLivingChatAttemptRuntimeInput<TestResponse>> = {}
|
||
): RunAssistantLivingChatAttemptRuntimeInput<TestResponse> {
|
||
return {
|
||
sessionId: "asst-1",
|
||
userMessage: "контрагенты со сверкой",
|
||
sessionItems: [{ role: "user", text: "контекст" }],
|
||
modeDecision: { mode: "chat", reason: "living_chat_signal_detected" },
|
||
sessionScope: {
|
||
knownOrganizations: [],
|
||
selectedOrganization: null,
|
||
activeOrganization: null
|
||
},
|
||
addressRuntimeMeta: null,
|
||
traceIdFactory: () => "chat-trace-1",
|
||
toNonEmptyString: (value: unknown) => (typeof value === "string" && value.trim() ? value.trim() : null),
|
||
mergeKnownOrganizations: (values: string[]) => values,
|
||
hasAssistantDataScopeMetaQuestionSignal: () => false,
|
||
shouldHandleAsAssistantCapabilityMetaQuery: () => false,
|
||
hasDestructiveDataActionSignal: () => false,
|
||
hasDangerOrCoercionSignal: () => false,
|
||
hasOperationalAdminActionRequestSignal: () => false,
|
||
hasOrganizationFactLookupSignal: () => false,
|
||
hasOrganizationFactFollowupSignal: () => false,
|
||
shouldEmitOrganizationSelectionReply: () => false,
|
||
hasAssistantCapabilityQuestionSignal: () => false,
|
||
resolveDataScopeProbe: async () => null,
|
||
applyScriptGuard: (text: string) => ({ text, applied: false, reason: null }),
|
||
applyGroundingGuard: (payload: { chatText: string }) => ({
|
||
text: payload.chatText,
|
||
applied: false,
|
||
reason: null
|
||
}),
|
||
buildAssistantSafetyRefusalReply: () => "safety",
|
||
buildAssistantDataScopeContractReply: () => "scope",
|
||
buildAssistantOrganizationFactBoundaryReply: () => "boundary",
|
||
buildAssistantDataScopeSelectionReply: () => "selection",
|
||
buildAssistantOperationalBoundaryReply: () => "operational",
|
||
buildAssistantCapabilityContractReply: () => "capability",
|
||
appendItem: () => {},
|
||
getSession: () => ({
|
||
session_id: "asst-1",
|
||
updated_at: "",
|
||
items: [],
|
||
investigation_state: null
|
||
}),
|
||
persistSession: () => {},
|
||
cloneConversation: (items: unknown[]) => items,
|
||
logEvent: () => {},
|
||
messageIdFactory: () => "msg-1",
|
||
nowIso: () => "2026-04-10T00:00:00.000Z",
|
||
payload: {
|
||
llmProvider: "openai",
|
||
apiKey: "key",
|
||
model: "gpt-5",
|
||
baseUrl: "http://localhost",
|
||
temperature: 0.2,
|
||
maxOutputTokens: 300
|
||
},
|
||
chatClient: {
|
||
chat: async () => ({ outputText: "chat-output" })
|
||
},
|
||
loadAssistantCanonExcerpt: () => "canon",
|
||
sanitizeOutgoingAssistantText: (value: unknown, fallback = "fallback") => {
|
||
const text = String(value ?? "").trim();
|
||
return text || fallback;
|
||
},
|
||
defaultModel: "gpt-5",
|
||
defaultBaseUrl: "http://localhost",
|
||
defaultApiKey: "key",
|
||
...overrides
|
||
};
|
||
}
|
||
|
||
describe("assistant living chat attempt runtime adapter", () => {
|
||
it("wires llm runtime output into living handler execute callback", async () => {
|
||
const runLivingChatLlm = vi.fn(async () => "llm-answer");
|
||
const runLivingChatHandler = vi.fn(async (input) => {
|
||
const llm = await input.executeLlmChat();
|
||
return { ok: true, llm };
|
||
});
|
||
|
||
const response = await runAssistantLivingChatAttemptRuntime(
|
||
buildInput({
|
||
runLivingChatHandler,
|
||
runLivingChatLlm
|
||
})
|
||
);
|
||
|
||
expect(runLivingChatHandler).toHaveBeenCalledTimes(1);
|
||
expect(runLivingChatLlm).toHaveBeenCalledWith(
|
||
expect.objectContaining({
|
||
userMessage: "контрагенты со сверкой"
|
||
})
|
||
);
|
||
expect(response).toEqual({ ok: true, llm: "llm-answer" });
|
||
});
|
||
|
||
it("returns null when delegated handler returns null", async () => {
|
||
const runLivingChatLlm = vi.fn(async () => "llm-answer");
|
||
const runLivingChatHandler = vi.fn(async () => null);
|
||
|
||
const response = await runAssistantLivingChatAttemptRuntime(
|
||
buildInput({
|
||
runLivingChatHandler,
|
||
runLivingChatLlm
|
||
})
|
||
);
|
||
|
||
expect(response).toBeNull();
|
||
expect(runLivingChatLlm).not.toHaveBeenCalled();
|
||
});
|
||
});
|