feat: integrate template awareness into AI assistant
- Add template configurations with file structures and conventions - Update AI route handler to include template context in system messages - Pass template type through AIChat component - Add template-specific run commands - Enhance AI responses with project structure knowledge - Move hardcoded run commands from navbar/run.tsx to templates/index.ts This improves the AI's understanding of different project templates (React, Next.js, Streamlit, Vanilla JS) and enables more contextual assistance based on the project type.
This commit is contained in:
parent
d87f818241
commit
46c715b1eb
@ -1,6 +1,7 @@
|
||||
import { currentUser } from "@clerk/nextjs"
|
||||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import { TIERS } from "@/lib/tiers"
|
||||
import { templateConfigs } from "@/lib/templates"
|
||||
|
||||
const anthropic = new Anthropic({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY!,
|
||||
@ -56,13 +57,35 @@ export async function POST(request: Request) {
|
||||
activeFileContent,
|
||||
isEditMode,
|
||||
fileName,
|
||||
line
|
||||
line,
|
||||
templateType
|
||||
} = await request.json()
|
||||
|
||||
// Get template configuration
|
||||
const templateConfig = templateConfigs[templateType]
|
||||
|
||||
// Create template context
|
||||
const templateContext = templateConfig ? `
|
||||
Project Template: ${templateConfig.name}
|
||||
|
||||
File Structure:
|
||||
${Object.entries(templateConfig.fileStructure)
|
||||
.map(([path, info]) => `${path} - ${info.description}`)
|
||||
.join('\n')}
|
||||
|
||||
Conventions:
|
||||
${templateConfig.conventions.join('\n')}
|
||||
|
||||
Dependencies:
|
||||
${JSON.stringify(templateConfig.dependencies, null, 2)}
|
||||
` : ''
|
||||
|
||||
// Create system message based on mode
|
||||
let systemMessage
|
||||
if (isEditMode) {
|
||||
systemMessage = `You are an AI code editor. Your task is to modify the given code based on the user's instructions. Only output the modified code, without any explanations or markdown formatting. The code should be a direct replacement for the existing code. If there is no code to modify, refer to the active file content and only output the code that is relevant to the user's instructions.
|
||||
systemMessage = `You are an AI code editor working in a ${templateType} project. Your task is to modify the given code based on the user's instructions. Only output the modified code, without any explanations or markdown formatting. The code should be a direct replacement for the existing code. If there is no code to modify, refer to the active file content and only output the code that is relevant to the user's instructions.
|
||||
|
||||
${templateContext}
|
||||
|
||||
File: ${fileName}
|
||||
Line: ${line}
|
||||
@ -77,7 +100,7 @@ Instructions: ${messages[0].content}
|
||||
|
||||
Respond only with the modified code that can directly replace the existing code.`
|
||||
} else {
|
||||
systemMessage = `You are an intelligent programming assistant. Please respond to the following request concisely. If your response includes code, please format it using triple backticks (\`\`\`) with the appropriate language identifier. For example:
|
||||
systemMessage = `You are an intelligent programming assistant for a ${templateType} project. Please respond to the following request concisely. If your response includes code, please format it using triple backticks (\`\`\`) with the appropriate language identifier. For example:
|
||||
|
||||
\`\`\`python
|
||||
print("Hello, World!")
|
||||
@ -85,6 +108,9 @@ Respond only with the modified code that can directly replace the existing code.
|
||||
|
||||
Provide a clear and concise explanation along with any code snippets. Keep your response brief and to the point.
|
||||
|
||||
This is the project template:
|
||||
${templateContext}
|
||||
|
||||
${context ? `Context:\n${context}\n` : ""}
|
||||
${activeFileContent ? `Active File Content:\n${activeFileContent}\n` : ""}`
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ export default function AIChat({
|
||||
editorRef,
|
||||
lastCopiedRangeRef,
|
||||
files,
|
||||
templateType,
|
||||
}: AIChatProps) {
|
||||
// Initialize socket and messages
|
||||
const { socket } = useSocket()
|
||||
@ -143,7 +144,9 @@ export default function AIChat({
|
||||
setIsGenerating,
|
||||
setIsLoading,
|
||||
abortControllerRef,
|
||||
activeFileContent
|
||||
activeFileContent,
|
||||
false,
|
||||
templateType
|
||||
)
|
||||
// Clear context tabs after sending
|
||||
setContextTabs([])
|
||||
|
@ -90,7 +90,8 @@ export const handleSend = async (
|
||||
setIsLoading: React.Dispatch<React.SetStateAction<boolean>>,
|
||||
abortControllerRef: React.MutableRefObject<AbortController | null>,
|
||||
activeFileContent: string,
|
||||
isEditMode: boolean = false
|
||||
isEditMode: boolean = false,
|
||||
templateType: string
|
||||
) => {
|
||||
// Return if input is empty and context is null
|
||||
if (input.trim() === "" && !context) return
|
||||
@ -141,6 +142,7 @@ export const handleSend = async (
|
||||
context: context || undefined,
|
||||
activeFileContent: activeFileContent,
|
||||
isEditMode: isEditMode,
|
||||
templateType: templateType,
|
||||
}),
|
||||
signal: abortControllerRef.current.signal,
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { TemplateConfig } from "@/lib/templates"
|
||||
import { TFile, TFolder } from "@/lib/types"
|
||||
import * as monaco from "monaco-editor"
|
||||
import { Socket } from "socket.io-client"
|
||||
@ -53,6 +54,8 @@ export interface AIChatProps {
|
||||
endLine: number
|
||||
} | null>
|
||||
files: (TFile | TFolder)[]
|
||||
templateType: string
|
||||
templateConfig?: TemplateConfig
|
||||
}
|
||||
|
||||
// Chat input props interface
|
||||
|
@ -1233,6 +1233,7 @@ export default function CodeEditor({
|
||||
editorRef={{ current: editorRef }}
|
||||
lastCopiedRangeRef={lastCopiedRangeRef}
|
||||
files={files}
|
||||
templateType={sandboxData.type}
|
||||
/>
|
||||
</ResizablePanel>
|
||||
</>
|
||||
|
@ -7,6 +7,7 @@ import { Sandbox } from "@/lib/types"
|
||||
import { Play, StopCircle } from "lucide-react"
|
||||
import { useEffect, useRef } from "react"
|
||||
import { toast } from "sonner"
|
||||
import { templateConfigs } from "@/lib/templates"
|
||||
|
||||
export default function RunButtonModal({
|
||||
isRunning,
|
||||
@ -42,10 +43,7 @@ export default function RunButtonModal({
|
||||
setIsPreviewCollapsed(true)
|
||||
previewPanelRef.current?.collapse()
|
||||
} else if (!isRunning && terminals.length < 4) {
|
||||
const command =
|
||||
sandboxData.type === "streamlit"
|
||||
? "./venv/bin/streamlit run main.py --server.runOnSave true"
|
||||
: "npm run dev"
|
||||
const command = templateConfigs[sandboxData.type]?.runCommand || "npm run dev"
|
||||
|
||||
try {
|
||||
// Create a new terminal with the appropriate command
|
||||
|
248
frontend/lib/templates/index.ts
Normal file
248
frontend/lib/templates/index.ts
Normal file
@ -0,0 +1,248 @@
|
||||
export interface TemplateConfig {
|
||||
id: string
|
||||
name: string,
|
||||
runCommand: string,
|
||||
fileStructure: {
|
||||
[key: string]: {
|
||||
purpose: string
|
||||
description: string
|
||||
}
|
||||
}
|
||||
conventions: string[]
|
||||
dependencies?: {
|
||||
[key: string]: string
|
||||
}
|
||||
scripts?: {
|
||||
[key: string]: string
|
||||
}
|
||||
}
|
||||
|
||||
export const templateConfigs: { [key: string]: TemplateConfig } = {
|
||||
reactjs: {
|
||||
id: "reactjs",
|
||||
name: "React",
|
||||
runCommand: "npm run dev",
|
||||
fileStructure: {
|
||||
"src/": {
|
||||
purpose: "source",
|
||||
description: "Contains all React components and application logic"
|
||||
},
|
||||
"src/components/": {
|
||||
purpose: "components",
|
||||
description: "Reusable React components"
|
||||
},
|
||||
"src/lib/": {
|
||||
purpose: "utilities",
|
||||
description: "Utility functions and shared code"
|
||||
},
|
||||
"src/App.tsx": {
|
||||
purpose: "entry",
|
||||
description: "Main application component"
|
||||
},
|
||||
"src/index.tsx": {
|
||||
purpose: "entry",
|
||||
description: "Application entry point"
|
||||
},
|
||||
"src/index.css": {
|
||||
purpose: "styles",
|
||||
description: "Global CSS styles"
|
||||
},
|
||||
"public/": {
|
||||
purpose: "static",
|
||||
description: "Static assets and index.html"
|
||||
},
|
||||
"tsconfig.json": {
|
||||
purpose: "config",
|
||||
description: "TypeScript configuration"
|
||||
},
|
||||
"vite.config.ts": {
|
||||
purpose: "config",
|
||||
description: "Vite bundler configuration"
|
||||
},
|
||||
"package.json": {
|
||||
purpose: "config",
|
||||
description: "Project dependencies and scripts"
|
||||
}
|
||||
},
|
||||
conventions: [
|
||||
"Use functional components with hooks",
|
||||
"Follow React naming conventions (PascalCase for components)",
|
||||
"Keep components small and focused",
|
||||
"Use TypeScript for type safety"
|
||||
],
|
||||
dependencies: {
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.441.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"tailwind-merge": "^2.5.2",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
scripts: {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
}
|
||||
},
|
||||
// Next.js template config
|
||||
nextjs: {
|
||||
id: "nextjs",
|
||||
name: "NextJS",
|
||||
runCommand: "npm run dev",
|
||||
fileStructure: {
|
||||
"pages/": {
|
||||
purpose: "routing",
|
||||
description: "Page components and API routes"
|
||||
},
|
||||
"pages/api/": {
|
||||
purpose: "api",
|
||||
description: "API route handlers"
|
||||
},
|
||||
"pages/_app.tsx": {
|
||||
purpose: "entry",
|
||||
description: "Application wrapper component"
|
||||
},
|
||||
"pages/index.tsx": {
|
||||
purpose: "page",
|
||||
description: "Homepage component"
|
||||
},
|
||||
"public/": {
|
||||
purpose: "static",
|
||||
description: "Static assets and files"
|
||||
},
|
||||
"styles/": {
|
||||
purpose: "styles",
|
||||
description: "CSS modules and global styles"
|
||||
},
|
||||
"styles/globals.css": {
|
||||
purpose: "styles",
|
||||
description: "Global CSS styles"
|
||||
},
|
||||
"styles/Home.module.css": {
|
||||
purpose: "styles",
|
||||
description: "Homepage-specific styles"
|
||||
},
|
||||
"next.config.js": {
|
||||
purpose: "config",
|
||||
description: "Next.js configuration"
|
||||
},
|
||||
"next-env.d.ts": {
|
||||
purpose: "types",
|
||||
description: "Next.js TypeScript declarations"
|
||||
},
|
||||
"tsconfig.json": {
|
||||
purpose: "config",
|
||||
description: "TypeScript configuration"
|
||||
},
|
||||
"package.json": {
|
||||
purpose: "config",
|
||||
description: "Project dependencies and scripts"
|
||||
}
|
||||
},
|
||||
conventions: [
|
||||
"Use file-system based routing",
|
||||
"Keep API routes in pages/api",
|
||||
"Use CSS Modules for component styles",
|
||||
"Follow Next.js data fetching patterns"
|
||||
],
|
||||
dependencies: {
|
||||
"next": "^14.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"tailwindcss": "^3.4.1"
|
||||
},
|
||||
scripts: {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
}
|
||||
},
|
||||
// Streamlit template config
|
||||
streamlit: {
|
||||
id: "streamlit",
|
||||
name: "Streamlit",
|
||||
runCommand: "./venv/bin/streamlit run main.py --server.runOnSave true",
|
||||
fileStructure: {
|
||||
"main.py": {
|
||||
purpose: "entry",
|
||||
description: "Main Streamlit application file"
|
||||
},
|
||||
"requirements.txt": {
|
||||
purpose: "dependencies",
|
||||
description: "Python package dependencies"
|
||||
},
|
||||
"Procfile": {
|
||||
purpose: "deployment",
|
||||
description: "Deployment configuration for hosting platforms"
|
||||
},
|
||||
"venv/": {
|
||||
purpose: "environment",
|
||||
description: "Python virtual environment directory"
|
||||
}
|
||||
},
|
||||
conventions: [
|
||||
"Use Streamlit components for UI",
|
||||
"Follow PEP 8 style guide",
|
||||
"Keep dependencies in requirements.txt",
|
||||
"Use virtual environment for isolation"
|
||||
],
|
||||
dependencies: {
|
||||
"streamlit": "^1.40.0",
|
||||
"altair": "^5.5.0"
|
||||
},
|
||||
scripts: {
|
||||
"start": "streamlit run main.py",
|
||||
"dev": "./venv/bin/streamlit run main.py --server.runOnSave true"
|
||||
}
|
||||
},
|
||||
// HTML template config
|
||||
vanillajs: {
|
||||
id: "vanillajs",
|
||||
name: "HTML/JS",
|
||||
runCommand: "npm run dev",
|
||||
fileStructure: {
|
||||
"index.html": {
|
||||
purpose: "entry",
|
||||
description: "Main HTML entry point"
|
||||
},
|
||||
"style.css": {
|
||||
purpose: "styles",
|
||||
description: "Global CSS styles"
|
||||
},
|
||||
"script.js": {
|
||||
purpose: "scripts",
|
||||
description: "JavaScript application logic"
|
||||
},
|
||||
"package.json": {
|
||||
purpose: "config",
|
||||
description: "Project dependencies and scripts"
|
||||
},
|
||||
"package-lock.json": {
|
||||
purpose: "config",
|
||||
description: "Locked dependency versions"
|
||||
},
|
||||
"vite.config.js": {
|
||||
purpose: "config",
|
||||
description: "Vite bundler configuration"
|
||||
}
|
||||
},
|
||||
conventions: [
|
||||
"Use semantic HTML elements",
|
||||
"Keep CSS modular and organized",
|
||||
"Write clean, modular JavaScript",
|
||||
"Follow modern ES6+ practices"
|
||||
],
|
||||
dependencies: {
|
||||
"vite": "^5.0.12"
|
||||
},
|
||||
scripts: {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user