THFTask & TChatAgent - FWAI utilities

Source: source/classes/thftask.prg, source/classes/tchatagent.prg

Two "FWAI" utility classes that bring everyday AI tasks into FiveWin apps with a few lines — see the PyTorch-lite roadmap.

THFTask — HuggingFace NLP tasks

One thin class over the HuggingFace Inference API for the common NLP tasks. Each method posts to the right model endpoint and returns plain Harbour values.

MethodReturns
Classify( cText [,cModel] ){ {label,score}, ... } descending
ZeroShot( cText, aLabels [,cModel] ){ {label,score}, ... } for your own labels
NER( cText [,cModel] ){ {entity,word,score}, ... }
Summarize( cText [,cModel] )summary string
Translate( cText [,cModel] )translation string (en→es by default)
Sentiment( cText [,cModel] ){ label, score } (top)
oT := THFTask():New()                            // uses HF_API_KEY
aCls := oT:Classify( "win a free prize now" )    // -> { {"spam",0.91}, {"ham",0.09} }
? oT:Summarize( cLongReport )
? oT:Translate( "Hello world" )                  // "Hola mundo"
aEnt := oT:NER( "John lives in Madrid" )         // PER John, LOC Madrid
aZ   := oT:ZeroShot( "my invoice is wrong", { "billing", "tech" } )

The transport is injectable via ::bSender { |cUrl, cJson| -> cResponseJson } — leave it NIL to use libcurl, or set it to mock responses (the test samples/ai/thftasktest.prg verifies parsing offline) or route through a different HTTP layer / a local server.

TChatAgent — chat over your app's data

A minimal agent with tool-calling: the LLM either answers, or asks the app to run a query; the app returns rows and the loop continues until the LLM answers. Backend-agnostic via two codeblocks.

flowchart LR U[User question] --> A[TChatAgent] A -->|prompt| L[LLM bChat] L -->|TOOL query| A A -->|run query| T[bTool → rows] T --> A L -->|ANSWER text| R[Answer]
MethodDescription
New( bChat, bTool [,cSystem] )bChat = { |cPrompt| -> cReply } (LLM, e.g. TOLlama or an API class); bTool = { |cQuery| -> cResultText } (you run the DBF/SQL query).
Ask( cQuestion )Run the loop and return the final answer text.
oLlm   := TOLlama():New( "llama3" )
oAgent := TChatAgent():New( ;
   { |cPrompt| oLlm:Send( cPrompt ), oLlm:GetValue() }, ;     // LLM backend
   { |cQuery|  RunMyQuery( cQuery ) } )                       // your data tool
? oAgent:Ask( "how many overdue invoices this month?" )

The LLM is instructed to reply either TOOL <query> or ANSWER <text>; tool results are fed back as RESULT: <rows> until it answers (or nMaxSteps). Pair it with TSemanticIndex to let the agent retrieve relevant records. Test: samples/ai/chatagenttest.prg.

TWhisperCpp — offline speech-to-text

Local, private speech-to-text via whisper.cpp (MIT, free). No API, no cost, no data leaving the machine — great for dictation into GETs or transcribing voice notes.

oW := TWhisperCpp():New( "ggml-base.bin" )
if oW:IsAvailable()
   cText := oW:Transcribe( "note.wav" )       // 16 kHz mono PCM WAV
endif
oW:End()

Optional, zero-cost for everyone else. The class lives in the FWH library, but the native binding (source/winapi/whispercpp.c) is not part of the FWH C library and is never linked by default — normal apps link nothing extra and IsAvailable() simply returns .F.. An app that wants speech-to-text builds whisper.cpp as a static lib and compiles that one file into its own build with -DHB_HAS_WHISPER + libwhisper. Models (ggml-tiny/base/small...) are free from huggingface.co/ggerganov/whisper.cpp. Demo: samples/ai/whisperdemo.prg; degradation test: samples/ai/whispertest.prg.

See Also