openai 2.0.0 → 2.1.0
raw patch · 4 files changed
+77/−7 lines, 4 files
Files
- CHANGELOG.md +4/−0
- openai.cabal +1/−1
- src/OpenAI/V1/Responses.hs +14/−6
- tasty/Main.hs +58/−0
CHANGELOG.md view
@@ -1,3 +1,7 @@+2.1.0:++- Add `Item_Input_Reasoning` constructor and JSON round-trip test so reasoning traces from tool calls can be echoed back via the Responses API.+ 2.0.0: - **BREAKING CHANGE**: Renamed `Item_InputMessage` to `Item_Input_Message` in `OpenAI.V1.Responses` for consistency with new constructors
openai.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: openai-version: 2.0.0+version: 2.1.0 synopsis: Servant bindings to OpenAI description: This package provides comprehensive and type-safe bindings to OpenAI, providing both a Servant interface and
src/OpenAI/V1/Responses.hs view
@@ -74,7 +74,7 @@ -- | Role of an input message data InputRole = User | System | Developer- deriving stock (Generic, Show)+ deriving stock (Eq, Generic, Show) instance FromJSON InputRole where parseJSON = genericParseJSON aesonOptions@@ -88,7 +88,7 @@ | Input_Image { image_url :: Maybe Text, file_id :: Maybe Text, detail :: Maybe Text } | Input_File { file_id :: Maybe Text, filename :: Maybe Text, file_url :: Maybe Text, file_data :: Maybe Text } | Input_Audio { input_audio :: Object }- deriving stock (Generic, Show)+ deriving stock (Eq, Generic, Show) inputContentOptions :: Options inputContentOptions =@@ -125,10 +125,17 @@ , output :: Text , status :: Maybe Text }+ | Item_Input_Reasoning+ { reasoning_id :: Text+ , reasoning_encrypted_content :: Maybe Text+ , reasoning_summary :: Maybe (Vector SummaryPart)+ , reasoning_content :: Maybe (Vector ReasoningText)+ , reasoning_status :: Maybe Text+ } | Item_Input_Item_Reference { id :: Maybe Text }- deriving stock (Generic, Show)+ deriving stock (Eq, Generic, Show) inputItemOptions :: Options inputItemOptions =@@ -136,6 +143,7 @@ { sumEncoding = TaggedObject{ tagFieldName = "type", contentsFieldName = "" } , tagSingleConstructors = True , constructorTagModifier = stripPrefix "Item_Input_"+ , fieldLabelModifier = stripPrefix "reasoning_" } instance FromJSON InputItem where@@ -491,7 +499,7 @@ -- | Reasoning summary part data SummaryPart = Summary_Text{ text :: Text }- deriving stock (Generic, Show)+ deriving stock (Eq, Generic, Show) summaryPartOptions :: Options summaryPartOptions = aesonOptions@@ -507,7 +515,7 @@ -- | Reasoning text part data ReasoningText = Reasoning_Text{ text :: Text }- deriving stock (Generic, Show)+ deriving stock (Eq, Generic, Show) reasoningTextOptions :: Options reasoningTextOptions = aesonOptions@@ -528,7 +536,7 @@ , summary :: Maybe (Vector SummaryPart) , content :: Maybe (Vector ReasoningText) , status :: Maybe Text- } deriving stock (Generic, Show)+ } deriving stock (Eq, Generic, Show) instance FromJSON ReasoningItem where parseJSON = genericParseJSON aesonOptions
tasty/Main.hs view
@@ -5,6 +5,8 @@ module Main where +import Data.Aeson ((.=))+import qualified Data.Aeson as Aeson import Control.Exception (SomeException, catch) import OpenAI.V1 (Methods(..)) import OpenAI.V1.Audio.Speech (CreateSpeech(..), Voice(..), _CreateSpeech)@@ -975,6 +977,61 @@ return () + let responsesReasoningInputSerializationTest =+ HUnit.testCase "Responses - reasoning input serialization" do+ let reasoningItem =+ Responses.Item_Input_Reasoning+ { Responses.reasoning_id = "reasoning_123"+ , Responses.reasoning_encrypted_content = Just "ciphertext"+ , Responses.reasoning_summary =+ Just+ [ Responses.Summary_Text+ { Responses.text = "High-level plan" }+ ]+ , Responses.reasoning_content =+ Just+ [ Responses.Reasoning_Text+ { Responses.text = "Step 1: inspect tool output." }+ ]+ , Responses.reasoning_status = Just Responses.statusCompleted+ }+ encoded = Aeson.encode reasoningItem+ expected :: Aeson.Value+ expected =+ Aeson.object+ [ "type" .= ("reasoning" :: Text.Text)+ , "id" .= ("reasoning_123" :: Text.Text)+ , "encrypted_content" .= ("ciphertext" :: Text.Text)+ , "summary"+ .= ( [ Aeson.object+ [ "type" .= ("summary_text" :: Text.Text)+ , "text" .= ("High-level plan" :: Text.Text)+ ]+ ]+ :: [Aeson.Value]+ )+ , "content"+ .= ( [ Aeson.object+ [ "type" .= ("reasoning_text" :: Text.Text)+ , "text" .= ("Step 1: inspect tool output." :: Text.Text)+ ]+ ]+ :: [Aeson.Value]+ )+ , "status" .= (Responses.statusCompleted :: Text.Text)+ ]+ case Aeson.decode encoded of+ Nothing ->+ HUnit.assertFailure "Failed to decode encoded reasoning input item"+ Just decodedValue ->+ HUnit.assertEqual "Encoded JSON mismatch" expected decodedValue++ case Aeson.fromJSON expected of+ Aeson.Error err ->+ HUnit.assertFailure ("Round-trip decode failed: " <> err)+ Aeson.Success decoded ->+ HUnit.assertEqual "Round-trip mismatch" reasoningItem decoded+ let tests = speechTests <> [ transcriptionTest,@@ -994,6 +1051,7 @@ createImageVariationMaximalTest, createModerationTest, responsesMinimalTest,+ responsesReasoningInputSerializationTest, responsesStreamingHaikuTest, responsesCodeInterpreterStreamingTest, assistantsTest,