diff --git a/examples/fast-api-server/library/piedpiper/spend_all_product.yaml b/examples/fast-api-server/library/piedpiper/spend_all_product.yaml
new file mode 100644
index 0000000..0e8049c
--- /dev/null
+++ b/examples/fast-api-server/library/piedpiper/spend_all_product.yaml
@@ -0,0 +1,29 @@
+display_name: Spend all products
+description: |
+ Show the spend for all products (top 5)
+sample_questions:
+ - show spend for all products
+parameters:
+ json_schema:
+ type: object
+ properties: {}
+ required: []
+type: DuckDBQueryFunction
+dataset: 'dummy-data/'
+sqls:
+ - |
+ SELECT purchases.product, SUM(purchases.amount) as amount,
+ FROM "purchases.csv"
+ GROUP BY purchases.product
+ ORDER BY amount DESC
+ LIMIT 5
+visualizations:
+ - |
+ def create_chart_config(dataframes):
+ df = dataframes[0]
+ data = [{'x': df.iloc[:, 0].tolist(), 'y': df.iloc[:, 1].tolist(), 'type': 'bar'}]
+ layout = {'title': 'Spend per Product', 'xaxis': {'title': 'Product'}, 'yaxis': {'title': 'Total Spend'}}
+ result = {'data': data, 'layout': layout}
+
+ return result
+summarization: 'Describe the spend per product'
diff --git a/examples/fast-api-server/library/piedpiper/spend_per_product.yaml b/examples/fast-api-server/library/piedpiper/spend_per_product.yaml
deleted file mode 100644
index 654eb32..0000000
--- a/examples/fast-api-server/library/piedpiper/spend_per_product.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-display_name: Spend per product
-description: |
- Show the spend per product (top 5)
-sample_questions:
-- show spend per product
-parameters:
- json_schema:
- type: object
- properties: {}
- required: []
-type: DuckDBQueryFunction
-dataset: "dummy-data/"
-sqls:
-- |
- SELECT purchases.product, SUM(purchases.amount) as amount,
- FROM "purchases.csv"
- GROUP BY purchases.product
- ORDER BY amount DESC
- LIMIT 5
-visualizations:
- - |
- def create_chart_config(dataframes):
- df = dataframes[0]
- data = [{'x': df.iloc[:, 0].tolist(), 'y': df.iloc[:, 1].tolist(), 'type': 'bar'}]
- layout = {'title': 'Spend per Product', 'xaxis': {'title': 'Product'}, 'yaxis': {'title': 'Total Spend'}}
- result = {'data': data, 'layout': layout}
-
- return result
-summarization: 'Describe the spend per product'
diff --git a/examples/next/src/app/page.tsx b/examples/next/src/app/page.tsx
index 712949d..91d2e8c 100644
--- a/examples/next/src/app/page.tsx
+++ b/examples/next/src/app/page.tsx
@@ -8,9 +8,8 @@ export default function Home() {
{
+ return (
+
+
{children};
+ },
+ code({ node, inline, className, children, ...props }) {
+ if (children.length) {
+ if (children[0] == '▍') {
+ return (
+ ▍
+ );
+ }
+
+ children[0] = (children[0] as string).replace('`▍`', '▍');
+ }
+
+ const match = /language-(\w+)/.exec(className || '');
+
+ if (inline) {
+ return (
+
+ {children}
+
+ );
+ }
+ return (
+
+ {
+
+ }
+
+ );
+ },
+ }}
+ >
+ {content}
+
+
+ );
+};
diff --git a/packages/openassistants-react/src/components/chat-list.tsx b/packages/openassistants-react/src/components/chat-list.tsx
index b5d4ad3..3e0542e 100644
--- a/packages/openassistants-react/src/components/chat-list.tsx
+++ b/packages/openassistants-react/src/components/chat-list.tsx
@@ -33,62 +33,17 @@ export function OpenAssistantsChatList({
return (
{messages.map((message, index) => {
- const outputs = 'outputs' in message ? message.outputs : null;
return (
- {outputs &&
- outputs.map((output, i) => {
- console.log(
- `output.type=${output.type} output=${JSON.stringify(output)}`
- );
- return (
-
- {output.type === 'dataframe' && (
-
- )}
- {output.type === 'visualization' && (
-
- )}
-
- );
- })}
-
- {getContent(message) && (
-
- onEdit(index, m)}
- isLoading={isLoading}
- />
- {index < messages.length - 1 && }
-
- )}
-
- {message.role === 'assistant' && message.input_request && (
-
{
- const newMessage = {
- role: 'user' as const,
- input_response: {
- name: message.input_request!.name,
- arguments: values,
- },
- content: '',
- };
- append(newMessage);
- }}
- >
- )}
+
onEdit(index, m)}
+ isLoading={isLoading}
+ />
);
})}
diff --git a/packages/openassistants-react/src/components/chat-message.tsx b/packages/openassistants-react/src/components/chat-message.tsx
index 7dbe111..eba474e 100644
--- a/packages/openassistants-react/src/components/chat-message.tsx
+++ b/packages/openassistants-react/src/components/chat-message.tsx
@@ -1,18 +1,21 @@
-import remarkGfm from 'remark-gfm';
-
import { cn } from '../lib/utils';
-import { CodeBlock } from './ui/codeblock';
-import { MemoizedReactMarkdown } from './markdown';
import { IconArrowRight, IconDefinitive, IconUser } from './ui/icons';
import { ChatMessageAction, ChatMessageActions } from './chat-message-actions';
import { useState } from 'react';
import React from 'react';
import { Message } from './chat-models';
-import { Button, Textarea } from './ui';
+import { Button, Separator, Textarea } from './ui';
+import { DataframeTable } from './dataframe-table';
+import { RenderVisMessage } from './plotly-vis';
+import { FunctionForm } from './function-form';
+import { ChatContent } from './chat-content';
export interface DefinitiveChatMessageProps {
+ index: number;
+ total: number;
message: Message;
onEdit?: (message: Message) => Promise
;
+ append: (message?: Message) => Promise;
isLoading: boolean;
}
@@ -36,24 +39,7 @@ const isCollapsable = (message: Message): boolean => {
);
};
-function formatCode(input: string): string {
- const trimmed = input
- .replace('BEGIN_CODE', '')
- .replace('END_CODE', '')
- .trim();
- return trimmed
- .split(';')
- .map((line) => line.trim())
- .join(';\n');
-}
-
export const getContent = (message: Message): string => {
- if (message.role === 'function') {
- const textOutput =
- message.outputs.find((output) => output.type === 'text') || {};
- //@ts-ignore
- return 'text' in textOutput ? (textOutput.text as string) : '';
- }
if (message.role === 'user') {
if (message.content) {
return message.content;
@@ -61,11 +47,6 @@ export const getContent = (message: Message): string => {
return '';
}
}
- if (message.role === 'assistant') {
- if (message.function_call) {
- return '';
- }
- }
if (message.role === 'assistant' && message.content) {
return message.content;
}
@@ -73,14 +54,17 @@ export const getContent = (message: Message): string => {
};
export function OpenAssistantsChatMessage({
+ index,
+ total,
message,
onEdit,
isLoading,
+ append,
...props
}: DefinitiveChatMessageProps) {
const [isEditing, setIsEditing] = useState(false);
+ const [submitted, setSubmitted] = React.useState(false);
const [editedMessageContent, setEditedMessageContent] = useState('');
- const content = getContent(message);
const supportedActions: ChatMessageAction[] = [];
if (!isCollapsable(message) && !isEditing) {
supportedActions.push('copy');
@@ -88,127 +72,136 @@ export function OpenAssistantsChatMessage({
if (message.role === 'user' && !isEditing && onEdit) {
supportedActions.push('edit');
}
- if (!content) {
+ const outputs = 'outputs' in message ? message.outputs : null;
+ // executed function call- ignore
+ if (message.role === 'assistant' && message.function_call) {
+ return ;
+ }
+ // completed input response from user- ignore
+ if (message.role === 'user' && message.input_response) {
return ;
}
return (
-
+
- {message && getIcon(message)}
-
-
- {isEditing && message.role === 'user' && (
-
-
- )}
- {!isEditing && (
-
-
{children};
- },
- code({ node, inline, className, children, ...props }) {
- if (children.length) {
- if (children[0] == '▍') {
- return (
-
- ▍
-
- );
- }
-
- children[0] = (children[0] as string).replace('`▍`', '▍');
- }
-
- const match = /language-(\w+)/.exec(className || '');
-
- if (inline) {
- return (
-
- {children}
-
- );
- }
- return (
-
- {
-
- }
-
- );
- },
- }}
- >
- {content}
-
-
- {'content' in message && (
-
{
- setIsEditing(true);
- setEditedMessageContent(message.content || '');
+
+ {message && getIcon(message)}
+
+
+ {message.role === 'assistant' && message.input_request && (
+ // add padding
+
+ {
+ const newMessage = {
+ role: 'user' as const,
+ input_response: {
+ name: message.input_request!.name,
+ arguments: values,
+ },
+ content: '',
+ };
+ setSubmitted(true);
+ append(newMessage);
}}
- />
+ >
+
+ )}
+
+
+ {isEditing && message.role === 'user' && (
+
+
+ )}
+ {outputs &&
+ outputs.map((output, i) => {
+ return (
+
+ {output.type === 'dataframe' && (
+
+ )}
+ {output.type === 'visualization' && (
+
+ )}
+ {output.type === 'text' && !isEditing && (
+
+ )}
+
+ );
+ })}
+ {'content' in message && !submitted && (
+
+
+ {
+ setIsEditing(true);
+ setEditedMessageContent(message.content || '');
+ }}
+ />
+
)}
- )}
+
+ {index < total - 1 &&
}
);
}
diff --git a/packages/openassistants-react/src/components/dataframe-table.tsx b/packages/openassistants-react/src/components/dataframe-table.tsx
index 7fca73f..421a898 100644
--- a/packages/openassistants-react/src/components/dataframe-table.tsx
+++ b/packages/openassistants-react/src/components/dataframe-table.tsx
@@ -22,7 +22,9 @@ export const DataframeTable = ({
className="max-h-96 overflow-y-auto px-4 mx-auto overflow-x-auto"
style={{ maxWidth: '90vw' }}
>
-
{title}
+ {title && (
+
{title}
+ )}
{
message: AssistantMessage;
+ submitted: boolean;
onSubmit: (values: any) => void;
}
@@ -44,8 +45,11 @@ const SelectWidget = (props: WidgetProps) => {
);
};
-export function FunctionForm({ message, onSubmit }: FuncionFormProps) {
- const [submitted, setSubmitted] = React.useState(false);
+export function FunctionForm({
+ message,
+ submitted,
+ onSubmit,
+}: FuncionFormProps) {
const json_schema = message?.input_request?.json_schema;
const uiSchema = {};
// expand with more widgets
@@ -120,7 +124,6 @@ export function FunctionForm({ message, onSubmit }: FuncionFormProps) {
validator={validator}
onSubmit={(form) => {
onSubmit(form.formData);
- setSubmitted(true);
}}
onError={() => console.log('errors')}
/>
diff --git a/packages/openassistants-react/src/components/plotly-vis.tsx b/packages/openassistants-react/src/components/plotly-vis.tsx
index 9431786..25569f9 100644
--- a/packages/openassistants-react/src/components/plotly-vis.tsx
+++ b/packages/openassistants-react/src/components/plotly-vis.tsx
@@ -14,7 +14,6 @@ export const RenderVisMessage = ({
}: RenderVisMessageProps) => {
const Plotly =
typeof window !== 'undefined' ? require('plotly.js-dist-min') : null;
- console.log('Plotly', Plotly);
useEffect(() => {
if (!data || !layout) {
return;