OpenRouteAI tutorial, without extensions, using only a Web component, a Webviewer and an html file

In this tutorial, we'll create a complete chatbot using OpenRouteAI. We'll need an API key, which is completely free and can be found at https://openrouter.ai/. With it, and depending on your subscription type, you can use all of AI's models, both free and paid.

In the design section, we need to include a WebViewer, a TextBox, a ListPicker, a button, a Label, a Web component, and a clock.

The resources section includes an HTML file with JavaScript for the chatbot, which is already included in the AI.

In the Blocks section, we'll create four global variables:
model: This is the default model; in my case, it's gemini-2.5-flash-lite.
models: This is a list of the 20 most commonly used models; you can expand the list if you wish.
sent: This is a control variable to determine if a request has been sent to the web component.
apikey: This is the OpenRoute API key you registered. It's recommended that you set it to obfuscated text.

Now I'll explain the two main blocks: the button to submit the question and the answer received by the web component.

Here is a clear, professional, and step-by-step description of your blocks in English, specifically tailored for a programming tutorial. It explains the core logic, including your custom safety mechanism (global sent) and how the text is processed.


Tutorial Section: Sending Messages and Managing App States

This block handles the user interaction when sending a message to the AI assistant. It safely manages network states, prepares the JSON payload, clears the UI, and starts a security timeout counter.

Step-by-Step Breakdown

1. Input Validation and "Mutex" Lock

  • Condition Check: Before performing any action, the block evaluates an if condition using an and logical operator.
  • It ensures that the input field (TextBox1.Text) is not empty (≠ "") and that no other request is currently active (not get global sent). This acts as a software lock (Mutex) to prevent the user from accidental double-submitting or flooding the connection.

2. Configuring HTTP Headers

  • If the condition is met, it configures the Web1 component's RequestHeaders.
  • It builds a structured list containing two key pairs:
  • Authorization: Set to "Bearer " combined with your global apikey.
  • Content-Type: Set to "application/json".

3. Updating the Chat Interface (JavaScript Bridge)

  • It triggers the addMessage function inside the WebViewer via call WebViewer1.RunJavaScript.
  • It uses a join block to format a clean JavaScript call: addUserMessage('User Text Here');.

