Class TOpenAI with Vision API support

387 views
Skip to first unread message

antonio....@gmail.com

unread,
Jan 3, 2025, 5:52:26 PMJan 3
to Harbour Users
A Magic Kings gift from FiveTech :-)  

#include "c:\harbour\contrib\hbcurl\hbcurl.ch"

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

function Main()

    local oChatgpt := TOpenAI():New()

    // oChatgpt:SendImage( "../bitmaps/logo/fivepowergm5.jpg" )
    oChatgpt:Send( "What is the capital of France ?" )

    MsgInfo( oChatgpt:GetValue() )

    oChatgpt:End()

return nil    

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

CLASS TOpenAI
   
    DATA   cKey   INIT ""
    DATA   cModel INIT "gpt-4o-mini"
    DATA   cResponse
    DATA   cUrl
    DATA   hCurl
    DATA   nError INIT 0
    DATA   nHttpCode INIT 0

    METHOD New( cKey, cModel )
    METHOD Send( cPrompt )    
    METHOD SendImage( cImageFileName )    
    METHOD SendImageURL( cImageUrl )
    METHOD End()    
    METHOD GetValue( cHKey )    

ENDCLASS        

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

METHOD New( cKey, cModel ) CLASS TOpenAI

    if Empty( cKey )
       ::cKey = GetEnv( "OPENAI_API_KEY" )
    endif

    if ! Empty( cModel )
       ::cModel = cModel
    endif
   
    ::hCurl = curl_easy_init()
   
return Self    

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

METHOD End() CLASS TOpenAI

    curl_easy_cleanup( ::hCurl )
    ::hCurl = nil

return nil    

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

METHOD GetValue( cHKey ) CLASS TOpenAI

    local aKeys := hb_AParams(), cKey
    local uValue := hb_jsonDecode( ::cResponse )

    hb_default( @cHKey, "content" )

    if cHKey == "content"
       uValue = uValue[ "choices" ][ 1 ][ "message" ][ "content" ]
    endif

    TRY
       for each cKey in aKeys
          if ValType( uValue[ cKey ] ) == "A"
             uValue = uValue[ cKey ][ 1 ][ "choices" ][ 1 ][ "message" ][ "content" ]
          else
             uValue = uValue[ cKey ]
          endif
       next
    CATCH
       XBrowser( uValue )
    END

return uValue

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

METHOD Send( cPrompt ) CLASS TOpenAI

    LOCAL aHeaders, cJson, hRequest := { => }, 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.)

    hb_HSet( hRequest, "model", ::cModel )
    hb_HSet( hMessage, "role", "user" )
    hb_HSet( hMessage, "content", cPrompt )

    hRequest[ "messages" ] = { hMessage }
    cJson = hb_jsonEncode( hRequest )
    curl_easy_setopt( ::hCurl, HB_CURLOPT_POSTFIELDS, cJson )
    ::nError = curl_easy_perform( ::hCurl )
    curl_easy_getinfo( ::hCurl, HB_CURLINFO_RESPONSE_CODE, @::nHttpCode )

    if ::nError == HB_CURLE_OK
       ::cResponse = curl_easy_dl_buff_get( ::hCurl )
    else
       ::cResponse := "Error code " + Str( ::nError )
    endif
 
return ::cResponse

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

METHOD SendImage( cImageFileName ) CLASS TOpenAI

    local aHeaders, cJson, cBase64Image, hMessage := { => }

    if ! File( cImageFileName )
       MsgAlert( "Image " + cImageFileName + " not found" )
       return nil
    endif

    cBase64Image := hb_base64Encode( memoRead( cImageFileName ) )

    curl_easy_setopt( ::hCurl, HB_CURLOPT_POST, .T. )
    curl_easy_setopt( ::hCurl, HB_CURLOPT_URL, "https://api.openai.com/v1/chat/completions" )

    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. )

    hb_HSet( hMessage, "role", "user" )
    hb_HSet( hMessage, "content", { ;
        { "type" => "text", "text" => "What is in this image?" }, ;
        { "type" => "image_url", ;
          "image_url" => { "url" => "data:image/jpeg;base64," + cBase64Image } } } )

    cJson := hb_jsonEncode( { "model" => ::cModel, ;
                              "messages" => { hMessage } } )

    curl_easy_setopt( ::hCurl, HB_CURLOPT_POSTFIELDS, cJson )

    ::nError = curl_easy_perform( ::hCurl )
    curl_easy_getinfo( ::hCurl, HB_CURLINFO_RESPONSE_CODE, @::nHttpCode )

    if ::nError == HB_CURLE_OK
       ::cResponse = curl_easy_dl_buff_get( ::hCurl )
    else
       ::cResponse = "Error code " + Str( ::nError )
    endif

