Sample code for 30+ languages & platforms
Android™

Streaming AI with Manual AI Tool Function Calling

See more AI Examples

Demonstrates how to get AI responses in streaming mode, including manual tool function calls.

Chilkat Android™ Downloads

Android™
// Important: Don't forget to include the call to System.loadLibrary
// as shown at the bottom of this code sample.
package com.test;

import android.app.Activity;
import com.chilkatsoft.*;

import android.widget.TextView;
import android.os.Bundle;

public class SimpleActivity extends Activity {

  private static final String TAG = "Chilkat";

  // Called when the activity is first created.
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    boolean success = false;

    // Create the following JSON to define tool functions available for the AI to use.
    // Note: You'll use the following JSON format regardless of the AI provider, whether
    // it be ChatGPT, Gemini, Claude, Grok, etc.  Chilkat automatically converts to the required
    // format needed for a given AI provider.

    // In this example, the application is providing a single function the AI may choose to call.

    // {
    //   "tools": [
    //     {
    //       "name": "get_horoscope",
    //       "description": "Get today's horoscope for an astrological sign.",
    //       "parameters": {
    //         "properties": {
    //           "sign": {
    //             "type": "string",
    //             "description": "An astrological sign like Taurus or Aquarius"
    //           }
    //         }
    //       }
    //     }
    //   ]
    // }

    CkJsonObject jsonTools = new CkJsonObject();

    int toolIdx = 0;

    jsonTools.put_I(toolIdx);
    jsonTools.UpdateString("tools[i].name","get_horoscope");
    jsonTools.UpdateString("tools[i].description","Get today's horoscope for an astrological sign.");
    jsonTools.UpdateString("tools[i].parameters.properties.sign.type","string");
    jsonTools.UpdateString("tools[i].parameters.properties.sign.description","An astrological sign like Taurus or Aquarius");
    // More tools can be added as desired..

    jsonTools.put_EmitCompact(false);
    Log.i(TAG, jsonTools.emit());

    CkAi ai = new CkAi();

    // Register the tools that will be made available to the AI.
    ai.RegisterManualTools(jsonTools);

    // The provider can be "openai", "google", "claude", "grok", "mistral", "custom", etc.
    ai.put_Provider("openai");
    // Use your provider's API key.
    ai.put_ApiKey("MY_API_KEY");
    // Choose a model.
    ai.put_Model("gpt-5-mini");

    // Tool function calling must always occur within a conversation.
    String conversation_name = "convo_astrology";
    String sysMessage = "You are a helpful astrologer";
    String devMessage = "Respond only with markdown.";
    ai.NewConvo(conversation_name,sysMessage,devMessage);

    // Provide inputs
    ai.InputAddText("What is my horoscope? I am an Aquarius.");

    // Get the response in streaming mode.
    ai.put_Streaming(true);

    // In streaming mode, if we receive an AI event that is a request for tool use,
    // we'll need to make the call to the JavaScript and then continue with a followup Ask,
    // until the final response is received.

    CkStringBuilder sbEventName = new CkStringBuilder();
    CkStringBuilder sbDelta = new CkStringBuilder();
    CkStringBuilder sbFullResponse = new CkStringBuilder();

    // When PollAi returns with an event, it's highly unlikely the
    // call to NextAiEvent does not immediately return.  Setting a max
    // timeout is just a precaution..
    int maxWaitMs = 5000;

    CkJsonObject jsonFn = new CkJsonObject();

    boolean finished = false;
    int numAsks = 0;
    // Set a max # of followup Asks to prevent any unexpected infinite looping.
    while (!finished and (numAsks < 10)) {

        // Send the request to the AI model.
        success = ai.Ask("text");
        if (success == false) {
            Log.i(TAG, ai.lastErrorText());
            return;
            }

        boolean madeFunctionCalls = false;
        boolean streamingDone = false;

        while (!streamingDone) {
            int result = ai.PollAi(false);
            if (result < 0) {
                Log.i(TAG, ai.lastErrorText());
                Log.i(TAG, "Failed.");
                return;
                }

            if (result > 0) {
                // We have an event..
                success = ai.NextAiEvent(maxWaitMs,sbEventName,sbDelta);
                if (success == false) {
                    Log.i(TAG, ai.lastErrorText());
                    return;
                    }

                // Is this an event where the AI is requesting a function call?
                if (sbEventName.ContentsEqual("function_call",true)) {
                    jsonFn.LoadSb(sbDelta);

                    // Note: Chilkat will convert responses from all AI providers to this format:

                    // {
                    //   "function_call": [
                    //     {
                    //       "name": "get_horoscope",
                    //       "call_id": "call_RYmeysYQFocFc7Z2ofkv61dW",
                    //       "arguments": "{\"sign\":\"Aquarius\"}",
                    //       "args": {
                    //         "sign": "Aquarius"
                    //       }
                    //     }
                    //   ]
                    // }

                    int numFnCalls = jsonFn.SizeOfArray("function_call");
                    int fn_idx = 0;
                    while ((fn_idx < numFnCalls)) {
                        jsonFn.put_I(fn_idx);

                        CkStringBuilder sbFnName = new CkStringBuilder();
                        jsonFn.StringOfSb("function_call[i].name",sbFnName);
                        String callId = jsonFn.stringOf("function_call[i].call_id");

                        if (sbFnName.ContentsEqual("get_horoscope",true) == true) {

                            // The get_horoscope function (as defined above) has one argument named "sign".
                            String zodiac_sign = jsonFn.stringOf("function_call[i].args.sign");
                            Log.i(TAG, "zodiac_sign = " + zodiac_sign);

                            // Insert application code here to call your app's get_horoscope function, passing the zodiac_sign to it..

                            // For this example, we'll pretend the app's get_horoscope function returned the following:
                            String applicationFnCallResult = "Aquarius: Next Tuesday you will befriend a baby otter.";

                            // Provide the tool call result as an input for the followup Ask.
                            ai.InputAddFnResult(callId,applicationFnCallResult);

                            madeFunctionCalls = true;
                            }

                        // Your application would add code to check for and handle each possible function call.

                        fn_idx = fn_idx + 1;
                        }

                    }
                else {
                    if (!sbEventName.ContentsEqual("empty",true)) {
                        sbFullResponse.AppendSb(sbDelta);

                        if (sbEventName.ContentsEqual("null_terminator",true)) {
                            streamingDone = true;
                            }

                        }

                    }

                }
            else {
                // No event arrived, so wait a short time rather than spin in a loop..
                ai.SleepMs(100);
                }

            }

        if (!madeFunctionCalls) {
            finished = true;
            }

        numAsks = numAsks + 1;
        }

    Log.i(TAG, "Full Response:");
    Log.i(TAG, sbFullResponse.getAsString());

  }

  static {
      System.loadLibrary("chilkat");

      // Note: If the incorrect library name is passed to System.loadLibrary,
      // then you will see the following error message at application startup:
      //"The application <your-application-name> has stopped unexpectedly. Please try again."
  }
}