UNPKG

@copilotkit/runtime-client-gql

Version:

<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />

455 lines (454 loc) • 16.4 kB
import { aguiToGQL } from "../chunk-SCACOKQX.mjs"; import { gqlToAGUI } from "../chunk-WXA3UP7I.mjs"; import { describe, globalExpect, test, vi } from "../chunk-2R7M2FWR.mjs"; import "../chunk-7ECCT6PK.mjs"; import "../chunk-FXYV7IUL.mjs"; import "../chunk-X2UAP3QY.mjs"; import "../chunk-HEODM5TW.mjs"; import "../chunk-4KTMZMM2.mjs"; import "../chunk-P2AUSQOK.mjs"; import { ActionExecutionMessage, AgentStateMessage, ImageMessage, ResultMessage, Role, TextMessage } from "../chunk-ROUIRR4B.mjs"; import "../chunk-WM3ARNBD.mjs"; import "../chunk-DELDZXUX.mjs"; // src/message-conversion/roundtrip-conversion.test.ts function stripFunctions(obj) { if (typeof obj === "function") return void 0; if (Array.isArray(obj)) return obj.map(stripFunctions); if (obj && typeof obj === "object") { const out = {}; for (const k in obj) { if (typeof obj[k] !== "function") { out[k] = stripFunctions(obj[k]); } } return out; } return obj; } describe("roundtrip message conversion", () => { test("text message AGUI -> GQL -> AGUI", () => { const aguiMsg = { id: "user-1", role: "user", content: "Hello!" }; const gqlMsgs = aguiToGQL(aguiMsg); const aguiMsgs2 = gqlToAGUI(gqlMsgs); globalExpect(stripFunctions(aguiMsgs2[0])).toEqual(stripFunctions(aguiMsg)); }); test("text message GQL -> AGUI -> GQL", () => { const gqlMsg = new TextMessage({ id: "assistant-1", content: "Hi!", role: Role.Assistant }); const aguiMsgs = gqlToAGUI(gqlMsg); const gqlMsgs2 = aguiToGQL(aguiMsgs); globalExpect(gqlMsgs2[0].id).toBe(gqlMsg.id); globalExpect(gqlMsgs2[0].content).toBe(gqlMsg.content); globalExpect(gqlMsgs2[0].role).toBe(gqlMsg.role); }); test("tool message AGUI -> GQL -> AGUI", () => { const aguiMsg = { id: "tool-1", role: "tool", content: "Tool result", toolCallId: "tool-call-1", toolName: "testAction" }; const gqlMsgs = aguiToGQL(aguiMsg); const aguiMsgs2 = gqlToAGUI(gqlMsgs); globalExpect(stripFunctions(aguiMsgs2[0])).toEqual(stripFunctions(aguiMsg)); }); test("tool message GQL -> AGUI -> GQL", () => { const gqlMsg = new ResultMessage({ id: "tool-1", result: "Tool result", actionExecutionId: "tool-call-1", actionName: "testAction" }); const aguiMsgs = gqlToAGUI(gqlMsg); const gqlMsgs2 = aguiToGQL(aguiMsgs); globalExpect(gqlMsgs2[0].id).toBe(gqlMsg.id); globalExpect(gqlMsgs2[0].result).toBe(gqlMsg.result); globalExpect(gqlMsgs2[0].actionExecutionId).toBe(gqlMsg.actionExecutionId); }); test("action execution AGUI -> GQL -> AGUI", () => { const aguiMsg = { id: "assistant-1", role: "assistant", content: "Running action", toolCalls: [ { id: "tool-call-1", type: "function", function: { name: "doSomething", arguments: JSON.stringify({ foo: "bar" }) } } ] }; const gqlMsgs = aguiToGQL(aguiMsg); const aguiMsgs2 = gqlToAGUI(gqlMsgs); globalExpect(aguiMsgs2[0].role).toBe("assistant"); globalExpect(aguiMsgs2[1].role).toBe("assistant"); if ("toolCalls" in aguiMsgs2[1]) { globalExpect(aguiMsgs2[1].toolCalls[0].function.name).toBe("doSomething"); } }); test("action execution GQL -> AGUI -> GQL", () => { const actionExecMsg = new ActionExecutionMessage({ id: "tool-call-1", name: "doSomething", arguments: { foo: "bar" }, parentMessageId: "assistant-1" }); const aguiMsgs = gqlToAGUI([actionExecMsg]); const gqlMsgs2 = aguiToGQL(aguiMsgs); globalExpect(gqlMsgs2[1].id).toBe("tool-call-1"); globalExpect(gqlMsgs2[1].name).toBe("doSomething"); globalExpect(gqlMsgs2[1].arguments).toEqual({ foo: "bar" }); }); test("agent state GQL -> AGUI -> GQL", () => { const agentStateMsg = new AgentStateMessage({ id: "agent-state-1", agentName: "testAgent", state: { status: "running" }, role: Role.Assistant }); const aguiMsgs = gqlToAGUI([agentStateMsg]); const gqlMsgs2 = aguiToGQL(aguiMsgs); globalExpect(gqlMsgs2[0].id).toBe("agent-state-1"); globalExpect(gqlMsgs2[0].agentName).toBe("testAgent"); }); test("action execution with render function roundtrip", () => { const mockRender = vi.fn(); const aguiMsg = { id: "assistant-1", role: "assistant", content: "Running action", toolCalls: [ { id: "tool-call-1", type: "function", function: { name: "doSomething", arguments: JSON.stringify({ foo: "bar" }) } } ], generativeUI: mockRender }; const actions = { doSomething: { name: "doSomething" } }; const gqlMsgs = aguiToGQL(aguiMsg, actions); const aguiMsgs2 = gqlToAGUI(gqlMsgs, actions); globalExpect(typeof actions.doSomething.render).toBe("function"); if ("toolCalls" in aguiMsgs2[1]) { globalExpect(aguiMsgs2[1].toolCalls[0].function.name).toBe("doSomething"); } }); test("image message GQL -> AGUI -> GQL", () => { const gqlMsg = new ImageMessage({ id: "img-1", format: "jpeg", bytes: "somebase64string", role: Role.User }); const aguiMsgs = gqlToAGUI(gqlMsg); const gqlMsgs2 = aguiToGQL(aguiMsgs); globalExpect(gqlMsgs2[0].id).toBe(gqlMsg.id); globalExpect(gqlMsgs2[0].format).toBe(gqlMsg.format); globalExpect(gqlMsgs2[0].bytes).toBe(gqlMsg.bytes); globalExpect(gqlMsgs2[0].role).toBe(gqlMsg.role); }); test("image message AGUI -> GQL -> AGUI (assistant and user)", () => { const aguiAssistantImageMsg = { id: "img-assistant-1", role: "assistant", image: { format: "jpeg", bytes: "assistantbase64data" }, content: "" // required for type }; const gqlAssistantMsgs = aguiToGQL(aguiAssistantImageMsg); const aguiAssistantMsgs2 = gqlToAGUI(gqlAssistantMsgs); globalExpect(aguiAssistantMsgs2[0].id).toBe(aguiAssistantImageMsg.id); globalExpect(aguiAssistantMsgs2[0].role).toBe("assistant"); globalExpect(aguiAssistantMsgs2[0].image.format).toBe("jpeg"); globalExpect(aguiAssistantMsgs2[0].image.bytes).toBe("assistantbase64data"); const aguiUserImageMsg = { id: "img-user-1", role: "user", image: { format: "png", bytes: "userbase64data" }, content: "" // required for type }; const gqlUserMsgs = aguiToGQL(aguiUserImageMsg); const aguiUserMsgs2 = gqlToAGUI(gqlUserMsgs); globalExpect(aguiUserMsgs2[0].id).toBe(aguiUserImageMsg.id); globalExpect(aguiUserMsgs2[0].role).toBe("user"); globalExpect(aguiUserMsgs2[0].image.format).toBe("png"); globalExpect(aguiUserMsgs2[0].image.bytes).toBe("userbase64data"); }); test("wild card action roundtrip conversion", () => { const mockRender = vi.fn((props) => `Wildcard rendered: ${props.args.test}`); const aguiMsg = { id: "assistant-wildcard-1", role: "assistant", content: "Running wild card action", toolCalls: [ { id: "tool-call-wildcard-1", type: "function", function: { name: "unknownAction", arguments: JSON.stringify({ test: "wildcard-value" }) } } ], generativeUI: mockRender }; const actions = { "*": { name: "*" } }; const gqlMsgs = aguiToGQL(aguiMsg, actions); const aguiMsgs2 = gqlToAGUI(gqlMsgs, actions); globalExpect(typeof actions["*"].render).toBe("function"); globalExpect(actions["*"].render).toBe(mockRender); globalExpect(aguiMsgs2).toHaveLength(2); globalExpect(aguiMsgs2[0].role).toBe("assistant"); globalExpect(aguiMsgs2[1].role).toBe("assistant"); if ("toolCalls" in aguiMsgs2[1]) { globalExpect(aguiMsgs2[1].toolCalls[0].function.name).toBe("unknownAction"); globalExpect(aguiMsgs2[1].toolCalls[0].function.arguments).toBe( '{"test":"wildcard-value"}' ); } }); test("wild card action with specific action priority roundtrip", () => { const mockRender = vi.fn((props) => `Specific action rendered: ${props.args.test}`); const aguiMsg = { id: "assistant-priority-1", role: "assistant", content: "Running specific action", toolCalls: [ { id: "tool-call-priority-1", type: "function", function: { name: "specificAction", arguments: JSON.stringify({ test: "specific-value" }) } } ], generativeUI: mockRender }; const actions = { specificAction: { name: "specificAction" }, "*": { name: "*" } }; const gqlMsgs = aguiToGQL(aguiMsg, actions); const aguiMsgs2 = gqlToAGUI(gqlMsgs, actions); globalExpect(typeof actions.specificAction.render).toBe("function"); globalExpect(actions.specificAction.render).toBe(mockRender); globalExpect(actions["*"].render).toBeUndefined(); globalExpect(aguiMsgs2).toHaveLength(2); globalExpect(aguiMsgs2[0].role).toBe("assistant"); globalExpect(aguiMsgs2[1].role).toBe("assistant"); if ("toolCalls" in aguiMsgs2[1]) { globalExpect(aguiMsgs2[1].toolCalls[0].function.name).toBe("specificAction"); globalExpect(aguiMsgs2[1].toolCalls[0].function.arguments).toBe( '{"test":"specific-value"}' ); } }); test("wild card action GQL -> AGUI -> GQL roundtrip", () => { const actionExecMsg = new ActionExecutionMessage({ id: "wildcard-action-1", name: "unknownAction", arguments: { test: "wildcard-gql-value" }, parentMessageId: "assistant-1" }); const actions = { "*": { name: "*", render: vi.fn((props) => `GQL wildcard rendered: ${props.args.test}`) } }; const aguiMsgs = gqlToAGUI([actionExecMsg], actions); const gqlMsgs2 = aguiToGQL(aguiMsgs, actions); globalExpect(gqlMsgs2).toHaveLength(2); globalExpect(gqlMsgs2[0].id).toBe("wildcard-action-1"); globalExpect(gqlMsgs2[0].role).toBe(Role.Assistant); globalExpect(gqlMsgs2[1].id).toBe("wildcard-action-1"); globalExpect(gqlMsgs2[1].name).toBe("unknownAction"); globalExpect(gqlMsgs2[1].arguments).toEqual({ test: "wildcard-gql-value" }); }); test("roundtrip conversion with result parsing edge cases", () => { const toolResultMsg = { id: "tool-result-json", role: "tool", content: '{"status": "success", "data": {"value": 42}}', toolCallId: "tool-call-json", toolName: "jsonAction" }; const gqlMsgs = aguiToGQL(toolResultMsg); const aguiMsgs = gqlToAGUI(gqlMsgs); globalExpect(gqlMsgs).toHaveLength(1); globalExpect(gqlMsgs[0]).toBeInstanceOf(ResultMessage); globalExpect(gqlMsgs[0].result).toBe('{"status": "success", "data": {"value": 42}}'); globalExpect(aguiMsgs).toHaveLength(1); globalExpect(aguiMsgs[0].role).toBe("tool"); globalExpect(aguiMsgs[0].content).toBe('{"status": "success", "data": {"value": 42}}'); }); test("roundtrip conversion with object content in tool results", () => { const toolResultMsg = { id: "tool-result-object", role: "tool", content: { status: "success", data: { value: 42 } }, toolCallId: "tool-call-object", toolName: "objectAction" }; const gqlMsgs = aguiToGQL(toolResultMsg); const aguiMsgs = gqlToAGUI(gqlMsgs); globalExpect(gqlMsgs).toHaveLength(1); globalExpect(gqlMsgs[0]).toBeInstanceOf(ResultMessage); globalExpect(gqlMsgs[0].result).toBe('{"status":"success","data":{"value":42}}'); globalExpect(aguiMsgs).toHaveLength(1); globalExpect(aguiMsgs[0].role).toBe("tool"); globalExpect(aguiMsgs[0].content).toBe('{"status":"success","data":{"value":42}}'); }); test("roundtrip conversion with action execution and result parsing", () => { const mockRender = vi.fn((props) => `Rendered: ${JSON.stringify(props.result)}`); const actionExecMsg = new ActionExecutionMessage({ id: "action-with-result", name: "testAction", arguments: { input: "test-value" }, parentMessageId: "parent-result" }); const resultMsg = new ResultMessage({ id: "result-with-json", result: '{"output": "processed", "count": 5}', actionExecutionId: "action-with-result", actionName: "testAction" }); const actions = { testAction: { name: "testAction", render: mockRender } }; const aguiMsgs = gqlToAGUI([actionExecMsg, resultMsg], actions); globalExpect(aguiMsgs).toHaveLength(2); globalExpect(aguiMsgs[0].role).toBe("assistant"); globalExpect("generativeUI" in aguiMsgs[0]).toBe(true); globalExpect(aguiMsgs[1].role).toBe("tool"); globalExpect(aguiMsgs[1].content).toBe('{"output": "processed", "count": 5}'); if ("generativeUI" in aguiMsgs[0] && aguiMsgs[0].generativeUI) { aguiMsgs[0].generativeUI({ result: '{"parsed": true}' }); globalExpect(mockRender).toHaveBeenCalledWith( globalExpect.objectContaining({ result: { parsed: true } // Should be parsed from string }) ); } const gqlMsgs2 = aguiToGQL(aguiMsgs, actions); globalExpect(gqlMsgs2).toHaveLength(3); globalExpect(gqlMsgs2[0]).toBeInstanceOf(TextMessage); globalExpect(gqlMsgs2[1]).toBeInstanceOf(ActionExecutionMessage); globalExpect(gqlMsgs2[2]).toBeInstanceOf(ResultMessage); globalExpect(gqlMsgs2[1].arguments).toEqual({ input: "test-value" }); globalExpect(gqlMsgs2[2].result).toBe('{"output": "processed", "count": 5}'); }); test("roundtrip conversion verifies correct property distribution for regular actions", () => { const mockRender = vi.fn((props) => `Regular action: ${JSON.stringify(props.args)}`); const actionExecMsg = new ActionExecutionMessage({ id: "regular-action-test", name: "regularAction", arguments: { test: "regular-value" }, parentMessageId: "parent-regular" }); const actions = { regularAction: { name: "regularAction", render: mockRender } }; const aguiMsgs = gqlToAGUI([actionExecMsg], actions); const gqlMsgs2 = aguiToGQL(aguiMsgs, actions); globalExpect(gqlMsgs2).toHaveLength(2); globalExpect(gqlMsgs2[1]).toBeInstanceOf(ActionExecutionMessage); globalExpect(gqlMsgs2[1].name).toBe("regularAction"); if ("generativeUI" in aguiMsgs[0] && aguiMsgs[0].generativeUI) { aguiMsgs[0].generativeUI(); globalExpect(mockRender).toHaveBeenCalledWith( globalExpect.objectContaining({ args: { test: "regular-value" } // name property should NOT be present for regular actions }) ); const callArgs = mockRender.mock.calls[0][0]; globalExpect(callArgs).not.toHaveProperty("name"); } }); test("roundtrip conversion verifies correct property distribution for wildcard actions", () => { const mockRender = vi.fn( (props) => `Wildcard action: ${props.name} with ${JSON.stringify(props.args)}` ); const actionExecMsg = new ActionExecutionMessage({ id: "wildcard-action-test", name: "unknownAction", arguments: { test: "wildcard-value" }, parentMessageId: "parent-wildcard" }); const actions = { "*": { name: "*", render: mockRender } }; const aguiMsgs = gqlToAGUI([actionExecMsg], actions); const gqlMsgs2 = aguiToGQL(aguiMsgs, actions); globalExpect(gqlMsgs2).toHaveLength(2); globalExpect(gqlMsgs2[1]).toBeInstanceOf(ActionExecutionMessage); globalExpect(gqlMsgs2[1].name).toBe("unknownAction"); if ("generativeUI" in aguiMsgs[0] && aguiMsgs[0].generativeUI) { aguiMsgs[0].generativeUI(); globalExpect(mockRender).toHaveBeenCalledWith( globalExpect.objectContaining({ args: { test: "wildcard-value" }, name: "unknownAction" // name property SHOULD be present for wildcard actions }) ); const callArgs = mockRender.mock.calls[0][0]; globalExpect(callArgs).toHaveProperty("name", "unknownAction"); } }); }); //# sourceMappingURL=roundtrip-conversion.test.mjs.map