Charts: User Fix.

This commit is contained in:
Enrico Ros
2024-09-25 01:40:58 -07:00
parent e7bd6f6746
commit 6d47b6024a
6 changed files with 72 additions and 17 deletions
@@ -22,21 +22,21 @@ const CodeFixes: Record<string, CodeFix> = {
// See `RenderCodeChartJS`
'chartjs-issue': {
description: 'Provides the corrected ChartJS configuration code.',
systemMessage: `You are an AI assistant that helps fix invalid ChartJS configuration JSON code.
When provided with invalid ChartJS code, you analyze it, identify errors, and output a corrected version in valid JSON format.
description: 'Provides the corrected Chart.js configuration code.',
systemMessage: `You are an AI assistant that helps fix invalid Chart.js configuration JSON code.
When provided with invalid Chart.js code, you analyze it, identify errors, and output a corrected version in valid JSON format.
Respond only by calling the \`{{functionName}}\` function.`,
userInstructionTemplate: `The following ChartJS configuration code is invalid and cannot be parsed:
userInstructionTemplate: `The following Chart.js configuration code is invalid and cannot be parsed:
\`\`\`json
{{codeToFix}}
\`\`\`
{{errorMessageSection}}
Please analyze the code, correct any errors, and provide a valid JSON configuration that can be parsed by ChartJS.
Please analyze the code, correct any errors, and provide a valid JSON configuration that can be parsed by Chart.js.
Call the function \`{{functionName}}\` once, providing the corrected code.`,
functionName: 'provide_corrected_chartjs_code',
outputSchema: z.object({
corrected_code: z.string().describe('The corrected ChartJS configuration code in valid JSON format.'),
corrected_code: z.string().describe('The corrected Chart.js configuration code in valid JSON format.'),
}),
},
@@ -70,13 +70,13 @@ export function aixRequireSingleFunctionCallInvocation(fragments: DMessageConten
if (!Array.isArray(fragments) || fragments.length !== 1) {
if (AIX_DEBUG_CLIENT_TOOLS)
console.error('[DEV] single-function-call: invalid fragments:', fragments, 'for', debugLabel);
throw new Error('AIX: Unexpected response for ' + debugLabel);
throw new Error('AIX: Unexpected response.');
}
if (!isContentFragment(fragments[0]) || fragments[0].part.pt !== 'tool_invocation') {
if (AIX_DEBUG_CLIENT_TOOLS)
console.error('[DEV] single-function-call: invalid fragment part:', fragments[0].part, 'for', debugLabel);
throw new Error('AIX: Missing tool invocation for ' + debugLabel);
throw new Error('AIX: Missing tool invocation.');
}
const { invocation } = fragments[0].part;
@@ -84,7 +84,7 @@ export function aixRequireSingleFunctionCallInvocation(fragments: DMessageConten
if (invocation.type !== 'function_call' || invocation.name !== expectedFunctionName) {
if (AIX_DEBUG_CLIENT_TOOLS)
console.error('[DEV] single-function-call: invalid invocation:', invocation, 'for', debugLabel);
throw new Error('AIX: Expected a function call to ' + expectedFunctionName + ' for ' + debugLabel);
throw new Error('AIX: Expected a function call.');
}
if (!invocation.args) {
+1 -1
View File
@@ -146,7 +146,7 @@ export function AutoBlocksRenderer(props: {
let enhancedStartCollapsed = false;
if (bkInput.title === BLOCK_CODE_VND_AGI_CHARTJS) {
disableEnhancedRender = false;
// For ChartJS charts, at the moment, we use the 'unwanted' refresh at the end of the message to start (that block) without collapse
// For Chart.js charts, at the moment, we use the 'unwanted' refresh at the end of the message to start (that block) without collapse
enhancedStartCollapsed = bkInput.isPartial;
}
+1 -1
View File
@@ -269,7 +269,7 @@ function RenderCodeImpl(props: RenderCodeBaseProps & {
</OverlayButton>
)}
{/* SVG, ChartJS, Mermaid, PlantUML -- including a max-out button */}
{/* SVG, Chart.js, Mermaid, PlantUML -- including a max-out button */}
{(isSVGCode || isChartJSCode || isMermaidCode || isPlantUMLCode) && (
<ButtonGroup aria-label='Diagram' sx={overlayGroupWithShadowSx}>
{/* Toggle rendering */}
@@ -1,7 +1,10 @@
import * as React from 'react';
import type { SxProps } from '@mui/joy/styles/types';
import { Box, Typography } from '@mui/joy';
import { Box, Button, Typography } from '@mui/joy';
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
import { useAgiFixupCode } from '~/modules/aifn/agicodefixup/useAgiFixupCode';
import { ChartConstructorType, fixupChartJSObject, useDynamicChartJS } from './useDynamicChartJS';
@@ -29,6 +32,7 @@ export function RenderCodeChartJS(props: {
// state
const [renderError, setRenderError] = React.useState<string | null>(null);
const [fixupError, setFixupError] = React.useState<string | null>(null);
const canvasRef = React.useRef<HTMLCanvasElement>(null);
const chartInstanceRef = React.useRef<ChartConstructorType | null>(null);
@@ -43,10 +47,13 @@ export function RenderCodeChartJS(props: {
fixupChartJSObject(config);
return { chartConfig: config, parseError: null };
} catch (error: any) {
return { chartConfig: null, parseError: 'Invalid Chart.js input: ' + (error.message || 'Unknown error.') };
return { chartConfig: null, parseError: error.message as string || 'Unknown error.' };
}
}, [props.chartJSCode]);
// AI functions
const { isFetching, refetch } = useAgiFixupCode('chartjs-issue', false, props.chartJSCode, parseResult.parseError);
// Rendering
React.useEffect(() => {
@@ -72,6 +79,24 @@ export function RenderCodeChartJS(props: {
}, [chartJS, parseResult.chartConfig]);
// handlers
const { onReplaceInCode } = props;
const handleChartRegenerate = React.useCallback(async () => {
if (!onReplaceInCode) return;
setFixupError(null);
refetch().then((result) => {
if (result.data)
onReplaceInCode(props.chartJSCode, result.data);
else if (result.error)
setFixupError(result.error.message || 'Unknown error.');
else
setFixupError('Unknown Fixup error.');
});
}, [onReplaceInCode, props.chartJSCode, refetch]);
// Handle all the non-chart states
switch (true) {
case isLibraryLoading:
@@ -80,8 +105,38 @@ export function RenderCodeChartJS(props: {
return null;
case !!loadingError:
return <Typography level='body-sm' color='danger'>{loadingError}</Typography>;
case !!parseResult.parseError:
return <Typography level='body-sm' color='warning'>{parseResult.parseError}</Typography>;
case !!parseResult.parseError || !!fixupError:
return (
<Box sx={{ display: 'grid', gap: 1, justifyItems: 'start' }}>
{props.onReplaceInCode && (
<Button
size='sm'
variant='outlined'
color='success'
onClick={handleChartRegenerate}
loading={isFetching}
loadingPosition='end'
sx={{
minWidth: 160,
backgroundColor: 'background.surface',
boxShadow: 'xs',
}}
endDecorator={<AutoAwesomeIcon />}
>
{isFetching ? 'Fixing Chart...' : 'Fix Chart'}
</Button>
)}
{fixupError ? (
<Typography level='body-sm' color='danger'>
Error fixing chart: {fixupError}
</Typography>
) : (parseResult.parseError && !isFetching) && (
<Typography level='body-xs'>
Invalid Chart.js input: {parseResult.parseError}
</Typography>
)}
</Box>
);
case !!renderError:
return <Typography level='body-sm' color='warning' variant='plain'>{renderError}</Typography>;
}
+2 -2
View File
@@ -84,10 +84,10 @@ export function bareBonesPromptMixer(_template: string, assistantLlmId: DLLMId |
mixed = mixed.replace('{{PreferTables}}', 'Data presentation: prefer tables (auto-columns)');
// {{Render...}}
mixed = mixed.replace('{{RenderChartJS}}', `
When presenting data that would be better visualized as a chart, output a ChartJS configuration object in this format:
When presenting data that would be better visualized as a chart, output a Chart.js configuration object in this format:
\`\`\`chartjs
{
// Valid and complete ChartJS configuration JSON (DO NOT USE FUNCTIONS)
// Valid and complete Chart.js configuration JSON (DO NOT USE FUNCTIONS)
}
\`\`\`
Choose the most suitable chart type based on the data and context. Include only the JSON configuration, without any explanatory text. Ensure the JSON is valid and complete.