Google Bard API Project

This project uses Google's 'gemini-pro' LLM to make a simple chatbot. This is the same LLM used by Google Bard starting in December of 2023. It is comparable to ChatGPT 3.5.
You can request and API key from Google AI Studio at https://makersuite.google.com/app/apikey

The app uses the HTTP POST method of sending and receiving data to and from the API (generativelanguage.googleapis.com). How that works can be seen in the 'Chatbot' procedure in this app.

Header information is sent to the API URL in the 'RequestHeaders' block followed by a messages section in the 'PostText' block that sends a prompt to the API on the web. I was able to determine the POST headers by extracting them from a curl API example for this service:

curl --trace-ascii curltorawpost2.txt -H 'Content-Type: application/json' -d '{"contents":[{
"parts":[{"text":"What is a Yoda quote?"}]}]}' -X POST https://generativelanguage.googleapis.com/v1beta/models/gemini-pr
o:generateContent?key=(your api key here)

The '--trace-ascii curltorawpost2.txt' part sends raw POST output from the curl command to the host service to the text file curltorawpost2.txt.

The relevant parts of that output are as follows:

0000: POST /v1beta/models/gemini-pro:generateContent?key=AIzaSyDFbcSc4
0040: D2-mNg2Z0CSumchsSHXt3IK02Q HTTP/1.1
0065: Host: generativelanguage.googleapis.com
008e: User-Agent: curl/8.4.0
00a6: Accept: /
00b3: Content-Type: application/json
00d3: Content-Length: 59
00e7:
=> Send data, 59 bytes (0x3b)
0000: {"contents":[{"parts":[{"text":"What is a Yoda quote?"}]}]}

where the App inventor Web component 'Url' block uses the 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=(your api key)' and the 'RequestHeaders' block contains the Host, Accept, Content-Type, and Content-Length information.

The PostText block contains the message information that includes the prompt to the LLM -- in this case "What is a Yoda quote?" that would be stored in the 'UserPrompt' variable.

The 'ChatbotAPI.GotText' event block receives the reply embedded in a json string which is then extracted and displayed on the device screen and optionally spoken using the text-to-speech component. I have not learned how to use the Dictionary blocks yet. Some of you could no doubt simplify accessing the embedded response which is linked to a "text" key label.

For fun I added speech input as well, so the app user and chatbot can have a voice conversation (the user must press the record button each time they speak, however, sort of like a walkie-talkie.)

Here's is version 2 of project modified 12/20/23 at 7:55pm CST. Changes: select list item blocks replaced with Dictionary blocks for the response content:

Gemini_ProV2.aia (6.6 KB)

END

2 Likes

please consider to use
image
to replace the big pile of ‘SELECT LIST ITEM’

1 Like

I was studying how Dictionary blocks work today. I just can't wrap my head around how to use them yet. If you can simplify this solution, I would be grateful.

I need to know the responseContent in text.

1 Like

Here ya go!

(this was in the MITAI2) app --

Prompt: "What is a Yoda quote?"

Response from gemini-pro:

{
"candidates": [
{
"content": {
"parts": [
{
"text": "* "Do or do not. There is no try."\n* "Fear is the path to the dark side. Fear leads to anger. Anger leads to hate. Hate leads to suffering."\n* "Judge me by my size, do you?"\n* "When you look at the dark side, careful you must be. For the dark side looks back."\n* "Patience you must have, my young Padawan."\n* "Control, control, you must learn control!"\n* "You must unlearn what you have learned."\n* "In a dark place we find ourselves, and a little more knowledge lights our way."\n* "The greatest teacher, failure is."\n* "Once you start down the dark path, forever will it dominate your destiny, consume you it will, as it did Obi-Wan's apprentice."\n* "Size matters not. Look at me. Judge me by my size, do you?"\n* "Train yourself to let go of everything you fear to lose."\n* "You will know when you are calm, at peace. Passive. A Jedi uses the Force for knowledge and defense, never for attack."\n* "Named must your fear be before banish it you can.""
}
],
"role": "model"
},
"finishReason": "STOP",
"index": 0,
"safetyRatings": [
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_HARASSMENT",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"probability": "NEGLIGIBLE"
}
],
"citationMetadata": {
"citationSources": [
{
"startIndex": 3,
"endIndex": 138,
"uri": "Category: Movies - Jordan Jeffers",
"license": ""
},
{
"startIndex": 38,
"endIndex": 177,
"uri": "https://ptl2010.com/page/1269/?chocaid=397",
"license": ""
},
{
"startIndex": 177,
"endIndex": 310,
"uri": "GitHub - celestialcrow/Yoda_Advice",
"license": ""
},
{
"startIndex": 521,
"endIndex": 644,
"uri": "Sith - Wikipedia",
"license": ""
}
]
}
}
],
"promptFeedback": {
"safetyRatings": [
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_HARASSMENT",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"probability": "NEGLIGIBLE"
}
]
}
}