4. Asynchronous Network Request (Web1.PostText)

  • It prepares and sends the raw JSON body to the OpenRouter API.
  • Inside the join block, it structures the payload required by standard chat completion endpoints:
  • Model: Injects the selected model from get global model.
  • Messages Array: It embeds the instructions. Notice the sanitization step: it uses a replace all text block to switch standard double quotes (") inside the user's message into single quotes (') to guarantee that the final JSON payload structure does not break or escape unexpectedly.

5. Interface Housekeeping and Keyboard Behavior

  • UI Clean-up: The TextBox1.Text is immediately set to an empty string (""), clearing the field so the user knows the message went through.
  • UX Comfort: It calls Screen1.HideKeyboard to dismiss the Android virtual keyboard, leaving more screen real estate open for the ongoing conversation.

6. System State Locking and Timers

  • Timeout Protection: It triggers set Clock1.TimerEnabled to true. This activates your background countdown loop to guard against frozen server sockets.
  • Variable State Flip: It sets global sent to true. This locks down the script block.
  • Physical Component Locks: Lastly, it locks down both interactive UI components by setting TextBox1.Enabled and Button1.Enabled to false. This visually signals to the user that the system is hard at work waiting for a backend response.

Technical Summary for Users

Pro-Tip for Your Tutorial: This layout represents an excellent architectural design patterns for mobile development. By disabling the buttons and changing a tracking variable (global sent), we completely isolate the network layer. The app remains visually frozen in an un-clickable state until either the network event finishes successfully or the security clock triggers an error handler.

Here is the tutorial description for your backend response event block (Web1.GotText), based on the updated logic shown in your new images.


Tutorial Section: Handling the API Response and Error Catching

This event block handles the backend data when OpenRouter responds. It acts as the "Receiver". It unlocks the application interface, securely parses the JSON text payload, updates the chat UI, and maps standard HTTP web errors into readable messages.

Step-by-Step Breakdown

1. Resetting the Application Lock (The Reset)

  • As soon as Web1.GotText triggers, the very first blocks reset your interface variables:
  • set global sent to false: Opens up the lock variable so a new question can be evaluated later.
  • set TextBox1.Enabled to true and set Button1.Enabled to true: Reactivates your input box and Send button visually.

2. Evaluating Success (HTTP 200 OK)

  • The main structure uses an if block to verify if responseCode = 200.
  • JSON Decoding: If successful, it passes responseContent through the native JsonTextDecodeWithDictionaries block to turn raw server text into accessible App Inventor structures (data).
  • Path Navigation: It initializes a local variable x and drills down through the nested response mapping keys: **choices ➔ Select Item Index 1messagecontent**.

3. Protection Against Empty or Null Values

  • The null Filter: Before updating the UI, it passes x through an internal text comparison check: if get x ≠ "null". This keeps empty server timeouts from throwing errors or printing ugly text on screen.
  • JavaScript Injected Update: If valid, it passes the text package over the bridge into your Canvas UI layout using call WebViewer1.RunJavaScript linked up to an addAIMessage execution script. (In your second variant, it safely hooks the specific global model indicator right below the chat line).
  • Timer Safety Switch: It forces set Clock1.TimerEnabled to false so that the background security clock doesn't fire off an accidental timeout alert.

4. The Advanced Error Trap List (else if Cascade)

If the response code is anything other than 200, the block drops down into a comprehensive error mapping array. It checks the specific HTTP status code and assigns a readable label to a local variable code:

Status Code Assigned Tutorial Label Meaning
400 400 Bad Request Error Broken JSON syntax or prompt structural error.
401 401 Unauthorized Error Wrong, expired, or missing API key.
403 403 Forbidden Error Access denied by the endpoint provider.
404 404 Not Found Error Wrong API URL path or missing endpoint model.
408 408 Request Timeout Error The backend provider upstream took too long to build a response.
429 429 Too Many Requests Error Rate limits hit or insufficient credits remaining.
500 / 502 / 503 Server / Gateway Errors Internal issues on OpenRouter's host nodes.

5. Sending the Error Layout to the Chat Screen

  • Once the error code is evaluated, the block issues a distinct JavaScript bridge command.
  • It formats the specific error text wrapped neatly inside an inline HTML styling parameter: <span style="color:red;"> get code </span>. This prints out a crisp, styled alert directly within the user's bubble feed without crashing the App Inventor workflow.
  • Finally, it shuts down the security clock timer to close out the request cycle safely.

Here is the tutorial explanation in English, explaining the technical reason behind using the Clock1 component instead of the native Web1.Timeout property, along with exactly what the Clock does behind the scenes.


Tutorial Section: Advanced Network Management – Clock vs. Web Timeout

When building real-time chat applications in MIT App Inventor, managing connection drops or frozen API servers is critical. In this project, we explicitly use a Clock (Timer) component to handle timeouts instead of relying on the native Web.Timeout property.

Here is the technical reasoning and an explanation of how it works.


Why the Native Web1.Timeout Fails

MIT App Inventor's Web component features a built-in Timeout property (measured in milliseconds). However, it has a severe platform limitation:

:warning: The technical limitation: The native Web.Timeout property only works for HTTP GET requests. When you are communicating with LLM APIs like OpenRouter, you are forced to use HTTP POST requests via the call Web1.PostText block.
Under the hood in Android, App Inventor ignores the native timeout setting entirely during a POST operation. If the OpenRouter server hangs, drops a socket, or gets overloaded, your application's network thread will stay open indefinitely in a frozen state.

Because we cannot natively Destroy an active POST request thread, we must handle the application's behavior using an external supervisor: Clock1.


Exactly What the Clock Component Does

The Clock1 component acts as a Security Watchdog Timer. Instead of closing the internet connection (which App Inventor doesn't allow dynamically), it safely manages the state of the application so the user is never left stranded.

Here is its precise lifecycle execution:

Phase 1: Arming the Timer (On Send)

When the user clicks the send button (Button1.Click), the app fires the web request and immediately executes:

  • set Clock1.TimerEnabled to true (with a preset interval, e.g., 15000 milliseconds / 15 seconds).
  • The clock begins its background countdown.

Phase 2: Scenario A – The Server Responds Quickly (Happy Path)

If OpenRouter responds within the 15-second window, the Web1.GotText event triggers instantly. The very first thing your code does is:

  • set Clock1.TimerEnabled to false
  • This disarms the watchdog. The countdown is aborted because the data was successfully received, and the chat continues normally.

Phase 3: Scenario B – The Server Freezes (Timeout Triggered)

If 15 seconds pass and the server remains completely silent, the when Clock1.Timer event fires automatically. Since we cannot stop the low-level Web1 background thread, the Clock executes a graceful software override:

  1. Disarms Itself: It sets Clock1.TimerEnabled to false so it stops counting.
  2. Alerts the User: It bypasses the missing server data and runs a JavaScript command (addAIMessage) to print a clean error directly into the chat bubble feed (e.g., "Connection Timeout. Server is taking too long.").
  3. Locks the App Defensively: It intentionally leaves your global sent variable as true and keeps the UI buttons disabled.

Technical Summary for the Tutorial

By leaving global sent as true inside the Clock timeout event, your app follows a Fail-Safe Design Pattern.

Since App Inventor leaves the broken HTTP thread running invisibly in Android's memory, forcing a manual app restart is the only way to guarantee that a late, corrupted, or null response from the frozen server won't suddenly pop up later and break your chat structure. The Clock ensures the user is informed immediately of the freeze rather than staring at a broken, unresponsive screen.

OpenRoute.aia (10,7 KB)

2 Likes