Class TOpenAI_ChatGPT by Charles Kwon

Post Reply
User avatar
Antonio Linares
Site Admin
Posts: 42655
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 67 times
Been thanked: 96 times
Contact:

Class TOpenAI_ChatGPT by Charles Kwon

Post by Antonio Linares »

Many thanks to master Charles Kwon

This code is adapted for gpt-3.5-turbo from one published by Reinaldo on the forums. Hats off to Reinaldo for publishing it.

Code: Select all | Expand

#include "Fivewin.ch"
#include "hbcurl.ch"


FUNCTION Main()
    LOCAL oApi 
    LOCAL cKey := ""  // you have get a APIKEY from https://platform.openai.com/api-keys

    oApi := TOpenAI_ChatGPT():New( cKey )

    oApi:prompt := "what is harbour-project?"
    oApi:Send()

    ?oApi:Response()


RETURN NIL    


/*

    Orignal code from : Reinaldo
    https://forums.fivetechsupport.com/viewtopic.php?f=3&t=43171&start=0&hilit=openai


    This code is adapted for gpt-3.5-turbo from one published by Reinaldo on the forums. Hats off to Reinaldo for publishing it.
    


    Modify : Charles KWON ( charleskwonohjun@gmail.com ) , www.charleskwon.com


Here's what I added

1. 
  Changed to allow you to pass in OpenAI's key value when calling CLASS.
  oApi := TOpenAI_ChatGPT():New( cKey )

 
2.
 According to the OpenAI development documentation, 
 We need to pass the following value in the message, 
 so I added it like this       

        hb_HSet( h, "model", ::model )
    
        hb_HSet( hMessage, "role", "user" )
        hb_HSet( hMessage, "content", ::prompt )



*/


CLASS TOpenAI_ChatGPT

    DATA cKey AS CHARACTER INIT "" 
    DATA hCurl
    DATA httpcode
    DATA nError             INIT 0
 
    DATA Response
    DATA Prompt

    DATA model              INIT "gpt-3.5-turbo"
    DATA cUrl               INIT "https://api.openai.com/v1/chat/completions"
    DATA max_tokens         INIT 2048
    DATA temperature        INIT 0
    DATA top_p              INIT 1
    DATA frequency_penalty  INIT 0.00
    DATA presence_penalty   INIT 0.00
 
    DATA cLogFile        INIT "TOpenAI.log"
    DATA nMaxLogSize     INIT 32768
    DATA lDebug          INIT .F.
 
    METHOD New()
    METHOD End()
    METHOD Send()
    METHOD Reset()
 
    //parses response fetching ICD10 codes coded by OpenAI.
    METHOD GetICD10sFromResponse()
 
 ENDCLASS
 
 // Changed to allow you to pass in OpenAI's key value when calling CLASS.
 //
 //------------------------------------------------------------------------------------------------
 
 METHOD New( cKey ) CLASS TOpenAI_ChatGPT

    ::cKey := cKey
    ::hCurl := curl_easy_init()
 
 RETURN Self
 
// According to the OpenAI development documentation, 
// We need to pass the following value in the message, 
// so I added it like this       
//
//hb_HSet( h, "model", ::model )
    
//hb_HSet( hMessage, "role", "user" )
//hb_HSet( hMessage, "content", ::prompt )

