@sqlrooms/ai 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,49 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ToolCall } from './ToolCall';
3
- import { ToolResult } from './ToolResult';
4
2
  import { SquareTerminalIcon, CodeIcon } from 'lucide-react';
5
3
  import { Button, Popover, PopoverContent, PopoverTrigger } from '@sqlrooms/ui';
4
+ import { ToolResult } from './ToolResult';
5
+ /**
6
+ * Stringify the result of the analysis, excluding toolCallMessages.
7
+ * Used to display raw result data in a code view.
8
+ *
9
+ * @param result - The complete analysis result
10
+ * @returns A JSON string representation of the result without toolCallMessages
11
+ */
12
+ const stringifyResult = (result) => {
13
+ // remove toolCallMessages from the result
14
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
15
+ const { toolCallMessages, ...rest } = result;
16
+ return JSON.stringify(rest);
17
+ };
18
+ /**
19
+ * Find a custom message element for a given tool call ID
20
+ * @param toolCallMessages - Array of tool call messages
21
+ * @param toolCallId - The ID of the tool call to find a message for
22
+ * @returns The custom message element if found, undefined otherwise
23
+ */
24
+ const findCustomMessage = (toolCallMessages, toolCallId) => {
25
+ return toolCallMessages.find((message) => message.toolCallId === toolCallId)
26
+ ?.element;
27
+ };
28
+ /**
29
+ * Component that displays the results of an AI analysis.
30
+ * Shows the original prompt, intermediate tool calls, final analysis text,
31
+ * and any tool results.
32
+ *
33
+ * @component
34
+ * @param props - Component props
35
+ * @param props.result - The analysis result data to display
36
+ * @returns A React component displaying the analysis results
37
+ */
6
38
  export const AnalysisResult = ({ result }) => {
7
- return (_jsxs("div", { className: "flex flex-col w-full text-sm gap-5 border py-6 px-4 rounded-md", children: [_jsxs("div", { className: "p-2 mb-2 rounded-md text-gray-700 dark:text-gray-100 flex items-center gap-2", children: [_jsx(SquareTerminalIcon, { className: "w-4 h-4" }), _jsx("div", { className: "text-sm flex-1", children: result.prompt }), _jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "w-6 h-6", children: _jsx(CodeIcon, { className: "w-4 h-4" }) }) }), _jsx(PopoverContent, { className: "w-[400px] max-h-[400px] overflow-auto", align: "end", side: "right", children: _jsx("pre", { className: "text-xs", children: JSON.stringify(result, null, 2) }) })] })] }), _jsx("div", { className: "flex flex-col gap-5 px-4", children: result.toolResults
8
- .filter((toolResult) => toolResult.toolName !== 'answer' || !toolResult.result.success)
9
- .map((toolResult) => (_jsx(ToolResult, { toolResult: toolResult }, toolResult.toolCallId))) }), _jsx("div", { className: "flex flex-col gap-5 px-4", children: result.toolCalls
10
- .filter((toolCall) => toolCall.toolName === 'answer')
11
- .map((toolCall) => (_jsx(ToolCall, { toolCall: toolCall }, toolCall.toolCallId))) })] }));
39
+ // the toolResults are reasoning steps that the LLM took to achieve the final result
40
+ // by calling function tools to answer the prompt
41
+ const analysisToolResults = result.toolResults;
42
+ return (_jsxs("div", { className: "flex flex-col w-full text-sm gap-5 border py-6 px-4 rounded-md", children: [_jsxs("div", { className: "p-2 mb-2 rounded-md text-gray-700 dark:text-gray-100 flex items-center gap-2", children: [_jsx(SquareTerminalIcon, { className: "w-4 h-4" }), _jsx("div", { className: "text-sm flex-1", children: result.prompt }), _jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "w-6 h-6", children: _jsx(CodeIcon, { className: "w-4 h-4" }) }) }), _jsx(PopoverContent, { className: "w-[400px] max-h-[400px] overflow-auto", align: "end", side: "right", children: _jsx("pre", { className: "text-xs", children: stringifyResult(result) }) })] })] }), analysisToolResults.length > 0 && (_jsx("div", { className: "flex flex-col gap-5 px-4", children: analysisToolResults.map((toolResult) => (_jsx(ToolResult, { toolResult: toolResult, customMessage: findCustomMessage(result.toolCallMessages, toolResult.toolCallId) }, toolResult.toolCallId))) })), result.analysis.length > 0 && (_jsx("div", { className: "flex flex-col gap-5 px-4", children: _jsx(ToolResult, { toolResult: {
43
+ toolName: 'answer',
44
+ toolCallId: result.id,
45
+ args: {},
46
+ result: { success: true, data: { analysis: result.analysis } },
47
+ } }, result.id + '-streaming-result') }))] }));
12
48
  };
13
49
  //# sourceMappingURL=AnalysisResult.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnalysisResult.js","sourceRoot":"","sources":["../src/AnalysisResult.tsx"],"names":[],"mappings":";AACA,OAAO,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AACpC,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AACxC,OAAO,EAAC,kBAAkB,EAAE,QAAQ,EAAC,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAC,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAC,MAAM,cAAc,CAAC;AAM7E,MAAM,CAAC,MAAM,cAAc,GAAkC,CAAC,EAAC,MAAM,EAAC,EAAE,EAAE;IACxE,OAAO,CACL,eAAK,SAAS,EAAC,gEAAgE,aAC7E,eAAK,SAAS,EAAC,8EAA8E,aAC3F,KAAC,kBAAkB,IAAC,SAAS,EAAC,SAAS,GAAG,EAC1C,cAAK,SAAS,EAAC,gBAAgB,YAAE,MAAM,CAAC,MAAM,GAAO,EACrD,MAAC,OAAO,eACN,KAAC,cAAc,IAAC,OAAO,kBACrB,KAAC,MAAM,IAAC,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,MAAM,EAAC,SAAS,EAAC,SAAS,YACrD,KAAC,QAAQ,IAAC,SAAS,EAAC,SAAS,GAAG,GACzB,GACM,EACjB,KAAC,cAAc,IACb,SAAS,EAAC,uCAAuC,EACjD,KAAK,EAAC,KAAK,EACX,IAAI,EAAC,OAAO,YAEZ,cAAK,SAAS,EAAC,SAAS,YAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAO,GACjD,IACT,IACN,EACN,cAAK,SAAS,EAAC,0BAA0B,YACtC,MAAM,CAAC,WAAW;qBAChB,MAAM,CACL,CAAC,UAAU,EAAE,EAAE,CACb,UAAU,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CACjE;qBACA,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CACnB,KAAC,UAAU,IAA6B,UAAU,EAAE,UAAU,IAA7C,UAAU,CAAC,UAAU,CAA4B,CACnE,CAAC,GACA,EACN,cAAK,SAAS,EAAC,0BAA0B,YACtC,MAAM,CAAC,SAAS;qBACd,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,KAAK,QAAQ,CAAC;qBACpD,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CACjB,KAAC,QAAQ,IAA2B,QAAQ,EAAE,QAAQ,IAAvC,QAAQ,CAAC,UAAU,CAAwB,CAC3D,CAAC,GACA,IACF,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {AnalysisResultSchema} from './schemas';\nimport {ToolCall} from './ToolCall';\nimport {ToolResult} from './ToolResult';\nimport {SquareTerminalIcon, CodeIcon} from 'lucide-react';\nimport {Button, Popover, PopoverContent, PopoverTrigger} from '@sqlrooms/ui';\n\ninterface AnalysisResultProps {\n result: AnalysisResultSchema;\n}\n\nexport const AnalysisResult: React.FC<AnalysisResultProps> = ({result}) => {\n return (\n <div className=\"flex flex-col w-full text-sm gap-5 border py-6 px-4 rounded-md\">\n <div className=\"p-2 mb-2 rounded-md text-gray-700 dark:text-gray-100 flex items-center gap-2\">\n <SquareTerminalIcon className=\"w-4 h-4\" />\n <div className=\"text-sm flex-1\">{result.prompt}</div>\n <Popover>\n <PopoverTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" className=\"w-6 h-6\">\n <CodeIcon className=\"w-4 h-4\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className=\"w-[400px] max-h-[400px] overflow-auto\"\n align=\"end\"\n side=\"right\"\n >\n <pre className=\"text-xs\">{JSON.stringify(result, null, 2)}</pre>\n </PopoverContent>\n </Popover>\n </div>\n <div className=\"flex flex-col gap-5 px-4\">\n {result.toolResults\n .filter(\n (toolResult) =>\n toolResult.toolName !== 'answer' || !toolResult.result.success,\n )\n .map((toolResult) => (\n <ToolResult key={toolResult.toolCallId} toolResult={toolResult} />\n ))}\n </div>\n <div className=\"flex flex-col gap-5 px-4\">\n {result.toolCalls\n .filter((toolCall) => toolCall.toolName === 'answer')\n .map((toolCall) => (\n <ToolCall key={toolCall.toolCallId} toolCall={toolCall} />\n ))}\n </div>\n </div>\n );\n};\n"]}
