Tcl Requires Chilkat v11.4.0+
Tcl
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 Tcl Downloads
load ./chilkat.dll
set success 0
# 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"
# }
# }
# }
# }
# ]
# }
set jsonTools [new_CkJsonObject]
set toolIdx 0
CkJsonObject_put_I $jsonTools $toolIdx
CkJsonObject_UpdateString $jsonTools "tools[i].name" "get_horoscope"
CkJsonObject_UpdateString $jsonTools "tools[i].description" "Get today's horoscope for an astrological sign."
CkJsonObject_UpdateString $jsonTools "tools[i].parameters.properties.sign.type" "string"
CkJsonObject_UpdateString $jsonTools "tools[i].parameters.properties.sign.description" "An astrological sign like Taurus or Aquarius"
# More tools can be added as desired..
CkJsonObject_put_EmitCompact $jsonTools 0
puts [CkJsonObject_emit $jsonTools]
set ai [new_CkAi]
# Register the tools that will be made available to the AI.
CkAi_RegisterManualTools $ai $jsonTools
# The provider can be "openai", "google", "claude", "grok", "mistral", "custom", etc.
CkAi_put_Provider $ai "openai"
# Use your provider's API key.
CkAi_put_ApiKey $ai "MY_API_KEY"
# Choose a model.
CkAi_put_Model $ai "gpt-5.6-luna"
# Tool function calling must always occur within a conversation.
set conversation_name "convo_astrology"
set sysMessage "You are a helpful astrologer"
set devMessage "Respond only with markdown."
CkAi_NewConvo $ai $conversation_name $sysMessage $devMessage
# Provide inputs
CkAi_InputAddText $ai "What is my horoscope? I am an Aquarius."
# Get the response in streaming mode.
CkAi_put_Streaming $ai 1
# 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.
set sbEventName [new_CkStringBuilder]
set sbDelta [new_CkStringBuilder]
set 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..
set maxWaitMs 5000
set jsonFn [new_CkJsonObject]
set finished 0
set numAsks 0
# Set a max # of followup Asks to prevent any unexpected infinite looping.
while {expr !$finished && [$numAsks < 10]} {
# Send the request to the AI model.
set success [CkAi_Ask $ai "text"]
if {$success == 0} then {
puts [CkAi_lastErrorText $ai]
delete_CkJsonObject $jsonTools
delete_CkAi $ai
delete_CkStringBuilder $sbEventName
delete_CkStringBuilder $sbDelta
delete_CkStringBuilder $sbFullResponse
delete_CkJsonObject $jsonFn
exit
}
set madeFunctionCalls 0
set streamingDone 0
while {!$streamingDone} {
set result [CkAi_PollAi $ai 0]
if {$result < 0} then {
puts [CkAi_lastErrorText $ai]
puts "Failed."
delete_CkJsonObject $jsonTools
delete_CkAi $ai
delete_CkStringBuilder $sbEventName
delete_CkStringBuilder $sbDelta
delete_CkStringBuilder $sbFullResponse
delete_CkJsonObject $jsonFn
exit
}
if {$result > 0} then {
# We have an event..
set success [CkAi_NextAiEvent $ai $maxWaitMs $sbEventName $sbDelta]
if {$success == 0} then {
puts [CkAi_lastErrorText $ai]
delete_CkJsonObject $jsonTools
delete_CkAi $ai
delete_CkStringBuilder $sbEventName
delete_CkStringBuilder $sbDelta
delete_CkStringBuilder $sbFullResponse
delete_CkJsonObject $jsonFn
exit
}
# Is this an event where the AI is requesting a function call?
if {CkStringBuilder_ContentsEqual $sbEventName "function_call" 1} then {
CkJsonObject_LoadSb $jsonFn $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"
# }
# }
# ]
# }
set numFnCalls [CkJsonObject_SizeOfArray $jsonFn "function_call"]
set fn_idx 0
while {$fn_idx < $numFnCalls} {
CkJsonObject_put_I $jsonFn $fn_idx
set sbFnName [new_CkStringBuilder]
CkJsonObject_StringOfSb $jsonFn "function_call[i].name" $sbFnName
set callId [CkJsonObject_stringOf $jsonFn "function_call[i].call_id"]
if {[CkStringBuilder_ContentsEqual $sbFnName "get_horoscope" 1] == 1} then {
# The get_horoscope function (as defined above) has one argument named "sign".
set zodiac_sign [CkJsonObject_stringOf $jsonFn "function_call[i].args.sign"]
puts "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:
set applicationFnCallResult "Aquarius: Next Tuesday you will befriend a baby otter."
# Provide the tool call result as an input for the followup Ask.
CkAi_InputAddFnResult $ai $callId $applicationFnCallResult
set madeFunctionCalls 1
}
# Your application would add code to check for and handle each possible function call.
set fn_idx [expr $fn_idx + 1]
}
} else {
if {![CkStringBuilder_ContentsEqual $sbEventName "empty" 1]} then {
CkStringBuilder_AppendSb $sbFullResponse $sbDelta
if {CkStringBuilder_ContentsEqual $sbEventName "null_terminator" 1} then {
set streamingDone 1
}
}
}
} else {
# No event arrived, so wait a short time rather than spin in a loop..
CkAi_SleepMs $ai 100
}
}
if {!$madeFunctionCalls} then {
set finished 1
}
set numAsks [expr $numAsks + 1]
}
puts "Full Response:"
puts [CkStringBuilder_getAsString $sbFullResponse]
delete_CkJsonObject $jsonTools
delete_CkAi $ai
delete_CkStringBuilder $sbEventName
delete_CkStringBuilder $sbDelta
delete_CkStringBuilder $sbFullResponse
delete_CkJsonObject $jsonFn
delete_CkStringBuilder $sbFnName