//h["messages"] := {hMessage}


 METHOD Send() CLASS TOpenAI_ChatGPT
    LOCAL aheaders
    Local httpcode
    Local cJson
    Local h := { => }
    LOCAL hMessage := { => }
 
    curl_easy_setopt( ::hCurl, HB_CURLOPT_POST, .T. )
    curl_easy_setopt( ::hCurl, HB_CURLOPT_URL, ::cUrl )
     
    aheaders := { "Content-Type: application/json", ;
                    "Authorization: Bearer " + ::cKey }
    
    curl_easy_setopt( ::hCurl, HB_CURLOPT_HTTPHEADER, aheaders )
 
    curl_easy_setopt( ::hCurl, HB_CURLOPT_USERNAME, '' )
    curl_easy_setopt( ::hCurl, HB_CURLOPT_DL_BUFF_SETUP )
    curl_easy_setopt( ::hCurl, HB_CURLOPT_SSL_VERIFYPEER, .F. )


   // According to the OpenAI development documentation, 
   // We need to pass the following value in the message, 
   // so I added it like this   
    
 
    hb_HSet( h, "model", ::model )
    
    hb_HSet( hMessage, "role", "user" )
    hb_HSet( hMessage, "content", ::prompt )
 
    h["messages"] := {hMessage}


    
 
    cJson := hb_jsonEncode( h )   
    curl_easy_setopt( ::hcurl, HB_CURLOPT_POSTFIELDS, cJson ) //cPostFields )
 
    ::nError := curl_easy_perform( ::hCurl )
    curl_easy_getinfo( ::hCurl, HB_CURLINFO_RESPONSE_CODE, @httpcode )
 
    ::httpcode := httpcode
 
    IF ::nError == HB_CURLE_OK
       ::Response = curl_easy_dl_buff_get( ::hCurl )
       IF ::lDebug

       ENDIF
    ELSE
 
    ENDIF
 
RETURN ::Response            
 
 //------------------------------------------------------------------------------------------------
 METHOD Reset() CLASS TOpenAI_ChatGPT
      
    curl_easy_reset( ::hCurl )
    ::nError := HB_CURLE_OK
    ::Response := Nil
 
 return NIL
 
 
 
 //------------------------------------------------------------------------------------------------
 METHOD End() CLASS TOpenAI_ChatGPT

    curl_easy_cleanup( ::hCurl )
    ::hCurl := Nil  
 
 return NIL
 
 
 /*------------------------------------------------------------------------------------------------
 -------------------------------------------------------------------------------------------------*/
 METHOD GetICD10sFromResponse() CLASS TOpenAI_ChatGPT
    LOCAL aICD10s := {}
    LOCAL aParsed, iter, acodelines, code
    LOCAL a := {}
    LOCAL aKeys
    LOCAL hResponse := hb_jsonDecode( ::Response )
 
    if ::lDebug
 
      // LogData( ::cLogFile, { "hb_isHash( hResponse )", hb_isHash( hResponse ), ;
      //                      "hb_HHasKey( hResponse, 'choices' )", hb_HHasKey( hResponse, "choices" ) } )
       aKeys := HGetKeys( hResponse )
       //LogData( ::cLogFile, aKeys, ::nMaxLogSize )
 
    ENDIF
 
 
    IF hb_isHash( hResponse ) .AND. ;
       hb_HHasKey( hResponse, "choices" )
 
       if HB_ISARRAY( hResponse[ "choices" ] )
          a := hResponse[ "choices" ]
       endif
 
       FOR iter := 1 TO LEN( a )
 
          IF hb_HHasKey( a[ iter ], "text" )
          
             acodelines := hb_ATokens( a[ iter ][ "text" ], CRLF )
 
             FOR EACH Code IN acodelines
 
                aParsed := hb_ATokens( Code, "-" )
                AADD( aIcd10s, aParsed )
 
             NEXT
 
          ENDIF
 
       NEXT iter
 
    ENDIF
 
 RETURN aICD10s
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Ruth
Posts: 173
Joined: Fri Dec 07, 2007 1:26 pm
Contact:

Re: Class TOpenAI_ChatGPT by Charles Kwon

Post by Ruth »

Dear friends,

how wonderful :-)
one question please ... could someone suggest to me a safe way of storing cKey in this context?
on openai they suggest an environment variable...is this a good approach? or is it even necessary to bother?

thank you for your expertice and kind regards
ruth
User avatar
CharlesKwon
Posts: 28
Joined: Sun Nov 02, 2014 7:03 am

Re: Class TOpenAI_ChatGPT by Charles Kwon

Post by CharlesKwon »

Hello

