diff --git a/simplex-bot.hs b/simplex-bot.hs new file mode 100644 index 0000000..268dbfa --- /dev/null +++ b/simplex-bot.hs @@ -0,0 +1,96 @@ +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE DeriveGeneric #-} + +module Main where + +import Simplex.Chat.Bot +import Simplex.Chat.Controller (versionNumber) +import Simplex.Chat.Core +import Simplex.Chat.Options +import Simplex.Chat.Terminal (terminalChatConfig) +import System.Directory (getAppUserDataDirectory) +import qualified Data.Text as T +import qualified Data.Text.Encoding as TE +import Network.HTTP.Simple +import Data.Aeson +import GHC.Generics +import qualified Data.ByteString.Lazy as BL +import System.Environment (getEnv) +import Control.Monad.IO.Class (liftIO) + +-- Data types for Venice.ai API +data ChatMessage = ChatMessage + { role :: String + , content :: String + } deriving (Show, Generic) + +data ChatRequest = ChatRequest + { model :: String + , messages :: [ChatMessage] + , temperature :: Float + } deriving (Show, Generic) + +data ChatResponse = ChatResponse + { choices :: [Choice] + } deriving (Show, Generic) + +data Choice = Choice + { message :: ChatMessage + } deriving (Show, Generic) + +instance ToJSON ChatMessage +instance ToJSON ChatRequest +instance FromJSON ChatMessage +instance FromJSON ChatResponse +instance FromJSON Choice + +-- Venice API client function +callVeniceAPI :: String -> String -> IO (Maybe String) +callVeniceAPI apiKey userMessage = do + let request = ChatRequest + { model = "venice-model" -- Replace with actual Venice model name + , messages = [ChatMessage "user" userMessage] + , temperature = 0.7 + } + + let jsonRequest = encode request + initReq <- parseRequest "POST https://api.venice.ai/api/v1/chat/completions" + let req = setRequestMethod "POST" + . setRequestHeader "Content-Type" ["application/json"] + . setRequestHeader "Authorization" ["Bearer " <> TE.encodeUtf8 (T.pack apiKey)] + . setRequestBodyLBS jsonRequest + $ initReq + + response <- httpLBS req + let decoded = decode (getResponseBody response) :: Maybe ChatResponse + return $ case decoded of + Just chatResponse -> case choices chatResponse of + (choice:_) -> Just $ content (message choice) + _ -> Nothing + Nothing -> Nothing + +main :: IO () +main = do + opts <- welcomeGetOpts + apiKey <- getEnv "VENICE_API_KEY" + simplexChatCore terminalChatConfig opts $ + chatBotRepl welcomeMessage $ \_contact msg -> do + case words msg of + ("/bot":rest) -> do + response <- liftIO $ callVeniceAPI apiKey (unwords rest) + pure $ case response of + Just reply -> reply + Nothing -> "Sorry, I couldn't process that request." + _ -> pure "" -- Don't respond to messages without the /bot prefix + +welcomeMessage :: String +welcomeMessage = "Hello! I'm a Venice.ai powered chat bot.\nUse /bot followed by your message to talk to me." + +welcomeGetOpts :: IO ChatOpts +welcomeGetOpts = do + appDir <- getAppUserDataDirectory "simplex" + opts@ChatOpts {coreOptions = CoreChatOpts {dbFilePrefix}} <- getChatOpts appDir "simplex_bot" + putStrLn $ "SimpleX Chat Bot v" ++ versionNumber + putStrLn $ "db: " <> dbFilePrefix <> "_chat.db, " <> dbFilePrefix <> "_agent.db" + pure opts