|
3 | 3 | {-# LANGUAGE ScopedTypeVariables #-}
|
4 | 4 |
|
5 | 5 | import System.Environment (getArgs, getEnv)
|
6 |
| -import qualified Data.Aeson as Aeson -- Used for Aeson.encode, Aeson.object etc. |
7 |
| -import Data.Aeson (FromJSON, ToJSON, eitherDecode) -- Specific functions needed |
8 |
| -import GHC.Generics (Generic) -- Needed for deriving ToJSON/FromJSON |
| 6 | +import qualified Data.Aeson as Aeson |
| 7 | +import Data.Aeson (FromJSON, ToJSON, eitherDecode) |
| 8 | +import GHC.Generics (Generic) |
9 | 9 | import Network.HTTP.Client.TLS (tlsManagerSettings)
|
10 | 10 | import Network.HTTP.Client (newManager, httpLbs, parseRequest, Manager, Request(..), RequestBody(..), Response(..), responseStatus)
|
11 | 11 | import Network.HTTP.Types.Status (statusCode)
|
12 |
| -import qualified Data.Text as T |
| 12 | +-- Replace qualified import with explicit import list: |
| 13 | +import Data.Text (Text, pack, unpack, splitOn, strip, null) |
13 | 14 | import Data.Text.Encoding (encodeUtf8)
|
14 | 15 | import Control.Exception (SomeException, handle)
|
15 | 16 |
|
16 | 17 | -- --- Request Data Types ---
|
17 | 18 |
|
18 | 19 | data RequestPart = RequestPart
|
19 |
| - { reqText :: T.Text -- Using reqText to avoid name clash with Response Part's text |
| 20 | + { reqText :: Text -- Using reqText to avoid name clash with Response Part's text |
20 | 21 | } deriving (Show, Generic)
|
21 | 22 |
|
22 | 23 | instance ToJSON RequestPart where
|
@@ -81,14 +82,14 @@ completion :: String -- ^ Google API Key
|
81 | 82 | -> IO (Either String String) -- ^ Left error message or Right completion text
|
82 | 83 | completion apiKey manager promptText = do
|
83 | 84 | initialRequest <- parseRequest "https://generativelanguage.googleapis.com/v1/models/gemini-2.0-flash:generateContent"
|
84 |
| - let reqContent = RequestContent { reqParts = [RequestPart { reqText = T.pack promptText }] } |
| 85 | + let reqContent = RequestContent { reqParts = [RequestPart { reqText = pack promptText }] } |
85 | 86 | let genConfig = GenerationConfig { temperature = 0.1, maxOutputTokens = 800 }
|
86 | 87 | let apiRequest = GeminiApiRequest { contents = [reqContent], generationConfig = genConfig }
|
87 | 88 |
|
88 | 89 | let request = initialRequest
|
89 | 90 | { requestHeaders =
|
90 | 91 | [ ("Content-Type", "application/json")
|
91 |
| - , ("x-goog-api-key", encodeUtf8 $ T.pack apiKey) |
| 92 | + , ("x-goog-api-key", encodeUtf8 $ pack apiKey) |
92 | 93 | ]
|
93 | 94 | , method = "POST"
|
94 | 95 | , requestBody = RequestBodyLBS $ Aeson.encode apiRequest
|
@@ -119,15 +120,83 @@ completion apiKey manager promptText = do
|
119 | 120 | let err = "Error: API request failed with status " ++ show (statusCode status) ++ "\nBody: " ++ show body
|
120 | 121 | return $ Left err
|
121 | 122 |
|
| 123 | +-- | Extracts potential place names from text using the Gemini API. |
| 124 | +findPlaces :: String -- ^ Google API Key |
| 125 | + -> Manager -- ^ HTTP Manager |
| 126 | + -> String -- ^ The input text to analyze (Renamed from text) |
| 127 | + -> IO (Either String [String]) -- ^ Left error or Right list of places |
| 128 | +findPlaces apiKey manager inputText = do |
| 129 | + -- Renamed parameter text -> inputText |
| 130 | + let prompt = "Extract only the place names strictly separated by commas from the following text. Do not include any explanation or introduction. Example: London,Paris,Tokyo\n\nText:\"" ++ inputText ++ "\"" |
| 131 | + apiResult <- completion apiKey manager prompt |
| 132 | + |
| 133 | + return $ case apiResult of |
| 134 | + Left err -> Left ("API call failed in findPlaces: " ++ err) |
| 135 | + Right responseText -> |
| 136 | + let -- Use Text functions directly (removed T. prefix) |
| 137 | + rawParts = splitOn (pack ",") (pack responseText) |
| 138 | + strippedParts = map strip rawParts |
| 139 | + nonEmptyParts = filter (not . Data.Text.null) strippedParts -- Use Data.Text.null to be explicit if Text type is inferred |
| 140 | + -- Convert final list back to [String] |
| 141 | + places = map unpack nonEmptyParts |
| 142 | + in Right places |
| 143 | + |
| 144 | +-- | Extracts potential person names from text using the Gemini API. |
| 145 | +findPeople :: String -- ^ Google API Key |
| 146 | + -> Manager -- ^ HTTP Manager |
| 147 | + -> String -- ^ The input text to analyze (Renamed from text) |
| 148 | + -> IO (Either String [String]) -- ^ Left error or Right list of people |
| 149 | +findPeople apiKey manager inputText = do |
| 150 | + -- Renamed parameter text -> inputText |
| 151 | + let prompt = "Extract only the person names strictly separated by commas from the following text. Do not include any explanation or introduction. Example: Alice,Bob,Charlie\n\nText:\"" ++ inputText ++ "\"" |
| 152 | + apiResult <- completion apiKey manager prompt |
| 153 | + |
| 154 | + return $ case apiResult of |
| 155 | + Left err -> Left ("API call failed in findPeople: " ++ err) |
| 156 | + Right responseText -> |
| 157 | + let -- Use Text functions directly (removed T. prefix) |
| 158 | + rawParts = splitOn (pack ",") (pack responseText) |
| 159 | + strippedParts = map strip rawParts |
| 160 | + nonEmptyParts = filter (not . Data.Text.null) strippedParts -- Use Data.Text.null to be explicit |
| 161 | + people = map unpack nonEmptyParts |
| 162 | + in Right people |
| 163 | + |
122 | 164 | -- --- Main Function ---
|
123 | 165 |
|
124 | 166 | main :: IO ()
|
125 | 167 | main = do
|
126 | 168 | args <- getArgs
|
127 | 169 | case args of
|
128 |
| - [] -> putStrLn "Error: Please provide a prompt as a command line argument." |
| 170 | + -- If no args, run demo extraction on sample text |
| 171 | + [] -> do |
| 172 | + putStrLn "No prompt provided. Running demo extraction on sample text:" |
| 173 | + let sampleText = "Dr. Evelyn Reed went to London last week with her colleague Bob Smith. They visited the Tower Bridge and met someone near Paris, Texas." |
| 174 | + putStrLn $ "Sample Text: \"" ++ sampleText ++ "\"\n" |
| 175 | + |
| 176 | + apiKeyResult <- lookupEnv "GOOGLE_API_KEY" |
| 177 | + case apiKeyResult of |
| 178 | + Nothing -> putStrLn "Error: GOOGLE_API_KEY environment variable not set." |
| 179 | + Just apiKey -> do |
| 180 | + manager <- newManager tlsManagerSettings |
| 181 | + |
| 182 | + -- Find Places |
| 183 | + putStrLn "Attempting to find places..." |
| 184 | + placesResult <- findPlaces apiKey manager sampleText |
| 185 | + case placesResult of |
| 186 | + Left err -> putStrLn $ "Error finding places: " ++ err |
| 187 | + Right places -> putStrLn $ "Found Places: " ++ show places |
| 188 | + |
| 189 | + putStrLn "\nAttempting to find people..." |
| 190 | + -- Find People |
| 191 | + peopleResult <- findPeople apiKey manager sampleText |
| 192 | + case peopleResult of |
| 193 | + Left err -> putStrLn $ "Error finding people: " ++ err |
| 194 | + Right people -> putStrLn $ "Found People: " ++ show people |
| 195 | + |
| 196 | + -- If args provided, run original completion behavior |
129 | 197 | (promptArg:_) -> do
|
130 |
| - apiKeyResult <- lookupEnv "GOOGLE_API_KEY" -- Using lookupEnv for safer handling |
| 198 | + putStrLn "Prompt provided. Running direct completion:" |
| 199 | + apiKeyResult <- lookupEnv "GOOGLE_API_KEY" |
131 | 200 | case apiKeyResult of
|
132 | 201 | Nothing -> putStrLn "Error: GOOGLE_API_KEY environment variable not set."
|
133 | 202 | Just apiKey -> do
|
|
0 commit comments