1
+ {"version":3,"file":"AnalysisResult.js","sourceRoot":"","sources":["../src/AnalysisResult.tsx"],"names":[],"mappings":";AACA,OAAO,EAAC,kBAAkB,EAAE,QAAQ,EAAC,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAC,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAC,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AAUxC;;;;;;GAMG;AACH,MAAM,eAAe,GAAG,CAAC,MAA4B,EAAE,EAAE;IACvD,0CAA0C;IAC1C,6DAA6D;IAC7D,MAAM,EAAC,gBAAgB,EAAE,GAAG,IAAI,EAAC,GAAG,MAAM,CAAC;IAC3C,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG,CACxB,gBAA0D,EAC1D,UAAkB,EAClB,EAAE;IACF,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,KAAK,UAAU,CAAC;QAC1E,EAAE,OAAO,CAAC;AACd,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,cAAc,GAAkC,CAAC,EAAC,MAAM,EAAC,EAAE,EAAE;IACxE,oFAAoF;IACpF,iDAAiD;IACjD,MAAM,mBAAmB,GAAG,MAAM,CAAC,WAAW,CAAC;IAE/C,OAAO,CACL,eAAK,SAAS,EAAC,gEAAgE,aAC7E,eAAK,SAAS,EAAC,8EAA8E,aAC3F,KAAC,kBAAkB,IAAC,SAAS,EAAC,SAAS,GAAG,EAC1C,cAAK,SAAS,EAAC,gBAAgB,YAAE,MAAM,CAAC,MAAM,GAAO,EACrD,MAAC,OAAO,eACN,KAAC,cAAc,IAAC,OAAO,kBACrB,KAAC,MAAM,IAAC,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,MAAM,EAAC,SAAS,EAAC,SAAS,YACrD,KAAC,QAAQ,IAAC,SAAS,EAAC,SAAS,GAAG,GACzB,GACM,EACjB,KAAC,cAAc,IACb,SAAS,EAAC,uCAAuC,EACjD,KAAK,EAAC,KAAK,EACX,IAAI,EAAC,OAAO,YAEZ,cAAK,SAAS,EAAC,SAAS,YAAE,eAAe,CAAC,MAAM,CAAC,GAAO,GACzC,IACT,IACN,EACL,mBAAmB,CAAC,MAAM,GAAG,CAAC,IAAI,CACjC,cAAK,SAAS,EAAC,0BAA0B,YACtC,mBAAmB,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CACvC,KAAC,UAAU,IAET,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,iBAAiB,CAC9B,MAAM,CAAC,gBAAgB,EACvB,UAAU,CAAC,UAAU,CACtB,IALI,UAAU,CAAC,UAAU,CAM1B,CACH,CAAC,GACE,CACP,EACA,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAC7B,cAAK,SAAS,EAAC,0BAA0B,YACvC,KAAC,UAAU,IAET,UAAU,EAAE;wBACV,QAAQ,EAAE,QAAQ;wBAClB,UAAU,EAAE,MAAM,CAAC,EAAE;wBACrB,IAAI,EAAE,EAAE;wBACR,MAAM,EAAE,EAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAC,EAAC;qBAC3D,IANI,MAAM,CAAC,EAAE,GAAG,mBAAmB,CAOpC,GACE,CACP,IACG,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {AnalysisResultSchema} from './schemas';\nimport {SquareTerminalIcon, CodeIcon} from 'lucide-react';\nimport {Button, Popover, PopoverContent, PopoverTrigger} from '@sqlrooms/ui';\nimport {ToolResult} from './ToolResult';\n\n/**\n * Props for the AnalysisResult component\n * @property {AnalysisResultSchema} result - The result of the analysis containing prompt, tool calls, and analysis data\n */\ntype AnalysisResultProps = {\n result: AnalysisResultSchema;\n};\n\n/**\n * Stringify the result of the analysis, excluding toolCallMessages.\n * Used to display raw result data in a code view.\n *\n * @param result - The complete analysis result\n * @returns A JSON string representation of the result without toolCallMessages\n */\nconst stringifyResult = (result: AnalysisResultSchema) => {\n // remove toolCallMessages from the result\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {toolCallMessages, ...rest} = result;\n return JSON.stringify(rest);\n};\n\n/**\n * Find a custom message element for a given tool call ID\n * @param toolCallMessages - Array of tool call messages\n * @param toolCallId - The ID of the tool call to find a message for\n * @returns The custom message element if found, undefined otherwise\n */\nconst findCustomMessage = (\n toolCallMessages: AnalysisResultSchema['toolCallMessages'],\n toolCallId: string,\n) => {\n return toolCallMessages.find((message) => message.toolCallId === toolCallId)\n ?.element;\n};\n\n/**\n * Component that displays the results of an AI analysis.\n * Shows the original prompt, intermediate tool calls, final analysis text,\n * and any tool results.\n *\n * @component\n * @param props - Component props\n * @param props.result - The analysis result data to display\n * @returns A React component displaying the analysis results\n */\nexport const AnalysisResult: React.FC<AnalysisResultProps> = ({result}) => {\n // the toolResults are reasoning steps that the LLM took to achieve the final result\n // by calling function tools to answer the prompt\n const analysisToolResults = result.toolResults;\n\n return (\n <div className=\"flex flex-col w-full text-sm gap-5 border py-6 px-4 rounded-md\">\n <div className=\"p-2 mb-2 rounded-md text-gray-700 dark:text-gray-100 flex items-center gap-2\">\n <SquareTerminalIcon className=\"w-4 h-4\" />\n <div className=\"text-sm flex-1\">{result.prompt}</div>\n <Popover>\n <PopoverTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" className=\"w-6 h-6\">\n <CodeIcon className=\"w-4 h-4\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className=\"w-[400px] max-h-[400px] overflow-auto\"\n align=\"end\"\n side=\"right\"\n >\n <pre className=\"text-xs\">{stringifyResult(result)}</pre>\n </PopoverContent>\n </Popover>\n </div>\n {analysisToolResults.length > 0 && (\n <div className=\"flex flex-col gap-5 px-4\">\n {analysisToolResults.map((toolResult) => (\n <ToolResult\n key={toolResult.toolCallId}\n toolResult={toolResult}\n customMessage={findCustomMessage(\n result.toolCallMessages,\n toolResult.toolCallId,\n )}\n />\n ))}\n </div>\n )}\n {result.analysis.length > 0 && (\n <div className=\"flex flex-col gap-5 px-4\">\n <ToolResult\n key={result.id + '-streaming-result'}\n toolResult={{\n toolName: 'answer',\n toolCallId: result.id,\n args: {},\n result: {success: true, data: {analysis: result.analysis}},\n }}\n />\n </div>\n )}\n </div>\n );\n};\n"]}
@@ -0,0 +1,9 @@
1
+ import { CustomFunctionCall } from '@openassistant/core';
2
+ /**
3
+ * Creates a query result message component based on a custom function call.
4
+ *
5
+ * @param {CustomFunctionCall} props - The custom function call properties
6
+ * @returns {JSX.Element | null} A query result table modal component or null if no data is available
7
+ */
8
+ export declare function queryMessage(props: CustomFunctionCall): import("react/jsx-runtime").JSX.Element | null;
9
+ //# sourceMappingURL=QueryResult.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryResult.d.ts","sourceRoot":"","sources":["../src/QueryResult.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,kBAAkB,EAAC,MAAM,qBAAqB,CAAC;AA6DvD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,kBAAkB,kDAarD"}
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { DataTableModal } from '@sqlrooms/data-table';
3
+ import { Expand, TableIcon } from 'lucide-react';
4
+ import { useState } from 'react';
5
+ /**
6
+ * A modal component that displays a data table with a title.
7
+ *
8
+ * @component
9
+ * @param {Object} props - The component props
10
+ * @param {string} props.title - The title to display in the modal header
11
+ * @param {string} props.sqlQuery - The SQL query that generated the data
12
+ * @returns {JSX.Element} A modal component containing a data table
13
+ */
14
+ function QueryResultTableModal({ title, sqlQuery, }) {
15
+ const [isModalOpen, setIsModalOpen] = useState(false);
16
+ const handleClick = () => {
17
+ setIsModalOpen(true);
18
+ };
19
+ const onClose = () => {
20
+ setIsModalOpen(false);
21
+ };
22
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex gap-2 flex-row items-center text-muted-foreground", onClick: handleClick, children: [_jsx(TableIcon, { className: "h-4 w-4" }), _jsx("h3", { className: "ml-1 text-xs uppercase", children: "Query Result" }), _jsx(Expand, { className: "h-3 w-3 shrink-0 text-muted-foreground transition-transform duration-200" })] }), _jsx(DataTableModal, { title: title, query: sqlQuery, tableModal: {
23
+ isOpen: isModalOpen,
24
+ onClose: onClose,
25
+ } })] }));
26
+ }
27
+ function isQueryToolOutputData(data) {
28
+ return typeof data === 'object' && data !== null && 'sqlQuery' in data;
29
+ }
30
+ /**
31
+ * Creates a query result message component based on a custom function call.
32
+ *
33
+ * @param {CustomFunctionCall} props - The custom function call properties
34
+ * @returns {JSX.Element | null} A query result table modal component or null if no data is available
35
+ */
36
+ export function queryMessage(props) {
37
+ const data = props.output?.data;
38
+ if (!isQueryToolOutputData(data)) {
39
+ throw new Error('Invalid query tool output data');
40
+ }
41
+ if (!data.sqlQuery) {
42
+ return null;
43
+ }
44
+ return (_jsx(QueryResultTableModal, { title: data.sqlQuery, sqlQuery: data.sqlQuery }));
45
+ }
46
+ //# sourceMappingURL=QueryResult.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryResult.js","sourceRoot":"","sources":["../src/QueryResult.tsx"],"names":[],"mappings":";AACA,OAAO,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAC,MAAM,EAAE,SAAS,EAAC,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAC;AAE/B;;;;;;;;GAQG;AACH,SAAS,qBAAqB,CAAC,EAC7B,KAAK,EACL,QAAQ,GAIT;IACC,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEtD,MAAM,WAAW,GAAG,GAAG,EAAE;QACvB,cAAc,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,cAAc,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC,CAAC;IAEF,OAAO,CACL,8BACE,eACE,SAAS,EAAC,wDAAwD,EAClE,OAAO,EAAE,WAAW,aAEpB,KAAC,SAAS,IAAC,SAAS,EAAC,SAAS,GAAG,EACjC,aAAI,SAAS,EAAC,wBAAwB,6BAAkB,EACxD,KAAC,MAAM,IAAC,SAAS,EAAC,0EAA0E,GAAG,IAC3F,EAEN,KAAC,cAAc,IACb,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,QAAQ,EACf,UAAU,EAAE;oBACV,MAAM,EAAE,WAAW;oBACnB,OAAO,EAAE,OAAO;iBACjB,GACD,IACD,CACJ,CAAC;AACJ,CAAC;AAMD,SAAS,qBAAqB,CAAC,IAAa;IAC1C,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,UAAU,IAAI,IAAI,CAAC;AACzE,CAAC;AACD;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,KAAyB;IACpD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC;IAEhC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CACL,KAAC,qBAAqB,IAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,GAAI,CACzE,CAAC;AACJ,CAAC","sourcesContent":["import {CustomFunctionCall} from '@openassistant/core';\nimport {DataTableModal} from '@sqlrooms/data-table';\nimport {Expand, TableIcon} from 'lucide-react';\nimport {useState} from 'react';\n\n/**\n * A modal component that displays a data table with a title.\n *\n * @component\n * @param {Object} props - The component props\n * @param {string} props.title - The title to display in the modal header\n * @param {string} props.sqlQuery - The SQL query that generated the data\n * @returns {JSX.Element} A modal component containing a data table\n */\nfunction QueryResultTableModal({\n title,\n sqlQuery,\n}: {\n title: string;\n sqlQuery: string;\n}) {\n const [isModalOpen, setIsModalOpen] = useState(false);\n\n const handleClick = () => {\n setIsModalOpen(true);\n };\n\n const onClose = () => {\n setIsModalOpen(false);\n };\n\n return (\n <>\n <div\n className=\"flex gap-2 flex-row items-center text-muted-foreground\"\n onClick={handleClick}\n >\n <TableIcon className=\"h-4 w-4\" />\n <h3 className=\"ml-1 text-xs uppercase\">Query Result</h3>\n <Expand className=\"h-3 w-3 shrink-0 text-muted-foreground transition-transform duration-200\" />\n </div>\n\n <DataTableModal\n title={title}\n query={sqlQuery}\n tableModal={{\n isOpen: isModalOpen,\n onClose: onClose,\n }}\n />\n </>\n );\n}\n\ntype QueryToolOutputData = {\n sqlQuery: string;\n};\n\nfunction isQueryToolOutputData(data: unknown): data is QueryToolOutputData {\n return typeof data === 'object' && data !== null && 'sqlQuery' in data;\n}\n/**\n * Creates a query result message component based on a custom function call.\n *\n * @param {CustomFunctionCall} props - The custom function call properties\n * @returns {JSX.Element | null} A query result table modal component or null if no data is available\n */\nexport function queryMessage(props: CustomFunctionCall) {\n const data = props.output?.data;\n\n if (!isQueryToolOutputData(data)) {\n throw new Error('Invalid query tool output data');\n }\n if (!data.sqlQuery) {\n return null;\n }\n\n return (\n <QueryResultTableModal title={data.sqlQuery} sqlQuery={data.sqlQuery} />\n );\n}\n"]}
@@ -1,7 +1,66 @@
1
- import { ToolCallSchema } from './schemas';
2
- interface ToolCallProps {
3
- toolCall: ToolCallSchema;
4
- }
5
- export declare function ToolCall({ toolCall }: ToolCallProps): import("react/jsx-runtime").JSX.Element;
1
+ import { ChartToolParameters, QueryToolParameters, ToolResultSchema } from './schemas';
2
+ import { ReactNode } from 'react';
3
+ import React from 'react';
4
+ /**
5
+ * Props for the QueryToolCall component
6
+ * @interface QueryToolCallProps
7
+ * @extends {QueryToolParameters}
8
+ * @property {ReactNode} [customMessage] - Optional custom message to display with the query
9
+ */
10
+ type QueryToolCallProps = QueryToolParameters & {
11
+ customMessage?: ReactNode;
12
+ };
13
+ /**
14
+ * Type guard to check if an argument object matches QueryToolParameters
15
+ * @param {unknown} args - The arguments to check
16
+ * @returns {boolean} True if args matches QueryToolParameters structure
17
+ */
18
+ export declare function isQueryToolParameters(args: unknown): args is QueryToolParameters;
19
+ /**
20
+ * Type guard to check if an argument object matches ChartToolParameters
21
+ * @param {unknown} args - The arguments to check
22
+ * @returns {boolean} True if args matches ChartToolParameters structure
23
+ */
24
+ export declare function isChartToolParameters(args: unknown): args is ChartToolParameters;
25
+ /**
26
+ * Renders a SQL query tool call with reasoning and query text
27
+ * @param {QueryToolCallProps} props - The component props
28
+ * @returns {JSX.Element} The rendered query tool call
29
+ */
30
+ export declare const QueryToolCall: React.NamedExoticComponent<QueryToolCallProps>;
31
+ /**
32
+ * Renders a chart tool call with visualization using Vega-Lite
33
+ * @param {ChartToolParameters} props - The component props
34
+ * @returns {JSX.Element} The rendered chart tool call
35
+ */
36
+ export declare const ChartToolCall: React.NamedExoticComponent<{
37
+ sqlQuery: string;
38
+ reasoning: string;
39
+ vegaLiteSpec: string;
40
+ }>;
41
+ /**
42
+ * Type guard to check if a result object matches AnalysisAnswerProps
43
+ * @param {unknown} result - The result to check
44
+ * @returns {boolean} True if result matches AnalysisAnswerProps structure
45
+ */
46
+ type AnalysisAnswerProps = {
47
+ success: true;
48
+ data: {
49
+ analysis: string;
50
+ };
51
+ };
52
+ /**
53
+ * Type guard to check if a result object matches AnalysisAnswerProps
54
+ * @param {unknown} result - The result to check
55
+ * @returns {boolean} True if result matches AnalysisAnswerProps structure
56
+ */
57
+ export declare function isAnalysisAnswer(result: ToolResultSchema['result']): result is AnalysisAnswerProps;
58
+ /**
59
+ * Renders an analysis answer with markdown content of the final streaming response.
60
+ *
61
+ * @param {AnalysisAnswerProps} props - The component props. See {@link AnalysisAnswerProps} for more details.
62
+ * @returns {JSX.Element} The rendered answer tool call
63
+ */
64
+ export declare const AnalysisAnswer: React.NamedExoticComponent<AnalysisAnswerProps>;
6
65
  export {};
