intelli-monad-0.1.0.2: src/IntelliMonad/Tools/DallE3.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module IntelliMonad.Tools.DallE3 where
import Codec.Picture
import Control.Monad.IO.Class
import qualified Data.Aeson as A
import qualified Data.ByteString.Lazy as LBS
import Data.Maybe (fromMaybe)
import qualified Data.OSC1337 as OSC
import qualified Data.Sixel as Sixel
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics
import IntelliMonad.Types
import Network.HTTP.Client (httpLbs, newManager, parseUrlThrow, responseBody)
import Network.HTTP.Client.TLS (tlsManagerSettings)
import qualified OpenAI.API as API
import qualified OpenAI.Types as API
import Servant.Client
import System.Environment (getEnv, lookupEnv)
putImage :: FilePath -> IO (Either String ())
putImage image' = do
termProgram <- lookupEnv "TERM_PROGRAM"
imageBin <- readImage image'
case imageBin of
Left err -> return $ Left $ "Image file " ++ image' ++ " can not be read. : " ++ show err
Right imageBin' -> do
let image = convertRGB8 imageBin'
case termProgram of
Just "iTerm.app" -> do
OSC.putOSC image
putStrLn ""
Just "vscode" -> do
Sixel.putSixel image
putStrLn ""
_ -> do
Sixel.putSixel image
putStrLn ""
return $ Right ()
data DallE3 = DallE3
{ prompt :: T.Text,
size :: T.Text
}
deriving (Eq, Show, Generic, JSONSchema, A.FromJSON, A.ToJSON)
instance HasFunctionObject DallE3 where
getFunctionName = "image_generation"
getFunctionDescription = "Creating images from scratch based on a text prompt"
getFieldDescription "prompt" = "A text description of the desired image. The maximum length is 4000 characters."
getFieldDescription "size" = "The size of the generated images. Must be one of 1024x1024, 1792x1024, or 1024x1792 for dall-e-3 models."
instance Tool DallE3 where
data Output DallE3 = DallE3Output
{ code :: Int,
stdout :: String,
stderr :: String,
url :: Text
}
deriving (Eq, Show, Generic, A.FromJSON, A.ToJSON)
toolExec args = liftIO $ do
api_key <- (API.clientAuth . T.pack) <$> getEnv "OPENAI_API_KEY"
url <- parseBaseUrl "https://api.openai.com/v1/"
manager <- newManager tlsManagerSettings
let API.OpenAIBackend {..} = API.createOpenAIClient
let request =
API.CreateImageRequest
{ createImageRequestPrompt = args.prompt,
createImageRequestModel = Just $ API.CreateImageRequestModel "dall-e-3",
createImageRequestN = Nothing,
createImageRequestQuality = Nothing,
createImageRequestResponseUnderscoreformat = Just "url",
createImageRequestSize = Just args.size,
createImageRequestStyle = Nothing,
createImageRequestUser = Nothing
}
res <- API.callOpenAI (mkClientEnv manager url) $ createImage api_key request
let url' = case res of
(API.ImagesResponse _ (img : _)) -> fromMaybe "" (API.imageUrl img)
_ -> ""
let downloadImage = do
request' <- parseUrlThrow $ T.unpack url'
manager' <- newManager tlsManagerSettings
response <- httpLbs request' manager'
let imageBytes = Network.HTTP.Client.responseBody response
LBS.writeFile "image.png" imageBytes
downloadImage
err <- do
liftIO $
putImage "image.png" >>= \case
Left err -> return err
Right _ -> return ""
return $ DallE3Output 0 "" err url'