FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour API-REST conexion con ollama local
Posts: 492
Joined: Wed Nov 16, 2005 12:03 PM
API-REST conexion con ollama local
Posted: Tue Oct 14, 2025 07:14 PM
Colegas
Saludos
Estamos evaluando el uso de la IA
Actualmente estamos utilizando PYTHON mientras actualizo FW
Compartimos este codigo para realizar validaciones y podamos compartir experiencia. El contenido lo obtuve desde COPILOT

¡Excelente iniciativa! Aunque Harbour, FiveWin y Visual FoxPro no tienen librerías modernas para consumir APIs REST como en Python, sí es posible conectarse a Ollama (o cualquier servidor local de IA que use HTTP) utilizando solicitudes HTTP básicas. Aquí te muestro cómo hacerlo en cada entorno, con ejemplos adaptados para conectarte al servidor de Ollama en red local.

---

🧵 1. Harbour + HbHttp (o Curl)

Si tienes Harbour con la librería hbhttp, puedes hacer algo así:

harbour
#include "hbclass.ch"

FUNCTION Main()
LOCAL cUrl:= "http://192.168.1.100:11434/api/generate"
LOCAL cPayload:= '{ "model": "llama2", "prompt": "¿Cuál es la capital de Venezuela?", "stream": false}'
LOCAL oHttp:= hb_HttpClient():New()
LOCAL cResponse

oHttp:SetHeader("Content-Type", "application/json")
cResponse:= oHttp:Post(cUrl, cPayload)

? "Respuesta del servidor:"
? cResponse
RETURN NIL


> Requiere hbhttp o integración con libcurl.

---

🪟 2. FiveWin (Harbour + GUI)

FiveWin puede usar WinInet o libcurl para hacer peticiones HTTP. Aquí tienes un ejemplo básico usando WinInet:

harbour
#include "FiveWin.ch"

FUNCTION Main()
LOCAL hInternet, hConnect, cUrl, cPayload, cResponse

cUrl:= "http://192.168.1.100:11434/api/generate"
cPayload:= '{ "model": "llama2", "prompt": "¿Cuál es la capital de Venezuela?", "stream": false}'

hInternet:= InternetOpen( "FiveWin", 1, "", "", 0)
hConnect:= InternetOpenUrl( hInternet, cUrl, "", 0, 0, 0)

IF hConnect!= NIL
InternetWriteFile( hConnect, cPayload)
cResponse:= InternetReadFile( hConnect)
MsgInfo( cResponse)
InternetCloseHandle( hConnect)
ENDIF

InternetCloseHandle( hInternet)
RETURN NIL


> Este ejemplo requiere adaptar funciones de lectura/escritura según tu versión de FiveWin.

---

🦊 3. Visual FoxPro

En FoxPro puedes usar MSXML2.XMLHTTP para hacer peticiones HTTP:

foxpro
LOCAL loHttp, lcUrl, lcPayload, lcResponse

lcUrl = "http://192.168.1.100:11434/api/generate"
lcPayload = '{ "model": "llama2", "prompt": "¿Cuál es la capital de Venezuela?", "stream": false}'

loHttp = CREATEOBJECT("MSXML2.XMLHTTP")
loHttp.Open("POST", lcUrl,.F.)
loHttp.setRequestHeader("Content-Type", "application/json")
loHttp.Send(lcPayload)

lcResponse = loHttp.ResponseText
? "Respuesta del servidor:"
? lcResponse


> Este método funciona bien en Windows y es ideal para FoxPro 9.

---

¿Quieres que te ayude a integrar esto en una interfaz gráfica, o que el resultado se muestre en una tabla o archivo? También puedo ayudarte a manejar errores de red o respuestas JSON en cada entorno.
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: API-REST conexion con ollama local
Posted: Tue Oct 14, 2025 07:28 PM
Querido Juan,

Las versiones recientes de FWH proporcionan la Clase TOllama, asi como la Clase TOpenAI, TDeepSeek, TGemini, TKimi.

tollama.prg
// Developed by FiveTech Software, using parts by Charles OhChul

#include "FiveWin.ch"
#include "hbcurl.ch"

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

