Page 1 of 1

Class TOpenAI_ChatGPT by Charles Kwon

PostPosted: Tue Jan 30, 2024 10:04 pm
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 view
#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
    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

Re: Class TOpenAI_ChatGPT by Charles Kwon

PostPosted: Wed Jan 31, 2024 10:18 am
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

Re: Class TOpenAI_ChatGPT by Charles Kwon

PostPosted: Thu Feb 01, 2024 12:27 am
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

Re: Class TOpenAI_ChatGPT by Charles Kwon

PostPosted: Thu Feb 01, 2024 6:52 am
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