END


This looks better, but can it be improved with other dictionary blocks? I also see that I have duplicated the data. So the json and output would be smaller.

May the path be with you.
blocks (1)

2 Likes

Yes, I saw these blocks. I cannot get them to work. BTW. I found this interesting that lookup in pairs can work in a similar way to get value for key in dictionary:

Reading your blocks right to left, I would think this is the path you need, one item per line:

candidates
1
content
parts
1
text

However, the text part is fouled by the markup between the Yoda quotes.
I am surprised you got it through the JSON conversion.

1 Like

Could you export a draggable block of that JSON text, one that survives the Web1.JSONDecodeWithDictionaries block?

1 Like

Correct. When we added the dictionaries implementation we tried to make sure that dictionaries and associative lists (lists of pairs) are interoperable. You can pass either type to either set of blocks and the language will coerce the representation. Of course, this is slower than passing it to the block that expects the correct type.

As for the get values at key path, the first argument specifies a list of the keys and/or indices that should be taken to retrieve a value somewhere in the object tree. It effectively collapses the nested blocks into a list of steps, so read the blocks from right to left to get the list you would need, e.g., ["candidates", 1, "content", "parts", 1, "text"]

1 Like

This extension will help you to create this project without any additional effort and with more features like "streaming" and also enable you to use the "Gemini-pro-vision" model too

1 Like

I am not sure what you mean by export a draggable block. Is this what you mean, ABG?

If you right click on a block and select "Download as PNG", it will create a PNG file with metadata that allows App Inventor to reconstruct the blocks when you drag and drop the image into the blocks editor.

If you were to apply a Do It to that global debug in your example, highlight all the data in the Do It result, hit Copy, then paste that JSON text into a text block, hopefully that would result in a text block full of legal JSON.

If you right click on that text block and choose Download Block as png (individual block, not all blocks), you would end up with a .png file.

Now here's the beauty of AI2.

That .png file, if left uneditted, can be posted here, and the posted file can be dragged into some one else's Blocks Editor workspace where it will magically appear, ready to feed some one else's app.

(Canned Reply: ABG - Download those blocks and post them here)

Please download and post each of those event block(s)/procedures here ...
P.S. These blocks can be dragged directly into your Blocks Editor workspace.

See Download Block Images for a demo.

candidates
1
content
parts
1
text

That block arrangement is working for me when using the Web component JsonTextDecodeWithDictionaries block.

1 Like

See this guide

Taifun


Pretty cool!

(these blocks can be imported into the blocks editor)

Thanks, Taifun. I did not understand the MIT Dictionary examples.

I consulted with Bing, Bard, Perplexity, Claude 2 and ChatGPT -- all confirmed that there are no examples of nested lists using MITAI2 Dictionary blocks. So now we have one. This solution uses about the same number of blocks, but uses both the 'get value at key path' and 'get value for key' blocks. The blocks extract the contents of the "text" key. Is this a more elegant solution than the one using a combination of 'get value for key' and 'select list item'? You be the judge. Personally, I find it less intuitive.

partial json object with nested lists:

{"candidates": [{"content": {"parts": [{"text": "“Do or do not. There is no try.”"}],...

(these blocks can be imported into the blocks editor)