FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour Error al compilar ejemplos Gemini# con xHb
Posts: 582
Joined: Fri Oct 07, 2005 02:17 PM
Error al compilar ejemplos Gemini# con xHb
Posted: Wed Feb 26, 2025 05:42 AM
Saludos

Compilando los ejemplos de FWH 25.01 con xHarbour 10288 en samples, compilan varios pero todos los gemini#.prg, me dan el error abajo adjunto



Y los ollama# no me funcionan ... algo me falta ?
Enrrique Vertiz Pitta

Lima-Peru

xHb 1.23.1026X, Fwh 25.01, BCC74, MySQL 8.0.X, SQLLIB 1.9m
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Error al compilar ejemplos Gemini# con xHb
Posted: Wed Feb 26, 2025 06:56 AM

Estimado Enrique,

Estamos terminando de adaptar los cambios m谩s recientes de la Clase TGemini a xHarbour

Hoy al comenzar el webinar os daremos la versi贸n m谩s reciente de todas las Clases de Inteligencia Artificial para Harbour y xHarbour

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Error al compilar ejemplos Gemini# con xHb
Posted: Wed Feb 26, 2025 07:33 AM
Class TGemini para Harbour y xHarbour
#include "FiveWin.ch"
#include "hbcurl.ch"

#ifdef __XHARBOUR__
   #define hb_hHasKey( h, k ) HHasKey( h, k )
#endif   

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

CLASS TGemini

   DATA   cKey   INIT ""
   DATA   cModel INIT "gemini-2.0-flash"
   DATA   cResponse
   DATA   cUrl   INIT "https://generativelanguage.googleapis.com/v1beta/models"
   DATA   cUploadUrl INIT "https://generativelanguage.googleapis.com/upload/v1beta/files"
   DATA   hCurl
   DATA   nError INIT 0
   DATA   nHttpCode INIT 0
   DATA   nTemperature INIT 0

   METHOD New( cKey, cModel )
   METHOD Send( uContent, cPrompt, bCallback )
   METHOD End()
   METHOD GetValue()
   METHOD UploadFile( cFileName, lDeleteAfter )
   METHOD GetTokens( cBuffer ) 

ENDCLASS

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

METHOD New( cKey, cModel ) CLASS TGemini

   if Empty( cKey )
      ::cKey = GetEnv( "GEMINI_API_KEY" )
   else
      ::cKey = cKey
   endif

   if ! Empty( cModel )
      ::cModel = cModel
   endif

   if Val( SubStr( Curl_Version_Info()[ 1 ], 1, RAt( ".", Curl_Version_Info()[ 1 ] ) - 1 ) ) - 8.10 > 0.2
      MsgAlert( "Please use an updated curl DLL" )
   endif    

   ::hCurl = curl_easy_init()

return Self

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

METHOD End() CLASS TGemini

   curl_easy_cleanup( ::hCurl )
   ::hCurl = nil

return nil

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

METHOD GetValue() CLASS TGemini

   local hResponse, uValue

   if ! Empty( ::cResponse )
      hb_jsonDecode( ::cResponse, @hResponse )
   endif

   if hb_isHash( hResponse )
      if hb_hHasKey( hResponse, "error" )
         uValue = "API Error: " + hResponse[ "error" ][ "message" ] + " (Code: " + hb_ntos( hResponse[ "error" ][ "code" ] ) + ")"
      elseif hb_hHasKey( hResponse, "candidates" ) .and. Len( hResponse[ "candidates" ] ) > 0
         TRY
            uValue = hResponse[ "candidates" ][ 1 ][ "content" ][ "parts" ][ 1 ][ "text" ]
         CATCH
            uValue = "Error: Unexpected response structure"
         END
      else
         uValue = "Error: No candidates in response"
      endif
   else
      uValue = "Error: Invalid response format"
   endif

return uValue

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