According to OpenAI's API protocol, the API communicates with the endpoint, and the communication protocol is https. In other words, there is no risk of leakage of cKey because the communication is encrypted.

However, if you want to use encryption to protect your key locally, you can encrypt and store your key and decrypt the key before calling OpenAI. Luckily, fivewin already has encrypt() and decrypt() functions.

Regards,
Charles KWON
Ruth wrote:Dear friends,

how wonderful :-)
one question please ... could someone suggest to me a safe way of storing cKey in this context?
on openai they suggest an environment variable...is this a good approach? or is it even necessary to bother?

thank you for your expertice and kind regards
ruth
User avatar
Ruth
Posts: 173
Joined: Fri Dec 07, 2007 1:26 pm
Contact:

Re: Class TOpenAI_ChatGPT by Charles Kwon

Post by Ruth »

Dear Mr. Kwon,

thanks a lot for your answer. And also thank you for pointing me to the encrypt() and decrypt() functions ... they seem very useful in several scenarios.

thanks again and kind regards
ruth
User avatar
Ari
Posts: 247
Joined: Fri Feb 03, 2006 4:21 pm
Location: São Paulo, SP - Brazil
Been thanked: 2 times
Contact:

Re: Class TOpenAI_ChatGPT by Charles Kwon

Post by Ari »

Hello

Is it possible to attach an XML from an Electronic Invoice with the code above?

And if it is only in JSON, does anyone have a function that converts XML to JSON?

I asked GPT all this, but the code it returns is not functional.
Thanks,
Ari

FWH 2501 - Harbour 3.2.0 - Embarcadero 7.43 - MySQL
São Paulo - SP - Brasil
www.sisrev.com.br
User avatar
Antonio Linares
Site Admin
Posts: 42655
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 67 times
Been thanked: 96 times
Contact:

Re: Class TOpenAI_ChatGPT by Charles Kwon

Post by Antonio Linares »

Dear Ari,

In FWH 25.01 we have a new Class TOpenAI.

Do you need OpenAI or would it be fine using Google Gemini ?
Because we already have sending files in Class TGemini.

A workaround is to read the XML and send it as part of the prompt as it is ascii.
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Ari
Posts: 247
Joined: Fri Feb 03, 2006 4:21 pm
Location: São Paulo, SP - Brazil
Been thanked: 2 times
Contact:

Re: Class TOpenAI_ChatGPT by Charles Kwon

Post by Ari »

OK

I issue electronic invoices in my ERP and I always get a tax error, invisible character, etc. I've been testing it on the direct prompt with GPT about Brazilian laws, which is crazy, and it's been responding very well. I simulated XML with forced errors and it figured out where the error was. I have to chat with it a lot, but it's useful here when it comes to technical support having more resources to respond faster. There are no rules of law here, each state has its own, it's a mess.

I asked him for a Harbor code, he replied to send the XML via curl, or to send it in JSON, but nothing worked.

I have the FWH2501. The idea is to activate the AI ​​only when the Treasury Department rejects the invoice (XML).

The idea is to say this way, GPT analyzes this XML and checks what's wrong: it does the magic :D

Your suggestion of posting with ASC could be the solution, I'll try it, thanks
Thanks,
Ari

FWH 2501 - Harbour 3.2.0 - Embarcadero 7.43 - MySQL
São Paulo - SP - Brasil
www.sisrev.com.br
User avatar
Ari
Posts: 247
Joined: Fri Feb 03, 2006 4:21 pm
Location: São Paulo, SP - Brazil
Been thanked: 2 times
Contact:

Re: Class TOpenAI_ChatGPT by Charles Kwon

Post by Ari »

I'm trying to run a test with TOpenAI() and it returns that I don't have permission with the public key, or the model doesn't exist, do I have to pay to test it too?

Code: Select all | Expand

