agentic-openai 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-openai.cabal +1/−1
- src/Agentic/OpenAI.hs +11/−11
- test-live/Live.hs +2/−2
- test/Spec.hs +5/−5
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Changelog for agentic-openai +## 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-openai.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: agentic-openai-version: 0.2.0.3+version: 0.2.0.4 synopsis: OpenAI as the System Two provider for agentic description: Runs agentic's draft steps on OpenAI models through the Responses API, with strict structured outputs and tools, and can stand in as System One.
src/Agentic/OpenAI.hs view
@@ -141,15 +141,15 @@ A.Object $ [("model", A.String cfg.model)] <> maybe [] (\s -> [("instructions", A.String s)]) cfg.system- <> [("tools", A.Array (map tool (tools c))) | not (null (tools c))]- <> [ ("input", A.Array (task : concatMap exchange (history c)))+ <> [("tools", A.Array (map tool c.tools)) | not (null c.tools)]+ <> [ ("input", A.Array (task : concatMap exchange c.history)) , ( "text" , A.Object [ ( "format" , A.Object [ ("type", A.String "json_schema")- , ("name", A.String (schemaName (outputSchema c)))- , ("schema", objectSchema (outputSchema c))+ , ("name", A.String (schemaName c.outputSchema))+ , ("schema", objectSchema c.outputSchema) , ("strict", A.Bool True) ] )@@ -161,16 +161,16 @@ <> maybe [] (\e -> [("reasoning", A.Object [("effort", A.String (effortName e))])]) cfg.effort <> maybe [] (\n -> [("max_output_tokens", A.Integer (toInteger n))]) cfg.maxTokens where- task = message "user" (instructionText (instruction c) <> inputText)- inputText = case input c of+ task = message "user" (c.instruction.text <> inputText)+ inputText = case c.input of A.Null -> "" s -> "\n\nInput:\n" <> A.renderJson s tool spec = A.Object [ ("type", A.String "function")- , ("name", A.String (specName spec))- , ("description", A.String (specDescription spec))- , ("parameters", objectSchema (specInput spec))+ , ("name", A.String spec.name)+ , ("description", A.String spec.description)+ , ("parameters", objectSchema spec.input) , ("strict", A.Bool True) ] -- A turn's raw value is its list of output items, which go back as input.@@ -235,13 +235,13 @@ -- 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 decodeJson text of- Just v -> unwrap (outputSchema c) v+ Just v -> unwrap c.outputSchema v Nothing -> A.String text json t = maybe (A.String t) id (decodeJson t) decodeJson t = fromAeson <$> J.decode (TL.encodeUtf8 (TL.fromStrict t)) unwrapInput name v = maybe v (`unwrap` v) (inputSchema name) inputSchema :: Text -> Maybe Schema- inputSchema name = case [specInput s | s <- tools c, specName s == name] of+ inputSchema name = case [s.input | s <- c.tools, s.name == name] 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 Groan = Mild | Solid | Unbearable
test/Spec.hs view
@@ -29,8 +29,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 = [] } @@ -57,7 +57,7 @@ ) it "names the schema after its type, and shares repeated types through $defs" $ do- let format = at "format" (at "text" (body conversation {outputSchema = codecSchema (contract @Board)}))+ let format = at "format" (at "text" (body conversation {outputSchema = (contract @Board).schema})) schema = at "schema" format at "name" format `shouldBe` J.String "Board" at "top" (at "properties" schema) `shouldBe` fromJust (J.decode "{\"$ref\":\"#/$defs/Row\"}")@@ -90,11 +90,11 @@ ) it "reads function calls, parsing and unwrapping their arguments" $- fmap action (decoded (fromJust (J.decode "{\"status\":\"completed\",\"output\":[{\"type\":\"reasoning\"},{\"type\":\"function_call\",\"call_id\":\"c1\",\"name\":\"search\",\"arguments\":\"{\\\"value\\\":\\\"rex\\\"}\"}]}")))+ fmap (.action) (decoded (fromJust (J.decode "{\"status\":\"completed\",\"output\":[{\"type\":\"reasoning\"},{\"type\":\"function_call\",\"call_id\":\"c1\",\"name\":\"search\",\"arguments\":\"{\\\"value\\\":\\\"rex\\\"}\"}]}"))) `shouldBe` Right (CallTools [ToolCall "c1" "search" (String "rex")]) it "reads a final answer from the message text" $- fmap action (decoded (fromJust (J.decode "{\"status\":\"completed\",\"output\":[{\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"text\":\"{\\\"value\\\":[\\\"Stegosaurus\\\"]}\"}]}]}")))+ fmap (.action) (decoded (fromJust (J.decode "{\"status\":\"completed\",\"output\":[{\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"text\":\"{\\\"value\\\":[\\\"Stegosaurus\\\"]}\"}]}]}"))) `shouldBe` Right (Respond (Array [String "Stegosaurus"])) it "reports refusals and truncation as errors" $ do