CLASS TOLlama
    
   DATA   cModel
   DATA   cPrompt
   DATA   cResponse
   DATA   cUrl
   DATA   hCurl
   DATA   nError INIT 0
   DATA   nHttpCode INIT 0
   DATA   aAgents

   METHOD New( cModel )
   METHOD Send( cPrompt )    
   METHOD SendStream( cPrompt, bWriteFunction )  
   METHOD SendImage( cImageFileName, cPrompt )   
   METHOD End()    
   METHOD GetValue()    

ENDCLASS        

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

METHOD New( cModel ) CLASS TOLlama

   DEFAULT cModel := "deepseek-r1:14b"

   ::cModel = cModel
   ::cUrl = "http://localhost:11434/api/chat"
   ::hCurl = curl_easy_init()
    
return Self    

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

METHOD End() CLASS TOLlama

    curl_easy_cleanup( ::hCurl )
    ::hCurl = nil

return nil    

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

METHOD GetValue() CLASS TOLlama

   local hResponse, uValue  
   
   hb_jsonDecode( ::cResponse, @hResponse )

   TRY 
      uValue = hResponse[ "message" ][ "content" ]
   CATCH
      uValue = hResponse[ "error" ][ "message" ]
   END   

return uValue

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

METHOD Send( cPrompt ) CLASS TOLlama 

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

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

   curl_easy_setopt( ::hCurl, HB_CURLOPT_POST, .T. )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_URL, ::cUrl )

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

   hRequest[ "model" ]       = ::cModel
   hMessage[ "role" ]        = "user"
   hMessage[ "content" ]     = cPrompt
   hRequest[ "messages" ]    = { hMessage }
   hRequest[ "stream" ]      = .F.
   hRequest[ "temperature" ] = 0.5

   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 SendStream( cPrompt, bWriteFunction ) CLASS TOLlama 

   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" }

   curl_easy_setopt( ::hCurl, HB_CURLOPT_HTTPHEADER, aHeaders )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_USERNAME, '' )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_SSL_VERIFYPEER, .F. )

   // Habilitar modo streaming
   hRequest[ "model" ]       = ::cModel
   hMessage[ "role" ]        = "user"
   hMessage[ "content" ]     = cPrompt
   hRequest[ "messages" ]    = { hMessage }
   hRequest[ "stream" ]      = .T.  // ACTIVAR STREAMING
   hRequest[ "temperature" ] = 0.5

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

   // Configurar la funci├│n de callback para recibir tokens en tiempo real
   curl_easy_setopt( ::hCurl, HB_CURLOPT_WRITEFUNCTION, bWriteFunction )

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

   if ::nError != HB_CURLE_OK
      ::cResponse := "Error code " + Str( ::nError )
   endif
    
return ::cResponse

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

METHOD SendImage( cImageFileName, cPrompt ) CLASS TOLlama

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

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

   DEFAULT cPrompt := "What is in this image?"

   cBase64Image = hb_base64Encode( memoRead( cImageFileName ) )

   curl_easy_setopt( ::hCurl, HB_CURLOPT_POST, .T. )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_URL, ::cUrl )

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

   hRequest[ "model" ]    = ::cModel
   hMessage[ "role" ]     = "user"
   hMessage[ "content" ]  = cPrompt
   hMessage[ "images" ]   = { cBase64Image }
   hRequest[ "messages" ] = { hMessage }
   hRequest[ "stream" ]   = .F.   

   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

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

CLASS TAgent 

   DATA cCategory
   DATA aTools 

   METHOD New( cCategory, aTools )

ENDCLASS      

//----------------------------------------------------------------------------//
ollama1.prg
#include "FiveWin.ch"

function Main()

    local oChat := TOLlama():New( "llama3.2-vision" ) // deepseek-r1:32b" 

    oChat:Send( "who are you ?" )
    ? oChat:GetValue()

    oChat:End()

return nil
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 492
Joined: Wed Nov 16, 2005 12:03 PM
Re: API-REST conexion con ollama local
Posted: Tue Oct 14, 2025 07:31 PM

Antonio

Gracias,

Excelente avance y potencia de FW.

Por ahora estoy resolviendo con lo que tengo, mientras que recompilo AdaptaPro con FW64, necesitare tus servicios.

Continue the discussion