7
66
  //# sourceMappingURL=ToolCall.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToolCall.d.ts","sourceRoot":"","sources":["../src/ToolCall.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,WAAW,CAAC;AAezC,UAAU,aAAa;IACrB,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED,wBAAgB,QAAQ,CAAC,EAAC,QAAQ,EAAC,EAAE,aAAa,2CAiFjD"}
1
+ {"version":3,"file":"ToolCall.d.ts","sourceRoot":"","sources":["../src/ToolCall.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAW,SAAS,EAAC,MAAM,OAAO,CAAC;AAG1C,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;;GAKG;AACH,KAAK,kBAAkB,GAAG,mBAAmB,GAAG;IAC9C,aAAa,CAAC,EAAE,SAAS,CAAC;CAC3B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,OAAO,GACZ,IAAI,IAAI,mBAAmB,CAO7B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,OAAO,GACZ,IAAI,IAAI,mBAAmB,CAE7B;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,gDAYxB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;;EAiCxB,CAAC;AAEH;;;;GAIG;AACH,KAAK,mBAAmB,GAAG;IACzB,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,GACjC,MAAM,IAAI,mBAAmB,CAE/B;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,iDAYzB,CAAC"}
package/dist/ToolCall.js CHANGED
@@ -1,16 +1,59 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Badge, Button, Popover, PopoverContent, PopoverTrigger, Spinner, cn, } from '@sqlrooms/ui';
3
- import { CodeIcon } from 'lucide-react';
2
+ import { Spinner } from '@sqlrooms/ui';
4
3
  import { Suspense } from 'react';
