kioku-core-0.6.0.0: src/Kioku/Distill/Runtime.hs
{-# LANGUAGE DataKinds #-}
module Kioku.Distill.Runtime
( DistillRuntime,
TestRunners (..),
testDistillRuntime,
withTestRunners,
withDistillWorkspace,
distillAvailability,
RuntimeSmokeInput (..),
RuntimeSmokeOutput (..),
distillWorkspaceRoot,
newDistillRuntime,
runConsolidation,
runDistillProgram,
runExtraction,
runPersonaDistillation,
runSceneDistillation,
runtimeSmokeProgram,
)
where
import Kioku.AI.Config
import Kioku.AI.Interactive
import Kioku.AI.Runtime
import Kioku.Distill.Consolidate (ConsolidateInput, ConsolidationDecision, consolidateProgram, consolidateSignature)
import Kioku.Distill.Extract (ExtractInput, ExtractOutput, extractProgram, extractSignature)
import Kioku.Distill.Persona (PersonaInput, PersonaOutput, personaProgram, personaSignature)
import Kioku.Distill.Scene (SceneInput, SceneOutput, sceneProgram, sceneSignature)
import Kioku.Prelude
import Shikumi.Adapter (ToPrompt)
import Shikumi.Error (ShikumiError)
import Shikumi.Module (predict)
import Shikumi.Program (Program)
import Shikumi.Schema (FromModel, ToSchema, Validatable)
import Shikumi.Schema.Types (Field)
import Shikumi.Signature (Signature, mkSignature)
import System.Directory (getCurrentDirectory)
data DistillRuntime = DistillRuntime AIRuntime (Maybe FilePath) (Maybe TestRunners)
-- | Explicit controlled-runner seam for tests. Production construction accepts
-- only a validated AIRuntime; its captured settings cannot be record-updated.
data TestRunners = TestRunners
{ runExtract :: ExtractInput -> IO (Either ShikumiError ExtractOutput),
runConsolidate :: ConsolidateInput -> IO (Either ShikumiError ConsolidationDecision),
runScene :: SceneInput -> IO (Either ShikumiError SceneOutput),
runPersona :: PersonaInput -> IO (Either ShikumiError PersonaOutput)
}
testDistillRuntime :: IO DistillRuntime
testDistillRuntime = pure (DistillRuntime disabledAIRuntime Nothing (Just emptyTestRunners))
emptyTestRunners :: TestRunners
emptyTestRunners = TestRunners (const missing) (const missing) (const missing) (const missing)
where
missing = fail "test distillation runner was not supplied"
withTestRunners :: DistillRuntime -> (TestRunners -> TestRunners) -> DistillRuntime
withTestRunners (DistillRuntime ai root runners) f =
DistillRuntime ai root (Just (f (fromMaybe emptyTestRunners runners)))
withDistillWorkspace :: FilePath -> DistillRuntime -> DistillRuntime
withDistillWorkspace root (DistillRuntime ai _ runners) = DistillRuntime ai (Just root) runners
distillWorkspaceRoot :: DistillRuntime -> IO FilePath
distillWorkspaceRoot (DistillRuntime _ root _) = maybe getCurrentDirectory pure root
newtype RuntimeSmokeInput = RuntimeSmokeInput
{ prompt :: Field "short input text to echo" Text
}
deriving stock (Generic, Eq, Show)
deriving anyclass (ToSchema, FromModel, ToPrompt)
newtype RuntimeSmokeOutput = RuntimeSmokeOutput
{ answer :: Field "the input text repeated back" Text
}
deriving stock (Generic, Eq, Show)
deriving anyclass (ToSchema, FromModel, ToPrompt, Validatable)
newDistillRuntime :: AIRuntime -> Maybe FilePath -> DistillRuntime
newDistillRuntime ai root = DistillRuntime ai root Nothing
distillAvailability :: DistillRuntime -> AIFeature -> Either AIExecutionError ()
distillAvailability (DistillRuntime ai _ runners) feature =
maybe (executionAvailability ai feature) (const (Right ())) runners
runDistillProgram :: DistillRuntime -> AIFeature -> Program i o -> i -> IO (Either AIExecutionError o)
runDistillProgram (DistillRuntime ai _ _) = runAIProgram ai
runTyped ::
(ToPrompt i, ToPrompt o, ToSchema o, FromModel o, Validatable o) =>
AIRuntime -> AIFeature -> Signature i o -> Program i o -> i -> IO (Either AIExecutionError o)
runTyped ai feature signature program = case featureConfiguration ai feature of
InteractiveConfig {} -> runInteractiveSignature ai feature signature
_ -> runAIProgram ai feature program
runExtraction :: DistillRuntime -> ExtractInput -> IO (Either AIExecutionError ExtractOutput)
runExtraction (DistillRuntime ai _ runners) =
maybe
(runTyped ai Extraction extractSignature extractProgram)
(\r i -> either (Left . AIProgramFailed) Right <$> r.runExtract i)
runners
runConsolidation :: DistillRuntime -> ConsolidateInput -> IO (Either AIExecutionError ConsolidationDecision)
runConsolidation (DistillRuntime ai _ runners) =
maybe
(runTyped ai Consolidation consolidateSignature consolidateProgram)
(\r i -> either (Left . AIProgramFailed) Right <$> r.runConsolidate i)
runners
runSceneDistillation :: DistillRuntime -> SceneInput -> IO (Either AIExecutionError SceneOutput)
runSceneDistillation (DistillRuntime ai _ runners) =
maybe
(runTyped ai Scene sceneSignature sceneProgram)
(\r i -> either (Left . AIProgramFailed) Right <$> r.runScene i)
runners
runPersonaDistillation :: DistillRuntime -> PersonaInput -> IO (Either AIExecutionError PersonaOutput)
runPersonaDistillation (DistillRuntime ai _ runners) =
maybe
(runTyped ai Persona personaSignature personaProgram)
(\r i -> either (Left . AIProgramFailed) Right <$> r.runPersona i)
runners
runtimeSmokeProgram :: Program RuntimeSmokeInput RuntimeSmokeOutput
runtimeSmokeProgram =
predict $
mkSignature
"Return the provided input text exactly once in the answer field."