ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов 2.41 - вынос склейки вызова live-chat из handleMessage в отдельный runtime-адаптер и переподключение на новый runtime bridge
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user