Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions openai-hs/src/OpenAI/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ module OpenAI.Client
ChatFunction (..),
ChatFunctionCall (..),
ChatFunctionCallStrategy (..),
ChatMessageContent (..),
ChatMessageContentPart (..),
ChatMessage (..),
ChatCompletionRequest (..),
ChatChoice (..),
Expand Down
52 changes: 51 additions & 1 deletion openai-servant/src/OpenAI/Resources.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ module OpenAI.Resources
ChatFunction (..),
ChatFunctionCall (..),
ChatFunctionCallStrategy (..),
ChatMessageContent (..),
ChatMessageContentPart (..),
ChatMessage (..),
ChatCompletionRequest (..),
ChatChoice (..),
Expand Down Expand Up @@ -86,8 +88,10 @@ module OpenAI.Resources
where

import qualified Data.Aeson as A
import qualified Data.Aeson.Types as A
import qualified Data.ByteString.Lazy as BSL
import Data.Maybe (catMaybes)
import Data.String (IsString(fromString))
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Data.Time
Expand Down Expand Up @@ -251,8 +255,54 @@ instance A.ToJSON ChatFunctionCall where
"arguments" A..= T.decodeUtf8 (BSL.toStrict (A.encode arguments))
]

newtype ChatMessageContent = ChatMessageContent [ChatMessageContentPart]
deriving (Eq, Show, Semigroup, Monoid)

instance IsString ChatMessageContent where
fromString s = ChatMessageContent [fromString s]

instance A.FromJSON ChatMessageContent where
parseJSON (A.String s) = pure $ ChatMessageContent [CMCP_Text s]
parseJSON (A.Array a) = ChatMessageContent <$> traverse A.parseJSON (V.toList a)
parseJSON invalid = A.typeMismatch "String or Array" invalid

instance A.ToJSON ChatMessageContent where
toJSON (ChatMessageContent xs) = go xs where
go [] = A.Array V.empty
go [CMCP_Text s] = A.String s
go xs' = A.Array . V.fromList $ map A.toJSON xs'

data ChatMessageContentPart = CMCP_Text T.Text
| CMCP_Image { imageUrl :: T.Text, imageDetail :: Maybe T.Text }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supernit: can be CMCPImageUrl?

deriving (Eq, Show)

instance IsString ChatMessageContentPart where
fromString = CMCP_Text . T.pack

instance A.FromJSON ChatMessageContentPart where
parseJSON (A.String s) = pure $ CMCP_Text s
parseJSON (A.Object o) = o A..: "type" >>= A.withText "Type" go where
go "text" = CMCP_Text <$> o A..: "text"
go "image_url" = o A..: "image_url" >>= img
go ty = A.unexpected $ A.String ty
img (A.String s) = pure $ CMCP_Image s Nothing
img (A.Object o') = CMCP_Image <$> o' A..: "url" <*> o' A..:? "detail"
img invalid = A.typeMismatch "String or Object" invalid
parseJSON invalid = A.typeMismatch "ChatMessageContentPart" invalid

instance A.ToJSON ChatMessageContentPart where
toJSON (CMCP_Text s) = A.object ["type" A..= ("text" :: T.Text), "text" A..= s]
toJSON (CMCP_Image u d) =
A.object [ "type" A..= ("image_url" :: T.Text),
"image_url" A..= case d of
Nothing -> A.String u
Just detail -> A.object [ "url" A..= u,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where did you find this? I don't see it in the API docs below but perhaps I'm looking in the wrong place?
image

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nevermind I found it! I needed to keep clicking the dropdowns - apologies!

@nickhs nickhs Mar 23, 2024

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and from the vision guide it looks like its an enum of high, low, and auto. Thoughts on making it an enum?

https://platform.openai.com/docs/guides/vision

"detail" A..= detail
]
]

data ChatMessage = ChatMessage
{ chmContent :: Maybe T.Text,
{ chmContent :: Maybe ChatMessageContent,
chmRole :: T.Text,
chmFunctionCall :: Maybe ChatFunctionCall,
chmName :: Maybe T.Text
Expand Down