unit uChatAPI;

interface

uses
  Classes, SysUtils, IdHTTP, System.JSON, IdSSLOpenSSL, IdGlobal;

type
  TMessage = procedure(s: string) of object;

type
  TChatAPI = class(TObject)
  protected
    FToken_api: string;
    FPrefix: string;
    FOnError: TMessage;
    function GetOtvetFromJson(jsonString: string; isOllama: boolean = false)
      : string; virtual;
    function CreateHTTPRequest(const url: string; const params: TStringStream;
      isOllama: boolean = false): string;

  public
    constructor Create(Sender: TObject; aToken: string;
      aprefix: string = ''); virtual;
    destructor Destroy; override;
    function GetGPTRequest(url: string; model: string; q: string;
      isOllama: boolean = false): string;
    property OnError: TMessage read FOnError write FOnError;
  end;

implementation

{ TChatAPI }

constructor TChatAPI.Create(Sender: TObject; aToken: string;
  aprefix: string = '');
begin
  FPrefix := aprefix;
  FToken_api := aToken;
end;

function TChatAPI.CreateHTTPRequest(const url: string;
  const params: TStringStream; isOllama: boolean = false): string;
var
  http: TIdHTTP;
  ssl: TIdSSLIOHandlerSocketOpenSSL;
  otv: string;
begin
  http := TIdHTTP.Create(nil);
  ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  try
    http.IOHandler := ssl;
    ssl.SSLOptions.method := sslvSSLv23;
    http.Request.UserAgent :=
      'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
    http.Request.CustomHeaders.Clear;
    // http.Request.CustomHeaders.Add('Content-Type: application/json; charset=utf-8');
    http.Request.ContentType := 'application/json; charset=utf-8';

    if FToken_api <> '' then
      http.Request.CustomHeaders.Add('Authorization: Bearer ' + FToken_api);
    http.Request.Accept := 'application/json; charset=utf-8';
    http.Request.CharSet := 'utf-8';
    http.Response.CharSet := 'utf-8';
    // http.Request.CustomHeaders.Add('Accept: application/json; charset=utf-8');
    http.Response.ContentEncoding := 'utf-8';
    http.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
    http.Request.ContentEncoding := 'utf-8';

    try
      otv := http.Post(url, params);
      Result := GetOtvetFromJson(otv, isOllama);
    except
      on E: Exception do
        if Assigned(OnError) then
          OnError(E.Message);
    end;
  finally
    params.Free;
    http.Free;
    ssl.Free;
  end;
end;

destructor TChatAPI.Destroy;
begin
  inherited;
end;

function ReplaceDelphiHexCodes(const InputStr: string): string;
var
  I, Start, HexVal: Integer;
  HexStr: string;
begin
  Result := '';
  I := 1;
  while I <= Length(InputStr) do
  begin
    if (I <= Length(InputStr) - 5) and (InputStr[I] = '#') and
      (InputStr[I + 1] = '$') then
    begin
      HexStr := Copy(InputStr, I + 2, 4);
      if TryStrToInt('$' + HexStr, HexVal) then
      begin
        Result := Result + WideChar(HexVal);
        Inc(I, 6); // Пропускаем #$XXXX
        Continue;
      end;
    end;
    Result := Result + InputStr[I];
    Inc(I);
  end;
end;

function ConvertAnsiToUtf8(const AStr: string): string;
var
  AnsiBytes: TBytes;
begin
  AnsiBytes := TEncoding.ANSI.GetBytes(AStr);
  Result := TEncoding.UTF8.GetString(AnsiBytes);
end;

function TChatAPI.GetOtvetFromJson(jsonString: string;
  isOllama: boolean = false): string;
var
  JSON: TJSONObject;
  dataArray: TJSONArray;
  JSONValue: TJSONValue;
  JsonParts: TStringList;
  I: Integer;
  CleanedJson: string;
  JsonObj: TJSONObject;
  ResponseStr, FullResponse: string;
begin

  Result := 'Произошла какая то ошибка, попробуйте спрашивать по очереди!';
  if isOllama then
  begin
    JSON := TJSONObject.ParseJSONValue(jsonString) as TJSONObject;
    try
      if Assigned(JSON) then
      begin
        JSONValue := TJSONObject(JSON);
        if JSONValue.TryGetValue('response', JSONValue) then
          Result := JSONValue.Value;
      end;
    finally
      JSON.Free;
    end;
  end
  else
  begin
    JSON := TJSONObject.ParseJSONValue(jsonString) as TJSONObject;
    try
      if Assigned(JSON) then
      begin
        if JSON.TryGetValue('messages', JSONValue) then
        begin
          dataArray := JSONValue as TJSONArray;
          if Assigned(dataArray) and (dataArray.Count > 0) then
            Result := dataArray.Items[0].GetValue<string>('content');
        end;
      end;
    finally
      JSON.Free;
    end;
  end;
end;

function TChatAPI.GetGPTRequest(url: string; model: string; q: string;
  isOllama: boolean = false): string;
var
  params: TStringStream;
  r: string;
begin

  q := StringReplace(q, '"', '''', [rfReplaceAll]);
  if isOllama then
    params := TStringStream.Create('{ "model": "' + model + '", "prompt": "' +
      FPrefix + q + '", "stream": false }', TEncoding.UTF8)
  else
    params := TStringStream.Create('{ "model": "' + model +
      '", "messages": [{ "role": "user", "content": "' + FPrefix + q +
      '" }], "stream": false }', CP_UTF8);

  try
    r := CreateHTTPRequest(url, params, isOllama);
  finally
    // params.Free;
  end;
  Result := r;

end;

end.
