packages feed

ollama-holes-plugin 0.1.5.0 → 0.1.5.1

raw patch · 4 files changed

+36/−24 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,5 +1,10 @@ # Revision history for OllamaHoles +## 0.1.5.1 -- 2024-05-01++* Bugfix: capture suggestion constraints and make sure they're respected+* Bugfix: Don't display errors when validating suggestions+ ## 0.1.5.0 -- 2024-05-01  * Improve the list of things in scope
README.md view
@@ -73,8 +73,9 @@ ```haskell {-# LANGUAGE DataKinds #-} {-# OPTIONS_GHC -fplugin=GHC.Plugin.OllamaHoles #-}-{-# OPTIONS_GHC -fplugin-opt=GHC.Plugin.OllamaHoles:model=gemma3:27b #-}+{-# OPTIONS_GHC -fplugin-opt=GHC.Plugin.OllamaHoles:model=gemma3:27b-it-qat #-} {-# OPTIONS_GHC -fplugin-opt=GHC.Plugin.OllamaHoles:n=5 #-}+{-# OPTIONS_GHC -fplugin-opt=GHC.Plugin.OllamaHoles:debug #-}  module Main where @@ -84,7 +85,7 @@ import Data.Proxy  main :: IO ()-main = do let _guide = Proxy :: Proxy (Text "The function should take the list, sort it, and then print each integer.")+main = do let _guide = Proxy :: Proxy (Text "The function should sort the list and then show each element")           let k = (_b :: [Int] -> [String])           print (k [1,2,3])     @@ -106,9 +107,11 @@         k :: [Int] -> [String] (bound at Main.hs:16:15)         _guide :: Proxy                     (Text-                       "The function should take the list, sort it, and then print each integer.")+                       "The function should sort the list and then show each element")           (bound at Main.hs:15:15)         main :: IO () (bound at Main.hs:15:1)+      Valid hole fits include+        L.map show . L.sort    | 16 |           let k = (_b :: [Int] -> [String])    |                    ^^
ollama-holes-plugin.cabal view
@@ -20,7 +20,7 @@ -- PVP summary:     +-+------- breaking API changes --                  | | +----- non-breaking API additions --                  | | | +--- code changes with no API change-version:            0.1.5.0+version:            0.1.5.1  -- A short (one-line) description of the package. synopsis: A typed-hole plugin that uses LLMs to generate valid hole-fits
src/GHC/Plugin/OllamaHoles.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE LambdaCase #-}  -- | The Ollama plugin for GHC module GHC.Plugin.OllamaHoles where@@ -14,7 +13,8 @@ import GHC.Plugins hiding ((<>)) import GHC.Tc.Types import GHC.Tc.Types.Constraint (Hole (..))-import GHC.Tc.Utils.Monad (getGblEnv, newTcRef, writeTcRef, readTcRef)+import GHC.Tc.Utils.Monad (getGblEnv, newTcRef, writeTcRef, readTcRef, discardErrs)+import qualified GHC.Tc.Utils.Monad as GHC  import GHC.Plugin.OllamaHoles.Backend import GHC.Plugin.OllamaHoles.Backend.Gemini (geminiBackend)@@ -30,12 +30,9 @@ import GHC.Parser.PostProcess qualified as GHC (runPV, unECP) import GHC.Rename.Expr qualified as GHC (rnLExpr) import GHC.Tc.Errors.Hole qualified as GHC (tcCheckHoleFit, withoutUnification)-import GHC.Tc.Gen.App qualified as GHC (tcInferSigma)+import GHC.Tc.Gen.App qualified as GHC  (tcInferSigma) import GHC.Tc.Utils.TcType qualified as GHC (tyCoFVsOfType) import GHC.Types.SrcLoc qualified as GHC (mkRealSrcLoc)-#if __GLASGOW_HASKELL__ >= 908-import GHC.Tc.Types.Constraint (CtLocEnv(..))-#endif  #if __GLASGOW_HASKELL__ >= 912 import GHC.Tc.Types.CtLoc (ctLocSpan)@@ -52,6 +49,8 @@ import qualified GHC.Types.Unique.Map as GHC import qualified GHC.Hs.Doc as GHC import qualified GHC.Iface.Load as GHC+import qualified GHC.Tc.Types.Constraint as GHC+import qualified GHC.Data.Bag as GHC  -- | Prompt used to prompt the LLM promptTemplate :: Text@@ -71,7 +70,7 @@         <> "Make sure you synthesize an expression that matches the type of the hole.\n"         <> "Output ONLY the raw Haskell expression(s), one per line.\n"         <> "Do not include explanations, introductions, or any surrounding text.\n"-        <> "If you are using a function from scope, make sure to use the fully qualified name.\n"+        <> "If you are using a function from scope, make sure to use the qualified name from the list of things in scope.\n"         <> "Output a maximum of {numexpr} expresssions.\n"  -- | Determine which backend to use@@ -98,9 +97,7 @@                     }         }   where-             pluginName = "Ollama Plugin"-            fitPlugin opts ref hole fits = do         cands <- readTcRef ref         let flags@Flags{..} = parseFlags opts@@ -147,7 +144,7 @@                         let cf = "Candidate fits: " <> showSDoc dflags (ppr fits)                         let scope = "Things in scope: " <> showSDoc dflags (ppr $ mapMaybe fullyQualified cands)                         docs <- if include_docs then getDocs cands else return ""-  +                         guide <- seekGuidance cands                         let prompt' =                                 replacePlaceholders@@ -164,14 +161,13 @@                                     , ("{scope}", scope)                                     , ("{docs}", docs)                                     ]+                        liftIO $ when debug $ do T.putStrLn $ "--- " <> pluginName <> ": Prompt ---\n" <> prompt'                         res <- liftIO $ generateFits backend prompt' model_name                         case res of                             Right rsp -> do                                 let lns = (preProcess . T.lines) rsp+                                liftIO $ when debug $ do T.putStrLn $ "--- " <> pluginName <> ": Response ---\n" <> rsp                                 verified <- filterM (verifyHoleFit debug hole) lns-                                liftIO $ when debug $ do-                                    T.putStrLn $ "--- " <> pluginName <> ": Prompt ---\n" <> prompt'-                                    T.putStrLn $ "--- " <> pluginName <> ": Response ---\n" <> rsp                                 let fits' = map (RawHoleFit . text . T.unpack) verified                                 -- Return the generated fits                                 return fits'@@ -198,13 +194,14 @@     case parsed of       GHC.PFailed st -> return $ Left (showPprUnsafe $ GHC.getPsErrorMessages st)       GHC.POk _ (p_e :: LHsExpr GhcPs) -> return $ Right p_e-       + -- | Check that the hole fit matches the type of the hole verifyHoleFit :: Bool -> TypedHole -> Text -> TcM Bool-verifyHoleFit debug hole fit | Just h <- th_hole hole = handleAll falseOnErr $ do+verifyHoleFit debug hole fit | Just h <- th_hole hole = discardErrs $ handleAll falseOnErr $ do     -- Instaniate a new IORef session with the current HscEnv.     -- First we try parsing the suggest hole-fits+    liftIO $ when debug $ T.putStrLn $ "--- Validating " <> fit <> " ---"     parsed <- parseInContext fit     case parsed of         Left err_msg-> do@@ -213,15 +210,22 @@               putStrLn err_msg             return False         Right p_e -> do+            when debug $ liftIO $ putStrLn $ showPprUnsafe p_e             -- If parsing was successful, we try renaming the expression             (rn_e, free_vars) <- GHC.rnLExpr p_e             when debug $ liftIO $ putStrLn $ showPprUnsafe free_vars             -- Finally, we infer the type of the expression-            expr_ty <- GHC.tcInferSigma False rn_e-            -- And check whether that is indeed a valid hole fit             (does_fit, wrapper) <--                GHC.withoutUnification (GHC.tyCoFVsOfType $ hole_ty h) $-                    GHC.tcCheckHoleFit hole (hole_ty h) expr_ty+                GHC.withoutUnification (GHC.tyCoFVsOfType $ hole_ty h) $ do+                    -- Make sure to capture the constraints and include those when checking the fit+                    (expr_ty, wanteds) <- GHC.captureConstraints $ GHC.tcInferSigma False rn_e+                    let evs = GHC.mapBag GHC.ctEvidence$ GHC.wc_simple wanteds+                        impls = GHC.bagToList $ GHC.wc_impl wanteds+                        hole' = hole {th_implics = th_implics hole ++ impls,+                                      th_relevant_cts = th_relevant_cts hole <> evs}+                    r <- GHC.tcCheckHoleFit hole' (hole_ty h) expr_ty+                    when debug $ liftIO $ putStrLn $ showPprUnsafe r+                    return r             when debug $ liftIO $ putStrLn $ showPprUnsafe wrapper             return does_fit   where@@ -308,7 +312,7 @@     allDocs docs [] = return docs     mkDoc :: DynFlags -> GHC.UniqMap Name [GHC.HsDoc GhcRn] -> HoleFitCandidate -> String     mkDoc dflags all_docs hfc | Just rn <- fullyQualified hfc,-                                Just doc <- GHC.lookupUniqMap all_docs (getName hfc) = +                                Just doc <- GHC.lookupUniqMap all_docs (getName hfc) =        "Documentation for `" <> showSDoc dflags (ppr rn ) <> "`:\n```\n"        <> processDoc dflags doc        <> "```"