Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
MarkFogle
Advisor
Advisor

Background


One activity that I like to engage in on weekends is learning about new compute topics that are outside my normal area of expertise (during the week, I’m a Senior Architect on the SAP BTP SDK for Android team). A couple of topics that have been on my radar for a while are “low-code, no-code” (specifically SAP’s Build Apps offering) and OpenAI APIs (specifically the “Chat Completion” API), so I figured “Why not do both at the same time… how difficult could that be?” Short answer - not difficult, not difficult at all, but that’s not to say there weren’t a few stumbling blocks along the way.

Step 1- Create an App with SAP Build


First things first - getting up to speed on SAP Build Apps. Being a complete novice in this area, I did a quick search for tutorials and landed almost immediately on “Create an Application with SAP Build Apps - a perfect introduction for a beginner like me. About an hour later, I had a functional app for scanning food product barcodes and displaying product name, image and caloric information - Step 1 accomplished!

Step 2 - Integrate the Chat Completion API


First, some exploration


On to Step 2, which I rightfully assumed might take a bit more than an hour. First, what enhancement could I make to the food scanner app within a reasonable amount of time, while still providing some relevant functionality. My first thought was, “I know… put a chat client into the app!”, but that didn’t really seem like it would fit the “reasonable amount of time” requirement and, as it turns out, kirill_l has already done some great work in this area, so no need to retread old ground. 

Going back then to the tutorial app and the product information displayed, I thought it might be interesting to display something more interesting than the caloric information… what about a clever marketing slogan based on the product name? 

Armed with the product name from the tutorial used in Step 1 (“Lakritsi Original”), I headed over to OpenAI Playground and submitted “Write some marketing copy explaining why I should buy Lakritsi Original” and received an eloquent response :



Using the Chat Playground to generate some marketing copy


Not bad… It reads a bit like a radio spot from the 1960’s, but pretty decent overall. One problem - it’s not going to fit very well within a mobile app because of the long length of the text, so I wanted to tighten it up a bit… 


Using the Chat Playground to generate a slogan instead


Much better. Combining the two prompts, I settled on “Write a marketing slogan explaining why I should buy product name” as the prompt to be used in the app.

Next, learning about the API and creating a key


Now, on to the fun part - adding the OpenAI “Chat Completion” API to the app. Fortunately, OpenAI has a great guide for its Chat Completion API. After reading through the guide and poking around a bit in the API documentation, I had a fairly good idea regarding the minimum parameters needed for the request as well as how to parse the response.

First, I had to get an OpenAI API Key to make any calls to any of the OpenAI APIs. For this, I went to https://platform.openai.com, created an account, and then created an API Key (available by navigating to “View API Keys” under account information and pressing “Create new secret key”) : 


Creating the API Key


As the dialog indicates, I made sure to copy the key before pressing OK, because I knew I wouldn't see it again (and don’t worry… I deleted this key right after creating this blog post, so don't get any ideas!).

Next, unfortunately, I had to enter a payment method for billing purposes. At the time of this writing, there’s no free tier when it comes to Chat Completion requests. Fortunately, the pricing is quite reasonable at $0.002 USD per 1K tokens (for reference, the chat completion used in this blog uses around 50 tokens). All the testing I did to write this blog entry (combined with some other miscellaneous poking around) came to a grand total of $0.05 USD : 


API usage costs for developing this integration



Storing the API Key


In order to keep the API Key in a central location where it can easily be changed (or revoked) as needed, I created an App Variable for it. These are located in the “APP VARIABLES” section of the “VARIABLES” pane.  I created a new App Variable, called it “APIKEY” and gave it the initial value of “Bearer ” (without the quotes) followed by the secret key I just created :


Creation of the APIKEY App Variable



Creating the OpenAI data entity


Having added the “Open Food Facts” data entity in the earlier tutorial, I was pretty sure I’d need another data entity for OpenAI, which should be a Direct REST Integration as well. Here’s what I entered for the “Base” information, with the mandatory parts outlined in red :


Base information for the Data Entity


The “Resource ID” and “Short description” can be anything, and the Resource URL of “https://api.openai.com/v1” comes from the OpenAI docs. The value of the AUTHORIZATION header (created by clicking on the + sign in the HTTP Header section) will be resolved at runtime using the APIKEY App Variable from the previous step (more on this later). Accordingly, I set both “Is static” and “Is optional” to off.

