openai 2.2.0 → 2.2.1
raw patch · 6 files changed
+245/−3 lines, 6 filesdep ~basenew-component:exe:chat-completions-stream-examplePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: base
API changes (from Hackage documentation)
+ OpenAI.V1: [createChatCompletionStreamTyped] :: Methods -> CreateChatCompletion -> (Either Text ChatCompletionStreamEvent -> IO ()) -> IO ()
+ OpenAI.V1: [createChatCompletionStream] :: Methods -> CreateChatCompletion -> (Either Text Value -> IO ()) -> IO ()
+ OpenAI.V1.Chat.Completions: ChatCompletionChunk :: Text -> Vector ChunkChoice -> POSIXTime -> Model -> Maybe ServiceTier -> Maybe Text -> Text -> Maybe (Usage CompletionTokensDetails PromptTokensDetails) -> ChatCompletionChunk
+ OpenAI.V1.Chat.Completions: ChunkChoice :: Delta -> Maybe Text -> Natural -> Maybe LogProbs -> ChunkChoice
+ OpenAI.V1.Chat.Completions: Delta :: Maybe Text -> Maybe Text -> Maybe Text -> Maybe (Vector ToolCall) -> Delta
+ OpenAI.V1.Chat.Completions: [delta] :: ChunkChoice -> Delta
+ OpenAI.V1.Chat.Completions: [delta_content] :: Delta -> Maybe Text
+ OpenAI.V1.Chat.Completions: [delta_refusal] :: Delta -> Maybe Text
+ OpenAI.V1.Chat.Completions: [delta_role] :: Delta -> Maybe Text
+ OpenAI.V1.Chat.Completions: [delta_tool_calls] :: Delta -> Maybe (Vector ToolCall)
+ OpenAI.V1.Chat.Completions: [stream] :: CreateChatCompletion -> Maybe Bool
+ OpenAI.V1.Chat.Completions: data ChatCompletionChunk
+ OpenAI.V1.Chat.Completions: data ChunkChoice
+ OpenAI.V1.Chat.Completions: data Delta
+ OpenAI.V1.Chat.Completions: instance Data.Aeson.Types.FromJSON.FromJSON OpenAI.V1.Chat.Completions.ChatCompletionChunk
+ OpenAI.V1.Chat.Completions: instance Data.Aeson.Types.FromJSON.FromJSON OpenAI.V1.Chat.Completions.ChunkChoice
+ OpenAI.V1.Chat.Completions: instance Data.Aeson.Types.FromJSON.FromJSON OpenAI.V1.Chat.Completions.Delta
+ OpenAI.V1.Chat.Completions: instance Data.Aeson.Types.ToJSON.ToJSON OpenAI.V1.Chat.Completions.ChatCompletionChunk
+ OpenAI.V1.Chat.Completions: instance Data.Aeson.Types.ToJSON.ToJSON OpenAI.V1.Chat.Completions.ChunkChoice
+ OpenAI.V1.Chat.Completions: instance Data.Aeson.Types.ToJSON.ToJSON OpenAI.V1.Chat.Completions.Delta
+ OpenAI.V1.Chat.Completions: instance GHC.Generics.Generic OpenAI.V1.Chat.Completions.ChatCompletionChunk
+ OpenAI.V1.Chat.Completions: instance GHC.Generics.Generic OpenAI.V1.Chat.Completions.ChunkChoice
+ OpenAI.V1.Chat.Completions: instance GHC.Generics.Generic OpenAI.V1.Chat.Completions.Delta
+ OpenAI.V1.Chat.Completions: instance GHC.Show.Show OpenAI.V1.Chat.Completions.ChatCompletionChunk
+ OpenAI.V1.Chat.Completions: instance GHC.Show.Show OpenAI.V1.Chat.Completions.ChunkChoice
+ OpenAI.V1.Chat.Completions: instance GHC.Show.Show OpenAI.V1.Chat.Completions.Delta
+ OpenAI.V1.Chat.Completions: type ChatCompletionStreamEvent = ChatCompletionChunk
- OpenAI.V1: Methods :: (CreateSpeech -> IO ByteString) -> (CreateTranscription -> IO TranscriptionObject) -> (CreateTranslation -> IO TranslationObject) -> (CreateChatCompletion -> IO ChatCompletionObject) -> (CreateResponse -> IO ResponseObject) -> (CreateResponse -> (Either Text Value -> IO ()) -> IO ()) -> (CreateEmbeddings -> IO (Vector EmbeddingObject)) -> (CreateResponse -> (Either Text ResponseStreamEvent -> IO ()) -> IO ()) -> (CreateFineTuningJob -> IO JobObject) -> (Maybe Text -> Maybe Natural -> IO (Vector JobObject)) -> (FineTuningJobID -> Maybe Text -> Maybe Natural -> IO (Vector EventObject)) -> (FineTuningJobID -> Maybe Text -> Maybe Natural -> IO (Vector CheckpointObject)) -> (FineTuningJobID -> IO JobObject) -> (FineTuningJobID -> IO JobObject) -> (CreateBatch -> IO BatchObject) -> (BatchID -> IO BatchObject) -> (BatchID -> IO BatchObject) -> (Maybe Text -> Maybe Natural -> IO (Vector BatchObject)) -> (UploadFile -> IO FileObject) -> (Maybe Purpose -> Maybe Natural -> Maybe Order -> Maybe Text -> IO (Vector FileObject)) -> (FileID -> IO FileObject) -> (FileID -> IO DeletionStatus) -> (FileID -> IO ByteString) -> (CreateUpload -> IO (UploadObject (Maybe Void))) -> (UploadID -> AddUploadPart -> IO PartObject) -> (UploadID -> CompleteUpload -> IO (UploadObject FileObject)) -> (UploadID -> IO (UploadObject (Maybe Void))) -> (CreateImage -> IO (Vector ImageObject)) -> (CreateImageEdit -> IO (Vector ImageObject)) -> (CreateImageVariation -> IO (Vector ImageObject)) -> IO (Vector ModelObject) -> (Model -> IO ModelObject) -> (Model -> IO DeletionStatus) -> (CreateModeration -> IO Moderation) -> (CreateAssistant -> IO AssistantObject) -> (Maybe Natural -> Maybe Order -> Maybe Text -> Maybe Text -> IO (Vector AssistantObject)) -> (AssistantID -> IO AssistantObject) -> (AssistantID -> ModifyAssistant -> IO AssistantObject) -> (AssistantID -> IO DeletionStatus) -> (Thread -> IO ThreadObject) -> (ThreadID -> IO ThreadObject) -> (ThreadID -> ModifyThread -> IO ThreadObject) -> (ThreadID -> IO DeletionStatus) -> (ThreadID -> Message -> IO MessageObject) -> (ThreadID -> IO (Vector MessageObject)) -> (ThreadID -> MessageID -> IO MessageObject) -> (ThreadID -> MessageID -> ModifyMessage -> IO MessageObject) -> (ThreadID -> MessageID -> IO DeletionStatus) -> (ThreadID -> Maybe Text -> CreateRun -> IO RunObject) -> (CreateThreadAndRun -> IO RunObject) -> (ThreadID -> Maybe Natural -> Maybe Order -> Maybe Text -> Maybe Text -> IO (Vector RunObject)) -> (ThreadID -> RunID -> IO RunObject) -> (ThreadID -> RunID -> ModifyRun -> IO RunObject) -> (ThreadID -> RunID -> SubmitToolOutputsToRun -> IO RunObject) -> (ThreadID -> RunID -> IO RunObject) -> (ThreadID -> RunID -> Maybe Natural -> Maybe Order -> Maybe Text -> Maybe Text -> Maybe Text -> IO (Vector RunStepObject)) -> (ThreadID -> RunID -> StepID -> Maybe Text -> IO RunStepObject) -> (Text -> IO ResponseObject) -> (Text -> IO ResponseObject) -> (Text -> IO (Vector InputItem)) -> (CreateVectorStore -> IO VectorStoreObject) -> (Maybe Natural -> Maybe Order -> Maybe Text -> Maybe Text -> IO (Vector VectorStoreObject)) -> (VectorStoreID -> IO VectorStoreObject) -> (VectorStoreID -> ModifyVectorStore -> IO VectorStoreObject) -> (VectorStoreID -> IO DeletionStatus) -> (VectorStoreID -> CreateVectorStoreFile -> IO VectorStoreFileObject) -> (VectorStoreID -> Maybe Natural -> Maybe Order -> Maybe Text -> Maybe Text -> Maybe Status -> IO (Vector VectorStoreFileObject)) -> (VectorStoreID -> VectorStoreFileID -> IO VectorStoreFileObject) -> (VectorStoreID -> VectorStoreFileID -> IO DeletionStatus) -> (VectorStoreID -> CreateVectorStoreFileBatch -> IO VectorStoreFilesBatchObject) -> (VectorStoreID -> VectorStoreFileBatchID -> IO VectorStoreFilesBatchObject) -> (VectorStoreID -> VectorStoreFileBatchID -> IO VectorStoreFilesBatchObject) -> (VectorStoreID -> VectorStoreFileBatchID -> Maybe Natural -> Maybe Order -> Maybe Text -> Maybe Text -> Maybe Status -> IO (Vector VectorStoreFilesBatchObject)) -> Methods
+ OpenAI.V1: Methods :: (CreateSpeech -> IO ByteString) -> (CreateTranscription -> IO TranscriptionObject) -> (CreateTranslation -> IO TranslationObject) -> (CreateChatCompletion -> IO ChatCompletionObject) -> (CreateChatCompletion -> (Either Text Value -> IO ()) -> IO ()) -> (CreateChatCompletion -> (Either Text ChatCompletionStreamEvent -> IO ()) -> IO ()) -> (CreateResponse -> IO ResponseObject) -> (CreateResponse -> (Either Text Value -> IO ()) -> IO ()) -> (CreateEmbeddings -> IO (Vector EmbeddingObject)) -> (CreateResponse -> (Either Text ResponseStreamEvent -> IO ()) -> IO ()) -> (CreateFineTuningJob -> IO JobObject) -> (Maybe Text -> Maybe Natural -> IO (Vector JobObject)) -> (FineTuningJobID -> Maybe Text -> Maybe Natural -> IO (Vector EventObject)) -> (FineTuningJobID -> Maybe Text -> Maybe Natural -> IO (Vector CheckpointObject)) -> (FineTuningJobID -> IO JobObject) -> (FineTuningJobID -> IO JobObject) -> (CreateBatch -> IO BatchObject) -> (BatchID -> IO BatchObject) -> (BatchID -> IO BatchObject) -> (Maybe Text -> Maybe Natural -> IO (Vector BatchObject)) -> (UploadFile -> IO FileObject) -> (Maybe Purpose -> Maybe Natural -> Maybe Order -> Maybe Text -> IO (Vector FileObject)) -> (FileID -> IO FileObject) -> (FileID -> IO DeletionStatus) -> (FileID -> IO ByteString) -> (CreateUpload -> IO (UploadObject (Maybe Void))) -> (UploadID -> AddUploadPart -> IO PartObject) -> (UploadID -> CompleteUpload -> IO (UploadObject FileObject)) -> (UploadID -> IO (UploadObject (Maybe Void))) -> (CreateImage -> IO (Vector ImageObject)) -> (CreateImageEdit -> IO (Vector ImageObject)) -> (CreateImageVariation -> IO (Vector ImageObject)) -> IO (Vector ModelObject) -> (Model -> IO ModelObject) -> (Model -> IO DeletionStatus) -> (CreateModeration -> IO Moderation) -> (CreateAssistant -> IO AssistantObject) -> (Maybe Natural -> Maybe Order -> Maybe Text -> Maybe Text -> IO (Vector AssistantObject)) -> (AssistantID -> IO AssistantObject) -> (AssistantID -> ModifyAssistant -> IO AssistantObject) -> (AssistantID -> IO DeletionStatus) -> (Thread -> IO ThreadObject) -> (ThreadID -> IO ThreadObject) -> (ThreadID -> ModifyThread -> IO ThreadObject) -> (ThreadID -> IO DeletionStatus) -> (ThreadID -> Message -> IO MessageObject) -> (ThreadID -> IO (Vector MessageObject)) -> (ThreadID -> MessageID -> IO MessageObject) -> (ThreadID -> MessageID -> ModifyMessage -> IO MessageObject) -> (ThreadID -> MessageID -> IO DeletionStatus) -> (ThreadID -> Maybe Text -> CreateRun -> IO RunObject) -> (CreateThreadAndRun -> IO RunObject) -> (ThreadID -> Maybe Natural -> Maybe Order -> Maybe Text -> Maybe Text -> IO (Vector RunObject)) -> (ThreadID -> RunID -> IO RunObject) -> (ThreadID -> RunID -> ModifyRun -> IO RunObject) -> (ThreadID -> RunID -> SubmitToolOutputsToRun -> IO RunObject) -> (ThreadID -> RunID -> IO RunObject) -> (ThreadID -> RunID -> Maybe Natural -> Maybe Order -> Maybe Text -> Maybe Text -> Maybe Text -> IO (Vector RunStepObject)) -> (ThreadID -> RunID -> StepID -> Maybe Text -> IO RunStepObject) -> (Text -> IO ResponseObject) -> (Text -> IO ResponseObject) -> (Text -> IO (Vector InputItem)) -> (CreateVectorStore -> IO VectorStoreObject) -> (Maybe Natural -> Maybe Order -> Maybe Text -> Maybe Text -> IO (Vector VectorStoreObject)) -> (VectorStoreID -> IO VectorStoreObject) -> (VectorStoreID -> ModifyVectorStore -> IO VectorStoreObject) -> (VectorStoreID -> IO DeletionStatus) -> (VectorStoreID -> CreateVectorStoreFile -> IO VectorStoreFileObject) -> (VectorStoreID -> Maybe Natural -> Maybe Order -> Maybe Text -> Maybe Text -> Maybe Status -> IO (Vector VectorStoreFileObject)) -> (VectorStoreID -> VectorStoreFileID -> IO VectorStoreFileObject) -> (VectorStoreID -> VectorStoreFileID -> IO DeletionStatus) -> (VectorStoreID -> CreateVectorStoreFileBatch -> IO VectorStoreFilesBatchObject) -> (VectorStoreID -> VectorStoreFileBatchID -> IO VectorStoreFilesBatchObject) -> (VectorStoreID -> VectorStoreFileBatchID -> IO VectorStoreFilesBatchObject) -> (VectorStoreID -> VectorStoreFileBatchID -> Maybe Natural -> Maybe Order -> Maybe Text -> Maybe Text -> Maybe Status -> IO (Vector VectorStoreFilesBatchObject)) -> Methods
- OpenAI.V1.Chat.Completions: CreateChatCompletion :: Vector (Message (Vector Content)) -> Model -> Maybe Bool -> Maybe (Map Text Text) -> Maybe Double -> Maybe (Map Word Int) -> Maybe Bool -> Maybe Word -> Maybe Natural -> Maybe Natural -> Maybe (Vector Modality) -> Maybe Prediction -> Maybe AudioParameters -> Maybe Double -> Maybe ReasoningEffort -> Maybe ResponseFormat -> Maybe Integer -> Maybe (AutoOr ServiceTier) -> Maybe (Vector Text) -> Maybe Double -> Maybe Double -> Maybe (Vector Tool) -> Maybe ToolChoice -> Maybe Bool -> Maybe Text -> Maybe WebSearchOptions -> CreateChatCompletion
+ OpenAI.V1.Chat.Completions: CreateChatCompletion :: Vector (Message (Vector Content)) -> Model -> Maybe Bool -> Maybe (Map Text Text) -> Maybe Double -> Maybe (Map Word Int) -> Maybe Bool -> Maybe Word -> Maybe Natural -> Maybe Natural -> Maybe (Vector Modality) -> Maybe Prediction -> Maybe AudioParameters -> Maybe Double -> Maybe ReasoningEffort -> Maybe ResponseFormat -> Maybe Integer -> Maybe (AutoOr ServiceTier) -> Maybe (Vector Text) -> Maybe Bool -> Maybe Double -> Maybe Double -> Maybe (Vector Tool) -> Maybe ToolChoice -> Maybe Bool -> Maybe Text -> Maybe WebSearchOptions -> CreateChatCompletion
- OpenAI.V1.Chat.Completions: [choices] :: ChatCompletionObject -> Vector Choice
+ OpenAI.V1.Chat.Completions: [choices] :: ChatCompletionChunk -> Vector ChunkChoice
- OpenAI.V1.Chat.Completions: [created] :: ChatCompletionObject -> POSIXTime
+ OpenAI.V1.Chat.Completions: [created] :: ChatCompletionChunk -> POSIXTime
- OpenAI.V1.Chat.Completions: [finish_reason] :: Choice -> Text
+ OpenAI.V1.Chat.Completions: [finish_reason] :: ChunkChoice -> Maybe Text
- OpenAI.V1.Chat.Completions: [index] :: Choice -> Natural
+ OpenAI.V1.Chat.Completions: [index] :: ChunkChoice -> Natural
- OpenAI.V1.Chat.Completions: [logprobs] :: Choice -> Maybe LogProbs
+ OpenAI.V1.Chat.Completions: [logprobs] :: ChunkChoice -> Maybe LogProbs
- OpenAI.V1.Chat.Completions: [model] :: ChatCompletionObject -> Model
+ OpenAI.V1.Chat.Completions: [model] :: ChatCompletionChunk -> Model
- OpenAI.V1.Chat.Completions: [object] :: ChatCompletionObject -> Text
+ OpenAI.V1.Chat.Completions: [object] :: ChatCompletionChunk -> Text
- OpenAI.V1.Chat.Completions: [service_tier] :: ChatCompletionObject -> Maybe ServiceTier
+ OpenAI.V1.Chat.Completions: [service_tier] :: ChatCompletionChunk -> Maybe ServiceTier
- OpenAI.V1.Chat.Completions: [system_fingerprint] :: ChatCompletionObject -> Maybe Text
+ OpenAI.V1.Chat.Completions: [system_fingerprint] :: ChatCompletionChunk -> Maybe Text
- OpenAI.V1.Chat.Completions: [usage] :: ChatCompletionObject -> Usage CompletionTokensDetails PromptTokensDetails
+ OpenAI.V1.Chat.Completions: [usage] :: ChatCompletionChunk -> Maybe (Usage CompletionTokensDetails PromptTokensDetails)
Files
- CHANGELOG.md +4/−0
- examples/chat-completions-stream-example/Main.hs +90/−0
- openai.cabal +11/−1
- src/OpenAI/V1.hs +24/−0
- src/OpenAI/V1/Chat/Completions.hs +52/−1
- tasty/Main.hs +64/−1
CHANGELOG.md view
@@ -1,3 +1,7 @@+2.2.1:++- [Add Chat Completion Streaming Support](https://github.com/MercuryTechnologies/openai/pull/78)+ 2.2.0: - Add structured reasoning support to `OpenAI.V1.Responses`, including the `Reasoning`, `ReasoningEffort`, and `ReasoningSummary` types, plus the `ServiceTier` alias.
+ examples/chat-completions-stream-example/Main.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}++module Main where++import System.Environment (getEnv)+import System.IO (hFlush, hPutStrLn, stderr, stdout)++import qualified Data.Text as T+import qualified Data.Text.IO as TIO+import qualified OpenAI.V1 as V1+import qualified OpenAI.V1.Chat.Completions as Chat++main :: IO ()+main = do+ key <- T.pack <$> getEnv "OPENAI_KEY"+ env <- V1.getClientEnv "https://api.openai.com"++ let V1.Methods{ createChatCompletionStreamTyped } = V1.makeMethods env key Nothing Nothing++ let onEvent (Left err) = hPutStrLn stderr ("stream error: " <> T.unpack err)+ onEvent (Right chunk) = case chunk of+ -- Print content deltas as they arrive+ Chat.ChatCompletionChunk{ Chat.choices = cs } ->+ mapM_ printChoice cs+ where+ printChoice Chat.ChunkChoice{ Chat.delta = d } = case Chat.delta_content d of+ Just content -> TIO.putStr content >> hFlush stdout+ Nothing -> pure ()++ -- 1) Simple haiku example+ putStrLn "Example 1: Simple haiku"+ putStrLn "========================"+ let reqHaiku = Chat._CreateChatCompletion+ { Chat.messages =+ [ Chat.User+ { Chat.content = [ Chat.Text{ Chat.text = "Write a short haiku about the sea." } ]+ , Chat.name = Nothing+ }+ ]+ , Chat.model = "gpt-5-mini"+ }++ createChatCompletionStreamTyped reqHaiku onEvent+ putStrLn "\n"++ putStrLn "--------------------------------"++ -- 2) Conversation example+ putStrLn "Example 2: Multi-turn conversation"+ putStrLn "===================================="+ let reqConversation = Chat._CreateChatCompletion+ { Chat.messages =+ [ Chat.System+ { Chat.content = [ Chat.Text{ Chat.text = "You are a helpful assistant that explains concepts simply." } ]+ , Chat.name = Nothing+ }+ , Chat.User+ { Chat.content = [ Chat.Text{ Chat.text = "What is quantum computing in simple terms?" } ]+ , Chat.name = Nothing+ }+ ]+ , Chat.model = "gpt-5-mini"+ , Chat.temperature = Just 0.7+ }++ createChatCompletionStreamTyped reqConversation onEvent+ putStrLn "\n"++ putStrLn "--------------------------------"++ -- 3) Code generation example+ putStrLn "Example 3: Code generation"+ putStrLn "=========================="+ let reqCode = Chat._CreateChatCompletion+ { Chat.messages =+ [ Chat.User+ { Chat.content = [ Chat.Text{ Chat.text = "Write a Python function that calculates fibonacci numbers recursively." } ]+ , Chat.name = Nothing+ }+ ]+ , Chat.model = "gpt-5-mini"+ , Chat.temperature = Just 0.3+ }++ createChatCompletionStreamTyped reqCode onEvent+ putStrLn "\n"+
openai.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: openai-version: 2.2.0+version: 2.2.1 synopsis: Servant bindings to OpenAI description: This package provides comprehensive and type-safe bindings to OpenAI, providing both a Servant interface and@@ -125,6 +125,16 @@ hs-source-dirs: examples/openai-example main-is: Main.hs build-depends: base+ , openai+ , text+ ghc-options: -Wall++executable chat-completions-stream-example+ default-language: Haskell2010+ hs-source-dirs: examples/chat-completions-stream-example+ main-is: Main.hs+ build-depends: base+ , aeson , openai , text ghc-options: -Wall
src/OpenAI/V1.hs view
@@ -338,6 +338,22 @@ Aeson.Error msg -> onEvent (Left (Text.pack msg)) Aeson.Success e -> onEvent (Right e) + -- Streaming implementation for chat completions+ createChatCompletionStream req onEvent = do+ let req' = req{ Chat.Completions.stream = Just True }+ ssePostJSON "/v1/chat/completions" req' onEvent++ createChatCompletionStreamTyped+ :: CreateChatCompletion+ -> (Either Text Chat.Completions.ChatCompletionStreamEvent -> IO ())+ -> IO ()+ createChatCompletionStreamTyped req onEvent =+ createChatCompletionStream req $ \ev -> case ev of+ Left err -> onEvent (Left err)+ Right val -> case Aeson.fromJSON val of+ Aeson.Error msg -> onEvent (Left (Text.pack msg))+ Aeson.Success e -> onEvent (Right e)+ ssePostJSON :: ToJSON a => String -> a@@ -465,6 +481,14 @@ , createTranscription :: CreateTranscription -> IO TranscriptionObject , createTranslation :: CreateTranslation -> IO TranslationObject , createChatCompletion :: CreateChatCompletion -> IO ChatCompletionObject+ , createChatCompletionStream+ :: CreateChatCompletion+ -> (Either Text Aeson.Value -> IO ())+ -> IO ()+ , createChatCompletionStreamTyped+ :: CreateChatCompletion+ -> (Either Text Chat.Completions.ChatCompletionStreamEvent -> IO ())+ -> IO () , createResponse :: CreateResponse -> IO ResponseObject , createResponseStream :: CreateResponse
src/OpenAI/V1/Chat/Completions.hs view
@@ -1,6 +1,6 @@ -- | @\/v1\/chat\/completions@ ----- Streaming results are not yet supported+-- Streaming is not implemented here; module OpenAI.V1.Chat.Completions ( -- * Main types CreateChatCompletion(..)@@ -10,6 +10,11 @@ , Message(..) , messageToContent , Content(..)+ -- * Streaming types+ , ChatCompletionChunk(..)+ , ChunkChoice(..)+ , Delta(..)+ , ChatCompletionStreamEvent -- * Other types , InputAudio(..) , ImageURL(..)@@ -298,6 +303,7 @@ , seed :: Maybe Integer , service_tier :: Maybe (AutoOr ServiceTier) , stop :: Maybe (Vector Text)+ , stream :: Maybe Bool , temperature :: Maybe Double , top_p :: Maybe Double , tools :: Maybe (Vector Tool)@@ -333,6 +339,7 @@ , seed = Nothing , service_tier = Nothing , stop = Nothing+ , stream = Nothing , temperature = Nothing , top_p = Nothing , tools = Nothing@@ -394,6 +401,50 @@ , usage :: Usage CompletionTokensDetails PromptTokensDetails } deriving stock (Generic, Show) deriving anyclass (FromJSON, ToJSON)++-- | Delta message content for streaming+data Delta = Delta+ { delta_content :: Maybe Text+ , delta_refusal :: Maybe Text+ , delta_role :: Maybe Text+ , delta_tool_calls :: Maybe (Vector ToolCall)+ } deriving stock (Generic, Show)++deltaOptions :: Options+deltaOptions = aesonOptions+ { fieldLabelModifier = stripPrefix "delta_"+ }++instance FromJSON Delta where+ parseJSON = genericParseJSON deltaOptions++instance ToJSON Delta where+ toJSON = genericToJSON deltaOptions++-- | A streaming choice chunk+data ChunkChoice = ChunkChoice+ { delta :: Delta+ , finish_reason :: Maybe Text+ , index :: Natural+ , logprobs :: Maybe LogProbs+ } deriving stock (Generic, Show)+ deriving anyclass (FromJSON, ToJSON)++-- | Chat completion chunk (streaming response)+data ChatCompletionChunk = ChatCompletionChunk+ { id :: Text+ , choices :: Vector ChunkChoice+ , created :: POSIXTime+ , model :: Model+ , service_tier :: Maybe ServiceTier+ , system_fingerprint :: Maybe Text+ , object :: Text+ , usage :: Maybe (Usage CompletionTokensDetails PromptTokensDetails)+ } deriving stock (Generic, Show)+ deriving anyclass (FromJSON, ToJSON)++-- | Type alias for streaming events (currently just chunks)+type ChatCompletionStreamEvent = ChatCompletionChunk -- | Servant API type API =
tasty/Main.hs view
@@ -14,7 +14,7 @@ import OpenAI.V1.Audio.Translations (CreateTranslation(..)) import OpenAI.V1.AutoOr (AutoOr(..)) import OpenAI.V1.Batches (BatchObject(..), CreateBatch(..))-import OpenAI.V1.Chat.Completions (CreateChatCompletion(..), Modality(..))+import OpenAI.V1.Chat.Completions (CreateChatCompletion(..), Modality(..), ChatCompletionChunk(..), ChunkChoice(..), delta_content) import OpenAI.V1.Embeddings (CreateEmbeddings(..), EncodingFormat(..)) import OpenAI.V1.Files (FileObject(..), Order(..), UploadFile(..)) import OpenAI.V1.Images.Edits (CreateImageEdit(..))@@ -178,6 +178,7 @@ seed = Nothing, service_tier = Nothing, stop = Nothing,+ stream = Nothing, temperature = Nothing, top_p = Nothing, tools = Nothing,@@ -218,6 +219,7 @@ seed = Nothing, service_tier = Nothing, stop = Nothing,+ stream = Nothing, temperature = Nothing, top_p = Nothing, tools = Nothing,@@ -279,6 +281,7 @@ seed = Just 0, service_tier = Just Auto, stop = Just [">>>"],+ stream = Nothing, temperature = Just 1, top_p = Just 1, tools =@@ -302,6 +305,65 @@ return () + let chatCompletionStreamingHaikuTest = do+ HUnit.testCase "Create chat completion - streaming haiku" do+ let req =+ CreateChatCompletion+ { messages =+ [ Completions.User+ { content = ["Hello, world!"],+ name = Nothing+ }+ ],+ model = chatModel,+ store = Nothing,+ metadata = Nothing,+ frequency_penalty = Nothing,+ logit_bias = Nothing,+ logprobs = Nothing,+ top_logprobs = Nothing,+ max_completion_tokens = Nothing,+ n = Nothing,+ modalities = Nothing,+ prediction = Nothing,+ audio = Nothing,+ presence_penalty = Nothing,+ reasoning_effort = Nothing,+ response_format = Nothing,+ seed = Nothing,+ service_tier = Nothing,+ stop = Nothing,+ stream = Nothing,+ temperature = Nothing,+ top_p = Nothing,+ tools = Nothing,+ tool_choice = Nothing,+ parallel_tool_calls = Nothing,+ user = Nothing,+ web_search_options = Nothing+ }++ acc <- IORef.newIORef (Text.empty)+ done <- Concurrent.newEmptyMVar++ let onEvent (Left _err) = Concurrent.putMVar done ()+ onEvent (Right ev) = case ev of+ ChatCompletionChunk{ choices = cs } ->+ mapM_ accChoice cs+ where+ accChoice ChunkChoice{ delta = d } = case delta_content d of+ Just content -> IORef.modifyIORef' acc (<> content)+ Nothing -> Concurrent.putMVar done ()++ createChatCompletionStreamTyped req onEvent++ _ <- Concurrent.takeMVar done+ text <- IORef.readIORef acc+ HUnit.assertBool "Expected non-empty streamed text" (not (Text.null text))++ return ()+ + let embeddingsTest = do HUnit.testCase "Create embedding" do _ <-@@ -1039,6 +1101,7 @@ completionsMinimalTest, completionsMinimalReasoningTest, completionsMaximalTest,+ chatCompletionStreamingHaikuTest, embeddingsTest, fineTuningTest, batchesTest,