agentic-anthropic 0.2.0.3 → 0.2.0.4
raw patch · 5 files changed
+23/−19 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +4/−0
- agentic-anthropic.cabal +1/−1
- src/Agentic/Anthropic.hs +10/−10
- test-live/Live.hs +2/−2
- test/Spec.hs +6/−6
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Changelog for agentic-anthropic +## 0.2.0.4 - 2026-10-06++* Follows agentic's record field changes; no API change.+ ## 0.2.0.3 - 2026-10-06 * Follows agentic's renames: `endpoint` is `Text`.
agentic-anthropic.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: agentic-anthropic-version: 0.2.0.3+version: 0.2.0.4 synopsis: Claude as the System Two provider for agentic description: Runs agentic's draft steps on Claude through the Messages API, with strict structured outputs and tools, and can stand in as System One.
src/Agentic/Anthropic.hs view
@@ -152,11 +152,11 @@ , ("max_tokens", A.Integer (toInteger cfg.maxTokens)) ] <> maybe [] (\s -> [("system", A.String s)]) cfg.system- <> [("tools", A.Array (map tool (tools c))) | not (null (tools c))]- <> [ ("messages", A.Array (task : concatMap exchange (history c)))+ <> [("tools", A.Array (map tool c.tools)) | not (null c.tools)]+ <> [ ("messages", A.Array (task : concatMap exchange c.history)) , ( "output_config" , A.Object- ( ("format", A.Object [("type", A.String "json_schema"), ("schema", objectSchema (outputSchema c))])+ ( ("format", A.Object [("type", A.String "json_schema"), ("schema", objectSchema c.outputSchema)]) : maybe [] (\e -> [("effort", A.String (effortName e))]) cfg.effort ) )@@ -164,15 +164,15 @@ ] <> [("fallbacks", A.String "default") | cfg.fallbacks] where- task = message "user" (A.String (instructionText (instruction c) <> inputText))- inputText = case input c of+ task = message "user" (A.String (c.instruction.text <> inputText))+ inputText = case c.input of A.Null -> "" s -> "\n\nInput:\n" <> A.renderJson s tool spec = A.Object- [ ("name", A.String (specName spec))- , ("description", A.String (specDescription spec))- , ("input_schema", objectSchema (specInput spec))+ [ ("name", A.String spec.name)+ , ("description", A.String spec.description)+ , ("input_schema", objectSchema spec.input) , ("strict", A.Bool True) ] exchange = \case@@ -223,11 +223,11 @@ -- A reply that isn't JSON goes back to the core as text; the output -- contract then rejects it and the model gets another go. final text = case J.eitherDecode (TL.encodeUtf8 (TL.fromStrict text)) of- Right v -> unwrap (outputSchema c) (fromAeson v)+ Right v -> unwrap c.outputSchema (fromAeson v) Left _ -> A.String text unwrapInput name v = maybe v (`unwrap` v) (inputSchema name) inputSchema :: Text -> Maybe Schema- inputSchema name = case catMaybes [if specName s == name then Just (specInput s) else Nothing | s <- tools c] of+ inputSchema name = case catMaybes [if s.name == name then Just s.input else Nothing | s <- c.tools] of s : _ -> Just s [] -> Nothing
test-live/Live.hs view
@@ -15,9 +15,9 @@ deriving (Generic, Show, Contract) data BetterJoke- = DadJoke {setup' :: Text, punchline' :: Text}+ = DadJoke {setup :: Text, punchline :: Text} | OneLiner {line :: Text}- | KnockKnock {whosThere :: Text, punchline' :: Text}+ | KnockKnock {whosThere :: Text, punchline :: Text} deriving (Generic, Show, Contract) data Square = Blank | X | O
test/Spec.hs view
@@ -20,8 +20,8 @@ , instruction = "Suggest 3 dinosaurs" , input = Null , inputSchema = schemaOf SNull- , tools = [ToolSpec "search" "Search the fossil database" (codecSchema (contract @Text))]- , outputSchema = codecSchema (contract @[Text])+ , tools = [ToolSpec "search" "Search the fossil database" ((contract @Text).schema)]+ , outputSchema = (contract @[Text]).schema , history = [] } @@ -48,7 +48,7 @@ ) it "keeps a record's field order in its schema" $ do- let body = renderJson (requestBody anthropic conversation {outputSchema = codecSchema (contract @(Text, Text, Text))})+ let body = renderJson (requestBody anthropic conversation {outputSchema = (contract @(Text, Text, Text)).schema}) at' k = T.length (fst (T.breakOn k body)) (at' "\"_1\"" < at' "\"_2\"", at' "\"_2\"" < at' "\"_3\"") `shouldBe` (True, True) @@ -73,15 +73,15 @@ ) it "reads tool calls, unwrapping their input" $- fmap action (decoded (fromJust (J.decode "{\"stop_reason\":\"tool_use\",\"content\":[{\"type\":\"thinking\",\"thinking\":\"\",\"signature\":\"x\"},{\"type\":\"tool_use\",\"id\":\"t1\",\"name\":\"search\",\"input\":{\"value\":\"rex\"}}]}")))+ fmap (.action) (decoded (fromJust (J.decode "{\"stop_reason\":\"tool_use\",\"content\":[{\"type\":\"thinking\",\"thinking\":\"\",\"signature\":\"x\"},{\"type\":\"tool_use\",\"id\":\"t1\",\"name\":\"search\",\"input\":{\"value\":\"rex\"}}]}"))) `shouldBe` Right (CallTools [ToolCall "t1" "search" (String "rex")]) it "reads a final answer, unwrapping it" $- fmap action (decoded (fromJust (J.decode "{\"stop_reason\":\"end_turn\",\"content\":[{\"type\":\"text\",\"text\":\"{\\\"value\\\":[\\\"Stegosaurus\\\"]}\"}]}")))+ fmap (.action) (decoded (fromJust (J.decode "{\"stop_reason\":\"end_turn\",\"content\":[{\"type\":\"text\",\"text\":\"{\\\"value\\\":[\\\"Stegosaurus\\\"]}\"}]}"))) `shouldBe` Right (Respond (Array [String "Stegosaurus"])) it "keeps the whole content, thinking included, as the raw turn" $- fmap raw (decoded (fromJust (J.decode "{\"stop_reason\":\"end_turn\",\"content\":[{\"type\":\"thinking\",\"thinking\":\"\",\"signature\":\"x\"},{\"type\":\"text\",\"text\":\"{}\"}]}")))+ fmap (.raw) (decoded (fromJust (J.decode "{\"stop_reason\":\"end_turn\",\"content\":[{\"type\":\"thinking\",\"thinking\":\"\",\"signature\":\"x\"},{\"type\":\"text\",\"text\":\"{}\"}]}"))) -- aeson orders object keys, which JSON ignores; the blocks themselves are unchanged. `shouldBe` Right (Raw (Array [Object [("signature", String "x"), ("thinking", String ""), ("type", String "thinking")], Object [("text", String "{}"), ("type", String "text")]]))