5
4
  import Markdown from 'react-markdown';
6
5
  import { VegaLiteChart } from '@sqlrooms/vega';
7
- export function ToolCall({ toolCall }) {
8
- const { args, toolName, toolCallId } = toolCall;
9
- const { type } = args;
10
- return (_jsxs("div", { className: cn('border-2 relative px-5 py-6 rounded-md dark:bg-gray-900 dark:text-gray-300 bg-gray-100 text-gray-700 text-xs', {
11
- ' border-blue-500': toolName === 'answer',
12
- }), children: [_jsx(Badge, { variant: "secondary", className: cn('text-xs absolute top-[-12px] left-2 dark:text-gray-100 text-gray-700', {
13
- ' border-blue-500': toolName === 'answer',
14
- }), children: toolName }), _jsx("div", { className: "absolute top-2 right-2", children: _jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", size: "icon", className: "w-6 h-6", children: _jsx(CodeIcon, { className: "w-4 h-4" }) }) }), _jsx(PopoverContent, { className: "w-[400px] max-h-[400px] overflow-auto", align: "start", side: "right", children: _jsx("pre", { className: "text-xs", children: JSON.stringify(toolCall, null, 2) }) })] }) }), type === 'query' ? (_jsxs("div", { className: "flex flex-col gap-5", children: [_jsx("div", { className: "text-xs text-gray-700 dark:text-gray-400", children: args.reasoning }), _jsx("div", { className: "font-mono", children: args.sqlQuery })] })) : type === 'answer' ? (_jsxs("div", { className: "flex flex-col gap-5", children: [_jsx("div", { className: "text-sm", children: _jsx(Markdown, { children: args.answer }) }), args.chart && (_jsxs("div", { className: "flex flex-col gap-2 w-full", children: [_jsx("div", { className: "text-xs text-muted-foreground font-mono", children: args.chart.sqlQuery }), _jsx(Suspense, { fallback: _jsx("div", { className: "w-full h-full flex items-center justify-center", children: _jsx(Spinner, { className: "w-4 h-4" }) }), children: _jsx(VegaLiteChart, { className: "max-w-[600px]", aspectRatio: 16 / 9, sqlQuery: args.chart.sqlQuery, spec: args.chart.vegaLiteSpec }) })] }))] })) : (_jsx("pre", { children: JSON.stringify(args, null, 2) }))] }, toolCallId));
6
+ import React from 'react';
7
+ /**
8
+ * Type guard to check if an argument object matches QueryToolParameters
9
+ * @param {unknown} args - The arguments to check
10
+ * @returns {boolean} True if args matches QueryToolParameters structure
11
+ */
12
+ export function isQueryToolParameters(args) {
13
+ return (typeof args === 'object' &&
14
+ args !== null &&
15
+ 'reasoning' in args &&
16
+ 'sqlQuery' in args);
15
17
  }
18
+ /**
19
+ * Type guard to check if an argument object matches ChartToolParameters
20
+ * @param {unknown} args - The arguments to check
21
+ * @returns {boolean} True if args matches ChartToolParameters structure
22
+ */
23
+ export function isChartToolParameters(args) {
24
+ return typeof args === 'object' && args !== null && 'vegaLiteSpec' in args;
25
+ }
26
+ /**
27
+ * Renders a SQL query tool call with reasoning and query text
28
+ * @param {QueryToolCallProps} props - The component props
29
+ * @returns {JSX.Element} The rendered query tool call
30
+ */
31
+ export const QueryToolCall = React.memo(function QueryToolCall({ reasoning, sqlQuery, customMessage, }) {
32
+ return (_jsxs("div", { className: "flex flex-col gap-5", children: [_jsx("div", { className: "text-xs text-gray-400", children: reasoning }), _jsx("div", { className: "font-mono", children: sqlQuery }), customMessage] }));
33
+ });
34
+ /**
35
+ * Renders a chart tool call with visualization using Vega-Lite
36
+ * @param {ChartToolParameters} props - The component props
37
+ * @returns {JSX.Element} The rendered chart tool call
38
+ */
39
+ export const ChartToolCall = React.memo(function ChartToolCall({ reasoning, sqlQuery, vegaLiteSpec, }) {
40
+ return (_jsxs("div", { className: "flex flex-col gap-5", children: [_jsx("div", { className: "text-sm", children: _jsx(Markdown, { children: reasoning }) }), vegaLiteSpec && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("div", { className: "text-xs text-muted-foreground font-mono", children: sqlQuery }), _jsx(Suspense, { fallback: _jsx("div", { className: "w-full h-full flex items-center justify-center", children: _jsx(Spinner, { className: "w-4 h-4" }) }), children: _jsx(VegaLiteChart, { width: 400, height: 250, sqlQuery: sqlQuery, spec: vegaLiteSpec }) })] }))] }));
41
+ });
42
+ /**
43
+ * Type guard to check if a result object matches AnalysisAnswerProps
44
+ * @param {unknown} result - The result to check
45
+ * @returns {boolean} True if result matches AnalysisAnswerProps structure
46
+ */
47
+ export function isAnalysisAnswer(result) {
48
+ return result.success && result.data.analysis !== undefined;
49
+ }
50
+ /**
51
+ * Renders an analysis answer with markdown content of the final streaming response.
52
+ *
53
+ * @param {AnalysisAnswerProps} props - The component props. See {@link AnalysisAnswerProps} for more details.
54
+ * @returns {JSX.Element} The rendered answer tool call
55
+ */
56
+ export const AnalysisAnswer = React.memo(function AnalysisAnswer(props) {
57
+ return (_jsx("div", { className: "flex flex-col gap-5", children: _jsx("div", { className: "text-xs", children: _jsx(Markdown, { className: "whitespace-pre-wrap break-words", children: props.data.analysis }) }) }));
58
+ });
16
59
  //# sourceMappingURL=ToolCall.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToolCall.js","sourceRoot":"","sources":["../src/ToolCall.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,KAAK,EACL,MAAM,EACN,OAAO,EACP,cAAc,EACd,cAAc,EACd,OAAO,EACP,EAAE,GACH,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAC;AACtC,OAAO,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAC;AAC/B,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAM7C,MAAM,UAAU,QAAQ,CAAC,EAAC,QAAQ,EAAgB;IAChD,MAAM,EAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAC,GAAG,QAAQ,CAAC;IAC9C,MAAM,EAAC,IAAI,EAAC,GAAG,IAAI,CAAC;IAEpB,OAAO,CACL,eAEE,SAAS,EAAE,EAAE,CACX,8GAA8G,EAC9G;YACE,kBAAkB,EAAE,QAAQ,KAAK,QAAQ;SAC1C,CACF,aAED,KAAC,KAAK,IACJ,OAAO,EAAC,WAAW,EACnB,SAAS,EAAE,EAAE,CACX,sEAAsE,EACtE;oBACE,kBAAkB,EAAE,QAAQ,KAAK,QAAQ;iBAC1C,CACF,YAEA,QAAQ,GACH,EAER,cAAK,SAAS,EAAC,wBAAwB,YACrC,MAAC,OAAO,eACN,KAAC,cAAc,IAAC,OAAO,kBACrB,KAAC,MAAM,IAAC,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,MAAM,EAAC,SAAS,EAAC,SAAS,YACvD,KAAC,QAAQ,IAAC,SAAS,EAAC,SAAS,GAAG,GACzB,GACM,EACjB,KAAC,cAAc,IACb,SAAS,EAAC,uCAAuC,EACjD,KAAK,EAAC,OAAO,EACb,IAAI,EAAC,OAAO,YAEZ,cAAK,SAAS,EAAC,SAAS,YAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAO,GACnD,IACT,GACN,EACL,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAClB,eAAK,SAAS,EAAC,qBAAqB,aAClC,cAAK,SAAS,EAAC,0CAA0C,YACtD,IAAI,CAAC,SAAS,GACX,EACN,cAAK,SAAS,EAAC,WAAW,YAAE,IAAI,CAAC,QAAQ,GAAO,IAC5C,CACP,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CACtB,eAAK,SAAS,EAAC,qBAAqB,aAClC,cAAK,SAAS,EAAC,SAAS,YACtB,KAAC,QAAQ,cAAE,IAAI,CAAC,MAAM,GAAY,GAC9B,EACL,IAAI,CAAC,KAAK,IAAI,CACb,eAAK,SAAS,EAAC,4BAA4B,aACzC,cAAK,SAAS,EAAC,yCAAyC,YACrD,IAAI,CAAC,KAAK,CAAC,QAAQ,GAChB,EACN,KAAC,QAAQ,IACP,QAAQ,EACN,cAAK,SAAS,EAAC,gDAAgD,YAC7D,KAAC,OAAO,IAAC,SAAS,EAAC,SAAS,GAAG,GAC3B,YAGR,KAAC,aAAa,IACZ,SAAS,EAAC,eAAe,EACzB,WAAW,EAAE,EAAE,GAAG,CAAC,EACnB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,EAC7B,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,GAC7B,GACO,IACP,CACP,IACG,CACP,CAAC,CAAC,CAAC,CACF,wBAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAO,CAC3C,KAxEI,UAAU,CAyEX,CACP,CAAC;AACJ,CAAC","sourcesContent":["import {ToolCallSchema} from './schemas';\nimport {\n Badge,\n Button,\n Popover,\n PopoverContent,\n PopoverTrigger,\n Spinner,\n cn,\n} from '@sqlrooms/ui';\nimport {CodeIcon} from 'lucide-react';\nimport {Suspense} from 'react';\nimport Markdown from 'react-markdown';\nimport {VegaLiteChart} from '@sqlrooms/vega';\n\ninterface ToolCallProps {\n toolCall: ToolCallSchema;\n}\n\nexport function ToolCall({toolCall}: ToolCallProps) {\n const {args, toolName, toolCallId} = toolCall;\n const {type} = args;\n\n return (\n <div\n key={toolCallId}\n className={cn(\n 'border-2 relative px-5 py-6 rounded-md dark:bg-gray-900 dark:text-gray-300 bg-gray-100 text-gray-700 text-xs',\n {\n ' border-blue-500': toolName === 'answer',\n },\n )}\n >\n <Badge\n variant=\"secondary\"\n className={cn(\n 'text-xs absolute top-[-12px] left-2 dark:text-gray-100 text-gray-700',\n {\n ' border-blue-500': toolName === 'answer',\n },\n )}\n >\n {toolName}\n </Badge>\n\n <div className=\"absolute top-2 right-2\">\n <Popover>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" size=\"icon\" className=\"w-6 h-6\">\n <CodeIcon className=\"w-4 h-4\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className=\"w-[400px] max-h-[400px] overflow-auto\"\n align=\"start\"\n side=\"right\"\n >\n <pre className=\"text-xs\">{JSON.stringify(toolCall, null, 2)}</pre>\n </PopoverContent>\n </Popover>\n </div>\n {type === 'query' ? (\n <div className=\"flex flex-col gap-5\">\n <div className=\"text-xs text-gray-700 dark:text-gray-400\">\n {args.reasoning}\n </div>\n <div className=\"font-mono\">{args.sqlQuery}</div>\n </div>\n ) : type === 'answer' ? (\n <div className=\"flex flex-col gap-5\">\n <div className=\"text-sm\">\n <Markdown>{args.answer}</Markdown>\n </div>\n {args.chart && (\n <div className=\"flex flex-col gap-2 w-full\">\n <div className=\"text-xs text-muted-foreground font-mono\">\n {args.chart.sqlQuery}\n </div>\n <Suspense\n fallback={\n <div className=\"w-full h-full flex items-center justify-center\">\n <Spinner className=\"w-4 h-4\" />\n </div>\n }\n >\n <VegaLiteChart\n className=\"max-w-[600px]\"\n aspectRatio={16 / 9}\n sqlQuery={args.chart.sqlQuery}\n spec={args.chart.vegaLiteSpec}\n />\n </Suspense>\n </div>\n )}\n </div>\n ) : (\n <pre>{JSON.stringify(args, null, 2)}</pre>\n )}\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"ToolCall.js","sourceRoot":"","sources":["../src/ToolCall.tsx"],"names":[],"mappings":";AAKA,OAAO,EAAC,OAAO,EAAC,MAAM,cAAc,CAAC;AACrC,OAAO,EAAC,QAAQ,EAAY,MAAM,OAAO,CAAC;AAC1C,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAC;AAY1B;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAa;IAEb,OAAO,CACL,OAAO,IAAI,KAAK,QAAQ;QACxB,IAAI,KAAK,IAAI;QACb,WAAW,IAAI,IAAI;QACnB,UAAU,IAAI,IAAI,CACnB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAa;IAEb,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,cAAc,IAAI,IAAI,CAAC;AAC7E,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,aAAa,CAAC,EAC7D,SAAS,EACT,QAAQ,EACR,aAAa,GACM;IACnB,OAAO,CACL,eAAK,SAAS,EAAC,qBAAqB,aAClC,cAAK,SAAS,EAAC,uBAAuB,YAAE,SAAS,GAAO,EACxD,cAAK,SAAS,EAAC,WAAW,YAAE,QAAQ,GAAO,EAC1C,aAAa,IACV,CACP,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,aAAa,CAAC,EAC7D,SAAS,EACT,QAAQ,EACR,YAAY,GACQ;IACpB,OAAO,CACL,eAAK,SAAS,EAAC,qBAAqB,aAClC,cAAK,SAAS,EAAC,SAAS,YACtB,KAAC,QAAQ,cAAE,SAAS,GAAY,GAC5B,EACL,YAAY,IAAI,CACf,eAAK,SAAS,EAAC,qBAAqB,aAClC,cAAK,SAAS,EAAC,yCAAyC,YACrD,QAAQ,GACL,EACN,KAAC,QAAQ,IACP,QAAQ,EACN,cAAK,SAAS,EAAC,gDAAgD,YAC7D,KAAC,OAAO,IAAC,SAAS,EAAC,SAAS,GAAG,GAC3B,YAGR,KAAC,aAAa,IACZ,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,GAAG,EACX,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,YAAY,GAClB,GACO,IACP,CACP,IACG,CACP,CAAC;AACJ,CAAC,CAAC,CAAC;AAcH;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAkC;IAElC,OAAO,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC;AAC9D,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,cAAc,CAC9D,KAA0B;IAE1B,OAAO,CACL,cAAK,SAAS,EAAC,qBAAqB,YAClC,cAAK,SAAS,EAAC,SAAS,YACtB,KAAC,QAAQ,IAAC,SAAS,EAAC,iCAAiC,YAClD,KAAK,CAAC,IAAI,CAAC,QAAQ,GACX,GACP,GACF,CACP,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import {\n ChartToolParameters,\n QueryToolParameters,\n ToolResultSchema,\n} from './schemas';\nimport {Spinner} from '@sqlrooms/ui';\nimport {Suspense, ReactNode} from 'react';\nimport Markdown from 'react-markdown';\nimport {VegaLiteChart} from '@sqlrooms/vega';\nimport React from 'react';\n\n/**\n * Props for the QueryToolCall component\n * @interface QueryToolCallProps\n * @extends {QueryToolParameters}\n * @property {ReactNode} [customMessage] - Optional custom message to display with the query\n */\ntype QueryToolCallProps = QueryToolParameters & {\n customMessage?: ReactNode;\n};\n\n/**\n * Type guard to check if an argument object matches QueryToolParameters\n * @param {unknown} args - The arguments to check\n * @returns {boolean} True if args matches QueryToolParameters structure\n */\nexport function isQueryToolParameters(\n args: unknown,\n): args is QueryToolParameters {\n return (\n typeof args === 'object' &&\n args !== null &&\n 'reasoning' in args &&\n 'sqlQuery' in args\n );\n}\n\n/**\n * Type guard to check if an argument object matches ChartToolParameters\n * @param {unknown} args - The arguments to check\n * @returns {boolean} True if args matches ChartToolParameters structure\n */\nexport function isChartToolParameters(\n args: unknown,\n): args is ChartToolParameters {\n return typeof args === 'object' && args !== null && 'vegaLiteSpec' in args;\n}\n\n/**\n * Renders a SQL query tool call with reasoning and query text\n * @param {QueryToolCallProps} props - The component props\n * @returns {JSX.Element} The rendered query tool call\n */\nexport const QueryToolCall = React.memo(function QueryToolCall({\n reasoning,\n sqlQuery,\n customMessage,\n}: QueryToolCallProps) {\n return (\n <div className=\"flex flex-col gap-5\">\n <div className=\"text-xs text-gray-400\">{reasoning}</div>\n <div className=\"font-mono\">{sqlQuery}</div>\n {customMessage}\n </div>\n );\n});\n\n/**\n * Renders a chart tool call with visualization using Vega-Lite\n * @param {ChartToolParameters} props - The component props\n * @returns {JSX.Element} The rendered chart tool call\n */\nexport const ChartToolCall = React.memo(function ChartToolCall({\n reasoning,\n sqlQuery,\n vegaLiteSpec,\n}: ChartToolParameters) {\n return (\n <div className=\"flex flex-col gap-5\">\n <div className=\"text-sm\">\n <Markdown>{reasoning}</Markdown>\n </div>\n {vegaLiteSpec && (\n <div className=\"flex flex-col gap-2\">\n <div className=\"text-xs text-muted-foreground font-mono\">\n {sqlQuery}\n </div>\n <Suspense\n fallback={\n <div className=\"w-full h-full flex items-center justify-center\">\n <Spinner className=\"w-4 h-4\" />\n </div>\n }\n >\n <VegaLiteChart\n width={400}\n height={250}\n sqlQuery={sqlQuery}\n spec={vegaLiteSpec}\n />\n </Suspense>\n </div>\n )}\n </div>\n );\n});\n\n/**\n * Type guard to check if a result object matches AnalysisAnswerProps\n * @param {unknown} result - The result to check\n * @returns {boolean} True if result matches AnalysisAnswerProps structure\n */\ntype AnalysisAnswerProps = {\n success: true;\n data: {\n analysis: string;\n };\n};\n\n/**\n * Type guard to check if a result object matches AnalysisAnswerProps\n * @param {unknown} result - The result to check\n * @returns {boolean} True if result matches AnalysisAnswerProps structure\n */\nexport function isAnalysisAnswer(\n result: ToolResultSchema['result'],\n): result is AnalysisAnswerProps {\n return result.success && result.data.analysis !== undefined;\n}\n\n/**\n * Renders an analysis answer with markdown content of the final streaming response.\n *\n * @param {AnalysisAnswerProps} props - The component props. See {@link AnalysisAnswerProps} for more details.\n * @returns {JSX.Element} The rendered answer tool call\n */\nexport const AnalysisAnswer = React.memo(function AnalysisAnswer(\n props: AnalysisAnswerProps,\n) {\n return (\n <div className=\"flex flex-col gap-5\">\n <div className=\"text-xs\">\n <Markdown className=\"whitespace-pre-wrap break-words\">\n {props.data.analysis}\n </Markdown>\n </div>\n </div>\n );\n});\n"]}
@@ -1,6 +1,9 @@
1
1
  import { ToolResultSchema } from './schemas';
2
+ import { ReactNode } from 'react';
3
+ import React from 'react';
2
4
  interface ToolResultProps {
3
5
  toolResult: ToolResultSchema;
6
+ customMessage?: ReactNode;
4
7
  }
5
8
  export declare const ToolResult: React.FC<ToolResultProps>;
6
9
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"ToolResult.d.ts","sourceRoot":"","sources":["../src/ToolResult.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAW3C,UAAU,eAAe;IACvB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA2DhD,CAAC"}
1
+ {"version":3,"file":"ToolResult.d.ts","sourceRoot":"","sources":["../src/ToolResult.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAU3C,OAAO,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAQhC,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,UAAU,eAAe;IACvB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,aAAa,CAAC,EAAE,SAAS,CAAC;CAC3B;AAkBD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAqEhD,CAAC"}
@@ -1,9 +1,26 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Badge, Button, Popover, PopoverContent, PopoverTrigger, cn, } from '@sqlrooms/ui';
3
3
  import { CheckCircle2Icon, CodeIcon, XCircleIcon } from 'lucide-react';
4
- export const ToolResult = ({ toolResult }) => {
4
+ import { AnalysisAnswer, ChartToolCall, isAnalysisAnswer, isChartToolParameters, } from './ToolCall';
5
+ import { isQueryToolParameters, QueryToolCall } from './ToolCall';
6
+ function getBorderColor(isSuccess, toolName) {
7
+ if (!isSuccess) {
8
+ return 'border-red-500';
9
+ }
10
+ switch (toolName) {
11
+ case 'query':
12
+ return 'border-green-500';
13
+ case 'chart':
14
+ return 'border-blue-500';
15
+ case 'answer':
16
+ return 'border-gray-500';
17
+ default:
18
+ return 'border-gray-500';
19
+ }
20
+ }
21
+ export const ToolResult = ({ toolResult, customMessage, }) => {
5
22
  const { toolName, args, result } = toolResult;
6
23
  const isSuccess = result.success;
7
- return (_jsxs("div", { className: cn('border-2 relative bg-gray-100 dark:bg-gray-900 px-5 py-6 rounded-md text-gray-700 dark:text-gray-300 text-xs', isSuccess ? 'border-green-500' : 'border-red-500'), children: [_jsxs(Badge, { variant: "secondary", className: cn('text-xs absolute top-[-12px] left-2 dark:text-gray-100 text-gray-700 flex items-center gap-1 border', isSuccess ? 'border-green-500' : 'border-red-500'), children: [isSuccess ? (_jsx(CheckCircle2Icon, { className: "w-3 h-3 text-green-500" })) : (_jsx(XCircleIcon, { className: "w-3 h-3 text-red-500" })), toolName] }), _jsx("div", { className: "absolute top-2 right-2", children: _jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", size: "icon", className: "w-6 h-6", children: _jsx(CodeIcon, { className: "w-4 h-4" }) }) }), _jsx(PopoverContent, { className: "w-[400px] max-h-[300px] overflow-auto p-4", side: "right", align: "start", children: _jsx("pre", { className: "whitespace-no-wrap text-xs", children: JSON.stringify(toolResult, null, 2) }) })] }) }), _jsxs("div", { className: "flex flex-col gap-5", children: [args.reasoning && (_jsx("div", { className: "text-xs text-gray-400", children: args.reasoning })), args.sqlQuery && _jsx("div", { className: "font-mono", children: args.sqlQuery }), !result.success && (_jsxs("div", { className: "text-red-500 gap-2 flex flex-col", children: [_jsx("p", { className: "text-sm font-bold", children: "Oops! Something went wrong..." }), _jsx("p", { className: "text-xs", children: result.error })] }))] })] }));
24
+ return (_jsxs("div", { className: cn('border-2 relative bg-gray-100 dark:bg-gray-900 px-5 py-6 rounded-md text-gray-700 dark:text-gray-300 text-xs', getBorderColor(isSuccess, toolName)), children: [_jsxs(Badge, { variant: "secondary", className: cn('text-xs absolute top-[-12px] left-2 dark:text-gray-100 text-gray-700 flex items-center gap-1 border', getBorderColor(isSuccess, toolName)), children: [isSuccess ? (_jsx(CheckCircle2Icon, { className: "w-3 h-3 text-green-500" })) : (_jsx(XCircleIcon, { className: "w-3 h-3 text-red-500" })), toolName] }), _jsx("div", { className: "absolute top-2 right-2", children: _jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", size: "icon", className: "w-6 h-6", children: _jsx(CodeIcon, { className: "w-4 h-4" }) }) }), _jsx(PopoverContent, { className: "w-[400px] max-h-[300px] overflow-auto p-4", side: "right", align: "start", children: _jsx("pre", { className: "whitespace-no-wrap text-xs", children: JSON.stringify(toolResult, null, 2) }) })] }) }), _jsxs("div", { className: "flex flex-col gap-5", children: [toolName === 'query' && isQueryToolParameters(args) ? (_jsx(QueryToolCall, { ...args, customMessage: customMessage })) : toolName === 'chart' && isChartToolParameters(args) ? (_jsx(ChartToolCall, { ...args })) : toolName === 'answer' && isAnalysisAnswer(result) ? (_jsx(AnalysisAnswer, { ...result })) : (Object.keys(args).length > 0 && (_jsx("pre", { children: JSON.stringify(args, null, 2) }))), !result.success && (_jsxs("div", { className: "text-red-500 gap-2 flex flex-col", children: [_jsx("p", { className: "text-sm font-bold", children: "Oops! Something went wrong..." }), _jsx("p", { className: "text-xs", children: result.error })] }))] })] }));
8
25
  };
9
26
  //# sourceMappingURL=ToolResult.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToolResult.js","sourceRoot":"","sources":["../src/ToolResult.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,KAAK,EACL,MAAM,EACN,OAAO,EACP,cAAc,EACd,cAAc,EACd,EAAE,GACH,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,gBAAgB,EAAE,QAAQ,EAAE,WAAW,EAAC,MAAM,cAAc,CAAC;AAMrE,MAAM,CAAC,MAAM,UAAU,GAA8B,CAAC,EAAC,UAAU,EAAC,EAAE,EAAE;IACpE,MAAM,EAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAC,GAAG,UAAU,CAAC;IAC5C,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC;IAEjC,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,8GAA8G,EAC9G,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,gBAAgB,CAClD,aAED,MAAC,KAAK,IACJ,OAAO,EAAC,WAAW,EACnB,SAAS,EAAE,EAAE,CACX,qGAAqG,EACrG,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,gBAAgB,CAClD,aAEA,SAAS,CAAC,CAAC,CAAC,CACX,KAAC,gBAAgB,IAAC,SAAS,EAAC,wBAAwB,GAAG,CACxD,CAAC,CAAC,CAAC,CACF,KAAC,WAAW,IAAC,SAAS,EAAC,sBAAsB,GAAG,CACjD,EACA,QAAQ,IACH,EAER,cAAK,SAAS,EAAC,wBAAwB,YACrC,MAAC,OAAO,eACN,KAAC,cAAc,IAAC,OAAO,kBACrB,KAAC,MAAM,IAAC,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,MAAM,EAAC,SAAS,EAAC,SAAS,YACvD,KAAC,QAAQ,IAAC,SAAS,EAAC,SAAS,GAAG,GACzB,GACM,EACjB,KAAC,cAAc,IACb,SAAS,EAAC,2CAA2C,EACrD,IAAI,EAAC,OAAO,EACZ,KAAK,EAAC,OAAO,YAEb,cAAK,SAAS,EAAC,4BAA4B,YACxC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,GAChC,GACS,IACT,GACN,EAEN,eAAK,SAAS,EAAC,qBAAqB,aACjC,IAAI,CAAC,SAAS,IAAI,CACjB,cAAK,SAAS,EAAC,uBAAuB,YAAE,IAAI,CAAC,SAAS,GAAO,CAC9D,EACA,IAAI,CAAC,QAAQ,IAAI,cAAK,SAAS,EAAC,WAAW,YAAE,IAAI,CAAC,QAAQ,GAAO,EACjE,CAAC,MAAM,CAAC,OAAO,IAAI,CAClB,eAAK,SAAS,EAAC,kCAAkC,aAC/C,YAAG,SAAS,EAAC,mBAAmB,8CAAkC,EAClE,YAAG,SAAS,EAAC,SAAS,YAAE,MAAM,CAAC,KAAK,GAAK,IACrC,CACP,IACG,IACF,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {ToolResultSchema} from './schemas';\nimport {\n Badge,\n Button,\n Popover,\n PopoverContent,\n PopoverTrigger,\n cn,\n} from '@sqlrooms/ui';\nimport {CheckCircle2Icon, CodeIcon, XCircleIcon} from 'lucide-react';\n\ninterface ToolResultProps {\n toolResult: ToolResultSchema;\n}\n\nexport const ToolResult: React.FC<ToolResultProps> = ({toolResult}) => {\n const {toolName, args, result} = toolResult;\n const isSuccess = result.success;\n\n return (\n <div\n className={cn(\n 'border-2 relative bg-gray-100 dark:bg-gray-900 px-5 py-6 rounded-md text-gray-700 dark:text-gray-300 text-xs',\n isSuccess ? 'border-green-500' : 'border-red-500',\n )}\n >\n <Badge\n variant=\"secondary\"\n className={cn(\n 'text-xs absolute top-[-12px] left-2 dark:text-gray-100 text-gray-700 flex items-center gap-1 border',\n isSuccess ? 'border-green-500' : 'border-red-500',\n )}\n >\n {isSuccess ? (\n <CheckCircle2Icon className=\"w-3 h-3 text-green-500\" />\n ) : (\n <XCircleIcon className=\"w-3 h-3 text-red-500\" />\n )}\n {toolName}\n </Badge>\n\n <div className=\"absolute top-2 right-2\">\n <Popover>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" size=\"icon\" className=\"w-6 h-6\">\n <CodeIcon className=\"w-4 h-4\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className=\"w-[400px] max-h-[300px] overflow-auto p-4\"\n side=\"right\"\n align=\"start\"\n >\n <pre className=\"whitespace-no-wrap text-xs\">\n {JSON.stringify(toolResult, null, 2)}\n </pre>\n </PopoverContent>\n </Popover>\n </div>\n\n <div className=\"flex flex-col gap-5\">\n {args.reasoning && (\n <div className=\"text-xs text-gray-400\">{args.reasoning}</div>\n )}\n {args.sqlQuery && <div className=\"font-mono\">{args.sqlQuery}</div>}\n {!result.success && (\n <div className=\"text-red-500 gap-2 flex flex-col\">\n <p className=\"text-sm font-bold\">Oops! Something went wrong...</p>\n <p className=\"text-xs\">{result.error}</p>\n </div>\n )}\n </div>\n </div>\n );\n};\n"]}
1
+ {"version":3,"file":"ToolResult.js","sourceRoot":"","sources":["../src/ToolResult.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ToolResultSchema} from './schemas';\nimport {\n Badge,\n Button,\n Popover,\n PopoverContent,\n PopoverTrigger,\n cn,\n} from '@sqlrooms/ui';\nimport {CheckCircle2Icon, CodeIcon, XCircleIcon} from 'lucide-react';\nimport {ReactNode} from 'react';\nimport {\n AnalysisAnswer,\n ChartToolCall,\n isAnalysisAnswer,\n isChartToolParameters,\n} from './ToolCall';\nimport {isQueryToolParameters, QueryToolCall} from './ToolCall';\nimport React from 'react';\n\ninterface ToolResultProps {\n toolResult: ToolResultSchema;\n customMessage?: ReactNode;\n}\n\nfunction getBorderColor(isSuccess: boolean, toolName: string) {\n if (!isSuccess) {\n return 'border-red-500';\n }\n switch (toolName) {\n case 'query':\n return 'border-green-500';\n case 'chart':\n return 'border-blue-500';\n case 'answer':\n return 'border-gray-500';\n default:\n return 'border-gray-500';\n }\n}\n\nexport const ToolResult: React.FC<ToolResultProps> = ({\n toolResult,\n customMessage,\n}) => {\n const {toolName, args, result} = toolResult;\n const isSuccess = result.success;\n\n return (\n <div\n className={cn(\n 'border-2 relative bg-gray-100 dark:bg-gray-900 px-5 py-6 rounded-md text-gray-700 dark:text-gray-300 text-xs',\n getBorderColor(isSuccess, toolName),\n )}\n >\n <Badge\n variant=\"secondary\"\n className={cn(\n 'text-xs absolute top-[-12px] left-2 dark:text-gray-100 text-gray-700 flex items-center gap-1 border',\n getBorderColor(isSuccess, toolName),\n )}\n >\n {isSuccess ? (\n <CheckCircle2Icon className=\"w-3 h-3 text-green-500\" />\n ) : (\n <XCircleIcon className=\"w-3 h-3 text-red-500\" />\n )}\n {toolName}\n </Badge>\n\n <div className=\"absolute top-2 right-2\">\n <Popover>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" size=\"icon\" className=\"w-6 h-6\">\n <CodeIcon className=\"w-4 h-4\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className=\"w-[400px] max-h-[300px] overflow-auto p-4\"\n side=\"right\"\n align=\"start\"\n >\n <pre className=\"whitespace-no-wrap text-xs\">\n {JSON.stringify(toolResult, null, 2)}\n </pre>\n </PopoverContent>\n </Popover>\n </div>\n\n <div className=\"flex flex-col gap-5\">\n {toolName === 'query' && isQueryToolParameters(args) ? (\n <QueryToolCall {...args} customMessage={customMessage} />\n ) : toolName === 'chart' && isChartToolParameters(args) ? (\n <ChartToolCall {...args} />\n ) : toolName === 'answer' && isAnalysisAnswer(result) ? (\n <AnalysisAnswer {...result} />\n ) : (\n Object.keys(args).length > 0 && (\n <pre>{JSON.stringify(args, null, 2)}</pre>\n )\n )}\n {!result.success && (\n <div className=\"text-red-500 gap-2 flex flex-col\">\n <p className=\"text-sm font-bold\">Oops! Something went wrong...</p>\n <p className=\"text-xs\">{result.error}</p>\n </div>\n )}\n </div>\n </div>\n );\n};\n"]}