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;" />

1 lines • 74.8 kB
{"version":3,"sources":["../../src/message-conversion/gql-to-agui.test.ts"],"sourcesContent":["import { describe, test, expect, vi } from \"vitest\";\nimport * as gql from \"../client\";\nimport { MessageStatusCode } from \"../graphql/@generated/graphql\";\nimport {\n gqlToAGUI,\n gqlTextMessageToAGUIMessage,\n gqlResultMessageToAGUIMessage,\n gqlImageMessageToAGUIMessage,\n gqlActionExecutionMessageToAGUIMessage,\n} from \"./gql-to-agui\";\nimport { AIMessage } from \"@copilotkit/shared\";\n\ndescribe(\"message-conversion\", () => {\n describe(\"gqlTextMessageToAGUIMessage\", () => {\n test(\"should convert developer message\", () => {\n const gqlMessage = new gql.TextMessage({\n id: \"dev-message-id\",\n content: \"Hello from developer\",\n role: gql.Role.Developer,\n });\n\n const result = gqlTextMessageToAGUIMessage(gqlMessage);\n\n expect(result).toEqual({\n id: \"dev-message-id\",\n role: \"developer\",\n content: \"Hello from developer\",\n });\n });\n\n test(\"should convert system message\", () => {\n const gqlMessage = new gql.TextMessage({\n id: \"system-message-id\",\n content: \"System instruction\",\n role: gql.Role.System,\n });\n\n const result = gqlTextMessageToAGUIMessage(gqlMessage);\n\n expect(result).toEqual({\n id: \"system-message-id\",\n role: \"system\",\n content: \"System instruction\",\n });\n });\n\n test(\"should convert assistant message\", () => {\n const gqlMessage = new gql.TextMessage({\n id: \"assistant-message-id\",\n content: \"Assistant response\",\n role: gql.Role.Assistant,\n });\n\n const result = gqlTextMessageToAGUIMessage(gqlMessage);\n\n expect(result).toEqual({\n id: \"assistant-message-id\",\n role: \"assistant\",\n content: \"Assistant response\",\n });\n });\n\n test(\"should throw error for unknown role\", () => {\n const gqlMessage = new gql.TextMessage({\n id: \"unknown-message-id\",\n content: \"Unknown message\",\n role: \"unknown\" as any,\n });\n\n expect(() => gqlTextMessageToAGUIMessage(gqlMessage)).toThrow(\"Unknown message role\");\n });\n });\n\n describe(\"gqlResultMessageToAGUIMessage\", () => {\n test(\"should convert result message to tool message\", () => {\n const gqlMessage = new gql.ResultMessage({\n id: \"result-id\",\n result: \"Function result data\",\n actionExecutionId: \"action-exec-123\",\n actionName: \"testAction\",\n });\n\n const result = gqlResultMessageToAGUIMessage(gqlMessage);\n\n expect(result).toEqual({\n id: \"result-id\",\n role: \"tool\",\n content: \"Function result data\",\n toolCallId: \"action-exec-123\",\n toolName: \"testAction\",\n });\n });\n });\n\n describe(\"gqlToAGUI\", () => {\n test(\"should convert an array of text messages\", () => {\n const gqlMessages = [\n new gql.TextMessage({\n id: \"dev-1\",\n content: \"Hello\",\n role: gql.Role.Developer,\n }),\n new gql.TextMessage({\n id: \"assistant-1\",\n content: \"Hi there\",\n role: gql.Role.Assistant,\n }),\n ];\n\n const result = gqlToAGUI(gqlMessages);\n\n expect(result).toHaveLength(2);\n expect(result[0]).toEqual({\n id: \"dev-1\",\n role: \"developer\",\n content: \"Hello\",\n });\n expect(result[1]).toEqual({\n id: \"assistant-1\",\n role: \"assistant\",\n content: \"Hi there\",\n });\n });\n\n test(\"should handle agent state messages\", () => {\n const gqlMessages = [new gql.AgentStateMessage({ id: \"agent-state-1\" })];\n\n const result = gqlToAGUI(gqlMessages);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual({\n id: \"agent-state-1\",\n role: \"assistant\",\n });\n });\n\n // test(\"should throw error for unknown message type\", () => {\n // // Create a message with unknown type\n // const unknownMessage = new gql.Message({ id: \"unknown-1\" });\n // // Override the type checking methods to simulate unknown type\n // unknownMessage.isTextMessage = () => false as any;\n // unknownMessage.isResultMessage = () => false as any;\n // unknownMessage.isActionExecutionMessage = () => false as any;\n // unknownMessage.isAgentStateMessage = () => false as any;\n // unknownMessage.isImageMessage = () => false as any;\n\n // expect(() => gqlToAGUI([unknownMessage])).toThrow(\"Unknown message type\");\n // });\n\n test(\"should handle a mix of message types\", () => {\n const gqlMessages = [\n new gql.TextMessage({\n id: \"dev-1\",\n content: \"Run action\",\n role: gql.Role.Developer,\n }),\n new gql.TextMessage({\n id: \"assistant-1\",\n content: \"I'll run the action\",\n role: gql.Role.Assistant,\n }),\n new gql.ResultMessage({\n id: \"result-1\",\n result: \"Action result\",\n actionExecutionId: \"action-exec-1\",\n actionName: \"testAction\",\n }),\n ];\n\n const result = gqlToAGUI(gqlMessages);\n\n expect(result).toHaveLength(3);\n expect(result[0]).toEqual({\n id: \"dev-1\",\n role: \"developer\",\n content: \"Run action\",\n });\n expect(result[1]).toEqual({\n id: \"assistant-1\",\n role: \"assistant\",\n content: \"I'll run the action\",\n });\n expect(result[2]).toEqual({\n id: \"result-1\",\n role: \"tool\",\n content: \"Action result\",\n toolCallId: \"action-exec-1\",\n toolName: \"testAction\",\n });\n });\n\n test(\"should handle action execution messages with parent messages\", () => {\n const assistantMsg = new gql.TextMessage({\n id: \"assistant-1\",\n content: \"I'll execute an action\",\n role: gql.Role.Assistant,\n });\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-1\",\n name: \"testAction\",\n arguments: { param: \"value\" },\n parentMessageId: \"assistant-1\",\n });\n\n const result = gqlToAGUI([assistantMsg, actionExecMsg]);\n\n // Now we expect 2 messages: the original assistant message and the action execution message\n expect(result).toHaveLength(2);\n expect(result[0]).toEqual({\n id: \"assistant-1\",\n role: \"assistant\",\n content: \"I'll execute an action\",\n });\n expect(result[1]).toEqual({\n id: \"action-1\",\n role: \"assistant\",\n name: \"testAction\",\n toolCalls: [\n {\n id: \"action-1\",\n function: {\n name: \"testAction\",\n arguments: JSON.stringify({ param: \"value\" }),\n },\n type: \"function\",\n },\n ],\n });\n });\n\n test(\"should handle multiple action execution messages for the same parent\", () => {\n const assistantMsg = new gql.TextMessage({\n id: \"assistant-1\",\n content: \"I'll execute multiple actions\",\n role: gql.Role.Assistant,\n });\n\n const action1 = new gql.ActionExecutionMessage({\n id: \"action-1\",\n name: \"firstAction\",\n arguments: { param: \"value1\" },\n parentMessageId: \"assistant-1\",\n });\n\n const action2 = new gql.ActionExecutionMessage({\n id: \"action-2\",\n name: \"secondAction\",\n arguments: { param: \"value2\" },\n parentMessageId: \"assistant-1\",\n });\n\n const result = gqlToAGUI([assistantMsg, action1, action2]);\n\n // Now we expect 3 messages: the original assistant message and 2 separate action execution messages\n expect(result).toHaveLength(3);\n expect(result[0]).toEqual({\n id: \"assistant-1\",\n role: \"assistant\",\n content: \"I'll execute multiple actions\",\n });\n expect(result[1]).toEqual({\n id: \"action-1\",\n role: \"assistant\",\n name: \"firstAction\",\n toolCalls: [\n {\n id: \"action-1\",\n function: {\n name: \"firstAction\",\n arguments: JSON.stringify({ param: \"value1\" }),\n },\n type: \"function\",\n },\n ],\n });\n expect(result[2]).toEqual({\n id: \"action-2\",\n role: \"assistant\",\n name: \"secondAction\",\n toolCalls: [\n {\n id: \"action-2\",\n function: {\n name: \"secondAction\",\n arguments: JSON.stringify({ param: \"value2\" }),\n },\n type: \"function\",\n },\n ],\n });\n });\n\n test(\"should not add toolCalls to non-assistant messages\", () => {\n const developerMsg = new gql.TextMessage({\n id: \"dev-1\",\n content: \"Developer message\",\n role: gql.Role.Developer,\n });\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-1\",\n name: \"testAction\",\n arguments: { param: \"value\" },\n parentMessageId: \"dev-1\", // This should be ignored since parent is not assistant\n });\n\n const result = gqlToAGUI([developerMsg, actionExecMsg]);\n\n // Now we expect 2 messages: the developer message and the action execution as assistant message\n expect(result).toHaveLength(2);\n expect(result[0]).toEqual({\n id: \"dev-1\",\n role: \"developer\",\n content: \"Developer message\",\n });\n // The action execution becomes its own assistant message regardless of parent\n expect(result[1]).toEqual({\n id: \"action-1\",\n role: \"assistant\",\n name: \"testAction\",\n toolCalls: [\n {\n id: \"action-1\",\n function: {\n name: \"testAction\",\n arguments: JSON.stringify({ param: \"value\" }),\n },\n type: \"function\",\n },\n ],\n });\n });\n\n test(\"should handle action execution messages without actions context\", () => {\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-1\",\n name: \"testAction\",\n arguments: { param: \"value\" },\n });\n\n const result = gqlToAGUI([actionExecMsg]);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual({\n id: \"action-1\",\n role: \"assistant\",\n name: \"testAction\",\n toolCalls: [\n {\n id: \"action-1\",\n function: {\n name: \"testAction\",\n arguments: JSON.stringify({ param: \"value\" }),\n },\n type: \"function\",\n },\n ],\n });\n // Should not have render functions without actions context\n expect(result[0]).not.toHaveProperty(\"render\");\n expect(result[0]).not.toHaveProperty(\"renderAndWaitForResponse\");\n });\n\n test(\"should handle action execution messages with actions context and render functions\", () => {\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-1\",\n name: \"testAction\",\n arguments: { param: \"value\" },\n status: { code: MessageStatusCode.Pending },\n });\n\n const mockRender = vi.fn();\n const mockRenderAndWaitForResponse = (props: any) => \"Test Render With Response\";\n\n const actions = {\n testAction: {\n name: \"testAction\",\n render: mockRender,\n renderAndWaitForResponse: mockRenderAndWaitForResponse,\n },\n };\n\n const result = gqlToAGUI([actionExecMsg], actions);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toMatchObject({\n id: \"action-1\",\n role: \"assistant\",\n name: \"testAction\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-1\",\n function: {\n name: \"testAction\",\n arguments: JSON.stringify({ param: \"value\" }),\n },\n type: \"function\",\n },\n ],\n });\n\n // Should have generativeUI function\n expect(result[0]).toHaveProperty(\"generativeUI\");\n expect(typeof (result[0] as any).generativeUI).toBe(\"function\");\n });\n\n test(\"should provide correct status in generativeUI function props\", () => {\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-1\",\n name: \"testAction\",\n arguments: { param: \"value\" },\n status: { code: MessageStatusCode.Pending },\n });\n\n const mockRender = vi.fn();\n const actions = {\n testAction: {\n name: \"testAction\",\n render: mockRender,\n },\n };\n\n const result = gqlToAGUI([actionExecMsg], actions);\n\n // Call the generativeUI function\n (result[0] as any).generativeUI?.();\n\n expect(mockRender).toHaveBeenCalledWith({\n status: \"inProgress\",\n args: { param: \"value\" },\n result: undefined,\n respond: expect.any(Function),\n messageId: \"action-1\",\n // Regular actions should NOT have the name property\n });\n });\n\n test(\"should provide executing status when not pending\", () => {\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-1\",\n name: \"testAction\",\n arguments: { param: \"value\" },\n status: { code: MessageStatusCode.Success },\n });\n\n const mockRender = vi.fn();\n const actions = {\n testAction: {\n name: \"testAction\",\n render: mockRender,\n },\n };\n\n const result = gqlToAGUI([actionExecMsg], actions);\n\n // Call the generativeUI function\n (result[0] as any).generativeUI?.();\n\n expect(mockRender).toHaveBeenCalledWith({\n status: \"executing\",\n args: { param: \"value\" },\n result: undefined,\n respond: expect.any(Function),\n messageId: \"action-1\",\n // Regular actions should NOT have the name property\n });\n });\n\n test(\"should provide complete status when result is available\", () => {\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-1\",\n name: \"testAction\",\n arguments: { param: \"value\" },\n status: { code: MessageStatusCode.Success },\n });\n\n const resultMsg = new gql.ResultMessage({\n id: \"result-1\",\n result: \"Action completed successfully\",\n actionExecutionId: \"action-1\",\n actionName: \"testAction\",\n });\n\n const mockRender = vi.fn();\n const actions = {\n testAction: {\n name: \"testAction\",\n render: mockRender,\n },\n };\n\n const result = gqlToAGUI([actionExecMsg, resultMsg], actions);\n\n // Find the action execution message result (not the tool result)\n const actionMessage = result.find((msg) => msg.role === \"assistant\" && \"toolCalls\" in msg);\n\n // Call the generativeUI function\n (actionMessage as any)?.generativeUI?.();\n\n expect(mockRender).toHaveBeenCalledWith({\n status: \"complete\",\n args: { param: \"value\" },\n result: \"Action completed successfully\",\n respond: expect.any(Function),\n messageId: \"action-1\",\n // Regular actions should NOT have the name property\n });\n });\n\n test(\"should handle generativeUI function props override\", () => {\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-1\",\n name: \"testAction\",\n arguments: { param: \"value\" },\n status: { code: MessageStatusCode.Pending },\n });\n\n const mockRender = vi.fn();\n const actions = {\n testAction: {\n name: \"testAction\",\n render: mockRender,\n },\n };\n\n const result = gqlToAGUI([actionExecMsg], actions);\n\n // Call with custom props\n (result[0] as any).generativeUI?.({\n status: \"custom\",\n customProp: \"test\",\n respond: () => \"custom respond\",\n });\n\n expect(mockRender).toHaveBeenCalledWith({\n status: \"custom\",\n args: { param: \"value\" },\n result: undefined,\n respond: expect.any(Function),\n customProp: \"test\",\n messageId: \"action-1\",\n // Regular actions should NOT have the name property\n });\n });\n\n test(\"should handle missing render functions gracefully\", () => {\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-1\",\n name: \"testAction\",\n arguments: { param: \"value\" },\n });\n\n const actions = {\n testAction: {\n name: \"testAction\",\n // No render functions provided\n },\n };\n\n const result = gqlToAGUI([actionExecMsg], actions);\n\n expect(result[0]).toMatchObject({\n id: \"action-1\",\n role: \"assistant\",\n name: \"testAction\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-1\",\n function: {\n name: \"testAction\",\n arguments: JSON.stringify({ param: \"value\" }),\n },\n type: \"function\",\n },\n ],\n });\n\n // Should have undefined generativeUI functions\n expect((result[0] as any).generativeUI).toBeUndefined();\n });\n\n test(\"should handle action not found in actions context\", () => {\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-1\",\n name: \"unknownAction\",\n arguments: { param: \"value\" },\n });\n\n const actions = {\n testAction: {\n name: \"testAction\",\n render: () => \"Test\",\n },\n };\n\n const result = gqlToAGUI([actionExecMsg], actions);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual({\n id: \"action-1\",\n role: \"assistant\",\n name: \"unknownAction\",\n toolCalls: [\n {\n id: \"action-1\",\n function: {\n name: \"unknownAction\",\n arguments: JSON.stringify({ param: \"value\" }),\n },\n type: \"function\",\n },\n ],\n });\n\n // Should not have generativeUI functions when action not found\n expect(result[0]).not.toHaveProperty(\"generativeUI\");\n });\n\n test(\"should handle agent state messages with coAgentStateRenders\", () => {\n const agentStateMsg = new gql.AgentStateMessage({\n id: \"agent-state-1\",\n agentName: \"testAgent\",\n state: { status: \"running\", data: \"test data\" },\n role: gql.Role.Assistant,\n });\n\n const mockRender = vi.fn();\n const coAgentStateRenders = {\n testAgent: {\n name: \"testAgent\",\n render: mockRender,\n },\n };\n\n const result = gqlToAGUI([agentStateMsg], undefined, coAgentStateRenders);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual({\n id: \"agent-state-1\",\n role: \"assistant\",\n agentName: \"testAgent\",\n state: { status: \"running\", data: \"test data\" },\n generativeUI: expect.any(Function),\n });\n\n // Should have generativeUI function\n expect(result[0]).toHaveProperty(\"generativeUI\");\n expect(typeof (result[0] as any).generativeUI).toBe(\"function\");\n\n // Call the generativeUI function\n (result[0] as any).generativeUI?.();\n\n expect(mockRender).toHaveBeenCalledWith({\n state: { status: \"running\", data: \"test data\" },\n });\n });\n\n test(\"should handle agent state messages without coAgentStateRenders\", () => {\n const agentStateMsg = new gql.AgentStateMessage({\n id: \"agent-state-1\",\n agentName: \"testAgent\",\n state: { status: \"running\", data: \"test data\" },\n role: gql.Role.Assistant,\n });\n\n const result = gqlToAGUI([agentStateMsg]);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual({\n id: \"agent-state-1\",\n role: \"assistant\",\n agentName: \"testAgent\",\n state: { status: \"running\", data: \"test data\" },\n });\n\n // Should not have generativeUI functions without coAgentStateRenders\n expect(result[0]).not.toHaveProperty(\"generativeUI\");\n });\n\n test(\"should handle agent state messages with agent not found in coAgentStateRenders\", () => {\n const agentStateMsg = new gql.AgentStateMessage({\n id: \"agent-state-1\",\n agentName: \"unknownAgent\",\n state: { status: \"running\", data: \"test data\" },\n role: gql.Role.Assistant,\n });\n\n const coAgentStateRenders = {\n testAgent: {\n name: \"testAgent\",\n render: () => \"Test\",\n },\n };\n\n const result = gqlToAGUI([agentStateMsg], undefined, coAgentStateRenders);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual({\n id: \"agent-state-1\",\n role: \"assistant\",\n agentName: \"unknownAgent\",\n state: { status: \"running\", data: \"test data\" },\n });\n\n // Should not have generativeUI functions when agent not found\n expect(result[0]).not.toHaveProperty(\"generativeUI\");\n });\n\n test(\"should handle user role messages\", () => {\n const userMsg = new gql.TextMessage({\n id: \"user-1\",\n content: \"Hello from user\",\n role: gql.Role.User,\n });\n\n const result = gqlToAGUI([userMsg]);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual({\n id: \"user-1\",\n role: \"user\",\n content: \"Hello from user\",\n });\n });\n\n test(\"should handle mixed message types including agent state messages\", () => {\n const textMsg = new gql.TextMessage({\n id: \"text-1\",\n content: \"Hello\",\n role: gql.Role.Assistant,\n });\n\n const agentStateMsg = new gql.AgentStateMessage({\n id: \"agent-state-1\",\n agentName: \"testAgent\",\n state: { status: \"running\" },\n role: gql.Role.Assistant,\n });\n\n const mockRender = vi.fn();\n const coAgentStateRenders = {\n testAgent: {\n name: \"testAgent\",\n render: mockRender,\n },\n };\n\n const result = gqlToAGUI([textMsg, agentStateMsg], undefined, coAgentStateRenders);\n\n expect(result).toHaveLength(2);\n expect(result[0]).toEqual({\n id: \"text-1\",\n role: \"assistant\",\n content: \"Hello\",\n });\n expect(result[1]).toMatchObject({\n id: \"agent-state-1\",\n role: \"assistant\",\n agentName: \"testAgent\",\n state: { status: \"running\" },\n generativeUI: expect.any(Function),\n });\n expect(result[1]).toHaveProperty(\"generativeUI\");\n });\n });\n\n describe(\"gqlImageMessageToAGUIMessage\", () => {\n test(\"should throw error for invalid image format\", () => {\n const invalidImageMsg = new gql.ImageMessage({\n id: \"img-1\",\n format: \"bmp\", // not in VALID_IMAGE_FORMATS\n bytes: \"somebase64string\",\n role: gql.Role.User,\n });\n expect(() => gqlImageMessageToAGUIMessage(invalidImageMsg)).toThrow(\"Invalid image format\");\n });\n\n test(\"should throw error for empty image bytes\", () => {\n const invalidImageMsg = new gql.ImageMessage({\n id: \"img-2\",\n format: \"jpeg\",\n bytes: \"\",\n role: gql.Role.User,\n });\n expect(() => gqlImageMessageToAGUIMessage(invalidImageMsg)).toThrow(\n \"Image bytes must be a non-empty string\",\n );\n });\n\n test(\"should convert valid image message\", () => {\n const validImageMsg = new gql.ImageMessage({\n id: \"img-3\",\n format: \"jpeg\",\n bytes: \"somebase64string\",\n role: gql.Role.User,\n });\n const result = gqlImageMessageToAGUIMessage(validImageMsg);\n expect(result).toMatchObject({\n id: \"img-3\",\n role: \"user\",\n content: \"\",\n image: {\n format: \"jpeg\",\n bytes: \"somebase64string\",\n },\n });\n });\n\n test(\"should convert valid user image message\", () => {\n const validImageMsg = new gql.ImageMessage({\n id: \"img-user-1\",\n format: \"jpeg\",\n bytes: \"userbase64string\",\n role: gql.Role.User,\n });\n const result = gqlImageMessageToAGUIMessage(validImageMsg);\n expect(result).toMatchObject({\n id: \"img-user-1\",\n role: \"user\",\n content: \"\",\n image: {\n format: \"jpeg\",\n bytes: \"userbase64string\",\n },\n });\n });\n\n test(\"should convert valid assistant image message\", () => {\n const validImageMsg = new gql.ImageMessage({\n id: \"img-assistant-1\",\n format: \"png\",\n bytes: \"assistantbase64string\",\n role: gql.Role.Assistant,\n });\n const result = gqlImageMessageToAGUIMessage(validImageMsg);\n expect(result).toMatchObject({\n id: \"img-assistant-1\",\n role: \"assistant\",\n content: \"\",\n image: {\n format: \"png\",\n bytes: \"assistantbase64string\",\n },\n });\n });\n });\n\n describe(\"Wild Card Actions\", () => {\n test(\"should handle action execution with specific action\", () => {\n const actions = {\n testAction: {\n name: \"testAction\",\n render: vi.fn((props) => `Rendered: ${props.args.test}`),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-1\",\n name: \"testAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-1\",\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n expect(result).toMatchObject({\n id: \"action-1\",\n role: \"assistant\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-1\",\n function: {\n name: \"testAction\",\n arguments: '{\"test\":\"value\"}',\n },\n type: \"function\",\n },\n ],\n generativeUI: expect.any(Function),\n name: \"testAction\",\n });\n });\n\n test(\"should handle action execution with wild card action\", () => {\n const actions = {\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => `Wildcard rendered: ${props.args.test}`),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-2\",\n name: \"unknownAction\",\n arguments: { test: \"wildcard-value\" },\n parentMessageId: \"parent-2\",\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n expect(result).toMatchObject({\n id: \"action-2\",\n role: \"assistant\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-2\",\n function: {\n name: \"unknownAction\",\n arguments: '{\"test\":\"wildcard-value\"}',\n },\n type: \"function\",\n },\n ],\n generativeUI: expect.any(Function),\n name: \"unknownAction\",\n });\n });\n\n test(\"should pass tool name to wildcard action render function\", () => {\n const mockRender = vi.fn(\n (props) => `Wildcard rendered: ${props.name} with args: ${JSON.stringify(props.args)}`,\n );\n const actions = {\n \"*\": {\n name: \"*\",\n render: mockRender,\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-wildcard-name\",\n name: \"testTool\",\n arguments: { param: \"value\" },\n parentMessageId: \"parent-wildcard-name\",\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n // Call the generativeUI function to trigger the render\n (result as any).generativeUI?.();\n\n // Verify that the render function was called with the name property\n expect(mockRender).toHaveBeenCalledWith(\n expect.objectContaining({\n name: \"testTool\",\n args: { param: \"value\" },\n }),\n );\n });\n\n test(\"should pass tool name to regular action render function\", () => {\n const mockRender = vi.fn((props) => `Regular action rendered: ${JSON.stringify(props.args)}`);\n const actions = {\n testAction: {\n name: \"testAction\",\n render: mockRender,\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-regular-name\",\n name: \"testAction\",\n arguments: { param: \"value\" },\n parentMessageId: \"parent-regular-name\",\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n // Call the generativeUI function to trigger the render\n (result as any).generativeUI?.();\n\n // Verify that the render function was called with the correct props\n // Regular actions should NOT have the name property\n expect(mockRender).toHaveBeenCalledWith(\n expect.objectContaining({\n args: { param: \"value\" },\n // name property should NOT be present for regular actions\n }),\n );\n\n // Verify that the name property is NOT present\n const callArgs = mockRender.mock.calls[0][0];\n expect(callArgs).not.toHaveProperty(\"name\");\n });\n\n test(\"should prioritize specific action over wild card action\", () => {\n const actions = {\n specificAction: {\n name: \"specificAction\",\n render: vi.fn((props) => \"Specific action rendered\"),\n },\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => \"Wildcard action rendered\"),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-3\",\n name: \"specificAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-3\",\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n expect(result).toMatchObject({\n id: \"action-3\",\n role: \"assistant\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-3\",\n function: {\n name: \"specificAction\",\n arguments: '{\"test\":\"value\"}',\n },\n type: \"function\",\n },\n ],\n generativeUI: expect.any(Function),\n name: \"specificAction\",\n });\n });\n\n test(\"should handle action execution without any matching actions\", () => {\n const actions = {\n otherAction: {\n name: \"otherAction\",\n render: vi.fn(),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-4\",\n name: \"unmatchedAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-4\",\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n expect(result).toMatchObject({\n id: \"action-4\",\n role: \"assistant\",\n toolCalls: [\n {\n id: \"action-4\",\n function: {\n name: \"unmatchedAction\",\n arguments: '{\"test\":\"value\"}',\n },\n type: \"function\",\n },\n ],\n name: \"unmatchedAction\",\n });\n expect(result).not.toHaveProperty(\"generativeUI\");\n });\n\n test(\"should handle action execution with no actions provided\", () => {\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-5\",\n name: \"anyAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-5\",\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg);\n\n expect(result).toMatchObject({\n id: \"action-5\",\n role: \"assistant\",\n toolCalls: [\n {\n id: \"action-5\",\n function: {\n name: \"anyAction\",\n arguments: '{\"test\":\"value\"}',\n },\n type: \"function\",\n },\n ],\n name: \"anyAction\",\n });\n expect(result).not.toHaveProperty(\"generativeUI\");\n });\n\n test(\"should handle action execution with completed result\", () => {\n const actions = {\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => `Result: ${props.result}`),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-6\",\n name: \"testAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-6\",\n });\n\n const actionResults = new Map([[\"action-6\", \"completed result\"]]);\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions, actionResults);\n\n expect(result).toMatchObject({\n id: \"action-6\",\n role: \"assistant\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-6\",\n function: {\n name: \"testAction\",\n arguments: '{\"test\":\"value\"}',\n },\n type: \"function\",\n },\n ],\n generativeUI: expect.any(Function),\n name: \"testAction\",\n });\n });\n\n test(\"should handle action execution with executing status\", () => {\n const actions = {\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => `Status: ${props.status}`),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-7\",\n name: \"testAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-7\",\n status: { code: MessageStatusCode.Success },\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n expect(result).toMatchObject({\n id: \"action-7\",\n role: \"assistant\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-7\",\n function: {\n name: \"testAction\",\n arguments: '{\"test\":\"value\"}',\n },\n type: \"function\",\n },\n ],\n generativeUI: expect.any(Function),\n name: \"testAction\",\n });\n });\n\n test(\"should handle action execution with pending status\", () => {\n const actions = {\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => `Status: ${props.status}`),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-8\",\n name: \"testAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-8\",\n status: { code: MessageStatusCode.Pending },\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n expect(result).toMatchObject({\n id: \"action-8\",\n role: \"assistant\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-8\",\n function: {\n name: \"testAction\",\n arguments: '{\"test\":\"value\"}',\n },\n type: \"function\",\n },\n ],\n generativeUI: expect.any(Function),\n name: \"testAction\",\n });\n });\n\n test(\"should handle action execution with failed status\", () => {\n const actions = {\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => `Status: ${props.status}`),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-9\",\n name: \"testAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-9\",\n status: { code: MessageStatusCode.Failed },\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n expect(result).toMatchObject({\n id: \"action-9\",\n role: \"assistant\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-9\",\n function: {\n name: \"testAction\",\n arguments: '{\"test\":\"value\"}',\n },\n type: \"function\",\n },\n ],\n generativeUI: expect.any(Function),\n name: \"testAction\",\n });\n });\n\n test(\"should handle action execution with undefined status\", () => {\n const actions = {\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => `Status: ${props.status}`),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-10\",\n name: \"testAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-10\",\n // No status field\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n expect(result).toMatchObject({\n id: \"action-10\",\n role: \"assistant\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-10\",\n function: {\n name: \"testAction\",\n arguments: '{\"test\":\"value\"}',\n },\n type: \"function\",\n },\n ],\n generativeUI: expect.any(Function),\n name: \"testAction\",\n });\n });\n\n test(\"should handle action execution with empty arguments\", () => {\n const actions = {\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => `Args: ${JSON.stringify(props.args)}`),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-11\",\n name: \"testAction\",\n arguments: {},\n parentMessageId: \"parent-11\",\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n expect(result).toMatchObject({\n id: \"action-11\",\n role: \"assistant\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-11\",\n function: {\n name: \"testAction\",\n arguments: \"{}\",\n },\n type: \"function\",\n },\n ],\n generativeUI: expect.any(Function),\n name: \"testAction\",\n });\n });\n\n test(\"should handle action execution with null arguments\", () => {\n const actions = {\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => `Args: ${JSON.stringify(props.args)}`),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-12\",\n name: \"testAction\",\n arguments: null as any,\n parentMessageId: \"parent-12\",\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n expect(result).toMatchObject({\n id: \"action-12\",\n role: \"assistant\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-12\",\n function: {\n name: \"testAction\",\n arguments: \"null\",\n },\n type: \"function\",\n },\n ],\n generativeUI: expect.any(Function),\n name: \"testAction\",\n });\n });\n\n test(\"should handle action execution with complex nested arguments\", () => {\n const actions = {\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => `Complex: ${JSON.stringify(props.args)}`),\n },\n };\n\n const complexArgs = {\n nested: {\n array: [1, 2, 3],\n object: { key: \"value\" },\n nullValue: null,\n undefinedValue: undefined,\n },\n string: \"test\",\n number: 42,\n boolean: true,\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-13\",\n name: \"testAction\",\n arguments: complexArgs,\n parentMessageId: \"parent-13\",\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n expect(result).toMatchObject({\n id: \"action-13\",\n role: \"assistant\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-13\",\n function: {\n name: \"testAction\",\n arguments: JSON.stringify(complexArgs),\n },\n type: \"function\",\n },\n ],\n generativeUI: expect.any(Function),\n name: \"testAction\",\n });\n });\n\n test(\"should handle multiple wild card actions (should use first one)\", () => {\n const actions = {\n wildcard1: {\n name: \"*\",\n render: vi.fn((props) => \"First wildcard\"),\n },\n wildcard2: {\n name: \"*\",\n render: vi.fn((props) => \"Second wildcard\"),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-14\",\n name: \"unknownAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-14\",\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions);\n\n expect(result).toMatchObject({\n id: \"action-14\",\n role: \"assistant\",\n content: \"\",\n toolCalls: [\n {\n id: \"action-14\",\n function: {\n name: \"unknownAction\",\n arguments: '{\"test\":\"value\"}',\n },\n type: \"function\",\n },\n ],\n generativeUI: expect.any(Function),\n name: \"unknownAction\",\n });\n });\n\n test(\"should parse string results in generativeUI props\", () => {\n const actions = {\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => `Result: ${JSON.stringify(props.result)}`),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-string-result\",\n name: \"stringResultAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-string\",\n });\n\n const actionResults = new Map<string, string>();\n actionResults.set(\"action-string-result\", '{\"parsed\": true, \"value\": 42}');\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions, actionResults);\n\n expect((result as AIMessage).generativeUI).toBeDefined();\n // Call the render function to test the result parsing\n const renderResult = (result as AIMessage).generativeUI!({\n result: '{\"from\": \"props\", \"data\": \"test\"}',\n });\n\n // Verify the render function was called and the result was parsed\n expect(actions[\"*\"].render).toHaveBeenCalledWith(\n expect.objectContaining({\n result: { from: \"props\", data: \"test\" }, // Should be parsed from string\n args: { test: \"value\" },\n status: \"complete\",\n messageId: \"action-string-result\",\n }),\n );\n });\n\n test(\"should handle malformed JSON strings gracefully in results\", () => {\n const actions = {\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => `Result: ${JSON.stringify(props.result)}`),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-malformed\",\n name: \"malformedAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-malformed\",\n });\n\n const actionResults = new Map<string, string>();\n actionResults.set(\"action-malformed\", \"invalid json {\");\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions, actionResults);\n\n expect((result as AIMessage).generativeUI).toBeDefined();\n // Call the render function to test malformed JSON handling\n const renderResult = (result as AIMessage).generativeUI!({\n result: \"invalid json {\",\n });\n\n // Verify the render function was called with the original string (unparsed)\n expect(actions[\"*\"].render).toHaveBeenCalledWith(\n expect.objectContaining({\n result: \"invalid json {\", // Should remain as string due to parse error\n args: { test: \"value\" },\n status: \"complete\",\n messageId: \"action-malformed\",\n }),\n );\n });\n\n test(\"should handle non-string results without parsing\", () => {\n const actions = {\n \"*\": {\n name: \"*\",\n render: vi.fn((props) => `Result: ${JSON.stringify(props.result)}`),\n },\n };\n\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-object-result\",\n name: \"objectResultAction\",\n arguments: { test: \"value\" },\n parentMessageId: \"parent-object\",\n });\n\n const actionResults = new Map<string, string>();\n actionResults.set(\"action-object-result\", '{\"already\": \"parsed\"}');\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg, actions, actionResults);\n\n expect((result as AIMessage).generativeUI).toBeDefined();\n // Call the render function with an object result\n const renderResult = (result as AIMessage).generativeUI!({\n result: { from: \"props\", data: \"object\" },\n });\n\n // Verify the render function was called with the object as-is\n expect(actions[\"*\"].render).toHaveBeenCalledWith(\n expect.objectContaining({\n result: { from: \"props\", data: \"object\" }, // Should remain as object\n args: { test: \"value\" },\n status: \"complete\",\n messageId: \"action-object-result\",\n }),\n );\n });\n\n test(\"should handle action execution arguments correctly with simplified conversion\", () => {\n const actionExecMsg = new gql.ActionExecutionMessage({\n id: \"action-simplified\",\n name: \"simplifiedAction\",\n arguments: { complex: { nested: \"value\" }, array: [1, 2, 3] },\n parentMessageId: \"parent-simplified\",\n });\n\n const result = gqlActionExecutionMessageToAGUIMessage(actionExecMsg);\n\n expect(result).toMatchObject({\n id: \"action-simplified\",\n role: \"assistant\",\n toolCalls: [\n {\n id: \"action-simplified\",\n function: {\n name: \"simplifiedAction\",\n arguments: '{\"complex\":{\"nested\":\"value\"},\"array\":[1,2,3]}',\n },\n type: \"function\",\n },\n ],\n name: \"simplifiedAction\",\n });\n });\n });\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,SAAS,sBAAsB,MAAM;AACnC,WAAS,+BAA+B,MAAM;AAC5C,SAAK,oCAAoC,MAAM;AAC7C,YAAM,aAAa,IAAQ,YAAY;AAAA,QACrC,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,MAAU,KAAK;AAAA,MACjB,CAAC;AAED,YAAM,SAAS,4BAA4B,UAAU;AAErD,mBAAO,MAAM,EAAE,QAAQ;AAAA,QACrB,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,SAAS;AAAA,MACX,CAAC;AAAA,IACH,CAAC;AAED,SAAK,iCAAiC,MAAM;AAC1C,YAAM,aAAa,IAAQ,YAAY;AAAA,QACrC,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,MAAU,KAAK;AAAA,MACjB,CAAC;AAED,YAAM,SAAS,4BAA4B,UAAU;AAErD,mBAAO,MAAM,EAAE,QAAQ;AAAA,QACrB,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,SAAS;AAAA,MACX,CAAC;AAAA,IACH,CAAC;AAED,SAAK,oCAAoC,MAAM;AAC7C,YAAM,aAAa,IAAQ,YAAY;AAAA,QACrC,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,MAAU,KAAK;AAAA,MACjB,CAAC;AAED,YAAM,SAAS,4BAA4B,UAAU;AAErD,mBAAO,MAAM,EAAE,QAAQ;AAAA,QACrB,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,SAAS;AAAA,MACX,CAAC;AAAA,IACH,CAAC;AAED,SAAK,uCAAuC,MAAM;AAChD,YAAM,aAAa,IAAQ,YAAY;AAAA,QACrC,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,MAAM;AAAA,MACR,CAAC;AAED,mBAAO,MAAM,4BAA4B,UAAU,CAAC,EAAE,QAAQ,sBAAsB;AAAA,IACtF,CAAC;AAAA,EACH,CAAC;AAED,WAAS,iCAAiC,MAAM;AAC9C,SAAK,iDAAiD,MAAM;AAC1D,YAAM,aAAa,IAAQ,cAAc;AAAA,QACvC,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR,mBAAmB;AAAA,QACnB,YAAY;AAAA,MACd,CAAC;AAED,YAAM,SAAS,8BAA8B,UAAU;AAEvD,mBAAO,MAAM,EAAE,QAAQ;AAAA,QACrB,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,UAAU;AAAA,MACZ,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AAED,WAAS,aAAa,MAAM;AAC1B,SAAK,4CAA4C,MAAM;AACrD,YAAM,cAAc;AAAA,QAClB,IAAQ,YAAY;AAAA,