Sample code for 30+ languages & platforms
Chilkat2-Python

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 Chilkat2-Python Downloads

Chilkat2-Python
import sys
import chilkat2

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"
#           }
#         }
#       }
#     }
#   ]
# }

jsonTools = chilkat2.JsonObject()

toolIdx = 0

jsonTools.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.EmitCompact = False
print(jsonTools.Emit())

ai = chilkat2.Ai()

# 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.Provider = "openai"
# Use your provider's API key.
ai.ApiKey = "MY_API_KEY"
# Choose a model.
ai.Model = "gpt-5-mini"

# Tool function calling must always occur within a conversation.
conversation_name = "convo_astrology"
sysMessage = "You are a helpful astrologer"
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.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.

sbEventName = chilkat2.StringBuilder()
sbDelta = chilkat2.StringBuilder()
sbFullResponse = chilkat2.StringBuilder()

# 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..
maxWaitMs = 5000

jsonFn = chilkat2.JsonObject()

finished = False
numAsks = 0
# Set a max # of followup Asks to prevent any unexpected infinite looping.
while not finished and (numAsks < 10) :

    # Send the request to the AI model.
    success = ai.Ask("text")
    if (success == False):
        print(ai.LastErrorText)
        sys.exit()

    madeFunctionCalls = False
    streamingDone = False

    while not streamingDone :
        result = ai.PollAi(False)
        if (result < 0):
            print(ai.LastErrorText)
            print("Failed.")
            sys.exit()

        if (result > 0):
            # We have an event..
            success = ai.NextAiEvent(maxWaitMs,sbEventName,sbDelta)
            if (success == False):
                print(ai.LastErrorText)
                sys.exit()

            # 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"
                #       }
                #     }
                #   ]
                # }

                numFnCalls = jsonFn.SizeOfArray("function_call")
                fn_idx = 0
                while (fn_idx < numFnCalls) :
                    jsonFn.I = fn_idx

                    sbFnName = chilkat2.StringBuilder()
                    jsonFn.StringOfSb("function_call[i].name",sbFnName)
                    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".
                        zodiac_sign = jsonFn.StringOf("function_call[i].args.sign")
                        print("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:
                        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 (not 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 (not madeFunctionCalls):
        finished = True

    numAsks = numAsks + 1

print("Full Response:")
print(sbFullResponse.GetAsString())