Lianja Requires Chilkat v11.4.0+
Lianja
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 Lianja Downloads
llSuccess = .F.
// 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"
// }
// }
// }
// }
// ]
// }
loJsonTools = createobject("CkJsonObject")
lnToolIdx = 0
loJsonTools.I = lnToolIdx
loJsonTools.UpdateString("tools[i].name","get_horoscope")
loJsonTools.UpdateString("tools[i].description","Get today's horoscope for an astrological sign.")
loJsonTools.UpdateString("tools[i].parameters.properties.sign.type","string")
loJsonTools.UpdateString("tools[i].parameters.properties.sign.description","An astrological sign like Taurus or Aquarius")
// More tools can be added as desired..
loJsonTools.EmitCompact = .F.
? loJsonTools.Emit()
loAi = createobject("CkAi")
// Register the tools that will be made available to the AI.
loAi.RegisterManualTools(loJsonTools)
// The provider can be "openai", "google", "claude", "grok", "mistral", "custom", etc.
loAi.Provider = "openai"
// Use your provider's API key.
loAi.ApiKey = "MY_API_KEY"
// Choose a model.
loAi.Model = "gpt-5.6-luna"
// Tool function calling must always occur within a conversation.
lcConversation_name = "convo_astrology"
lcSysMessage = "You are a helpful astrologer"
lcDevMessage = "Respond only with markdown."
loAi.NewConvo(lcConversation_name,lcSysMessage,lcDevMessage)
// Provide inputs
loAi.InputAddText("What is my horoscope? I am an Aquarius.")
// Get the response in streaming mode.
loAi.Streaming = .T.
// 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.
loSbEventName = createobject("CkStringBuilder")
loSbDelta = createobject("CkStringBuilder")
loSbFullResponse = createobject("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..
lnMaxWaitMs = 5000
loJsonFn = createobject("CkJsonObject")
llFinished = .F.
lnNumAsks = 0
// Set a max # of followup Asks to prevent any unexpected infinite looping.
do while not llFinished and (lnNumAsks < 10)
// Send the request to the AI model.
llSuccess = loAi.Ask("text")
if (llSuccess = .F.) then
? loAi.LastErrorText
release loJsonTools
release loAi
release loSbEventName
release loSbDelta
release loSbFullResponse
release loJsonFn
return
endif
llMadeFunctionCalls = .F.
llStreamingDone = .F.
do while not llStreamingDone
lnResult = loAi.PollAi(.F.)
if (lnResult < 0) then
? loAi.LastErrorText
? "Failed."
release loJsonTools
release loAi
release loSbEventName
release loSbDelta
release loSbFullResponse
release loJsonFn
return
endif
if (lnResult > 0) then
// We have an event..
llSuccess = loAi.NextAiEvent(lnMaxWaitMs,loSbEventName,loSbDelta)
if (llSuccess = .F.) then
? loAi.LastErrorText
release loJsonTools
release loAi
release loSbEventName
release loSbDelta
release loSbFullResponse
release loJsonFn
return
endif
// Is this an event where the AI is requesting a function call?
if (loSbEventName.ContentsEqual("function_call",.T.)) then
loJsonFn.LoadSb(loSbDelta)
// 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"
// }
// }
// ]
// }
lnNumFnCalls = loJsonFn.SizeOfArray("function_call")
lnFn_idx = 0
do while (lnFn_idx < lnNumFnCalls)
loJsonFn.I = lnFn_idx
loSbFnName = createobject("CkStringBuilder")
loJsonFn.StringOfSb("function_call[i].name",loSbFnName)
lcCallId = loJsonFn.StringOf("function_call[i].call_id")
if (loSbFnName.ContentsEqual("get_horoscope",.T.) = .T.) then
// The get_horoscope function (as defined above) has one argument named "sign".
lcZodiac_sign = loJsonFn.StringOf("function_call[i].args.sign")
? "zodiac_sign = " + lcZodiac_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:
lcApplicationFnCallResult = "Aquarius: Next Tuesday you will befriend a baby otter."
// Provide the tool call result as an input for the followup Ask.
loAi.InputAddFnResult(lcCallId,lcApplicationFnCallResult)
llMadeFunctionCalls = .T.
endif
// Your application would add code to check for and handle each possible function call.
lnFn_idx = lnFn_idx + 1
enddo
else
if (not loSbEventName.ContentsEqual("empty",.T.)) then
loSbFullResponse.AppendSb(loSbDelta)
if (loSbEventName.ContentsEqual("null_terminator",.T.)) then
llStreamingDone = .T.
endif
endif
endif
else
// No event arrived, so wait a short time rather than spin in a loop..
loAi.SleepMs(100)
endif
enddo
if (not llMadeFunctionCalls) then
llFinished = .T.
endif
lnNumAsks = lnNumAsks + 1
enddo
? "Full Response:"
? loSbFullResponse.GetAsString()
release loJsonTools
release loAi
release loSbEventName
release loSbDelta
release loSbFullResponse
release loJsonFn
release loSbFnName