Sample code for 30+ languages & platforms
Rust Requires Chilkat v11.4.0+

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 Rust Downloads

Rust

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

let json_tools = chilkat::JsonObject::new();

let tool_idx = 0;

json_tools.set_i(tool_idx);
let _ = json_tools.update_string("tools[i].name", "get_horoscope");
let _ = json_tools.update_string("tools[i].description", "Get today's horoscope for an astrological sign.");
let _ = json_tools.update_string("tools[i].parameters.properties.sign.type", "string");
let _ = json_tools.update_string("tools[i].parameters.properties.sign.description", "An astrological sign like Taurus or Aquarius");
// More tools can be added as desired..

json_tools.set_emit_compact(false);
println!("{}", json_tools.emit().unwrap_or_default());

let ai = chilkat::Ai::new();

// Register the tools that will be made available to the AI.
let _ = ai.register_manual_tools(&json_tools);

// The provider can be "openai", "google", "claude", "grok", "mistral", "custom", etc.
ai.set_provider("openai");
// Use your provider's API key.
ai.set_api_key("MY_API_KEY");
// Choose a model.
ai.set_model("gpt-5.6-luna");

// Tool function calling must always occur within a conversation.
let conversation_name = "convo_astrology".to_string();
let sys_message = "You are a helpful astrologer".to_string();
let dev_message = "Respond only with markdown.".to_string();
let _ = ai.new_convo(&conversation_name, &sys_message, &dev_message);

// Provide inputs
let _ = ai.input_add_text("What is my horoscope? I am an Aquarius.");

// Get the response in streaming mode.
ai.set_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.

let sb_event_name = chilkat::StringBuilder::new();
let sb_delta = chilkat::StringBuilder::new();
let sb_full_response = chilkat::StringBuilder::new();

// 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..
let max_wait_ms = 5000;

let json_fn = chilkat::JsonObject::new();

let mut finished = false;
let mut num_asks = 0;
// Set a max # of followup Asks to prevent any unexpected infinite looping.
while !finished && (num_asks < 10) {

    // Send the request to the AI model.
    if ai.ask("text").is_err() {
        println!("{}", ai.last_error_text());
        return;
    }

    let mut made_function_calls = false;
    let mut streaming_done = false;

    while !streaming_done {
        let result = ai.poll_ai(false);
        if result < 0 {
            println!("{}", ai.last_error_text());
            println!("Failed.");
            return;
        }

        if result > 0 {
            // We have an event..
            if ai.next_ai_event(max_wait_ms, &sb_event_name, &sb_delta).is_err() {
                println!("{}", ai.last_error_text());
                return;
            }

            // Is this an event where the AI is requesting a function call?
            if sb_event_name.contents_equal("function_call", true) {
                let _ = json_fn.load_sb(&sb_delta);

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

                let num_fn_calls = json_fn.size_of_array("function_call");
                let mut fn_idx = 0;
                while fn_idx < num_fn_calls {
                    json_fn.set_i(fn_idx);

                    let sb_fn_name = chilkat::StringBuilder::new();
                    let _ = json_fn.string_of_sb("function_call[i].name", &sb_fn_name);
                    let call_id = json_fn.string_of("function_call[i].call_id").unwrap_or_default();

                    if sb_fn_name.contents_equal("get_horoscope", true) {

                        // The get_horoscope function (as defined above) has one argument named "sign".
                        let zodiac_sign = json_fn.string_of("function_call[i].args.sign").unwrap_or_default();
                        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:
                        let application_fn_call_result = "Aquarius: Next Tuesday you will befriend a baby otter.".to_string();

                        // Provide the tool call result as an input for the followup Ask.
                        let _ = ai.input_add_fn_result(&call_id, &application_fn_call_result);

                        made_function_calls = true;
                    }

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

                    fn_idx = fn_idx + 1;
                }

            } else {
                if !sb_event_name.contents_equal("empty", true) {
                    let _ = sb_full_response.append_sb(&sb_delta);

                    if sb_event_name.contents_equal("null_terminator", true) {
                        streaming_done = true;
                    }

                }

            }

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

    }

    if !made_function_calls {
        finished = true;
    }

    num_asks = num_asks + 1;
}

println!("Full Response:");
println!("{}", sb_full_response.get_as_string().unwrap_or_default());