return ::cResponse

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

METHOD SendImageURL( cImageUrl ) CLASS TOpenAI

    local aHeaders, cJson, hRequest := { => }, 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. )

    hb_HSet( hRequest, "model", ::cModel )
    hb_HSet( hMessage, "role", "user" )

    hb_HSet( hMessage, "content", { ;
        { "type" => "text", "text" => "What's in this image?" }, ;
        { "type" => "image_url", "image_url" => { "url" => cImageUrl } } } )

    hRequest[ "messages" ] = { hMessage }

    cJson = hb_jsonEncode( hRequest )

    curl_easy_setopt( ::hCurl, HB_CURLOPT_POSTFIELDS, cJson )

    ::nError = curl_easy_perform( ::hCurl )
    curl_easy_getinfo( ::hCurl, HB_CURLINFO_RESPONSE_CODE, @::nHttpCode )

    if ::nError == HB_CURLE_OK
       ::cResponse = curl_easy_dl_buff_get( ::hCurl )
    else
       ::cResponse = "Error code " + Str( ::nError )
    endif

return ::cResponse

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

Clippero

unread,
Jan 4, 2025, 10:05:54 AMJan 4
to Harbour Users
Dónde está todo el código fuente necesario para utilizar esto en Harbour ?

Lautaro Moreira

unread,
Jan 4, 2025, 6:10:03 PMJan 4
to harbou...@googlegroups.com, antonio....@gmail.com

Estimado Antonio,

Muchas gracias por el regalo.

Saludos

Lautaro Moreira
--
You received this message because you are subscribed to the Google Groups "Harbour Users" group.
Unsubscribe: harbour-user...@googlegroups.com
Web: https://groups.google.com/group/harbour-users
---
You received this message because you are subscribed to the Google Groups "Harbour Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to harbour-user...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/harbour-users/93a8d9b9-ef05-4603-8b8d-f46d044f6d6bn%40googlegroups.com.

antonio....@gmail.com

unread,
Jan 5, 2025, 9:20:53 AMJan 5
to Harbour Users
Todo el código está incluido, salvo MsgInfo() y XBrowser() que los puedes quitar

saludos

Eric Lendvai

unread,
Jan 5, 2025, 10:16:24 PMJan 5
to Harbour Users
Thank you Antonio for ALL your contribution !!!
Eric

roberto....@gmail.com

unread,
Jan 6, 2025, 4:46:32 PMJan 6
to Harbour Users
Thank you Antonio!

Antonio Cardinaux

unread,
Dec 3, 2025, 9:09:34 AM (yesterday) Dec 3
to Harbour Users
Hola. Estuve probando la clase. Como es harbour quité xbrowser() y msginfo() lo reemplacé por Alert(). En el método:


METHOD GetValue( cHKey ) CLASS TOpenAI

    local aKeys := hb_AParams(), cKey
    local uValue := hb_jsonDecode( ::cResponse )
    Altd()
    hb_default( @cHKey, "content" )

    if cHKey == "content"
       uValue = uValue[ "choices" ][ 1 ][ "message" ][ "content" ]
    endif

    TRY
       for each cKey in aKeys
          if ValType( uValue[ cKey ] ) == "A"
             uValue = uValue[ cKey ][ 1 ][ "choices" ][ 1 ][ "message" ][ "content" ]
          else
             uValue = uValue[ cKey ]
          endif
       next
    CATCH
       // XBrowser( uValue )
    END

return uValue

Me arroja un error: Argument Error array access, o sea en uValue. Cuando me fijo que valor tiene uValue veo que aparece "495". Evidentemente a mi no me esta dando un Hash ¿Que puede estar fallando? de hecho lo único que hice fue poner la clase como un PRG aparte y el módulo que está como Main() en otro lugar, el resto del código es tal cual como aparece

Antonio
Sistemas
Resipol

antonio....@gmail.com

unread,
10:35 AM (9 hours ago) 10:35 AM
to Harbour Users
Estimado Antonio,

Has establecido asi tu key de OpenAI antes de usar la clase ?

set OPENAI_API_KEY= sk-...

saludos
Reply all
Reply to author
Forward
0 new messages