METHOD Send( uContent, cPrompt, bCallback ) CLASS TGemini

   local aHeaders, cJson, hRequest := {=>}, hContents := { => }, hGenerationConfig
   local cFileUri, cMimeType, lIsFile := .F., cUrlEndpoint
   local aFiles, nI, aParts := {}, cFileNameToUpload, cTempFile

   if Empty( cPrompt )
      cPrompt = "what is this or solve this"
   endif

   if hb_isArray( uContent )
      aFiles = uContent
      for nI = 1 to Len( aFiles )
         if hb_isChar( aFiles[ nI ] ) .and. File( aFiles[ nI ] )
            cFileNameToUpload = aFiles[ nI ]
            cTempFile = nil
            if Lower( Right( aFiles[ nI ], 3 ) ) == "prg"
               cTempFile = hb_FNameMerge( hb_FNameDir( aFiles[ nI ] ), hb_FNameName( aFiles[ nI ] ), "txt" )
               hb_FCopy( aFiles[ nI ], cTempFile )
               cFileNameToUpload = cTempFile
            elseif Lower( Right( aFiles[ nI ], 2 ) ) == "ch"
               cTempFile = hb_FNameMerge( hb_FNameDir( aFiles[ nI ] ), hb_FNameName( aFiles[ nI ] ), "txt" )
               hb_FCopy( aFiles[ nI ], cTempFile )
               cFileNameToUpload = cTempFile
            endif
            cFileUri = ::UploadFile( cFileNameToUpload, !Empty( cTempFile ) )
            if Empty( cFileUri )
               if !Empty( cTempFile ) .and. File( cTempFile )
                  hb_FileDelete( cTempFile )
               endif
               return "Error uploading file: " + aFiles[ nI ]
            endif
            do case
               case Lower( Right( aFiles[ nI ], 3 ) ) == "png"
                  cMimeType = "image/png"
               case Lower( Right( aFiles[ nI ], 3 ) ) $ "jpg|jpeg"
                  cMimeType = "image/jpeg"
               case Lower( Right( aFiles[ nI ], 3 ) ) == "pdf"
                  cMimeType = "application/pdf"
               case Lower( Right( aFiles[ nI ], 3 ) ) == "txt"
                  cMimeType = "text/plain"
               case Lower( Right( aFiles[ nI ], 3 ) ) == "csv"
                  cMimeType = "text/csv"
               case Lower( Right( aFiles[ nI ], 3 ) ) == "prg"
                  cMimeType = "text/plain"
               case Lower( Right( aFiles[ nI ], 2 ) ) == "ch"
                  cMimeType = "text/plain"
               otherwise
                  if !Empty( cTempFile ) .and. File( cTempFile )
                     hb_FileDelete( cTempFile )
                  endif
                  return "Unsupported file type: " + aFiles[ nI ]
            endcase
            AAdd( aParts, { "fileData" => { "fileUri" => cFileUri, "mimeType" => cMimeType } } )
            if !Empty( cTempFile ) .and. File( cTempFile )
               hb_FileDelete( cTempFile )
            endif
         else
            return "Invalid file in array: " + aFiles[ nI ]
         endif
      next
      lIsFile = .T.
   elseif hb_isChar( uContent ) .and. File( uContent )
      lIsFile = .T.
      cFileNameToUpload = uContent
      cTempFile = nil
      if Lower( Right( uContent, 3 ) ) == "prg"
         cTempFile = hb_FNameMerge( hb_FNameDir( uContent ), hb_FNameName( uContent ), "txt" )
         hb_FCopy( uContent, cTempFile )
         cFileNameToUpload = cTempFile
      elseif Lower( Right( uContent, 2 ) ) == "ch"
         cTempFile = hb_FNameMerge( hb_FNameDir( uContent ), hb_FNameName( uContent ), "txt" )
         hb_FCopy( uContent, cTempFile )
         cFileNameToUpload = cTempFile
      endif
      cFileUri = ::UploadFile( cFileNameToUpload, !Empty( cTempFile ) )
      if Empty( cFileUri )
         if !Empty( cTempFile ) .and. File( cTempFile )
            hb_FileDelete( cTempFile )
         endif
         return ""
      endif
      do case
         case Lower( Right( uContent, 3 ) ) == "png"
            cMimeType = "image/png"
         case Lower( Right( uContent, 3 ) ) $ "jpg|jpeg"
            cMimeType = "image/jpeg"
         case Lower( Right( uContent, 3 ) ) == "pdf"
            cMimeType = "application/pdf"
         case Lower( Right( uContent, 3 ) ) == "txt"
            cMimeType = "text/plain"
         case Lower( Right( uContent, 3 ) ) == "csv"
            cMimeType = "text/csv"
         case Lower( Right( uContent, 3 ) ) == "prg"
            cMimeType = "text/plain"
         case Lower( Right( uContent, 2 ) ) == "ch"
            cMimeType = "text/plain"
         otherwise
            if !Empty( cTempFile ) .and. File( cTempFile )
               hb_FileDelete( cTempFile )
            endif
            return "Unsupported file type"
      endcase
      AAdd( aParts, { "fileData" => { "fileUri" => cFileUri, "mimeType" => cMimeType } } )
      if !Empty( cTempFile ) .and. File( cTempFile )
         hb_FileDelete( cTempFile )
      endif
   endif

   cUrlEndpoint = iif( hb_isBlock( bCallback ), ":streamGenerateContent", ":generateContent" )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_POST, .T. )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_URL, ::cUrl + "/" + ::cModel + cUrlEndpoint + "?key=" + ::cKey )

   aHeaders := { "Content-Type: application/json" }
   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. )

   hContents[ "role" ] = "user"
   if lIsFile
      hRequest[ "contents" ] = { { "role" => "user", "parts" => aParts } }
      if ! Empty( cPrompt )
         AAdd( hRequest[ "contents" ], { "role" => "user", "parts" => { { "text" => cPrompt } } } )
      endif
   else
      hContents[ "parts" ] = { { "text" => iif( hb_isChar( uContent ), uContent, cPrompt ) } }
      hRequest[ "contents" ] = { hContents }
   endif

   hGenerationConfig = { "temperature" => ::nTemperature,;
                         "topK" => 40, "topP" => 0.95, "maxOutputTokens" => 8192,;
                         "responseMimeType" => "text/plain" }
   hRequest[ "generationConfig" ] = hGenerationConfig

   cJson = hb_jsonEncode( hRequest )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_POSTFIELDS, cJson )

   if hb_isBlock( bCallback )
      curl_easy_setopt( ::hCurl, HB_CURLOPT_WRITEFUNCTION, bCallback )
   endif

   ::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 = "CURL Error code " + Str( ::nError )
   endif

