Skip to content

Add fix with AI functionality BEN-1080 #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/api/generate/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const benchify = new Benchify({
apiKey: process.env.BENCHIFY_API_KEY,
});

const debug = false;
const debug = true;
const buggyCode = [
{
path: "src/App.tsx",
Expand Down Expand Up @@ -79,7 +79,7 @@ export async function POST(request: NextRequest) {

// Determine if this is an edit request or new generation
if (existingFiles && editInstruction) {
// Edit existing code
// Edit existing code (including error fixes)
console.log('Processing edit request...');
console.log('Existing files:', existingFiles.map(f => ({ path: f.path, contentLength: f.content.length })));

Expand Down
13 changes: 8 additions & 5 deletions app/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ export default function ChatPage() {
}
};

const handleUpdateResult = (updatedResult: GenerationResult) => {
setResult(updatedResult);
// Save updated result to sessionStorage
sessionStorage.setItem('builderResult', JSON.stringify(updatedResult));
};

// Show loading spinner if we don't have prompt yet
if (!initialPrompt) {
return (
Expand All @@ -101,11 +107,7 @@ export default function ChatPage() {
<ChatInterface
initialPrompt={initialPrompt}
currentFiles={result?.repairedFiles || result?.originalFiles}
onUpdateResult={(updatedResult) => {
setResult(updatedResult);
// Save updated result to sessionStorage
sessionStorage.setItem('builderResult', JSON.stringify(updatedResult));
}}
onUpdateResult={handleUpdateResult}
/>
</div>

Expand All @@ -118,6 +120,7 @@ export default function ChatPage() {
prompt={initialPrompt}
buildErrors={result?.buildErrors}
hasErrors={result?.hasErrors}
onFixComplete={handleUpdateResult}
/>
</div>
</div>
Expand Down
94 changes: 91 additions & 3 deletions components/ui-builder/error-display.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
'use client';

import { AlertCircle, Code, FileX, Terminal } from 'lucide-react';
import { useState } from 'react';
import { AlertCircle, Code, FileX, Terminal, Wand2 } from 'lucide-react';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { ScrollArea } from '@/components/ui/scroll-area';
import { benchifyFileSchema } from '@/lib/schemas';
import { z } from 'zod';

interface BuildError {
type: 'typescript' | 'build' | 'runtime';
Expand All @@ -15,9 +19,13 @@ interface BuildError {

interface ErrorDisplayProps {
errors: BuildError[];
currentFiles?: z.infer<typeof benchifyFileSchema>;
onFixComplete?: (result: any) => void;
}

export function ErrorDisplay({ errors }: ErrorDisplayProps) {
export function ErrorDisplay({ errors, currentFiles, onFixComplete }: ErrorDisplayProps) {
const [isFixing, setIsFixing] = useState(false);

const getErrorIcon = (type: BuildError['type']) => {
switch (type) {
case 'typescript':
Expand Down Expand Up @@ -52,15 +60,94 @@ export function ErrorDisplay({ errors }: ErrorDisplayProps) {
return acc;
}, {} as Record<string, BuildError[]>);

const handleFixWithAI = async () => {
if (!currentFiles || errors.length === 0 || isFixing) return;

setIsFixing(true);

try {
// Format errors into an edit instruction
const errorDetails = errors.map(error => {
let errorInfo = `${error.type.toUpperCase()} ERROR: ${error.message}`;
if (error.file) {
errorInfo += ` (in ${error.file}`;
if (error.line) errorInfo += ` at line ${error.line}`;
if (error.column) errorInfo += `, column ${error.column}`;
errorInfo += ')';
}
return errorInfo;
}).join('\n\n');

const fixInstruction = `Fix the following build errors:

${errorDetails}

Please make the minimal changes necessary to resolve these errors while maintaining existing functionality.`;

// Use the existing edit API
const response = await fetch('/api/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'component',
description: '',
existingFiles: currentFiles,
editInstruction: fixInstruction,
}),
});

if (!response.ok) {
throw new Error('Failed to fix errors with AI');
}

const fixResult = await response.json();

// Notify parent component of the fix result
if (onFixComplete) {
onFixComplete(fixResult);
}

} catch (error) {
console.error('Error fixing with AI:', error);
// Could add error toast here
} finally {
setIsFixing(false);
}
};

return (
<div className="w-full h-full flex items-center justify-center rounded-md border bg-background p-6">
<div className="w-full max-w-2xl">
<div className="text-center mb-6">
<AlertCircle className="h-12 w-12 text-destructive mx-auto mb-4" />
<h3 className="text-lg font-semibold mb-2">Build Errors Detected</h3>
<p className="text-muted-foreground text-sm">
<p className="text-muted-foreground text-sm mb-4">
Your project has some issues that need to be fixed before it can run properly.
</p>

{/* Fix with AI Button */}
{currentFiles && (
<Button
onClick={handleFixWithAI}
disabled={isFixing}
className="mb-4"
size="sm"
>
{isFixing ? (
<>
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-current mr-2" />
Fixing with AI...
</>
) : (
<>
<Wand2 className="h-4 w-4 mr-2" />
Fix with AI
</>
)}
</Button>
)}
</div>

<ScrollArea className="max-h-96">
Expand Down Expand Up @@ -109,6 +196,7 @@ export function ErrorDisplay({ errors }: ErrorDisplayProps) {
<li>• Verify that all props are properly typed</li>
<li>• Make sure all dependencies are correctly installed</li>
<li>• Try regenerating the component with more specific requirements</li>
{currentFiles && <li>• Use the "Fix with AI" button above for automatic error resolution</li>}
</ul>
</div>
</div>
Expand Down
10 changes: 8 additions & 2 deletions components/ui-builder/preview-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ interface PreviewCardProps {
prompt?: string;
buildErrors?: BuildError[];
hasErrors?: boolean;
onFixComplete?: (result: any) => void;
}

export function PreviewCard({
Expand All @@ -60,7 +61,8 @@ export function PreviewCard({
isGenerating = false,
prompt,
buildErrors = [],
hasErrors = false
hasErrors = false,
onFixComplete
}: PreviewCardProps) {
const files = code || [];
const [currentStep, setCurrentStep] = useState(0);
Expand Down Expand Up @@ -147,7 +149,11 @@ export function PreviewCard({
</div>
) : hasErrors && buildErrors.length > 0 ? (
// Show build errors if there are any
<ErrorDisplay errors={buildErrors} />
<ErrorDisplay
errors={buildErrors}
currentFiles={files}
onFixComplete={onFixComplete}
/>
) : previewUrl ? (
// Show the actual preview iframe when ready
<div className="w-full h-full overflow-hidden rounded-md border bg-background">
Expand Down
2 changes: 0 additions & 2 deletions lib/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ const fileSchema = z.object({
content: z.string()
});



// Generate a new application using AI SDK
export async function generateApp(
description: string,
Expand Down