@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
JavaScript
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