Skip to content

Commit f72612b

Browse files
committed
Gemini command line example
1 parent 67675f1 commit f72612b

File tree

4 files changed

+174
-1
lines changed

4 files changed

+174
-1
lines changed

gemini_commandline/Main.hs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import Control.Monad.IO.Class (liftIO)
2+
import System.Environment (getArgs, getEnv)
3+
import qualified Data.Aeson as Aeson
4+
import Data.Aeson (FromJSON, ToJSON)
5+
import GHC.Generics
6+
import Network.HTTP.Client.TLS (tlsManagerSettings)
7+
import Network.HTTP.Client (newManager, httpLbs, parseRequest, Request(..), RequestBody(..), responseBody, responseStatus)
8+
import Network.HTTP.Types.Status (statusCode)
9+
import qualified Data.Text as T
10+
import Data.Text.Encoding (encodeUtf8)
11+
import qualified Data.Vector as V
12+
13+
data GeminiRequest = GeminiRequest
14+
{ prompt :: String
15+
} deriving (Show, Generic, ToJSON)
16+
17+
data GeminiResponse = GeminiResponse
18+
{ candidates :: [Candidate] -- Changed from choices to candidates
19+
} deriving (Show, Generic, FromJSON)
20+
21+
data Candidate = Candidate
22+
{ content :: Content
23+
} deriving (Show, Generic, FromJSON)
24+
25+
data Content = Content
26+
{ parts :: [Part]
27+
} deriving (Show, Generic, FromJSON)
28+
29+
data Part = Part
30+
  { text :: String
31+
  } deriving (Show, Generic, FromJSON, ToJSON)
32+
33+
data PromptFeedback = PromptFeedback
34+
  { blockReason :: Maybe String
35+
  , safetyRatings :: Maybe [SafetyRating]
36+
  } deriving (Show, Generic, FromJSON, ToJSON)
37+
38+
data SafetyRating = SafetyRating
39+
  { category :: String
40+
  , probability :: String
41+
  } deriving (Show, Generic, FromJSON, ToJSON)
42+
43+
main :: IO ()
44+
main = do
45+
args <- getArgs
46+
case args of
47+
[] -> putStrLn "Error: Please provide a prompt as a command-line argument."
48+
(arg:_) -> do -- Extract the argument directly
49+
apiKey <- getEnv "GOOGLE_API_KEY"
50+
51+
manager <- newManager tlsManagerSettings
52+
53+
initialRequest <- parseRequest "https://generativelanguage.googleapis.com/v1/models/gemini-1.5-pro:generateContent"
54+
55+
let geminiRequestBody = Aeson.object [
56+
     ("contents", Aeson.Array $ V.singleton $ Aeson.object [
57+
         ("parts", Aeson.Array $ V.singleton $ Aeson.object [
58+
             ("text", Aeson.String $ T.pack arg)
59+
         ])
60+
     ]),
61+
     ("generationConfig", Aeson.object [
62+
         ("temperature", Aeson.Number 0.1),
63+
         ("maxOutputTokens", Aeson.Number 800)
64+
     ])
65+
]
66+
67+
let request = initialRequest
68+
{ requestHeaders =
69+
[ ("Content-Type", "application/json")
70+
, ("x-goog-api-key", encodeUtf8 $ T.pack apiKey)
71+
]
72+
, method = "POST"
73+
, requestBody = RequestBodyLBS $ Aeson.encode geminiRequestBody
74+
}
75+
76+
response <- httpLbs request manager
77+
78+
let responseStatus' = responseStatus response
79+
80+
if statusCode responseStatus' == 200
81+
   then do
82+
     let maybeGeminiResponse = Aeson.decode (responseBody response) :: Maybe GeminiResponse
83+
     case maybeGeminiResponse of
84+
       Just geminiResponse -> do
85+
         case candidates geminiResponse of
86+
           (candidate:_) -> do
87+
             case parts (content candidate) of
88+
               (part:_) -> do -- Changed text_ to _ since it's unused
89+
                 liftIO $ putStrLn $ "Response:\n\n" ++ text part
90+
                [] -> do
91+
liftIO $ putStrLn "Error: No parts in content"
92+
           [] -> do
93+
             liftIO $ putStrLn "Error: No candidates in response"
94+
Nothing -> do
95+
liftIO $ putStrLn "Error: Failed to parse response"
96+
   else do
97+
     putStrLn $ "Error: " ++ show responseStatus'

gemini_commandline/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Command Line Utility To Use the Google Gemini APIs
2+
3+
This example is similar to the example in ../webchat but here we build a command line application, not a web application to use the Google Gemini LLM APIs.
4+
5+
Example:
6+
7+
```
8+
$ gemini "what is the square of pi?"
9+
Response:
10+
11+
The square of pi (π) is π multiplied by itself: π². Since π is approximately 3.14159, π² is approximately 9.8696.
12+
```
13+
14+
The executable file **gemini** is on my path because I copied the executable file to my personal bin directory:
15+
16+
```
17+
$ cabal build
18+
$ find . -name gemini
19+
... output not shown
20+
$ cp ./dist-newstyle/build/aarch64-osx/ghc-9.4.8/gemini-0.1.0.0/x/gemini/build/gemini/gemini ~/bin
21+
```
22+
23+
If you don’t want to permanently install this example on your laptop, then just run it with cabal:
24+
25+
```
26+
$ cabal run gemini "what is 11 + 23?"
27+
Response:
28+
29+
11 + 23 = 34
30+
```

gemini_commandline/gemini.cabal

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
cabal-version: 2.4
2+
name: gemini
3+
version: 0.1.0.0
4+
-- synopsis:
5+
-- description:
6+
license: MIT
7+
license-file: LICENSE
8+
author: Bard
9+
maintainer: Bard
10+
-- copyright:
11+
-- category:
12+
build-type: Simple
13+
extra-source-files: CHANGELOG.md
14+
15+
executable gemini
16+
main-is: Main.hs
17+
-- other-modules:
18+
-- other-extensions:
19+
build-depends: base >= 4.7 && < 5
20+
, aeson
21+
, bytestring
22+
, text
23+
, http-client
24+
, http-client-tls
25+
, http-types
26+
, directory
27+
, vector
28+
if os(darwin)
29+
ghc-options:
30+
ld-options:
31+
32+
-- Language extensions used in the code
33+
default-extensions:
34+
OverloadedStrings
35+
DeriveGeneric
36+
LambdaCase
37+
DeriveAnyClass
38+
39+
program-default-options
40+
hsc2hs: --with-hsc2hs=/opt/homebrew/bin/hsc2hs
41+
42+
-- Compiler flags
43+
ghc-options: -Wall -O2
44+
45+

webchat/main.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ main = do
114114
              )
115115
            ]
116116

117+
117118
let request2 = initialRequest
118119
            { requestHeaders =
119120
                [ ("Content-Type", "application/json")
@@ -135,7 +136,7 @@ main = do
135136

136137
let maybeGeminiResponse = Aeson.decode (responseBody response2) :: Maybe GeminiResponse
137138

138-
liftIO $ putStrLn $ "Parsed response: " ++ show maybeGeminiResponse -- Debug print
139+
liftIO $ putStrLn $ "Parsed response: " ++ show maybeGeminiResponse
139140
liftIO $ hFlush stdout
140141

141142
case maybeGeminiResponse of

0 commit comments

Comments
 (0)