/*========================================================================
                     S I S R E V INFORMATICA LTDA.
              Prestando serviços com qualidade desde 1993
                       CNPJ: 02.623.572/0001-52
                       CNPJ: 02.623.572/0002-33
  ========================================================================
   Sistema..: S i s r e v - Win 5.0
   Autor(es): Ariovaldo da Costa Foliene
   Aplicação: TSisrevIA
   Notas....: Inteligência artificial com ChatGPT
   Data.....: 06-03-2025
  ========================================================================

  ========================================================================
*/
#include "fivewin.ch"
#include "Objects.ch"

Function Main()
    local cAPI_URL := "https://api.openai.com/v1/chat/completions"
    
    local cAPI_KEY := "my key"
    local cXML     := '"'+MemoRead("C:\Users\Ari\Desktop\35250302623572000152550010000000041326996985nfe.xml")+'"'
    
    local cResponse, cResult, hHttp, cJSON, cRequest

    cRequest := '"'
    cRequest += 'Você é um Analista de Notas Fiscais, que informará'
    cRequest += ' erros, na tributação e formatação geral do XML que vou encamihar, observando as'
    cRequest += ' da NF-e do Brasil para todos os estado, me alertando quando tem problemas de regras '
    cRequest += ' calculos errados, localizar e informar em que parte do XML apresanta os erros, a fim de '
    cRequest += ' evitar multas e cobranças indevidas, e quando for surgerir algumas revisões já '
    cRequest += ' verifica o que devo fazer exatamente, reposta precisa e objetiva'
    cRequest += '"'
    
    // Construindo JSON manualmente
    cJSON := ;
        '{ "model": "gpt-3.5-turbo", "messages": ['          + ;
        '{ "role": "system", "content": ' + cRequest + ' },' + ;
        '{ "role": "user"  , "content": "Olá com quem eu falo ?" } ] }'
        
//        '{ "role": "user"  , "content": ' + hb_jsonEncode( cXML ) + ' } ] }'        
                
    // Criar objeto HTTP
    hHttp := CREATEOBJECT( "Msxml2.XMLHTTP" )
    hHttp:Open( "POST", cAPI_URL, .F. )
    hHttp:SetRequestHeader( "Content-Type" , "application/json"   )
    hHttp:SetRequestHeader( "Authorization", "Bearer " + cAPI_KEY )

    // Enviar requisição
    hHttp:Send( cJSON )

    // Capturar resposta
    IF hHttp:Status = 200
        cResponse := hHttp:ResponseText
        cResult   := ParseJSONResponse( cResponse )
        ? "Resposta do ChatGPT:", cResult
    ELSE
        ? "Erro na requisicao:", hHttp:Status, hHttp:StatusText
        ? "Resposta completa:", hHttp:ResponseText
    ENDIF

Return nil

// Função para interpretar o JSON da resposta
Function ParseJSONResponse( cJSON )
    local hResponse := hb_jsonDecode( cJSON )
    local cMessage := ""

    IF ! Empty( hResponse ) .AND. HB_HHasKey( hResponse, "choices" )
        cMessage := hResponse[ "choices" ][1][ "message" ][ "content" ]
    ELSE
        cMessage := "Erro na resposta da API"
    ENDIF

    RETURN cMessage
Thanks,
Ari

FWH 2501 - Harbour 3.2.0 - Embarcadero 7.43 - MySQL
São Paulo - SP - Brasil
www.sisrev.com.br
User avatar
Antonio Linares
Site Admin
Posts: 42655
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 67 times
Been thanked: 96 times
Contact:

Re: Class TOpenAI_ChatGPT by Charles Kwon

Post by Antonio Linares »

Dear Ari,

Yes, if you want to use OpenAI then you have create an account and get an OPENAI_API_KEY
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 42655
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 67 times
Been thanked: 96 times
Contact:

Re: Class TOpenAI_ChatGPT by Charles Kwon

Post by Antonio Linares »

Gemini is free for now (don't know for how long) so you can use Class TGemini.

Or use Class TOllama for always free :)
regards, saludos

Antonio Linares
www.fivetechsoft.com
Post Reply