return ::cResponse

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

METHOD UploadFile( cFileName, lDeleteAfter ) CLASS TGemini

   local pCurl, aPost := {}, hHash

   if hb_isPointer( pCurl := curl_easy_init() )

      curl_easy_setopt( pCurl, HB_CURLOPT_CUSTOMREQUEST, "POST" )
      curl_easy_setopt( pCurl, HB_CURLOPT_URL, ::cUploadUrl + "?key=" + ::cKey )
      curl_easy_setopt( pCurl, HB_CURLOPT_FOLLOWLOCATION, .T. )
      curl_easy_setopt( pCurl, HB_CURLOPT_DL_BUFF_SETUP )
      curl_easy_setopt( pCurl, HB_CURLOPT_SSL_VERIFYPEER, .F. )

      AAdd( aPost, { "file", hb_jsonEncode( { "display_name" => cFileName } ) } )
      AAdd( aPost, { nil, cFileName } )

      curl_easy_setopt( pCurl, HB_CURLOPT_MIMEPOST, aPost )

      if ( ::nError := curl_easy_perform( pCurl ) ) == HB_CURLE_OK
         hHash = hb_jsonDecode( ::cResponse := curl_easy_dl_buff_get( pCurl ) )
      else
         MsgAlert( "curl error: " + AllTrim( Str( ::nError ) ) )
      endif

      curl_easy_cleanup( pCurl )
   endif

   if hb_isHash( hHash )
      #ifndef __XHARBOUR__
         if hb_hHasKey( hHash, "file" ) .and. hb_hHasKey( hHash[ "file" ], "uri" )
      #else
         if HHasKey( hHash, "file" ) .and. HHasKey( hHash[ "file" ], "uri" )      
      #endif      
        return hHash[ "file" ][ "uri" ]
      endif
   endif

   if lDeleteAfter .and. File( cFileName )
      hb_FileDelete( cFileName )
   endif

