shikumi-okf-0.1.0.0: example/Main.hs
{-# LANGUAGE OverloadedStrings #-}
-- | A worked example of the OKF generator that doubles as the regenerate-and-diff
-- fixture. It builds a small two-program manifest — one typed @Predict@ program and
-- one opaque @Embed@ program (the shape an agent runtime such as shikigami produces)
-- — and writes the bundle to the directory given as the first argument (default
-- @./out@, i.e. @shikumi-okf/example/out@ when run from the package directory).
--
-- Run it with:
--
-- > cabal run shikumi-okf-example
--
-- then validate the result with the standalone @okf@ CLI (see the package docs and
-- EP-31). Because no timestamp is passed, regenerating an unchanged manifest yields
-- byte-identical output, so the committed @example/out@ tree can be regenerated and
-- diffed in CI.
module Main (main) where
import Data.Text (Text)
import GHC.Generics (Generic)
import Shikumi.Adapter (ToPrompt)
import Shikumi.Module (predict)
import Shikumi.Okf.Generate (writeProgramBundle)
import Shikumi.Okf.Types
( AppInfo (..),
ProgramDoc (..),
ProgramManifest (..),
SomeProgram (..),
)
import Shikumi.Program (Program, embed)
import Shikumi.Schema (FromModel, ToSchema, Validatable)
import Shikumi.Signature (Signature, mkSignature)
import System.Environment (getArgs)
-- A typed program's input/output records.
newtype Ticket = Ticket {ticket :: Text}
deriving stock (Generic, Show, Eq)
instance ToSchema Ticket
instance FromModel Ticket
instance ToPrompt Ticket
instance Validatable Ticket
newtype Category = Category {category :: Text}
deriving stock (Generic, Show, Eq)
instance ToSchema Category
instance FromModel Category
instance ToPrompt Category
instance Validatable Category
classifyProgram :: Program Ticket Category
classifyProgram = predict (mkSignature "Classify the support ticket." :: Signature Ticket Category)
-- An opaque embedded program: no inspectable internal structure.
heartbeatProgram :: Program () ()
heartbeatProgram = embed (\_ -> pure ())
manifest :: ProgramManifest
manifest =
ProgramManifest
[ ProgramDoc
{ name = "classify-ticket",
title = Just "Classify Ticket",
description = Just "Assigns a support ticket to a category.",
tags = ["support", "classification"],
declaredInputs = Nothing,
declaredOutputs = Nothing,
program = Just (SomeProgram classifyProgram)
},
ProgramDoc
{ name = "heartbeat",
title = Just "Heartbeat",
description = Just "Declared-agent heartbeat that emits a fixed digest.",
tags = ["agent"],
declaredInputs = Just "Trigger payload (ignored).",
declaredOutputs = Just "A fixed heartbeat digest.",
program = Just (SomeProgram heartbeatProgram)
}
]
exampleApp :: AppInfo
exampleApp =
AppInfo
{ appNamespace = "shinzui",
appName = "example-app",
appTitle = Just "Example Application",
appDescription = Just "A worked example of shikumi-okf program documentation."
}
main :: IO ()
main = do
args <- getArgs
let root = case args of
(dir : _) -> dir
[] -> "out"
result <- writeProgramBundle root exampleApp Nothing manifest
case result of
Left err -> error ("bundle generation failed: " <> show err)
Right () -> putStrLn ("Wrote OKF bundle to " <> root)