Sample code for 30+ languages & platforms
Pascal (Lazarus/Delphi)

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 Pascal (Lazarus/Delphi) Downloads

Pascal (Lazarus/Delphi)
program ChilkatDemo;

// Demonstrates using the Chilkat Pascal wrapper via the C bridge DLL.
// Builds as a console application under Lazarus (FPC) or Delphi.

{$IFDEF FPC}
  {$MODE DELPHI}
{$ENDIF}
{$APPTYPE CONSOLE}

uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  SysUtils,
  CkDllLoader,
  Chilkat.StringBuilder,
  Chilkat.Ai,
  Chilkat.JsonObject;

// ---------------------------------------------------------------------------

procedure RunDemo;
var
  success: Boolean;
  jsonTools: TJsonObject;
  toolIdx: Integer;
  ai: TAi;
  conversation_name: string;
  sysMessage: string;
  devMessage: string;
  sbEventName: TStringBuilder;
  sbDelta: TStringBuilder;
  sbFullResponse: TStringBuilder;
  maxWaitMs: Integer;
  jsonFn: TJsonObject;
  finished: Boolean;
  numAsks: Integer;
  madeFunctionCalls: Boolean;
  streamingDone: Boolean;
  result: Integer;
  numFnCalls: Integer;
  fn_idx: Integer;
  sbFnName: TStringBuilder;
  callId: string;
  zodiac_sign: string;
  applicationFnCallResult: string;

begin
  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 := TJsonObject.Create;

  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;
  WriteLn(jsonTools.Emit());

  ai := TAi.Create;

  //  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 := TStringBuilder.Create;
  sbDelta := TStringBuilder.Create;
  sbFullResponse := TStringBuilder.Create;

  //  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 := TJsonObject.Create;

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

      //  Send the request to the AI model.
      success := ai.Ask('text');
      if (success = False) then
        begin
          WriteLn(ai.LastErrorText);
          Exit;
        end;

      madeFunctionCalls := False;
      streamingDone := False;

      while not streamingDone do
        begin
          result := ai.PollAi(False);
          if (result < 0) then
            begin
              WriteLn(ai.LastErrorText);
              WriteLn('Failed.');
              Exit;
            end;
          if (result > 0) then
            begin
              //  We have an event..
              success := ai.NextAiEvent(maxWaitMs,sbEventName,sbDelta);
              if (success = False) then
                begin
                  WriteLn(ai.LastErrorText);
                  Exit;
                end;

              //  Is this an event where the AI is requesting a function call?
              if (sbEventName.ContentsEqual('function_call',True)) then
                begin
                  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) do
                    begin
                      jsonFn.I := fn_idx;

                      sbFnName := TStringBuilder.Create;
                      jsonFn.StringOfSb('function_call[i].name',sbFnName);
                      callId := jsonFn.StringOf('function_call[i].call_id');

                      if (sbFnName.ContentsEqual('get_horoscope',True) = True) then
                        begin

                          //  The get_horoscope function (as defined above) has one argument named "sign".
                          zodiac_sign := jsonFn.StringOf('function_call[i].args.sign');
                          WriteLn('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;
                        end;
                      //  Your application would add code to check for and handle each possible function call.

                      fn_idx := fn_idx + 1;
                    end;

                end
              else
                begin
                  if (not sbEventName.ContentsEqual('empty',True)) then
                    begin
                      sbFullResponse.AppendSb(sbDelta);

                      if (sbEventName.ContentsEqual('null_terminator',True)) then
                        begin
                          streamingDone := True;
                        end;
                    end;
                end;

            end
          else
            begin
              //  No event arrived, so wait a short time rather than spin in a loop..
              ai.SleepMs(100);
            end;

        end;

      if (not madeFunctionCalls) then
        begin
          finished := True;
        end;

      numAsks := numAsks + 1;
    end;

  WriteLn('Full Response:');
  WriteLn(sbFullResponse.GetAsString());


  jsonTools.Free;
  ai.Free;
  sbEventName.Free;
  sbDelta.Free;
  sbFullResponse.Free;
  jsonFn.Free;
                      sbFnName.Free;

end;

// ---------------------------------------------------------------------------

begin

  try
    RunDemo;
  except
    on E: Exception do
      WriteLn('Unhandled exception: ', E.ClassName, ': ', E.Message);
  end;

  WriteLn;
  {$IFDEF MSWINDOWS}
  WriteLn('Press Enter to exit...');
  ReadLn;
  {$ENDIF}
end.