shikumi-compile-0.1.0.0: test/Test/Fixtures.hs
-- | Shared fixtures for the EP-9 compiler tests: a minimal @Question -> Answer@
-- signature pair (plus an intermediate @Draft@ so a two-node pipeline can prove a
-- compiler reaches a nested node), the base programs every milestone rewrites, and
-- canned fallback-adapter 'Response's the capturing stub returns.
--
-- The record instances mirror @ProgramFixtures@ in the @shikumi@ test tree (which
-- lives there and is not importable here): each record derives 'Generic' and gets
-- empty 'ToSchema' / 'FromModel' / 'ToPrompt' / 'Validatable' bodies (Generic
-- defaults), plus 'ToJSON' / 'FromJSON' so 'Shikumi.Compile.fewShotTyped' can build
-- demos from typed pairs.
module Test.Fixtures
( -- * Records
Question (..),
Answer (..),
Draft (..),
-- * Base programs
qaBase,
qaPipeline,
-- * Demo pool
demoPairs,
-- * Canned responses
answerResponse,
draftResponse,
cotDraftResponse,
cotAnswerResponse,
markerBody,
mkResponse,
)
where
import Baikai
( AssistantContent (..),
Response,
_Response,
_TextContent,
)
import Control.Lens ((&), (.~))
import Data.Aeson (FromJSON, ToJSON)
import Data.Generics.Labels ()
import Data.Text (Text)
import Data.Text qualified as T
import Data.Vector qualified as V
import GHC.Generics (Generic)
import Shikumi.Adapter (ToPrompt)
import Shikumi.Module (predict)
import Shikumi.Program (Program, pipeline)
import Shikumi.Schema (FromModel, ToSchema, Validatable)
import Shikumi.Signature (Signature, mkSignature)
-- ---------------------------------------------------------------------------
-- Records
-- ---------------------------------------------------------------------------
newtype Question = Question {question :: Text}
deriving stock (Generic, Show, Eq)
deriving anyclass (ToJSON, FromJSON, ToSchema, FromModel, ToPrompt, Validatable)
newtype Answer = Answer {answer :: Text}
deriving stock (Generic, Show, Eq)
deriving anyclass (ToJSON, FromJSON, ToSchema, FromModel, ToPrompt, Validatable)
newtype Draft = Draft {draft :: Text}
deriving stock (Generic, Show, Eq)
deriving anyclass (ToJSON, FromJSON, ToSchema, FromModel, ToPrompt, Validatable)
-- ---------------------------------------------------------------------------
-- Signatures and base programs
-- ---------------------------------------------------------------------------
qaSig :: Signature Question Answer
qaSig = mkSignature "Answer the question."
questionToDraft :: Signature Question Draft
questionToDraft = mkSignature "Draft an answer to the question."
draftToAnswer :: Signature Draft Answer
draftToAnswer = mkSignature "Finalize the answer from the draft."
-- | A single-node base program (demos line up exactly for the strong few-shot
-- assertion).
qaBase :: Program Question Answer
qaBase = predict qaSig
-- | A two-node base program: @Question -> Draft -> Answer@, so a compiler can be
-- shown to reach a node nested inside a 'Shikumi.Program.Compose'.
qaPipeline :: Program Question Answer
qaPipeline = pipeline (predict questionToDraft) (predict draftToAnswer)
-- | A small demo pool, as typed pairs (used via 'Shikumi.Compile.fewShotTyped').
-- The answer texts are distinctive so a prompt assertion is unambiguous.
demoPairs :: [(Question, Answer)]
demoPairs =
[ (Question "what is two plus two", Answer "four"),
(Question "capital of france", Answer "Paris"),
(Question "color of the clear sky", Answer "blue")
]
-- ---------------------------------------------------------------------------
-- Canned responses (rendered as the fallback adapter's [[ ## field ## ]] sections)
-- ---------------------------------------------------------------------------
-- | Build a fallback-style response body from @(field, value)@ sections, closing
-- with the @completed@ marker.
markerBody :: [(Text, Text)] -> Text
markerBody fields = T.unlines (concatMap sect fields ++ ["[[ ## completed ## ]]"])
where
sect (k, v) = ["[[ ## " <> k <> " ## ]]", v]
-- | An assistant 'Response' carrying @t@ as its single text block.
mkResponse :: Text -> Response
mkResponse t =
_Response & #message . #content .~ V.singleton (AssistantText (_TextContent & #text .~ t))
-- | Decodes as an @Answer@ under the fallback adapter.
answerResponse :: Response
answerResponse = mkResponse (markerBody [("answer", "forty-two")])
-- | Decodes as a @Draft@ under the fallback adapter.
draftResponse :: Response
draftResponse = mkResponse (markerBody [("draft", "a rough answer")])
-- | Decodes as a @WithReasoning Draft@ (the chain-of-thought augmented output):
-- the @value@ section carries the nested @Draft@ JSON object.
cotDraftResponse :: Response
cotDraftResponse =
mkResponse (markerBody [("reasoning", "thinking about it"), ("value", "{\"draft\":\"a rough answer\"}")])
-- | Decodes as a @WithReasoning Answer@.
cotAnswerResponse :: Response
cotAnswerResponse =
mkResponse (markerBody [("reasoning", "thinking about it"), ("value", "{\"answer\":\"forty-two\"}")])