return ""

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

METHOD GetTokens( cBuffer ) CLASS TGemini

   local hResponse, cValue := ""

   if Left( cBuffer, 1 ) == ","
      cBuffer = SubStr( cBuffer, 2 )
   endif

   hb_jsonDecode( cBuffer, @hResponse )

   if ! Empty( hResponse )
      if ValType( hResponse ) == "A"  // Streaming response (array of chunks)
         if hb_hHasKey( hResponse[ 1 ], "error" )
            cValue = "API Error: " + hResponse[ 1 ][ "error" ][ "message" ] + " (Code: " + hb_ntos( hResponse[ 1 ][ "error" ][ "code" ] ) + ")"
         elseif hb_hHasKey( hResponse[ 1 ], "candidates" ) .and. Len( hResponse[ 1 ][ "candidates" ] ) > 0
            TRY
               cValue = hResponse[ 1 ][ "candidates" ][ 1 ][ "content" ][ "parts" ][ 1 ][ "text" ]
            CATCH
               cValue = "Error: Unexpected streaming response structure"
            END
         else
            cValue = "Error: No candidates in streaming response"
         endif
      elseif hb_isHash( hResponse )  // Non-streaming response
         if hb_hHasKey( hResponse, "error" )
            cValue = "API Error: " + hResponse[ "error" ][ "message" ] + " (Code: " + hb_ntos( hResponse[ "error" ][ "code" ] ) + ")"
         elseif hb_hHasKey( hResponse, "candidates" ) .and. Len( hResponse[ "candidates" ] ) > 0
            TRY
               cValue = hResponse[ "candidates" ][ 1 ][ "content" ][ "parts" ][ 1 ][ "text" ]
            CATCH
               cValue = "Error: Unexpected response structure"
            END
         else
            cValue = "Error: No candidates in response"
         endif
      else
         cValue = "Error: Invalid response format in streaming buffer"
      endif
   endif

return cValue

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

#ifdef __XHARBOUR__
   