Next I came to the most complex (for me, anyway) part of this whole exercise; configuring the “POST” request.  First, I clicked on “CREATE RECORD (POST)” and toggled "Create record (POST) is current disabled, enable to configure" to "on" to ensure that “Method enabled” was toggled to true. Then, I entered “/chat/completions” in the “Relative Path” box as shown in the OpenAI API docs  :


Base configuration for the POST request


Then, I clicked on the SCHEMA tab to configure the Schema for the request and selected "Custom Schema" for both "Create record (POST) request schema" and "Create record (POST) response schema" to start creating the schema.

Pressing the "ADD PROPERTY" button to the right of "Properties of this schema", I added an element with a "Key" value of "messages", a "Value Type" of "List" and a "List Item Type" of "Object" :


Creating the POST Schema - Step 1


Note that this automatically adds a property of the object with the key value of "id" of type text.  Clicking on this property, I changed the Key to "content" and then added another Text property, "role", checking "Value is required" for both. There were more parameters I could have added here (based on the API documentation available at https://platform.openai.com/docs/api-reference/chat/create), but I kept it to a minimum for the time being :


Creating the POST Schema - Step 2


 

It was also at this point that I made the accidental discovery that changing the "Value type" from "Text" to "Number" resulted in a new "Initial value" field being added to the property panel (and that the field remained after toggling the "Value type" back to "Text"). Taking advantage of this, I was able to add initial values for the following properties :

  • role (inside the "messages" Object) - Initial value = user (this specifies that the content is entered by the user)

  • model - Initial value = gpt-3.5-turbo (the gpt-3.5-turbo model is the latest latest readily available language model, optimized for speed)


This prepared me for the next step which was...

Running a test


Having configured the request schema, I was ready to run a test (which also helped in creating the schema for the Response). I clicked on the "TEST" tab, and then on the "ABC" button below "Authorization" : 


Initializing the Authorization Header for the test


 On the next screen, I clicked on "Data and Variables" :


Selecting Data and Variables


Next, I clicked on "App Variable" :


Selecting App Variable


Finally, I selected "APIKEY" (the only App Variable available) and "SAVE" :


Saving the APIKEY


This, in turn, brought me back to the "TEST" tab with the Authorization Header now initialized. I then clicked on “Custom Object” in the “Record properties” section to fill that in :


TEST Tab with Authorization Header initialized


I noted that "gpt-3.5-turbo" and "user" were filled in automatically using the initial values from the previous step. The the full text I entered in the content box was “Write a marketing slogan explaining why I should buy Lakritsi Original” : 


Creating the test Request


I then pressed save and, having entered the test data, pressed “RUN TEST”. After a short period of time, the following appeared : 


Results of a successful test


A Status of "OK" was a definite requirement before proceeding, as it was my only indication that everything was configured properly.

Creating the Response schema


Having confirmed a Status of "OK", I then proceeded to create the Response schema. This was as simple as pressing “SET SCHEMA FROM RESPONSE”, which took me back to the SCHEMA tab once more. Scrolling down to the bottom, I saw : 


Top level of the Response schema


Expanding the “choices” section and then the “message” section within that produced this : 


Expanding the "choices" and "message" elements


As was the case with the POST request, it's really the content that matters in the response.  I'll come back to this later when modifying the Scan button logic.

First, though, I pressed “SAVE DATA ENTITY” to complete the configuration (and then saved the whole app for good measure - I definitely didn't want to have to create that POST Request schema again). 

Creating a Variable


Next, I added the variable to store the content of the response in order to eventually display it in the UI. To do this, I started by switching to the “Variables” view and clicking on “PAGE VARIABLES”, then "ADD PAGE VARIABLE" : 


Adding a Page Variable


 

I then created a new Text Variable named "slogan" :


Creating a Text variable named "slogan"


Finally, I saved the app again.

Adding more logic


Now it was time to add the logic steps to make the call to the Chat Completion API and populate the results into the Page Variable I'd just created. 

First, I went back to the UI Canvas View, selected the “Scan” button and pressed the “Add logic to BUTTON 1” link. I hadn't made any changes to the food scanner tutorial, so it looked like this :




Initial state of the Scan Button logic


Next, just as I used a “Get record” call to retrieve the product data in the tutorial from Step 1, I knew that I was going to use a “Create record” call against the OpenAI Data Entity in order to create the marketing slogan. So I dragged a “Create record” component into the logic editor and created a connection between the top connector on the “Get record” node for “OpenFoodFacts” and the input to the new “Create Record” node : 


Adding the Create record node


Then, I clicked on the “Create record” node and noticed that, in the Inputs section on the right, “OpenAI” was already selected as the Resource name (since OpenFoodFacts doesn’t have a Create record request). Right away though I could also see that I was going to have to enter the Authorization Header value again :


Entering the Authorization Header in the Create Record node


Here, I went through the same steps of selecting the APIKEY App Variable that I followed when running the Test Request; I won't bother repeating them here.

Once I was finished with that step, this is what the INPUTS panel looked like :


Create record INPUTS


I then clicked on “Custom object” under “Record properties”. Here, as expected, the model was already filled in :


Object properties with model filled in


Then, I clicked on “Custom list (0 items)” and then “Add a value”, noting that "user" was prefilled : 


Adding the role and content


Next I wanted to populate the “content” parameter with a formula composed of the static string "Write a marketing slogan explaining why I should buy" followed by the product name from the OpenFoodFacts GET request. 

To do this, I pressed on the “ABC” button under content, which brought up a collection of possible binding types. I clicked on “Formula” : 


Creating a formula for the "content" element


In the Formula dialog, I clicked in the Formula edit box, which brought up a context-sensitive editor. I entered the static string "Write a marketing slogan explaining why I should buy " (with the “non-smart” quotes) followed by a plus sign : 


Beginning of the "content" element formula


The validation logic complained that the syntax is incomplete… which it was. To complete it, I entered “product_name” in the search box : 


Searching for "product_name"


Then, I selected the entry labeled data.OpenFoodFacts1.product.product_name and pressed the Enter key (double-click would have worked as well) : 


The completed "content" Formula


Then I pressed “SAVE” followed by “SAVE” two more times, and then saved the app.

Setting the Variable


I knew I was entering the home stretch at this point… just one more logic step to go and then I could add the new marketing slogan to the UI. For this step, I dragged a “Set page variable” component to the right of the “Create record” node and drew a connection from the top output on the “Create record” node to the Variables’ input : 


Adding a new "Page variable" node


Then, I clicked on the “Set page variable” node that was just added. In the Properties pane, I noted that the "slogan" variable name was already entered (since it's the only Page Variable I had) and clicked on the "ABC" button below "Assigned Value" : 


Customize the Variable


On the next screen, I clicked on “Formula” :


Creating a Formula for the Page Variable


This took me to a Formula dialog containing an edit box with a pair of empty quotes : 


Initial "binding type" formula


I clicked in the “Formula” edit box to bring up the context-sensitive editor : 


Context-sensitive Formula editor


I removed the quotes and typed in “choices” (without the quotes) to narrow down the list of prospective source values : 


Entering "choices" into the formula


 

I then clicked on the one ending with “.message.content”, followed by a double-click (the Enter Key would have worked here as well) and then "SAVE", which took me back to the Page Variable properties :


Completed Page Variable


At this point I saved the app again. 

Updating the UI


And now, I had just one more step before being able to test out the updated app. Back in the UI Canvas view, I selected the Text element that was being used to display the caloric content : 


Initial contents of the Text element


In the “Properties” tab, I wanted to edit the formula to refer to the new page variable instead, so I clicked the box to the right of the “Formula” icon that contains the text starting with “data.OpenFoodFacts1” which displayed the context-sensitive edit dialog for the formula.

I then removed the existing content and selected "Page Variables" below the Search box, which displayed my "slogan" page variable : 


Text element formula


I double clicked on "pageVars.slogan", pressed "SAVE" and then "SAVE" again before finally saving the app one final time.

Testing the completed app


I then loaded the app into the SAP Build Apps preview app on my phone and tried scanning various food products, treating me to an assortment of clever and innovative product slogans! Here’s one that was generated for a bag of prawn crisps I happened to have sitting around :


A slogan generated by OpenAI for a bag of crisps



Conclusion


All things considered, not a bad way to spend an afternoon. I had broadened my knowledge of products in the SAP BTP portfolio and learned a bit about OpenAI as well!

I hope that you found reading this blog to be time well spent as well. Please let me know if you have any questions or suggested improvements… I’m still a novice on both of these topics, so all comments are welcome.

Disclaimer:
SAP notes that posts about potential uses of generative AI and large language models are merely the individual poster's ideas and opinions, and do not represent SAP's official position or future development roadmap. SAP has no legal obligation or other commitment to pursue any course of business, or develop or release any functionality, mentioned in any post or related content on this website.
13 Comments
Farid
Active Participant
Great blog! I spent a good part of my Sunday following your technical insights, and it was definitely worth my time.
MarkFogle
Advisor
Advisor
Thanks! I'm glad you found it helpful.
Venkat_Vyza
Active Participant
Hello Mark,

Truly this is great post!

I have been looking for something akin to this for a couple of weeks.

I'm trying to replicate it and got stuck at creating customer schema under "Create Record (POST) Request Schema". Could you please post the  print screens showing the properties of "messages" ?

Not sure how you got content and role under "messages"!?

 

Thank you,

Venkat
MarkFogle
Advisor
Advisor

Hi Venkat,

Glad you liked the post!

The screenshot showing the content under messages is the one titled "Creating the request schema", but I see now that I could have provided some additional clarification on that step. So, for the sake of completeness, once I started creating the custom schema, the steps were :

  1. Click on ADD PROPERTY
  2. Enter "messages" as the key and select "Object" from "Complex types" at the bottom of the "Value Type" drop down.
  3. This will create an object with one property, "id" of type Text :
  4. Either click on the "id" property and change its name to "content" or remove it (using the REMOVE PROPERTY button in the PROPERTIES panel) and add a new Text property called "content"
  5. Click on the plus sign to the right of "object with 1 property" and add a second Text property called role.  For this one, if you change the Value Type to "Number" and then back to "Text", an "Initial value" field will appear at the bottom of the PROPERTIES panel.  You can enter "user" here (without quotes) to provide a default role so that the value doesn't need to be specified on each call.

I hope this helps! I'll try adding some more content to the blog post to help clarify this step.

Regards,

Mark

Venkat_Vyza
Active Participant
0 Kudos
Hello Mark,

I greatly appreciate your quick help.

By the way, I'm getting the following error now while testing API call response.

Looks like it's related to value type of "content"! Could you please advise?

 


error while testing API call response


 

Thank you,

Venkat Vyza
MarkFogle
Advisor
Advisor
0 Kudos

Hi Venkat,

Yes... that's an error on my part in my previous response, as well as one of the screen shots in the blog post; "messages" should be a list of objects, not an individual object :

Thanks for catching that (and sorry about the confusion).

 

Regards,

Mark

MarkFogle
Advisor
Advisor

P.S. Here's the new screen shot I'm going to add to the blog, showing the initial appearance of the "messages" element (before "id" is changed to "prompt") :


Venkat_Vyza
Active Participant
Thank YOU 🙂

It worked well. Finally could complete it.

Really appreciate your efforts in posting this blog and answering the questions as well.

Best,

Venkat Vyza
MarkFogle
Advisor
Advisor
0 Kudos
You're welcome!
Venkat_Vyza
Active Participant
0 Kudos
Hi Mark,

I need your advice on "how to manipulate the data I got from REST API call before displaying it on Build Apps?" For example, I don't want to display the 1st paragraph of the information received from ChatGPT. How can I achive this?

 

Thank you,

 

Venkat Vyza
MarkFogle
Advisor
Advisor
Hi Venkat,

That's a little outside my experience level with Build Apps.  But from looking at the docs my guess is that it will require the use of some of the "text" Formula Functions (e.g., "Contains", "Matches_regex", "Substring") to strip out the unwanted text and extract the information you need.

Regards,

Mark
Venkat_Vyza
Active Participant
Thank you Mark, for the pointers.

Will check that.

 

Best regards,

Venkat Vyza
RaviKant_Ranjan
Participant

Hi Mark,

Thanks for writing such an insightful blog. Successfully completed it. I am sure its going to help me in my other use case going forward. Also if you have any more uses please share so that we can practice and make our hands even more stronger.

Thank you,

Ravikant