Go
Go
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 Go Downloads
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 := chilkat.NewJsonObject()
toolIdx := 0
jsonTools.SetI(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.SetEmitCompact(false)
fmt.Println(*jsonTools.Emit())
ai := chilkat.NewAi()
// 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.SetProvider("openai")
// Use your provider's API key.
ai.SetApiKey("MY_API_KEY")
// Choose a model.
ai.SetModel("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.SetStreaming(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 := chilkat.NewStringBuilder()
sbDelta := chilkat.NewStringBuilder()
sbFullResponse := chilkat.NewStringBuilder()
// 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 := chilkat.NewJsonObject()
finished := false
numAsks := 0
// Set a max # of followup Asks to prevent any unexpected infinite looping.
for !finished && (numAsks < 10) {
// Send the request to the AI model.
success = ai.Ask("text")
if success == false {
fmt.Println(ai.LastErrorText())
jsonTools.DisposeJsonObject()
ai.DisposeAi()
sbEventName.DisposeStringBuilder()
sbDelta.DisposeStringBuilder()
sbFullResponse.DisposeStringBuilder()
jsonFn.DisposeJsonObject()
return
}
madeFunctionCalls := false
streamingDone := false
for !streamingDone {
result := ai.PollAi(false)
if result < 0 {
fmt.Println(ai.LastErrorText())
fmt.Println("Failed.")
jsonTools.DisposeJsonObject()
ai.DisposeAi()
sbEventName.DisposeStringBuilder()
sbDelta.DisposeStringBuilder()
sbFullResponse.DisposeStringBuilder()
jsonFn.DisposeJsonObject()
return
}
if result > 0 {
// We have an event..
success = ai.NextAiEvent(maxWaitMs,sbEventName,sbDelta)
if success == false {
fmt.Println(ai.LastErrorText())
jsonTools.DisposeJsonObject()
ai.DisposeAi()
sbEventName.DisposeStringBuilder()
sbDelta.DisposeStringBuilder()
sbFullResponse.DisposeStringBuilder()
jsonFn.DisposeJsonObject()
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"
// }
// }
// ]
// }
numFnCalls := jsonFn.SizeOfArray("function_call")
fn_idx := 0
for (fn_idx < numFnCalls) {
jsonFn.SetI(fn_idx)
sbFnName := chilkat.NewStringBuilder()
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")
fmt.Println("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 !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
}
fmt.Println("Full Response:")
fmt.Println(*sbFullResponse.GetAsString())
jsonTools.DisposeJsonObject()
ai.DisposeAi()
sbEventName.DisposeStringBuilder()
sbDelta.DisposeStringBuilder()
sbFullResponse.DisposeStringBuilder()
jsonFn.DisposeJsonObject()
sbFnName.DisposeStringBuilder()