mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-08-16 18:50:44 +03:00
Asks GPT to list its fixes
Signed-off-by: Mihovil Ilakovac <mihovil@ilakovac.com>
This commit is contained in:
parent
03c2f685fb
commit
8dba4f163f
@ -65,19 +65,19 @@ generateNewProject newProjectDetails waspProjectSkeletonFiles = do
|
||||
-- do it blindly though, some of them are relevant only to plan (e.g. not generating login /
|
||||
-- signup page), we would have to do some adapting.
|
||||
writeToLog "Fixing any mistakes in Wasp file..."
|
||||
fixWaspFile newProjectDetails waspFilePath plan
|
||||
writeToLog "Wasp file fixed."
|
||||
listOfWaspFileFixes <- fixWaspFile newProjectDetails waspFilePath plan
|
||||
writeToLog $ appendFixes "Wasp file fixed." listOfWaspFileFixes
|
||||
|
||||
writeToLog "Fixing any mistakes in NodeJS operations files..."
|
||||
forM_ (nub $ getOperationJsFilePath <$> (queries <> actions)) $ \opFp -> do
|
||||
fixOperationsJsFile newProjectDetails waspFilePath opFp
|
||||
writeToLog $ T.pack $ "Fixed NodeJS operations file '" <> opFp <> "'."
|
||||
listOfFixes <- fixOperationsJsFile newProjectDetails waspFilePath opFp
|
||||
writeToLog $ appendFixes ("Fixed NodeJS operations file '" <> T.pack opFp <> "'.") listOfFixes
|
||||
writeToLog "NodeJS operations files fixed."
|
||||
|
||||
writeToLog "Fixing any mistakes in pages..."
|
||||
forM_ (getPageComponentPath <$> pages) $ \pageFp -> do
|
||||
fixPageComponent newProjectDetails waspFilePath pageFp
|
||||
writeToLog $ T.pack $ "Fixed '" <> pageFp <> "' page."
|
||||
listOfFixes <- fixPageComponent newProjectDetails waspFilePath pageFp
|
||||
writeToLog $ appendFixes ("Fixed '" <> T.pack pageFp <> "' page.") listOfFixes
|
||||
writeToLog "Pages fixed."
|
||||
|
||||
(promptTokensUsed, completionTokensUsed) <- getTotalTokensUsage
|
||||
@ -106,3 +106,9 @@ generateNewProject newProjectDetails waspProjectSkeletonFiles = do
|
||||
|
||||
showT :: Show a => a -> Text
|
||||
showT = T.pack . show
|
||||
|
||||
appendFixes :: Text -> Text -> Text
|
||||
appendFixes text fixesText =
|
||||
if T.null text
|
||||
then fixesText
|
||||
else text <> "\n" <> fixesText
|
||||
|
@ -5,11 +5,13 @@ module Wasp.AI.GenerateNewProject.OperationsJsFile
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Monad (when)
|
||||
import Data.Aeson (FromJSON)
|
||||
import Data.Aeson.Types (ToJSON)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Debug.Trace (trace)
|
||||
import GHC.Generics (Generic)
|
||||
import NeatInterpolation (trimming)
|
||||
import Wasp.AI.CodeAgent (CodeAgent, getFile, writeToFile)
|
||||
@ -22,8 +24,9 @@ import Wasp.AI.GenerateNewProject.Common.Prompts (appDescriptionBlock)
|
||||
import qualified Wasp.AI.GenerateNewProject.Common.Prompts as Prompts
|
||||
import Wasp.AI.GenerateNewProject.Operation (actionDocPrompt, queryDocPrompt)
|
||||
import Wasp.AI.OpenAI.ChatGPT (ChatMessage (..), ChatRole (..))
|
||||
import Wasp.Util.StringDiff (printTwoFiles)
|
||||
|
||||
fixOperationsJsFile :: NewProjectDetails -> FilePath -> FilePath -> CodeAgent ()
|
||||
fixOperationsJsFile :: NewProjectDetails -> FilePath -> FilePath -> CodeAgent Text
|
||||
fixOperationsJsFile newProjectDetails waspFilePath opJsFilePath = do
|
||||
currentWaspFileContent <- fromMaybe (error "couldn't find wasp file") <$> getFile waspFilePath
|
||||
currentOpJsFileContent <- fromMaybe (error "couldn't find operation js file to fix") <$> getFile opJsFilePath
|
||||
@ -37,6 +40,17 @@ fixOperationsJsFile newProjectDetails waspFilePath opJsFilePath = do
|
||||
ChatMessage {role = User, content = fixOpJsFilePrompt currentWaspFileContent currentOpJsFileContent}
|
||||
]
|
||||
writeToFile opJsFilePath (const $ opJsFileContent fixedOpJsFile)
|
||||
|
||||
when True
|
||||
$ trace
|
||||
( "=== Diff between old and new "
|
||||
<> opJsFilePath
|
||||
<> " operation: ===\n"
|
||||
<> printTwoFiles (T.unpack currentOpJsFileContent) (T.unpack $ opJsFileContent fixedOpJsFile)
|
||||
)
|
||||
$ return ()
|
||||
|
||||
return $ listOfFixes fixedOpJsFile
|
||||
where
|
||||
fixOpJsFilePrompt currentWaspFileContent currentOpJsFileContent =
|
||||
[trimming|
|
||||
@ -81,11 +95,12 @@ fixOperationsJsFile newProjectDetails waspFilePath opJsFilePath = do
|
||||
const { name, description } = arg;
|
||||
```
|
||||
|
||||
With this in mind, generate a new, fixed NodeJS file with operations (${opJsFilePathText}).
|
||||
List all of the fixes first and then generate a new, fixed NodeJS file with operations (${opJsFilePathText}).
|
||||
The fixes should be a - prefixed list of lines, each line being a fix.
|
||||
Don't do too big changes, like deleting or adding whole functions, focus on smaller things and those listed above.
|
||||
DO NOT add new queries / actions to the file, or delete existing ones!
|
||||
Do actual fixes, don't leave comments with "TODO"!
|
||||
Please respond ONLY with a valid JSON of the format { opJsFileContent: string }.
|
||||
Please respond ONLY with a valid JSON of the format { opJsFileContent: string, listOfFixes: string }.
|
||||
There should be no other text in your response. Don't wrap content with the "```" code delimiters.
|
||||
|
||||
${appDescriptionBlockText}
|
||||
@ -95,7 +110,8 @@ fixOperationsJsFile newProjectDetails waspFilePath opJsFilePath = do
|
||||
opJsFilePathText = T.pack opJsFilePath
|
||||
|
||||
data OperationsJsFile = OperationsJsFile
|
||||
{ opJsFileContent :: Text
|
||||
{ opJsFileContent :: Text,
|
||||
listOfFixes :: Text
|
||||
}
|
||||
deriving (Generic, Show)
|
||||
|
||||
|
@ -5,10 +5,12 @@ module Wasp.AI.GenerateNewProject.PageComponentFile
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Monad (when)
|
||||
import Data.Aeson (FromJSON)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Debug.Trace (trace)
|
||||
import GHC.Generics (Generic)
|
||||
import NeatInterpolation (trimming)
|
||||
import Wasp.AI.CodeAgent (CodeAgent, getFile, writeToFile)
|
||||
@ -21,8 +23,9 @@ import Wasp.AI.GenerateNewProject.Common.Prompts (appDescriptionBlock)
|
||||
import qualified Wasp.AI.GenerateNewProject.Common.Prompts as Prompts
|
||||
import Wasp.AI.GenerateNewProject.Page (pageDocPrompt)
|
||||
import Wasp.AI.OpenAI.ChatGPT (ChatMessage (..), ChatRole (..))
|
||||
import Wasp.Util.StringDiff (printTwoFiles)
|
||||
|
||||
fixPageComponent :: NewProjectDetails -> FilePath -> FilePath -> CodeAgent ()
|
||||
fixPageComponent :: NewProjectDetails -> FilePath -> FilePath -> CodeAgent Text
|
||||
fixPageComponent newProjectDetails waspFilePath pageComponentPath = do
|
||||
currentWaspFileContent <- fromMaybe (error "couldn't find wasp file") <$> getFile waspFilePath
|
||||
currentPageComponentContent <- fromMaybe (error "couldn't find page file to fix") <$> getFile pageComponentPath
|
||||
@ -33,6 +36,17 @@ fixPageComponent newProjectDetails waspFilePath pageComponentPath = do
|
||||
ChatMessage {role = User, content = fixPageComponentPrompt currentWaspFileContent currentPageComponentContent}
|
||||
]
|
||||
writeToFile pageComponentPath (const $ pageComponentImpl fixedPageComponent)
|
||||
|
||||
when True
|
||||
$ trace
|
||||
( "=== Diff between old and new "
|
||||
<> pageComponentPath
|
||||
<> " page component: ===\n"
|
||||
<> printTwoFiles (T.unpack currentPageComponentContent) (T.unpack $ pageComponentImpl fixedPageComponent)
|
||||
)
|
||||
$ return ()
|
||||
|
||||
return $ listOfFixes fixedPageComponent
|
||||
where
|
||||
fixPageComponentPrompt currentWaspFileContent currentPageContent =
|
||||
[trimming|
|
||||
@ -70,9 +84,10 @@ fixPageComponent newProjectDetails waspFilePath pageComponentPath = do
|
||||
- If there are any duplicate imports, make sure to remove them.
|
||||
- There might be some invalid JS or JSX syntax -> fix it if there is any.
|
||||
|
||||
With this in mind, generate a new, fixed React component (${pageComponentPathText}).
|
||||
List all of the fixes first and then generate a new, fixed React component (${pageComponentPathText}).
|
||||
The fixes should be a - prefixed list of lines, each line being a fix.
|
||||
Do actual fixes, don't leave comments with "TODO"!
|
||||
Please respond ONLY with a valid JSON of the format { pageComponentImpl: string }.
|
||||
Please respond ONLY with a valid JSON of the format { pageComponentImpl: string, listOfFixes: string }.
|
||||
There should be no other text in your response. Don't wrap content with the "```" code delimiters.
|
||||
|
||||
${appDescriptionBlockText}
|
||||
@ -82,7 +97,8 @@ fixPageComponent newProjectDetails waspFilePath pageComponentPath = do
|
||||
pageComponentPathText = T.pack pageComponentPath
|
||||
|
||||
data PageComponent = PageComponent
|
||||
{ pageComponentImpl :: Text
|
||||
{ pageComponentImpl :: Text,
|
||||
listOfFixes :: Text
|
||||
}
|
||||
deriving (Generic, Show)
|
||||
|
||||
|
@ -5,6 +5,7 @@ module Wasp.AI.GenerateNewProject.WaspFile
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Monad (when)
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
import Data.Aeson (FromJSON)
|
||||
import Data.Aeson.Types (ToJSON)
|
||||
@ -13,6 +14,7 @@ import Data.List (intercalate)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Debug.Trace (trace)
|
||||
import GHC.Generics (Generic)
|
||||
import NeatInterpolation (trimming)
|
||||
import Wasp.AI.CodeAgent (CodeAgent, getFile, writeToFile)
|
||||
@ -28,8 +30,9 @@ import Wasp.AI.OpenAI.ChatGPT (ChatMessage (..), ChatRole (..))
|
||||
import Wasp.Analyzer.Parser.Ctx (Ctx (..))
|
||||
import Wasp.Project.Analyze (analyzeWaspFileContent)
|
||||
import qualified Wasp.Util.Aeson as Utils.Aeson
|
||||
import Wasp.Util.StringDiff (printTwoFiles)
|
||||
|
||||
fixWaspFile :: NewProjectDetails -> FilePath -> Plan -> CodeAgent ()
|
||||
fixWaspFile :: NewProjectDetails -> FilePath -> Plan -> CodeAgent Text
|
||||
fixWaspFile newProjectDetails waspFilePath plan = do
|
||||
currentWaspFileContent <- getFile waspFilePath <&> fromMaybe (error "couldn't find wasp file to fix")
|
||||
|
||||
@ -37,18 +40,28 @@ fixWaspFile newProjectDetails waspFilePath plan = do
|
||||
-- to give chatGPT opportunity to fix some other stuff we mention in the prompt.
|
||||
-- Then, we do two more attempts at fixing, but only if there are compiler errors.
|
||||
fixedWaspFile <-
|
||||
pure (WaspFile {waspFileContent = currentWaspFileContent})
|
||||
pure (WaspFile {waspFileContent = currentWaspFileContent, listOfFixes = ""})
|
||||
>>= askChatGptToFixWaspFile EvenIfNoCompileErrors
|
||||
>>= askChatGptToFixWaspFile OnlyIfCompileErrors
|
||||
>>= askChatGptToFixWaspFile OnlyIfCompileErrors
|
||||
|
||||
writeToFile waspFilePath (const $ waspFileContent fixedWaspFile)
|
||||
|
||||
when True
|
||||
$ trace
|
||||
( "=== Diff between old and new "
|
||||
<> "main.wasp ===\n"
|
||||
<> printTwoFiles (T.unpack currentWaspFileContent) (T.unpack $ waspFileContent fixedWaspFile)
|
||||
)
|
||||
$ return ()
|
||||
|
||||
return $ listOfFixes fixedWaspFile
|
||||
where
|
||||
askChatGptToFixWaspFile :: ShouldContinueIfCompileErrors -> WaspFile -> CodeAgent WaspFile
|
||||
askChatGptToFixWaspFile shouldContinueIfCompileErrors WaspFile {waspFileContent = wfContent} = do
|
||||
compileErrors <- liftIO $ getWaspFileCompileErrors wfContent
|
||||
case shouldContinueIfCompileErrors of
|
||||
OnlyIfCompileErrors | null compileErrors -> return $ WaspFile {waspFileContent = wfContent}
|
||||
OnlyIfCompileErrors | null compileErrors -> return $ WaspFile {waspFileContent = wfContent, listOfFixes = ""}
|
||||
_otherwise ->
|
||||
queryChatGPTForJSON
|
||||
defaultChatGPTParamsForFixing
|
||||
@ -125,12 +138,13 @@ fixWaspFile newProjectDetails waspFilePath plan = do
|
||||
- I noticed that you sometimes by accident add redundant "}" at the end of the Wasp file while fixing it.
|
||||
Be careful not to do that.
|
||||
|
||||
With this in mind, generate a new, fixed wasp file.
|
||||
List all of the fixes first and then generate a new, fixed wasp file.
|
||||
The fixes should be a - prefixed list of lines, each line being a fix.
|
||||
Try not to do big changes like changing names, removing/adding declarations and similar, those are usually correct, focus more on obvious, smaller errors.
|
||||
Don't touch `app` declaration, `Login` page, and `Signup` page.
|
||||
Do actual fixes, don't leave comments with "TODO"!
|
||||
Make extra sure to fix compiler errors, if there are any.
|
||||
Please respond ONLY with a valid JSON of the format { waspFileContent: string }.
|
||||
Please respond ONLY with a valid JSON of the format { waspFileContent: string, listOfFixes: string }.
|
||||
There should be no other text in your response. Don't wrap content with the "```" code delimiters.
|
||||
|
||||
${appDescriptionBlockText}
|
||||
@ -150,7 +164,8 @@ getWaspFileCompileErrors waspSource =
|
||||
showCompileError (errMsg, Ctx {ctxSourceRegion = loc}) = show loc <> ": " <> errMsg
|
||||
|
||||
data WaspFile = WaspFile
|
||||
{ waspFileContent :: Text
|
||||
{ waspFileContent :: Text,
|
||||
listOfFixes :: Text
|
||||
}
|
||||
deriving (Generic, Show)
|
||||
|
||||
|
6
waspc/src/Wasp/Util/StringDiff.hs
Normal file
6
waspc/src/Wasp/Util/StringDiff.hs
Normal file
@ -0,0 +1,6 @@
|
||||
module Wasp.Util.StringDiff where
|
||||
|
||||
data DiffType = Same | Different
|
||||
|
||||
printTwoFiles :: String -> String -> String
|
||||
printTwoFiles text1 text2 = "=== First ===\n" ++ text1 ++ "\n=== Second ===\n" ++ text2 ++ "\n=== End ===\n"
|
@ -353,6 +353,7 @@ library
|
||||
Wasp.Util.Network.HTTP
|
||||
Wasp.Util.Network.Socket
|
||||
Wasp.Util.Terminal
|
||||
Wasp.Util.StringDiff
|
||||
Wasp.Version
|
||||
Wasp.WaspignoreFile
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user