function HB_FNAMEDIR( cFileName )
   local nLastSlash := Max( RAt( "\", cFileName ), RAt( "/", cFileName ) )
   if nLastSlash > 0
      return Left( cFileName, nLastSlash )
   endif
return ""

function HB_FNAMENAME( cFileName )
   local cName := cFileName
   local nLastSlash := Max( RAt( "\", cFileName ), RAt( "/", cFileName ) )
   local nLastDot

   if nLastSlash > 0
      cName = SubStr( cFileName, nLastSlash + 1 )
   endif

   nLastDot = RAt( ".", cName )
   if nLastDot > 0
      cName = Left( cName, nLastDot - 1 )
   endif

return cName

function HB_FCOPY( cSource, cDest )

   local hSource, hDest, nBytes, nRead, aBuffer := {}

   if hb_isPointer( hSource := FOpen( cSource, "rb" ) )
      if hb_isPointer( hDest := FOpen( cDest, "wb" ) )
         while ! hb_feof( hSource )
            nRead := FRead( aBuffer, 1, 1024, hSource )
            FWrite( aBuffer, 1, nRead, hDest )
         end
         FClose( hDest )
      endif
      FClose( hSource )
   endif

return nil   

function HB_FILEDELETE( cFileName )

   if File( cFileName )
      return FErase( cFileName )
   endif

return nil

#endif
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 582
Joined: Fri Oct 07, 2005 02:17 PM
Re: Error al compilar ejemplos Gemini# con xHb
Posted: Wed Feb 26, 2025 01:29 PM
Saludos Antonio

Copie el codigo y lo grabe como tgemini.prg en la carpeta clases, lo aumente en el Buildx.bat y ahora me sale el siguiente error

Enrrique Vertiz Pitta

Lima-Peru

xHb 1.23.1026X, Fwh 25.01, BCC74, MySQL 8.0.X, SQLLIB 1.9m
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Error al compilar ejemplos Gemini# con xHb
Posted: Wed Feb 26, 2025 03:39 PM

Enrique,

Vamos a distribuir un build actualizado de FWH 25.01

El error que te sale es porque est谩 intentando usar el PRG como si fuese un OBJ

gracias!

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 582
Joined: Fri Oct 07, 2005 02:17 PM
Re: Error al compilar ejemplos Gemini# con xHb
Posted: Wed Feb 26, 2025 08:44 PM

Gracias, estaremos atentos al nuevo realease, muy bueno el Webinar ... hay para hacer mucho, el lenguaje natural para la consulta SQL de los Foros fue sencillamente expectacular !!!

Enrrique Vertiz Pitta

Lima-Peru

xHb 1.23.1026X, Fwh 25.01, BCC74, MySQL 8.0.X, SQLLIB 1.9m
Posts: 582
Joined: Fri Oct 07, 2005 02:17 PM
Re: Error al compilar ejemplos Gemini# con xHb
Posted: Thu Feb 27, 2025 02:09 AM

Saludos Antonio, compila y ejecuta agentdb.prg, cambie los datos apuntando a mi MySQL y cambie tambien la pregunta, pero me boto ese error (abajo adjunto), me parece que se le puede colocar un controlador de errores a la clase para que no bote ... es una idea, pero esto se debera a que la nueva pregunta que puse no da ninguna respuesta ??

Application

===========

Path and name: D:\Fwh\Fwh2501\samples\agentdb.exe (32 bits)

Size: 4,460,032 bytes

Compiler version: xHarbour 1.3.1 Intl. (SimpLex) (Build 20250202)

FiveWin version: FWH 24.10

C compiler version: Borland/Embarcadero C++ 7.7 (32-bit)

Windows 11 64 Bits, version: 6.2, Build 9200

Time from start: 0 hours 0 mins 21 secs

Error occurred at: 02/26/25, 20:51:57

Error description: Error BASE/1068 Argument error: array access

Args:

 [   1] = U

 [   2] = C   error

Stack Calls

===========

Called from: tdeepseek.prg => TDEEPSEEK:GETVALUE( 65 )

Called from: agentdb.prg => MAIN( 28 )

Enrrique Vertiz Pitta

Lima-Peru

xHb 1.23.1026X, Fwh 25.01, BCC74, MySQL 8.0.X, SQLLIB 1.9m
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Error al compilar ejemplos Gemini# con xHb
Posted: Thu Feb 27, 2025 03:45 AM

Enrique,

agentdb.prg usa DeepSeek en la web por lo que es preciso obtener una API key de DeepSeek. Cuesta unos 5 d贸lares solamente.

Mi consejo es cambiar en el c贸digo TDeepSeek() por TGemini() y asi aunque hay que darle una API key en el caso de Google de momento es gratis.

Otra opci贸n es usar TOllama() y asi no hay que darle ninguna API key. Lo id贸neo es usar Phi4 con TOllama() en caso de tener RAM suficiente 贸 sino usar Llama3.2

regards, saludos

Antonio Linares
www.fivetechsoft.com

Continue the discussion